AptlyDigital commited on
Commit
a41249b
·
verified ·
1 Parent(s): 1b46bc8

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +2230 -1
index.html CHANGED
@@ -720,4 +720,2233 @@
720
  }
721
 
722
  .side-panel {
723
- posi
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
720
  }
721
 
722
  .side-panel {
723
+ position: fixed;
724
+ top: 0;
725
+ height: 100vh;
726
+ transform: translateX(-100%);
727
+ z-index: 1000;
728
+ }
729
+
730
+ .side-panel.left {
731
+ left: 0;
732
+ }
733
+
734
+ .side-panel.right {
735
+ right: 0;
736
+ transform: translateX(100%);
737
+ }
738
+
739
+ .side-panel.active {
740
+ transform: translateX(0);
741
+ }
742
+
743
+ .mobile-nav {
744
+ display: block;
745
+ }
746
+
747
+ .main-content {
748
+ padding: 80px 20px 20px 20px;
749
+ }
750
+
751
+ #canvasContainer {
752
+ height: 50vh;
753
+ }
754
+
755
+ .chat-interface {
756
+ margin-top: 20px;
757
+ }
758
+
759
+ .chat-messages {
760
+ height: 250px;
761
+ }
762
+ }
763
+
764
+ @media (max-width: 768px) {
765
+ #canvasContainer {
766
+ height: 40vh;
767
+ }
768
+
769
+ .chat-messages {
770
+ height: 200px;
771
+ }
772
+
773
+ .side-panel {
774
+ width: 100%;
775
+ max-width: 350px;
776
+ }
777
+ }
778
+
779
+ @media (max-width: 480px) {
780
+ #canvasContainer {
781
+ height: 35vh;
782
+ }
783
+
784
+ .chat-interface {
785
+ margin-top: 15px;
786
+ }
787
+
788
+ .chat-messages {
789
+ height: 180px;
790
+ padding: 15px;
791
+ }
792
+
793
+ .chat-input-container {
794
+ padding: 15px;
795
+ }
796
+
797
+ .chat-input, .voice-input-btn, .send-btn {
798
+ min-height: 48px;
799
+ height: 48px;
800
+ }
801
+ }
802
+ </style>
803
+ </head>
804
+ <body>
805
+ <!-- Mobile Navigation -->
806
+ <div class="mobile-nav">
807
+ <div class="mobile-menu-btn" id="mobileMenuBtn">
808
+ <span></span>
809
+ <span></span>
810
+ <span></span>
811
+ </div>
812
+ </div>
813
+
814
+ <div class="mobile-panel-overlay" id="mobileOverlay"></div>
815
+
816
+ <!-- Loading Screen -->
817
+ <div class="loading-screen" id="loadingScreen">
818
+ <div class="spinner"></div>
819
+ <div id="loadingText">Initializing Neural Network Interface...</div>
820
+ </div>
821
+
822
+ <!-- Error Screen -->
823
+ <div class="error-screen" id="errorScreen">
824
+ <h2>⚠️ Initialization Failed</h2>
825
+ <p id="errorMessage">Three.js library failed to load. Please check your internet connection or try again.</p>
826
+ <button class="retry-btn" id="retryBtn">Retry</button>
827
+ </div>
828
+
829
+ <!-- Main Container -->
830
+ <div class="main-container">
831
+ <!-- Left Side Panel -->
832
+ <aside class="side-panel left">
833
+ <div class="card">
834
+ <div class="card-title">
835
+ <i class="fas fa-sliders-h"></i>
836
+ <span>Animation Controls</span>
837
+ </div>
838
+
839
+ <div class="accordion">
840
+ <div class="accordion-header" data-target="animationControls">
841
+ <span>Particle Settings</span>
842
+ <i class="fas fa-chevron-down"></i>
843
+ </div>
844
+ <div class="accordion-content" id="animationControls">
845
+ <div class="control-group">
846
+ <label>Animation Intensity</label>
847
+ <div class="slider-container">
848
+ <input type="range" id="intensity" min="0" max="100" value="50">
849
+ <span class="value-display" id="intensityValue">50</span>
850
+ </div>
851
+ </div>
852
+
853
+ <div class="control-group">
854
+ <label>Particle Count</label>
855
+ <div class="slider-container">
856
+ <input type="range" id="particleCount" min="100" max="5000" value="2000" step="100">
857
+ <span class="value-display" id="particleCountValue">2000</span>
858
+ </div>
859
+ </div>
860
+
861
+ <div class="control-group">
862
+ <label>Energy Level</label>
863
+ <div class="slider-container">
864
+ <input type="range" id="energy" min="0" max="100" value="30">
865
+ <span class="value-display" id="energyValue">30</span>
866
+ </div>
867
+ </div>
868
+
869
+ <div class="button-grid">
870
+ <button class="preset-btn" data-preset="listening">🎤 Listening</button>
871
+ <button class="preset-btn" data-preset="processing">⚡ Processing</button>
872
+ <button class="preset-btn" data-preset="responding">💬 Responding</button>
873
+ <button class="preset-btn" data-preset="exploring">🔍 Exploring</button>
874
+ <button class="preset-btn" data-preset="teaching">📚 Teaching</button>
875
+ <button class="preset-btn" data-preset="idle">🌀 Idle</button>
876
+ </div>
877
+ </div>
878
+ </div>
879
+ </div>
880
+
881
+ <div class="card">
882
+ <div class="card-title">
883
+ <i class="fas fa-graduation-cap"></i>
884
+ <span>AI Training</span>
885
+ </div>
886
+
887
+ <div class="accordion">
888
+ <div class="accordion-header" data-target="trainingControls">
889
+ <span>RAG Training</span>
890
+ <i class="fas fa-chevron-down"></i>
891
+ </div>
892
+ <div class="accordion-content" id="trainingControls">
893
+ <textarea class="rag-textarea" id="knowledgeText" placeholder="Add knowledge for AI training..."></textarea>
894
+
895
+ <div class="rag-controls">
896
+ <button class="rag-btn" id="addKnowledgeBtn">
897
+ <i class="fas fa-plus"></i>
898
+ Add Text
899
+ </button>
900
+ <button class="rag-btn primary" id="trainBtn">
901
+ <i class="fas fa-brain"></i>
902
+ Train AI
903
+ </button>
904
+ </div>
905
+
906
+ <div class="rag-progress">
907
+ <div class="rag-progress-bar" id="trainingProgress"></div>
908
+ </div>
909
+
910
+ <div class="rag-status" id="ragStatus" style="display: none;">
911
+ <i class="fas fa-sync-alt fa-spin"></i>
912
+ <span id="ragStatusText">Training in progress...</span>
913
+ </div>
914
+ </div>
915
+ </div>
916
+ </div>
917
+ </aside>
918
+
919
+ <!-- Main Content -->
920
+ <main class="main-content">
921
+ <div id="canvasContainer">
922
+ <canvas id="mainCanvas"></canvas>
923
+ </div>
924
+
925
+ <div class="chat-interface">
926
+ <div class="chat-container">
927
+ <div class="chat-header">
928
+ <div class="chat-title">
929
+ <i class="fas fa-robot"></i>
930
+ <span>AI Tutor Assistant</span>
931
+ </div>
932
+ <div class="chat-status">
933
+ <span class="status-indicator"></span>
934
+ <span id="chatStatusText">Online</span>
935
+ </div>
936
+ </div>
937
+
938
+ <div class="chat-messages" id="chatMessages">
939
+ <!-- Messages will be dynamically added here -->
940
+ <div class="message message-ai">
941
+ <div class="message-content">
942
+ Hello! I'm your AI Tutor. I can help explain concepts, answer questions, and guide your learning. How can I assist you today?
943
+ </div>
944
+ <div class="message-time">Just now</div>
945
+ </div>
946
+
947
+ <div class="message message-ai">
948
+ <div class="message-content">
949
+ You can type your questions or click the microphone to speak. I'll respond with explanations, examples, and follow-up questions to enhance your understanding.
950
+ </div>
951
+ <div class="message-time">Just now</div>
952
+ </div>
953
+ </div>
954
+
955
+ <div class="chat-input-container">
956
+ <div class="input-wrapper">
957
+ <textarea
958
+ class="chat-input"
959
+ id="chatInput"
960
+ placeholder="Type your question or click the microphone to speak..."
961
+ rows="1"
962
+ ></textarea>
963
+
964
+ <button class="voice-input-btn" id="voiceBtn" title="Start voice input">
965
+ <i class="fas fa-microphone"></i>
966
+ </button>
967
+
968
+ <button class="send-btn" id="sendBtn" title="Send message">
969
+ <i class="fas fa-paper-plane"></i>
970
+ </button>
971
+ </div>
972
+ </div>
973
+ </div>
974
+ </div>
975
+ </main>
976
+
977
+ <!-- Right Side Panel -->
978
+ <aside class="side-panel right">
979
+ <div class="card">
980
+ <div class="card-title">
981
+ <i class="fas fa-robot"></i>
982
+ <span>Voice Synthesis</span>
983
+ </div>
984
+
985
+ <div class="accordion">
986
+ <div class="accordion-header" data-target="voiceControls">
987
+ <span>Voice Settings</span>
988
+ <i class="fas fa-chevron-down"></i>
989
+ </div>
990
+ <div class="accordion-content" id="voiceControls">
991
+ <div class="control-group">
992
+ <label>Voice Selection</label>
993
+ <select class="voice-select" id="voiceSelect">
994
+ <option value="">Loading voices...</option>
995
+ </select>
996
+ </div>
997
+
998
+ <div class="control-group">
999
+ <label>Speech Rate</label>
1000
+ <div class="slider-container">
1001
+ <input type="range" class="voice-slider" id="rateSlider" min="0.5" max="2" step="0.1" value="1">
1002
+ <span class="value-display" id="rateValue">1.0x</span>
1003
+ </div>
1004
+ </div>
1005
+
1006
+ <div class="control-group">
1007
+ <label>Pitch Variation</label>
1008
+ <div class="slider-container">
1009
+ <input type="range" class="voice-slider" id="pitchSlider" min="0.5" max="2" step="0.1" value="1">
1010
+ <span class="value-display" id="pitchValue">1.0</span>
1011
+ </div>
1012
+ </div>
1013
+
1014
+ <div class="control-group">
1015
+ <label>Volume Level</label>
1016
+ <div class="slider-container">
1017
+ <input type="range" class="voice-slider" id="volumeSlider" min="0.1" max="1" step="0.1" value="0.8">
1018
+ <span class="value-display" id="volumeValue">80%</span>
1019
+ </div>
1020
+ </div>
1021
+
1022
+ <div class="voice-control-buttons">
1023
+ <button class="voice-btn" id="autoSpeakBtn" data-enabled="true">
1024
+ <i class="fas fa-bullhorn"></i>
1025
+ Auto-Speak
1026
+ </button>
1027
+ <button class="voice-btn stop" id="stopBtn">
1028
+ <i class="fas fa-stop"></i>
1029
+ Stop
1030
+ </button>
1031
+ </div>
1032
+
1033
+ <button class="voice-btn" id="previewBtn" style="width: 100%; margin-top: 10px;">
1034
+ <i class="fas fa-play"></i>
1035
+ Preview Voice
1036
+ </button>
1037
+ </div>
1038
+ </div>
1039
+ </div>
1040
+
1041
+ <div class="card">
1042
+ <div class="card-title">
1043
+ <i class="fas fa-plug"></i>
1044
+ <span>LLM Integration</span>
1045
+ </div>
1046
+
1047
+ <div class="accordion">
1048
+ <div class="accordion-header" data-target="llmControls">
1049
+ <span>API Settings</span>
1050
+ <i class="fas fa-chevron-down"></i>
1051
+ </div>
1052
+ <div class="accordion-content" id="llmControls">
1053
+ <div class="control-group">
1054
+ <label>Primary LLM API</label>
1055
+ <select class="voice-select" id="llmProvider">
1056
+ <option value="huggingface">Hugging Face</option>
1057
+ <option value="openai">OpenAI</option>
1058
+ <option value="anthropic">Anthropic Claude</option>
1059
+ <option value="google">Google Gemini</option>
1060
+ <option value="azure">Azure OpenAI</option>
1061
+ <option value="local">Local API</option>
1062
+ </select>
1063
+ </div>
1064
+
1065
+ <div class="control-group">
1066
+ <label>API Endpoint</label>
1067
+ <input type="text" class="rag-textarea" id="apiEndpoint" placeholder="https://api-inference.huggingface.co/models/microsoft/DialoGPT-medium">
1068
+ </div>
1069
+
1070
+ <div class="control-group">
1071
+ <label>API Key</label>
1072
+ <input type="password" class="rag-textarea" id="apiKey" placeholder="Enter your API key">
1073
+ </div>
1074
+
1075
+ <div class="control-group">
1076
+ <label>Model Name</label>
1077
+ <input type="text" class="rag-textarea" id="modelName" placeholder="microsoft/DialoGPT-medium">
1078
+ </div>
1079
+
1080
+ <div class="rag-controls">
1081
+ <button class="rag-btn" id="testApiBtn">
1082
+ <i class="fas fa-wifi"></i>
1083
+ Test Connection
1084
+ </button>
1085
+ <button class="rag-btn primary" id="saveApiBtn">
1086
+ <i class="fas fa-save"></i>
1087
+ Save Settings
1088
+ </button>
1089
+ </div>
1090
+
1091
+ <div class="rag-status" id="apiStatus" style="display: none;">
1092
+ <i class="fas fa-sync-alt fa-spin"></i>
1093
+ <span id="apiStatusText">Testing connection...</span>
1094
+ </div>
1095
+ </div>
1096
+ </div>
1097
+ </div>
1098
+ </aside>
1099
+ </div>
1100
+
1101
+ <script>
1102
+ // Check if Three.js loaded properly
1103
+ function checkThreeJS() {
1104
+ if (typeof THREE === 'undefined') {
1105
+ console.error('Three.js failed to load');
1106
+ showError('Three.js library failed to load. Please check your internet connection.');
1107
+ return false;
1108
+ }
1109
+ return true;
1110
+ }
1111
+
1112
+ function showError(message) {
1113
+ const errorScreen = document.getElementById('errorScreen');
1114
+ const errorMessage = document.getElementById('errorMessage');
1115
+ const loadingScreen = document.getElementById('loadingScreen');
1116
+
1117
+ loadingScreen.style.display = 'none';
1118
+ errorMessage.textContent = message;
1119
+ errorScreen.style.display = 'flex';
1120
+ }
1121
+
1122
+ function hideLoading() {
1123
+ const loadingScreen = document.getElementById('loadingScreen');
1124
+ loadingScreen.style.opacity = '0';
1125
+ setTimeout(() => {
1126
+ loadingScreen.style.display = 'none';
1127
+ }, 500);
1128
+ }
1129
+
1130
+ function updateStatus(text) {
1131
+ document.getElementById('loadingText').textContent = text;
1132
+ }
1133
+
1134
+ // ==================== UPDATED NEURAL WEB VISUALIZATION ====================
1135
+ class AITutorVisualization {
1136
+ constructor() {
1137
+ if (!checkThreeJS()) return;
1138
+
1139
+ this.scene = null;
1140
+ this.camera = null;
1141
+ this.renderer = null;
1142
+ this.particles = null;
1143
+ this.lines = null;
1144
+ this.controls = null;
1145
+
1146
+ this.params = {
1147
+ intensity: 0.5,
1148
+ particleCount: 2000,
1149
+ energy: 0.3,
1150
+ mode: 'idle'
1151
+ };
1152
+
1153
+ this.audioData = new Array(32).fill(0);
1154
+ this.time = 0;
1155
+ this.animationFrameId = null;
1156
+ this.particlePositions = [];
1157
+
1158
+ try {
1159
+ this.init();
1160
+ this.createNeuralWeb();
1161
+ this.setupControls();
1162
+ this.animate();
1163
+ updateStatus('Neural Network Interface Ready');
1164
+ setTimeout(hideLoading, 500);
1165
+ } catch (error) {
1166
+ console.error('Initialization error:', error);
1167
+ showError('Failed to initialize 3D scene: ' + error.message);
1168
+ }
1169
+ }
1170
+
1171
+ init() {
1172
+ updateStatus('Creating Neural Network...');
1173
+
1174
+ this.scene = new THREE.Scene();
1175
+ this.scene.background = new THREE.Color(0x0a0a0f);
1176
+
1177
+ this.camera = new THREE.PerspectiveCamera(
1178
+ 60,
1179
+ window.innerWidth / window.innerHeight,
1180
+ 0.1,
1181
+ 1000
1182
+ );
1183
+ this.camera.position.set(0, 0, 5);
1184
+
1185
+ const canvas = document.getElementById('mainCanvas');
1186
+ this.renderer = new THREE.WebGLRenderer({
1187
+ canvas: canvas,
1188
+ antialias: true,
1189
+ alpha: true
1190
+ });
1191
+ this.renderer.setSize(canvas.parentElement.clientWidth, canvas.parentElement.clientHeight);
1192
+ this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
1193
+
1194
+ // Lighting
1195
+ const ambientLight = new THREE.AmbientLight(0x222244, 0.6);
1196
+ this.scene.add(ambientLight);
1197
+
1198
+ const directionalLight = new THREE.DirectionalLight(0x5a6cff, 0.8);
1199
+ directionalLight.position.set(3, 2, 3);
1200
+ this.scene.add(directionalLight);
1201
+
1202
+ const pointLight = new THREE.PointLight(0x00ff9d, 0.4, 10);
1203
+ pointLight.position.set(-2, -1, 2);
1204
+ this.scene.add(pointLight);
1205
+
1206
+ this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
1207
+ this.controls.enableDamping = true;
1208
+ this.controls.dampingFactor = 0.05;
1209
+ this.controls.maxDistance = 10;
1210
+ this.controls.minDistance = 2;
1211
+ }
1212
+
1213
+ createNeuralWeb() {
1214
+ // Remove existing objects if recreating
1215
+ if (this.particles) this.scene.remove(this.particles);
1216
+ if (this.lines) this.scene.remove(this.lines);
1217
+ if (this.core) this.scene.remove(this.core);
1218
+
1219
+ // Create particles
1220
+ const particleGeometry = new THREE.BufferGeometry();
1221
+ const positions = new Float32Array(this.params.particleCount * 3);
1222
+ const colors = new Float32Array(this.params.particleCount * 3);
1223
+
1224
+ this.particlePositions = [];
1225
+
1226
+ // Create an organic neural network structure
1227
+ const layers = 6;
1228
+ const particlesPerLayer = this.params.particleCount / layers;
1229
+
1230
+ for (let i = 0; i < this.params.particleCount; i++) {
1231
+ const i3 = i * 3;
1232
+ const layer = Math.floor(i / particlesPerLayer);
1233
+ const layerProgress = layer / layers;
1234
+
1235
+ // Spherical coordinates with some noise
1236
+ const radius = 1.5 + layerProgress * 0.8;
1237
+ const theta = Math.random() * Math.PI * 2;
1238
+ const phi = Math.acos(2 * Math.random() - 1);
1239
+
1240
+ // Add organic noise
1241
+ const noise = 0.3;
1242
+ const x = radius * Math.sin(phi) * Math.cos(theta) + (Math.random() - 0.5) * noise;
1243
+ const y = radius * Math.sin(phi) * Math.sin(theta) + (Math.random() - 0.5) * noise;
1244
+ const z = radius * Math.cos(phi) + (Math.random() - 0.5) * noise;
1245
+
1246
+ positions[i3] = x;
1247
+ positions[i3 + 1] = y;
1248
+ positions[i3 + 2] = z;
1249
+
1250
+ this.particlePositions.push(new THREE.Vector3(x, y, z));
1251
+
1252
+ // Color based on position and layer
1253
+ const hue = 0.6 + (layer / layers) * 0.2; // Blue to purple gradient
1254
+ const saturation = 0.8 - layerProgress * 0.3;
1255
+ const brightness = 0.7 + (y / 3) * 0.3;
1256
+
1257
+ colors[i3] = hue;
1258
+ colors[i3 + 1] = saturation;
1259
+ colors[i3 + 2] = brightness;
1260
+ }
1261
+
1262
+ particleGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
1263
+ particleGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
1264
+
1265
+ const particleMaterial = new THREE.PointsMaterial({
1266
+ size: 0.03,
1267
+ vertexColors: true,
1268
+ transparent: true,
1269
+ opacity: 0.9,
1270
+ blending: THREE.AdditiveBlending,
1271
+ sizeAttenuation: true
1272
+ });
1273
+
1274
+ this.particles = new THREE.Points(particleGeometry, particleMaterial);
1275
+ this.scene.add(this.particles);
1276
+
1277
+ // Create neural connections
1278
+ this.createNeuralConnections();
1279
+
1280
+ // Add central core
1281
+ const coreGeometry = new THREE.IcosahedronGeometry(0.4, 1);
1282
+ const coreMaterial = new THREE.MeshBasicMaterial({
1283
+ color: 0x5a6cff,
1284
+ wireframe: true,
1285
+ transparent: true,
1286
+ opacity: 0.3
1287
+ });
1288
+ this.core = new THREE.Mesh(coreGeometry, coreMaterial);
1289
+ this.scene.add(this.core);
1290
+ }
1291
+
1292
+ createNeuralConnections() {
1293
+ const lineGeometry = new THREE.BufferGeometry();
1294
+ const linePositions = [];
1295
+ const lineColors = [];
1296
+
1297
+ // Connect nearby particles
1298
+ const connectionDistance = 1.8;
1299
+ const maxConnections = 3;
1300
+
1301
+ for (let i = 0; i < this.params.particleCount; i++) {
1302
+ const source = this.particlePositions[i];
1303
+ let connections = 0;
1304
+
1305
+ for (let j = i + 1; j < this.params.particleCount && connections < maxConnections; j++) {
1306
+ const target = this.particlePositions[j];
1307
+ const distance = source.distanceTo(target);
1308
+
1309
+ if (distance < connectionDistance) {
1310
+ // Add line from source to target
1311
+ linePositions.push(source.x, source.y, source.z);
1312
+ linePositions.push(target.x, target.y, target.z);
1313
+
1314
+ // Line colors with gradient
1315
+ const intensity = 1 - (distance / connectionDistance);
1316
+ const color1 = new THREE.Color(0x5a6cff);
1317
+ const color2 = new THREE.Color(0x00ff9d);
1318
+
1319
+ lineColors.push(color1.r, color1.g, color1.b, intensity * 0.6);
1320
+ lineColors.push(color2.r, color2.g, color2.b, intensity * 0.6);
1321
+
1322
+ connections++;
1323
+ }
1324
+ }
1325
+ }
1326
+
1327
+ lineGeometry.setAttribute('position', new THREE.Float32BufferAttribute(linePositions, 3));
1328
+ lineGeometry.setAttribute('color', new THREE.Float32BufferAttribute(lineColors, 4));
1329
+
1330
+ const lineMaterial = new THREE.LineBasicMaterial({
1331
+ vertexColors: true,
1332
+ transparent: true,
1333
+ opacity: 0.3,
1334
+ linewidth: 1
1335
+ });
1336
+
1337
+ this.lines = new THREE.LineSegments(lineGeometry, lineMaterial);
1338
+ this.scene.add(this.lines);
1339
+ }
1340
+
1341
+ setupControls() {
1342
+ const intensitySlider = document.getElementById('intensity');
1343
+ const particleCountSlider = document.getElementById('particleCount');
1344
+ const energySlider = document.getElementById('energy');
1345
+
1346
+ const intensityValue = document.getElementById('intensityValue');
1347
+ const particleCountValue = document.getElementById('particleCountValue');
1348
+ const energyValue = document.getElementById('energyValue');
1349
+
1350
+ // Set initial values
1351
+ intensityValue.textContent = intensitySlider.value;
1352
+ particleCountValue.textContent = particleCountSlider.value;
1353
+ energyValue.textContent = energySlider.value;
1354
+
1355
+ intensitySlider.addEventListener('input', (e) => {
1356
+ this.params.intensity = e.target.value / 100;
1357
+ intensityValue.textContent = e.target.value;
1358
+ });
1359
+
1360
+ particleCountSlider.addEventListener('input', (e) => {
1361
+ this.params.particleCount = parseInt(e.target.value);
1362
+ particleCountValue.textContent = e.target.value;
1363
+ this.updateParticleCount();
1364
+ });
1365
+
1366
+ energySlider.addEventListener('input', (e) => {
1367
+ this.params.energy = e.target.value / 100;
1368
+ energyValue.textContent = e.target.value;
1369
+ });
1370
+
1371
+ // Preset buttons
1372
+ document.querySelectorAll('.preset-btn').forEach(btn => {
1373
+ btn.addEventListener('click', (e) => {
1374
+ const preset = e.target.dataset.preset;
1375
+ this.setPreset(preset);
1376
+ });
1377
+ });
1378
+
1379
+ document.getElementById('retryBtn').addEventListener('click', () => {
1380
+ location.reload();
1381
+ });
1382
+
1383
+ // Set initial preset
1384
+ this.setPreset('idle');
1385
+ }
1386
+
1387
+ setPreset(preset) {
1388
+ this.params.mode = preset;
1389
+
1390
+ // Update button styles
1391
+ document.querySelectorAll('.preset-btn').forEach(btn => {
1392
+ btn.style.background = btn.dataset.preset === preset
1393
+ ? 'rgba(90, 108, 255, 0.4)'
1394
+ : 'rgba(90, 108, 255, 0.1)';
1395
+ btn.style.transform = btn.dataset.preset === preset
1396
+ ? 'translateY(-2px)'
1397
+ : 'translateY(0)';
1398
+ });
1399
+
1400
+ // Preset parameters
1401
+ const presets = {
1402
+ 'listening': { intensity: 70, energy: 60, particleCount: 2200 },
1403
+ 'processing': { intensity: 85, energy: 80, particleCount: 2500 },
1404
+ 'responding': { intensity: 75, energy: 70, particleCount: 2300 },
1405
+ 'exploring': { intensity: 60, energy: 50, particleCount: 2100 },
1406
+ 'teaching': { intensity: 65, energy: 55, particleCount: 2200 },
1407
+ 'idle': { intensity: 50, energy: 30, particleCount: 2000 }
1408
+ };
1409
+
1410
+ if (presets[preset]) {
1411
+ const presetValues = presets[preset];
1412
+
1413
+ document.getElementById('intensity').value = presetValues.intensity;
1414
+ document.getElementById('energy').value = presetValues.energy;
1415
+ document.getElementById('particleCount').value = presetValues.particleCount;
1416
+
1417
+ document.getElementById('intensityValue').textContent = presetValues.intensity;
1418
+ document.getElementById('energyValue').textContent = presetValues.energy;
1419
+ document.getElementById('particleCountValue').textContent = presetValues.particleCount;
1420
+
1421
+ this.params.intensity = presetValues.intensity / 100;
1422
+ this.params.energy = presetValues.energy / 100;
1423
+
1424
+ if (this.params.particleCount !== presetValues.particleCount) {
1425
+ this.params.particleCount = presetValues.particleCount;
1426
+ this.updateParticleCount();
1427
+ }
1428
+ }
1429
+ }
1430
+
1431
+ updateParticleCount() {
1432
+ this.createNeuralWeb();
1433
+ }
1434
+
1435
+ simulateAudioData() {
1436
+ const time = performance.now() * 0.001;
1437
+ const frequency = 0.5 + this.params.energy * 2;
1438
+
1439
+ for (let i = 0; i < this.audioData.length; i++) {
1440
+ const base = Math.sin(time * frequency + i * 0.3) * 0.5 + 0.5;
1441
+ const variation = Math.sin(time * 2 + i * 0.5) * 0.2;
1442
+ this.audioData[i] = base + variation * this.params.intensity;
1443
+ }
1444
+ }
1445
+
1446
+ updateParticles() {
1447
+ if (!this.particles) return;
1448
+
1449
+ const time = this.time;
1450
+ const positions = this.particles.geometry.attributes.position.array;
1451
+ const colors = this.particles.geometry.attributes.color.array;
1452
+
1453
+ // Update particles
1454
+ for (let i = 0; i < this.params.particleCount; i++) {
1455
+ const i3 = i * 3;
1456
+
1457
+ let wave = 0;
1458
+ switch(this.params.mode) {
1459
+ case 'listening':
1460
+ wave = Math.sin(time * 2 + i * 0.01) * 0.1;
1461
+ break;
1462
+ case 'processing':
1463
+ wave = Math.sin(time * 3 + i * 0.02) * 0.15;
1464
+ break;
1465
+ case 'responding':
1466
+ wave = (Math.sin(time * 1.5 + i * 0.008) + 1) * 0.08;
1467
+ break;
1468
+ case 'exploring':
1469
+ wave = Math.sin(time * 1.2 + i * 0.015) * 0.12;
1470
+ break;
1471
+ case 'teaching':
1472
+ wave = Math.sin(time * 2.5 + i * 0.01) * 0.1;
1473
+ break;
1474
+ default: // idle
1475
+ wave = Math.sin(time * 0.5 + i * 0.005) * 0.05;
1476
+ }
1477
+
1478
+ // Apply movement with intensity and energy
1479
+ const movement = wave * this.params.intensity * this.params.energy;
1480
+
1481
+ // Update position with organic movement
1482
+ positions[i3] += Math.sin(time * 0.7 + i) * 0.002 * movement;
1483
+ positions[i3 + 1] += Math.cos(time * 0.5 + i) * 0.002 * movement;
1484
+ positions[i3 + 2] += Math.sin(time * 0.3 + i) * 0.002 * movement;
1485
+
1486
+ // Update color based on activity
1487
+ const activity = Math.abs(wave) * this.params.intensity;
1488
+ colors[i3] = 0.6 + activity * 0.2; // Hue shift
1489
+ colors[i3 + 1] = 0.8 - activity * 0.2; // Saturation
1490
+ colors[i3 + 2] = 0.7 + Math.sin(time * 2 + i * 0.1) * 0.1; // Brightness pulse
1491
+
1492
+ // Update stored positions
1493
+ if (this.particlePositions[i]) {
1494
+ this.particlePositions[i].set(positions[i3], positions[i3 + 1], positions[i3 + 2]);
1495
+ }
1496
+ }
1497
+
1498
+ this.particles.geometry.attributes.position.needsUpdate = true;
1499
+ this.particles.geometry.attributes.color.needsUpdate = true;
1500
+
1501
+ // Update lines (simplified - just update opacity)
1502
+ if (this.lines) {
1503
+ const lineColors = this.lines.geometry.attributes.color.array;
1504
+
1505
+ for (let i = 0; i < lineColors.length; i += 8) {
1506
+ const pulse = Math.sin(time * 2 + i * 0.01) * 0.2 + 0.5;
1507
+ lineColors[i + 3] = pulse * this.params.intensity * 0.6;
1508
+ lineColors[i + 7] = pulse * this.params.intensity * 0.6;
1509
+ }
1510
+
1511
+ this.lines.geometry.attributes.color.needsUpdate = true;
1512
+ }
1513
+
1514
+ // Update core
1515
+ if (this.core) {
1516
+ this.core.rotation.y = time * 0.2;
1517
+ this.core.rotation.x = time * 0.1;
1518
+
1519
+ // Pulsing core
1520
+ const scale = 1 + Math.sin(time * 3) * 0.1;
1521
+ this.core.scale.setScalar(scale);
1522
+ }
1523
+
1524
+ // Gentle rotation of entire system
1525
+ this.particles.rotation.y = time * 0.03;
1526
+ this.particles.rotation.x = Math.sin(time * 0.02) * 0.02;
1527
+ }
1528
+
1529
+ animate() {
1530
+ this.animationFrameId = requestAnimationFrame(() => this.animate());
1531
+
1532
+ this.time += 0.016;
1533
+
1534
+ this.simulateAudioData();
1535
+ this.updateParticles();
1536
+
1537
+ this.controls.update();
1538
+ this.renderer.render(this.scene, this.camera);
1539
+ }
1540
+
1541
+ onWindowResize() {
1542
+ const canvasContainer = document.getElementById('canvasContainer');
1543
+ const width = canvasContainer.clientWidth;
1544
+ const height = canvasContainer.clientHeight;
1545
+
1546
+ this.camera.aspect = width / height;
1547
+ this.camera.updateProjectionMatrix();
1548
+ this.renderer.setSize(width, height);
1549
+ }
1550
+
1551
+ destroy() {
1552
+ if (this.animationFrameId) {
1553
+ cancelAnimationFrame(this.animationFrameId);
1554
+ }
1555
+ if (this.controls) {
1556
+ this.controls.dispose();
1557
+ }
1558
+ if (this.renderer) {
1559
+ this.renderer.dispose();
1560
+ }
1561
+ }
1562
+ }
1563
+
1564
+ // ==================== ALL ORIGINAL CODE BELOW REMAINS EXACTLY THE SAME ====================
1565
+ // Mobile Menu Functionality
1566
+ class MobileMenu {
1567
+ constructor() {
1568
+ this.menuBtn = document.getElementById('mobileMenuBtn');
1569
+ this.overlay = document.getElementById('mobileOverlay');
1570
+ this.leftPanel = document.querySelector('.side-panel.left');
1571
+ this.rightPanel = document.querySelector('.side-panel.right');
1572
+ this.isOpen = false;
1573
+ this.currentPanel = null;
1574
+
1575
+ this.init();
1576
+ }
1577
+
1578
+ init() {
1579
+ this.menuBtn.addEventListener('click', () => this.toggleMenu());
1580
+ this.overlay.addEventListener('click', () => this.closeMenu());
1581
+
1582
+ document.addEventListener('keydown', (e) => {
1583
+ if (e.key === 'Escape') {
1584
+ this.closeMenu();
1585
+ }
1586
+ });
1587
+ }
1588
+
1589
+ toggleMenu() {
1590
+ if (!this.isOpen) {
1591
+ this.openMenu('left');
1592
+ } else {
1593
+ this.closeMenu();
1594
+ }
1595
+ }
1596
+
1597
+ openMenu(panel) {
1598
+ this.isOpen = true;
1599
+ this.currentPanel = panel;
1600
+ this.menuBtn.classList.add('active');
1601
+
1602
+ if (panel === 'left') {
1603
+ this.leftPanel.classList.add('active');
1604
+ } else if (panel === 'right') {
1605
+ this.rightPanel.classList.add('active');
1606
+ }
1607
+
1608
+ this.overlay.style.display = 'block';
1609
+ setTimeout(() => {
1610
+ this.overlay.style.opacity = '1';
1611
+ }, 10);
1612
+ }
1613
+
1614
+ closeMenu() {
1615
+ this.isOpen = false;
1616
+ this.menuBtn.classList.remove('active');
1617
+ this.leftPanel.classList.remove('active');
1618
+ this.rightPanel.classList.remove('active');
1619
+
1620
+ this.overlay.style.opacity = '0';
1621
+ setTimeout(() => {
1622
+ this.overlay.style.display = 'none';
1623
+ }, 300);
1624
+ }
1625
+ }
1626
+
1627
+ // Accordion Functionality
1628
+ class Accordion {
1629
+ constructor() {
1630
+ this.accordions = document.querySelectorAll('.accordion-header');
1631
+ this.init();
1632
+ }
1633
+
1634
+ init() {
1635
+ this.accordions.forEach(accordion => {
1636
+ accordion.addEventListener('click', () => {
1637
+ const targetId = accordion.getAttribute('data-target');
1638
+ const content = document.getElementById(targetId);
1639
+ const icon = accordion.querySelector('i');
1640
+
1641
+ accordion.classList.toggle('active');
1642
+ content.classList.toggle('active');
1643
+
1644
+ if (accordion.classList.contains('active')) {
1645
+ icon.classList.remove('fa-chevron-down');
1646
+ icon.classList.add('fa-chevron-up');
1647
+ } else {
1648
+ icon.classList.remove('fa-chevron-up');
1649
+ icon.classList.add('fa-chevron-down');
1650
+ }
1651
+ });
1652
+ });
1653
+ }
1654
+ }
1655
+
1656
+ // RAG AI Tutor System
1657
+ class RAGAITutor {
1658
+ constructor() {
1659
+ this.knowledgeBase = [];
1660
+ this.vectorStore = null;
1661
+ this.isTraining = false;
1662
+ this.currentSession = null;
1663
+ this.init();
1664
+ }
1665
+
1666
+ init() {
1667
+ this.setupEventListeners();
1668
+ this.loadSampleKnowledge();
1669
+ }
1670
+
1671
+ loadSampleKnowledge() {
1672
+ this.addKnowledgeItem({
1673
+ id: Date.now(),
1674
+ title: "Physics Basics",
1675
+ content: "Newton's laws of motion: 1) An object at rest stays at rest unless acted upon by a force. 2) Force equals mass times acceleration. 3) For every action, there is an equal and opposite reaction.",
1676
+ type: "text",
1677
+ timestamp: new Date().toISOString()
1678
+ });
1679
+
1680
+ this.addKnowledgeItem({
1681
+ id: Date.now() + 1,
1682
+ title: "Calculus Fundamentals",
1683
+ content: "The derivative measures the rate of change of a function. The integral measures the accumulation of a quantity. The Fundamental Theorem of Calculus connects derivatives and integrals.",
1684
+ type: "text",
1685
+ timestamp: new Date().toISOString()
1686
+ });
1687
+ }
1688
+
1689
+ setupEventListeners() {
1690
+ document.getElementById('addKnowledgeBtn').addEventListener('click', () => {
1691
+ this.addKnowledgeFromText();
1692
+ });
1693
+
1694
+ document.getElementById('trainBtn').addEventListener('click', () => {
1695
+ this.trainRAGModel();
1696
+ });
1697
+ }
1698
+
1699
+ addKnowledgeFromText() {
1700
+ const textArea = document.getElementById('knowledgeText');
1701
+ const content = textArea.value.trim();
1702
+
1703
+ if (!content) {
1704
+ this.showRAGStatus('Please enter some knowledge content', 'warning');
1705
+ return;
1706
+ }
1707
+
1708
+ const title = content.substring(0, 50) + (content.length > 50 ? '...' : '');
1709
+
1710
+ const knowledgeItem = {
1711
+ id: Date.now(),
1712
+ title: title,
1713
+ content: content,
1714
+ type: "text",
1715
+ timestamp: new Date().toISOString()
1716
+ };
1717
+
1718
+ this.addKnowledgeItem(knowledgeItem);
1719
+ textArea.value = '';
1720
+ this.showRAGStatus('Knowledge added successfully!', 'success');
1721
+ }
1722
+
1723
+ addKnowledgeItem(item) {
1724
+ this.knowledgeBase.push(item);
1725
+ }
1726
+
1727
+ async trainRAGModel() {
1728
+ if (this.knowledgeBase.length === 0) {
1729
+ this.showRAGStatus('No knowledge to train on. Please add some content first.', 'warning');
1730
+ return;
1731
+ }
1732
+
1733
+ this.isTraining = true;
1734
+ this.showRAGStatus('Starting RAG training...', 'processing');
1735
+
1736
+ const progressBar = document.getElementById('trainingProgress');
1737
+ progressBar.style.width = '0%';
1738
+
1739
+ const steps = ['Preprocessing text', 'Creating embeddings', 'Building vector store', 'Optimizing retrieval'];
1740
+
1741
+ for (let i = 0; i < steps.length; i++) {
1742
+ this.showRAGStatus(steps[i], 'processing');
1743
+ progressBar.style.width = `${((i + 1) / steps.length) * 100}%`;
1744
+
1745
+ await this.sleep(1000);
1746
+ }
1747
+
1748
+ this.vectorStore = {
1749
+ size: this.knowledgeBase.length,
1750
+ trainedAt: new Date().toISOString(),
1751
+ model: 'all-MiniLM-L6-v2'
1752
+ };
1753
+
1754
+ this.isTraining = false;
1755
+ this.showRAGStatus(`RAG training complete! ${this.knowledgeBase.length} documents indexed.`, 'success');
1756
+
1757
+ setTimeout(() => {
1758
+ progressBar.style.width = '0%';
1759
+ }, 2000);
1760
+ }
1761
+
1762
+ async queryKnowledge(query) {
1763
+ if (!this.vectorStore) {
1764
+ return "I haven't been trained on any specific knowledge yet. Please add some content and train me first!";
1765
+ }
1766
+
1767
+ const relevantKnowledge = this.findRelevantKnowledge(query);
1768
+
1769
+ if (relevantKnowledge.length === 0) {
1770
+ return "I couldn't find specific information about that in my knowledge base. Could you rephrase or ask something else?";
1771
+ }
1772
+
1773
+ return this.generateResponse(query, relevantKnowledge);
1774
+ }
1775
+
1776
+ findRelevantKnowledge(query) {
1777
+ const queryLower = query.toLowerCase();
1778
+ const relevant = [];
1779
+
1780
+ for (const item of this.knowledgeBase) {
1781
+ if (item.content.toLowerCase().includes(queryLower) ||
1782
+ item.title.toLowerCase().includes(queryLower)) {
1783
+ relevant.push(item);
1784
+
1785
+ if (relevant.length >= 3) break;
1786
+ }
1787
+ }
1788
+
1789
+ return relevant;
1790
+ }
1791
+
1792
+ generateResponse(query, relevantKnowledge) {
1793
+ const context = relevantKnowledge.map(item => item.content).join('\n\n');
1794
+ return `Based on my knowledge base:\n\n${context}\n\nRegarding your question "${query}", here's what I can explain...`;
1795
+ }
1796
+
1797
+ showRAGStatus(message, type = 'info') {
1798
+ const statusEl = document.getElementById('ragStatus');
1799
+ const statusText = document.getElementById('ragStatusText');
1800
+
1801
+ statusText.textContent = message;
1802
+
1803
+ const colors = {
1804
+ 'info': '#5a6cff',
1805
+ 'success': '#00ff9d',
1806
+ 'warning': '#ffa500',
1807
+ 'error': '#ff5a5a',
1808
+ 'processing': '#a0b0ff'
1809
+ };
1810
+
1811
+ statusEl.style.borderColor = colors[type] + '30';
1812
+ statusEl.style.background = colors[type] + '10';
1813
+ statusEl.style.color = colors[type];
1814
+
1815
+ statusEl.style.display = 'flex';
1816
+
1817
+ if (type !== 'processing') {
1818
+ setTimeout(() => {
1819
+ statusEl.style.display = 'none';
1820
+ }, 5000);
1821
+ }
1822
+ }
1823
+
1824
+ sleep(ms) {
1825
+ return new Promise(resolve => setTimeout(resolve, ms));
1826
+ }
1827
+ }
1828
+
1829
+ // ==================== FULLY FUNCTIONAL LLM INTEGRATION ====================
1830
+ class LLMIntegration {
1831
+ constructor() {
1832
+ this.provider = 'huggingface';
1833
+ this.apiEndpoint = '';
1834
+ this.apiKey = '';
1835
+ this.modelName = '';
1836
+ this.isConnected = false;
1837
+ this.connectionTested = false;
1838
+
1839
+ // Default endpoints for each provider
1840
+ this.defaultEndpoints = {
1841
+ 'huggingface': 'https://api-inference.huggingface.co/models/microsoft/DialoGPT-medium',
1842
+ 'openai': 'https://api.openai.com/v1/chat/completions',
1843
+ 'anthropic': 'https://api.anthropic.com/v1/messages',
1844
+ 'google': 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent',
1845
+ 'azure': 'https://YOUR_RESOURCE.openai.azure.com/openai/deployments/YOUR_DEPLOYMENT/chat/completions',
1846
+ 'local': 'http://localhost:11434/api/generate'
1847
+ };
1848
+
1849
+ this.init();
1850
+ }
1851
+
1852
+ init() {
1853
+ this.loadSettings();
1854
+ this.setupEventListeners();
1855
+ this.updateProviderUI();
1856
+ }
1857
+
1858
+ loadSettings() {
1859
+ try {
1860
+ const settings = JSON.parse(localStorage.getItem('aiTutorLLMSettings')) || {};
1861
+
1862
+ this.provider = settings.provider || 'huggingface';
1863
+ this.apiEndpoint = settings.apiEndpoint || this.defaultEndpoints[this.provider];
1864
+ this.apiKey = settings.apiKey || '';
1865
+ this.modelName = settings.modelName || '';
1866
+
1867
+ // Update UI
1868
+ document.getElementById('llmProvider').value = this.provider;
1869
+ document.getElementById('apiEndpoint').value = this.apiEndpoint;
1870
+ document.getElementById('apiKey').value = this.apiKey;
1871
+ document.getElementById('modelName').value = this.modelName;
1872
+
1873
+ console.log('Loaded LLM settings:', {
1874
+ provider: this.provider,
1875
+ endpoint: this.apiEndpoint.substring(0, 50) + '...',
1876
+ hasKey: !!this.apiKey,
1877
+ model: this.modelName
1878
+ });
1879
+
1880
+ } catch (error) {
1881
+ console.error('Error loading LLM settings:', error);
1882
+ }
1883
+ }
1884
+
1885
+ saveSettings() {
1886
+ try {
1887
+ const settings = {
1888
+ provider: this.provider,
1889
+ apiEndpoint: this.apiEndpoint,
1890
+ apiKey: this.apiKey,
1891
+ modelName: this.modelName,
1892
+ savedAt: new Date().toISOString()
1893
+ };
1894
+
1895
+ localStorage.setItem('aiTutorLLMSettings', JSON.stringify(settings));
1896
+ console.log('Saved LLM settings');
1897
+ return true;
1898
+ } catch (error) {
1899
+ console.error('Error saving LLM settings:', error);
1900
+ return false;
1901
+ }
1902
+ }
1903
+
1904
+ setupEventListeners() {
1905
+ // Provider change
1906
+ document.getElementById('llmProvider').addEventListener('change', (e) => {
1907
+ this.provider = e.target.value;
1908
+ this.apiEndpoint = this.defaultEndpoints[this.provider];
1909
+ document.getElementById('apiEndpoint').value = this.apiEndpoint;
1910
+ this.updateProviderUI();
1911
+ });
1912
+
1913
+ // Endpoint change
1914
+ document.getElementById('apiEndpoint').addEventListener('input', (e) => {
1915
+ this.apiEndpoint = e.target.value.trim();
1916
+ });
1917
+
1918
+ // API Key change
1919
+ document.getElementById('apiKey').addEventListener('input', (e) => {
1920
+ this.apiKey = e.target.value.trim();
1921
+ });
1922
+
1923
+ // Model name change
1924
+ document.getElementById('modelName').addEventListener('input', (e) => {
1925
+ this.modelName = e.target.value.trim();
1926
+ });
1927
+
1928
+ // Test connection button
1929
+ document.getElementById('testApiBtn').addEventListener('click', () => {
1930
+ this.testConnection();
1931
+ });
1932
+
1933
+ // Save settings button
1934
+ document.getElementById('saveApiBtn').addEventListener('click', () => {
1935
+ this.saveSettings();
1936
+ this.showApiStatus('Settings saved successfully!', 'success');
1937
+ });
1938
+ }
1939
+
1940
+ updateProviderUI() {
1941
+ // Update placeholder based on provider
1942
+ const endpointInput = document.getElementById('apiEndpoint');
1943
+ const keyInput = document.getElementById('apiKey');
1944
+ const modelInput = document.getElementById('modelName');
1945
+
1946
+ switch(this.provider) {
1947
+ case 'huggingface':
1948
+ endpointInput.placeholder = 'https://api-inference.huggingface.co/models/{model}';
1949
+ keyInput.placeholder = 'Your Hugging Face API token';
1950
+ modelInput.placeholder = 'microsoft/DialoGPT-medium';
1951
+ break;
1952
+ case 'openai':
1953
+ endpointInput.placeholder = 'https://api.openai.com/v1/chat/completions';
1954
+ keyInput.placeholder = 'Your OpenAI API key';
1955
+ modelInput.placeholder = 'gpt-3.5-turbo';
1956
+ break;
1957
+ case 'anthropic':
1958
+ endpointInput.placeholder = 'https://api.anthropic.com/v1/messages';
1959
+ keyInput.placeholder = 'Your Anthropic API key';
1960
+ modelInput.placeholder = 'claude-3-sonnet-20240229';
1961
+ break;
1962
+ case 'google':
1963
+ endpointInput.placeholder = 'https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent';
1964
+ keyInput.placeholder = 'Your Google API key';
1965
+ modelInput.placeholder = 'gemini-pro';
1966
+ break;
1967
+ case 'azure':
1968
+ endpointInput.placeholder = 'https://{resource}.openai.azure.com/openai/deployments/{deployment}/chat/completions';
1969
+ keyInput.placeholder = 'Your Azure OpenAI API key';
1970
+ modelInput.placeholder = 'gpt-35-turbo';
1971
+ break;
1972
+ case 'local':
1973
+ endpointInput.placeholder = 'http://localhost:11434/api/generate';
1974
+ keyInput.placeholder = 'Leave empty for local setup';
1975
+ modelInput.placeholder = 'llama2';
1976
+ break;
1977
+ }
1978
+ }
1979
+
1980
+ async testConnection() {
1981
+ if (!this.apiEndpoint) {
1982
+ this.showApiStatus('Please enter an API endpoint', 'error');
1983
+ return false;
1984
+ }
1985
+
1986
+ if (this.provider !== 'local' && !this.apiKey) {
1987
+ this.showApiStatus('Please enter an API key', 'error');
1988
+ return false;
1989
+ }
1990
+
1991
+ this.showApiStatus('Testing connection...', 'processing');
1992
+
1993
+ try {
1994
+ let testResult;
1995
+
1996
+ switch(this.provider) {
1997
+ case 'huggingface':
1998
+ testResult = await this.testHuggingFaceConnection();
1999
+ break;
2000
+ case 'openai':
2001
+ testResult = await this.testOpenAIConnection();
2002
+ break;
2003
+ case 'anthropic':
2004
+ testResult = await this.testAnthropicConnection();
2005
+ break;
2006
+ case 'google':
2007
+ testResult = await this.testGoogleConnection();
2008
+ break;
2009
+ case 'azure':
2010
+ testResult = await this.testAzureConnection();
2011
+ break;
2012
+ case 'local':
2013
+ testResult = await this.testLocalConnection();
2014
+ break;
2015
+ default:
2016
+ testResult = await this.testGenericConnection();
2017
+ }
2018
+
2019
+ if (testResult.success) {
2020
+ this.isConnected = true;
2021
+ this.connectionTested = true;
2022
+ this.showApiStatus(`Connected successfully to ${this.provider}`, 'success');
2023
+ return true;
2024
+ } else {
2025
+ this.isConnected = false;
2026
+ this.showApiStatus(`Connection failed: ${testResult.error}`, 'error');
2027
+ return false;
2028
+ }
2029
+ } catch (error) {
2030
+ this.isConnected = false;
2031
+ this.showApiStatus(`Connection error: ${error.message}`, 'error');
2032
+ return false;
2033
+ }
2034
+ }
2035
+
2036
+ async testHuggingFaceConnection() {
2037
+ const model = this.modelName || 'microsoft/DialoGPT-medium';
2038
+ const endpoint = this.apiEndpoint.replace('{model}', model);
2039
+
2040
+ const response = await fetch(endpoint, {
2041
+ method: 'POST',
2042
+ headers: {
2043
+ 'Authorization': `Bearer ${this.apiKey}`,
2044
+ 'Content-Type': 'application/json'
2045
+ },
2046
+ body: JSON.stringify({
2047
+ inputs: 'Hello',
2048
+ parameters: {
2049
+ max_new_tokens: 10
2050
+ }
2051
+ })
2052
+ });
2053
+
2054
+ if (response.ok) {
2055
+ return { success: true };
2056
+ } else {
2057
+ return {
2058
+ success: false,
2059
+ error: `HTTP ${response.status}: ${response.statusText}`
2060
+ };
2061
+ }
2062
+ }
2063
+
2064
+ async testOpenAIConnection() {
2065
+ const response = await fetch(this.apiEndpoint, {
2066
+ method: 'POST',
2067
+ headers: {
2068
+ 'Authorization': `Bearer ${this.apiKey}`,
2069
+ 'Content-Type': 'application/json'
2070
+ },
2071
+ body: JSON.stringify({
2072
+ model: this.modelName || 'gpt-3.5-turbo',
2073
+ messages: [{ role: 'user', content: 'Hello' }],
2074
+ max_tokens: 10
2075
+ })
2076
+ });
2077
+
2078
+ if (response.ok) {
2079
+ return { success: true };
2080
+ } else {
2081
+ const errorData = await response.json().catch(() => ({}));
2082
+ return {
2083
+ success: false,
2084
+ error: errorData.error?.message || `HTTP ${response.status}`
2085
+ };
2086
+ }
2087
+ }
2088
+
2089
+ async testAnthropicConnection() {
2090
+ const response = await fetch(this.apiEndpoint, {
2091
+ method: 'POST',
2092
+ headers: {
2093
+ 'x-api-key': this.apiKey,
2094
+ 'anthropic-version': '2023-06-01',
2095
+ 'Content-Type': 'application/json'
2096
+ },
2097
+ body: JSON.stringify({
2098
+ model: this.modelName || 'claude-3-sonnet-20240229',
2099
+ messages: [{ role: 'user', content: 'Hello' }],
2100
+ max_tokens: 10
2101
+ })
2102
+ });
2103
+
2104
+ if (response.ok) {
2105
+ return { success: true };
2106
+ } else {
2107
+ const errorData = await response.json().catch(() => ({}));
2108
+ return {
2109
+ success: false,
2110
+ error: errorData.error?.message || `HTTP ${response.status}`
2111
+ };
2112
+ }
2113
+ }
2114
+
2115
+ async testGoogleConnection() {
2116
+ const endpoint = this.apiEndpoint.includes('{model}')
2117
+ ? this.apiEndpoint.replace('{model}', this.modelName || 'gemini-pro')
2118
+ : this.apiEndpoint;
2119
+
2120
+ const url = new URL(endpoint);
2121
+ url.searchParams.append('key', this.apiKey);
2122
+
2123
+ const response = await fetch(url.toString(), {
2124
+ method: 'POST',
2125
+ headers: {
2126
+ 'Content-Type': 'application/json'
2127
+ },
2128
+ body: JSON.stringify({
2129
+ contents: [{
2130
+ parts: [{ text: 'Hello' }]
2131
+ }]
2132
+ })
2133
+ });
2134
+
2135
+ if (response.ok) {
2136
+ return { success: true };
2137
+ } else {
2138
+ const errorData = await response.json().catch(() => ({}));
2139
+ return {
2140
+ success: false,
2141
+ error: errorData.error?.message || `HTTP ${response.status}`
2142
+ };
2143
+ }
2144
+ }
2145
+
2146
+ async testAzureConnection() {
2147
+ const response = await fetch(this.apiEndpoint, {
2148
+ method: 'POST',
2149
+ headers: {
2150
+ 'api-key': this.apiKey,
2151
+ 'Content-Type': 'application/json'
2152
+ },
2153
+ body: JSON.stringify({
2154
+ messages: [{ role: 'user', content: 'Hello' }],
2155
+ max_tokens: 10
2156
+ })
2157
+ });
2158
+
2159
+ if (response.ok) {
2160
+ return { success: true };
2161
+ } else {
2162
+ const errorData = await response.json().catch(() => ({}));
2163
+ return {
2164
+ success: false,
2165
+ error: errorData.error?.message || `HTTP ${response.status}`
2166
+ };
2167
+ }
2168
+ }
2169
+
2170
+ async testLocalConnection() {
2171
+ const response = await fetch(this.apiEndpoint, {
2172
+ method: 'POST',
2173
+ headers: {
2174
+ 'Content-Type': 'application/json'
2175
+ },
2176
+ body: JSON.stringify({
2177
+ model: this.modelName || 'llama2',
2178
+ prompt: 'Hello',
2179
+ stream: false
2180
+ })
2181
+ });
2182
+
2183
+ if (response.ok) {
2184
+ return { success: true };
2185
+ } else {
2186
+ return {
2187
+ success: false,
2188
+ error: `HTTP ${response.status}`
2189
+ };
2190
+ }
2191
+ }
2192
+
2193
+ async testGenericConnection() {
2194
+ const response = await fetch(this.apiEndpoint, {
2195
+ method: 'GET',
2196
+ headers: {
2197
+ 'Authorization': this.apiKey ? `Bearer ${this.apiKey}` : undefined,
2198
+ 'Content-Type': 'application/json'
2199
+ }
2200
+ });
2201
+
2202
+ if (response.ok) {
2203
+ return { success: true };
2204
+ } else {
2205
+ return {
2206
+ success: false,
2207
+ error: `HTTP ${response.status}`
2208
+ };
2209
+ }
2210
+ }
2211
+
2212
+ async generateResponse(prompt, context = '') {
2213
+ if (!this.isConnected) {
2214
+ console.log('LLM not connected, using fallback');
2215
+ return null;
2216
+ }
2217
+
2218
+ try {
2219
+ const fullPrompt = context ? `${context}\n\nQuestion: ${prompt}` : prompt;
2220
+
2221
+ switch(this.provider) {
2222
+ case 'huggingface':
2223
+ return await this.generateHuggingFaceResponse(fullPrompt);
2224
+ case 'openai':
2225
+ return await this.generateOpenAIResponse(fullPrompt);
2226
+ case 'anthropic':
2227
+ return await this.generateAnthropicResponse(fullPrompt);
2228
+ case 'google':
2229
+ return await this.generateGoogleResponse(fullPrompt);
2230
+ case 'azure':
2231
+ return await this.generateAzureResponse(fullPrompt);
2232
+ case 'local':
2233
+ return await this.generateLocalResponse(fullPrompt);
2234
+ default:
2235
+ return await this.generateGenericResponse(fullPrompt);
2236
+ }
2237
+ } catch (error) {
2238
+ console.error('LLM generation error:', error);
2239
+ return null;
2240
+ }
2241
+ }
2242
+
2243
+ async generateHuggingFaceResponse(prompt) {
2244
+ const model = this.modelName || 'microsoft/DialoGPT-medium';
2245
+ const endpoint = this.apiEndpoint.replace('{model}', model);
2246
+
2247
+ const response = await fetch(endpoint, {
2248
+ method: 'POST',
2249
+ headers: {
2250
+ 'Authorization': `Bearer ${this.apiKey}`,
2251
+ 'Content-Type': 'application/json'
2252
+ },
2253
+ body: JSON.stringify({
2254
+ inputs: prompt,
2255
+ parameters: {
2256
+ max_new_tokens: 500,
2257
+ temperature: 0.7,
2258
+ top_p: 0.9,
2259
+ repetition_penalty: 1.1,
2260
+ return_full_text: false
2261
+ }
2262
+ })
2263
+ });
2264
+
2265
+ if (!response.ok) {
2266
+ throw new Error(`HTTP ${response.status}`);
2267
+ }
2268
+
2269
+ const data = await response.json();
2270
+ return data[0]?.generated_text || data.generated_text || '';
2271
+ }
2272
+
2273
+ async generateOpenAIResponse(prompt) {
2274
+ const response = await fetch(this.apiEndpoint, {
2275
+ method: 'POST',
2276
+ headers: {
2277
+ 'Authorization': `Bearer ${this.apiKey}`,
2278
+ 'Content-Type': 'application/json'
2279
+ },
2280
+ body: JSON.stringify({
2281
+ model: this.modelName || 'gpt-3.5-turbo',
2282
+ messages: [
2283
+ { role: 'system', content: 'You are a helpful AI tutor.' },
2284
+ { role: 'user', content: prompt }
2285
+ ],
2286
+ max_tokens: 500,
2287
+ temperature: 0.7
2288
+ })
2289
+ });
2290
+
2291
+ if (!response.ok) {
2292
+ const errorData = await response.json();
2293
+ throw new Error(errorData.error?.message || `HTTP ${response.status}`);
2294
+ }
2295
+
2296
+ const data = await response.json();
2297
+ return data.choices[0]?.message?.content || '';
2298
+ }
2299
+
2300
+ async generateAnthropicResponse(prompt) {
2301
+ const response = await fetch(this.apiEndpoint, {
2302
+ method: 'POST',
2303
+ headers: {
2304
+ 'x-api-key': this.apiKey,
2305
+ 'anthropic-version': '2023-06-01',
2306
+ 'Content-Type': 'application/json'
2307
+ },
2308
+ body: JSON.stringify({
2309
+ model: this.modelName || 'claude-3-sonnet-20240229',
2310
+ messages: [{ role: 'user', content: prompt }],
2311
+ max_tokens: 500
2312
+ })
2313
+ });
2314
+
2315
+ if (!response.ok) {
2316
+ const errorData = await response.json();
2317
+ throw new Error(errorData.error?.message || `HTTP ${response.status}`);
2318
+ }
2319
+
2320
+ const data = await response.json();
2321
+ return data.content[0]?.text || '';
2322
+ }
2323
+
2324
+ async generateGoogleResponse(prompt) {
2325
+ const endpoint = this.apiEndpoint.includes('{model}')
2326
+ ? this.apiEndpoint.replace('{model}', this.modelName || 'gemini-pro')
2327
+ : this.apiEndpoint;
2328
+
2329
+ const url = new URL(endpoint);
2330
+ url.searchParams.append('key', this.apiKey);
2331
+
2332
+ const response = await fetch(url.toString(), {
2333
+ method: 'POST',
2334
+ headers: {
2335
+ 'Content-Type': 'application/json'
2336
+ },
2337
+ body: JSON.stringify({
2338
+ contents: [{
2339
+ parts: [{ text: prompt }]
2340
+ }],
2341
+ generationConfig: {
2342
+ maxOutputTokens: 500,
2343
+ temperature: 0.7
2344
+ }
2345
+ })
2346
+ });
2347
+
2348
+ if (!response.ok) {
2349
+ const errorData = await response.json();
2350
+ throw new Error(errorData.error?.message || `HTTP ${response.status}`);
2351
+ }
2352
+
2353
+ const data = await response.json();
2354
+ return data.candidates?.[0]?.content?.parts?.[0]?.text || '';
2355
+ }
2356
+
2357
+ async generateAzureResponse(prompt) {
2358
+ const response = await fetch(this.apiEndpoint, {
2359
+ method: 'POST',
2360
+ headers: {
2361
+ 'api-key': this.apiKey,
2362
+ 'Content-Type': 'application/json'
2363
+ },
2364
+ body: JSON.stringify({
2365
+ messages: [
2366
+ { role: 'system', content: 'You are a helpful AI tutor.' },
2367
+ { role: 'user', content: prompt }
2368
+ ],
2369
+ max_tokens: 500,
2370
+ temperature: 0.7
2371
+ })
2372
+ });
2373
+
2374
+ if (!response.ok) {
2375
+ const errorData = await response.json();
2376
+ throw new Error(errorData.error?.message || `HTTP ${response.status}`);
2377
+ }
2378
+
2379
+ const data = await response.json();
2380
+ return data.choices[0]?.message?.content || '';
2381
+ }
2382
+
2383
+ async generateLocalResponse(prompt) {
2384
+ const response = await fetch(this.apiEndpoint, {
2385
+ method: 'POST',
2386
+ headers: {
2387
+ 'Content-Type': 'application/json'
2388
+ },
2389
+ body: JSON.stringify({
2390
+ model: this.modelName || 'llama2',
2391
+ prompt: prompt,
2392
+ stream: false,
2393
+ options: {
2394
+ temperature: 0.7,
2395
+ num_predict: 500
2396
+ }
2397
+ })
2398
+ });
2399
+
2400
+ if (!response.ok) {
2401
+ throw new Error(`HTTP ${response.status}`);
2402
+ }
2403
+
2404
+ const data = await response.json();
2405
+ return data.response || '';
2406
+ }
2407
+
2408
+ async generateGenericResponse(prompt) {
2409
+ const response = await fetch(this.apiEndpoint, {
2410
+ method: 'POST',
2411
+ headers: {
2412
+ 'Authorization': this.apiKey ? `Bearer ${this.apiKey}` : undefined,
2413
+ 'Content-Type': 'application/json'
2414
+ },
2415
+ body: JSON.stringify({
2416
+ prompt: prompt,
2417
+ max_tokens: 500,
2418
+ temperature: 0.7
2419
+ })
2420
+ });
2421
+
2422
+ if (!response.ok) {
2423
+ throw new Error(`HTTP ${response.status}`);
2424
+ }
2425
+
2426
+ const data = await response.json();
2427
+ return data.text || data.response || data.choices?.[0]?.text || '';
2428
+ }
2429
+
2430
+ showApiStatus(message, type = 'info') {
2431
+ const statusEl = document.getElementById('apiStatus');
2432
+ const statusText = document.getElementById('apiStatusText');
2433
+
2434
+ statusText.textContent = message;
2435
+
2436
+ const colors = {
2437
+ 'info': '#5a6cff',
2438
+ 'success': '#00ff9d',
2439
+ 'error': '#ff5a5a',
2440
+ 'processing': '#a0b0ff'
2441
+ };
2442
+
2443
+ statusEl.style.borderColor = colors[type] + '30';
2444
+ statusEl.style.background = colors[type] + '10';
2445
+ statusEl.style.color = colors[type];
2446
+
2447
+ statusEl.style.display = 'flex';
2448
+
2449
+ if (type !== 'processing') {
2450
+ setTimeout(() => {
2451
+ statusEl.style.display = 'none';
2452
+ }, 5000);
2453
+ }
2454
+ }
2455
+ }
2456
+
2457
+ // Voice Synthesis System
2458
+ class VoiceSynthesis {
2459
+ constructor() {
2460
+ this.synth = window.speechSynthesis;
2461
+ this.voices = [];
2462
+ this.currentVoice = null;
2463
+ this.isAutoSpeak = true;
2464
+ this.isSpeaking = false;
2465
+ this.currentUtterance = null;
2466
+
2467
+ this.settings = {
2468
+ rate: 1.0,
2469
+ pitch: 1.0,
2470
+ volume: 0.8
2471
+ };
2472
+
2473
+ this.testScript = "Hello there! This is a test to see how natural and fluid my voice sounds. I'm going to tell you a little story about learning and discovery.";
2474
+
2475
+ this.initVoices();
2476
+ this.setupEventListeners();
2477
+ }
2478
+
2479
+ initVoices() {
2480
+ const loadVoices = () => {
2481
+ this.voices = this.synth.getVoices();
2482
+ this.populateVoiceList();
2483
+ this.selectBestVoice();
2484
+ };
2485
+
2486
+ if (this.synth.onvoiceschanged !== undefined) {
2487
+ this.synth.onvoiceschanged = loadVoices;
2488
+ }
2489
+
2490
+ if (this.synth.getVoices().length > 0) {
2491
+ loadVoices();
2492
+ }
2493
+ }
2494
+
2495
+ selectBestVoice() {
2496
+ const englishVoices = this.voices.filter(voice =>
2497
+ voice.lang.startsWith('en-')
2498
+ );
2499
+
2500
+ if (englishVoices.length === 0) {
2501
+ console.warn('No English voices found');
2502
+ return;
2503
+ }
2504
+
2505
+ const preferredVoices = [
2506
+ 'Google UK English Male',
2507
+ 'Google US English',
2508
+ 'Microsoft David',
2509
+ 'Microsoft Zira',
2510
+ 'Alex',
2511
+ 'Samantha',
2512
+ 'Daniel'
2513
+ ];
2514
+
2515
+ for (const voiceName of preferredVoices) {
2516
+ const voice = englishVoices.find(v => v.name.includes(voiceName));
2517
+ if (voice) {
2518
+ this.currentVoice = voice;
2519
+ this.updateVoiceSelect(voice);
2520
+ break;
2521
+ }
2522
+ }
2523
+
2524
+ if (!this.currentVoice && englishVoices.length > 0) {
2525
+ this.currentVoice = englishVoices[0];
2526
+ this.updateVoiceSelect(englishVoices[0]);
2527
+ }
2528
+ }
2529
+
2530
+ populateVoiceList() {
2531
+ const voiceSelect = document.getElementById('voiceSelect');
2532
+ voiceSelect.innerHTML = '';
2533
+
2534
+ const englishVoices = this.voices.filter(voice =>
2535
+ voice.lang.startsWith('en-')
2536
+ );
2537
+
2538
+ if (englishVoices.length === 0) {
2539
+ const option = document.createElement('option');
2540
+ option.textContent = 'No English voices found';
2541
+ option.disabled = true;
2542
+ voiceSelect.appendChild(option);
2543
+ return;
2544
+ }
2545
+
2546
+ englishVoices.forEach(voice => {
2547
+ const option = document.createElement('option');
2548
+ option.value = voice.name;
2549
+ option.textContent = `${voice.name} (${voice.lang})`;
2550
+ option.dataset.lang = voice.lang;
2551
+ voiceSelect.appendChild(option);
2552
+ });
2553
+ }
2554
+
2555
+ updateVoiceSelect(voice) {
2556
+ const voiceSelect = document.getElementById('voiceSelect');
2557
+ const options = voiceSelect.querySelectorAll('option');
2558
+
2559
+ options.forEach(option => {
2560
+ if (option.value === voice.name) {
2561
+ option.selected = true;
2562
+ }
2563
+ });
2564
+ }
2565
+
2566
+ setupEventListeners() {
2567
+ document.getElementById('voiceSelect').addEventListener('change', (e) => {
2568
+ const selectedVoice = this.voices.find(v => v.name === e.target.value);
2569
+ if (selectedVoice) {
2570
+ this.currentVoice = selectedVoice;
2571
+ }
2572
+ });
2573
+
2574
+ document.getElementById('rateSlider').addEventListener('input', (e) => {
2575
+ const value = parseFloat(e.target.value);
2576
+ this.settings.rate = value;
2577
+ document.getElementById('rateValue').textContent = value.toFixed(1) + 'x';
2578
+ });
2579
+
2580
+ document.getElementById('pitchSlider').addEventListener('input', (e) => {
2581
+ const value = parseFloat(e.target.value);
2582
+ this.settings.pitch = value;
2583
+ document.getElementById('pitchValue').textContent = value.toFixed(1);
2584
+ });
2585
+
2586
+ document.getElementById('volumeSlider').addEventListener('input', (e) => {
2587
+ const value = parseFloat(e.target.value);
2588
+ this.settings.volume = value;
2589
+ document.getElementById('volumeValue').textContent = Math.round(value * 100) + '%';
2590
+ });
2591
+
2592
+ document.getElementById('autoSpeakBtn').addEventListener('click', (e) => {
2593
+ this.isAutoSpeak = !this.isAutoSpeak;
2594
+ const btn = e.target.closest('.voice-btn');
2595
+ btn.dataset.enabled = this.isAutoSpeak;
2596
+ btn.innerHTML = this.isAutoSpeak
2597
+ ? '<i class="fas fa-bullhorn"></i> Auto-Speak'
2598
+ : '<i class="fas fa-volume-mute"></i> Auto-Speak';
2599
+
2600
+ btn.classList.toggle('active', this.isAutoSpeak);
2601
+ });
2602
+
2603
+ document.getElementById('stopBtn').addEventListener('click', () => {
2604
+ this.stopSpeaking();
2605
+ });
2606
+
2607
+ document.getElementById('previewBtn').addEventListener('click', () => {
2608
+ this.previewCurrentVoice();
2609
+ });
2610
+ }
2611
+
2612
+ speak(text) {
2613
+ if (!this.isAutoSpeak || !text || this.isSpeaking) return;
2614
+
2615
+ this.stopSpeaking();
2616
+
2617
+ const utterance = new SpeechSynthesisUtterance(text);
2618
+
2619
+ if (this.currentVoice) {
2620
+ utterance.voice = this.currentVoice;
2621
+ }
2622
+
2623
+ utterance.rate = this.settings.rate;
2624
+ utterance.pitch = this.settings.pitch;
2625
+ utterance.volume = this.settings.volume;
2626
+
2627
+ utterance.text = this.addNaturalPauses(text);
2628
+
2629
+ utterance.onerror = (event) => {
2630
+ console.error('Speech synthesis error:', event);
2631
+ };
2632
+
2633
+ this.currentUtterance = utterance;
2634
+ this.synth.speak(utterance);
2635
+ }
2636
+
2637
+ addNaturalPauses(text) {
2638
+ return text
2639
+ .replace(/,/g, ',<break time="200ms"/>')
2640
+ .replace(/\./g, '.<break time="300ms"/>')
2641
+ .replace(/\?/g, '?<break time="400ms"/>')
2642
+ .replace(/\!/g, '!<break time="400ms"/>');
2643
+ }
2644
+
2645
+ stopSpeaking() {
2646
+ if (this.isSpeaking) {
2647
+ this.synth.cancel();
2648
+ this.isSpeaking = false;
2649
+ this.currentUtterance = null;
2650
+ }
2651
+ }
2652
+
2653
+ previewCurrentVoice() {
2654
+ this.speak(this.testScript);
2655
+ }
2656
+ }
2657
+
2658
+ // Chat Interface - FULLY INTEGRATED WITH LLM
2659
+ class ChatInterface {
2660
+ constructor(ragTutor, llmIntegration) {
2661
+ this.ragTutor = ragTutor;
2662
+ this.llmIntegration = llmIntegration;
2663
+ this.isListening = false;
2664
+ this.initChat();
2665
+ }
2666
+
2667
+ initChat() {
2668
+ this.chatInput = document.getElementById('chatInput');
2669
+ this.chatMessages = document.getElementById('chatMessages');
2670
+ this.voiceBtn = document.getElementById('voiceBtn');
2671
+ this.sendBtn = document.getElementById('sendBtn');
2672
+ this.chatStatusText = document.getElementById('chatStatusText');
2673
+
2674
+ this.setupEventListeners();
2675
+ this.autoResizeTextarea();
2676
+ }
2677
+
2678
+ setupEventListeners() {
2679
+ this.sendBtn.addEventListener('click', () => this.sendMessage());
2680
+
2681
+ this.chatInput.addEventListener('keydown', (e) => {
2682
+ if (e.key === 'Enter' && !e.shiftKey) {
2683
+ e.preventDefault();
2684
+ this.sendMessage();
2685
+ }
2686
+ });
2687
+
2688
+ this.voiceBtn.addEventListener('click', () => this.toggleVoiceInput());
2689
+ this.chatInput.addEventListener('input', () => this.autoResizeTextarea());
2690
+ }
2691
+
2692
+ autoResizeTextarea() {
2693
+ const textarea = this.chatInput;
2694
+ textarea.style.height = 'auto';
2695
+ const newHeight = Math.min(textarea.scrollHeight, 120);
2696
+ textarea.style.height = newHeight + 'px';
2697
+ }
2698
+
2699
+ toggleVoiceInput() {
2700
+ this.isListening = !this.isListening;
2701
+
2702
+ if (this.isListening) {
2703
+ this.voiceBtn.classList.add('listening');
2704
+ this.voiceBtn.innerHTML = '<i class="fas fa-stop"></i>';
2705
+ this.chatStatusText.textContent = 'Listening...';
2706
+
2707
+ if (window.visualization) {
2708
+ window.visualization.setPreset('listening');
2709
+ }
2710
+
2711
+ setTimeout(() => {
2712
+ this.simulateVoiceInput();
2713
+ }, 1500);
2714
+ } else {
2715
+ this.voiceBtn.classList.remove('listening');
2716
+ this.voiceBtn.innerHTML = '<i class="fas fa-microphone"></i>';
2717
+ this.chatStatusText.textContent = 'Online';
2718
+
2719
+ if (window.visualization) {
2720
+ window.visualization.setPreset('processing');
2721
+ }
2722
+ }
2723
+ }
2724
+
2725
+ simulateVoiceInput() {
2726
+ if (!this.isListening) return;
2727
+
2728
+ const simulatedQuestions = [
2729
+ "Can you explain quantum computing?",
2730
+ "How do neural networks learn?",
2731
+ "What's the difference between AI and machine learning?",
2732
+ "Help me understand blockchain technology",
2733
+ "Explain the concept of derivatives in calculus"
2734
+ ];
2735
+
2736
+ const randomQuestion = simulatedQuestions[Math.floor(Math.random() * simulatedQuestions.length)];
2737
+
2738
+ this.addMessage(randomQuestion, 'user');
2739
+ this.processAIResponse(randomQuestion);
2740
+
2741
+ setTimeout(() => {
2742
+ this.isListening = false;
2743
+ this.voiceBtn.classList.remove('listening');
2744
+ this.voiceBtn.innerHTML = '<i class="fas fa-microphone"></i>';
2745
+ this.chatStatusText.textContent = 'Online';
2746
+ }, 500);
2747
+ }
2748
+
2749
+ async sendMessage() {
2750
+ const message = this.chatInput.value.trim();
2751
+ if (message === '') return;
2752
+
2753
+ this.addMessage(message, 'user');
2754
+ this.chatInput.value = '';
2755
+ this.autoResizeTextarea();
2756
+ await this.processAIResponse(message);
2757
+ }
2758
+
2759
+ addMessage(content, sender) {
2760
+ const messageDiv = document.createElement('div');
2761
+ messageDiv.className = `message message-${sender}`;
2762
+
2763
+ const time = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
2764
+
2765
+ messageDiv.innerHTML = `
2766
+ <div class="message-content">${this.escapeHtml(content)}</div>
2767
+ <div class="message-time">${time}</div>
2768
+ `;
2769
+
2770
+ this.chatMessages.appendChild(messageDiv);
2771
+ this.chatMessages.scrollTop = this.chatMessages.scrollHeight;
2772
+
2773
+ if (window.visualization && sender === 'user') {
2774
+ window.visualization.setPreset('processing');
2775
+ }
2776
+ }
2777
+
2778
+ escapeHtml(text) {
2779
+ const div = document.createElement('div');
2780
+ div.textContent = text;
2781
+ return div.innerHTML;
2782
+ }
2783
+
2784
+ async processAIResponse(userMessage) {
2785
+ // Update 3D visualization
2786
+ if (window.visualization) {
2787
+ window.visualization.setPreset('processing');
2788
+ }
2789
+
2790
+ // Show typing indicator (simulated delay)
2791
+ const processingTime = 1000 + Math.random() * 2000;
2792
+
2793
+ setTimeout(async () => {
2794
+ let response;
2795
+
2796
+ // Try LLM API first if available and connected
2797
+ if (this.llmIntegration && this.llmIntegration.isConnected) {
2798
+ try {
2799
+ // Get context from RAG if available
2800
+ let context = '';
2801
+ if (this.ragTutor && this.ragTutor.vectorStore) {
2802
+ const relevantKnowledge = await this.ragTutor.queryKnowledge(userMessage);
2803
+ if (relevantKnowledge && !relevantKnowledge.includes("I haven't been trained") &&
2804
+ !relevantKnowledge.includes("I couldn't find")) {
2805
+ context = relevantKnowledge;
2806
+ }
2807
+ }
2808
+
2809
+ // Call LLM API
2810
+ response = await this.llmIntegration.generateResponse(userMessage, context);
2811
+
2812
+ if (!response) {
2813
+ throw new Error('No response from LLM API');
2814
+ }
2815
+
2816
+ console.log('LLM API response received');
2817
+
2818
+ } catch (error) {
2819
+ console.log('LLM API failed, using fallback:', error);
2820
+ response = await this.getFallbackResponse(userMessage);
2821
+ }
2822
+ } else {
2823
+ // LLM not connected, use fallback
2824
+ response = await this.getFallbackResponse(userMessage);
2825
+ }
2826
+
2827
+ // Add AI response to chat
2828
+ this.addMessage(response, 'ai');
2829
+
2830
+ // Speak the response
2831
+ if (window.voiceSynthesis) {
2832
+ setTimeout(() => {
2833
+ window.voiceSynthesis.speak(response);
2834
+ }, 300);
2835
+ }
2836
+
2837
+ // Update 3D visualization
2838
+ if (window.visualization) {
2839
+ window.visualization.setPreset('responding');
2840
+
2841
+ setTimeout(() => {
2842
+ window.visualization.setPreset('idle');
2843
+ }, 3000);
2844
+ }
2845
+ }, processingTime);
2846
+ }
2847
+
2848
+ async getFallbackResponse(userMessage) {
2849
+ // Try RAG first
2850
+ if (this.ragTutor && this.ragTutor.vectorStore) {
2851
+ const ragResponse = await this.ragTutor.queryKnowledge(userMessage);
2852
+ if (ragResponse && !ragResponse.includes("I haven't been trained") &&
2853
+ !ragResponse.includes("I couldn't find")) {
2854
+ return ragResponse;
2855
+ }
2856
+ }
2857
+
2858
+ // Fallback to local responses
2859
+ return this.generateAIResponse(userMessage);
2860
+ }
2861
+
2862
+ generateAIResponse(userMessage) {
2863
+ const responses = {
2864
+ quantum: "Quantum computing leverages quantum mechanics to process information. Unlike classical bits (0 or 1), quantum bits (qubits) can exist in superposition, enabling parallel computation.",
2865
+ neural: "Neural networks learn through backpropagation and gradient descent. They adjust weights based on prediction errors, minimizing loss functions.",
2866
+ blockchain: "Blockchain is a decentralized, distributed ledger technology. Each block contains cryptographic hashes linking to previous blocks, ensuring immutability.",
2867
+ calculus: "Derivatives measure instantaneous rate of change. The derivative of f(x) at point a is the slope of the tangent line.",
2868
+ default: "I understand you're asking about " + userMessage.substring(0, 30) + "... This is a complex topic that requires careful explanation."
2869
+ };
2870
+
2871
+ const lowerMessage = userMessage.toLowerCase();
2872
+
2873
+ if (lowerMessage.includes('quantum')) return responses.quantum;
2874
+ if (lowerMessage.includes('neural') || lowerMessage.includes('network')) return responses.neural;
2875
+ if (lowerMessage.includes('blockchain')) return responses.blockchain;
2876
+ if (lowerMessage.includes('calculus') || lowerMessage.includes('derivative')) return responses.calculus;
2877
+
2878
+ return responses.default;
2879
+ }
2880
+ }
2881
+
2882
+ // Initialize everything
2883
+ window.addEventListener('load', () => {
2884
+ updateStatus('Loading Neural Network Interface...');
2885
+
2886
+ setTimeout(() => {
2887
+ // Initialize UI components first
2888
+ window.mobileMenu = new MobileMenu();
2889
+ window.accordion = new Accordion();
2890
+
2891
+ // Initialize 3D visualization - NEURAL WEB VERSION
2892
+ window.visualization = new AITutorVisualization();
2893
+
2894
+ // Initialize RAG AI Tutor
2895
+ window.ragTutor = new RAGAITutor();
2896
+
2897
+ // Initialize FULLY FUNCTIONAL LLM Integration
2898
+ window.llmIntegration = new LLMIntegration();
2899
+
2900
+ // Initialize chat interface with BOTH RAG and LLM
2901
+ window.chatInterface = new ChatInterface(window.ragTutor, window.llmIntegration);
2902
+
2903
+ // Initialize voice synthesis
2904
+ if ('speechSynthesis' in window) {
2905
+ window.voiceSynthesis = new VoiceSynthesis();
2906
+ updateStatus('Voice synthesis initialized');
2907
+ }
2908
+
2909
+ if (window.visualization && window.visualization.scene) {
2910
+ window.addEventListener('resize', () => window.visualization.onWindowResize());
2911
+
2912
+ window.addEventListener('keydown', (e) => {
2913
+ if (e.code === 'Space') {
2914
+ window.visualization.setPreset(
2915
+ window.visualization.params.mode === 'idle' ? 'listening' : 'idle'
2916
+ );
2917
+ }
2918
+ if (e.code === 'Space' && e.ctrlKey && window.voiceSynthesis) {
2919
+ window.voiceSynthesis.stopSpeaking();
2920
+ }
2921
+ });
2922
+
2923
+ window.visualization.setPreset('idle');
2924
+ }
2925
+
2926
+ // Test LLM connection on startup if credentials exist
2927
+ if (window.llmIntegration.apiKey && window.llmIntegration.apiEndpoint) {
2928
+ setTimeout(() => {
2929
+ window.llmIntegration.testConnection().then(connected => {
2930
+ if (connected) {
2931
+ console.log('LLM API auto-connected on startup');
2932
+ }
2933
+ });
2934
+ }, 2000);
2935
+ }
2936
+
2937
+ hideLoading();
2938
+
2939
+ }, 100);
2940
+ });
2941
+
2942
+ window.addEventListener('beforeunload', () => {
2943
+ if (window.visualization) {
2944
+ window.visualization.destroy();
2945
+ }
2946
+ if (window.voiceSynthesis) {
2947
+ window.voiceSynthesis.stopSpeaking();
2948
+ }
2949
+ });
2950
+ </script>
2951
+ </body>
2952
+ </html>