seawolf2357 commited on
Commit
b3ca1d5
Β·
verified Β·
1 Parent(s): 040bfa1

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +728 -0
index.html CHANGED
@@ -724,6 +724,734 @@ dropMappings.forEach(({ta,fs})=>{
724
  });
725
  });
726
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
727
  </script>
728
  </body>
729
  </html>
 
724
  });
725
  });
726
 
727
+ </script>
728
+ </body>
729
+ </html><!DOCTYPE html>
730
+ <html lang="ko">
731
+ <head>
732
+ <meta charset="UTF-8">
733
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
734
+ <title> TeXray </title>
735
+ <link href="https://fonts.googleapis.com/css2?family=Sora:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&family=Noto+Sans+KR:wght@300;400;500;700;900&display=swap" rel="stylesheet">
736
+ <style>
737
+ /* ═══════════════════════════════════════════════════════════════
738
+ TeXray β€” Premium Dark Glassmorphism
739
+ ═══════════════════════════════════════════════════════════════ */
740
+ *{margin:0;padding:0;box-sizing:border-box;}
741
+ :root{
742
+ --bg-deep:#03030a;
743
+ --bg:#07071a;
744
+ --surface:rgba(14,14,38,0.65);
745
+ --surface-solid:#0c0c24;
746
+ --glass:rgba(18,18,50,0.45);
747
+ --glass2:rgba(24,24,60,0.55);
748
+ --glass-border:rgba(80,80,160,0.12);
749
+ --glass-border-hover:rgba(120,120,200,0.22);
750
+ --card-glow:rgba(100,80,220,0.06);
751
+ --text:#e8e8f4;
752
+ --text-dim:#9898c0;
753
+ --text-muted:#5a5a88;
754
+ --accent:#8b7aff;
755
+ --accent-bright:#a594ff;
756
+ --accent-glow:rgba(139,122,255,0.15);
757
+ --accent-glow2:rgba(139,122,255,0.08);
758
+ --teal:#3dd6c8;
759
+ --teal-glow:rgba(61,214,200,0.12);
760
+ --rose:#ff6b8a;
761
+ --rose-glow:rgba(255,107,138,0.12);
762
+ --amber:#ffb347;
763
+ --amber-glow:rgba(255,179,71,0.12);
764
+ --sky:#54b5ff;
765
+ --radius:16px;
766
+ --radius-sm:10px;
767
+ --radius-xs:6px;
768
+ --font-display:'Sora','Noto Sans KR',sans-serif;
769
+ --font-body:'Noto Sans KR','Sora',sans-serif;
770
+ --font-mono:'JetBrains Mono',monospace;
771
+ --transition:0.3s cubic-bezier(0.4,0,0.2,1);
772
+ }
773
+ html{scroll-behavior:smooth;}
774
+ body{
775
+ font-family:var(--font-body);
776
+ background:var(--bg-deep);
777
+ color:var(--text);
778
+ min-height:100vh;
779
+ overflow-x:hidden;
780
+ -webkit-font-smoothing:antialiased;
781
+ }
782
+ ::-webkit-scrollbar{width:6px;}
783
+ ::-webkit-scrollbar-track{background:transparent;}
784
+ ::-webkit-scrollbar-thumb{background:rgba(139,122,255,0.25);border-radius:10px;}
785
+ ::-webkit-scrollbar-thumb:hover{background:rgba(139,122,255,0.4);}
786
+ ::selection{background:rgba(139,122,255,0.3);color:#fff;}
787
+
788
+ /* ═══ CANVAS BACKGROUND ═══ */
789
+ #mesh-canvas{position:fixed;top:0;left:0;width:100%;height:100%;z-index:0;pointer-events:none;opacity:0.5;}
790
+ .noise-overlay{
791
+ position:fixed;top:0;left:0;width:100%;height:100%;z-index:1;pointer-events:none;opacity:0.025;
792
+ background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
793
+ background-size:128px;
794
+ }
795
+ .vignette{
796
+ position:fixed;top:0;left:0;width:100%;height:100%;z-index:1;pointer-events:none;
797
+ background:radial-gradient(ellipse 70% 60% at 50% 50%,transparent 0%,var(--bg-deep) 100%);
798
+ }
799
+
800
+ /* ═══ LAYOUT ═══ */
801
+ .app-wrapper{
802
+ position:relative;z-index:2;
803
+ display:flex;flex-direction:column;
804
+ min-height:100vh;
805
+ max-width:900px;
806
+ margin:0 auto;
807
+ padding:32px 24px 48px;
808
+ }
809
+
810
+ /* ═══ HEADER ═══ */
811
+ .header{text-align:center;padding:48px 0 36px;animation:headerIn 1s ease-out;}
812
+ @keyframes headerIn{from{opacity:0;transform:translateY(-24px);}to{opacity:1;transform:translateY(0);}}
813
+ .header-eyebrow{
814
+ font-family:var(--font-mono);font-size:11px;font-weight:500;
815
+ letter-spacing:5px;text-transform:uppercase;
816
+ color:var(--accent);margin-bottom:12px;opacity:0.7;
817
+ }
818
+ .header-title{
819
+ font-family:var(--font-display);font-size:52px;font-weight:800;
820
+ line-height:1.15;letter-spacing:-1.5px;margin-bottom:14px;
821
+ position:relative;display:inline-block;
822
+ }
823
+ .header-title .gradient-text{
824
+ background:linear-gradient(135deg,#a594ff 0%,#54b5ff 35%,#3dd6c8 65%,#a594ff 100%);
825
+ background-size:200% 200%;
826
+ -webkit-background-clip:text;-webkit-text-fill-color:transparent;
827
+ animation:shimmer 6s ease-in-out infinite;
828
+ }
829
+ @keyframes shimmer{0%,100%{background-position:0% 50%;}50%{background-position:100% 50%;}}
830
+ .header-subtitle{
831
+ font-size:14.5px;color:var(--text-dim);line-height:1.7;letter-spacing:0.2px;
832
+ max-width:520px;margin:0 auto 20px;
833
+ }
834
+ .header-subtitle b{color:var(--text);font-weight:600;}
835
+ .pill-row{display:flex;gap:6px;justify-content:center;flex-wrap:wrap;animation:fadeUp 1s 0.3s ease-out both;}
836
+ @keyframes fadeUp{from{opacity:0;transform:translateY(10px);}to{opacity:1;transform:translateY(0);}}
837
+ .pill{
838
+ padding:5px 14px;background:var(--glass);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
839
+ border:1px solid var(--glass-border);border-radius:24px;
840
+ font-family:var(--font-mono);font-size:10px;font-weight:500;color:var(--text-muted);
841
+ transition:var(--transition);
842
+ }
843
+ .pill:hover{border-color:var(--glass-border-hover);color:var(--text-dim);}
844
+
845
+ /* ═══ NAV TABS ═══ */
846
+ .nav{
847
+ display:flex;gap:4px;
848
+ background:var(--glass);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
849
+ border:1px solid var(--glass-border);border-radius:14px;padding:4px;
850
+ margin-bottom:28px;position:sticky;top:12px;z-index:100;
851
+ animation:fadeUp 1s 0.4s ease-out both;
852
+ }
853
+ .nav-item{
854
+ flex:1;display:flex;align-items:center;justify-content:center;gap:6px;
855
+ padding:13px 8px;border-radius:var(--radius-sm);cursor:pointer;
856
+ font-size:13px;font-weight:600;color:var(--text-muted);
857
+ transition:var(--transition);position:relative;user-select:none;
858
+ }
859
+ .nav-item:hover{color:var(--text-dim);background:rgba(139,122,255,0.06);}
860
+ .nav-item.active{
861
+ color:#fff;
862
+ background:linear-gradient(135deg,rgba(139,122,255,0.9),rgba(100,80,220,0.95));
863
+ box-shadow:0 4px 20px rgba(139,122,255,0.3),inset 0 1px 0 rgba(255,255,255,0.12);
864
+ }
865
+ .nav-icon{font-size:15px;}
866
+
867
+ /* ═══ PANELS ═══ */
868
+ .panel{display:none;animation:panelReveal 0.45s ease-out;}
869
+ .panel.active{display:block;}
870
+ @keyframes panelReveal{from{opacity:0;transform:translateY(16px) scale(0.995);}to{opacity:1;transform:translateY(0) scale(1);}}
871
+
872
+ /* ═══ GLASS CARD ═══ */
873
+ .card{
874
+ background:var(--glass);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
875
+ border:1px solid var(--glass-border);border-radius:var(--radius);
876
+ padding:28px;margin-bottom:20px;position:relative;overflow:hidden;
877
+ transition:border-color 0.4s,box-shadow 0.4s;
878
+ }
879
+ .card::before{
880
+ content:'';position:absolute;top:0;left:0;right:0;height:1px;
881
+ background:linear-gradient(90deg,transparent,rgba(139,122,255,0.2),transparent);
882
+ opacity:0;transition:opacity 0.4s;
883
+ }
884
+ .card:hover{border-color:var(--glass-border-hover);box-shadow:0 8px 40px var(--card-glow);}
885
+ .card:hover::before{opacity:1;}
886
+ .card-header{display:flex;align-items:center;gap:12px;margin-bottom:6px;}
887
+ .card-icon{
888
+ width:38px;height:38px;border-radius:var(--radius-sm);
889
+ display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0;
890
+ }
891
+ .card-icon.purple{background:var(--accent-glow);border:1px solid rgba(139,122,255,0.2);}
892
+ .card-icon.teal{background:var(--teal-glow);border:1px solid rgba(61,214,200,0.2);}
893
+ .card-icon.rose{background:var(--rose-glow);border:1px solid rgba(255,107,138,0.2);}
894
+ .card-icon.amber{background:var(--amber-glow);border:1px solid rgba(255,179,71,0.2);}
895
+ .card-title{font-family:var(--font-display);font-size:17px;font-weight:700;letter-spacing:-0.3px;}
896
+ .card-desc{
897
+ font-size:12.5px;color:var(--text-muted);line-height:1.65;
898
+ margin-bottom:18px;padding-left:50px;
899
+ }
900
+
901
+ /* ═══ TEXTAREA ═══ */
902
+ .input-group{margin-bottom:18px;}
903
+ .textarea-wrap{position:relative;}
904
+ textarea{
905
+ width:100%;min-height:200px;padding:18px 20px 32px;
906
+ background:rgba(8,8,28,0.6);border:1px solid var(--glass-border);
907
+ border-radius:var(--radius-sm);color:var(--text);
908
+ font-family:var(--font-body);font-size:14px;line-height:1.8;
909
+ resize:vertical;outline:none;
910
+ transition:border-color 0.35s,box-shadow 0.35s;
911
+ }
912
+ textarea:focus{
913
+ border-color:rgba(139,122,255,0.4);
914
+ box-shadow:0 0 0 3px var(--accent-glow),0 4px 20px rgba(0,0,0,0.3);
915
+ }
916
+ textarea::placeholder{color:var(--text-muted);font-size:13px;}
917
+ .char-counter{
918
+ position:absolute;bottom:10px;right:16px;
919
+ font-family:var(--font-mono);font-size:10px;color:var(--text-muted);
920
+ background:rgba(8,8,28,0.7);padding:2px 8px;border-radius:4px;pointer-events:none;
921
+ }
922
+
923
+ /* ═══ BUTTONS ═══ */
924
+ .btn-group{display:flex;gap:8px;flex-wrap:wrap;align-items:center;}
925
+ .btn{
926
+ padding:11px 22px;border:none;border-radius:var(--radius-sm);cursor:pointer;
927
+ font-family:var(--font-body);font-weight:600;font-size:13px;
928
+ transition:all 0.25s cubic-bezier(0.4,0,0.2,1);
929
+ position:relative;overflow:hidden;letter-spacing:0.2px;
930
+ }
931
+ .btn-primary{
932
+ background:linear-gradient(135deg,var(--accent),#6854e0);color:#fff;
933
+ box-shadow:0 4px 20px rgba(139,122,255,0.25),inset 0 1px 0 rgba(255,255,255,0.1);
934
+ }
935
+ .btn-primary:hover{transform:translateY(-2px);box-shadow:0 8px 32px rgba(139,122,255,0.35),inset 0 1px 0 rgba(255,255,255,0.15);}
936
+ .btn-primary:active{transform:translateY(0);box-shadow:0 2px 12px rgba(139,122,255,0.2);}
937
+ .btn-primary:disabled{opacity:0.45;cursor:not-allowed;transform:none!important;}
938
+ .btn-primary::after{
939
+ content:'';position:absolute;inset:0;
940
+ background:radial-gradient(circle at var(--x,50%) var(--y,50%),rgba(255,255,255,0.25) 0%,transparent 60%);
941
+ opacity:0;transition:opacity 0.5s;
942
+ }
943
+ .btn-primary:active::after{opacity:1;transition:none;}
944
+ .btn-ghost{
945
+ padding:9px 16px;background:rgba(255,255,255,0.03);color:var(--text-muted);
946
+ border:1px solid var(--glass-border);font-size:12px;border-radius:var(--radius-xs);
947
+ }
948
+ .btn-ghost:hover{background:rgba(139,122,255,0.08);color:var(--text-dim);border-color:var(--glass-border-hover);}
949
+
950
+ /* ═══ LOADING ═══ */
951
+ .loader{display:none;text-align:center;padding:48px 24px;}
952
+ .loader.active{display:block;}
953
+ .loader-ring{
954
+ width:44px;height:44px;
955
+ border:3px solid rgba(139,122,255,0.1);border-top-color:var(--accent);
956
+ border-radius:50%;animation:ring-spin 0.9s linear infinite;
957
+ margin:0 auto 16px;position:relative;
958
+ }
959
+ .loader-ring::after{
960
+ content:'';position:absolute;inset:4px;
961
+ border:2px solid transparent;border-top-color:var(--teal);
962
+ border-radius:50%;animation:ring-spin 1.4s linear infinite reverse;
963
+ }
964
+ @keyframes ring-spin{to{transform:rotate(360deg);}}
965
+ .loader-text{font-size:13px;color:var(--text-dim);animation:breathe 2s ease-in-out infinite;}
966
+ .loader-dots{display:inline-flex;gap:4px;margin-top:8px;}
967
+ .loader-dots span{
968
+ width:4px;height:4px;border-radius:50%;background:var(--accent);opacity:0.3;
969
+ animation:dotPulse 1.2s ease-in-out infinite;
970
+ }
971
+ .loader-dots span:nth-child(2){animation-delay:0.15s;}
972
+ .loader-dots span:nth-child(3){animation-delay:0.3s;}
973
+ @keyframes dotPulse{0%,100%{opacity:0.2;transform:scale(0.8);}50%{opacity:1;transform:scale(1.2);}}
974
+ @keyframes breathe{0%,100%{opacity:1;}50%{opacity:0.5;}}
975
+
976
+ /* ═══ RESULT ═══ */
977
+ .result-html{border-radius:var(--radius);overflow:hidden;margin-top:16px;animation:resultIn 0.5s ease-out;}
978
+ .result-html:empty{display:none;}
979
+ @keyframes resultIn{from{opacity:0;transform:translateY(12px);}to{opacity:1;transform:translateY(0);}}
980
+ .log-box{
981
+ background:rgba(8,8,28,0.7);border:1px solid var(--glass-border);
982
+ border-radius:var(--radius-xs);padding:14px 18px;
983
+ font-family:var(--font-mono);font-size:11px;color:var(--text-muted);
984
+ line-height:1.7;white-space:pre-wrap;margin-top:10px;backdrop-filter:blur(8px);
985
+ }
986
+ .log-box:empty{display:none;}
987
+
988
+ /* ═══ HUMANIZER RESULT ═══ */
989
+ .result-card{
990
+ background:var(--glass);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
991
+ border:1px solid rgba(61,214,200,0.2);border-radius:var(--radius);
992
+ padding:24px;position:relative;overflow:hidden;
993
+ }
994
+ .result-card::before{
995
+ content:'';position:absolute;top:0;left:0;right:0;height:2px;
996
+ background:linear-gradient(90deg,var(--teal),var(--accent),var(--teal));
997
+ }
998
+ .result-card-title{
999
+ display:flex;align-items:center;gap:8px;
1000
+ font-family:var(--font-display);font-size:15px;font-weight:700;
1001
+ color:var(--teal);margin-bottom:14px;
1002
+ }
1003
+ .converted-text{
1004
+ background:rgba(8,8,28,0.5);border:1px solid var(--glass-border);
1005
+ border-radius:var(--radius-sm);padding:18px 20px;
1006
+ font-size:14px;line-height:1.9;position:relative;
1007
+ white-space:pre-wrap;min-height:60px;color:var(--text);
1008
+ }
1009
+ .copy-btn{
1010
+ position:absolute;top:10px;right:10px;
1011
+ background:var(--glass2);backdrop-filter:blur(10px);
1012
+ border:1px solid var(--glass-border);color:var(--text-muted);
1013
+ padding:5px 12px;border-radius:var(--radius-xs);cursor:pointer;
1014
+ font-family:var(--font-mono);font-size:10px;font-weight:600;
1015
+ transition:all 0.25s;letter-spacing:0.5px;text-transform:uppercase;
1016
+ }
1017
+ .copy-btn:hover{background:var(--accent);color:#fff;border-color:var(--accent);}
1018
+ .copy-btn.copied{background:var(--teal);color:#fff;border-color:var(--teal);}
1019
+
1020
+ /* ═══ FOOTER ═══ */
1021
+ .footer{
1022
+ text-align:center;margin-top:56px;padding:28px 0;
1023
+ border-top:1px solid var(--glass-border);
1024
+ animation:fadeUp 1s 0.6s ease-out both;
1025
+ }
1026
+ .footer-brand{font-family:var(--font-display);font-size:13px;font-weight:600;color:var(--text-muted);letter-spacing:0.5px;margin-bottom:6px;}
1027
+ .footer-tech{font-family:var(--font-mono);font-size:10px;color:rgba(90,90,136,0.6);letter-spacing:1px;}
1028
+ .footer-line{
1029
+ width:40px;height:2px;
1030
+ background:linear-gradient(90deg,transparent,var(--accent-glow2),var(--accent),var(--accent-glow2),transparent);
1031
+ margin:12px auto;border-radius:2px;
1032
+ }
1033
+
1034
+ /* ═══ RESULTS TABLE OVERRIDE ═══ */
1035
+ .result-html table{border-collapse:collapse;width:100%;}
1036
+ .result-html td,.result-html th{padding:8px 12px;border:1px solid var(--glass-border);}
1037
+ .result-html th{background:rgba(139,122,255,0.08);}
1038
+
1039
+ /* ═══ STATUS DOT ═══ */
1040
+ .status-dot{
1041
+ width:6px;height:6px;border-radius:50%;background:var(--teal);
1042
+ display:inline-block;animation:statusPulse 2s ease-in-out infinite;margin-right:4px;
1043
+ }
1044
+ @keyframes statusPulse{0%,100%{opacity:1;box-shadow:0 0 0 0 rgba(61,214,200,0.4);}50%{opacity:0.7;box-shadow:0 0 0 4px rgba(61,214,200,0);}}
1045
+
1046
+ /* ═══ FILE UPLOAD ═══ */
1047
+ .file-drop{
1048
+ border:2px dashed var(--glass-border);border-radius:var(--radius);
1049
+ padding:32px;text-align:center;cursor:pointer;transition:all 0.3s;
1050
+ background:rgba(8,8,28,0.4);position:relative;
1051
+ }
1052
+ .file-drop:hover,.file-drop.dragover{
1053
+ border-color:var(--accent);background:var(--accent-glow);
1054
+ }
1055
+ .file-drop-icon{font-size:32px;margin-bottom:8px;}
1056
+ .file-drop-text{font-size:13px;color:var(--text-muted);}
1057
+ .file-drop-hint{font-size:10px;color:var(--text-muted);margin-top:6px;opacity:0.6;}
1058
+ .file-drop input[type=file]{position:absolute;inset:0;opacity:0;cursor:pointer;}
1059
+ .file-info{display:none;padding:12px 16px;background:var(--glass);border:1px solid var(--glass-border);border-radius:var(--radius-sm);margin-top:10px;font-size:12px;color:var(--text-dim);}
1060
+ .file-info.active{display:flex;align-items:center;gap:10px;}
1061
+ .file-info-name{font-weight:600;color:var(--text);}
1062
+ .file-info-size{color:var(--text-muted);font-family:var(--font-mono);font-size:10px;}
1063
+ .file-remove{cursor:pointer;color:var(--rose);font-size:14px;margin-left:auto;}
1064
+
1065
+ /* ═══ INLINE FILE UPLOAD (νƒ­1-4) ═══ */
1066
+ .btn-file{position:relative;overflow:hidden;}
1067
+ .btn-file input[type=file]{position:absolute;inset:0;opacity:0;cursor:pointer;font-size:0;}
1068
+ .file-status{display:none;padding:8px 14px;background:rgba(139,122,255,0.08);border:1px solid rgba(139,122,255,0.2);border-radius:var(--radius-sm);margin-top:10px;font-size:11px;color:var(--accent);align-items:center;gap:8px;}
1069
+ .file-status.active{display:flex;}
1070
+ .file-status .fname{font-weight:600;color:var(--text);max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
1071
+ .file-status .fsize{color:var(--text-muted);font-family:var(--font-mono);font-size:10px;}
1072
+ .file-status .fclose{cursor:pointer;color:var(--rose);margin-left:auto;font-size:13px;padding:0 4px;}
1073
+ .file-loading{display:inline-block;width:12px;height:12px;border:2px solid var(--accent);border-top-color:transparent;border-radius:50%;animation:ring-spin 0.6s linear infinite;}
1074
+
1075
+ /* ═══ RESPONSIVE ═══ */
1076
+ @media(max-width:640px){
1077
+ .app-wrapper{padding:16px 12px 32px;}
1078
+ .header{padding:32px 0 24px;}
1079
+ .header-title{font-size:32px;letter-spacing:-0.5px;}
1080
+ .header-subtitle{font-size:13px;}
1081
+ .nav{flex-wrap:wrap;position:static;}
1082
+ .nav-item{font-size:11px;padding:10px 6px;}
1083
+ .nav-icon{font-size:13px;}
1084
+ .card{padding:20px 16px;}
1085
+ .card-desc{padding-left:0;margin-top:8px;}
1086
+ textarea{min-height:160px;font-size:13px;padding:14px 16px 28px;}
1087
+ .btn{padding:10px 16px;font-size:12px;}
1088
+ .btn-group{gap:6px;}
1089
+ }
1090
+ </style>
1091
+ </head>
1092
+ <body>
1093
+
1094
+ <!-- Background -->
1095
+ <canvas id="mesh-canvas"></canvas>
1096
+ <div class="noise-overlay"></div>
1097
+ <div class="vignette"></div>
1098
+
1099
+ <div class="app-wrapper">
1100
+
1101
+ <!-- Header -->
1102
+ <header class="header">
1103
+ <div class="header-eyebrow">AI Text Detector</div>
1104
+ <h1 class="header-title"><span class="gradient-text">TeXray</span></h1>
1105
+ <p class="header-subtitle">
1106
+ <b>5μΆ• 앙상블 탐지</b> Β· <b>ν’ˆμ§ˆ μΈ‘μ •</b> Β· <b>LLM ꡐ차검증</b> Β· <b>Adversarial Humanizer</b> Β· <b>ν‘œμ ˆ 검사</b> Β· <b>λ¬Έμ„œ 뢄석</b>
1107
+ </p>
1108
+ <div class="pill-row">
1109
+ <span class="pill">GPT-OSS 120B</span>
1110
+ <span class="pill">Qwen3 32B</span>
1111
+ <span class="pill">Kimi-K2</span>
1112
+ <span class="pill">Brave + KCI + arXiv</span>
1113
+ <span class="pill">PDFΒ·DOCXΒ·HWP</span>
1114
+ <span class="pill"><span class="status-dot"></span>v4.0</span>
1115
+ </div>
1116
+ </header>
1117
+
1118
+ <!-- Nav -->
1119
+ <nav class="nav">
1120
+ <div class="nav-item active" data-tab="detect" onclick="switchTab('detect')"><span class="nav-icon">πŸ”</span><span>AI 뢄석</span></div>
1121
+ <div class="nav-item" data-tab="highlight" onclick="switchTab('highlight')"><span class="nav-icon">🎨</span><span>ν•˜μ΄λΌμ΄νŠΈ</span></div>
1122
+ <div class="nav-item" data-tab="humanize" onclick="switchTab('humanize')"><span class="nav-icon">βš”οΈ</span><span>AI→인간</span></div>
1123
+ <div class="nav-item" data-tab="plagiarism" onclick="switchTab('plagiarism')"><span class="nav-icon">πŸ“‹</span><span>ν‘œμ ˆ</span></div>
1124
+ </nav>
1125
+
1126
+ <!-- Panel 1 -->
1127
+ <div class="panel active" id="panel-detect">
1128
+ <div class="card">
1129
+ <div class="card-header"><div class="card-icon purple">πŸ“„</div><div class="card-title">AI ν…μŠ€νŠΈ 뢄석</div></div>
1130
+ <div class="card-desc">5μΆ• 앙상블(ν†΅κ³„Β·λ¬Έμ²΄Β·λ°˜λ³΅Β·κ΅¬μ‘°Β·μ§€λ¬Έ) + 6ν•­λͺ© ν’ˆμ§ˆ μΈ‘μ • + LLM 3λͺ¨λΈ κ΅μ°¨κ²€μ¦μœΌλ‘œ μ •λ°€ νŒλ³„ν•©λ‹ˆλ‹€.</div>
1131
+ <div class="input-group"><div class="textarea-wrap">
1132
+ <textarea id="input-detect" placeholder="AI νŒλ³„ν•  ν…μŠ€νŠΈλ₯Ό μž…λ ₯ν•˜μ„Έμš” (μ΅œμ†Œ 50자)" oninput="uc('input-detect','count-detect')"></textarea>
1133
+ <div class="char-counter" id="count-detect">0자</div>
1134
+ </div></div>
1135
+ <div class="btn-group">
1136
+ <button class="btn btn-primary" id="btn-detect" onclick="runDetect()">πŸš€ AI νŒλ³„ + ν’ˆμ§ˆ 뢄석</button>
1137
+ <button class="btn btn-ghost btn-file">πŸ“ 파일<input type="file" accept=".pdf,.docx,.hwp,.hwpx,.txt,.md,.csv" onchange="loadFile(this,'input-detect','fs-detect')"></button>
1138
+ <button class="btn btn-ghost" onclick="ls('input-detect','ai')">πŸ“ AI μ˜ˆμ‹œ</button>
1139
+ <button class="btn btn-ghost" onclick="ls('input-detect','human')">✍️ 인간 μ˜ˆμ‹œ</button>
1140
+ </div>
1141
+ <div class="file-status" id="fs-detect"><span class="fname"></span><span class="fsize"></span><span class="fclose" onclick="clearInline('input-detect','fs-detect')">βœ•</span></div>
1142
+ </div>
1143
+ <div class="loader" id="loader-detect"><div class="loader-ring"></div><div class="loader-text">5μΆ• 뢄석 + LLM ꡐ차검증 쀑</div><div class="loader-dots"><span></span><span></span><span></span></div></div>
1144
+ <div class="result-html" id="result-detect"></div><div class="log-box" id="log-detect"></div>
1145
+ </div>
1146
+
1147
+ <!-- Panel 2 -->
1148
+ <div class="panel" id="panel-highlight">
1149
+ <div class="card">
1150
+ <div class="card-header"><div class="card-icon teal">🎨</div><div class="card-title">λ¬Έμž₯별 ν•˜μ΄λΌμ΄νŠΈ</div></div>
1151
+ <div class="card-desc">νƒ­1κ³Ό λ™μΌν•œ κΈ°μ€€μœΌλ‘œ λ¬Έμž₯별 AI ν™•λ₯ μ„ μƒ‰μƒμœΌλ‘œ ν‘œμ‹œν•©λ‹ˆλ‹€. 마우슀 μ˜€λ²„ μ‹œ νŒμ • κ·Όκ±°λ₯Ό ν™•μΈν•˜μ„Έμš”.</div>
1152
+ <div class="input-group"><div class="textarea-wrap">
1153
+ <textarea id="input-highlight" placeholder="λ¬Έμž₯별 AI ν™•λ₯  색상 ν‘œμ‹œ (μ΅œμ†Œ 30자)" oninput="uc('input-highlight','count-highlight')"></textarea>
1154
+ <div class="char-counter" id="count-highlight">0자</div>
1155
+ </div></div>
1156
+ <div class="btn-group">
1157
+ <button class="btn btn-primary" id="btn-highlight" onclick="runHighlight()">🎨 ν•˜μ΄λΌμ΄νŠΈ 뢄석</button>
1158
+ <button class="btn btn-ghost btn-file">πŸ“ 파일<input type="file" accept=".pdf,.docx,.hwp,.hwpx,.txt,.md,.csv" onchange="loadFile(this,'input-highlight','fs-highlight')"></button>
1159
+ <button class="btn btn-ghost" onclick="ls('input-highlight','ai')">πŸ“ AI μ˜ˆμ‹œ</button>
1160
+ </div>
1161
+ <div class="file-status" id="fs-highlight"><span class="fname"></span><span class="fsize"></span><span class="fclose" onclick="clearInline('input-highlight','fs-highlight')">βœ•</span></div>
1162
+ </div>
1163
+ <div class="loader" id="loader-highlight"><div class="loader-ring"></div><div class="loader-text">λ¬Έμž₯ 뢄석 쀑</div><div class="loader-dots"><span></span><span></span><span></span></div></div>
1164
+ <div class="result-html" id="result-highlight"></div>
1165
+ </div>
1166
+
1167
+ <!-- Panel 3 -->
1168
+ <div class="panel" id="panel-humanize">
1169
+ <div class="card">
1170
+ <div class="card-header"><div class="card-icon rose">βš”οΈ</div><div class="card-title">Adversarial Humanizer v2</div></div>
1171
+ <div class="card-desc">탐지기 vs λ³€ν™˜κΈ° μžκΈ°λŒ€μ „ 루프. μ΅œλŒ€ 3λΌμš΄λ“œ 반볡 μ΅œμ ν™” β€” κ·œμΉ™/LLM/LLM+κ·œμΉ™ λ§€ λΌμš΄λ“œ 경쟁.</div>
1172
+ <div class="input-group"><div class="textarea-wrap">
1173
+ <textarea id="input-humanize" placeholder="AIκ°€ μž‘μ„±ν•œ ν…μŠ€νŠΈλ₯Ό μž…λ ₯ν•˜μ„Έμš” (μ΅œμ†Œ 50자)" oninput="uc('input-humanize','count-humanize')"></textarea>
1174
+ <div class="char-counter" id="count-humanize">0자</div>
1175
+ </div></div>
1176
+ <div class="btn-group">
1177
+ <button class="btn btn-primary" id="btn-humanize" onclick="runHumanize()">βš”οΈ μ λŒ€μ  λ³€ν™˜ + 검증</button>
1178
+ <button class="btn btn-ghost btn-file">πŸ“ 파일<input type="file" accept=".pdf,.docx,.hwp,.hwpx,.txt,.md,.csv" onchange="loadFile(this,'input-humanize','fs-humanize')"></button>
1179
+ <button class="btn btn-ghost" onclick="ls('input-humanize','ai')">πŸ“ AI μ˜ˆμ‹œ</button>
1180
+ </div>
1181
+ <div class="file-status" id="fs-humanize"><span class="fname"></span><span class="fsize"></span><span class="fclose" onclick="clearInline('input-humanize','fs-humanize')">βœ•</span></div>
1182
+ </div>
1183
+ <div class="loader" id="loader-humanize"><div class="loader-ring"></div><div class="loader-text">κ·œμΉ™ β†’ LLM β†’ 3-way 비ꡐ β†’ 검증</div><div class="loader-dots"><span></span><span></span><span></span></div></div>
1184
+ <div id="result-humanize-area" style="display:none;">
1185
+ <div class="result-card">
1186
+ <div class="result-card-title">βœ… λ³€ν™˜ μ™„λ£Œ</div>
1187
+ <div class="converted-text" id="result-humanize-text"><button class="copy-btn" onclick="copyText()">COPY</button></div>
1188
+ </div>
1189
+ <div class="log-box" id="result-humanize-log"></div>
1190
+ <div class="result-html" id="result-humanize-compare"></div>
1191
+ </div>
1192
+ </div>
1193
+
1194
+ <!-- Panel 4 -->
1195
+ <div class="panel" id="panel-plagiarism">
1196
+ <div class="card">
1197
+ <div class="card-header"><div class="card-icon amber">πŸ“‹</div><div class="card-title">ν‘œμ ˆ 검사</div></div>
1198
+ <div class="card-desc">Brave Search 병렬(μ΅œλŒ€ 20) + KCI Β· RISS Β· arXiv ν•™μˆ DB + Gemini Google Search 톡합 검사. API ν‚€ 없이도 자체 크둀링으둜 μž‘λ™ν•©λ‹ˆλ‹€.</div>
1199
+ <div class="input-group"><div class="textarea-wrap">
1200
+ <textarea id="input-plagiarism" placeholder="ν‘œμ ˆ 검사할 ν…μŠ€νŠΈλ₯Ό μž…λ ₯ν•˜μ„Έμš” (οΏ½οΏ½μ†Œ 50자)" oninput="uc('input-plagiarism','count-plagiarism')"></textarea>
1201
+ <div class="char-counter" id="count-plagiarism">0자</div>
1202
+ </div></div>
1203
+ <div class="btn-group">
1204
+ <button class="btn btn-primary" id="btn-plagiarism" onclick="runPlagiarism()">πŸ“‹ ν‘œμ ˆ 검사 μ‹œμž‘</button>
1205
+ <button class="btn btn-ghost btn-file">πŸ“ 파일<input type="file" accept=".pdf,.docx,.hwp,.hwpx,.txt,.md,.csv" onchange="loadFile(this,'input-plagiarism','fs-plagiarism')"></button>
1206
+ <button class="btn btn-ghost" onclick="ls('input-plagiarism','ai')">πŸ“ AI μ˜ˆμ‹œ</button>
1207
+ </div>
1208
+ <div class="file-status" id="fs-plagiarism"><span class="fname"></span><span class="fsize"></span><span class="fclose" onclick="clearInline('input-plagiarism','fs-plagiarism')">βœ•</span></div>
1209
+ </div>
1210
+ <div class="loader" id="loader-plagiarism"><div class="loader-ring"></div><div class="loader-text">웹검색 + ν•™μˆ DB + λ³΄κ³ μ„œ 생성</div><div class="loader-dots"><span></span><span></span><span></span></div></div>
1211
+ <div id="result-plagiarism-area" style="display:none;">
1212
+ <div class="result-html" id="result-plagiarism"></div>
1213
+ <div class="log-box" id="result-plagiarism-log"></div>
1214
+ </div>
1215
+ </div>
1216
+
1217
+ <footer class="footer">
1218
+ <div class="footer-brand">TeXray</div>
1219
+ <div class="footer-line"></div>
1220
+ <div class="footer-tech">KIWI Β· GROQ Β· BRAVE Β· KCI Β· RISS Β· ARXIV Β· GEMINI</div>
1221
+ </footer>
1222
+ </div>
1223
+
1224
+ <script>
1225
+ /* ═══ MESH GRADIENT CANVAS (WebGL) ═══ */
1226
+ (function(){
1227
+ const c=document.getElementById('mesh-canvas');
1228
+ const gl=c.getContext('webgl')||c.getContext('experimental-webgl');
1229
+ if(!gl){
1230
+ c.style.background='radial-gradient(ellipse at 20% 20%,rgba(139,122,255,0.08),transparent 50%),radial-gradient(ellipse at 80% 80%,rgba(61,214,200,0.06),transparent 50%),radial-gradient(ellipse at 50% 50%,rgba(255,107,138,0.04),transparent 40%)';
1231
+ return;
1232
+ }
1233
+ function resize(){c.width=window.innerWidth;c.height=window.innerHeight;gl.viewport(0,0,c.width,c.height);}
1234
+ resize();window.addEventListener('resize',resize);
1235
+ const vs=`attribute vec2 p;void main(){gl_Position=vec4(p,0,1);}`;
1236
+ const fs=`
1237
+ precision mediump float;
1238
+ uniform float t;
1239
+ uniform vec2 r;
1240
+ void main(){
1241
+ vec2 u=gl_FragCoord.xy/r;
1242
+ float n=sin(u.x*3.+t*0.3)*cos(u.y*2.5+t*0.2)*0.5+0.5;
1243
+ float n2=cos(u.x*2.-t*0.15)*sin(u.y*3.5+t*0.25)*0.5+0.5;
1244
+ float n3=sin((u.x+u.y)*2.+t*0.18)*0.5+0.5;
1245
+ vec3 c1=vec3(0.545,0.478,1.0)*n*0.12;
1246
+ vec3 c2=vec3(0.24,0.84,0.78)*n2*0.08;
1247
+ vec3 c3=vec3(1.0,0.42,0.54)*n3*0.05;
1248
+ float v=smoothstep(0.0,0.3,u.y)*smoothstep(1.0,0.7,u.y);
1249
+ gl_FragColor=vec4((c1+c2+c3)*v,1.0);
1250
+ }`;
1251
+ function compile(src,type){const s=gl.createShader(type);gl.shaderSource(s,src);gl.compileShader(s);return s;}
1252
+ const prog=gl.createProgram();
1253
+ gl.attachShader(prog,compile(vs,gl.VERTEX_SHADER));
1254
+ gl.attachShader(prog,compile(fs,gl.FRAGMENT_SHADER));
1255
+ gl.linkProgram(prog);gl.useProgram(prog);
1256
+ const buf=gl.createBuffer();
1257
+ gl.bindBuffer(gl.ARRAY_BUFFER,buf);
1258
+ gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,1,1]),gl.STATIC_DRAW);
1259
+ const pLoc=gl.getAttribLocation(prog,'p');
1260
+ gl.enableVertexAttribArray(pLoc);
1261
+ gl.vertexAttribPointer(pLoc,2,gl.FLOAT,false,0,0);
1262
+ const tLoc=gl.getUniformLocation(prog,'t');
1263
+ const rLoc=gl.getUniformLocation(prog,'r');
1264
+ let start=performance.now();
1265
+ function frame(){
1266
+ const elapsed=(performance.now()-start)/1000;
1267
+ gl.uniform1f(tLoc,elapsed);
1268
+ gl.uniform2f(rLoc,c.width,c.height);
1269
+ gl.drawArrays(gl.TRIANGLE_STRIP,0,4);
1270
+ requestAnimationFrame(frame);
1271
+ }
1272
+ frame();
1273
+ })();
1274
+
1275
+ /* ═══ API ═══ */
1276
+ const B=window.location.origin;
1277
+ const SA=`인곡지λŠ₯ κΈ°μˆ μ€ ν˜„λŒ€ μ‚¬νšŒμ—μ„œ 맀우 μ€‘μš”ν•œ 역할을 ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. 특히 μžμ—°μ–΄ 처리 λΆ„μ•Όμ—μ„œμ˜ λ°œμ „μ€ λˆˆλΆ€μ‹  μ„±κ³Όλ₯Ό 거두고 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ 기술의 λ°œμ „μ€ λ‹€μ–‘ν•œ μ‚°μ—… 뢄야에 긍정적인 영ν–₯을 미치고 있으며, ν–₯ν›„ λ”μš± λ°œμ „ν•  κ²ƒμœΌλ‘œ μ˜ˆμƒλ©λ‹ˆλ‹€.\n\nλ˜ν•œ μƒμ„±ν˜• AI의 λ“±μž₯으둜 μ½˜ν…μΈ  μ œμž‘ 방식이 크게 λ³€ν™”ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό 톡해 기업듀은 효율적인 μ½˜ν…μΈ  생산이 κ°€λŠ₯ν•΄μ‘ŒμœΌλ©°, 개인 μ‚¬μš©μžλ“€λ„ λ‹€μ–‘ν•œ μ°½μž‘ ν™œλ™μ— AIλ₯Ό ν™œμš©ν•  수 있게 λ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ AI λ¦¬ν„°λŸ¬μ‹œμ˜ μ€‘μš”μ„±μ΄ λ”μš± λΆ€κ°λ˜κ³  μžˆμŠ΅λ‹ˆλ‹€.\n\nλ‚˜μ•„κ°€ AI μœ€λ¦¬μ™€ κ·œμ œμ— λŒ€ν•œ λ…Όμ˜λ„ ν™œλ°œνžˆ μ§„ν–‰λ˜κ³  μžˆμŠ΅λ‹ˆλ‹€.`;
1278
+ const SH=`μ•„ μ§„μ§œ μš”μ¦˜ AI λ•Œλ¬Έμ— 머리 μ•„ν”„λ‹€γ…‹γ…‹γ…‹ μ–΄μ œ chatgptν•œν…Œ 레포트 써달라고 ν–ˆλŠ”λ° μ™„μ „ κ΅κ³Όμ„œ 같은 κΈ€λ§Œ μ¨μ€˜μ„œ κ·Έλƒ₯ λ‚΄κ°€ λ‹€μ‹œ 썼음;;\n\n근데 생각해보면 AIκ°€ μ“΄ κΈ€μ΄λž‘ μ‚¬λžŒμ΄ μ“΄ 글이 ν™•μ‹€νžˆ λ‹€λ₯΄κΈ΄ ν•΄. λ­”κ°€... λ„ˆλ¬΄ κΉ”λ”ν•˜λ‹¬κΉŒ?\n\nκ΅μˆ˜λ‹˜μ΄ AI 탐지기 λŒλ¦°λ‹€κ³  ν•΄μ„œ μ’€ λ¬΄μ„œμš΄λ° γ… γ…  κ±±μ •λœλ‹€ μ§„μ‹¬μœΌλ‘œ.`;
1279
+
1280
+ function switchTab(t){
1281
+ document.querySelectorAll('.nav-item').forEach(e=>e.classList.remove('active'));
1282
+ document.querySelectorAll('.panel').forEach(e=>e.classList.remove('active'));
1283
+ document.querySelector(`[data-tab="${t}"]`).classList.add('active');
1284
+ document.getElementById(`panel-${t}`).classList.add('active');
1285
+ }
1286
+ function uc(a,b){document.getElementById(b).textContent=document.getElementById(a).value.length+'자';}
1287
+ function ls(id,t){const e=document.getElementById(id);e.value=t==='ai'?SA:SH;e.dispatchEvent(new Event('input'));}
1288
+ function sw(id){document.getElementById(id).classList.add('active');}
1289
+ function hw(id){document.getElementById(id).classList.remove('active');}
1290
+
1291
+ async function gc(api,data){
1292
+ const urls=[`${B}/gradio/gradio_api/call${api}`,`${B}/gradio/api${api}`,`${B}/gradio_api/call${api}`,`${B}/api${api}`];
1293
+ for(const u of urls){
1294
+ try{
1295
+ const r=await fetch(u,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({data})});
1296
+ if(!r.ok)continue;
1297
+ const j=await r.json();if(!j.event_id)continue;
1298
+ return new Promise((ok,no)=>{
1299
+ const es=new EventSource(`${u}/${j.event_id}`);let d=false;
1300
+ const handle=e=>{if(d)return;try{const raw=JSON.parse(e.data);const p=Array.isArray(raw)?raw:(raw&&raw.data?raw.data:null);if(p){d=true;es.close();ok(p);}}catch{}};
1301
+ es.onmessage=handle;
1302
+ es.addEventListener('complete',handle);
1303
+ es.addEventListener('process_completed',e=>{if(d)return;try{const raw=JSON.parse(e.data);const out=raw&&raw.output&&raw.output.data?raw.output.data:null;if(out){d=true;es.close();ok(out);}}catch{}});
1304
+ es.onerror=()=>{if(!d){d=true;es.close();no(new Error('μ—°κ²° 였λ₯˜'));}};
1305
+ setTimeout(()=>{if(!d){d=true;es.close();no(new Error('μ‹œκ°„ 초과 (120s)'));}},120000);
1306
+ });
1307
+ }catch(e){continue;}
1308
+ }
1309
+ throw new Error('API μ—°κ²° μ‹€νŒ¨ β€” Gradio μ„œλ²„ 확인 ν•„μš”');
1310
+ }
1311
+
1312
+ function showError(el,msg){
1313
+ el.innerHTML=`<div style="padding:28px;text-align:center;color:var(--rose);font-size:14px;background:var(--glass);border:1px solid rgba(255,107,138,0.2);border-radius:var(--radius);">⚠️ ${msg}</div>`;
1314
+ }
1315
+
1316
+ async function runDetect(){
1317
+ const t=document.getElementById('input-detect').value;if(!t||t.length<50){alert('μ΅œμ†Œ 50자 이상 μž…λ ₯ν•΄μ£Όμ„Έμš”.');return;}
1318
+ const b=document.getElementById('btn-detect');b.disabled=true;
1319
+ document.getElementById('result-detect').innerHTML='';document.getElementById('log-detect').textContent='';sw('loader-detect');
1320
+ try{const d=await gc('/run_detection',[t]);hw('loader-detect');document.getElementById('result-detect').innerHTML=d[0]||'';document.getElementById('log-detect').textContent=d[1]||'';}
1321
+ catch(e){hw('loader-detect');showError(document.getElementById('result-detect'),e.message);}
1322
+ b.disabled=false;
1323
+ }
1324
+ async function runHighlight(){
1325
+ const t=document.getElementById('input-highlight').value;if(!t||t.length<30){alert('ν…μŠ€νŠΈλ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš”.');return;}
1326
+ const b=document.getElementById('btn-highlight');b.disabled=true;sw('loader-highlight');
1327
+ try{const d=await gc('/run_highlight',[t]);hw('loader-highlight');document.getElementById('result-highlight').innerHTML=d[0]||'';}
1328
+ catch(e){hw('loader-highlight');showError(document.getElementById('result-highlight'),e.message);}
1329
+ b.disabled=false;
1330
+ }
1331
+ async function runHumanize(){
1332
+ const t=document.getElementById('input-humanize').value;if(!t||t.length<50){alert('μ΅œμ†Œ 50자 이상 μž…λ ₯ν•΄μ£Όμ„Έμš”.');return;}
1333
+ const b=document.getElementById('btn-humanize');b.disabled=true;document.getElementById('result-humanize-area').style.display='none';sw('loader-humanize');
1334
+ try{const d=await gc('/run_humanizer',[t]);hw('loader-humanize');document.getElementById('result-humanize-area').style.display='block';
1335
+ const el=document.getElementById('result-humanize-text');el.innerHTML=`<button class="copy-btn" onclick="copyText()">COPY</button>${(d[0]||'').replace(/\n/g,'<br>')}`;
1336
+ document.getElementById('result-humanize-log').textContent=d[1]||'';document.getElementById('result-humanize-compare').innerHTML=d[2]||'';}
1337
+ catch(e){hw('loader-humanize');document.getElementById('result-humanize-area').style.display='block';showError(document.getElementById('result-humanize-text'),e.message);}
1338
+ b.disabled=false;
1339
+ }
1340
+ async function runPlagiarism(){
1341
+ const t=document.getElementById('input-plagiarism').value;if(!t||t.length<50){alert('μ΅œμ†Œ 50자 이상 μž…λ ₯ν•΄μ£Όμ„Έμš”.');return;}
1342
+ const b=document.getElementById('btn-plagiarism');b.disabled=true;document.getElementById('result-plagiarism-area').style.display='none';sw('loader-plagiarism');
1343
+ try{const d=await gc('/run_plagiarism',[t]);hw('loader-plagiarism');document.getElementById('result-plagiarism-area').style.display='block';
1344
+ document.getElementById('result-plagiarism').innerHTML=d[0]||'';document.getElementById('result-plagiarism-log').textContent=d[1]||'';}
1345
+ catch(e){hw('loader-plagiarism');document.getElementById('result-plagiarism-area').style.display='block';showError(document.getElementById('result-plagiarism'),e.message);}
1346
+ b.disabled=false;
1347
+ }
1348
+ function copyText(){
1349
+ const el=document.getElementById('result-humanize-text');
1350
+ const t=el.textContent.replace('COPY','').replace('βœ“ COPIED','').trim();
1351
+ navigator.clipboard.writeText(t).then(()=>{
1352
+ const btn=el.querySelector('.copy-btn');btn.textContent='βœ“ COPIED';btn.classList.add('copied');
1353
+ setTimeout(()=>{btn.textContent='COPY';btn.classList.remove('copied');},2000);
1354
+ });
1355
+ }
1356
+ /* Ctrl+Enter shortcut */
1357
+ document.addEventListener('keydown',e=>{
1358
+ if(e.ctrlKey&&e.key==='Enter'){
1359
+ const active=document.querySelector('.panel.active');
1360
+ if(!active)return;
1361
+ const btn=active.querySelector('.btn-primary');
1362
+ if(btn&&!btn.disabled)btn.click();
1363
+ }
1364
+ });
1365
+
1366
+ /* ═══ 곡톡 파일 μ—…λ‘œλ“œ μ—”μ§„ ═══ */
1367
+ async function uploadFileToGradio(file){
1368
+ const formData=new FormData();formData.append('files',file);
1369
+ const urls=[`${B}/gradio/gradio_api/upload`,`${B}/gradio/upload`,`${B}/gradio_api/upload`,`${B}/upload`];
1370
+ for(const u of urls){
1371
+ try{
1372
+ const r=await fetch(u,{method:'POST',body:formData});
1373
+ if(r.ok){const j=await r.json();return Array.isArray(j)?j[0]:j;}
1374
+ }catch{}
1375
+ }
1376
+ throw new Error('파일 μ—…λ‘œλ“œ μ‹€νŒ¨');
1377
+ }
1378
+
1379
+ /* Gradio 6.x FileData 객체 생성 */
1380
+ function makeFileData(path, origName){
1381
+ return {path:path, meta:{_type:'gradio.FileData'}, orig_name:origName||'', mime_type:''};
1382
+ }
1383
+
1384
+ /* ═══ νƒ­1~4: 파일 β†’ ν…μŠ€νŠΈ μΆ”μΆœ β†’ textarea μ±„μš°κΈ° ═══ */
1385
+ async function loadFile(input, textareaId, statusId){
1386
+ const file=input.files&&input.files[0];
1387
+ if(!file)return;
1388
+ const fname=file.name;
1389
+ const fsize=Math.round(file.size/1024)+'KB';
1390
+ const statusEl=document.getElementById(statusId);
1391
+ const textarea=document.getElementById(textareaId);
1392
+
1393
+ /* μƒνƒœ ν‘œμ‹œ: λ‘œλ”© */
1394
+ statusEl.classList.add('active');
1395
+ statusEl.querySelector('.fname').innerHTML=`<span class="file-loading"></span> ${fname} μΆ”μΆœ 쀑...`;
1396
+ statusEl.querySelector('.fsize').textContent=fsize;
1397
+
1398
+ try{
1399
+ /* 1. Gradio에 파일 μ—…λ‘œλ“œ */
1400
+ const uploadedPath=await uploadFileToGradio(file);
1401
+
1402
+ /* 2. extract_file_text API 호좜 (Gradio 6.x FileData ν˜•μ‹) */
1403
+ const fileData=makeFileData(uploadedPath, fname);
1404
+ const result=await gc('/extract_file_text',[fileData]);
1405
+ const text=result[0]||'';
1406
+
1407
+ if(text.startsWith('⚠️')){
1408
+ statusEl.querySelector('.fname').textContent='❌ '+text;
1409
+ statusEl.querySelector('.fname').style.color='var(--rose)';
1410
+ return;
1411
+ }
1412
+
1413
+ /* 3. textarea에 ν…μŠ€νŠΈ μ‚½μž… */
1414
+ textarea.value=text;
1415
+ textarea.dispatchEvent(new Event('input'));
1416
+ statusEl.querySelector('.fname').textContent='βœ… '+fname;
1417
+ statusEl.querySelector('.fname').style.color='var(--teal)';
1418
+ statusEl.querySelector('.fsize').textContent=fsize+' Β· '+text.length+'자 μΆ”μΆœ';
1419
+ }catch(e){
1420
+ statusEl.querySelector('.fname').textContent='❌ '+e.message;
1421
+ statusEl.querySelector('.fname').style.color='var(--rose)';
1422
+ }
1423
+ /* input μ΄ˆκΈ°ν™” (같은 파일 μž¬μ„ νƒ κ°€λŠ₯) */
1424
+ input.value='';
1425
+ }
1426
+
1427
+ function clearInline(textareaId, statusId){
1428
+ document.getElementById(statusId).classList.remove('active');
1429
+ document.getElementById(statusId).querySelector('.fname').style.color='';
1430
+ }
1431
+ /* ═══ textarea λ“œλž˜κ·Έμ•€λ“œλ‘­ 파일 (νƒ­1~4) ═══ */
1432
+ const dropMappings=[
1433
+ {ta:'input-detect',fs:'fs-detect'},
1434
+ {ta:'input-highlight',fs:'fs-highlight'},
1435
+ {ta:'input-humanize',fs:'fs-humanize'},
1436
+ {ta:'input-plagiarism',fs:'fs-plagiarism'}
1437
+ ];
1438
+ dropMappings.forEach(({ta,fs})=>{
1439
+ const el=document.getElementById(ta);
1440
+ if(!el)return;
1441
+ el.addEventListener('dragover',e=>{e.preventDefault();el.style.borderColor='var(--accent)';el.style.background='rgba(139,122,255,0.04)';});
1442
+ el.addEventListener('dragleave',()=>{el.style.borderColor='';el.style.background='';});
1443
+ el.addEventListener('drop',e=>{
1444
+ e.preventDefault();el.style.borderColor='';el.style.background='';
1445
+ const file=e.dataTransfer.files&&e.dataTransfer.files[0];
1446
+ if(!file)return;
1447
+ const ext=file.name.split('.').pop().toLowerCase();
1448
+ if(!['pdf','docx','hwp','hwpx','txt','md','csv'].includes(ext))return;
1449
+ /* fake input for loadFile */
1450
+ const fakeInput={files:[file],value:file.name};
1451
+ loadFile(fakeInput,ta,fs);
1452
+ });
1453
+ });
1454
+
1455
  </script>
1456
  </body>
1457
  </html>