protae5544 commited on
Commit
eb61337
·
verified ·
1 Parent(s): f1574d6

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +71 -27
index.html CHANGED
@@ -303,8 +303,8 @@
303
  transition: all 0.3s ease; color: var(--text-secondary);
304
  }
305
  .chip:hover {
306
- background: var(--accent-orange); color: var(--bg-primary);
307
- transform: translateY(-2px); box-shadow: 0 6px 15px var(--shadow-color-orange);
308
  }
309
 
310
  .floating-buttons {
@@ -319,8 +319,8 @@
319
  transition: all 0.3s ease; padding: 0; color: var(--text-primary);
320
  }
321
  .floating-btn:hover {
322
- background: var(--accent-green); color: var(--bg-primary);
323
- transform: scale(1.1); box-shadow: 0 0 15px var(--accent-green);
324
  }
325
 
326
  #chatMode { display: none; flex: 1; flex-direction: column; }
@@ -401,7 +401,7 @@
401
  <div class="input-container">
402
  <button id="attachButton" class="input-btn"><span class="material-icons">attach_file</span></button>
403
  <input type="file" id="fileInput" multiple accept="image/*" style="display: none;">
404
- <span id="fileName" style="font-size: 0.8rem; color: var(--accent-green); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; max-width: 100px;"></span>
405
  <textarea id="userInput" placeholder="พิมพ์ข้อความ..."></textarea>
406
  <button id="sendButton" class="input-btn"><span class="material-icons">send</span></button>
407
  </div>
@@ -412,13 +412,15 @@
412
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
413
  <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js"></script>
414
  <script>
415
- const HUGGINGFACE_TOKEN = "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"; // <<< !!! ใส่ Hugging Face Token ของคุณที่นี่ !!!
 
416
 
417
  let attachedFiles = [];
418
  let currentCarouselIndex = 0;
419
  let isLoading = false;
420
  let carouselElement;
421
 
 
422
  const prompts = [
423
  { id: 'primarySystemPrompt', label: '🎯 Primary System Prompt', value: 'You are a powerful AI assistant that excels at understanding code, images, and technical content. Provide clear, accurate, and helpful responses. Focus on practical solutions and detailed explanations.' },
424
  { id: 'ocrSystemPrompt', label: '👁️ OCR System Prompt', value: 'Extract all text from the provided image clearly and accurately. Preserve formatting, structure, and layout when possible. If text is unclear, indicate uncertain parts.' },
@@ -437,6 +439,7 @@
437
  switchMode('carousel'); // Start with carousel
438
  });
439
 
 
440
  function populateCarousel() {
441
  const carousel = document.getElementById('promptCarousel');
442
  const indicator = document.getElementById('carouselIndicator');
@@ -466,12 +469,14 @@
466
  carousel.dataset.translateZ = translateZ; // Store for later use
467
  }
468
 
 
469
  function loadSettings() {
470
  document.getElementById('modelSelect').value = localStorage.getItem('selectedModel') || 'Qwen/Qwen2.5-Coder-32B-Instruct';
471
  document.getElementById('ocrModelSelect').value = localStorage.getItem('selectedOcrModel') || 'none';
472
- // Values are loaded during populateCarousel now
473
  }
474
 
 
475
  function saveSettings() {
476
  localStorage.setItem('selectedModel', document.getElementById('modelSelect').value);
477
  localStorage.setItem('selectedOcrModel', document.getElementById('ocrModelSelect').value);
@@ -479,6 +484,7 @@
479
  showNotification('บันทึกแล้ว!', 'success');
480
  }
481
 
 
482
  function setupEventListeners() {
483
  document.getElementById('carouselModeBtn').addEventListener('click', () => switchMode('carousel'));
484
  document.getElementById('chatModeBtn').addEventListener('click', () => switchMode('chat'));
@@ -520,6 +526,7 @@
520
  }
521
  }
522
 
 
523
  function switchMode(mode) {
524
  const carouselMode = document.getElementById('carouselMode');
525
  const chatMode = document.getElementById('chatMode');
@@ -545,17 +552,20 @@
545
  }
546
  }
547
 
 
548
  function rotateCarousel(direction) {
549
  const totalCards = prompts.length;
550
  currentCarouselIndex = (currentCarouselIndex + direction + totalCards) % totalCards;
551
  updateCarouselPosition();
552
  }
553
 
 
554
  function goToCarouselIndex(index) {
555
  currentCarouselIndex = index;
556
  updateCarouselPosition();
557
  }
558
 
 
559
  function updateCarouselPosition() {
560
  const totalCards = prompts.length;
561
  const anglePerCard = 360 / totalCards;
@@ -573,17 +583,20 @@
573
  });
