2.0 - WebUI builder ("Cielight" merge) #9
@@ -80,8 +80,6 @@ function renderTags(container, tags, imgIndex) {
 | 
				
			|||||||
  // Suggestion dropdown
 | 
					  // Suggestion dropdown
 | 
				
			||||||
  const suggestionBox = document.createElement('ul');
 | 
					  const suggestionBox = document.createElement('ul');
 | 
				
			||||||
  suggestionBox.className = 'suggestions';
 | 
					  suggestionBox.className = 'suggestions';
 | 
				
			||||||
  suggestionBox.style.fontStyle = 'italic';
 | 
					 | 
				
			||||||
  suggestionBox.style.textAlign = 'left';
 | 
					 | 
				
			||||||
  container.appendChild(suggestionBox);
 | 
					  container.appendChild(suggestionBox);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let selectedIndex = -1;
 | 
					  let selectedIndex = -1;
 | 
				
			||||||
@@ -90,14 +88,22 @@ function renderTags(container, tags, imgIndex) {
 | 
				
			|||||||
    tag = tag.trim();
 | 
					    tag = tag.trim();
 | 
				
			||||||
    if (!tag) return;
 | 
					    if (!tag) return;
 | 
				
			||||||
    if (!tags.includes(tag)) tags.push(tag);
 | 
					    if (!tags.includes(tag)) tags.push(tag);
 | 
				
			||||||
    updateAllTags();
 | 
					    updateTags(imgIndex, tags); // save to galleryImages and server
 | 
				
			||||||
    updateTags(imgIndex, tags);
 | 
					 | 
				
			||||||
    renderTags(container, tags, imgIndex);
 | 
					    renderTags(container, tags, imgIndex);
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const updateSuggestions = () => {
 | 
					  const updateSuggestions = () => {
 | 
				
			||||||
    const value = input.value.toLowerCase();
 | 
					    const value = input.value.toLowerCase();
 | 
				
			||||||
    const suggestions = allTags.filter(t => !tags.includes(t) && t.toLowerCase().startsWith(value));
 | 
					
 | 
				
			||||||
 | 
					    const allTagsFlat = galleryImages.flatMap(img => img.tags || []);
 | 
				
			||||||
 | 
					    const tagCount = {};
 | 
				
			||||||
 | 
					    allTagsFlat.forEach(t => tagCount[t] = (tagCount[t] || 0) + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const allTagsSorted = Object.keys(tagCount)
 | 
				
			||||||
 | 
					      .sort((a, b) => tagCount[b] - tagCount[a]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Show suggestions that start with input (or all if empty)
 | 
				
			||||||
 | 
					    const suggestions = allTagsSorted.filter(t => t.toLowerCase().startsWith(value) && !tags.includes(t));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    suggestionBox.innerHTML = '';
 | 
					    suggestionBox.innerHTML = '';
 | 
				
			||||||
    selectedIndex = -1;
 | 
					    selectedIndex = -1;
 | 
				
			||||||
@@ -106,10 +112,21 @@ function renderTags(container, tags, imgIndex) {
 | 
				
			|||||||
      suggestionBox.style.display = 'block';
 | 
					      suggestionBox.style.display = 'block';
 | 
				
			||||||
      suggestions.forEach((s, idx) => {
 | 
					      suggestions.forEach((s, idx) => {
 | 
				
			||||||
        const li = document.createElement('li');
 | 
					        const li = document.createElement('li');
 | 
				
			||||||
 | 
					        li.style.fontStyle = 'italic';
 | 
				
			||||||
 | 
					        li.style.textAlign = 'left';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const boldPart = `<b>${s.substring(0, input.value.length)}</b>`;
 | 
					        const boldPart = `<b>${s.substring(0, input.value.length)}</b>`;
 | 
				
			||||||
        const rest = s.substring(input.value.length);
 | 
					        const rest = s.substring(input.value.length);
 | 
				
			||||||
        li.innerHTML = boldPart + rest;
 | 
					        li.innerHTML = boldPart + rest;
 | 
				
			||||||
        li.onclick = () => addTag(s);
 | 
					
 | 
				
			||||||
 | 
					        li.addEventListener('mousedown', (e) => {
 | 
				
			||||||
 | 
					          e.preventDefault();
 | 
				
			||||||
 | 
					          addTag(s);
 | 
				
			||||||
 | 
					          input.value = '';
 | 
				
			||||||
 | 
					          input.focus();
 | 
				
			||||||
 | 
					          updateSuggestions();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        li.onmouseover = () => selectedIndex = idx;
 | 
					        li.onmouseover = () => selectedIndex = idx;
 | 
				
			||||||
        suggestionBox.appendChild(li);
 | 
					        suggestionBox.appendChild(li);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
@@ -119,46 +136,50 @@ function renderTags(container, tags, imgIndex) {
 | 
				
			|||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  input.addEventListener('input', updateSuggestions);
 | 
					  input.addEventListener('input', updateSuggestions);
 | 
				
			||||||
  input.addEventListener('focus', updateSuggestions);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Keyboard navigation
 | 
					  input.addEventListener('focus', updateSuggestions); // Show suggestions on focus
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  input.addEventListener('keydown', (e) => {
 | 
					  input.addEventListener('keydown', (e) => {
 | 
				
			||||||
    const items = suggestionBox.querySelectorAll('li');
 | 
					    const items = suggestionBox.querySelectorAll('li');
 | 
				
			||||||
    if (items.length) {
 | 
					 | 
				
			||||||
    if (e.key === 'ArrowDown') {
 | 
					    if (e.key === 'ArrowDown') {
 | 
				
			||||||
      e.preventDefault();
 | 
					      e.preventDefault();
 | 
				
			||||||
 | 
					      if (!items.length) return;
 | 
				
			||||||
      selectedIndex = (selectedIndex + 1) % items.length;
 | 
					      selectedIndex = (selectedIndex + 1) % items.length;
 | 
				
			||||||
      items.forEach((li, i) => li.classList.toggle('selected', i === selectedIndex));
 | 
					      items.forEach((li, i) => li.classList.toggle('selected', i === selectedIndex));
 | 
				
			||||||
    } else if (e.key === 'ArrowUp') {
 | 
					    } else if (e.key === 'ArrowUp') {
 | 
				
			||||||
      e.preventDefault();
 | 
					      e.preventDefault();
 | 
				
			||||||
 | 
					      if (!items.length) return;
 | 
				
			||||||
      selectedIndex = (selectedIndex - 1 + items.length) % items.length;
 | 
					      selectedIndex = (selectedIndex - 1 + items.length) % items.length;
 | 
				
			||||||
      items.forEach((li, i) => li.classList.toggle('selected', i === selectedIndex));
 | 
					      items.forEach((li, i) => li.classList.toggle('selected', i === selectedIndex));
 | 
				
			||||||
    } else if (e.key === 'Enter') {
 | 
					    } else if (e.key === 'Enter') {
 | 
				
			||||||
      e.preventDefault();
 | 
					      e.preventDefault();
 | 
				
			||||||
        if (selectedIndex >= 0) addTag(items[selectedIndex].textContent.replace(/×$/,''));
 | 
					      if (selectedIndex >= 0 && items[selectedIndex]) {
 | 
				
			||||||
        else addTag(input.value);
 | 
					        addTag(items[selectedIndex].textContent);
 | 
				
			||||||
      } else if (e.key === 'Escape') {
 | 
					      } else {
 | 
				
			||||||
        suggestionBox.style.display = 'none';
 | 
					        addTag(input.value);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      input.value = '';
 | 
				
			||||||
 | 
					      updateSuggestions();
 | 
				
			||||||
    } else if ([' ', ','].includes(e.key)) {
 | 
					    } else if ([' ', ','].includes(e.key)) {
 | 
				
			||||||
      e.preventDefault();
 | 
					      e.preventDefault();
 | 
				
			||||||
      addTag(input.value);
 | 
					      addTag(input.value);
 | 
				
			||||||
      }
 | 
					      input.value = '';
 | 
				
			||||||
    } else if (['Enter', ' ', ','].includes(e.key)) {
 | 
					      updateSuggestions();
 | 
				
			||||||
      e.preventDefault();
 | 
					 | 
				
			||||||
      addTag(input.value);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  input.addEventListener('blur', () => {
 | 
					  input.addEventListener('blur', () => {
 | 
				
			||||||
    setTimeout(() => {
 | 
					    setTimeout(() => {
 | 
				
			||||||
      if (input.value.trim()) addTag(input.value);
 | 
					 | 
				
			||||||
      suggestionBox.style.display = 'none';
 | 
					      suggestionBox.style.display = 'none';
 | 
				
			||||||
    }, 100);
 | 
					      input.value = ''; // Clear input without saving
 | 
				
			||||||
 | 
					    }, 150);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  input.focus();
 | 
					  input.focus();
 | 
				
			||||||
 | 
					  updateSuggestions(); // show suggestions on render
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// --- Update tags in galleryImages array ---
 | 
					// --- Update tags in galleryImages array ---
 | 
				
			||||||
function updateTags(index, tags) {
 | 
					function updateTags(index, tags) {
 | 
				
			||||||
  galleryImages[index].tags = tags;
 | 
					  galleryImages[index].tags = tags;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user