diff --git a/web/js/autocomplete.js b/web/js/autocomplete.js index ef92b31..63cb278 100644 --- a/web/js/autocomplete.js +++ b/web/js/autocomplete.js @@ -1,4 +1,4 @@ -import { +import { TagCategory, autoCompleteData } from './data.js'; @@ -538,7 +538,7 @@ const autocompleteUI = new AutocompleteUI(); * @returns {Array<{tag: string, count: number, alias?: string}>} */ function findCompletionCandidates(query) { - const startTime = performance.now(); // 検索開始時間を記録 + const startTime = performance.now(); // Record start time for performance measurement if (!query) { return []; @@ -568,7 +568,7 @@ function findCompletionCandidates(query) { // Ensure tagData.tag is treated as lowercase for comparison const lowerTag = tagData.tag.toLowerCase(); - + // Check primary tag against all variations for exact match first for (const variation of queryVariations) { if (lowerTag === variation || lowerTag === variation.replace(/[\-_\s]/g, '')) { @@ -577,7 +577,7 @@ function findCompletionCandidates(query) { break; } } - + // If not an exact match, check for partial matches in the tag if (!isExactMatch) { for (const variation of queryVariations) { @@ -596,7 +596,7 @@ function findCompletionCandidates(query) { if (!matched && tagData.alias && Array.isArray(tagData.alias) && tagData.alias.length > 0) { for (const alias of tagData.alias) { const lowerAlias = alias.toLowerCase(); - + // Check for exact matches in aliases first for (const variation of queryVariations) { if (lowerAlias === variation) { @@ -606,7 +606,7 @@ function findCompletionCandidates(query) { break; } } - + // If not an exact match in alias, check for partial matches if (!isExactMatch) { for (const variation of queryVariations) { @@ -617,7 +617,7 @@ function findCompletionCandidates(query) { } } } - + if (matched) break; // Stop checking aliases for this tag if one matched } } @@ -630,25 +630,27 @@ function findCompletionCandidates(query) { category: tagData.category, count: tagData.count, }; - + // Add to exact matches or partial matches based on match type if (isExactMatch) { exactMatches.push(candidateItem); } else { partialMatches.push(candidateItem); } - + addedTags.add(tagData.tag); - + // Check if we've reached the maximum suggestions limit combining both arrays if (exactMatches.length + partialMatches.length >= settingValues.maxSuggestions) { // Return the combined results, prioritizing exact matches const result = [...exactMatches, ...partialMatches].slice(0, settingValues.maxSuggestions); - // const endTime = performance.now(); - // const duration = endTime - startTime; - // console.debug(`[Autocomplete-Plus] Search for "${query}" took ${duration.toFixed(2)}ms. Found ${result.length} candidates (max reached).`); - + if (settingValues.logprocessingTime) { + const endTime = performance.now(); + const duration = endTime - startTime; + console.debug(`[Autocomplete-Plus] Search for "${query}" took ${duration.toFixed(2)}ms. Found ${result.length} candidates (max reached).`); + } + return result; // Early exit } } @@ -656,10 +658,12 @@ function findCompletionCandidates(query) { // Combine results, with exact matches first const candidates = [...exactMatches, ...partialMatches]; - - // const endTime = performance.now(); - // const duration = endTime - startTime; - // console.debug(`[Autocomplete-Plus] Search for "${query}" took ${duration.toFixed(2)}ms. Found ${candidates.length} candidates.`); + + if (settingValues.logprocessingTime) { + const endTime = performance.now(); + const duration = endTime - startTime; + console.debug(`[Autocomplete-Plus] Search for "${query}" took ${duration.toFixed(2)}ms. Found ${candidates.length} candidates.`); + } return candidates; } @@ -790,7 +794,7 @@ export class AutocompleteEventHandler { } handleBlur(event) { - if(!settingValues.hideWhenOutofFocus) return; + if (!settingValues.hideWhenOutofFocus) return; // Need a slight delay because clicking the autocomplete list causes blur setTimeout(() => { diff --git a/web/js/data.js b/web/js/data.js index aeef3af..48f89ea 100644 --- a/web/js/data.js +++ b/web/js/data.js @@ -1,4 +1,5 @@ -// Constants +// --- Constants --- +// Tag categories for display export const TagCategory = [ 'general', 'artist', diff --git a/web/js/settings.js b/web/js/settings.js index 598357b..9e7b31f 100644 --- a/web/js/settings.js +++ b/web/js/settings.js @@ -7,5 +7,9 @@ export const settingValues = { enableSimilarTags: true, maxSimilarTags: 20, similarTagsDisplayPosition: 'horizontal', // 'horizontal' or 'vertical' - hideWhenOutofFocus: true, // Hide similar tags when the input is out of focus + + // Debugging settings (use internally) + hideWhenOutofFocus: true, // Hide UI when the input is out of focus + logprocessingTime: true, // Log processing time for debugging + } \ No newline at end of file diff --git a/web/js/similar-tags.js b/web/js/similar-tags.js index 9e9e0c4..e1e84af 100644 --- a/web/js/similar-tags.js +++ b/web/js/similar-tags.js @@ -4,6 +4,7 @@ import { TagCategory, autoCompleteData } from './data.js'; import { normalizeTagToInsert, normalizeTagToSearch, + isValidTag, getViewportMargin } from './utils.js'; @@ -96,7 +97,7 @@ class SimilarTagsUI { // Update header with current tag this.header.innerHTML = ''; // Clear previous content - this.header.textContent = 'Similar Tags: '; + this.header.textContent = 'Tags related to: '; const tagNameSpan = document.createElement('span'); tagNameSpan.className = 'similar-tags-header-tag-name'; tagNameSpan.textContent = this.currentTag; @@ -266,15 +267,15 @@ class SimilarTagsUI { height: Math.min(elemHeight, viewportHeight - margin.top - margin.bottom) }; - if(settingValues.similarTagsDisplayPosition === 'vertical') { + if (settingValues.similarTagsDisplayPosition === 'vertical') { // Vertical placement const topSpace = inputRect.top - margin.top; const bottomSpace = viewportHeight - inputRect.bottom - margin.bottom; - if(topSpace > bottomSpace) { + if (topSpace > bottomSpace) { // Place above area.height = Math.min(area.height, topSpace); area.y = Math.max(inputRect.y - area.height, margin.top); - }else{ + } else { // Place below area.height = Math.min(area.height, bottomSpace); area.y = inputRect.bottom; @@ -282,15 +283,15 @@ class SimilarTagsUI { // Adjust x position to avoid overflow area.x = Math.min(area.x, viewportWidth - area.width - margin.right); - }else{ + } else { // Horizontal placement const leftSpace = inputRect.x - margin.left; const rightSpace = viewportWidth - inputRect.right - margin.right; - if(leftSpace > rightSpace) { + if (leftSpace > rightSpace) { // Place left area.width = Math.min(area.width, leftSpace); area.x = Math.max(inputRect.x - area.width, margin.left); - }else{ + } else { // Place right area.width = Math.min(area.width, rightSpace); area.x = inputRect.right; @@ -337,6 +338,8 @@ function calculateJaccardSimilarity(tagA, tagB) { * @returns {Array<{tag: string, similarity: number, count: number, alias?: string[]}>} */ function findSimilarTags(tag) { + const startTime = performance.now(); // Record start time for performance measurement + if (!tag || !autoCompleteData.cooccurrenceMap.has(tag)) { return []; } @@ -369,7 +372,13 @@ function findSimilarTags(tag) { similarTags.sort((a, b) => b.similarity - a.similarity); // Limit to max number of suggestions - return similarTags.slice(0, settingValues.maxSimilarTags); + const result = similarTags.slice(0, settingValues.maxSimilarTags); + + const endTime = performance.now(); + const duration = endTime - startTime; + console.debug(`[Related-Tags] Find tags to related "${tag}" took ${duration.toFixed(2)}ms.`); + + return result; } /** @@ -408,7 +417,7 @@ function getCurrentTag(inputElement) { // If no tag found, return null if (!tag) return null; - + // Process the tag: swap underscores/spaces and unescape parentheses return normalizeTagToSearch(tag); } @@ -573,7 +582,7 @@ function showSimilarTagsForCurrentPosition(textareaElement) { const currentTag = getCurrentTag(textareaElement); // If no valid tag or tag is too short, hide the panel - if (!currentTag || currentTag.length < 2) { + if (!isValidTag(currentTag)) { similarTagsUI.hide(); return; } @@ -603,7 +612,7 @@ export class SimilarTagsEventHandler { } handleBlur(event) { - if(!settingValues.hideWhenOutofFocus) { + if (!settingValues.hideWhenOutofFocus) { return; } @@ -643,7 +652,7 @@ export class SimilarTagsEventHandler { } handleMouseMove(event) { - + } handleClick(event) { diff --git a/web/js/utils.js b/web/js/utils.js index 4764fe5..86e1ab8 100644 --- a/web/js/utils.js +++ b/web/js/utils.js @@ -85,10 +85,10 @@ export function unescapeParentheses(str) { */ export function removePromptWeight(str) { if (!str) return str; - + // First remove weight notation (e.g., ":1.2") let result = str.replace(/(.+?):\d+(\.\d+)?/, '$1'); - + // Then remove non-escaped brackets at the beginning and/or end // Use negative lookbehind (?") + if (//i.test(tag)) { + return false; + } + + return true; +} + // --- End String Helper Functions --- // Function to load a CSS file @@ -133,11 +156,11 @@ export function loadCSS(href) { * Get the viewport margin based on the positions of the top, bottom, left, and right bars. * @returns {Object} - An object containing the top, bottom, left, and right margins of the viewport. */ -export function getViewportMargin(){ - const topBarRect = document.querySelector("#comfyui-body-top")?.getBoundingClientRect() || {top: 0, bottom: 0}; - const bottomBarRect = document.querySelector("#comfyui-body-bottom")?.getBoundingClientRect() || {top: 0, bottom: 0}; - const leftBarRect = document.querySelector("#comfyui-body-left")?.getBoundingClientRect() || {left: 0, right: 0}; - const rightBarRect = document.querySelector("#comfyui-body-right")?.getBoundingClientRect() || {left: 0, right: 0}; +export function getViewportMargin() { + const topBarRect = document.querySelector("#comfyui-body-top")?.getBoundingClientRect() || { top: 0, bottom: 0 }; + const bottomBarRect = document.querySelector("#comfyui-body-bottom")?.getBoundingClientRect() || { top: 0, bottom: 0 }; + const leftBarRect = document.querySelector("#comfyui-body-left")?.getBoundingClientRect() || { left: 0, right: 0 }; + const rightBarRect = document.querySelector("#comfyui-body-right")?.getBoundingClientRect() || { left: 0, right: 0 }; return { top: topBarRect.height,