574
  }
575
 
 
576
  function applyPromptsAndSwitchToChat() {
577
  saveSettings();
578
  switchMode('chat');
579
  showNotification('ใช้ Prompt แล้ว!', 'success');
580
  }
581
 
 
582
  function handleFileSelect(e) {
583
  attachedFiles = Array.from(e.target.files);
584
  updateFileDisplay();
585
  }
586
 
 
587
  function updateFileDisplay() {
588
  const fileNameEl = document.getElementById('fileName');
589
  if (attachedFiles.length === 0) fileNameEl.textContent = '';
@@ -591,6 +604,7 @@
591
  else fileNameEl.textContent = `${attachedFiles.length} ไฟล์`;
592
  }
593
 
 
594
  function handleKeyDown(e) {
595
  if (e.key === 'Enter' && !e.shiftKey) {
596
  e.preventDefault();
@@ -598,22 +612,23 @@
598
  }
599
  }
600
 
 
601
  async function sendMessage() {
602
  const userInput = document.getElementById('userInput');
603
  const message = userInput.value.trim();
604
- if (!message && attachedFiles.length === 0 || isLoading) return;
605
 
606
  setLoading(true);
607
  const chatContainer = document.getElementById('chatContainer');
608
  const userMessage = message;
609
- const currentFiles = [...attachedFiles];
610
 
611
- userInput.value = '';
612
- attachedFiles = [];
613
  updateFileDisplay();
614
 
615
  if (userMessage) addMessage(userMessage, 'user');
616
- currentFiles.forEach(file => addImageMessage(URL.createObjectURL(file), 'user'));
617
  scrollToBottom(chatContainer);
618
 
619
  try {
@@ -629,24 +644,40 @@
629
  }
630
  }
631
 
 
632
  async function callHuggingFaceAPI(message, files) {
633
  const model = document.getElementById('modelSelect').value;
634
  const ocrModel = document.getElementById('ocrModelSelect').value;
635
  let fullPrompt = `${localStorage.getItem('primarySystemPrompt')}\n\n${localStorage.getItem('promptPrefix')}\n\nUser: ${message}`;
636
 
637
- if (files.length > 0 && ocrModel !== 'none' && (model.includes('VL') || model.includes('Florence'))) {
638
- try {
639
- const ocrText = await performOCR(files[0], ocrModel);
640
- fullPrompt += `\n\n[Image Content: ${ocrText}]`;
641
- } catch (error) {
642
- fullPrompt += `\n\n[Image attached, OCR failed]`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
643
  }
644
- } else if (files.length > 0) {
645
- fullPrompt += `\n\n[Image attached]`;
646
  }
647
 
648
  fullPrompt += `\n\n${localStorage.getItem('promptSuffix')}\n\nAI:`;
649
 
 
 
650
  const payload = {
651
  inputs: fullPrompt,
652
  parameters: { max_new_tokens: 2048, temperature: 0.6, top_p: 0.9 }
@@ -659,27 +690,32 @@
659
  });
660
 
661
  if (!response.ok) {
662
- const errorBody = await response.text();
663
- throw new Error(`HTTP ${response.status}: ${errorBody}`);
664
  }
665
  const data = await response.json();
666
  if (data.error) throw new Error(data.error);
667
  let text = data[0]?.generated_text || 'No response.';
 
668
  return text.replace(fullPrompt, '').trim();
669
  }
670
 
 
671
  async function performOCR(file, ocrModel) {
672
  const response = await fetch(`https://api-inference.huggingface.co/models/${ocrModel}`, {
673
  method: 'POST',
674
  headers: { 'Authorization': `Bearer ${HUGGINGFACE_TOKEN}` },
675
  body: file
676
  });
677
- if (!response.ok) throw new Error(`OCR failed: ${response.statusText}`);
 
 
 
678
  const data = await response.json();
679
  return data[0]?.generated_text || 'Could not extract text.';
680
  }
681
 
