simikkk commited on
Commit
ff31e7b
·
verified ·
1 Parent(s): edfd6e5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +410 -163
app.py CHANGED
@@ -1,3 +1,10 @@
 
 
 
 
 
 
 
1
  """
2
  OmniParse AI - Enterprise Document Parsing System
3
  Single-file Hugging Face Spaces Application
@@ -446,19 +453,21 @@ def bridge_export(extracted_json: str) -> str:
446
  return generate_csv_b64(extracted_json)
447
 
448
  # ─────────────────────────────────────────────────────────────────────────────
449
- # GRADIO UI & CUSTOM HTML INJECTION
450
  # ─────────────────────────────────────────────────────────────────────────────
451
 
452
  CUSTOM_CSS = """
453
  :root {
454
- --bg-primary: #09090b;
455
- --bg-surface: #111113;
456
- --bg-elevated: #18181b;
457
- --border-color: #27272a;
458
- --accent-blue: #3b82f6;
459
- --accent-emerald: #10b981;
460
- --text-primary: #fafafa;
461
- --text-muted: #a1a1aa;
 
 
462
  }
463
  .gradio-container {
464
  max-width: 100% !important;
@@ -471,11 +480,140 @@ CUSTOM_CSS = """
471
  background: var(--bg-primary);
472
  color: var(--text-primary);
473
  font-family: 'Inter', sans-serif;
 
 
 
474
  }
475
  footer { display: none !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  """
477
 
478
- # The massive custom HTML, CSS, and JS frontend
479
  def get_frontend_html() -> str:
480
  return """
481
  <!DOCTYPE html>
@@ -485,88 +623,125 @@ def get_frontend_html() -> str:
485
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
486
  <title>OmniParse AI</title>
487
  <script src="https://cdn.tailwindcss.com"></script>
 
 
488
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
489
- <style>
490
- body { font-family: 'Inter', sans-serif; }
491
- .font-mono { font-family: 'JetBrains Mono', monospace; }
492
- ::-webkit-scrollbar { width: 6px; height: 6px; }
493
- ::-webkit-scrollbar-track { background: #111113; }
494
- ::-webkit-scrollbar-thumb { background: #27272a; border-radius: 3px; }
495
- ::-webkit-scrollbar-thumb:hover { background: #3f3f46; }
496
- .modal-bg { background: rgba(9, 9, 11, 0.8); backdrop-filter: blur(8px); }
497
- .glow-blue { box-shadow: 0 0 15px rgba(59, 130, 246, 0.3); }
498
- .glow-emerald { box-shadow: 0 0 15px rgba(16, 185, 129, 0.3); }
499
- .drop-zone-active { border-color: #10b981 !important; background: rgba(16, 185, 129, 0.05) !important; }
500
- .hidden-gradio { position: absolute; left: -9999px; top: -9999px; opacity: 0; pointer-events: none; }
501
- .terminal-line { animation: fadeIn 0.3s ease-in; }
502
- @keyframes fadeIn { from { opacity: 0; transform: translateY(2px); } to { opacity: 1; transform: translateY(0); } }
503
- </style>
504
  </head>
505
- <body class="bg-[#09090b] text-white">
506
 
507
  <div id="omniparse-root">
508
  <!-- Navigation -->
509
- <nav class="border-b border-[#27272a] bg-[#09090b] sticky top-0 z-40">
510
- <div class="max-w-7xl mx-auto px-6 py-4 flex justify-between items-center">
511
- <div class="flex items-center space-x-3">
512
- <div class="w-8 h-8 bg-gradient-to-br from-blue-500 to-emerald-500 rounded-lg flex items-center justify-center font-bold">O</div>
513
- <span class="text-xl font-bold tracking-tight">OmniParse <span class="text-blue-400">AI</span></span>
514
- </div>
515
- <div class="flex items-center space-x-4">
516
- <span id="user-badge" class="text-xs px-3 py-1.5 bg-[#18181b] border border-[#27272a] rounded-full text-gray-400">Not Authenticated</span>
517
- <button id="upgrade-btn" class="px-4 py-1.5 bg-blue-600 hover:bg-blue-700 rounded-full text-sm font-semibold transition">Upgrade to Pro</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
518
  </div>
519
  </div>
520
  </nav>
521
 
522
- <!-- Main Content -->
523
- <main class="max-w-7xl mx-auto px-6 py-8">
524
 
525
  <!-- Auth View -->
526
- <div id="auth-view" class="min-h-[70vh] flex flex-col items-center justify-center">
527
- <div class="w-full max-w-md bg-[#111113] border border-[#27272a] rounded-2xl p-8">
528
- <h2 class="text-2xl font-bold mb-6 text-center">Access Terminal</h2>
529
- <div class="space-y-4">
530
- <div>
531
- <label class="block text-xs font-mono text-gray-400 mb-1">EMAIL</label>
532
- <input id="auth-email" type="email" class="w-full bg-[#18181b] border border-[#27272a] rounded-lg px-4 py-2.5 text-sm focus:border-blue-500 outline-none" placeholder="user@corp.com">
533
- </div>
534
- <div>
535
- <label class="block text-xs font-mono text-gray-400 mb-1">PASSWORD</label>
536
- <input id="auth-pass" type="password" class="w-full bg-[#18181b] border border-[#27272a] rounded-lg px-4 py-2.5 text-sm focus:border-blue-500 outline-none" placeholder="••••••••">
537
- </div>
538
- <div class="flex gap-2">
539
- <button id="login-btn" class="flex-1 bg-blue-600 hover:bg-blue-700 rounded-lg py-2.5 text-sm font-semibold transition">Login</button>
540
- <button id="signup-btn" class="flex-1 bg-[#18181b] border border-[#27272a] hover:bg-[#27272a] rounded-lg py-2.5 text-sm font-semibold transition">Sign Up</button>
541
- </div>
542
- <div class="relative py-2">
543
- <div class="absolute inset-0 flex items-center"><div class="w-full border-t border-[#27272a]"></div></div>
544
- <div class="relative flex justify-center"><span class="bg-[#111113] px-2 text-xs text-gray-500">OR</span></div>
 
 
 
 
 
 
 
 
 
 
545
  </div>
546
- <button id="guest-btn" class="w-full bg-emerald-600/20 border border-emerald-600/40 text-emerald-400 hover:bg-emerald-600/30 rounded-lg py-2.5 text-sm font-semibold transition">Continue as Guest (Free Plan)</button>
547
  </div>
548
  </div>
549
  </div>
550
 
551
  <!-- Dashboard View -->
552
  <div id="dashboard-view" class="hidden">
 
 
 
 
 
553
  <!-- Stats Banner -->
554
- <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
555
- <div class="bg-[#111113] border border-[#27272a] rounded-xl p-5">
556
- <div class="text-xs font-mono text-gray-500 uppercase">Processed Docs</div>
557
- <div id="stat-docs" class="text-3xl font-bold mt-1">0</div>
 
 
 
 
 
 
558
  </div>
559
- <div class="bg-[#111113] border border-[#27272a] rounded-xl p-5">
560
- <div class="text-xs font-mono text-gray-500 uppercase">Extracted Fields</div>
561
- <div id="stat-fields" class="text-3xl font-bold mt-1 text-blue-400">0</div>
 
 
 
 
 
 
562
  </div>
563
- <div class="bg-[#111113] border border-[#27272a] rounded-xl p-5">
564
- <div class="text-xs font-mono text-gray-500 uppercase">Execution Time</div>
565
- <div id="stat-time" class="text-3xl font-bold mt-1 text-emerald-400">0.00s</div>
 
 
 
 
 
 
566
  </div>
567
- <div class="bg-[#111113] border border-[#27272a] rounded-xl p-5">
568
- <div class="text-xs font-mono text-gray-500 uppercase">Accuracy Score</div>
569
- <div id="stat-acc" class="text-3xl font-bold mt-1 text-emerald-400">0%</div>
 
 
 
 
 
 
570
  </div>
571
  </div>
572
 
@@ -574,44 +749,61 @@ def get_frontend_html() -> str:
574
  <!-- Left Column: Upload & Terminal -->
575
  <div class="lg:col-span-2 space-y-6">
576
  <!-- Upload Zone -->
577
- <div id="drop-zone" class="border-2 border-dashed border-[#27272a] rounded-2xl p-12 text-center cursor-pointer hover:border-blue-500 transition">
578
  <input id="file-input" type="file" class="hidden" accept="image/*">
579
- <svg class="mx-auto h-12 w-12 text-gray-500 mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" /></svg>
580
- <p class="text-lg font-semibold">Drop invoice image here</p>
581
- <p class="text-sm text-gray-500 mt-1">or click to browse</p>
 
 
 
 
582
  </div>
583
 
584
  <!-- Terminal -->
585
- <div class="bg-[#111113] border border-[#27272a] rounded-xl overflow-hidden">
586
- <div class="flex items-center justify-between px-4 py-2.5 border-b border-[#27272a] bg-[#18181b]">
587
- <div class="flex items-center space-x-2">
588
- <div class="w-2.5 h-2.5 bg-red-500 rounded-full"></div>
589
- <div class="w-2.5 h-2.5 bg-yellow-500 rounded-full"></div>
590
- <div class="w-2.5 h-2.5 bg-green-500 rounded-full"></div>
 
 
 
591
  </div>
592
- <span class="text-xs font-mono text-gray-500">live_terminal_output.json</span>
593
  </div>
594
- <pre id="terminal-output" class="p-4 text-xs font-mono text-emerald-400 h-80 overflow-y-auto whitespace-pre-wrap">> Awaiting input...</pre>
595
  </div>
596
  </div>
597
 
598
  <!-- Right Column: Export & Status -->
599
  <div class="space-y-6">
600
  <!-- Status Card -->
601
- <div class="bg-[#111113] border border-[#27272a] rounded-xl p-5">
602
- <h3 class="text-sm font-bold uppercase text-gray-400 mb-4">Compliance Status</h3>
603
- <div id="compliance-status" class="space-y-3 text-sm">
604
- <div class="flex justify-between"><span class="text-gray-500">Validation</span><span class="text-gray-600">--</span></div>
605
- <div class="flex justify-between"><span class="text-gray-500">Duplicates</span><span class="text-gray-600">--</span></div>
 
 
 
 
 
 
 
 
 
 
606
  </div>
607
  </div>
608
 
609
  <!-- Export Card -->
610
- <div class="bg-[#111113] border border-[#27272a] rounded-xl p-5">
611
- <h3 class="text-sm font-bold uppercase text-gray-400 mb-4">Data Export</h3>
612
- <button id="export-btn" class="w-full bg-[#18181b] border border-[#27272a] hover:bg-[#27272a] rounded-lg py-2.5 text-sm font-semibold transition flex items-center justify-center">
613
- <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /></svg>
614
- Download CSV
615
  </button>
616
  <a id="csv-download-link" href="#" download="omniparse_export.csv" class="hidden">dl</a>
617
  </div>
@@ -622,30 +814,39 @@ def get_frontend_html() -> str:
622
 
623
  <!-- Chat Companion Widget -->
624
  <div id="chat-widget" class="fixed bottom-6 right-6 z-30 hidden">
625
- <div class="bg-[#111113] border border-[#27272a] rounded-2xl w-80 shadow-2xl flex flex-col" style="height: 400px;">
626
- <div class="px-4 py-3 border-b border-[#27272a] flex justify-between items-center">
627
- <h3 class="text-sm font-bold flex items-center"><span class="w-2 h-2 bg-emerald-500 rounded-full mr-2"></span> AI Audit Companion</h3>
628
- <button id="chat-close" class="text-gray-500 hover:text-white">&times;</button>
 
 
 
 
 
629
  </div>
630
- <div id="chat-log" class="flex-1 p-3 overflow-y-auto space-y-2 text-sm"></div>
631
- <div class="p-3 border-t border-[#27272a] flex gap-2">
632
- <input id="chat-input" type="text" placeholder="Ask about the data..." class="flex-1 bg-[#18181b] border border-[#27272a] rounded-lg px-3 py-1.5 text-xs outline-none focus:border-blue-500">
633
- <button id="chat-send" class="bg-blue-600 rounded-lg px-3 py-1.5 text-xs font-bold">Send</button>
 
 
 
 
634
  </div>
635
  </div>
636
  </div>
637
- <button id="chat-toggle" class="fixed bottom-6 right-6 z-30 bg-blue-600 hover:bg-blue-700 w-14 h-14 rounded-full flex items-center justify-center shadow-lg glow-blue hidden">
638
- <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z" /></svg>
639
  </button>
640
 
641
  <!-- Footer -->
642
- <footer class="border-t border-[#27272a] mt-12 py-6">
643
- <div class="max-w-7xl mx-auto px-6 flex flex-col md:flex-row justify-between items-center">
644
- <p class="text-xs text-gray-600">© 2024 OmniParse AI. All rights reserved.</p>
645
- <div class="flex gap-4 mt-4 md:mt-0">
646
- <button onclick="openModal('terms-modal')" class="text-xs text-gray-500 hover:text-white">Terms of Service</button>
647
- <button onclick="openModal('privacy-modal')" class="text-xs text-gray-500 hover:text-white">Privacy Policy</button>
648
- <button onclick="openModal('gdpr-modal')" class="text-xs text-gray-500 hover:text-white">GDPR Compliance</button>
649
  </div>
650
  </div>
651
  </footer>
@@ -653,92 +854,115 @@ def get_frontend_html() -> str:
653
 
654
  <!-- Modals -->
655
  <div id="terms-modal" class="modal-bg fixed inset-0 z-50 hidden flex items-center justify-center p-4">
656
- <div class="bg-[#111113] border border-[#27272a] rounded-2xl max-w-2xl w-full p-8 max-h-[80vh] overflow-y-auto">
657
- <h2 class="text-2xl font-bold mb-4">Terms of Service</h2>
658
- <div class="text-sm text-gray-400 space-y-3">
 
 
 
659
  <p>1. Acceptance of Terms: By accessing OmniParse AI, you agree to be bound by these terms. AI data processing carries inherent risks, and outputs should be verified by a human operator.</p>
660
  <p>2. Enterprise Liability: OmniParse AI is not liable for misclassified financial data. The system provides high-confidence extractions but does not replace formal accounting audits.</p>
661
  <p>3. Data Retention: Parsed documents are retained for a maximum of 30 days for caching purposes unless explicitly deleted by the user or enterprise admin.</p>
662
  </div>
663
- <button onclick="closeModal('terms-modal')" class="mt-6 bg-[#18181b] border border-[#27272a] rounded-lg px-4 py-2 text-sm">Close</button>
664
  </div>
665
  </div>
666
 
667
  <div id="privacy-modal" class="modal-bg fixed inset-0 z-50 hidden flex items-center justify-center p-4">
668
- <div class="bg-[#111113] border border-[#27272a] rounded-2xl max-w-2xl w-full p-8 max-h-[80vh] overflow-y-auto">
669
- <h2 class="text-2xl font-bold mb-4">Privacy Policy</h2>
670
- <div class="text-sm text-gray-400 space-y-3">
 
 
 
671
  <p>1. Data Collection: We collect minimal authentication data (email, hashed password). Document images are processed in memory and not permanently stored unless explicitly saved by the user.</p>
672
  <p>2. AI Processing: Images are processed via our internal neural networks. We do not share your documents with third-party APIs.</p>
673
  <p>3. Security: Passwords are hashed using PBKDF2 with SHA-256 and unique cryptographic salts. Database access is strictly isolated.</p>
674
  </div>
675
- <button onclick="closeModal('privacy-modal')" class="mt-6 bg-[#18181b] border border-[#27272a] rounded-lg px-4 py-2 text-sm">Close</button>
676
  </div>
677
  </div>
678
 
679
  <div id="gdpr-modal" class="modal-bg fixed inset-0 z-50 hidden flex items-center justify-center p-4">
680
- <div class="bg-[#111113] border border-[#27272a] rounded-2xl max-w-2xl w-full p-8 max-h-[80vh] overflow-y-auto">
681
- <h2 class="text-2xl font-bold mb-4">GDPR Compliance</h2>
682
- <div class="text-sm text-gray-400 space-y-3">
 
 
 
683
  <p>1. Right to Erasure: You may request complete deletion of your account and associated parsed invoices at any time. Execution occurs within 72 hours.</p>
684
  <p>2. Data Portability: Users can export all extracted data in CSV format compliant with machine-readable portability standards.</p>
685
  <p>3. Automated Decision Making: Our AI parsing does not make automated legal decisions. All outputs are advisory and require human review.</p>
686
  </div>
687
- <button onclick="closeModal('gdpr-modal')" class="mt-6 bg-[#18181b] border border-[#27272a] rounded-lg px-4 py-2 text-sm">Close</button>
688
  </div>
689
  </div>
690
 
691
  <!-- Upgrade & Checkout Modals -->
692
  <div id="upgrade-modal" class="modal-bg fixed inset-0 z-50 hidden flex items-center justify-center p-4">
693
- <div class="bg-[#111113] border border-[#27272a] rounded-2xl max-w-md w-full p-8">
694
- <h2 class="text-2xl font-bold mb-2">Upgrade to Enterprise</h2>
695
- <p class="text-gray-500 mb-6">Unlock database persistence, duplicate detection, and priority AI processing.</p>
696
- <div class="bg-[#18181b] border border-[#27272a] rounded-xl p-6 mb-6">
697
- <div class="flex justify-between items-center mb-4">
 
 
 
 
 
 
698
  <span class="text-sm text-gray-400">Enterprise Plan</span>
699
- <span class="text-3xl font-bold">$49<span class="text-sm text-gray-500">/mo</span></span>
 
 
 
 
 
 
 
 
 
700
  </div>
701
- <ul class="text-sm text-gray-400 space-y-2">
702
- <li class="flex items-center"><span class="text-emerald-400 mr-2">✓</span> Unlimited Document Parsing</li>
703
- <li class="flex items-center"><span class="text-emerald-400 mr-2">✓</span> Cross-Field Math Validation</li>
704
- <li class="flex items-center"><span class="text-emerald-400 mr-2">✓</span> Duplicate Invoice Detection</li>
705
- <li class="flex items-center"><span class="text-emerald-400 mr-2">✓</span> Priority GPU Access</li>
706
- </ul>
707
  </div>
708
- <button onclick="openCheckout()" class="w-full bg-blue-600 hover:bg-blue-700 rounded-lg py-3 font-semibold transition">Subscribe Now</button>
709
- <button onclick="closeModal('upgrade-modal')" class="w-full mt-2 text-sm text-gray-500 hover:text-white">Maybe later</button>
710
  </div>
711
  </div>
712
 
713
  <div id="checkout-modal" class="modal-bg fixed inset-0 z-50 hidden flex items-center justify-center p-4">
714
- <div class="bg-[#111113] border border-[#27272a] rounded-2xl max-w-md w-full p-8">
 
715
  <div id="checkout-form">
716
- <h2 class="text-2xl font-bold mb-6">Secure Checkout</h2>
 
 
 
 
 
 
717
  <div class="space-y-4">
718
  <div>
719
- <label class="block text-xs font-mono text-gray-500 mb-1">CARD NUMBER</label>
720
- <input id="cc-num" type="text" placeholder="4242 4242 4242 4242" class="w-full bg-[#18181b] border border-[#27272a] rounded-lg px-4 py-2.5 text-sm font-mono outline-none focus:border-blue-500">
721
  </div>
722
  <div class="flex gap-4">
723
  <div class="flex-1">
724
- <label class="block text-xs font-mono text-gray-500 mb-1">EXPIRY</label>
725
- <input id="cc-exp" type="text" placeholder="MM/YY" class="w-full bg-[#18181b] border border-[#27272a] rounded-lg px-4 py-2.5 text-sm font-mono outline-none focus:border-blue-500">
726
  </div>
727
  <div class="flex-1">
728
- <label class="block text-xs font-mono text-gray-500 mb-1">CVC</label>
729
- <input id="cc-cvc" type="text" placeholder="123" class="w-full bg-[#18181b] border border-[#27272a] rounded-lg px-4 py-2.5 text-sm font-mono outline-none focus:border-blue-500">
730
  </div>
731
  </div>
732
- <button onclick="processMockPayment()" class="w-full bg-emerald-600 hover:bg-emerald-700 rounded-lg py-3 font-semibold mt-4">Pay $49.00</button>
 
733
  </div>
734
  </div>
735
  <div id="checkout-success" class="hidden text-center py-8">
736
- <div class="w-16 h-16 bg-emerald-500/20 rounded-full flex items-center justify-center mx-auto mb-4">
737
- <svg class="w-8 h-8 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>
738
  </div>
739
  <h2 class="text-2xl font-bold">Payment Successful</h2>
740
  <p class="text-gray-500 mt-2">Your account has been upgraded to Enterprise.</p>
741
- <button onclick="closeCheckout()" class="mt-6 bg-[#18181b] border border-[#27272a] rounded-lg px-6 py-2 text-sm">Close</button>
742
  </div>
743
  </div>
744
  </div>
@@ -831,7 +1055,9 @@ def get_frontend_html() -> str:
831
  const reader = new FileReader();
832
  reader.onload = (e) => {
833
  const b64 = e.target.result;
834
- $('terminal-output').innerHTML = '<span class="text-gray-500">> Processing image...</span>';
 
 
835
  bridge.parse(b64);
836
  };
837
  reader.readAsDataURL(file);
@@ -840,7 +1066,7 @@ def get_frontend_html() -> str:
840
  function sendChat() {
841
  const msg = $('chat-input').value;
842
  if(!msg) return;
843
- $('chat-log').innerHTML += `<div class="bg-blue-600/20 text-blue-300 p-2 rounded-lg ml-4">${msg}</div>`;
844
  $('chat-input').value = '';
845
  bridge.chat(msg);
846
  }
@@ -863,7 +1089,7 @@ def get_frontend_html() -> str:
863
  // Chat Response
864
  let chatRes = $('bridge-chat-output').value;
865
  if(chatRes && chatRes !== "") {
866
- $('chat-log').innerHTML += `<div class="bg-[#18181b] text-gray-300 p-2 rounded-lg mr-4">${chatRes.replace(/\\n/g, '<br>')}</div>`;
867
  $('chat-log').scrollTop = $('chat-log').scrollHeight;
868
  $('bridge-chat-output').value = "";
869
  }
@@ -884,9 +1110,8 @@ def get_frontend_html() -> str:
884
  $('auth-view').classList.add('hidden');
885
  $('dashboard-view').classList.remove('hidden');
886
  $('chat-toggle').classList.remove('hidden');
887
- $('user-badge').textContent = data.email;
888
- $('user-badge').classList.remove('text-gray-400');
889
- $('user-badge').classList.add('text-emerald-400');
890
  } else {
891
  alert(data.message || 'Authentication failed');
892
  }
@@ -894,7 +1119,10 @@ def get_frontend_html() -> str:
894
 
895
  function handleParseResponse(data) {
896
  if(data.error) {
897
- $('terminal-output').innerHTML = `<span class="text-red-500">> Error: ${data.error}</span>`;
 
 
 
898
  return;
899
  }
900
 
@@ -914,24 +1142,42 @@ def get_frontend_html() -> str:
914
  $('stat-acc').textContent = stats.acc + '%';
915
 
916
  // Update Terminal
 
 
 
 
917
  const jsonStr = JSON.stringify(data, null, 2);
918
- $('terminal-output').innerHTML = `<span class="text-gray-500">> Parsing complete. Confidence: ${stats.acc}%</span>\n<span class="text-emerald-400">${jsonStr.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</span>`;
919
 
920
- // Update Compliance
921
- const valColor = comp.cross_field_validation.includes('WARNING') ? 'text-red-400' : 'text-emerald-400';
922
- const dupText = comp.duplicate_detected ? '<span class="text-red-400">Detected</span>' : (comp.registered_user ? '<span class="text-emerald-400">None</span>' : '<span class="text-gray-500">🔒 Requires Registration</span>');
 
 
 
 
 
 
923
 
924
  $('compliance-status').innerHTML = `
925
- <div class="flex justify-between"><span class="text-gray-500">Validation</span><span class="${valColor}">${comp.cross_field_validation}</span></div>
926
- <div class="flex justify-between"><span class="text-gray-500">Duplicates</span>${dupText}</div>
927
- <div class="flex justify-between"><span class="text-gray-500">Review Req</span><span class="${comp.human_review_required ? 'text-yellow-400' : 'text-emerald-400'}">${comp.human_review_required ? 'Yes' : 'No'}</span></div>
 
 
 
 
 
 
 
 
 
928
  `;
929
  }
930
 
931
  function processMockPayment() {
932
  $('checkout-form').classList.add('hidden');
933
  $('checkout-success').classList.remove('hidden');
934
- // In a real app, we'd update the DB via bridge
935
  }
936
  </script>
937
  </body>
@@ -1015,4 +1261,5 @@ def build_app():
1015
 
1016
  if __name__ == "__main__":
1017
  app = build_app()
1018
- app.launch(server_name="0.0.0.0", server_port=7860, show_error=True, css=CUSTOM_CSS)
 
 
1
+ Ten předchozí vzhled vypadal prýdě, protože se Tailwind CSS styly z CDNa občas neaplikovaly včas a chyběly ucelené vizuální struktury. Udělal jsem kompletní redesign frontendového kódu přímo uvnitř `app.py`.
2
+
3
+ Vytvořil jsem **ultra-profesionální, čistý "Enterprise" design** ve stylu moderních SaaS platforem (Vercel / Linear / Stripe). Vše je postaveno na dokonalém odstupu (spacing), luxusních stínech, průhlednostech, skleněných efektech (glassmorphism) a dokonale nastaveném tmavém režimu.
4
+
5
+ Zde je vylepšený `app.py`. Stačí zkopírovat a vložit.
6
+
7
+ ```python
8
  """
9
  OmniParse AI - Enterprise Document Parsing System
10
  Single-file Hugging Face Spaces Application
 
453
  return generate_csv_b64(extracted_json)
454
 
455
  # ─────────────────────────────────────────────────────────────────────────────
456
+ # GRADIO UI & LUXURY ENTERPRISE HTML INJECTION
457
  # ─────────────────────────────────────────────────────────────────────────────
458
 
459
  CUSTOM_CSS = """
460
  :root {
461
+ --bg-primary: #050507;
462
+ --bg-surface: #0A0A0F;
463
+ --bg-elevated: #101015;
464
+ --border-color: #1F1F25;
465
+ --border-hover: #2A2A35;
466
+ --accent-blue: #3B82F6;
467
+ --accent-emerald: #10B981;
468
+ --accent-violet: #8B5CF6;
469
+ --text-primary: #FAFAFA;
470
+ --text-muted: #71717A;
471
  }
472
  .gradio-container {
473
  max-width: 100% !important;
 
480
  background: var(--bg-primary);
481
  color: var(--text-primary);
482
  font-family: 'Inter', sans-serif;
483
+ background-image:
484
+ radial-gradient(circle at 15% 50%, rgba(59, 130, 246, 0.04) 0%, transparent 25%),
485
+ radial-gradient(circle at 85% 30%, rgba(139, 92, 246, 0.04) 0%, transparent 25%);
486
  }
487
  footer { display: none !important; }
488
+ .glass-card {
489
+ background: rgba(16, 16, 21, 0.6);
490
+ backdrop-filter: blur(24px);
491
+ -webkit-backdrop-filter: blur(24px);
492
+ border: 1px solid var(--border-color);
493
+ border-radius: 16px;
494
+ transition: border-color 0.2s ease, transform 0.2s ease;
495
+ }
496
+ .glass-card:hover {
497
+ border-color: var(--border-hover);
498
+ }
499
+ .luxury-input {
500
+ background: var(--bg-surface) !important;
501
+ border: 1px solid var(--border-color) !important;
502
+ border-radius: 10px !important;
503
+ padding: 14px 16px !important;
504
+ color: var(--text-primary) !important;
505
+ font-size: 14px !important;
506
+ transition: all 0.2s ease !important;
507
+ width: 100% !important;
508
+ box-sizing: border-box !important;
509
+ }
510
+ .luxury-input:focus {
511
+ outline: none !important;
512
+ border-color: var(--accent-blue) !important;
513
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15) !important;
514
+ }
515
+ .luxury-btn-primary {
516
+ background: var(--text-primary) !important;
517
+ color: var(--bg-primary) !important;
518
+ font-weight: 600 !important;
519
+ padding: 12px 20px !important;
520
+ border-radius: 10px !important;
521
+ transition: all 0.2s ease !important;
522
+ width: 100% !important;
523
+ border: 1px solid transparent !important;
524
+ }
525
+ .luxury-btn-primary:hover {
526
+ opacity: 0.9 !important;
527
+ transform: translateY(-1px) !important;
528
+ }
529
+ .luxury-btn-secondary {
530
+ background: transparent !important;
531
+ color: var(--text-primary) !important;
532
+ font-weight: 500 !important;
533
+ padding: 12px 20px !important;
534
+ border-radius: 10px !important;
535
+ border: 1px solid var(--border-color) !important;
536
+ transition: all 0.2s ease !important;
537
+ width: 100% !important;
538
+ }
539
+ .luxury-btn-secondary:hover {
540
+ background: var(--bg-elevated) !important;
541
+ border-color: var(--border-hover) !important;
542
+ }
543
+ .luxury-btn-accent {
544
+ background: linear-gradient(180deg, #3B82F6, #2563EB) !important;
545
+ color: white !important;
546
+ font-weight: 600 !important;
547
+ padding: 12px 20px !important;
548
+ border-radius: 10px !important;
549
+ transition: all 0.2s ease !important;
550
+ width: 100% !important;
551
+ border: 1px solid rgba(255,255,255,0.1) !important;
552
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2) !important;
553
+ }
554
+ .luxury-btn-accent:hover {
555
+ transform: translateY(-1px) !important;
556
+ box-shadow: 0 6px 16px rgba(59, 130, 246, 0.3) !important;
557
+ }
558
+ .drop-zone {
559
+ border: 2px dashed var(--border-color) !important;
560
+ background: var(--bg-surface) !important;
561
+ border-radius: 16px !important;
562
+ transition: all 0.3s ease !important;
563
+ cursor: pointer !important;
564
+ }
565
+ .drop-zone:hover {
566
+ border-color: var(--accent-blue) !important;
567
+ background: rgba(59, 130, 246, 0.02) !important;
568
+ }
569
+ .drop-zone-active {
570
+ border-color: var(--accent-emerald) !important;
571
+ background: rgba(16, 185, 129, 0.05) !important;
572
+ transform: scale(1.01);
573
+ }
574
+ .hidden-gradio {
575
+ position: absolute !important;
576
+ left: -9999px !important;
577
+ top: -9999px !important;
578
+ opacity: 0 !important;
579
+ pointer-events: none !important;
580
+ width: 1px !important;
581
+ height: 1px !important;
582
+ overflow: hidden !important;
583
+ }
584
+ .terminal-block {
585
+ background: #000000;
586
+ border: 1px solid var(--border-color);
587
+ border-radius: 12px;
588
+ font-family: 'JetBrains Mono', monospace;
589
+ font-size: 12px;
590
+ color: #A1A1AA;
591
+ }
592
+ .status-badge {
593
+ padding: 4px 10px;
594
+ border-radius: 999px;
595
+ font-size: 11px;
596
+ font-weight: 600;
597
+ text-transform: uppercase;
598
+ letter-spacing: 0.5px;
599
+ }
600
+ .modal-bg {
601
+ background: rgba(0, 0, 0, 0.7);
602
+ backdrop-filter: blur(8px);
603
+ -webkit-backdrop-filter: blur(8px);
604
+ }
605
+ .modal-content {
606
+ background: var(--bg-elevated);
607
+ border: 1px solid var(--border-color);
608
+ border-radius: 24px;
609
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.8);
610
+ }
611
+ .fancy-scroll::-webkit-scrollbar { width: 6px; height: 6px; }
612
+ .fancy-scroll::-webkit-scrollbar-track { background: transparent; }
613
+ .fancy-scroll::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 3px; }
614
+ .fancy-scroll::-webkit-scrollbar-thumb:hover { background: var(--border-hover); }
615
  """
616
 
 
617
  def get_frontend_html() -> str:
618
  return """
619
  <!DOCTYPE html>
 
623
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
624
  <title>OmniParse AI</title>
625
  <script src="https://cdn.tailwindcss.com"></script>
626
+ <link rel="preconnect" href="https://fonts.googleapis.com">
627
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
628
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
629
  </head>
630
+ <body class="bg-[#050507] text-[#FAFAFA] antialiased">
631
 
632
  <div id="omniparse-root">
633
  <!-- Navigation -->
634
+ <nav class="sticky top-0 z-40 border-b border-[#1F1F25] bg-[#050507]/80 backdrop-blur-xl">
635
+ <div class="max-w-7xl mx-auto px-6 lg:px-8">
636
+ <div class="flex items-center justify-between h-16">
637
+ <div class="flex items-center space-x-3">
638
+ <div class="w-9 h-9 bg-gradient-to-br from-blue-500 to-violet-500 rounded-xl flex items-center justify-center shadow-lg shadow-blue-500/20">
639
+ <svg class="w-5 h-5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
640
+ </div>
641
+ <div>
642
+ <span class="text-lg font-bold tracking-tight block">OmniParse <span class="text-blue-400">AI</span></span>
643
+ <span class="text-[10px] text-gray-500 uppercase tracking-widest font-medium">Enterprise Engine</span>
644
+ </div>
645
+ </div>
646
+ <div class="flex items-center space-x-4">
647
+ <span id="user-badge" class="hidden md:flex items-center space-x-2 text-xs px-3 py-1.5 bg-[#101015] border border-[#1F1F25] rounded-full">
648
+ <span class="w-2 h-2 bg-gray-500 rounded-full"></span>
649
+ <span class="text-gray-400">Unauthenticated</span>
650
+ </span>
651
+ <button id="upgrade-btn" class="flex items-center space-x-2 px-4 py-2 bg-gradient-to-r from-blue-600 to-violet-600 hover:from-blue-500 hover:to-violet-500 rounded-full text-xs font-semibold transition-all shadow-lg shadow-blue-500/20">
652
+ <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M5 11l7-7 7 7M12 4v16" /></svg>
653
+ <span>Upgrade</span>
654
+ </button>
655
+ </div>
656
  </div>
657
  </div>
658
  </nav>
659
 
660
+ <main class="max-w-7xl mx-auto px-6 lg:px-8 py-10">
 
661
 
662
  <!-- Auth View -->
663
+ <div id="auth-view" class="min-h-[75vh] flex flex-col items-center justify-center">
664
+ <div class="w-full max-w-md">
665
+ <div class="text-center mb-10">
666
+ <h1 class="text-4xl font-extrabold tracking-tight mb-3">Access the Engine</h1>
667
+ <p class="text-gray-500 max-w-sm mx-auto">Securely parse sensitive financial documents with enterprise-grade AI.</p>
668
+ </div>
669
+
670
+ <div class="glass-card p-8">
671
+ <div class="space-y-5">
672
+ <div>
673
+ <label class="block text-[11px] font-mono text-gray-500 uppercase tracking-wider mb-2">Email Address</label>
674
+ <input id="auth-email" type="email" class="luxury-input" placeholder="admin@enterprise.com">
675
+ </div>
676
+ <div>
677
+ <label class="block text-[11px] font-mono text-gray-500 uppercase tracking-wider mb-2">Password</label>
678
+ <input id="auth-pass" type="password" class="luxury-input" placeholder="••••••••••••">
679
+ </div>
680
+ <div class="flex gap-3 pt-2">
681
+ <button id="login-btn" class="luxury-btn-primary">Log In</button>
682
+ <button id="signup-btn" class="luxury-btn-secondary">Sign Up</button>
683
+ </div>
684
+ <div class="relative py-4">
685
+ <div class="absolute inset-0 flex items-center"><div class="w-full border-t border-[#1F1F25]"></div></div>
686
+ <div class="relative flex justify-center"><span class="bg-[#101015] px-3 text-[10px] text-gray-500 uppercase tracking-widest">Or</span></div>
687
+ </div>
688
+ <button id="guest-btn" class="flex items-center justify-center space-x-2 w-full py-3 border border-[#1F1F25] rounded-xl text-sm font-medium text-gray-400 hover:text-white hover:border-[#2A2A35] hover:bg-[#101015] transition-all">
689
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" /></svg>
690
+ <span>Continue as Guest</span>
691
+ </button>
692
  </div>
 
693
  </div>
694
  </div>
695
  </div>
696
 
697
  <!-- Dashboard View -->
698
  <div id="dashboard-view" class="hidden">
699
+ <div class="mb-10">
700
+ <h2 class="text-3xl font-bold tracking-tight">Dashboard</h2>
701
+ <p class="text-gray-500 mt-1">Monitor document parsing metrics and compliance in real-time.</p>
702
+ </div>
703
+
704
  <!-- Stats Banner -->
705
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
706
+ <div class="glass-card p-5">
707
+ <div class="flex justify-between items-start mb-3">
708
+ <span class="text-[11px] font-mono text-gray-500 uppercase tracking-wider">Processed</span>
709
+ <div class="w-8 h-8 bg-blue-500/10 rounded-lg flex items-center justify-center">
710
+ <svg class="w-4 h-4 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" /></svg>
711
+ </div>
712
+ </div>
713
+ <div id="stat-docs" class="text-3xl font-bold text-white">0</div>
714
+ <div class="text-xs text-gray-500 mt-1">Total documents</div>
715
  </div>
716
+ <div class="glass-card p-5">
717
+ <div class="flex justify-between items-start mb-3">
718
+ <span class="text-[11px] font-mono text-gray-500 uppercase tracking-wider">Fields</span>
719
+ <div class="w-8 h-8 bg-violet-500/10 rounded-lg flex items-center justify-center">
720
+ <svg class="w-4 h-4 text-violet-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" /></svg>
721
+ </div>
722
+ </div>
723
+ <div id="stat-fields" class="text-3xl font-bold text-white">0</div>
724
+ <div class="text-xs text-gray-500 mt-1">Data points extracted</div>
725
  </div>
726
+ <div class="glass-card p-5">
727
+ <div class="flex justify-between items-start mb-3">
728
+ <span class="text-[11px] font-mono text-gray-500 uppercase tracking-wider">Exec Time</span>
729
+ <div class="w-8 h-8 bg-amber-500/10 rounded-lg flex items-center justify-center">
730
+ <svg class="w-4 h-4 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
731
+ </div>
732
+ </div>
733
+ <div id="stat-time" class="text-3xl font-bold text-white">0.00s</div>
734
+ <div class="text-xs text-gray-500 mt-1">Processing speed</div>
735
  </div>
736
+ <div class="glass-card p-5">
737
+ <div class="flex justify-between items-start mb-3">
738
+ <span class="text-[11px] font-mono text-gray-500 uppercase tracking-wider">Accuracy</span>
739
+ <div class="w-8 h-8 bg-emerald-500/10 rounded-lg flex items-center justify-center">
740
+ <svg class="w-4 h-4 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z" /></svg>
741
+ </div>
742
+ </div>
743
+ <div id="stat-acc" class="text-3xl font-bold text-white">0%</div>
744
+ <div class="text-xs text-gray-500 mt-1">AI confidence score</div>
745
  </div>
746
  </div>
747
 
 
749
  <!-- Left Column: Upload & Terminal -->
750
  <div class="lg:col-span-2 space-y-6">
751
  <!-- Upload Zone -->
752
+ <div id="drop-zone" class="drop-zone p-12 text-center">
753
  <input id="file-input" type="file" class="hidden" accept="image/*">
754
+ <div class="flex flex-col items-center">
755
+ <div class="w-16 h-16 bg-[#101015] border border-[#1F1F25] rounded-2xl flex items-center justify-center mb-5">
756
+ <svg class="w-7 h-7 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" /></svg>
757
+ </div>
758
+ <p class="text-base font-semibold text-white mb-1">Drop invoice image here</p>
759
+ <p class="text-sm text-gray-500">PNG, JPG up to 10MB</p>
760
+ </div>
761
  </div>
762
 
763
  <!-- Terminal -->
764
+ <div class="glass-card overflow-hidden">
765
+ <div class="flex items-center justify-between px-5 py-3 border-b border-[#1F1F25]">
766
+ <div class="flex items-center space-x-3">
767
+ <div class="flex space-x-1.5">
768
+ <div class="w-2.5 h-2.5 bg-red-500/80 rounded-full"></div>
769
+ <div class="w-2.5 h-2.5 bg-yellow-500/80 rounded-full"></div>
770
+ <div class="w-2.5 h-2.5 bg-green-500/80 rounded-full"></div>
771
+ </div>
772
+ <span class="text-xs font-mono text-gray-400 ml-2">output_stream.log</span>
773
  </div>
774
+ <span id="terminal-status" class="text-[10px] font-mono text-gray-500 uppercase tracking-widest">Idle</span>
775
  </div>
776
+ <pre id="terminal-output" class="fancy-scroll p-5 text-xs font-mono text-gray-400 h-96 overflow-y-auto whitespace-pre-wrap"><span class="text-gray-600">System ready. Awaiting document input...</span></pre>
777
  </div>
778
  </div>
779
 
780
  <!-- Right Column: Export & Status -->
781
  <div class="space-y-6">
782
  <!-- Status Card -->
783
+ <div class="glass-card p-6">
784
+ <h3 class="text-[11px] font-mono text-gray-500 uppercase tracking-wider mb-5">Compliance Status</h3>
785
+ <div id="compliance-status" class="space-y-4">
786
+ <div class="flex justify-between items-center pb-3 border-b border-[#1F1F25]">
787
+ <span class="text-sm text-gray-400">Validation</span>
788
+ <span class="status-badge bg-gray-500/10 text-gray-500">PENDING</span>
789
+ </div>
790
+ <div class="flex justify-between items-center pb-3 border-b border-[#1F1F25]">
791
+ <span class="text-sm text-gray-400">Duplicates</span>
792
+ <span class="status-badge bg-gray-500/10 text-gray-500">N/A</span>
793
+ </div>
794
+ <div class="flex justify-between items-center">
795
+ <span class="text-sm text-gray-400">Review Required</span>
796
+ <span class="status-badge bg-gray-500/10 text-gray-500">N/A</span>
797
+ </div>
798
  </div>
799
  </div>
800
 
801
  <!-- Export Card -->
802
+ <div class="glass-card p-6">
803
+ <h3 class="text-[11px] font-mono text-gray-500 uppercase tracking-wider mb-5">Data Export</h3>
804
+ <button id="export-btn" class="luxury-btn-secondary flex items-center justify-center space-x-2">
805
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /></svg>
806
+ <span>Download CSV</span>
807
  </button>
808
  <a id="csv-download-link" href="#" download="omniparse_export.csv" class="hidden">dl</a>
809
  </div>
 
814
 
815
  <!-- Chat Companion Widget -->
816
  <div id="chat-widget" class="fixed bottom-6 right-6 z-30 hidden">
817
+ <div class="glass-card flex flex-col" style="width: 360px; height: 480px; box-shadow: 0 20px 50px rgba(0,0,0,0.5);">
818
+ <div class="px-5 py-4 border-b border-[#1F1F25] flex justify-between items-center">
819
+ <h3 class="text-sm font-semibold flex items-center">
820
+ <span class="w-2 h-2 bg-emerald-500 rounded-full mr-2.5 shadow-sm shadow-emerald-500/50"></span>
821
+ AI Audit Companion
822
+ </h3>
823
+ <button id="chat-close" class="text-gray-500 hover:text-white transition">
824
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
825
+ </button>
826
  </div>
827
+ <div id="chat-log" class="fancy-scroll flex-1 p-4 overflow-y-auto space-y-3 text-sm"></div>
828
+ <div class="p-3 border-t border-[#1F1F25]">
829
+ <div class="flex gap-2 items-center bg-[#050507] rounded-xl p-1.5 border border-[#1F1F25]">
830
+ <input id="chat-input" type="text" placeholder="Ask about the data..." class="flex-1 bg-transparent border-none text-sm outline-none placeholder-gray-600 px-2">
831
+ <button id="chat-send" class="bg-blue-600 hover:bg-blue-500 rounded-lg p-2 transition">
832
+ <svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" /></svg>
833
+ </button>
834
+ </div>
835
  </div>
836
  </div>
837
  </div>
838
+ <button id="chat-toggle" class="fixed bottom-6 right-6 z-30 w-14 h-14 bg-gradient-to-br from-blue-600 to-violet-600 hover:from-blue-500 hover:to-violet-500 rounded-full flex items-center justify-center shadow-xl shadow-blue-500/30 transition-all hidden">
839
+ <svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z" /></svg>
840
  </button>
841
 
842
  <!-- Footer -->
843
+ <footer class="border-t border-[#1F1F25] mt-12 py-8">
844
+ <div class="max-w-7xl mx-auto px-6 lg:px-8 flex flex-col md:flex-row justify-between items-center">
845
+ <p class="text-xs text-gray-600 mb-4 md:mb-0">© 2024 OmniParse AI. Enterprise Edition.</p>
846
+ <div class="flex gap-6">
847
+ <button onclick="openModal('terms-modal')" class="text-xs text-gray-500 hover:text-white transition">Terms of Service</button>
848
+ <button onclick="openModal('privacy-modal')" class="text-xs text-gray-500 hover:text-white transition">Privacy Policy</button>
849
+ <button onclick="openModal('gdpr-modal')" class="text-xs text-gray-500 hover:text-white transition">GDPR Compliance</button>
850
  </div>
851
  </div>
852
  </footer>
 
854
 
855
  <!-- Modals -->
856
  <div id="terms-modal" class="modal-bg fixed inset-0 z-50 hidden flex items-center justify-center p-4">
857
+ <div class="modal-content max-w-2xl w-full p-8 max-h-[80vh] overflow-y-auto fancy-scroll">
858
+ <div class="flex justify-between items-start mb-6">
859
+ <h2 class="text-2xl font-bold">Terms of Service</h2>
860
+ <button onclick="closeModal('terms-modal')" class="text-gray-500 hover:text-white"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg></button>
861
+ </div>
862
+ <div class="text-sm text-gray-400 space-y-4 leading-relaxed">
863
  <p>1. Acceptance of Terms: By accessing OmniParse AI, you agree to be bound by these terms. AI data processing carries inherent risks, and outputs should be verified by a human operator.</p>
864
  <p>2. Enterprise Liability: OmniParse AI is not liable for misclassified financial data. The system provides high-confidence extractions but does not replace formal accounting audits.</p>
865
  <p>3. Data Retention: Parsed documents are retained for a maximum of 30 days for caching purposes unless explicitly deleted by the user or enterprise admin.</p>
866
  </div>
 
867
  </div>
868
  </div>
869
 
870
  <div id="privacy-modal" class="modal-bg fixed inset-0 z-50 hidden flex items-center justify-center p-4">
871
+ <div class="modal-content max-w-2xl w-full p-8 max-h-[80vh] overflow-y-auto fancy-scroll">
872
+ <div class="flex justify-between items-start mb-6">
873
+ <h2 class="text-2xl font-bold">Privacy Policy</h2>
874
+ <button onclick="closeModal('privacy-modal')" class="text-gray-500 hover:text-white"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg></button>
875
+ </div>
876
+ <div class="text-sm text-gray-400 space-y-4 leading-relaxed">
877
  <p>1. Data Collection: We collect minimal authentication data (email, hashed password). Document images are processed in memory and not permanently stored unless explicitly saved by the user.</p>
878
  <p>2. AI Processing: Images are processed via our internal neural networks. We do not share your documents with third-party APIs.</p>
879
  <p>3. Security: Passwords are hashed using PBKDF2 with SHA-256 and unique cryptographic salts. Database access is strictly isolated.</p>
880
  </div>
 
881
  </div>
882
  </div>
883
 
884
  <div id="gdpr-modal" class="modal-bg fixed inset-0 z-50 hidden flex items-center justify-center p-4">
885
+ <div class="modal-content max-w-2xl w-full p-8 max-h-[80vh] overflow-y-auto fancy-scroll">
886
+ <div class="flex justify-between items-start mb-6">
887
+ <h2 class="text-2xl font-bold">GDPR Compliance</h2>
888
+ <button onclick="closeModal('gdpr-modal')" class="text-gray-500 hover:text-white"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg></button>
889
+ </div>
890
+ <div class="text-sm text-gray-400 space-y-4 leading-relaxed">
891
  <p>1. Right to Erasure: You may request complete deletion of your account and associated parsed invoices at any time. Execution occurs within 72 hours.</p>
892
  <p>2. Data Portability: Users can export all extracted data in CSV format compliant with machine-readable portability standards.</p>
893
  <p>3. Automated Decision Making: Our AI parsing does not make automated legal decisions. All outputs are advisory and require human review.</p>
894
  </div>
 
895
  </div>
896
  </div>
897
 
898
  <!-- Upgrade & Checkout Modals -->
899
  <div id="upgrade-modal" class="modal-bg fixed inset-0 z-50 hidden flex items-center justify-center p-4">
900
+ <div class="modal-content max-w-md w-full p-8 relative overflow-hidden">
901
+ <div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-blue-600 to-violet-600"></div>
902
+ <div class="flex justify-between items-start mb-6">
903
+ <div>
904
+ <h2 class="text-2xl font-bold">Upgrade to Enterprise</h2>
905
+ <p class="text-gray-500 mt-1 text-sm">Unlock the full potential of OmniParse AI.</p>
906
+ </div>
907
+ <button onclick="closeModal('upgrade-modal')" class="text-gray-500 hover:text-white"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg></button>
908
+ </div>
909
+ <div class="bg-[#050507] border border-[#1F1F25] rounded-2xl p-6 mb-6">
910
+ <div class="flex justify-between items-baseline mb-1">
911
  <span class="text-sm text-gray-400">Enterprise Plan</span>
912
+ <div>
913
+ <span class="text-4xl font-extrabold tracking-tight">$49</span>
914
+ <span class="text-sm text-gray-500">/mo</span>
915
+ </div>
916
+ </div>
917
+ <div class="mt-5 pt-5 border-t border-[#1F1F25] space-y-3 text-sm text-gray-400">
918
+ <div class="flex items-center"><svg class="w-4 h-4 text-emerald-400 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>Unlimited Document Parsing</div>
919
+ <div class="flex items-center"><svg class="w-4 h-4 text-emerald-400 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>Cross-Field Math Validation</div>
920
+ <div class="flex items-center"><svg class="w-4 h-4 text-emerald-400 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>Duplicate Invoice Detection</div>
921
+ <div class="flex items-center"><svg class="w-4 h-4 text-emerald-400 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>Priority GPU Access</div>
922
  </div>
 
 
 
 
 
 
923
  </div>
924
+ <button onclick="openCheckout()" class="luxury-btn-accent">Subscribe Now</button>
925
+ <button onclick="closeModal('upgrade-modal')" class="w-full mt-3 text-xs text-gray-500 hover:text-white py-2">Maybe later</button>
926
  </div>
927
  </div>
928
 
929
  <div id="checkout-modal" class="modal-bg fixed inset-0 z-50 hidden flex items-center justify-center p-4">
930
+ <div class="modal-content max-w-md w-full p-8 relative overflow-hidden">
931
+ <div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-emerald-600 to-blue-600"></div>
932
  <div id="checkout-form">
933
+ <div class="flex justify-between items-start mb-6">
934
+ <div>
935
+ <h2 class="text-2xl font-bold">Secure Checkout</h2>
936
+ <p class="text-gray-500 mt-1 text-sm">Complete your Enterprise subscription.</p>
937
+ </div>
938
+ <button onclick="closeCheckout()" class="text-gray-500 hover:text-white"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg></button>
939
+ </div>
940
  <div class="space-y-4">
941
  <div>
942
+ <label class="block text-[11px] font-mono text-gray-500 uppercase tracking-wider mb-2">Card Number</label>
943
+ <input id="cc-num" type="text" placeholder="4242 4242 4242 4242" class="luxury-input font-mono">
944
  </div>
945
  <div class="flex gap-4">
946
  <div class="flex-1">
947
+ <label class="block text-[11px] font-mono text-gray-500 uppercase tracking-wider mb-2">Expiry</label>
948
+ <input id="cc-exp" type="text" placeholder="MM/YY" class="luxury-input font-mono">
949
  </div>
950
  <div class="flex-1">
951
+ <label class="block text-[11px] font-mono text-gray-500 uppercase tracking-wider mb-2">CVC</label>
952
+ <input id="cc-cvc" type="text" placeholder="123" class="luxury-input font-mono">
953
  </div>
954
  </div>
955
+ <button onclick="processMockPayment()" class="luxury-btn-accent mt-2">Pay $49.00</button>
956
+ <p class="text-center text-xs text-gray-600 mt-2">🔒 Payments secured by MockStripe</p>
957
  </div>
958
  </div>
959
  <div id="checkout-success" class="hidden text-center py-8">
960
+ <div class="w-20 h-20 bg-emerald-500/10 rounded-full flex items-center justify-center mx-auto mb-5">
961
+ <svg class="w-10 h-10 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>
962
  </div>
963
  <h2 class="text-2xl font-bold">Payment Successful</h2>
964
  <p class="text-gray-500 mt-2">Your account has been upgraded to Enterprise.</p>
965
+ <button onclick="closeCheckout()" class="luxury-btn-secondary mt-6">Close</button>
966
  </div>
967
  </div>
968
  </div>
 
1055
  const reader = new FileReader();
1056
  reader.onload = (e) => {
1057
  const b64 = e.target.result;
1058
+ $('terminal-output').innerHTML = '<span class="text-amber-400">> Initializing neural parser...</span>\n<span class="text-gray-600">> Processing image data...</span>';
1059
+ $('terminal-status').innerText = 'Processing';
1060
+ $('terminal-status').classList.add('text-amber-400');
1061
  bridge.parse(b64);
1062
  };
1063
  reader.readAsDataURL(file);
 
1066
  function sendChat() {
1067
  const msg = $('chat-input').value;
1068
  if(!msg) return;
1069
+ $('chat-log').innerHTML += `<div class="flex justify-end"><div class="bg-blue-600/20 border border-blue-600/30 text-blue-300 p-2.5 rounded-xl rounded-br-sm max-w-[80%]">${msg}</div></div>`;
1070
  $('chat-input').value = '';
1071
  bridge.chat(msg);
1072
  }
 
1089
  // Chat Response
1090
  let chatRes = $('bridge-chat-output').value;
1091
  if(chatRes && chatRes !== "") {
1092
+ $('chat-log').innerHTML += `<div class="flex justify-start"><div class="bg-[#101015] border border-[#1F1F25] text-gray-300 p-2.5 rounded-xl rounded-bl-sm max-w-[80%]">${chatRes.replace(/\\n/g, '<br>')}</div></div>`;
1093
  $('chat-log').scrollTop = $('chat-log').scrollHeight;
1094
  $('bridge-chat-output').value = "";
1095
  }
 
1110
  $('auth-view').classList.add('hidden');
1111
  $('dashboard-view').classList.remove('hidden');
1112
  $('chat-toggle').classList.remove('hidden');
1113
+
1114
+ $('user-badge').innerHTML = `<span class="w-2 h-2 bg-emerald-500 rounded-full shadow-sm shadow-emerald-500/50"></span><span class="text-emerald-400">${data.email}</span>`;
 
1115
  } else {
1116
  alert(data.message || 'Authentication failed');
1117
  }
 
1119
 
1120
  function handleParseResponse(data) {
1121
  if(data.error) {
1122
+ $('terminal-output').innerHTML = `<span class="text-red-400">> Error: ${data.error}</span>`;
1123
+ $('terminal-status').innerText = 'Error';
1124
+ $('terminal-status').classList.remove('text-amber-400');
1125
+ $('terminal-status').classList.add('text-red-400');
1126
  return;
1127
  }
1128
 
 
1142
  $('stat-acc').textContent = stats.acc + '%';
1143
 
1144
  // Update Terminal
1145
+ $('terminal-status').innerText = 'Complete';
1146
+ $('terminal-status').classList.remove('text-amber-400');
1147
+ $('terminal-status').classList.add('text-emerald-400');
1148
+
1149
  const jsonStr = JSON.stringify(data, null, 2);
1150
+ $('terminal-output').innerHTML = `<span class="text-emerald-400">> Parsing complete. Confidence: ${stats.acc}%</span>\n<span class="text-gray-500">> Extracted Data Structure:</span>\n<span class="text-gray-300">${jsonStr.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</span>`;
1151
 
1152
+ // Update Compliance Badges
1153
+ const valBg = comp.cross_field_validation.includes('WARNING') ? 'bg-red-500/10 text-red-400' : 'bg-emerald-500/10 text-emerald-400';
1154
+ const valText = comp.cross_field_validation.includes('WARNING') ? 'WARNING' : 'PASSED';
1155
+
1156
+ const dupBg = comp.duplicate_detected ? 'bg-red-500/10 text-red-400' : (comp.registered_user ? 'bg-emerald-500/10 text-emerald-400' : 'bg-gray-500/10 text-gray-500');
1157
+ const dupText = comp.duplicate_detected ? 'DETECTED' : (comp.registered_user ? 'CLEAN' : 'LOCKED');
1158
+
1159
+ const revBg = comp.human_review_required ? 'bg-amber-500/10 text-amber-400' : 'bg-emerald-500/10 text-emerald-400';
1160
+ const revText = comp.human_review_required ? 'YES' : 'NO';
1161
 
1162
  $('compliance-status').innerHTML = `
1163
+ <div class="flex justify-between items-center pb-3 border-b border-[#1F1F25]">
1164
+ <span class="text-sm text-gray-400">Validation</span>
1165
+ <span class="status-badge ${valBg}">${valText}</span>
1166
+ </div>
1167
+ <div class="flex justify-between items-center pb-3 border-b border-[#1F1F25]">
1168
+ <span class="text-sm text-gray-400">Duplicates</span>
1169
+ <span class="status-badge ${dupBg}">${dupText}</span>
1170
+ </div>
1171
+ <div class="flex justify-between items-center">
1172
+ <span class="text-sm text-gray-400">Review Required</span>
1173
+ <span class="status-badge ${revBg}">${revText}</span>
1174
+ </div>
1175
  `;
1176
  }
1177
 
1178
  function processMockPayment() {
1179
  $('checkout-form').classList.add('hidden');
1180
  $('checkout-success').classList.remove('hidden');
 
1181
  }
1182
  </script>
1183
  </body>
 
1261
 
1262
  if __name__ == "__main__":
1263
  app = build_app()
1264
+ app.launch(server_name="0.0.0.0", server_port=7860, show_error=True, css=CUSTOM_CSS)
1265
+ ```