mirabarukaso
Update Tag Complete JS
901270b
//gosh... this is a mess... I'm sorry... I'll clean it up later... I promise...
function my_tag_autocomplete() {
function dark_theme() {
const url = new URL(window.location);
if (url.searchParams.get('__theme') !== 'dark') {
url.searchParams.set('__theme', 'dark');
window.location.href = url.href;
}
}
function setupSuggestionSystem() {
// Select all textarea elements with specific IDs
const textboxes = document.querySelectorAll(
'#custom_prompt_text textarea, ' +
'#positive_prompt_text textarea, ' +
'#negative_prompt_text textarea, ' +
'#ai_prompt_text textarea, ' +
'#prompt_ban_text textarea'
);
// Log the number of textboxes found
//console.log("Found specific textboxes:", textboxes.length);
textboxes.forEach(textbox => {
// Skip if the suggestion system is already set up for this textbox
if (textbox.dataset.suggestionSetup) return;
// Log that the suggestion system is being set up for this textbox
console.log("Setting up suggestion system for", textbox);
let suggestionBox = document.createElement('div');
suggestionBox.className = 'suggestion-box';
// Hide the suggestion box initially
suggestionBox.style.display = 'none';
// Append the suggestion box to the body element
document.body.appendChild(suggestionBox);
let selectedIndex = -1; // Index of the currently selected suggestion item
let currentSuggestions = []; // Array to store the current suggestion items
// Handle input events on the textbox
textbox.addEventListener('input', async function () {
const value = textbox.value; // Current value of the textbox
const cursorPosition = textbox.selectionStart; // Current cursor position in the textbox
// Extract the word to send for suggestions
let wordToSend = '';
if (cursorPosition === value.length) {
// If cursor is at the end, extract the word after the last comma
const lastCommaIndex = value.lastIndexOf(',');
wordToSend = value.slice(lastCommaIndex + 1).trim();
} else {
// If cursor is not at the end, extract the word between the nearest commas
const beforeCursor = value.slice(0, cursorPosition);
const afterCursor = value.slice(cursorPosition);
const lastCommaBeforeCursor = beforeCursor.lastIndexOf(',');
const firstCommaAfterCursor = afterCursor.indexOf(',');
const start = lastCommaBeforeCursor >= 0 ? lastCommaBeforeCursor + 1 : 0; // Start position for word extraction
const end = firstCommaAfterCursor >= 0 ? cursorPosition + firstCommaAfterCursor : value.length; // End position for word extraction
wordToSend = value.slice(start, end).trim();
}
// If no word is extracted, hide the suggestion box and skip the API request
if (!wordToSend) {
//console.log("Skipping API request due to empty word.");
suggestionBox.style.display = 'none';
return;
}
// Log the word being sent for the initial API request
//console.log("Sending initial API request with word:", wordToSend);
let eventId; // Variable to store the event ID from the API response
try {
// Make the first API request to get an event ID
const initialResponse = await fetch('/gradio_api/call/update_suggestions_js', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
fn_index: 0,
data: [wordToSend] // Send the extracted word instead of the full textbox content
})
});
// Log the status of the initial API response
//console.log("Initial API response status:", initialResponse.status);
// Check if the initial API request failed
if (!initialResponse.ok) {
console.error("Initial API request failed:", initialResponse.status, initialResponse.statusText);
return;
}
const initialResult = await initialResponse.json();
// Log the data received from the initial API response
//console.log("Initial API response data:", initialResult);
// Extract the event ID from the response
eventId = initialResult.event_id;
if (!eventId) {
console.error("No event_id found in initial API response:", initialResult);
return;
}
// Log the extracted event ID
//console.log("Extracted event_id:", eventId);
} catch (error) {
// Log any errors that occur during the initial API request
console.error("Error during initial API request:", error);
return;
}
let suggestions; // Variable to store the suggestion data
try {
// Make the second API request to get suggestion data using the event ID
const suggestionResponse = await fetch(`/gradio_api/call/update_suggestions_js/${eventId}`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' }
});
// Log the status of the suggestion API response
//console.log("Suggestion API response status:", suggestionResponse.status);
// Check if the suggestion API request failed
if (!suggestionResponse.ok) {
console.error("Suggestion API request failed:", suggestionResponse.status, suggestionResponse.statusText);
return;
}
// Log the full suggestion API response object
//console.log("Suggestion API response object:", suggestionResponse);
// Get the raw suggestion data as text
const rawSuggestions = await suggestionResponse.text();
// Log the raw suggestion data received
//console.log("Raw suggestions received:", rawSuggestions);
// Parse the Python-formatted list into a JavaScript array
const lines = rawSuggestions.split('\n'); // Split the response into lines
let dataLine = lines.find(line => line.startsWith('data:')); // Find the line starting with "data:"
if (!dataLine) {
console.error("No data line found in raw suggestions:", rawSuggestions);
return;
}
// Remove the "data:" prefix and parse the JSON string into an array
const jsonString = dataLine.replace('data:', '').trim();
suggestions = JSON.parse(jsonString);
// Log the parsed suggestion data
//console.log("Parsed suggestions:", suggestions);
} catch (error) {
// Log any errors that occur during the suggestion API request
console.error("Error during suggestion API request:", error);
return;
}
// Clear the suggestion box content
suggestionBox.innerHTML = '';
currentSuggestions = []; // Reset the current suggestions array
// Check if there are no valid suggestions to display
if (!suggestions || suggestions.length === 0 || suggestions.every(suggestion => suggestion.length === 0)) {
//console.log("No suggestions available.");
suggestionBox.style.display = 'none';
return;
}
// Calculate the width of the longest suggestion item
let maxWidth = 0;
const tempDiv = document.createElement('div'); // Temporary div to measure text width
tempDiv.style.position = 'absolute';
tempDiv.style.visibility = 'hidden';
tempDiv.style.whiteSpace = 'nowrap';
document.body.appendChild(tempDiv);
// Bind click events to suggestion items during input event
suggestions.forEach((suggestion, index) => {
if (!Array.isArray(suggestion) || suggestion.length === 0) {
console.warn(`Invalid suggestion format at index ${index}:`, suggestion);
return;
}
suggestion.forEach(element => {
const item = document.createElement('div');
item.className = 'suggestion-item';
item.textContent = element;
item.dataset.value = element;
tempDiv.textContent = element;
maxWidth = Math.max(maxWidth, tempDiv.offsetWidth);
currentSuggestions.push({ prompt: element });
item.addEventListener('click', () => applySuggestion(element));
suggestionBox.appendChild(item);
});
});
// Remove the temporary div after measuring
document.body.removeChild(tempDiv);
// Update the suggestion box position if it is already visible
if (suggestionBox.style.display !== 'none') {
updateSuggestionBoxPosition();
}
// Set the width of the suggestion box
setSuggestionBoxWidth(maxWidth);
// Log the set width of the suggestion box
//console.log("Set suggestionBox width:", suggestionBox.style.width);
// Log the actual rendered width of the suggestion box
//console.log("Actual suggestionBox width:", suggestionBox.offsetWidth);
selectedIndex = -1; // Reset the selected index
// Log that the suggestions have been successfully displayed
//console.log("Suggestions successfully displayed.");
});
// Handle keyboard navigation for the suggestion box
textbox.addEventListener('keydown', function (e) {
if (suggestionBox.style.display === 'none')
return; // Exit if the suggestion box is not visible
const items = suggestionBox.querySelectorAll('.suggestion-item');
if (items.length === 0) return; // Exit if there are no suggestion items
if (e.key === 'Tab' || e.key === 'Enter') {
e.preventDefault(); // Prevent default behavior
if (selectedIndex >= 0 && selectedIndex < currentSuggestions.length) {
applySuggestion(currentSuggestions[selectedIndex].prompt); // Apply the selected suggestion
} else if (items.length > 0) {
applySuggestion(currentSuggestions[0].prompt); // Apply the first suggestion if none selected
}
} else if (e.key === 'ArrowDown') {
e.preventDefault(); // Prevent default scrolling
selectedIndex = Math.min(selectedIndex + 1, items.length - 1); // Move selection down
items.forEach((item, idx) => item.classList.toggle('selected', idx === selectedIndex));
if (selectedIndex >= 0) items[selectedIndex].scrollIntoView({ block: 'nearest' });
textbox.focus(); // Keep focus on the textbox
} else if (e.key === 'ArrowUp') {
e.preventDefault(); // Prevent default scrolling
selectedIndex = Math.max(selectedIndex - 1, 0); // Move selection up
items.forEach((item, idx) => item.classList.toggle('selected', idx === selectedIndex));
if (selectedIndex >= 0) items[selectedIndex].scrollIntoView({ block: 'nearest' });
textbox.focus(); // Keep focus on the textbox
} else if (e.key === 'Escape') {
suggestionBox.style.display = 'none'; // Hide the suggestion box
}
});
// Hide the suggestion box when clicking outside
document.addEventListener('click', function(e) {
if (!suggestionBox.contains(e.target) && e.target !== textbox) {
suggestionBox.style.display = 'none'; // Hide if click is outside textbox and suggestion box
}
});
function setSuggestionBoxWidth(maxWidth) {
suggestionBox.style.display = 'block'; // Show the suggestion box
suggestionBox.style.width = `${Math.min(maxWidth + 20, 600)}px`; // Set width based on max suggestion width
suggestionBox.style.minWidth = '0px'; // Remove minimum width restriction
suggestionBox.style.maxWidth = 'none'; // Remove maximum width restriction
suggestionBox.offsetWidth; // Force a reflow to apply styles
}
function formatSuggestion(suggestion) {
// Remove popularity info (number in parentheses) from the suggestion
const withoutHeat = suggestion.replace(/\s\(\d+\)$/, '');
// Replace underscores with spaces while preserving parentheses content
let formatted = withoutHeat.replace(/_/g, ' ').replace(/:/g, ' ');
// Escape parentheses
formatted = formatted.replace(/\(/g, '\\(').replace(/\)/g, '\\)');
return formatted;
}
function applySuggestion(promptText) {
// Log the prompt text before formatting for debugging
//console.log("Debug: promptText before formatting:", promptText);
const formattedText = formatSuggestion(promptText[0]); // Format the suggestion text
const cursorPosition = textbox.selectionStart; // Get the current cursor position
const value = textbox.value; // Get the current textbox value
// Split the text around the cursor
const beforeCursor = value.slice(0, cursorPosition);
const afterCursor = value.slice(cursorPosition);
// Find the position of the last comma before the cursor
const lastCommaIndex = beforeCursor.lastIndexOf(',');
// Determine if a comma is needed after the suggestion
const needsComma = afterCursor.trim().length === 0;
// Insert the suggestion, replacing the text after the last comma or at the start
const newValue = lastCommaIndex >= 0
? beforeCursor.slice(0, lastCommaIndex + 1) + ` ${formattedText}${needsComma ? ',' : ''}` + afterCursor
: `${formattedText}${needsComma ? ',' : ''} ${afterCursor}`;
textbox.value = newValue.trim(); // Update the textbox with the new value
// Clear the current suggestions and hide the suggestion box
currentSuggestions = [];
suggestionBox.style.display = 'none';
// Trigger an input event to notify other listeners
textbox.dispatchEvent(new Event('input', { bubbles: true }));
textbox.focus(); // Refocus the textbox
}
// Update the position of the suggestion box dynamically
function updateSuggestionBoxPosition() {
const rect = textbox.getBoundingClientRect(); // Get the textbox's position and size
suggestionBox.style.top = `${rect.bottom + window.scrollY}px`; // Position below the textbox
const cursorPosition = textbox.selectionStart; // Get the cursor position
const textBeforeCursor = textbox.value.substring(0, cursorPosition); // Text before the cursor
// Create a temporary span to measure the cursor position
const tempSpan = document.createElement('span');
tempSpan.style.position = 'absolute';
tempSpan.style.visibility = 'hidden';
tempSpan.style.font = window.getComputedStyle(textbox).font; // Match the textbox font
tempSpan.textContent = textBeforeCursor;
document.body.appendChild(tempSpan);
// Calculate the offset of the cursor
const cursorOffset = tempSpan.offsetWidth;
document.body.removeChild(tempSpan); // Remove the temporary span
// Set the left position of the suggestion box based on cursor offset
let newLeft = rect.left + window.scrollX + cursorOffset;
// Prevent the suggestion box from overflowing the right edge of the viewport
const suggestionWidth = suggestionBox.offsetWidth;
const windowWidth = window.innerWidth;
if (newLeft + suggestionWidth > windowWidth) {
newLeft = windowWidth - suggestionWidth;
}
// Prevent the suggestion box from going beyond the left edge
if (newLeft < 0) {
newLeft = 0;
}
suggestionBox.style.left = `${newLeft}px`; // Apply the calculated left position
// Force a reflow to ensure the position updates
suggestionBox.style.transform = 'translateZ(0)';
}
// Update the suggestion box position on input
textbox.addEventListener('input', function () {
updateSuggestionBoxPosition();
});
// Update the suggestion box position on scroll
document.addEventListener('scroll', function () {
if (suggestionBox.style.display !== 'none') {
updateSuggestionBoxPosition();
}
}, true);
textbox.dataset.suggestionSetup = 'true'; // Mark the textbox as having the suggestion system set up
});
}
// Log that the script has loaded and attempt initial setup
console.log("Auto Tag JS: Script loaded, attempting initial setup");
setupSuggestionSystem(); // Initialize the suggestion system immediately
dark_theme(); // Apply the dark theme
}