682
-
683
  function addMessage(content, sender, isError = false) {
684
  const chatContainer = document.getElementById('chatContainer');
685
  const messageDiv = document.createElement('div');
@@ -687,9 +723,12 @@
687
  if (isError) messageDiv.style.border = '1px solid var(--error)';
688
 
689
  if (sender === 'ai') {
 
690
  messageDiv.innerHTML = marked.parse(content);
691
  messageDiv.querySelectorAll('pre code').forEach(block => {
 
692
  hljs.highlightElement(block);
 
693
  addCodeTools(block.parentElement);
694
  });
695
  } else {
@@ -698,6 +737,7 @@
698
  chatContainer.appendChild(messageDiv);
699
  }
700
 
 
701
  function addImageMessage(imageUrl, sender) {
702
  const chatContainer = document.getElementById('chatContainer');
703
  const messageDiv = document.createElement('div');
@@ -706,6 +746,7 @@
706
  chatContainer.appendChild(messageDiv);
707
  }
708
 
 
709
  function addCodeTools(preElement) {
710
  const toolsDiv = document.createElement('div');
711
  toolsDiv.className = 'code-tools';
@@ -720,7 +761,7 @@
720
  preElement.appendChild(toolsDiv);
721
  }
722
 
723
-
724
  function setLoading(loading) {
725
  isLoading = loading;
726
  const sendButton = document.getElementById('sendButton');
@@ -728,6 +769,7 @@
728
  sendButton.disabled = loading;
729
  }
730
 
 
731
  function toggleFullscreen() {
732
  const icon = document.querySelector('#fullscreenToggle .material-icons');
733
  if (!document.fullscreenElement) {
@@ -739,16 +781,18 @@
739
  }
740
  }
741
 
 
742
  function showNotification(message, type = 'success') {
743
  // Simple alert for now, can be replaced with a styled div
744
  console.log(`Notification (${type}): ${message}`);
745
- // alert(`(${type}) ${message}`);
746
  }
747
 
 
748
  function scrollToBottom(element) {
749
  element.scrollTo({ top: element.scrollHeight, behavior: 'smooth' });
750
  }
751
 
752
  </script>
753
  </body>
754
- </html>
 
303
  transition: all 0.3s ease; color: var(--text-secondary);
304
  }
305
  .chip:hover {
306
+ background: var(--accent-secondary); color: var(--bg-primary);
307
+ transform: translateY(-2px); box-shadow: 0 6px 15px rgba(255, 153, 51, 0.3);
308
  }
309
 
310
  .floating-buttons {
 
319
  transition: all 0.3s ease; padding: 0; color: var(--text-primary);
320
  }
321
  .floating-btn:hover {
322
+ background: var(--accent-primary); color: var(--bg-primary);
323
+ transform: scale(1.1); box-shadow: 0 0 15px var(--accent-primary);
324
  }
325
 
326
  #chatMode { display: none; flex: 1; flex-direction: column; }
 
401
  <div class="input-container">
402
  <button id="attachButton" class="input-btn"><span class="material-icons">attach_file</span></button>
403
  <input type="file" id="fileInput" multiple accept="image/*" style="display: none;">
404
+ <span id="fileName" style="font-size: 0.8rem; color: var(--accent-primary); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; max-width: 100px;"></span>
405
  <textarea id="userInput" placeholder="พิมพ์ข้อความ..."></textarea>
406
  <button id="sendButton" class="input-btn"><span class="material-icons">send</span></button>
407
  </div>
 
412
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
413
  <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js"></script>
414
  <script>
415
+ // Set up the Hugging Face API Token
416
+ const HUGGINGFACE_TOKEN = "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";
417
 
418
  let attachedFiles = [];
419
  let currentCarouselIndex = 0;
420
  let isLoading = false;
421
  let carouselElement;
422
 
423
+ // Default prompts for the carousel
424
  const prompts = [
425
  { id: 'primarySystemPrompt', label: '🎯 Primary System Prompt', value: 'You are a powerful AI assistant that excels at understanding code, images, and technical content. Provide clear, accurate, and helpful responses. Focus on practical solutions and detailed explanations.' },
426
  { id: 'ocrSystemPrompt', label: '👁️ OCR System Prompt', value: 'Extract all text from the provided image clearly and accurately. Preserve formatting, structure, and layout when possible. If text is unclear, indicate uncertain parts.' },
 
439
  switchMode('carousel'); // Start with carousel
440
  });
441
 
442
+ // Populates the carousel with prompt cards and navigation dots
443
  function populateCarousel() {
444
  const carousel = document.getElementById('promptCarousel');
445
  const indicator = document.getElementById('carouselIndicator');
 
469
  carousel.dataset.translateZ = translateZ; // Store for later use
470
  }
471
 
472
+ // Loads saved settings from local storage
473
  function loadSettings() {
474
  document.getElementById('modelSelect').value = localStorage.getItem('selectedModel') || 'Qwen/Qwen2.5-Coder-32B-Instruct';
475
  document.getElementById('ocrModelSelect').value = localStorage.getItem('selectedOcrModel') || 'none';
476
+ // Prompt values are loaded directly in populateCarousel
477
  }
478
 
