Ashok75 commited on
Commit
8833cd8
·
verified ·
1 Parent(s): 6774dc4

Update chat.html

Browse files
Files changed (1) hide show
  1. chat.html +395 -331
chat.html CHANGED
@@ -668,387 +668,451 @@
668
  </div>
669
  </div>
670
  </div>
671
- <script>
672
- document.addEventListener('DOMContentLoaded', function() {
673
- const chatContainer = document.getElementById('chatContainer');
674
- const userInput = document.getElementById('userInput'); // This is the textarea
675
- const submitButton = document.getElementById('submitButton');
676
- const typingIndicator = document.getElementById('typingIndicator');
677
- const welcomeMessage = document.getElementById('welcomeMessage');
678
- const initialMessage = document.getElementById('initialMessage');
679
- const loginPrompt = document.getElementById('loginPrompt');
680
- const continueGuest = document.getElementById('continueGuest');
681
-
682
- const addButton = document.getElementById('addButton'); // Get the new add button
683
- const attachmentOptionsContainer = document.querySelector('.attachment-options-container'); // Get the options container
684
-
685
- // New elements for file handling
686
- const cameraInput = document.getElementById('cameraInput');
687
- const galleryInput = document.getElementById('galleryInput');
688
- const filesInput = document.getElementById('filesInput');
689
- const attachedFilePreviewContainer = document.getElementById('attachedFilePreviewContainer');
690
- const inputContainer = document.getElementById('inputContainer'); // The .gemini-input-container
691
-
692
- let messageCount = 0;
693
- let attachedFiles = []; // Array to store File objects
694
-
695
- // --- Textarea Auto-Grow & Scroll Logic (Enhanced) ---
696
- const style = getComputedStyle(userInput);
697
- const lineHeight = parseFloat(style.lineHeight);
698
- const minHeight = lineHeight; // Height for one line of text
699
- const maxHeight = lineHeight * 5; // Height for 5 lines of text
700
-
701
- // Initial padding values for the input container, adjusted for icons
702
- const initialInputContainerPaddingTop = parseFloat(getComputedStyle(inputContainer).paddingTop);
703
- const initialInputContainerPaddingBottom = parseFloat(getComputedStyle(inputContainer).paddingBottom);
704
- const initialActionButtonsHeight = document.querySelector('.gemini-input-actions').offsetHeight; // Height of mic/send icons
705
-
706
- function autoGrowTextarea() {
707
- if (!userInput) {
708
- console.error("autoGrowTextarea called but userInput element not found.");
709
- return;
710
- }
711
-
712
- // Reset height to auto to get the true scrollHeight
713
- userInput.style.height = 'auto';
714
- const scrollHeight = userInput.scrollHeight;
715
-
716
- // Calculate actual textarea height based on content, respecting min/max
717
- let newTextareaHeight = Math.min(Math.max(scrollHeight, minHeight), maxHeight);
718
- userInput.style.height = newTextareaHeight + 'px';
719
-
720
- // Adjust padding of the input container if files are present
721
- if (attachedFiles.length > 0) {
722
- // The `attachedFilePreviewContainer` already has height, we need space below it for the textarea
723
- // The overall `inputContainer` needs to grow, and `userInput`'s height is part of that.
724
- // The gap between chips and textarea is handled by `gap` on `inputContainer`.
725
- // We need to ensure the textarea's padding is minimal, and its effective content area is what's used.
726
- userInput.style.paddingTop = '0px';
727
- userInput.style.paddingBottom = '0px';
728
- inputContainer.style.alignItems = 'flex-start'; // Align content to the top
729
- } else {
730
- // Reset to default alignment and padding if no files
731
- userInput.style.paddingTop = '0px'; // Controlled by the overall inputContainer padding
732
- userInput.style.paddingBottom = '0px'; // Controlled by the overall inputContainer padding
733
- inputContainer.style.alignItems = 'flex-end'; // Align to bottom for default state
734
- }
735
-
736
- // Manage overflow-y based on whether it hits max height
737
- if (scrollHeight > maxHeight) {
738
- userInput.style.overflowY = 'auto';
739
- } else {
740
- userInput.style.overflowY = 'hidden';
741
- }
742
 
743
- // Enable submit button if there's text or attached files
744
- if (userInput.value.trim().length > 0 || attachedFiles.length > 0) {
745
- submitButton.classList.remove('disabled');
746
- } else {
747
- submitButton.classList.add('disabled');
748
- }
749
- }
750
 
751
- userInput.addEventListener('input', autoGrowTextarea);
752
- // Run on load to set initial height correctly
753
- setTimeout(autoGrowTextarea, 0);
754
- // --- End Textarea Auto-Grow & Scroll Logic ---
755
 
 
 
 
 
 
 
 
 
 
756
 
757
- // Handle Enter key for sending (allow Shift+Enter for new line)
758
- userInput.addEventListener('keydown', function(event) {
759
- if (event.key === 'Enter' && !event.shiftKey && !submitButton.classList.contains('disabled')) {
760
- event.preventDefault();
761
- sendMessage();
762
- }
763
- });
764
 
765
- // Handle submit button click
766
- submitButton.addEventListener('click', function() {
767
- if (!this.classList.contains('disabled')) {
768
- sendMessage();
769
- }
770
- });
 
771
 
772
- // Handle continue as guest button click
773
- continueGuest.addEventListener('click', function() {
774
- loginPrompt.classList.remove('show');
775
- });
776
 
777
- // --- Handle Add Button Click ---
778
- addButton.addEventListener('click', function(event) {
779
- event.stopPropagation();
780
- attachmentOptionsContainer.classList.toggle('visible');
781
- });
782
 
783
- // --- Handle Attachment Option Clicks ---
784
- document.getElementById('cameraOption').addEventListener('click', function() {
785
- cameraInput.click();
786
- attachmentOptionsContainer.classList.remove('visible');
787
- });
 
 
788
 
789
- document.getElementById('galleryOption').addEventListener('click', function() {
790
- galleryInput.click();
791
- attachmentOptionsContainer.classList.remove('visible');
792
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
793
 
794
- document.getElementById('filesOption').addEventListener('click', function() {
795
- filesInput.click();
796
- attachmentOptionsContainer.classList.remove('visible');
797
- });
798
 
799
- document.getElementById('driveOption').addEventListener('click', function() {
800
- alert("Google Drive integration requires backend setup and Google API access.");
801
- attachmentOptionsContainer.classList.remove('visible');
802
- });
 
 
803
 
804
- // --- Handle File Input Change ---
805
- [cameraInput, galleryInput, filesInput].forEach(input => {
806
- input.addEventListener('change', function(event) {
807
- const files = event.target.files;
808
- if (files.length > 0) {
809
- for (let i = 0; i < files.length; i++) {
810
- attachedFiles.push(files[i]);
811
- addFileChip(files[i]);
812
- }
813
- autoGrowTextarea(); // Re-evaluate button state and input height
814
- userInput.focus(); // Keep focus on the textarea
815
- }
816
- // Clear the input so the same file can be selected again
817
- event.target.value = '';
818
- });
819
- });
820
 
821
- // Function to add a file chip to the input container
822
- function addFileChip(file) {
823
- const chip = document.createElement('div');
824
- chip.className = 'attached-file-chip';
825
- chip.setAttribute('data-filename', file.name); // Store filename for removal
826
-
827
- let previewElement;
828
- if (file.type.startsWith('image/')) {
829
- const img = document.createElement('img');
830
- img.src = URL.createObjectURL(file);
831
- img.alt = file.name;
832
- img.onload = () => URL.revokeObjectURL(img.src); // Clean up object URL
833
- previewElement = img;
834
- } else {
835
- const icon = document.createElement('i');
836
- icon.className = 'fas fa-paperclip'; // Generic file icon
837
- previewElement = icon;
838
- }
839
 
840
- const fileNameSpan = document.createElement('span');
841
- fileNameSpan.className = 'file-name';
842
- fileNameSpan.textContent = file.name;
843
-
844
- const removeButton = document.createElement('span');
845
- removeButton.className = 'remove-file';
846
- removeButton.innerHTML = '&times;'; // 'x' icon
847
- removeButton.addEventListener('click', function() {
848
- removeFileChip(file.name);
849
- });
850
-
851
- chip.appendChild(previewElement);
852
- chip.appendChild(fileNameSpan);
853
- chip.appendChild(removeButton);
854
-
855
- attachedFilePreviewContainer.appendChild(chip);
856
- // No need to set userInput.style.paddingTop here directly,
857
- // autoGrowTextarea will re-evaluate based on the container's contents.
858
- autoGrowTextarea(); // Re-run autoGrow to adjust textarea based on new chip height
859
- }
860
 
861
- // Function to remove a file chip
862
- function removeFileChip(filename) {
863
- const chipToRemove = attachedFilePreviewContainer.querySelector(`[data-filename="${filename}"]`);
864
- if (chipToRemove) {
865
- attachedFilePreviewContainer.removeChild(chipToRemove);
866
- attachedFiles = attachedFiles.filter(file => file.name !== filename);
867
- autoGrowTextarea(); // Re-evaluate button state and input height
868
- }
869
- }
870
 
 
 
 
 
871
 
872
- // --- Hide options when clicking outside ---
873
- document.addEventListener('click', function(event) {
874
- const isClickInsideAddButton = addButton && addButton.contains(event.target);
875
- const isClickInsideOptions = attachmentOptionsContainer && attachmentOptionsContainer.contains(event.target);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
876
 
877
- if (attachmentOptionsContainer && attachmentOptionsContainer.classList.contains('visible') && !isClickInsideAddButton && !isClickInsideOptions) {
878
- attachmentOptionsContainer.classList.remove('visible');
879
- }
880
- });
881
 
882
- if (attachmentOptionsContainer) {
883
- attachmentOptionsContainer.addEventListener('click', function(event){
884
- event.stopPropagation();
885
- });
886
- }
887
- // --- END Hide options when clicking outside ---
 
 
 
 
 
 
 
 
 
 
 
888
 
 
 
 
889
 
890
- // Function to send message (MODIFIED to include files)
891
- async function sendMessage() {
892
- const text = userInput.value.trim();
893
- if (!text && attachedFiles.length === 0) return; // Don't send empty message with no files
894
 
895
- if (!welcomeMessage.classList.contains('d-none')) {
896
- welcomeMessage.classList.add('d-none');
897
- }
898
 
899
- if (initialMessage.classList.contains('d-none')) {
900
- initialMessage.classList.remove('d-none');
901
- }
 
 
 
 
902
 
903
- // Display user message. If files are attached, mention them.
904
- let userMessageContent = text;
905
- if (attachedFiles.length > 0) {
906
- const fileNames = attachedFiles.map(f => f.name).join(', ');
907
- userMessageContent += ` (Attached: ${fileNames})`;
908
- }
909
- addMessage(userMessageContent, 'user');
910
 
911
- userInput.value = '';
912
- // Clear attached files and chips
913
- attachedFiles = [];
914
- attachedFilePreviewContainer.innerHTML = '';
915
- // Trigger autoGrow to reset padding and alignment
916
- autoGrowTextarea();
917
- userInput.dispatchEvent(new Event('input', { bubbles: true }));
918
 
 
 
 
919
 
920
- typingIndicator.classList.remove('d-none');
921
- scrollToBottom();
 
 
 
922
 
923
- // --- MODIFIED: Create FormData for sending text and files ---
924
- const formData = new FormData();
925
- formData.append('message', text);
926
- formData.append('api_key', 'gakr-ai-2025-secret'); // ← ADD THIS LINE
927
- attachedFiles.forEach((file, index) => {
928
- formData.append(`file${index}`, file);
929
- });
930
 
931
- try {
932
- const response = await fetch('/api/chat', {
933
- method: 'POST',
934
- // Do NOT set 'Content-Type': 'application/json' for FormData.
935
- // The browser sets the correct 'Content-Type: multipart/form-data' automatically.
936
- body: formData
937
- });
938
-
939
- if (!response.ok) {
940
- console.error('HTTP error', response.status);
941
- const errorText = await response.text();
942
- throw new Error(`HTTP error ${response.status}: ${errorText}`);
943
- }
944
 
945
- const data = await response.json();
946
- console.log("AI response received:", data);
947
-
948
- typingIndicator.classList.add('d-none');
949
-
950
- let responseText = data;
951
- if (typeof data === 'object' && data !== null) {
952
- responseText = data.response;
953
- if (responseText === undefined) {
954
- try {
955
- responseText = JSON.stringify(data, null, 2);
956
- } catch (e) {
957
- responseText = data.toString();
958
- }
959
- }
960
- } else {
961
- responseText = String(data);
962
  }
 
 
 
 
963
 
964
- addMessage(responseText, 'ai');
965
- messageCount++;
966
 
967
- if (messageCount === 5 && !loginPrompt.classList.contains('show')) {
968
- loginPrompt.classList.add('show');
969
- }
970
- } catch (error) {
971
- console.error('Fetch Error:', error);
972
- typingIndicator.classList.add('d-none');
973
- if (chatContainer) {
974
- addMessage('Sorry, I encountered an error sending your message or files. Please try again.', 'ai');
975
- }
976
- } finally {
977
- if (typingIndicator) {
978
- typingIndicator.classList.add('d-none');
979
- }
980
- scrollToBottom();
981
- }
982
  }
 
 
 
 
 
 
 
 
 
 
 
983
 
984
- // Function to add message to chat (keep existing)
985
- function addMessage(text, type) {
986
- const messageDiv = document.createElement('div');
987
- messageDiv.className = `gemini-message gemini-message-${type}`;
988
 
989
- const headerDiv = document.createElement('div');
990
- headerDiv.className = 'gemini-message-header';
991
 
992
- const avatarDiv = document.createElement('div');
993
- avatarDiv.className = 'gemini-message-avatar';
994
 
995
- const avatarIcon = document.createElement('i');
996
- avatarIcon.className = type === 'user' ? 'fas fa-user' : 'fas fa-robot';
997
 
998
- const nameSpan = document.createElement('span');
999
- nameSpan.textContent = type === 'user' ? 'You' : 'GAKR AI';
1000
 
1001
- const contentDiv = document.createElement('div');
1002
- contentDiv.className = 'gemini-message-content';
1003
- contentDiv.textContent = text; // Use textContent to prevent XSS if text comes from API
1004
 
1005
- avatarDiv.appendChild(avatarIcon);
1006
- headerDiv.appendChild(avatarDiv);
1007
- headerDiv.appendChild(nameSpan);
1008
 
1009
- messageDiv.appendChild(headerDiv);
1010
- messageDiv.appendChild(contentDiv);
1011
 
1012
- chatContainer.appendChild(messageDiv);
1013
 
1014
- scrollToBottom();
1015
- }
1016
 
1017
- // Function to scroll chat to bottom (keep existing)
1018
- function scrollToBottom() {
1019
- setTimeout(() => {
1020
- if (chatContainer) {
1021
- chatContainer.scrollTop = chatContainer.scrollHeight;
1022
- }
1023
- }, 0);
1024
  }
 
 
1025
 
1026
- // Initialize based on URL query params (keep existing)
1027
- const urlParams = new URLSearchParams(window.location.search);
1028
- const initialQuery = urlParams.get('q');
1029
 
1030
- if (initialQuery) {
1031
- userInput.value = initialQuery;
1032
- userInput.dispatchEvent(new Event('input', { bubbles: true }));
1033
 
1034
- setTimeout(function() {
1035
- if (submitButton && !submitButton.classList.contains('disabled')) {
1036
- submitButton.click();
1037
- }
1038
- }, 100);
 
 
 
 
1039
  } else {
1040
- if (userInput) {
1041
- // Initial check for submit button state
1042
- if (userInput.value.trim().length === 0 && attachedFiles.length === 0) {
1043
- if (submitButton) submitButton.classList.add('disabled');
1044
- } else {
1045
- if (submitButton) submitButton.classList.remove('disabled');
1046
- }
1047
- // Trigger autoGrowTextarea immediately on load for correct initial height
1048
- autoGrowTextarea();
1049
- }
1050
  }
1051
- });
1052
- </script>
 
 
 
 
 
 
1053
  </body>
1054
  </html>
 
668
  </div>
669
  </div>
670
  </div>
671
+ <script>
672
+ document.addEventListener('DOMContentLoaded', function() {
673
+ const chatContainer = document.getElementById('chatContainer');
674
+ const userInput = document.getElementById('userInput'); // textarea
675
+ const submitButton = document.getElementById('submitButton');
676
+ const typingIndicator = document.getElementById('typingIndicator');
677
+ const welcomeMessage = document.getElementById('welcomeMessage');
678
+ const initialMessage = document.getElementById('initialMessage');
679
+ const loginPrompt = document.getElementById('loginPrompt');
680
+ const continueGuest = document.getElementById('continueGuest');
681
+
682
+ const addButton = document.getElementById('addButton');
683
+ const attachmentOptionsContainer = document.querySelector('.attachment-options-container');
684
+
685
+ const cameraInput = document.getElementById('cameraInput');
686
+ const galleryInput = document.getElementById('galleryInput');
687
+ const filesInput = document.getElementById('filesInput');
688
+ const attachedFilePreviewContainer = document.getElementById('attachedFilePreviewContainer');
689
+ const inputContainer = document.getElementById('inputContainer');
690
+
691
+ let messageCount = 0;
692
+ let attachedFiles = [];
693
+
694
+ // Simple login flag (set in auth flow: localStorage.setItem("gakr_is_logged_in","true"))
695
+ const isLoggedIn = localStorage.getItem("gakr_is_logged_in") === "true";
696
+
697
+ // --- Textarea Auto-Grow & Scroll Logic (original, kept) ---
698
+ const style = getComputedStyle(userInput);
699
+ const lineHeight = parseFloat(style.lineHeight);
700
+ const minHeight = lineHeight;
701
+ const maxHeight = lineHeight * 5;
702
+
703
+ const initialInputContainerPaddingTop = parseFloat(getComputedStyle(inputContainer).paddingTop);
704
+ const initialInputContainerPaddingBottom = parseFloat(getComputedStyle(inputContainer).paddingBottom);
705
+ const initialActionButtonsHeight = document.querySelector('.gemini-input-actions').offsetHeight;
706
+
707
+ function autoGrowTextarea() {
708
+ if (!userInput) {
709
+ console.error("autoGrowTextarea called but userInput element not found.");
710
+ return;
711
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
712
 
713
+ userInput.style.height = 'auto';
714
+ const scrollHeight = userInput.scrollHeight;
 
 
 
 
 
715
 
716
+ let newTextareaHeight = Math.min(Math.max(scrollHeight, minHeight), maxHeight);
717
+ userInput.style.height = newTextareaHeight + 'px';
 
 
718
 
719
+ if (attachedFiles.length > 0) {
720
+ userInput.style.paddingTop = '0px';
721
+ userInput.style.paddingBottom = '0px';
722
+ inputContainer.style.alignItems = 'flex-start';
723
+ } else {
724
+ userInput.style.paddingTop = '0px';
725
+ userInput.style.paddingBottom = '0px';
726
+ inputContainer.style.alignItems = 'flex-end';
727
+ }
728
 
729
+ if (scrollHeight > maxHeight) {
730
+ userInput.style.overflowY = 'auto';
731
+ } else {
732
+ userInput.style.overflowY = 'hidden';
733
+ }
 
 
734
 
735
+ // Prompt is required; files alone cannot send
736
+ if (userInput.value.trim().length > 0) {
737
+ submitButton.classList.remove('disabled');
738
+ } else {
739
+ submitButton.classList.add('disabled');
740
+ }
741
+ }
742
 
743
+ userInput.addEventListener('input', autoGrowTextarea);
744
+ setTimeout(autoGrowTextarea, 0);
745
+ // --- End Textarea Auto-Grow & Scroll Logic ---
 
746
 
 
 
 
 
 
747
 
748
+ // Handle Enter key for sending (Shift+Enter = newline)
749
+ userInput.addEventListener('keydown', function(event) {
750
+ if (event.key === 'Enter' && !event.shiftKey && !submitButton.classList.contains('disabled')) {
751
+ event.preventDefault();
752
+ sendMessage();
753
+ }
754
+ });
755
 
756
+ // Handle submit button click
757
+ submitButton.addEventListener('click', function() {
758
+ if (!this.classList.contains('disabled')) {
759
+ sendMessage();
760
+ }
761
+ });
762
+
763
+ // Existing “continue as guest” modal
764
+ continueGuest.addEventListener('click', function() {
765
+ loginPrompt.classList.remove('show');
766
+ });
767
+
768
+ // --- Handle Add Button Click ---
769
+ addButton.addEventListener('click', function(event) {
770
+ event.stopPropagation();
771
+ attachmentOptionsContainer.classList.toggle('visible');
772
+ });
773
+
774
+ // --- Attachment Options ---
775
+ document.getElementById('cameraOption').addEventListener('click', function() {
776
+ cameraInput.click();
777
+ attachmentOptionsContainer.classList.remove('visible');
778
+ });
779
+
780
+ document.getElementById('galleryOption').addEventListener('click', function() {
781
+ galleryInput.click();
782
+ attachmentOptionsContainer.classList.remove('visible');
783
+ });
784
+
785
+ document.getElementById('filesOption').addEventListener('click', function() {
786
+ filesInput.click();
787
+ attachmentOptionsContainer.classList.remove('visible');
788
+ });
789
+
790
+ document.getElementById('driveOption').addEventListener('click', function() {
791
+ alert("Google Drive integration requires backend setup and Google API access.");
792
+ attachmentOptionsContainer.classList.remove('visible');
793
+ });
794
+
795
+ // --- File input change (camera/gallery/files) ---
796
+ [cameraInput, galleryInput, filesInput].forEach(input => {
797
+ input.addEventListener('change', function(event) {
798
+ const files = event.target.files;
799
+ if (files.length > 0) {
800
+ for (let i = 0; i < files.length; i++) {
801
+ attachedFiles.push(files[i]);
802
+ addFileChip(files[i]);
803
+ }
804
+ autoGrowTextarea();
805
+ userInput.focus();
806
+ }
807
+ event.target.value = '';
808
+ });
809
+ });
810
+
811
+ function addFileChip(file) {
812
+ const chip = document.createElement('div');
813
+ chip.className = 'attached-file-chip';
814
+ chip.setAttribute('data-filename', file.name);
815
+
816
+ let previewElement;
817
+ if (file.type.startsWith('image/')) {
818
+ const img = document.createElement('img');
819
+ img.src = URL.createObjectURL(file);
820
+ img.alt = file.name;
821
+ img.onload = () => URL.revokeObjectURL(img.src);
822
+ previewElement = img;
823
+ } else {
824
+ const icon = document.createElement('i');
825
+ icon.className = 'fas fa-paperclip';
826
+ previewElement = icon;
827
+ }
828
 
829
+ const fileNameSpan = document.createElement('span');
830
+ fileNameSpan.className = 'file-name';
831
+ fileNameSpan.textContent = file.name;
 
832
 
833
+ const removeButton = document.createElement('span');
834
+ removeButton.className = 'remove-file';
835
+ removeButton.innerHTML = '&times;';
836
+ removeButton.addEventListener('click', function() {
837
+ removeFileChip(file.name);
838
+ });
839
 
840
+ chip.appendChild(previewElement);
841
+ chip.appendChild(fileNameSpan);
842
+ chip.appendChild(removeButton);
 
 
 
 
 
 
 
 
 
 
 
 
 
843
 
844
+ attachedFilePreviewContainer.appendChild(chip);
845
+ autoGrowTextarea();
846
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
847
 
848
+ function removeFileChip(filename) {
849
+ const chipToRemove = attachedFilePreviewContainer.querySelector(`[data-filename="${filename}"]`);
850
+ if (chipToRemove) {
851
+ attachedFilePreviewContainer.removeChild(chipToRemove);
852
+ attachedFiles = attachedFiles.filter(file => file.name !== filename);
853
+ autoGrowTextarea();
854
+ }
855
+ }
 
 
 
 
 
 
 
 
 
 
 
 
856
 
857
+ // Hide attachment options when clicking outside
858
+ document.addEventListener('click', function(event) {
859
+ const isClickInsideAddButton = addButton && addButton.contains(event.target);
860
+ const isClickInsideOptions = attachmentOptionsContainer && attachmentOptionsContainer.contains(event.target);
 
 
 
 
 
861
 
862
+ if (attachmentOptionsContainer && attachmentOptionsContainer.classList.contains('visible') && !isClickInsideAddButton && !isClickInsideOptions) {
863
+ attachmentOptionsContainer.classList.remove('visible');
864
+ }
865
+ });
866
 
867
+ if (attachmentOptionsContainer) {
868
+ attachmentOptionsContainer.addEventListener('click', function(event){
869
+ event.stopPropagation();
870
+ });
871
+ }
872
+
873
+ // --- Centered login reminder mini-window ---
874
+ let inlineLoginPromptShown = false;
875
+ function maybeShowInlineLoginPrompt() {
876
+ if (isLoggedIn) return;
877
+ if (inlineLoginPromptShown) return;
878
+ if (messageCount % 5 !== 0) return;
879
+
880
+ inlineLoginPromptShown = true;
881
+
882
+ const overlay = document.createElement('div');
883
+ overlay.style.position = 'fixed';
884
+ overlay.style.inset = '0';
885
+ overlay.style.backgroundColor = 'rgba(0,0,0,0.35)';
886
+ overlay.style.display = 'flex';
887
+ overlay.style.alignItems = 'center';
888
+ overlay.style.justifyContent = 'center';
889
+ overlay.style.zIndex = '9999';
890
+
891
+ const card = document.createElement('div');
892
+ card.style.width = '100%';
893
+ card.style.maxWidth = '420px';
894
+ card.style.borderRadius = '16px';
895
+ card.style.backgroundColor = 'var(--bs-body-bg)';
896
+ card.style.border = '1px solid var(--bs-border-color)';
897
+ card.style.boxShadow = '0 16px 40px rgba(0,0,0,0.25)';
898
+ card.style.padding = '1.25rem 1.5rem';
899
+ card.style.display = 'flex';
900
+ card.style.flexDirection = 'column';
901
+ card.style.gap = '0.75rem';
902
+
903
+ const title = document.createElement('div');
904
+ title.textContent = "Save your conversations";
905
+ title.style.fontWeight = '600';
906
+ title.style.fontSize = '1rem';
907
+
908
+ const text = document.createElement('div');
909
+ text.textContent = "Log in to keep your chat history across devices. If you continue as a guest, your conversations may be lost later.";
910
+ text.style.fontSize = '0.9rem';
911
+ text.style.opacity = '0.9';
912
+
913
+ const buttonRow = document.createElement('div');
914
+ buttonRow.style.display = 'flex';
915
+ buttonRow.style.gap = '0.75rem';
916
+ buttonRow.style.marginTop = '0.5rem';
917
+
918
+ const skipBtn = document.createElement('button');
919
+ skipBtn.textContent = "Continue without login";
920
+ skipBtn.style.flex = '1';
921
+ skipBtn.style.borderRadius = '999px';
922
+ skipBtn.style.border = '1px solid var(--bs-border-color)';
923
+ skipBtn.style.backgroundColor = 'transparent';
924
+ skipBtn.style.color = 'var(--bs-body-color)';
925
+ skipBtn.style.padding = '0.5rem 0.75rem';
926
+ skipBtn.style.fontSize = '0.85rem';
927
+ skipBtn.style.cursor = 'pointer';
928
+
929
+ const loginBtn = document.createElement('button');
930
+ loginBtn.textContent = "Log in to save chats";
931
+ loginBtn.style.flex = '1';
932
+ loginBtn.style.borderRadius = '999px';
933
+ loginBtn.style.border = 'none';
934
+ loginBtn.style.backgroundColor = 'var(--gakr-blue)';
935
+ loginBtn.style.color = '#fff';
936
+ loginBtn.style.padding = '0.5rem 0.75rem';
937
+ loginBtn.style.fontSize = '0.85rem';
938
+ loginBtn.style.cursor = 'pointer';
939
+
940
+ skipBtn.addEventListener('click', function () {
941
+ overlay.remove();
942
+ });
943
 
944
+ loginBtn.addEventListener('click', function () {
945
+ window.location.href = 'auth.html';
946
+ });
 
947
 
948
+ buttonRow.appendChild(skipBtn);
949
+ buttonRow.appendChild(loginBtn);
950
+ card.appendChild(title);
951
+ card.appendChild(text);
952
+ card.appendChild(buttonRow);
953
+ overlay.appendChild(card);
954
+ document.body.appendChild(overlay);
955
+ }
956
+
957
+ // --- sendMessage: prompt required, files optional, generic error ---
958
+ async function sendMessage() {
959
+ const text = userInput.value.trim();
960
+
961
+ // Prompt is required; files alone cannot be sent
962
+ if (!text) {
963
+ return;
964
+ }
965
 
966
+ if (!welcomeMessage.classList.contains('d-none')) {
967
+ welcomeMessage.classList.add('d-none');
968
+ }
969
 
970
+ if (initialMessage.classList.contains('d-none')) {
971
+ initialMessage.classList.remove('d-none');
972
+ }
 
973
 
974
+ const filesSnapshot = [...attachedFiles];
 
 
975
 
976
+ let userMessageContent = text;
977
+ if (filesSnapshot.length > 0) {
978
+ const fileNames = filesSnapshot.map(f => f.name).join(', ');
979
+ userMessageContent += ` (Attached: ${fileNames})`;
980
+ }
981
+ addMessage(userMessageContent, 'user');
982
+ messageCount++;
983
 
984
+ userInput.value = '';
985
+ attachedFiles = [];
986
+ attachedFilePreviewContainer.innerHTML = '';
987
+ autoGrowTextarea();
988
+ userInput.dispatchEvent(new Event('input', { bubbles: true }));
 
 
989
 
990
+ typingIndicator.classList.remove('d-none');
991
+ scrollToBottom();
 
 
 
 
 
992
 
993
+ const formData = new FormData();
994
+ formData.append('api_key', 'gakr-ai-2025-secret');
995
+ formData.append('prompt', text);
996
 
997
+ if (filesSnapshot.length > 0) {
998
+ filesSnapshot.forEach((file) => {
999
+ formData.append('files', file);
1000
+ });
1001
+ }
1002
 
1003
+ try {
1004
+ const response = await fetch('/api/analyze', {
1005
+ method: 'POST',
1006
+ body: formData
1007
+ });
 
 
1008
 
1009
+ if (!response.ok) {
1010
+ typingIndicator.classList.add('d-none');
1011
+ addMessage('There was a server issue. Please try again later.', 'ai');
1012
+ return;
1013
+ }
 
 
 
 
 
 
 
 
1014
 
1015
+ const data = await response.json();
1016
+ typingIndicator.classList.add('d-none');
1017
+
1018
+ let responseText = data;
1019
+ if (typeof data === 'object' && data !== null) {
1020
+ responseText = data.response;
1021
+ if (responseText === undefined) {
1022
+ try {
1023
+ responseText = JSON.stringify(data, null, 2);
1024
+ } catch (e) {
1025
+ responseText = data.toString();
 
 
 
 
 
 
1026
  }
1027
+ }
1028
+ } else {
1029
+ responseText = String(data);
1030
+ }
1031
 
1032
+ addMessage(responseText, 'ai');
 
1033
 
1034
+ if (messageCount > 0 && messageCount % 5 === 0) {
1035
+ maybeShowInlineLoginPrompt();
 
 
 
 
 
 
 
 
 
 
 
 
 
1036
  }
1037
+ } catch (error) {
1038
+ console.error('Fetch Error:', error);
1039
+ typingIndicator.classList.add('d-none');
1040
+ addMessage('There was a server issue. Please try again later.', 'ai');
1041
+ } finally {
1042
+ if (typingIndicator) {
1043
+ typingIndicator.classList.add('d-none');
1044
+ }
1045
+ scrollToBottom();
1046
+ }
1047
+ }
1048
 
1049
+ function addMessage(text, type) {
1050
+ const messageDiv = document.createElement('div');
1051
+ messageDiv.className = `gemini-message gemini-message-${type}`;
 
1052
 
1053
+ const headerDiv = document.createElement('div');
1054
+ headerDiv.className = 'gemini-message-header';
1055
 
1056
+ const avatarDiv = document.createElement('div');
1057
+ avatarDiv.className = 'gemini-message-avatar';
1058
 
1059
+ const avatarIcon = document.createElement('i');
1060
+ avatarIcon.className = type === 'user' ? 'fas fa-user' : 'fas fa-robot';
1061
 
1062
+ const nameSpan = document.createElement('span');
1063
+ nameSpan.textContent = type === 'user' ? 'You' : 'GAKR AI';
1064
 
1065
+ const contentDiv = document.createElement('div');
1066
+ contentDiv.className = 'gemini-message-content';
1067
+ contentDiv.textContent = text;
1068
 
1069
+ avatarDiv.appendChild(avatarIcon);
1070
+ headerDiv.appendChild(avatarDiv);
1071
+ headerDiv.appendChild(nameSpan);
1072
 
1073
+ messageDiv.appendChild(headerDiv);
1074
+ messageDiv.appendChild(contentDiv);
1075
 
1076
+ chatContainer.appendChild(messageDiv);
1077
 
1078
+ scrollToBottom();
1079
+ }
1080
 
1081
+ function scrollToBottom() {
1082
+ setTimeout(() => {
1083
+ if (chatContainer) {
1084
+ chatContainer.scrollTop = chatContainer.scrollHeight;
 
 
 
1085
  }
1086
+ }, 0);
1087
+ }
1088
 
1089
+ // Initialize from URL ?q=...
1090
+ const urlParams = new URLSearchParams(window.location.search);
1091
+ const initialQuery = urlParams.get('q');
1092
 
1093
+ if (initialQuery) {
1094
+ userInput.value = initialQuery;
1095
+ userInput.dispatchEvent(new Event('input', { bubbles: true }));
1096
 
1097
+ setTimeout(function() {
1098
+ if (submitButton && !submitButton.classList.contains('disabled')) {
1099
+ submitButton.click();
1100
+ }
1101
+ }, 100);
1102
+ } else {
1103
+ if (userInput) {
1104
+ if (userInput.value.trim().length === 0 && attachedFiles.length === 0) {
1105
+ if (submitButton) submitButton.classList.add('disabled');
1106
  } else {
1107
+ if (submitButton) submitButton.classList.remove('disabled');
 
 
 
 
 
 
 
 
 
1108
  }
1109
+ autoGrowTextarea();
1110
+ }
1111
+ }
1112
+ });
1113
+ </script>
1114
+
1115
+
1116
+
1117
  </body>
1118
  </html>