Spaces:
Runtime error
Runtime error
Update app.py
Browse files
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 &
|
| 450 |
# ─────────────────────────────────────────────────────────────────────────────
|
| 451 |
|
| 452 |
CUSTOM_CSS = """
|
| 453 |
:root {
|
| 454 |
-
--bg-primary: #
|
| 455 |
-
--bg-surface: #
|
| 456 |
-
--bg-elevated: #
|
| 457 |
-
--border-color: #
|
| 458 |
-
--
|
| 459 |
-
--accent-
|
| 460 |
-
--
|
| 461 |
-
--
|
|
|
|
|
|
|
| 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-[#
|
| 506 |
|
| 507 |
<div id="omniparse-root">
|
| 508 |
<!-- Navigation -->
|
| 509 |
-
<nav class="border-b border-[#
|
| 510 |
-
<div class="max-w-7xl mx-auto px-6
|
| 511 |
-
<div class="flex items-center
|
| 512 |
-
<div class="
|
| 513 |
-
|
| 514 |
-
|
| 515 |
-
|
| 516 |
-
|
| 517 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 518 |
</div>
|
| 519 |
</div>
|
| 520 |
</nav>
|
| 521 |
|
| 522 |
-
<
|
| 523 |
-
<main class="max-w-7xl mx-auto px-6 py-8">
|
| 524 |
|
| 525 |
<!-- Auth View -->
|
| 526 |
-
<div id="auth-view" class="min-h-[
|
| 527 |
-
<div class="w-full max-w-md
|
| 528 |
-
<
|
| 529 |
-
|
| 530 |
-
<
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
|
| 534 |
-
<div>
|
| 535 |
-
<
|
| 536 |
-
|
| 537 |
-
|
| 538 |
-
|
| 539 |
-
<
|
| 540 |
-
|
| 541 |
-
|
| 542 |
-
|
| 543 |
-
<div class="
|
| 544 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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="
|
| 556 |
-
<div class="
|
| 557 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 558 |
</div>
|
| 559 |
-
<div class="
|
| 560 |
-
<div class="
|
| 561 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 562 |
</div>
|
| 563 |
-
<div class="
|
| 564 |
-
<div class="
|
| 565 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 566 |
</div>
|
| 567 |
-
<div class="
|
| 568 |
-
<div class="
|
| 569 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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="
|
| 578 |
<input id="file-input" type="file" class="hidden" accept="image/*">
|
| 579 |
-
<
|
| 580 |
-
|
| 581 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 582 |
</div>
|
| 583 |
|
| 584 |
<!-- Terminal -->
|
| 585 |
-
<div class="
|
| 586 |
-
<div class="flex items-center justify-between px-
|
| 587 |
-
<div class="flex items-center space-x-
|
| 588 |
-
<div class="
|
| 589 |
-
|
| 590 |
-
|
|
|
|
|
|
|
|
|
|
| 591 |
</div>
|
| 592 |
-
<span class="text-
|
| 593 |
</div>
|
| 594 |
-
<pre id="terminal-output" class="p-
|
| 595 |
</div>
|
| 596 |
</div>
|
| 597 |
|
| 598 |
<!-- Right Column: Export & Status -->
|
| 599 |
<div class="space-y-6">
|
| 600 |
<!-- Status Card -->
|
| 601 |
-
<div class="
|
| 602 |
-
<h3 class="text-
|
| 603 |
-
<div id="compliance-status" class="space-y-
|
| 604 |
-
<div class="flex justify-between
|
| 605 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 606 |
</div>
|
| 607 |
</div>
|
| 608 |
|
| 609 |
<!-- Export Card -->
|
| 610 |
-
<div class="
|
| 611 |
-
<h3 class="text-
|
| 612 |
-
<button id="export-btn" class="
|
| 613 |
-
<svg class="w-4 h-4
|
| 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="
|
| 626 |
-
<div class="px-
|
| 627 |
-
<h3 class="text-sm font-
|
| 628 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 629 |
</div>
|
| 630 |
-
<div id="chat-log" class="flex-1 p-
|
| 631 |
-
<div class="p-3 border-t border-[#
|
| 632 |
-
<
|
| 633 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 634 |
</div>
|
| 635 |
</div>
|
| 636 |
</div>
|
| 637 |
-
<button id="chat-toggle" class="fixed bottom-6 right-6 z-30 bg-blue-600 hover:
|
| 638 |
-
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round"
|
| 639 |
</button>
|
| 640 |
|
| 641 |
<!-- Footer -->
|
| 642 |
-
<footer class="border-t border-[#
|
| 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.
|
| 645 |
-
<div class="flex gap-
|
| 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="
|
| 657 |
-
<
|
| 658 |
-
|
|
|
|
|
|
|
|
|
|
| 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="
|
| 669 |
-
<
|
| 670 |
-
|
|
|
|
|
|
|
|
|
|
| 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="
|
| 681 |
-
<
|
| 682 |
-
|
|
|
|
|
|
|
|
|
|
| 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="
|
| 694 |
-
<
|
| 695 |
-
<
|
| 696 |
-
|
| 697 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 698 |
<span class="text-sm text-gray-400">Enterprise Plan</span>
|
| 699 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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="
|
| 709 |
-
<button onclick="closeModal('upgrade-modal')" class="w-full mt-
|
| 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="
|
|
|
|
| 715 |
<div id="checkout-form">
|
| 716 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 717 |
<div class="space-y-4">
|
| 718 |
<div>
|
| 719 |
-
<label class="block text-
|
| 720 |
-
<input id="cc-num" type="text" placeholder="4242 4242 4242 4242" class="
|
| 721 |
</div>
|
| 722 |
<div class="flex gap-4">
|
| 723 |
<div class="flex-1">
|
| 724 |
-
<label class="block text-
|
| 725 |
-
<input id="cc-exp" type="text" placeholder="MM/YY" class="
|
| 726 |
</div>
|
| 727 |
<div class="flex-1">
|
| 728 |
-
<label class="block text-
|
| 729 |
-
<input id="cc-cvc" type="text" placeholder="123" class="
|
| 730 |
</div>
|
| 731 |
</div>
|
| 732 |
-
<button onclick="processMockPayment()" class="
|
|
|
|
| 733 |
</div>
|
| 734 |
</div>
|
| 735 |
<div id="checkout-success" class="hidden text-center py-8">
|
| 736 |
-
<div class="w-
|
| 737 |
-
<svg class="w-
|
| 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="
|
| 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-
|
|
|
|
|
|
|
| 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-
|
| 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-[#
|
| 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 |
-
|
| 888 |
-
$('user-badge').
|
| 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-
|
|
|
|
|
|
|
|
|
|
| 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-
|
| 919 |
|
| 920 |
-
// Update Compliance
|
| 921 |
-
const
|
| 922 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 923 |
|
| 924 |
$('compliance-status').innerHTML = `
|
| 925 |
-
<div class="flex justify-between
|
| 926 |
-
|
| 927 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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, '<').replace(/>/g, '>')}</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 |
+
```
|