479
+ // Saves current settings to local storage
480
  function saveSettings() {
481
  localStorage.setItem('selectedModel', document.getElementById('modelSelect').value);
482
  localStorage.setItem('selectedOcrModel', document.getElementById('ocrModelSelect').value);
 
484
  showNotification('บันทึกแล้ว!', 'success');
485
  }
486
 
487
+ // Sets up all event listeners for interactive elements
488
  function setupEventListeners() {
489
  document.getElementById('carouselModeBtn').addEventListener('click', () => switchMode('carousel'));
490
  document.getElementById('chatModeBtn').addEventListener('click', () => switchMode('chat'));
 
526
  }
527
  }
528
 
529
+ // Switches between carousel and chat modes
530
  function switchMode(mode) {
531
  const carouselMode = document.getElementById('carouselMode');
532
  const chatMode = document.getElementById('chatMode');
 
552
  }
553
  }
554
 
555
+ // Rotates the carousel by one card in the given direction
556
  function rotateCarousel(direction) {
557
  const totalCards = prompts.length;
558
  currentCarouselIndex = (currentCarouselIndex + direction + totalCards) % totalCards;
559
  updateCarouselPosition();
560
  }
561
 
562
+ // Jumps to a specific carousel card by index
563
  function goToCarouselIndex(index) {
564
  currentCarouselIndex = index;
565
  updateCarouselPosition();
566
  }
567
 
568
+ // Updates the visual position of the carousel based on currentCarouselIndex
569
  function updateCarouselPosition() {
570
  const totalCards = prompts.length;
571
  const anglePerCard = 360 / totalCards;
 
583
  });
584
  }
585
 
586
+ // Saves prompts and switches to chat mode
587
  function applyPromptsAndSwitchToChat() {
588
  saveSettings();
589
  switchMode('chat');
590
  showNotification('ใช้ Prompt แล้ว!', 'success');
591
  }
592
 
593
+ // Handles file selection for attachments
594
  function handleFileSelect(e) {
595
  attachedFiles = Array.from(e.target.files);
596
  updateFileDisplay();
597
  }
598
 
599
+ // Updates the displayed file name(s)
600
  function updateFileDisplay() {
601
  const fileNameEl = document.getElementById('fileName');
602
  if (attachedFiles.length === 0) fileNameEl.textContent = '';
 
604
  else fileNameEl.textContent = `${attachedFiles.length} ไฟล์`;
605
  }
606
 
607
+ // Handles key presses in the user input field
608
  function handleKeyDown(e) {
609
  if (e.key === 'Enter' && !e.shiftKey) {
610
  e.preventDefault();
 
612
  }
613
  }
614
 
615
+ // Sends a message to the Hugging Face API
616
  async function sendMessage() {
617
  const userInput = document.getElementById('userInput');
618
  const message = userInput.value.trim();
619
+ if (!message && attachedFiles.length === 0 || isLoading) return; // Prevent sending empty messages or while loading
620
 
621
  setLoading(true);
622
  const chatContainer = document.getElementById('chatContainer');
623
  const userMessage = message;
624
+ const currentFiles = [...attachedFiles]; // Snapshot files to send
625
 
626
+ userInput.value = ''; // Clear input
627
+ attachedFiles = []; // Clear attached files
628
  updateFileDisplay();
629
 
630
  if (userMessage) addMessage(userMessage, 'user');
631
+ currentFiles.forEach(file => addImageMessage(URL.createObjectURL(file), 'user')); // Display attached images
632
  scrollToBottom(chatContainer);
633
 
634
  try {
 
644
  }
645
  }
646
 
647
+ // Calls the Hugging Face Inference API
648
  async function callHuggingFaceAPI(message, files) {
649
  const model = document.getElementById('modelSelect').value;
650
  const ocrModel = document.getElementById('ocrModelSelect').value;
651
  let fullPrompt = `${localStorage.getItem('primarySystemPrompt')}\n\n${localStorage.getItem('promptPrefix')}\n\nUser: ${message}`;
652
 
653
+ if (files.length > 0) {
654
+ const imageData = await Promise.all(files.map(file => {
655
+ return new Promise((resolve) => {
656
+ const reader = new FileReader();
657
+ reader.onloadend = () => resolve(reader.result.split(',')[1]); // Get base64 content
658
+ reader.readAsDataURL(file);
659
+ });
660
+ }));
661
+
662
+ for (let i = 0; i < imageData.length; i++) {
663
+ fullPrompt += `\n\nImage:\n<img src="data:${files[i].type};base64,${imageData[i]}"/>`;
664
+
665
+ if (ocrModel !== 'none') {
666
+ try {
667
+ const ocrText = await performOCR(files[i], ocrModel);
668
+ fullPrompt += `\n\n[Image OCR Content: ${ocrText}]`;
669
+ } catch (ocrError) {
670
+ console.error(`OCR failed for file ${files[i].name}:`, ocrError);
671
+ fullPrompt += `\n\n[Image attached, OCR failed for this image]`;
672
+ }
673
+ }
674
  }
 
 
675
  }
676
 
677
  fullPrompt += `\n\n${localStorage.getItem('promptSuffix')}\n\nAI:`;
678
 
679
+ // Adjust payload for models that support multi-modal input if necessary.
680
+ // For now, we're encoding images into the prompt itself for simpler integration.
681
  const payload = {
682
  inputs: fullPrompt,
683
  parameters: { max_new_tokens: 2048, temperature: 0.6, top_p: 0.9 }
 
690
  });
