Spaces:
Running
Running
File size: 20,058 Bytes
901270b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
//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
}
|