Team Ai
Apppublic

luanfreitas5/Bubble_Prompter

sourceHugging Facecc-by-nc-4.0updated 2y agoView on Hugging Face
0likes
script.js943 linesDownload Raw Back to root
1const inputText = document.getElementById('input-text');2const bubbleContainer = document.getElementById('bubble-container');3const replaceSpacesButton = document.getElementById('replace-spaces-button');4const replaceUnderscoreButton = document.getElementById('replace-underscore-button');5const revertUnderscoreButton = document.getElementById('revert-underscore-button');6const replaceLineBreaksButton = document.getElementById('replace-linebreaks-button');7const replaceCommasButton = document.getElementById('replace-commas-button');8const removeDuplicatesButton = document.getElementById('remove-duplicates-button');9const revertSpacesButton = document.getElementById('revert-spaces-button');10const copyButton = document.getElementById('copy-button');11const undoButton = document.getElementById('undo-button');12const redoButton = document.getElementById('redo-button');13const deleteModeButton = document.getElementById('delete-mode-button');14const emphasisModeButton = document.getElementById('emphasis-mode-button');15const mitigateModeButton = document.getElementById('mitigate-mode-button');16const literalParenthesisButton = document.getElementById('literal-parenthesis-button');17const fictionalParenthesisButton = document.getElementById('fictional-parenthesis-button');18 19const bubbleColors = {};20 21let history = [];22let historyIndex = -1;23 24let deleteModeEnabled = false;25 26let emphasisModeEnabled = false;27 28let mitigateModeEnabled = false;29 30inputText.addEventListener('input', handleTextChange);31replaceSpacesButton.addEventListener('click', replaceSpaces);32replaceUnderscoreButton.addEventListener('click', replaceUnderscores);33revertUnderscoreButton.addEventListener('click', revertUnderscores);34replaceLineBreaksButton.addEventListener('click', replaceLineBreaks);35replaceCommasButton.addEventListener('click', replaceCommas);36removeDuplicatesButton.addEventListener('click', removeDuplicates);37revertSpacesButton.addEventListener('click', revertSpaces);38copyButton.addEventListener('click', copyToClipboard);39undoButton.addEventListener('click', undoChanges);40redoButton.addEventListener('click', redoChanges);41deleteModeButton.addEventListener('click', toggleDeleteMode);42emphasisModeButton.addEventListener('click', toggleEmphasisMode);43mitigateModeButton.addEventListener('click', toggleMitigateMode);44literalParenthesisButton.addEventListener('click', literalParenthesis);45fictionalParenthesisButton.addEventListener('click', fictionalParenthesis);46 47const sortable = new Sortable(bubbleContainer, {48  animation: 250,49  ghostClass: 'ghost',50  onEnd: handleBubbleChange,51  disabled: deleteModeEnabled || emphasisModeEnabled || mitigateModeEnabled52});53 54function handleTextChange() {55  const text = inputText.value;56 57  if (history.length === 0) {58    history.push('');59    historyIndex++;60  }61 62  if (text !== history[historyIndex]) {63    if (historyIndex < history.length - 1) {64 65      history.splice(historyIndex + 1);66    }67 68    history.push(text);69    historyIndex++;70 71    updateBubbles();72    updateButtonStates();73  }74}75 76function handleBubbleChange() {77  const conceptList = Array.from(bubbleContainer.getElementsByClassName('bubble')).map(bubble => bubble.textContent.trim());78  inputText.value = conceptList.join(', ');79  handleTextChange();80}81 82function updateBubbles() {83  bubbleContainer.innerHTML = '';84  const text = inputText.value.trim();85 86  if (text === '') return;87 88  const concepts = text.split(', ');89 90  concepts.forEach(concept => {91    const bubble = createBubble(concept);92    bubbleContainer.appendChild(bubble);93  });94}95 96function createBubble(text) {97  const bubble = document.createElement('div');98  bubble.classList.add('bubble');99 100  if (!bubbleColors.hasOwnProperty(text)) {101    bubbleColors[text] = getRandomColor();102  }103 104  bubble.style.backgroundColor = bubbleColors[text];105 106  const bubbleText = document.createElement('span');107  bubbleText.classList.add('bubble-text');108  bubbleText.innerText = text;109 110  bubble.appendChild(bubbleText);111 112  if (deleteModeEnabled) {113    bubble.classList.add('delete-mode');114    bubble.addEventListener('click', deleteBubble);115  }116 117  if (emphasisModeEnabled) {118    bubble.classList.add('emphasis-mode');119    bubble.addEventListener('click', emphasizeBubble);120  }121 122  if (mitigateModeEnabled) {123    bubble.classList.add('mitigate-mode');124    bubble.addEventListener('click', mitigateBubble);125  }126 127  return bubble;128}129 130function getRandomColor() {131  const h = Math.floor(Math.random() * 360);132  const s = Math.floor(Math.random() * 30) + 50;133  const l = Math.floor(Math.random() * 40) + 30;134 135  return `hsl(${h}, ${s}%, ${l}%)`;136}137 138function updateButtonStates() {139  undoButton.disabled = historyIndex === 0;140  redoButton.disabled = historyIndex === history.length - 1;141}142 143function replaceSpaces() {144  let text = inputText.value;145 146  text = text.replace(/,(\S)/g, ', $1');147  text = text.replace(/ {2,}/g, ' ');148  text = text.replace(/([^,])\s+/g, '$1, ');149 150  inputText.value = text;151  handleTextChange();152}153 154function revertSpaces() {155  const text = inputText.value;156  const replacedText = text.replace(/, /g, ' ');157  inputText.value = replacedText;158  handleTextChange();159}160 161function replaceUnderscores() {162  const text = inputText.value;163  const replacedText = text.replace(/_/g, ' ');164  inputText.value = replacedText;165  handleTextChange();166}167 168function revertUnderscores() {169  const text = inputText.value;170  const replacedText = text.replace(/([^,]) /g, '$1_');171  inputText.value = replacedText;172  handleTextChange();173}174 175function replaceLineBreaks() {176  const text = inputText.value;177  const replacedText = text.replace(/\n/g, ', ');178  inputText.value = replacedText;179  handleTextChange();180}181 182function replaceCommas() {183  const text = inputText.value;184  const step1 = text.replace(/,,/g, ',');185  const step2 = step1.replace(/, ,/g, ', ');186  const step3 = step2.trimRight();187  const step4 = step3.replace(/,$/, '');188  const step5 = step4.replace(/^,/, '');189  const step6 = step5.replace(/,  /g, ', ');190  const replacedText = step6.replace(/^ /, '');191 192  inputText.value = replacedText;193  handleTextChange();194}195 196function removeDuplicates() {197  const text = inputText.value;198  const concepts = text.split(', ');199 200  const uniqueConcepts = [...new Set(concepts)];201 202  inputText.value = uniqueConcepts.join(', ');203  handleTextChange();204}205 206 207function copyToClipboard() {208  inputText.select();209  inputText.setSelectionRange(0, 99999);210  document.execCommand('copy');211}212 213function literalParenthesis() {214  const inputText = document.getElementById('input-text');215  const text = inputText.value;216 217  // Reemplazar ' (' que no estén precedidos por una ',' y el paréntesis de cierre siguiente más cercano218  let replacedText = text.replace(/([^,]) \((.*?)\)/g, '$1 \\($2\\)');219 220  // Reemplazar '_(' y el paréntesis de cierre siguiente más cercano221  replacedText = replacedText.replace(/_\((.*?)\)/g, '_\\($1\\)');222 223  inputText.value = replacedText;224  handleTextChange();225}226 227 228function fictionalParenthesis() {229  const inputText = document.getElementById('input-text');230  const text = inputText.value;231 232  // Reemplazar '\(' por '('233  let replacedText = text.replace(/\\\(/g, '(');234 235  // Reemplazar '\)' por ')'236  replacedText = replacedText.replace(/\\\)/g, ')');237 238  inputText.value = replacedText;239  handleTextChange();240}241 242document.getElementById('download-text-file-button').addEventListener('click', showFilenameInput);243 244function showFilenameInput() {245  const inputText = document.getElementById('input-text').value;246  const currentDate = new Date();247  248  const year = currentDate.getFullYear();249  const month = String(currentDate.getMonth() + 1).padStart(2, '0');250  const day = String(currentDate.getDate()).padStart(2, '0');251  const hours = String(currentDate.getHours()).padStart(2, '0');252  const minutes = String(currentDate.getMinutes()).padStart(2, '0');253  254  const formattedDate = `${year}-${month}-${day}_${hours}-${minutes}`;255  const defaultFileName = `Bubble Prompter_${formattedDate}.txt`;256 257  let container = document.getElementById('custom-filename-container');258  259  if (!container) {260    container = document.createElement('div');261    container.id = 'custom-filename-container';262    document.body.appendChild(container);263 264    const input = document.createElement('input');265    input.type = 'text';266    input.id = 'custom-filename';267    container.appendChild(input);268 269    const confirmButton = document.createElement('button');270    confirmButton.innerText = '✅';271    confirmButton.classList.add('confirm-button'); // Agregar clase272    confirmButton.addEventListener('click', downloadTextFile);273    container.appendChild(confirmButton);274 275    const cancelButton = document.createElement('button');276    cancelButton.innerText = '❌';277    cancelButton.classList.add('cancel-button'); // Agregar clase278    cancelButton.addEventListener('click', hideFilenameInput);279    container.appendChild(cancelButton);280 281    document.addEventListener('click', function(event) {282      if (!container.contains(event.target) && event.target !== document.getElementById('download-text-file-button')) {283        hideFilenameInput();284      }285    });286 287    input.addEventListener('keydown', function(event) {288      if (event.key === 'Enter') {289        downloadTextFile();290      }291    });292  }293 294  container.style.display = 'block';295  container.style.top = `${document.getElementById('download-text-file-button').offsetTop + document.getElementById('download-text-file-button').offsetHeight}px`;296  container.style.left = `${document.getElementById('download-text-file-button').offsetLeft}px`;297 298  const input = document.getElementById('custom-filename');299  input.value = defaultFileName;300  input.select();301  input.focus();302}303 304function hideFilenameInput() {305  const container = document.getElementById('custom-filename-container');306  if (container) {307    container.style.display = 'none';308  }309}310 311function downloadTextFile() {312  const inputText = document.getElementById('input-text').value;313  let customFilename = document.getElementById('custom-filename').value.trim();314 315  // Añadir .txt si no está presente316  if (!customFilename.endsWith('.txt')) {317    customFilename += '.txt';318  }319  320  const fileName = customFilename ? customFilename : 'Bubble Prompter.txt';321  322  const blob = new Blob([inputText], { type: 'text/plain' });323 324  const link = document.createElement('a');325  link.href = URL.createObjectURL(blob);326  link.download = fileName;327 328  document.body.appendChild(link);329  link.click();330  document.body.removeChild(link);331 332  hideFilenameInput();333}334 335document.getElementById('load-text-file-button').addEventListener('click', loadTextFile);336 337function loadTextFile() {338  const fileInput = document.createElement('input');339  fileInput.type = 'file';340  fileInput.accept = '.txt';341 342  fileInput.addEventListener('change', (event) => {343    const file = event.target.files[0];344    if (file) {345      const reader = new FileReader();346      reader.onload = (e) => {347        const inputText = document.getElementById('input-text');348        let currentText = inputText.value;349 350        // Añadir una coma y un espacio si el campo de texto no está vacío351        if (currentText) {352          currentText += ', ';353        }354 355        // Añadir el contenido del archivo356        inputText.value = currentText + e.target.result;357        handleTextChange();358      };359      reader.readAsText(file);360    }361  });362 363  // Simular un clic en el input de archivo para abrir el cuadro de diálogo de archivo364  fileInput.click();365}366 367document.addEventListener('DOMContentLoaded', function() {368  const inputText = document.getElementById('input-text');369 370  // Evitar comportamiento por defecto de arrastrar y soltar371  ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {372    inputText.addEventListener(eventName, preventDefaults, false);373    document.body.addEventListener(eventName, preventDefaults, false);374  });375 376  // Añadir clases para los estilos de arrastrar y soltar377  ['dragenter', 'dragover'].forEach(eventName => {378    inputText.addEventListener(eventName, () => inputText.classList.add('highlight'), false);379  });380 381  ['dragleave', 'drop'].forEach(eventName => {382    inputText.addEventListener(eventName, () => inputText.classList.remove('highlight'), false);383  });384 385  // Manejar el evento de soltar386  inputText.addEventListener('drop', handleDrop, false);387 388  function preventDefaults(e) {389    e.preventDefault();390    e.stopPropagation();391  }392 393  function handleDrop(e) {394    const dt = e.dataTransfer;395    const files = dt.files;396 397    if (files.length) {398      const file = files[0];399      if (file.type === "text/plain") {400        const reader = new FileReader();401        reader.onload = (e) => {402          let currentText = inputText.value;403 404          // Añadir una coma y un espacio si el campo de texto no está vacío405          if (currentText) {406            currentText += ', ';407          }408 409          // Añadir el contenido del archivo410          inputText.value = currentText + e.target.result;411          handleTextChange();412        };413        reader.readAsText(file);414      } else {415        alert("Please drop a valid .txt file.");416      }417    }418  }419});420 421 422function undoChanges() {423  if (historyIndex > 0) {424    historyIndex--;425    inputText.value = history[historyIndex];426    updateBubbles();427    updateButtonStates();428  }429}430 431function redoChanges() {432  if (historyIndex < history.length - 1) {433    historyIndex++;434    inputText.value = history[historyIndex];435    updateBubbles();436    updateButtonStates();437  }438}439 440function toggleDeleteMode() {441  deleteModeEnabled = !deleteModeEnabled;442  deleteModeButton.classList.toggle('active');443 444  if (deleteModeEnabled) {445    emphasisModeEnabled = false;446    emphasisModeButton.classList.remove('active');447    mitigateModeEnabled = false;448    mitigateModeButton.classList.remove('active');449  }450 451  sortable.option('disabled', deleteModeEnabled);452  bubbleContainer.classList.toggle('delete-mode-container');453 454  const bubbles = bubbleContainer.getElementsByClassName('bubble');455 456  for (let i = 0; i < bubbles.length; i++) {457    const bubble = bubbles[i];458      bubble.classList.remove('emphasis-mode');459      bubble.classList.remove('mitigate-mode');460    if (deleteModeEnabled) {461      bubble.classList.add('delete-mode');462      bubble.removeEventListener('click', emphasizeBubble);463      bubble.removeEventListener('click', mitigateBubble);464      bubble.addEventListener('click', deleteBubble);465    } else {466      bubble.classList.remove('delete-mode');467      bubble.removeEventListener('click', deleteBubble);468    }469  }470}471 472function toggleEmphasisMode() {473  emphasisModeEnabled = !emphasisModeEnabled;474  emphasisModeButton.classList.toggle('active');475 476  if (emphasisModeEnabled) {477    deleteModeEnabled = false;478    deleteModeButton.classList.remove('active');479    mitigateModeEnabled = false;480    mitigateModeButton.classList.remove('active');481  }482 483  sortable.option('disabled', emphasisModeEnabled);484  bubbleContainer.classList.toggle('emphasis-mode-container');485 486  const bubbles = bubbleContainer.getElementsByClassName('bubble');487 488  for (let i = 0; i < bubbles.length; i++) {489    const bubble = bubbles[i];490     bubble.classList.remove('delete-mode');491     bubble.classList.remove('mitigate-mode');492    if (emphasisModeEnabled) {493      bubble.classList.add('emphasis-mode');494      bubble.removeEventListener('click', deleteBubble);495      bubble.removeEventListener('click', mitigateBubble);496      bubble.addEventListener('click', emphasizeBubble);497    } else {498      bubble.classList.remove('emphasis-mode');499      bubble.removeEventListener('click', emphasizeBubble);500    }501  }502}503 504function toggleMitigateMode() {505  mitigateModeEnabled = !mitigateModeEnabled;506  mitigateModeButton.classList.toggle('active');507 508  if (mitigateModeEnabled) {509    deleteModeEnabled = false;510    deleteModeButton.classList.remove('active');511    emphasisModeEnabled = false;512    emphasisModeButton.classList.remove('active');513  }514 515  sortable.option('disabled', mitigateModeEnabled);516  bubbleContainer.classList.toggle('mitigate-mode-container');517 518  const bubbles = bubbleContainer.getElementsByClassName('bubble');519 520  for (let i = 0; i < bubbles.length; i++) {521    const bubble = bubbles[i];522      bubble.classList.remove('delete-mode');523      bubble.classList.remove('emphasis-mode');524    if (mitigateModeEnabled) {525      bubble.classList.add('mitigate-mode');526      bubble.removeEventListener('click', deleteBubble);527      bubble.removeEventListener('click', emphasizeBubble);528      bubble.addEventListener('click', mitigateBubble);529    } else {530      bubble.classList.remove('mitigate-mode');531      bubble.removeEventListener('click', mitigateBubble);532    }533  }534}535 536function deleteBubble(event) {537  const bubble = event.target.closest('.bubble');538  const bubbleText = bubble.querySelector('.bubble-text');539  const nextComma = bubbleText.nextSibling;540 541  bubble.remove();542 543  if (nextComma && nextComma.nodeType === Node.TEXT_NODE && nextComma.textContent.trim() === ',') {544    nextComma.remove();545  }546 547  handleBubbleChange();548}549 550function emphasizeBubble(event) {551  const bubble = event.target.closest('.bubble');552  const bubbleText = bubble.querySelector('.bubble-text');553  const text = bubbleText.innerText.trim();554 555  if (text.startsWith('[') && text.endsWith(']')) {556    bubbleText.innerText = text.slice(1, -1);557  } else {558    bubbleText.innerText = `(${text})`;559  }560 561  handleBubbleChange();562}563 564function mitigateBubble(event) {565 const bubble = event.target.closest('.bubble');566  const bubbleText = bubble.querySelector('.bubble-text');567  const text = bubbleText.innerText.trim();568 569  if (text.startsWith('(') && text.endsWith(')')) {570    bubbleText.innerText = text.slice(1, -1);571  } else {572    bubbleText.innerText = `[${text}]`;573  }574 575  handleBubbleChange();576}577 578document.addEventListener('DOMContentLoaded', function() {579  let words = [];580  let categoriesData = {};581 582  // Datos de respaldo583  const backupData = {584    'Composition and Style': {585      'Style': [586        {587          type: 'subcategory',588          name: 'Classic',589          items: [590            { type: 'word', name: 'Elegant' },591            { type: 'word', name: 'Traditional' }592          ]593        },594        {595          type: 'subcategory',596          name: 'Modern',597          items: [598            { type: 'word', name: 'Sleek' },599            { type: 'word', name: 'Minimalist' }600          ]601        }602      ],603      'Composition': [604        { type: 'word', name: 'Balanced' },605        { type: 'word', name: 'Dynamic' }606      ]607    },608    'Body': {609      'Head': [610        {611          type: 'subcategory',612          name: 'Facial Features',613          items: [614            { type: 'word', name: 'Eyes' },615            { type: 'word', name: 'Nose' }616          ]617        },618        {619          type: 'subcategory',620          name: 'Hair',621          items: [622            { type: 'word', name: 'Curly' },623            { type: 'word', name: 'Straight' }624          ]625        }626      ],627      'Torso': [628        { type: 'word', name: 'Chest' },629        { type: 'word', name: 'Back' }630      ]631    }632  };633 634  // Archivos CSV a cargar635  const csvFiles = [636    'Composition.csv',637    'Body.csv',638    'Clothes.csv',639    'Activity.csv',640    'Objects.csv',641    'Creatures.csv',642    'Plants.csv',643    'World.csv',644    'NSFW.csv',645    'More.csv'646  ];647 648  const categoryEmojis = {649    'Composition': '🖼️',650    'Body': '🧏🏻‍♀️',651    'Clothes': '👒',652    'Activity': '🤺',653    'Objects': '🎸',654    'Creatures': '🐈',655    'Plants': '☘️',656    'World': '🌍',657    'NSFW': '🍑',658    'More': '📚'659  };660 661  const categoryDropdownButton = document.getElementById('category-dropdown-button');662  const categoryDropdown = document.getElementById('category-dropdown');663  const randomWordButton = document.getElementById('random-word-button');664 665  categoryDropdownButton.addEventListener('click', toggleCategoryDropdown);666  randomWordButton.addEventListener('click', addRandomWordToInput);667  document.addEventListener('click', closeDropdownOutsideClick);668 669  function toggleCategoryDropdown() {670    categoryDropdown.classList.toggle('hidden');671  }672 673  function closeDropdownOutsideClick(event) {674    if (!categoryDropdown.contains(event.target) && event.target !== categoryDropdownButton) {675      categoryDropdown.classList.add('hidden');676    }677  }678 679  async function loadCSVFiles() {680    for (const file of csvFiles) {681      try {682        const response = await fetch(file);683        if (!response.ok) throw new Error(`Error al cargar ${file}`);684        const text = await response.text();685        processCSVData(file.split('.csv')[0], text);686      } catch (error) {687        console.error(error);688        // Usa datos de respaldo en caso de error689        const categoryName = file.split('.csv')[0];690        if (backupData[categoryName]) {691          categoriesData[categoryName] = backupData[categoryName];692        }693      }694    }695 696    renderCategories();697    populateWordsForAutocomplete();698  }699 700  function processCSVData(categoryName, csvText) {701    const lines = csvText.split('\n').map(line => line.trim()).filter(line => line.length > 0);702    const headers = lines[0].split(',');703 704    if (!categoriesData[categoryName]) {705      categoriesData[categoryName] = {};706    }707 708    headers.forEach((header, index) => {709      if (!categoriesData[categoryName][header]) {710        categoriesData[categoryName][header] = [];711      }712 713      let currentSubcategory = null;714      let currentSubSubcategory = null;715 716      for (let i = 1; i < lines.length; i++) {717        const wordsInLine = lines[i].split(',');718        const word = wordsInLine[index].trim();719 720        if (word) {721          // Si la palabra tiene una celda vacía directamente sobre ella en la misma columna722          if (i > 1 && lines[i - 1].split(',')[index].trim() === '') {723            if (currentSubcategory) {724              currentSubSubcategory = { type: 'subsubcategory', name: word, items: [] };725              currentSubcategory.items.push(currentSubSubcategory);726            } else {727              currentSubcategory = { type: 'subcategory', name: word, items: [] };728              categoriesData[categoryName][header].push(currentSubcategory);729            }730          } else if (currentSubSubcategory) {731            currentSubSubcategory.items.push({ type: 'word', name: word });732          } else if (currentSubcategory) {733            currentSubcategory.items.push({ type: 'word', name: word });734          } else {735            categoriesData[categoryName][header].push({ type: 'word', name: word });736          }737        } else {738          if (currentSubSubcategory) {739            currentSubSubcategory = null;740          } else {741            currentSubcategory = null;742          }743        }744      }745    });746  }747 748  function renderCategories() {749    categoryDropdown.innerHTML = '';750  751    for (const [categoryName, subcategories] of Object.entries(categoriesData)) {752      const categoryItem = document.createElement('div');753      categoryItem.classList.add('category-item');754  755      const emojiSpan = document.createElement('span');756      emojiSpan.classList.add('category-emoji');757      emojiSpan.innerText = categoryEmojis[categoryName] || '';758  759      const nameSpan = document.createElement('span');760      nameSpan.innerText = categoryName;761  762      categoryItem.appendChild(emojiSpan);763      categoryItem.appendChild(nameSpan);764  765      categoryItem.addEventListener('click', () => toggleSubcategory(categoryItem, subcategories));766      categoryDropdown.appendChild(categoryItem);767    }768  }769 770  function toggleSubcategory(categoryItem, subcategories) {771    if (categoryItem.nextElementSibling && categoryItem.nextElementSibling.classList.contains('subcategory-container')) {772      categoryItem.nextElementSibling.remove();773    } else {774      const subcategoryContainer = document.createElement('div');775      subcategoryContainer.classList.add('subcategory-container');776 777      for (const [subcategoryName, items] of Object.entries(subcategories)) {778        const subcategoryItem = document.createElement('div');779        subcategoryItem.classList.add('subcategory-item');780        subcategoryItem.innerText = subcategoryName;781 782        // Crear botón para añadir palabra aleatoria783        const randomButton = document.createElement('button');784        randomButton.innerText = '🎲';785        randomButton.classList.add('random-word-button');786        randomButton.addEventListener('click', (e) => {787          e.stopPropagation();788          addRandomWordFromSubcategory(items);789          handleTextChange();790        });791 792        subcategoryItem.appendChild(randomButton);793        subcategoryItem.addEventListener('click', (e) => {794          e.stopPropagation();795          toggleItems(subcategoryItem, items);796        });797        subcategoryContainer.appendChild(subcategoryItem);798      }799 800      categoryItem.insertAdjacentElement('afterend', subcategoryContainer);801    }802  }803 804  function toggleItems(subcategoryItem, items) {805    if (subcategoryItem.nextElementSibling && subcategoryItem.nextElementSibling.classList.contains('item-container')) {806      subcategoryItem.nextElementSibling.remove();807    } else {808      const itemContainer = document.createElement('div');809      itemContainer.classList.add('item-container');810 811      items.forEach(item => {812        const itemElement = document.createElement('div');813        itemElement.classList.add(item.type === 'subcategory' || item.type === 'subsubcategory' ? 'subcategory-item' : 'word-item');814        itemElement.innerText = item.name;815 816        // Si es subcategoría o sub-subcategoría, añadir botón de palabra aleatoria817        if (item.type === 'subcategory' || item.type === 'subsubcategory') {818          const randomButton = document.createElement('button');819          randomButton.innerText = '🎲';820          randomButton.classList.add('random-word-button');821          randomButton.addEventListener('click', (e) => {822            e.stopPropagation();823            addRandomWordFromSubcategory(item.items);824            handleTextChange();825          });826 827          itemElement.appendChild(randomButton);828        }829 830        if (item.type === 'subcategory' || item.type === 'subsubcategory') {831          itemElement.addEventListener('click', (e) => {832            e.stopPropagation();833            toggleItems(itemElement, item.items);834          });835        } else {836          itemElement.addEventListener('click', () => addItemToInput(item.name));837        }838 839        itemContainer.appendChild(itemElement);840      });841 842      subcategoryItem.insertAdjacentElement('afterend', itemContainer);843    }844  }845 846  function addItemToInput(word) {847    const inputText = document.getElementById('input-text');848    inputText.value += (inputText.value ? ', ' : '') + word;849    updateBubbles();850  }851 852  function populateWordsForAutocomplete() {853    words = [];854    for (const category of Object.values(categoriesData)) {855      for (const subcategory of Object.values(category)) {856        for (const item of subcategory) {857          if (item.type === 'word') {858            words.push(item.name);859          } else if (item.type === 'subcategory' || item.type === 'subsubcategory') {860            item.items.forEach(subItem => {861              if (subItem.type === 'word') {862                words.push(subItem.name);863              }864            });865          }866        }867      }868    }869    console.log('Palabras cargadas para autocompletador:', words);870  }871 872  function addRandomWordToInput() {873    if (words.length === 0) return;874    const randomWord = words[Math.floor(Math.random() * words.length)];875    addItemToInput(randomWord);876  }877 878  function addRandomWordFromSubcategory(subcategoryItems) {879    const subcategoryWords = subcategoryItems.flatMap(item => item.type === 'word' ? [item.name] : item.items.map(subItem => subItem.name));880    if (subcategoryWords.length === 0) return;881    const randomWord = subcategoryWords[Math.floor(Math.random() * subcategoryWords.length)];882    addItemToInput(randomWord);883  }884 885  const input = document.getElementById('autocomplete-input');886  const autocompleteList = document.getElementById('autocomplete-list');887 888  // Función que maneja tanto el evento de 'input' como de 'focus'889  function handleAutocomplete() {890    const value = input.value.toLowerCase();891    console.log('Valor de entrada:', value); // Verifica el valor de entrada892 893    if (!value) {894      autocompleteList.innerHTML = '';895      return;896    }897 898    // Filtra y ordena las palabras que empiezan con el valor ingresado, sin distinguir mayúsculas899    const startsWith = words900      .filter(word => word.toLowerCase().startsWith(value))901      .sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));902      903    // Filtra y ordena las palabras que contienen el valor ingresado pero no empiezan con él, sin distinguir mayúsculas904    const contains = words905      .filter(word => word.toLowerCase().includes(value) && !word.toLowerCase().startsWith(value))906      .sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));907 908    // Crear un conjunto para filtrar palabras únicas909    const uniqueSuggestions = new Set([...startsWith, ...contains]);910 911    // Convertir el conjunto a un array y tomar las primeras 20 sugerencias912    const suggestions = Array.from(uniqueSuggestions).slice(0, 20);913 914    console.log('Sugerencias encontradas:', suggestions); // Verifica las sugerencias encontradas915 916    autocompleteList.innerHTML = suggestions.map(suggestion => `<div>${suggestion}</div>`).join('');917 918    autocompleteList.querySelectorAll('div').forEach(item => {919      item.addEventListener('click', function() {920        const selectedWord = this.textContent;921        const inputText = document.getElementById('input-text');922        inputText.value += (inputText.value ? ', ' : '') + selectedWord;923        updateBubbles();924        input.value = ''; // Limpia el campo de autocompletado925        autocompleteList.innerHTML = ''; // Limpia las sugerencias926        input.focus(); // Enfoca el campo de autocompletado927      });928    });929  }930 931  // Escucha tanto al evento de 'input' como al de 'focus'932  input.addEventListener('input', handleAutocomplete);933  input.addEventListener('focus', handleAutocomplete);934 935  document.addEventListener('click', function(event) {936    if (event.target !== input) {937      autocompleteList.innerHTML = '';938      handleTextChange();939    }940  });941 942  loadCSVFiles();943});