691
 
692
  if (!response.ok) {
693
+ const errorBody = await response.json(); // Attempt to parse JSON error
694
+ throw new Error(`HTTP ${response.status}: ${JSON.stringify(errorBody.error || errorBody)}`);
695
  }
696
  const data = await response.json();
697
  if (data.error) throw new Error(data.error);
698
  let text = data[0]?.generated_text || 'No response.';
699
+ // Remove the prompt from the generated text, as the API often returns the full input + output
700
  return text.replace(fullPrompt, '').trim();
701
  }
702
 
703
+ // Performs OCR on an image file using a specified OCR model
704
  async function performOCR(file, ocrModel) {
705
  const response = await fetch(`https://api-inference.huggingface.co/models/${ocrModel}`, {
706
  method: 'POST',
707
  headers: { 'Authorization': `Bearer ${HUGGINGFACE_TOKEN}` },
708
  body: file
709
  });
710
+ if (!response.ok) {
711
+ const errorBody = await response.json();
712
+ throw new Error(`OCR failed: HTTP ${response.status}: ${JSON.stringify(errorBody.error || errorBody)}`);
713
+ }
714
  const data = await response.json();
715
  return data[0]?.generated_text || 'Could not extract text.';
716
  }
717
 
718
+ // Adds a text message to the chat display
719
  function addMessage(content, sender, isError = false) {
720
  const chatContainer = document.getElementById('chatContainer');
721
  const messageDiv = document.createElement('div');
 
723
  if (isError) messageDiv.style.border = '1px solid var(--error)';
724
 
725
  if (sender === 'ai') {
726
+ // Use marked.js for Markdown parsing
727
  messageDiv.innerHTML = marked.parse(content);
728
  messageDiv.querySelectorAll('pre code').forEach(block => {
729
+ // Highlight code blocks
730
  hljs.highlightElement(block);
731
+ // Add copy button to code blocks
732
  addCodeTools(block.parentElement);
733
  });
734
  } else {
 
737
  chatContainer.appendChild(messageDiv);
738
  }
739
 
740
+ // Adds an image message to the chat display
741
  function addImageMessage(imageUrl, sender) {
742
  const chatContainer = document.getElementById('chatContainer');
743
  const messageDiv = document.createElement('div');
 
746
  chatContainer.appendChild(messageDiv);
747
  }
748
 
749
+ // Adds copy and paste buttons to code blocks
750
  function addCodeTools(preElement) {
751
  const toolsDiv = document.createElement('div');
752
  toolsDiv.className = 'code-tools';
 
761
  preElement.appendChild(toolsDiv);
762
  }
763
 
764
+ // Sets the loading state of the UI
765
  function setLoading(loading) {
766
  isLoading = loading;
767
  const sendButton = document.getElementById('sendButton');
 
769
  sendButton.disabled = loading;
770
  }
771
 
772
+ // Toggles fullscreen mode
773
  function toggleFullscreen() {
774
  const icon = document.querySelector('#fullscreenToggle .material-icons');
775
  if (!document.fullscreenElement) {
 
781
  }
782
  }
783
 
784
+ // Displays a temporary notification (can be enhanced with styled toasts)
785
  function showNotification(message, type = 'success') {
786
  // Simple alert for now, can be replaced with a styled div
787
  console.log(`Notification (${type}): ${message}`);
788
+ // alert(`(${type}) ${message}`); // Uncomment for actual alerts, or implement a proper notification UI
789
  }
790
 
791
+ // Scrolls an element to its bottom
792
  function scrollToBottom(element) {
793
  element.scrollTo({ top: element.scrollHeight, behavior: 'smooth' });
794
  }
795
 
796
  </script>
797
  </body>
798
+ </html>