{"id":407,"date":"2025-03-27T19:16:35","date_gmt":"2025-03-27T19:16:35","guid":{"rendered":"https:\/\/neocloud05.com\/neo\/?page_id=407"},"modified":"2025-03-28T19:09:36","modified_gmt":"2025-03-28T19:09:36","slug":"laboratorio-2-0-4-modelo-conversacional-perfiles-anexos-completo","status":"publish","type":"page","link":"https:\/\/neocloud05.com\/neo\/laboratorio-2-0-4-modelo-conversacional-perfiles-anexos-completo\/","title":{"rendered":"Laboratorio 2.0.4 Modelo Conversacional Chat GPT"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"407\" class=\"elementor elementor-407\">\n\t\t\t\t<div class=\"elementor-element elementor-element-dead8d3 e-flex e-con-boxed e-con e-parent\" data-id=\"dead8d3\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-41ce17a elementor-widget elementor-widget-heading\" data-id=\"41ce17a\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Neo 2.0.6 Beta\n(Base Chat GPT)<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-9f96e12 e-flex e-con-boxed e-con e-parent\" data-id=\"9f96e12\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-ef60ff3 elementor-widget elementor-widget-html\" data-id=\"ef60ff3\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\n<html lang=\"es\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Chat IA con Configuraci\u00f3n<\/title>\n  <style>\n    * { box-sizing: border-box; margin: 0; padding: 0; }\n    html, body {\n      height: 100%; width: 100%; font-family: sans-serif;\n    }\n    body {\n      display: flex; justify-content: center; align-items: center;\n      background: #f0f0f0;\n    }\n    .chat-container {\n      width: 100%; max-width: 420px; height: 90vh;\n      background: white; display: flex; flex-direction: column;\n      border-radius: 20px; border: 1px solid #ddd;\n      box-shadow: 0 4px 20px rgba(0,0,0,0.1);\n      position: relative; overflow: hidden;\n    }\n    .chat-window {\n      flex: 1; padding: 1rem; overflow-y: auto;\n      background: #f7f7f7; display: flex; flex-direction: column;\n    }\n    .chat-message {\n      max-width: 75%; padding: 0.75rem 1rem; margin-bottom: 0.5rem;\n      border-radius: 1rem; background: #e9ecef;\n    }\n    .chat-message.user {\n      background: #cce5ff; align-self: flex-end;\n    }\n    .chat-message.bot {\n      align-self: flex-start;\n    }\n    .chat-input-area {\n      padding: 0.75rem; display: flex; gap: 0.5rem;\n      border-top: 1px solid #ccc; background: white; align-items: center;\n    }\n    .chat-input-area input[type=\"text\"] {\n      flex: 1; padding: 0.5rem 1rem;\n      border-radius: 999px; border: 1px solid #ccc;\n    }\n    .chat-input-area button {\n      padding: 0.5rem 0.75rem;\n      border: none; border-radius: 999px;\n      background: #007bff; color: white; cursor: pointer;\n    }\n    .file-upload input[type=\"file\"] { display: none; }\n    .file-upload { font-size: 18px; cursor: pointer; }\n    .file-item { display: flex; flex-direction: column; align-items: center; font-size: 12px; max-width: 100px; margin-top: 5px; }\n    .file-item img { width: 64px; height: 64px; object-fit: cover; border-radius: 8px; border: 1px solid #ccc; }\n    .file-item .file-icon { font-size: 40px; }\n    .chat-window.dragover { border: 2px dashed #007bff; background-color: #eef6ff; }\n    .sidebar {\n      position: fixed; top: 0; right: -300px; width: 300px; height: 100%;\n      background: white; box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1);\n      padding: 1rem; transition: right 0.3s ease;\n      display: flex; flex-direction: column; gap: 1rem; z-index: 999;\n    }\n    .sidebar.open { right: 0; }\n    .sidebar-toggle {\n      position: fixed; top: 1rem; right: 1rem; z-index: 1000;\n      background: #007bff; color: white; border: none; border-radius: 50%;\n      width: 44px; height: 44px; font-size: 22px; cursor: pointer;\n      box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);\n    }\n    label { font-size: 14px; font-weight: bold; }\n    select, input[type=\"range\"] { width: 100%; }\n    \n    #micBtn {\n        background-color: #007bff;\n        color: white;\n        border: none;\n        border-radius: 50%;\n        padding: 10px;\n        font-size: 20px;\n        transition: background-color 0.3s ease;\n      }\n    \n      #micBtn.active {\n        background-color: #dc3545 !important;\n        color: white;\n      }\n    \n      #toggleVoiceChat {\n        background-color: #007bff;\n        color: white;\n        border: none;\n        border-radius: 50%;\n        padding: 10px;\n        font-size: 20px;    transition: background-color 0.3s ease;\n      }\n    \n      #toggleVoiceChat.active {\n        background-color: #28a745 !important;\n        color: white;\n      }\n    \n      #listening-indicator {\n        display: none;\n        color: #28a745;\n        font-weight: bold;\n        margin-left: 10px;\n        font-size: 14px;\n      }\n    \n      #listening-indicator.active {\n        display: inline;\n      }\n\n\n    \n    \n<\/style>\n<\/head>\n<body>\n\n\n  <div class=\"chat-container\">\n    <div class=\"chat-window\" id=\"chatWindow\"><\/div>\n    <div class=\"chat-input-area\">\n        <label class=\"file-upload\">\ud83d\udcce\n        <input type=\"file\" id=\"fileInput\" multiple \/>\n        <\/label>\n        <button id=\"micBtn\">\ud83c\udfa4<\/button>\n        <span id=\"listening-indicator\">\ud83c\udfa7 Escuchando...<\/span>\n        <button id=\"toggleVoiceChat\">\ud83e\udde0\ud83c\udfa4<\/button>\n        <input type=\"text\" id=\"userInput\" placeholder=\"Escribe tu mensaje...\">\n        <button id=\"sendBtn\">Enviar<\/button>\n    <\/div>\n  <\/div>\n\n\n  <div class=\"sidebar-toggle\" onclick=\"document.getElementById('configSidebar').classList.toggle('open')\">\u2699\ufe0f<\/div>\n  <div class=\"sidebar\" id=\"configSidebar\">\n    <h3>Configuraci\u00f3n de Voz<\/h3>\n    <label for=\"voiceSelect\">Voz:<\/label>\n    <select id=\"voiceSelect\"><\/select>\n    <label for=\"rateRange\">Velocidad: <span id=\"rateValue\">1<\/span>x<\/label>\n    <input type=\"range\" id=\"rateRange\" min=\"0.5\" max=\"2\" step=\"0.1\" value=\"1\">\n    <label for=\"pitchRange\">Tono: <span id=\"pitchValue\">1<\/span><\/label>\n    <input type=\"range\" id=\"pitchRange\" min=\"0.5\" max=\"2\" step=\"0.1\" value=\"1\">\n  <\/div>\n  \n  \n  <script>\n    const chatWindow = document.getElementById(\"chatWindow\");\n    const userInput = document.getElementById(\"userInput\");\n    const sendBtn = document.getElementById(\"sendBtn\");\n    const fileInput = document.getElementById(\"fileInput\");\n    const micBtn = document.getElementById(\"micBtn\");\n    const toggleVoiceChatBtn = document.getElementById(\"toggleVoiceChat\");\n    const voiceSelect = document.getElementById(\"voiceSelect\");\n    const rateRange = document.getElementById(\"rateRange\");\n    const pitchRange = document.getElementById(\"pitchRange\");\n    const rateValue = document.getElementById(\"rateValue\");\n    const pitchValue = document.getElementById(\"pitchValue\");\n\n    let recognition;\n    let isListening = false;\n    let isVoiceChatActive = false;\n    let shouldRestartRecognition = false;\n    let recognitionCooldown = false;\n    let voices = [];\n\n    function addMessage(message, sender) {\n      const msgDiv = document.createElement(\"div\");\n      msgDiv.className = `chat-message ${sender}`;\n      msgDiv.textContent = message;\n      chatWindow.appendChild(msgDiv);\n      chatWindow.scrollTop = chatWindow.scrollHeight;\n    }\n\n    function addImage(src, filename) {\n      const wrapper = document.createElement(\"div\");\n      wrapper.className = \"chat-message user\";\n      const fileItem = document.createElement(\"div\");\n      fileItem.className = \"file-item\";\n      const img = document.createElement(\"img\");\n      img.src = src;\n      const caption = document.createElement(\"div\");\n      caption.textContent = filename;\n      fileItem.appendChild(img);\n      fileItem.appendChild(caption);\n      wrapper.appendChild(fileItem);\n      chatWindow.appendChild(wrapper);\n    }\n\n    function addFile(filename) {\n      const wrapper = document.createElement(\"div\");\n      wrapper.className = \"chat-message user\";\n      const fileItem = document.createElement(\"div\");\n      fileItem.className = \"file-item\";\n      const icon = document.createElement(\"div\");\n      icon.className = \"file-icon\";\n      icon.textContent = \"\ud83d\udcc4\";\n      const caption = document.createElement(\"div\");\n      caption.textContent = filename;\n      fileItem.appendChild(icon);\n      fileItem.appendChild(caption);\n      wrapper.appendChild(fileItem);\n      chatWindow.appendChild(wrapper);\n    }\n\n    sendBtn.addEventListener(\"click\", sendMessage);\n    userInput.addEventListener(\"keydown\", e => {\n      if (e.key === \"Enter\") {\n        e.preventDefault();\n        sendMessage();\n      }\n    });\n\n    function sendMessage() {\n      const text = userInput.value.trim();\n      const files = fileInput.files;\n      if (text !== \"\") {\n        addMessage(text, \"user\");\n        simulateBotReply(text);\n        userInput.value = \"\";\n      }\n      if (files.length > 0) {\n        for (const file of files) {\n          const fileURL = URL.createObjectURL(file);\n          if (file.type.startsWith(\"image\/\")) {\n            addImage(fileURL, file.name);\n          } else {\n            addFile(file.name);\n          }\n        }\n        fileInput.value = \"\";\n      }\n    }\n\n    chatWindow.addEventListener(\"dragover\", (e) => {\n      e.preventDefault();\n      chatWindow.classList.add(\"dragover\");\n    });\n    chatWindow.addEventListener(\"dragleave\", (e) => {\n      e.preventDefault();\n      chatWindow.classList.remove(\"dragover\");\n    });\n    chatWindow.addEventListener(\"drop\", (e) => {\n      e.preventDefault();\n      chatWindow.classList.remove(\"dragover\");\n      const files = e.dataTransfer.files;\n      for (const file of files) {\n        const fileURL = URL.createObjectURL(file);\n        if (file.type.startsWith(\"image\/\")) {\n          addImage(fileURL, file.name);\n        } else {\n          addFile(file.name);\n        }\n      }\n    });\n\n   function speak(text) {\n        if (recognition && isListening) {\n          recognition.abort();\n          isListening = false;\n        }\n        speechSynthesis.cancel();\n        const utter = new SpeechSynthesisUtterance(text);\n        const voiceIndex = voiceSelect.value;\n        if (voices[voiceIndex]) utter.voice = voices[voiceIndex];\n        utter.rate = parseFloat(rateRange.value);\n        utter.pitch = parseFloat(pitchRange.value);\n        utter.lang = \"es-ES\";\n        utter.onend = () => {\n          if (isVoiceChatActive && recognition && !isListening && !recognitionCooldown) {\n            shouldRestartRecognition = true;\n            setTimeout(() => {\n              if (!isListening) safeStartRecognition();\n            }, 500);\n          }\n        };\n        speechSynthesis.speak(utter);\n      }\n\n    function simulateBotReply(userText) {\n      const reply = userText.toLowerCase().includes(\"hola\") ? \"Hola, \u00bfen qu\u00e9 puedo ayudarte?\" : \"No entend\u00ed muy bien, pero dime m\u00e1s.\";\n      setTimeout(() => {\n        addMessage(reply, \"bot\");\n        speak(reply);\n      }, 600);\n    }\n\n    function safeStartRecognition() {\n    if (!isListening && recognition && !recognitionCooldown) {\n      try {\n        recognition.start();\n        micBtn.classList.add(\"active\");\n      } catch (error) {\n        console.warn(\"Error start recog:\", error.message);\n      }\n    }\n  }\n\n    function loadVoices() {\n      voices = speechSynthesis.getVoices().filter(v => v.lang.startsWith(\"es\"));\n      voiceSelect.innerHTML = voices.map((v, i) => `<option value=\"${i}\">${v.name} (${v.lang})<\/option>`).join(\"\");\n    }\n    \n   speechSynthesis.onvoiceschanged = loadVoices;\n   window.onload = () => {\n     loadVoices();\n     voiceSelect.value = localStorage.getItem('voiceIndex') || 0;\n     rateRange.value = localStorage.getItem('rate') || 1;\n     pitchRange.value = localStorage.getItem('pitch') || 1;\n     rateValue.textContent = rateRange.value;\n     pitchValue.textContent = pitchRange.value;\n     isVoiceChatActive = localStorage.getItem('conversationMode') === 'true';\n     if (isVoiceChatActive) toggleVoiceChatBtn.classList.add('active');\n     safeStartRecognition();\n      };\n      \n     voiceSelect.onchange = () => localStorage.setItem('voiceIndex', voiceSelect.value);\n     rateRange.oninput = () => {\n        rateValue.textContent = rateRange.value;\n        localStorage.setItem('rate', rateRange.value);\n      };\n     pitchRange.oninput = () => {\n        pitchValue.textContent = pitchRange.value;\n        localStorage.setItem('pitch', pitchRange.value);\n      };\n    \n     toggleVoiceChatBtn.addEventListener(\"click\", () => {\n        isVoiceChatActive = !isVoiceChatActive;\n        toggleVoiceChatBtn.classList.toggle(\"active\", isVoiceChatActive);\n        localStorage.setItem('conversationMode', isVoiceChatActive);\n        if (isVoiceChatActive) safeStartRecognition();\n        else if (recognition) recognition.abort();\n      });\n\n    if ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window) {\n    const SR = window.SpeechRecognition || window.webkitSpeechRecognition;\n    recognition = new SR();\n    recognition.lang = 'es-ES';\n    recognition.continuous = false;\n    recognition.interimResults = false;\n    recognition.maxAlternatives = 1;\n\n    recognition.onstart = () => {\n      isListening = true;\n      micBtn.classList.add(\"active\");\n    };\n\n    recognition.onend = () => {\n      isListening = false;\n      micBtn.classList.remove(\"active\");\n      if (isVoiceChatActive && shouldRestartRecognition && !recognitionCooldown) safeStartRecognition();\n    };\n\n    recognition.onerror = (event) => {\n      console.error(\"Error voz:\", event.error);\n      isListening = false;\n      micBtn.classList.remove(\"active\");\n      if (event.error === \"no-speech\") {\n        recognitionCooldown = true;\n        setTimeout(() => {\n          recognitionCooldown = false;\n          safeStartRecognition();\n        }, 3000);\n        return;\n      }\n      if (event.error !== \"aborted\" && isVoiceChatActive) {\n        shouldRestartRecognition = true;\n        setTimeout(() => safeStartRecognition(), 1000);\n      } else {\n        shouldRestartRecognition = false;\n      }\n    };\n\n    micBtn.onclick = () => {\n      if (isListening) {\n        recognition.abort();\n        isListening = false;\n        shouldRestartRecognition = false;\n        micBtn.classList.remove(\"active\");\n      } else {\n        safeStartRecognition();\n        shouldRestartRecognition = false;\n        micBtn.classList.add(\"active\");\n      }\n    };\n\n    recognition.onresult = function (event) {\n      const transcript = event.results[0][0].transcript;\n      addMessage(transcript, \"user\");\n      simulateBotReply(transcript);\n    };\n    }\n  <\/script>\n<\/body>\n<\/html>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Neo 2.0.6 Beta (Base Chat GPT) Chat IA con Configuraci\u00f3n \ud83d\udcce \ud83c\udfa4 \ud83c\udfa7 Escuchando&#8230; \ud83e\udde0\ud83c\udfa4 Enviar \u2699\ufe0f Configuraci\u00f3n de Voz [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"site-sidebar-layout":"no-sidebar","site-content-layout":"","ast-site-content-layout":"full-width-container","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"disabled","footer-sml-layout":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-407","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/neocloud05.com\/neo\/wp-json\/wp\/v2\/pages\/407","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/neocloud05.com\/neo\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/neocloud05.com\/neo\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/neocloud05.com\/neo\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/neocloud05.com\/neo\/wp-json\/wp\/v2\/comments?post=407"}],"version-history":[{"count":129,"href":"https:\/\/neocloud05.com\/neo\/wp-json\/wp\/v2\/pages\/407\/revisions"}],"predecessor-version":[{"id":574,"href":"https:\/\/neocloud05.com\/neo\/wp-json\/wp\/v2\/pages\/407\/revisions\/574"}],"wp:attachment":[{"href":"https:\/\/neocloud05.com\/neo\/wp-json\/wp\/v2\/media?parent=407"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}