From 86b63047c251692b1de6e1b9310c4482aaf9df87 Mon Sep 17 00:00:00 2001 From: newtextdoc1111 Date: Tue, 10 Jun 2025 04:05:22 +0900 Subject: [PATCH 1/3] feat: Add tag aliases to related tags display --- web/css/autocomplete-plus.css | 305 +++++++++++++++++----------------- web/js/related-tags.js | 76 +++++++-- 2 files changed, 213 insertions(+), 168 deletions(-) diff --git a/web/css/autocomplete-plus.css b/web/css/autocomplete-plus.css index 29b52b8..1059446 100644 --- a/web/css/autocomplete-plus.css +++ b/web/css/autocomplete-plus.css @@ -37,6 +37,62 @@ body.dark-theme { color: var(--input-text); } +#autocomplete-plus-root .danbooru[data-tag-category="general"] { + color: var(--autocomplete-plus-text-color-cat-blue); +} + +#autocomplete-plus-root .danbooru[data-tag-category="artist"] { + color: var(--autocomplete-plus-text-color-cat-red); +} + +#autocomplete-plus-root .danbooru[data-tag-category="copyright"] { + color: var(--autocomplete-plus-text-color-cat-purple); +} + +#autocomplete-plus-root .danbooru[data-tag-category="character"] { + color: var(--autocomplete-plus-text-color-cat-green); +} + +#autocomplete-plus-root .danbooru[data-tag-category="meta"] { + color: var(--autocomplete-plus-text-color-cat-yellow); +} + +#autocomplete-plus-root .danbooru[data-tag-category="unknown"] { + color: var(--autocomplete-plus-text-color-cat-gray); +} + +#autocomplete-plus-root .e621[data-tag-category="general"] { + color: var(--autocomplete-plus-text-color-cat-sky); +} + +#autocomplete-plus-root .e621[data-tag-category="artist"] { + color: var(--autocomplete-plus-text-color-cat-orange); +} + +#autocomplete-plus-root .e621[data-tag-category="copyright"] { + color: var(--autocomplete-plus-text-color-cat-purple); +} + +#autocomplete-plus-root .e621[data-tag-category="character"] { + color: var(--autocomplete-plus-text-color-cat-green); +} + +#autocomplete-plus-root .e621[data-tag-category="species"] { + color: var(--autocomplete-plus-text-color-cat-red); +} + +#autocomplete-plus-root .e621[data-tag-category="meta"] { + color: var(--autocomplete-plus-text-color-cat-white); +} + +#autocomplete-plus-root .e621[data-tag-category="lore"] { + color: var(--autocomplete-plus-text-color-cat-red); +} + +#autocomplete-plus-root .e621[data-tag-category="unknown"] { + color: var(--autocomplete-plus-text-color-cat-purple); +} + #autocomplete-plus-list { display: grid; box-shadow: 0 2px 8px rgb(0 0 0 / 30%); @@ -52,62 +108,6 @@ body.dark-theme { grid-template-columns: subgrid; } -.autocomplete-plus-item.danbooru[data-tag-category="general"] { - color: var(--autocomplete-plus-text-color-cat-blue); -} - -.autocomplete-plus-item.danbooru[data-tag-category="artist"] { - color: var(--autocomplete-plus-text-color-cat-red); -} - -.autocomplete-plus-item.danbooru[data-tag-category="copyright"] { - color: var(--autocomplete-plus-text-color-cat-purple); -} - -.autocomplete-plus-item.danbooru[data-tag-category="character"] { - color: var(--autocomplete-plus-text-color-cat-green); -} - -.autocomplete-plus-item.danbooru[data-tag-category="meta"] { - color: var(--autocomplete-plus-text-color-cat-yellow); -} - -.autocomplete-plus-item.danbooru[data-tag-category="unknown"] { - color: var(--autocomplete-plus-text-color-cat-gray); -} - -.autocomplete-plus-item.e621[data-tag-category="general"] { - color: var(--autocomplete-plus-text-color-cat-sky); -} - -.autocomplete-plus-item.e621[data-tag-category="artist"] { - color: var(--autocomplete-plus-text-color-cat-orange); -} - -.autocomplete-plus-item.e621[data-tag-category="copyright"] { - color: var(--autocomplete-plus-text-color-cat-purple); -} - -.autocomplete-plus-item.e621[data-tag-category="character"] { - color: var(--autocomplete-plus-text-color-cat-green); -} - -.autocomplete-plus-item.e621[data-tag-category="species"] { - color: var(--autocomplete-plus-text-color-cat-red); -} - -.autocomplete-plus-item.e621[data-tag-category="meta"] { - color: var(--autocomplete-plus-text-color-cat-white); -} - -.autocomplete-plus-item.e621[data-tag-category="lore"] { - color: var(--autocomplete-plus-text-color-cat-red); -} - -.autocomplete-plus-item.e621[data-tag-category="unknown"] { - color: var(--autocomplete-plus-text-color-cat-purple); -} - .autocomplete-plus-item span { align-content: center; padding: 4px 8px; @@ -124,11 +124,7 @@ body.dark-theme { } .autocomplete-plus-item.selected span { - background-color: var( - --comfy-menu-bg-selected, - var(--comfy-button-bg) - ); - font-weight: bold; + background-color: var(--border-color); } .autocomplete-plus-item:hover span { @@ -172,26 +168,91 @@ body.dark-theme { color: var(--input-text); } +#related-tags-root .danbooru[data-tag-category="general"] { + color: var(--autocomplete-plus-text-color-cat-blue); +} + +#related-tags-root .danbooru[data-tag-category="artist"] { + color: var(--autocomplete-plus-text-color-cat-red); +} + +#related-tags-root .danbooru[data-tag-category="copyright"] { + color: var(--autocomplete-plus-text-color-cat-purple); +} + +#related-tags-root .danbooru[data-tag-category="character"] { + color: var(--autocomplete-plus-text-color-cat-green); +} + +#related-tags-root .danbooru[data-tag-category="meta"] { + color: var(--autocomplete-plus-text-color-cat-yellow); +} + +#related-tags-root .danbooru[data-tag-category="unknown"] { + color: var(--autocomplete-plus-text-color-cat-gray); +} + +#related-tags-root .e621[data-tag-category="general"] { + color: var(--autocomplete-plus-text-color-cat-sky); +} + +#related-tags-root .e621[data-tag-category="artist"] { + color: var(--autocomplete-plus-text-color-cat-orange); +} + +#related-tags-root .e621[data-tag-category="copyright"] { + color: var(--autocomplete-plus-text-color-cat-purple); +} + +#related-tags-root .e621[data-tag-category="character"] { + color: var(--autocomplete-plus-text-color-cat-green); +} + +#related-tags-root .e621[data-tag-category="species"] { + color: var(--autocomplete-plus-text-color-cat-red); +} + +#related-tags-root .e621[data-tag-category="meta"] { + color: var(--autocomplete-plus-text-color-cat-white); +} + +#related-tags-root .e621[data-tag-category="lore"] { + color: var(--autocomplete-plus-text-color-cat-red); +} + +#related-tags-root .e621[data-tag-category="unknown"] { + color: var(--autocomplete-plus-text-color-cat-gray); +} + #related-tags-header { position: sticky; z-index: 1; top: 0; display: flex; align-items: center; - justify-content: space-between; - padding: 8px 12px; + padding: 8px; border-bottom: 1px solid var(--border-color); background-color: var(--comfy-menu-bg); color: var(--descrip-text); + gap: 8px; } -.related-tags-header-text { - flex-grow: 1; +.related-tags-header-text-container { + display:flex; + flex: 1; + flex-wrap: wrap; + align-items: center; + gap: 12px; +} +.related-tags-header-tag-text { + padding: 2px 0; + line-height: 1.25em; } .related-tags-header-tag-name { font-weight: bold; + word-break: break-all; } .related-tags-header-tag-name .autocomplete-plus-tag-icon-svg { @@ -200,37 +261,37 @@ body.dark-theme { vertical-align: middle; } +.related-tags-header-tag-alias { + overflow: auto; + max-height: 5em; + padding: 2px 4px; + border-radius: 4px; + background-color: var(--bg-color); + color: var(--descrip-text); + line-height: 1.25em; +} + .related-tags-header-controls { - display: flex; - align-items: center; - margin-left: 16px; - gap: 4px; + margin-left: auto; +} + +.related-tags-header-controls button { + border: none; + background: none; + color: var(--input-text); + cursor: pointer; + font-size: 14px; + transition: background-color 0.2s; +} + +.related-tags-header-controls button:hover { + background-color: var(--border-color); } .related-tags-layout-toggle { - padding: 2px 6px; - border: none; - border-radius: 4px; - background: none; - color: var(--input-text); - cursor: pointer; - font-size: 14px; - transition: background-color 0.2s; -} - -.related-tags-layout-toggle:hover { - background-color: var(--comfy-button-bg); } .related-tags-pin-toggle { - padding: 2px 6px; - border: none; - border-radius: 4px; - background: none; - color: var(--input-text); - cursor: pointer; - font-size: 14px; - transition: background-color 0.2s; } #related-tags-list { @@ -248,62 +309,6 @@ body.dark-theme { grid-template-columns: subgrid; } -.related-tag-item.danbooru[data-tag-category="general"] { - color: var(--autocomplete-plus-text-color-cat-blue); -} - -.related-tag-item.danbooru[data-tag-category="artist"] { - color: var(--autocomplete-plus-text-color-cat-red); -} - -.related-tag-item.danbooru[data-tag-category="copyright"] { - color: var(--autocomplete-plus-text-color-cat-purple); -} - -.related-tag-item.danbooru[data-tag-category="character"] { - color: var(--autocomplete-plus-text-color-cat-green); -} - -.related-tag-item.danbooru[data-tag-category="meta"] { - color: var(--autocomplete-plus-text-color-cat-yellow); -} - -.related-tag-item.danbooru[data-tag-category="unknown"] { - color: var(--autocomplete-plus-text-color-cat-gray); -} - -.related-tag-item.e621[data-tag-category="general"] { - color: var(--autocomplete-plus-text-color-cat-sky); -} - -.related-tag-item.e621[data-tag-category="artist"] { - color: var(--autocomplete-plus-text-color-cat-orange); -} - -.related-tag-item.e621[data-tag-category="copyright"] { - color: var(--autocomplete-plus-text-color-cat-purple); -} - -.related-tag-item.e621[data-tag-category="character"] { - color: var(--autocomplete-plus-text-color-cat-green); -} - -.related-tag-item.e621[data-tag-category="species"] { - color: var(--autocomplete-plus-text-color-cat-red); -} - -.related-tag-item.e621[data-tag-category="meta"] { - color: var(--autocomplete-plus-text-color-cat-white); -} - -.related-tag-item.e621[data-tag-category="lore"] { - color: var(--autocomplete-plus-text-color-cat-red); -} - -.related-tag-item.e621[data-tag-category="unknown"] { - color: var(--autocomplete-plus-text-color-cat-gray); -} - /* stylelint-disable-next-line no-descending-specificity */ .related-tag-item span { align-content: center; @@ -324,11 +329,7 @@ body.dark-theme { } .related-tag-item.selected span { - background-color: var( - --comfy-menu-bg-selected, - var(--comfy-button-bg) - ); - font-weight: bold; + background-color: var(--border-color); } .related-tag-item .related-tag-name { @@ -352,9 +353,11 @@ body.dark-theme { white-space: nowrap; } -.related-tags-loading-message { +.related-tags-message { + width: 100%; padding: 12px; font-style: italic; + grid-column: 1 / -1; text-align: center; } diff --git a/web/js/related-tags.js b/web/js/related-tags.js index f687dee..e1f779e 100644 --- a/web/js/related-tags.js +++ b/web/js/related-tags.js @@ -67,7 +67,7 @@ function searchRelatedTags(tag) { const tagSource = TagSource.Danbooru; // TODO: Leave the tag source as Danbooru until e621_tags_cooccurrence.csv is ready - if (!tag || !autoCompleteData[tagSource].cooccurrenceMap.has(tag)) { + if (!tag || !autoCompleteData[tagSource].initialized || !autoCompleteData[tagSource].cooccurrenceMap.has(tag)) { return []; } @@ -199,11 +199,20 @@ class RelatedTagsUI { this.header = document.createElement('div'); this.header.id = 'related-tags-header'; + this.headerTextContainer = document.createElement('div'); + this.headerTextContainer.className = 'related-tags-header-text-container'; + this.header.appendChild(this.headerTextContainer); + // Create header text div for the left side this.headerText = document.createElement('div'); - this.headerText.className = 'related-tags-header-text'; + this.headerText.className = 'related-tags-header-tag-text'; this.headerText.textContent = 'Related Tags'; - this.header.appendChild(this.headerText); + this.headerTextContainer.appendChild(this.headerText); + + // Create header alias div for the 2nd line + this.headerAlias = document.createElement('div'); + this.headerAlias.className = 'related-tags-header-tag-alias'; + this.headerTextContainer.appendChild(this.headerAlias); // Create header controls for the right side this.headerControls = document.createElement('div'); @@ -249,7 +258,7 @@ class RelatedTagsUI { this.headerControls.appendChild(this.pinBtn); this.header.appendChild(this.headerControls); - + this.root.appendChild(this.header); // Create a tbody for the tags @@ -391,7 +400,7 @@ class RelatedTagsUI { */ #updateHeader() { // Find the tag data for the current tag - const tagData = Object.values(TagSource) + let tagData = Object.values(TagSource) .map((source) => { if (source in autoCompleteData && autoCompleteData[source].tagMap.has(this.currentTag)) { return autoCompleteData[source].tagMap.get(this.currentTag); @@ -399,21 +408,49 @@ class RelatedTagsUI { }) .find((tagData) => tagData !== undefined); + if (!tagData) { + tagData = new TagData({ + tag: this.currentTag, + source: TagSource.Danbooru, + category: 'unknown', + count: 0, + alias: [], + }); + } + + const tagText = this.currentTag; + const categoryText = TagCategory[tagData.source][tagData.category]; + // Update header text with current tag this.headerText.innerHTML = ''; // Clear previous content this.headerText.textContent = 'Tags related to: '; + const tagName = document.createElement('span'); - tagName.className = 'related-tags-header-tag-name'; - tagName.textContent = this.currentTag; - if (tagData && ['left', 'right'].includes(settingValues.tagSourceIconPosition)) { + tagName.classList.add('related-tags-header-tag-name', tagData.source); + tagName.title = `Count: ${tagData.count}\nCategory: ${categoryText}`; + tagName.dataset.tagCategory = categoryText; + if (tagData.source && ['left', 'right'].includes(settingValues.tagSourceIconPosition)) { const tagSourceIconHtml = ``; tagName.innerHTML = settingValues.tagSourceIconPosition == 'left' - ? `${tagSourceIconHtml} ${tagData.tag}` - : `${tagData.tag} ${tagSourceIconHtml}`; + ? `${tagSourceIconHtml} ${tagText}` + : `${tagText} ${tagSourceIconHtml}`; + } else { + tagName.textContent += tagText; } + this.headerText.appendChild(tagName); + // Clear previous alias + this.headerAlias.style.display = 'none'; + this.headerAlias.innerHTML = ''; + + // Add alias if available + if (tagData.alias?.length > 0) { + this.headerAlias.textContent = tagData.alias.join(', '); + this.headerAlias.style.display = 'block'; + } + // Update pin button this.pinBtn.textContent = this.isPinned ? '🎯' : '📌'; @@ -432,7 +469,7 @@ class RelatedTagsUI { if (!autoCompleteData[TagSource.Danbooru].initialized) { // Show loading message const messageDiv = document.createElement('div'); - messageDiv.className = 'related-tags-loading-message'; + messageDiv.className = 'related-tags-message'; messageDiv.textContent = `Initializing cooccurrence data... [${autoCompleteData[TagSource.Danbooru].baseLoadingProgress.cooccurrence}%]`; this.tagsContainer.appendChild(messageDiv); return; @@ -441,6 +478,7 @@ class RelatedTagsUI { if (!this.relatedTags || this.relatedTags.length === 0) { // Show no related tags message const messageDiv = document.createElement('div'); + messageDiv.className = 'related-tags-message'; messageDiv.textContent = 'No related tags found'; this.tagsContainer.appendChild(messageDiv); return; @@ -530,20 +568,24 @@ class RelatedTagsUI { this.root.style.maxWidth = ''; this.tagsContainer.style.maxHeight = ''; const rootRect = this.root.getBoundingClientRect(); - const headerRect = this.header.getBoundingClientRect(); - // Hide it again after measurement - this.root.style.display = 'none'; - this.root.style.visibility = 'visible'; // Get the optimal placement area const placementArea = this.#getOptimalPlacementArea(rootRect.width, rootRect.height); - // Apply Styles + // Apply position and size this.root.style.left = `${placementArea.x}px`; this.root.style.top = `${placementArea.y}px`; this.root.style.maxWidth = `${placementArea.width}px`; - this.tagsContainer.style.maxHeight = `${placementArea.height - headerRect.height}px`; + const newHeaderRect = this.header.getBoundingClientRect(); + + if(this.relatedTags.length > 0){ + this.tagsContainer.style.maxHeight = `${placementArea.height - newHeaderRect.height}px`; + } + + // Hide it again after measurement + this.root.style.display = 'none'; + this.root.style.visibility = 'visible'; } /** Highlights the item (row) at the given index */ From 292c99ae1747b2a23aac07d3c5096bddeee5da7e Mon Sep 17 00:00:00 2001 From: newtextdoc1111 Date: Tue, 10 Jun 2025 04:10:08 +0900 Subject: [PATCH 2/3] fix: Remove unused CSS classes for related tags layout --- web/css/autocomplete-plus.css | 6 ------ 1 file changed, 6 deletions(-) diff --git a/web/css/autocomplete-plus.css b/web/css/autocomplete-plus.css index 1059446..5d7e8bb 100644 --- a/web/css/autocomplete-plus.css +++ b/web/css/autocomplete-plus.css @@ -288,12 +288,6 @@ body.dark-theme { background-color: var(--border-color); } -.related-tags-layout-toggle { -} - -.related-tags-pin-toggle { -} - #related-tags-list { display: grid; box-shadow: 0 2px 8px rgb(0 0 0 / 30%); From 31f8fe4517a314e47da40088ecb1f9a0a31021a7 Mon Sep 17 00:00:00 2001 From: newtextdoc1111 Date: Sat, 14 Jun 2025 17:44:32 +0900 Subject: [PATCH 3/3] fix: Update version to 1.1.0 in pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b40afec..a3f2352 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui-autocomplete-plus" description = "Autocomplete and Related Tag display for ComfyUI" -version = "1.0.2" +version = "1.1.0" license = {file = "LICENSE"} dependencies = ["",]