diff --git a/web/css/autocomplete-plus.css b/web/css/autocomplete-plus.css index 2a0282c..e265296 100644 --- a/web/css/autocomplete-plus.css +++ b/web/css/autocomplete-plus.css @@ -114,8 +114,8 @@ body.dark-theme { white-space: nowrap; } -/* Similar Tags UI Styles */ -#similar-tags-container { +/* Related Tags UI Styles */ +#related-tags-container { position: absolute; z-index: 999; top: 0; @@ -127,7 +127,7 @@ body.dark-theme { overflow-y: auto; } -#similar-tags-header { +#related-tags-header { position: sticky; z-index: 1; top: 0; @@ -137,89 +137,89 @@ body.dark-theme { color: var(--descrip-text); } -.similar-tags-header-tag-name { +.related-tags-header-tag-name { font-weight: bold; } -#similar-tags-list { +#related-tags-list { display: grid; box-shadow: 0 2px 8px rgb(0 0 0 / 30%); grid-auto-rows: auto; grid-template-columns: max-content 1fr auto auto; } -.similar-tag-item { +.related-tag-item { display: contents; cursor: pointer; } -.similar-tag-item[data-tag-category="general"] { +.related-tag-item[data-tag-category="general"] { color: var(--autocomplete-plus-text-color-cat-general); } -.similar-tag-item[data-tag-category="artist"] { +.related-tag-item[data-tag-category="artist"] { color: var(--autocomplete-plus-text-color-cat-artist); } -.similar-tag-item[data-tag-category="copyright"] { +.related-tag-item[data-tag-category="copyright"] { color: var(--autocomplete-plus-text-color-cat-copyright); } -.similar-tag-item[data-tag-category="character"] { +.related-tag-item[data-tag-category="character"] { color: var(--autocomplete-plus-text-color-cat-character); } -.similar-tag-item[data-tag-category="meta"] { +.related-tag-item[data-tag-category="meta"] { color: var(--autocomplete-plus-text-color-cat-meta); } -.similar-tag-item[data-tag-category="unknown"] { +.related-tag-item[data-tag-category="unknown"] { color: var(--autocomplete-plus-text-color-cat-unknown); } -.similar-tag-item span { +.related-tag-item span { align-content: center; padding: 4px 8px; border-bottom: 1px solid var(--border-color); } -.similar-tag-item:nth-child(even) span { +.related-tag-item:nth-child(even) span { background-color: var(--comfy-menu-bg); } -.similar-tag-item:nth-child(odd) span { +.related-tag-item:nth-child(odd) span { background-color: var(--comfy-menu-secondary-bg); } -.similar-tag-item:hover span { +.related-tag-item:hover span { background-color: var(--p-form-field-filled-hover-background); } -.similar-tag-name { +.related-tag-name { padding: 8px; white-space: nowrap; } -.similar-tag-alias { +.related-tag-alias { overflow: hidden; color: var(--descrip-text); text-overflow: ellipsis; white-space: nowrap; } -.similar-tag-similarity { +.related-tag-relatedity { color: var(--descrip-text); text-align: right; white-space: nowrap; } -.similar-tags-loading-message { +.related-tags-loading-message { padding: 12px; font-style: italic; text-align: center; } -.similar-tags-empty { +.related-tags-empty { padding: 12px; color: var(--error-text); font-style: italic; diff --git a/web/js/main.js b/web/js/main.js index dfd2c62..a90e278 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -4,11 +4,11 @@ import { settingValues } from "./settings.js"; import { loadCSS } from "./utils.js"; import { loadAllData } from "./data.js"; import { AutocompleteEventHandler } from "./autocomplete.js"; -import { SimilarTagsEventHandler } from "./similar-tags.js"; +import { RelatedTagsEventHandler } from "./related-tags.js"; function initializeEventHandlers() { const autocompleteEventHandler = new AutocompleteEventHandler(); - const similarTagsEventHandler = new SimilarTagsEventHandler(); + const relatedTagsEventHandler = new RelatedTagsEventHandler(); const attachedElements = new WeakSet(); // Keep track of elements that have listeners attached // Function to attach listeners @@ -22,7 +22,7 @@ function initializeEventHandlers() { element.addEventListener('keyup', handleKeyUp); element.addEventListener('keypress', handleKeyPress); - // Add new event listeners for similar tags feature + // Add new event listeners for related tags feature element.addEventListener('mousemove', handleMouseMove); element.addEventListener('click', handleClick); @@ -38,7 +38,7 @@ function initializeEventHandlers() { const result = originalStringWidget.apply(this, arguments); // Check if the widget has an inputEl and if it's a TEXTAREA - // This is to ensure we are targeting multiline text inputs, similar to '.comfy-multiline-input' + // This is to ensure we are targeting multiline text inputs, related to '.comfy-multiline-input' if (result && result.widget && result.widget.inputEl && result.widget.inputEl.tagName === 'TEXTAREA') { const widgetConfig = inputData && inputData[1] ? inputData[1] : {}; // Future: Add checks for Autocomplete Plus specific configurations if needed @@ -85,44 +85,44 @@ function initializeEventHandlers() { function handleInput(event) { autocompleteEventHandler.handleInput(event); - similarTagsEventHandler.handleInput(event); + relatedTagsEventHandler.handleInput(event); } function handleFocus(event) { autocompleteEventHandler.handleFocus(event); - similarTagsEventHandler.handleFocus(event); + relatedTagsEventHandler.handleFocus(event); } function handleBlur(event) { autocompleteEventHandler.handleBlur(event); - similarTagsEventHandler.handleBlur(event); + relatedTagsEventHandler.handleBlur(event); } function handleKeyDown(event) { autocompleteEventHandler.handleKeyDown(event); - similarTagsEventHandler.handleKeyDown(event); + relatedTagsEventHandler.handleKeyDown(event); } function handleKeyUp(event) { autocompleteEventHandler.handleKeyUp(event); - similarTagsEventHandler.handleKeyUp(event); + relatedTagsEventHandler.handleKeyUp(event); } function handleKeyPress(event) { autocompleteEventHandler.handleKeyPress(event); - similarTagsEventHandler.handleKeyPress(event); + relatedTagsEventHandler.handleKeyPress(event); } - // New event handler for mousemove to show similar tags on hover + // New event handler for mousemove to show related tags on hover function handleMouseMove(event) { autocompleteEventHandler.handleMouseMove(event); - similarTagsEventHandler.handleMouseMove(event); + relatedTagsEventHandler.handleMouseMove(event); } - // New event handler for click to show similar tags + // New event handler for click to show related tags function handleClick(event) { autocompleteEventHandler.handleClick(event); - similarTagsEventHandler.handleClick(event); + relatedTagsEventHandler.handleClick(event); } } @@ -166,22 +166,22 @@ app.registerExtension({ settingValues.maxSuggestions = newVal; } }, - // Similar Tags Settings + // Related Tags Settings { - id: id + ".similar_tags_enable", - name: "Enable Similar Tags", - description: "Enable or disable the similar tags feature.", + id: id + ".related_tags_enable", + name: "Enable Related Tags", + description: "Enable or disable the related tags feature.", type: "boolean", defaultValue: true, - category: [name, "Similar Tags", "Enable Similar Tags"], + category: [name, "Related Tags", "Enable Related Tags"], onChange: (newVal, oldVal) => { - settingValues.enableSimilarTags = newVal; + settingValues.enableRelatedTags = newVal; } }, { - id: id + ".max_similar_tags", - name: "Max similar tags", - description: "Maximum number of similar tags to display", + id: id + ".max_related_tags", + name: "Max related tags", + description: "Maximum number of related tags to display", type: "slider", attrs: { min: 5, @@ -189,21 +189,21 @@ app.registerExtension({ step: 5, }, defaultValue: 20, - category: [name, "Similar Tags", "Max similar tags"], + category: [name, "Related Tags", "Max related tags"], onChange: (newVal, oldVal) => { - settingValues.maxSimilarTags = newVal; + settingValues.maxRelatedTags = newVal; } }, { - id: id + ".similar_tags_position", - name: "Similar Tags Display Position", - description: "Position of similar tags display", + id: id + ".related_tags_position", + name: "Related Tags Display Position", + description: "Position of related tags display", type: "combo", options: ["horizontal", "vertical"], defaultValue: "horizontal", - category: [name, "Similar Tags", "Display Position"], + category: [name, "Related Tags", "Display Position"], onChange: (newVal, oldVal) => { - settingValues.similarTagsDisplayPosition = newVal; + settingValues.relatedTagsDisplayPosition = newVal; } } ] diff --git a/web/js/similar-tags.js b/web/js/related-tags.js similarity index 85% rename from web/js/similar-tags.js rename to web/js/related-tags.js index ce44f49..1c54beb 100644 --- a/web/js/similar-tags.js +++ b/web/js/related-tags.js @@ -1,4 +1,4 @@ -// filepath: v:\Programs\StabilityMatrix-win-x64\Data\Packages\ComfyUI-New\custom_nodes\ComfyUI-Autocomplete-Plus\web\js\similar-tags.js +// filepath: v:\Programs\StabilityMatrix-win-x64\Data\Packages\ComfyUI-New\custom_nodes\ComfyUI-Autocomplete-Plus\web\js\related-tags.js import { settingValues } from './settings.js'; import { TagCategory, autoCompleteData } from './data.js'; import { @@ -9,27 +9,27 @@ import { getViewportMargin } from './utils.js'; -// --- SimilarTags UI Class --- +// --- RelatedTags UI Class --- /** - * Class that manages the UI for displaying similar tags. - * Shows a panel with tags similar to the current tag under cursor. + * Class that manages the UI for displaying related tags. + * Shows a panel with tags related to the current tag under cursor. */ -class SimilarTagsUI { +class RelatedTagsUI { constructor() { // Create the main container this.root = document.createElement('div'); - this.root.id = 'similar-tags-container'; + this.root.id = 'related-tags-container'; // Create header row this.header = document.createElement('div'); - this.header.id = 'similar-tags-header'; - this.header.textContent = 'Similar Tags'; + this.header.id = 'related-tags-header'; + this.header.textContent = 'Related Tags'; this.root.appendChild(this.header); // Create a tbody for the tags this.tagsContainer = document.createElement('div'); - this.tagsContainer.id = 'similar-tags-list'; + this.tagsContainer.id = 'related-tags-list'; this.root.appendChild(this.tagsContainer); // Add to DOM @@ -41,7 +41,7 @@ class SimilarTagsUI { // Add click handler for tag selection this.tagsContainer.addEventListener('mousedown', (e) => { - const row = e.target.closest('.similar-tag-item'); + const row = e.target.closest('.related-tag-item'); if (row && row.dataset.tag) { this.selectTag(row.dataset.tag); e.preventDefault(); @@ -54,13 +54,13 @@ class SimilarTagsUI { } /** - * Shows the similar tags UI for a specific tag and text input element. + * Shows the related tags UI for a specific tag and text input element. * @param {HTMLTextAreaElement} inputElement The textarea being used - * @param {string} tag The tag to find similarities for - * @param {Array<{tag: string, similarity: number, count: number, alias?: string[]}>} similarTags List of similar tags + * @param {string} tag The tag to find relatedities for + * @param {Array<{tag: string, similarity: number, count: number, alias?: string[]}>} relatedTags List of related tags */ - show(inputElement, tag, similarTags) { - if (!settingValues.enableSimilarTags) { + show(inputElement, tag, relatedTags) { + if (!settingValues.enableRelatedTags) { this.hide(); return; } @@ -68,8 +68,8 @@ class SimilarTagsUI { this.activeInput = inputElement; this.currentTag = tag; - // Update content (even if there are no similar tags, we'll show a message) - this.updateContent(similarTags); + // Update content (even if there are no related tags, we'll show a message) + this.updateContent(relatedTags); // Calculate and update position this.updatePosition(inputElement); @@ -82,13 +82,13 @@ class SimilarTagsUI { clearTimeout(this.autoRefreshTimerId); } this.autoRefreshTimerId = setTimeout(() => { - similarTagsUI.show(inputElement, tag, similarTags); + relatedTagsUI.show(inputElement, tag, relatedTags); }, 500); } } /** - * Hides the similar tags UI. + * Hides the related tags UI. */ hide() { if(this.autoRefreshTimerId){ @@ -101,10 +101,10 @@ class SimilarTagsUI { } /** - * Updates the content of the similar tags panel with the provided tags. - * @param {Array<{tag: string, similarity: number, count: number, alias?: string[]}>} similarTags + * Updates the content of the related tags panel with the provided tags. + * @param {Array<{tag: string, similarity: number, count: number, alias?: string[]}>} relatedTags */ - updateContent(similarTags) { + updateContent(relatedTags) { this.root.style.left = 0; this.root.style.top = 0; this.root.style.maxWidth = `${window.innerWidth / 2}px`; @@ -115,34 +115,34 @@ class SimilarTagsUI { this.header.innerHTML = ''; // Clear previous content this.header.textContent = 'Tags related to: '; const tagNameSpan = document.createElement('span'); - tagNameSpan.className = 'similar-tags-header-tag-name'; + tagNameSpan.className = 'related-tags-header-tag-name'; tagNameSpan.textContent = this.currentTag; this.header.appendChild(tagNameSpan); if (!autoCompleteData.cooccurrenceLoaded) { const messageDiv = document.createElement('div'); - messageDiv.className = 'similar-tags-loading-message'; + messageDiv.className = 'related-tags-loading-message'; messageDiv.textContent = `Initializing cooccurrence data... [${autoCompleteData.cooccurrenceInitProgress}%]`; this.tagsContainer.appendChild(messageDiv); return; } - if (!similarTags || similarTags.length === 0) { + if (!relatedTags || relatedTags.length === 0) { const messageCell = document.createElement('div'); - messageCell.textContent = 'No similar tags found'; + messageCell.textContent = 'No related tags found'; this.tagsContainer.appendChild(messageCell); return; } // Create tag rows - similarTags.forEach(tagData => { + relatedTags.forEach(tagData => { const tagRow = this.createTagElement(tagData); this.tagsContainer.appendChild(tagRow); }); } /** - * Creates an HTML table row for a similar tag. + * Creates an HTML table row for a related tag. * @param {{tag: string, similarity: number, count: number, alias?: string[]}} tagData * @returns {HTMLTableRowElement} The tag row element */ @@ -150,18 +150,18 @@ class SimilarTagsUI { const categoryText = TagCategory[tagData.category] || "unknown"; const tagRow = document.createElement('div'); - tagRow.className = 'similar-tag-item'; + tagRow.className = 'related-tag-item'; tagRow.dataset.tag = tagData.tag; tagRow.dataset.tagCategory = categoryText; // Tag name cell const tagNameCell = document.createElement('span'); - tagNameCell.className = 'similar-tag-name'; + tagNameCell.className = 'related-tag-name'; tagNameCell.textContent = tagData.tag; // Alias cell (middle column) const aliasCell = document.createElement('span'); - aliasCell.className = 'similar-tag-alias'; + aliasCell.className = 'related-tag-alias'; // Display alias if available if (tagData.alias && tagData.alias.length > 0) { @@ -172,12 +172,12 @@ class SimilarTagsUI { // Category cell const categoryCell = document.createElement('span'); - categoryCell.className = `similar-tag-category`; + categoryCell.className = `related-tag-category`; categoryCell.textContent = `${categoryText.substring(0, 2)}`; // Similarity cell const similarityCell = document.createElement('span'); - similarityCell.className = 'similar-tag-similarity'; + similarityCell.className = 'related-tag-similarity'; similarityCell.textContent = `${(tagData.similarity * 100).toFixed(2)}%`; // Create tooltip with more info @@ -197,9 +197,9 @@ class SimilarTagsUI { } /** - * Updates the position of the similar tags panel. + * Updates the position of the related tags panel. * Position is calculated based on the input element, available space, - * and the setting `similarTagsDisplayPosition`. + * and the setting `relatedTagsDisplayPosition`. * @param {HTMLElement} inputElement The input element to position */ updatePosition(inputElement) { @@ -241,7 +241,7 @@ class SimilarTagsUI { } /** - * Handles the selection of a similar tag. + * Handles the selection of a related tag. * Inserts the tag into the active input. * @param {string} tag */ @@ -256,7 +256,7 @@ class SimilarTagsUI { } /** - * Checks if the similar tags UI is currently visible. + * Checks if the related tags UI is currently visible. * @returns {boolean} */ isVisible() { @@ -289,7 +289,7 @@ class SimilarTagsUI { height: Math.min(elemHeight, viewportHeight - margin.top - margin.bottom) }; - if (settingValues.similarTagsDisplayPosition === 'vertical') { + if (settingValues.relatedTagsDisplayPosition === 'vertical') { // Vertical placement const topSpace = inputRect.top - margin.top; const bottomSpace = viewportHeight - inputRect.bottom - margin.bottom; @@ -355,11 +355,11 @@ function calculateJaccardSimilarity(tagA, tagB) { } /** - * Finds similar tags for a given tag. - * @param {string} tag The tag to find similar tags for + * Finds related tags for a given tag. + * @param {string} tag The tag to find related tags for * @returns {Array<{tag: string, similarity: number, count: number, alias?: string[]}>} */ -function findSimilarTags(tag) { +function findRelatedTags(tag) { const startTime = performance.now(); // Record start time for performance measurement if (!tag || !autoCompleteData.cooccurrenceMap.has(tag)) { @@ -367,7 +367,7 @@ function findSimilarTags(tag) { } const cooccurrences = autoCompleteData.cooccurrenceMap.get(tag); - const similarTags = []; + const relatedTags = []; // Convert to array for sorting cooccurrences.forEach((count, coTag) => { @@ -381,7 +381,7 @@ function findSimilarTags(tag) { // Calculate similarity const similarity = calculateJaccardSimilarity(tag, coTag); - similarTags.push({ + relatedTags.push({ tag: coTag, similarity: similarity, alias: tagData.alias, @@ -391,10 +391,10 @@ function findSimilarTags(tag) { }); // Sort by similarity (highest first) - similarTags.sort((a, b) => b.similarity - a.similarity); + relatedTags.sort((a, b) => b.similarity - a.similarity); // Limit to max number of suggestions - const result = similarTags.slice(0, settingValues.maxSimilarTags); + const result = relatedTags.slice(0, settingValues.maxRelatedTags); const endTime = performance.now(); const duration = endTime - startTime; @@ -555,27 +555,27 @@ function findAllTagPositions(text) { // --- Main Exports --- // Create a singleton instance -const similarTagsUI = new SimilarTagsUI(); +const relatedTagsUI = new RelatedTagsUI(); -// Helper function to show similar tags based on cursor position -function showSimilarTagsForCurrentPosition(textareaElement) { +// Helper function to show related tags based on cursor position +function showRelatedTagsForCurrentPosition(textareaElement) { // Get the tag at current cursor position const currentTag = normalizeTagToSearch(getCurrentTag(textareaElement)); // If no valid tag or tag is too short, hide the panel if (!isValidTag(currentTag)) { - similarTagsUI.hide(); + relatedTagsUI.hide(); return; } - // Find similar tags - const similarTagsResults = findSimilarTags(currentTag); + // Find related tags + const relatedTagsResults = findRelatedTags(currentTag); - // Always show the panel with current tag, even if there are no similar tags - similarTagsUI.show(textareaElement, currentTag, similarTagsResults); + // Always show the panel with current tag, even if there are no related tags + relatedTagsUI.show(textareaElement, currentTag, relatedTagsResults); } -export class SimilarTagsEventHandler { +export class RelatedTagsEventHandler { constructor() { } @@ -585,9 +585,9 @@ export class SimilarTagsEventHandler { * @param {KeyboardEvent} event */ handleInput(event) { - if (settingValues.enableSimilarTags) { - if (similarTagsUI && similarTagsUI.isVisible()) { - similarTagsUI.hide(); + if (settingValues.enableRelatedTags) { + if (relatedTagsUI && relatedTagsUI.isVisible()) { + relatedTagsUI.hide(); } } } @@ -609,17 +609,17 @@ export class SimilarTagsEventHandler { return; } - // Use setTimeout to delay hiding. This allows clicks on the similar tags UI + // Use setTimeout to delay hiding. This allows clicks on the related tags UI // to be processed before the UI is hidden. setTimeout(() => { - // Check if the new focused element is part of the similar tags UI. + // Check if the new focused element is part of the related tags UI. // document.activeElement refers to the currently focused element. const activeElement = document.activeElement; - const similarTagsElement = similarTagsUI.root; + const relatedTagsElement = relatedTagsUI.root; - // If the focus is not within the similar tags UI, hide it. - if (similarTagsUI && !similarTagsElement.contains(activeElement)) { - similarTagsUI.hide(); + // If the focus is not within the related tags UI, hide it. + if (relatedTagsUI && !relatedTagsElement.contains(activeElement)) { + relatedTagsUI.hide(); } }, 150); // Delay in milliseconds (adjust if necessary) } @@ -631,19 +631,19 @@ export class SimilarTagsEventHandler { handleKeyDown(event) { const textareaElement = event.target; - // For similar tags panel, handle Escape key - if (similarTagsUI && similarTagsUI.isVisible()) { + // For related tags panel, handle Escape key + if (relatedTagsUI && relatedTagsUI.isVisible()) { if (event.key === 'Escape') { event.preventDefault(); - similarTagsUI.hide(); + relatedTagsUI.hide(); } } - // Show similar tags on Ctrl+Space - if (settingValues.enableSimilarTags) { + // Show related tags on Ctrl+Space + if (settingValues.enableRelatedTags) { if (event.key === ' ' && event.ctrlKey && event.shiftKey ) { event.preventDefault(); - showSimilarTagsForCurrentPosition(textareaElement); + showRelatedTagsForCurrentPosition(textareaElement); } } } @@ -680,6 +680,6 @@ export class SimilarTagsEventHandler { */ handleClick(event) { const textareaElement = event.target; - showSimilarTagsForCurrentPosition(textareaElement); + showRelatedTagsForCurrentPosition(textareaElement); } } \ No newline at end of file diff --git a/web/js/settings.js b/web/js/settings.js index d8f4451..03c8875 100644 --- a/web/js/settings.js +++ b/web/js/settings.js @@ -3,10 +3,10 @@ export const settingValues = { enabled: true, maxSuggestions: 10, - // Similar tags feature settings - enableSimilarTags: true, - maxSimilarTags: 20, - similarTagsDisplayPosition: 'horizontal', // 'horizontal' or 'vertical' + // Related tags feature settings + enableRelatedTags: true, + maxRelatedTags: 20, + relatedTagsDisplayPosition: 'horizontal', // 'horizontal' or 'vertical' // Internal logic settings _useFallbackAttachmentForEventListener: false, // Fallback to attach event listener if not using jQuery