From 3aad1ca36481dafe904edb83e37779cb5732e3b0 Mon Sep 17 00:00:00 2001 From: KennyG Date: Wed, 18 Feb 2026 09:37:31 -0500 Subject: [PATCH 1/9] Add Stash ID icon feature to performer cards - Introduced a new setting to enable/disable the Stash ID icon on performer cards. - Implemented functionality to display an icon indicating the presence of Stash IDs for performers. - Updated CSS styles to accommodate the new icon. --- plugins/cjCardTweaks/cjCardTweaks.js | 44 ++++++++++++++++++++++++++- plugins/cjCardTweaks/cjCardTweaks.yml | 4 +++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/plugins/cjCardTweaks/cjCardTweaks.js b/plugins/cjCardTweaks/cjCardTweaks.js index 923e5640..0a1c9e1b 100644 --- a/plugins/cjCardTweaks/cjCardTweaks.js +++ b/plugins/cjCardTweaks/cjCardTweaks.js @@ -26,7 +26,8 @@ if ( key === "fileCount" || key === "addBannerDimension" || - key === "performerProfileCards" + key === "performerProfileCards" || + key === "stashIDIcon" ) { acc[key] = settings[key]; } else { @@ -42,6 +43,8 @@ ".performer-card:hover img.performer-card-image{box-shadow: 0 0 0 rgb(0 0 0 / 20%), 0 0 6px rgb(0 0 0 / 90%);transition: box-shadow .5s .5s}@media (min-width: 1691px){.performer-recommendations .card .performer-card-image{height: unset}}button.btn.favorite-button.not-favorite,button.btn.favorite-button.favorite{transition: filter .5s .5s}.performer-card:hover .thumbnail-section button.btn.favorite-button.not-favorite, .performer-card:hover .thumbnail-section button.btn.favorite-button.favorite{filter: drop-shadow(0 0 2px rgba(0, 0, 0, .9))}.performer-card .thumbnail-section button.btn.favorite-button.not-favorite, .performer-card .thumbnail-section button.btn.favorite-button.favorite{top: 10px;filter: drop-shadow(0 2px 2px rgba(0, 0, 0, .9))}.item-list-container .performer-card__age,.recommendation-row .performer-card__age,.item-list-container .performer-card .card-section-title,.recommendation-row .performer-card .card-section-title,.item-list-container .performer-card .thumbnail-section,.recommendation-row .performer-card .thumbnail-section{display: flex;align-content: center;justify-content: center}.item-list-container .performer-card .thumbnail-section a,.recommendation-row .performer-card .thumbnail-section a{display: contents}.item-list-container .performer-card-image,.recommendation-row .performer-card-image{aspect-ratio: 1 / 1;display: flex;object-fit: cover;border: 3px solid var(--plex-yelow);border-radius: 50%;min-width: unset;position: relative;width: 58%;margin: auto;z-index: 1;margin-top: 1.5rem;box-shadow:0 13px 26px rgb(0 0 0 / 20%),0 3px 6px rgb(0 0 0 / 90%);object-position: center;transition: box-shadow .5s .5s}.item-list-container .performer-card hr,.recommendation-row .performer-card hr{width: 90%}.item-list-container .performer-card .fi,.recommendation-row .performer-card .fi{position: absolute;top: 81.5%;left: 69%;border-radius: 50% !important;background-size: cover;margin-left: -1px;height: 1.5rem;width: 1.5rem;z-index: 10;border: solid 2px #252525;box-shadow: unset}.item-list-container .performer-card .card-popovers .btn,.recommendation-row .performer-card .card-popovers .btn{font-size: 0.9rem}"; const RATING_BANNER_3D_STYLE = ".grid-card{overflow:unset}.detail-group .rating-banner-3d,.rating-banner{display:none}.grid-card:hover .rating-banner-3d{opacity:0;transition:opacity .5s}.rating-banner-3d{height:110px;left:-6px;overflow:hidden;position:absolute;top:-6px;width:110px}.rating-banner-3d span{box-shadow:0 5px 4px rgb(0 0 0 / 50%);position:absolute;display:block;width:170px;padding:10px 5px 10px 0;background-color:#ff6a07;color:#fff;font:700 1rem/1 Lato,sans-serif;text-shadow:0 1px 1px rgba(0,0,0,.2);text-transform:uppercase;text-align:center;letter-spacing:1px;right:-20px;top:24px;transform:rotate(-45deg)}.rating-banner-3d::before{top:0;right:0;position:absolute;z-index:-1;content:'';display:block;border:5px solid #a34405;border-top-color:transparent;border-left-color:transparent}.rating-banner-3d::after{bottom:0;left:0;position:absolute;z-index:-1;content:'';display:block;border:5px solid #963e04}"; + const STASH_ID_ICON_STYLE = + ".stash-id-icon{position:absolute;top:0.3rem;left:0.5rem;z-index:10;width:1.2rem;height:1.2rem;background-color:#007bff;border:2px solid #fff;border-radius:3px;box-shadow:0 2px 4px rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center}.stash-id-icon svg{width:0.8rem;height:0.8rem;fill:#fff}"; /** * Element to inject custom CSS styles. @@ -54,6 +57,8 @@ styleElement.innerHTML += RATING_BANNER_3D_STYLE; if (SETTINGS.performerProfileCards) styleElement.innerHTML += PERFORMER_PROFILE_CARD_STYLE; + if (SETTINGS.stashIDIcon) + styleElement.innerHTML += STASH_ID_ICON_STYLE; function createElementFromHTML(htmlString) { const div = document.createElement("div"); @@ -207,6 +212,9 @@ cards.forEach((card) => { maybeAddFileCount(card, stashData, isContentCard); maybeAddDimensionToBanner(card); + if (cardClass === "performer-card") { + maybeAddStashIDIcon(card, stashData); + } }); } @@ -269,4 +277,38 @@ link.parentElement.appendChild(el); oldBanner.remove(); } + + /** + * Add Stash ID icon to performer cards that have stash IDs + * + * @param {Element} card - Card element from cards list. + * @param {Object} stashData - Data fetched from the GraphQL interceptor. e.g. stash.performers. + */ + function maybeAddStashIDIcon(card, stashData) { + if (!SETTINGS.stashIDIcon) return; + + // Verify this function was not run twice on the same card + const existingIcon = card.querySelector(".stash-id-icon"); + if (existingIcon) return; + + const link = card.querySelector(".thumbnail-section > a"); + if (!link) return; + + const id = new URL(link.href).pathname.split("/").pop(); + const data = stashData[id]; + + if (!data) return; + + // Check if performer has stash IDs (stash_ids or stashIDs) + const stashIDs = data.stash_ids || data.stashIDs || []; + if (!Array.isArray(stashIDs) || stashIDs.length === 0) return; + + // Box icon SVG + const boxIconSVG = ``; + + const el = createElementFromHTML( + `${boxIconSVG}` + ); + link.parentElement.appendChild(el); + } })(); diff --git a/plugins/cjCardTweaks/cjCardTweaks.yml b/plugins/cjCardTweaks/cjCardTweaks.yml index f24ff003..3fd2ad3f 100644 --- a/plugins/cjCardTweaks/cjCardTweaks.yml +++ b/plugins/cjCardTweaks/cjCardTweaks.yml @@ -24,3 +24,7 @@ settings: displayName: Performer profile cards description: "Tweaks performer cards to use a traditional profile design." type: BOOLEAN + stashIDIcon: + displayName: Stash ID icon + description: "Adds a Stash ID icon to the performer cards." + type: BOOLEAN \ No newline at end of file From 3e892904107cb42921ab45007c688462498f835f Mon Sep 17 00:00:00 2001 From: KennyG Date: Wed, 18 Feb 2026 10:16:33 -0500 Subject: [PATCH 2/9] Trigger run --- plugins/cjCardTweaks/cjCardTweaks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/cjCardTweaks/cjCardTweaks.js b/plugins/cjCardTweaks/cjCardTweaks.js index 0a1c9e1b..948e4d5a 100644 --- a/plugins/cjCardTweaks/cjCardTweaks.js +++ b/plugins/cjCardTweaks/cjCardTweaks.js @@ -98,7 +98,7 @@ } /** - * Handles gallery cards to specific paths in Stash. + * Handles gallery cards to specific paths in Stash. * * The supported paths are: * - /galleries From f58585b7dc0b03b6c6f5b155770e27caeb8ad490 Mon Sep 17 00:00:00 2001 From: KennyG Date: Wed, 18 Feb 2026 10:26:33 -0500 Subject: [PATCH 3/9] bump --- plugins/cjCardTweaks/cjCardTweaks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/cjCardTweaks/cjCardTweaks.js b/plugins/cjCardTweaks/cjCardTweaks.js index 948e4d5a..385d2062 100644 --- a/plugins/cjCardTweaks/cjCardTweaks.js +++ b/plugins/cjCardTweaks/cjCardTweaks.js @@ -99,7 +99,7 @@ /** * Handles gallery cards to specific paths in Stash. - * + * * The supported paths are: * - /galleries * - /performers/{id}/galleries From fc0e3f9d6a107ff902a2765919857053b2695eda Mon Sep 17 00:00:00 2001 From: KennyG Date: Wed, 18 Feb 2026 10:28:03 -0500 Subject: [PATCH 4/9] b --- plugins/cjCardTweaks/cjCardTweaks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/cjCardTweaks/cjCardTweaks.js b/plugins/cjCardTweaks/cjCardTweaks.js index 385d2062..948e4d5a 100644 --- a/plugins/cjCardTweaks/cjCardTweaks.js +++ b/plugins/cjCardTweaks/cjCardTweaks.js @@ -99,7 +99,7 @@ /** * Handles gallery cards to specific paths in Stash. - * + * * The supported paths are: * - /galleries * - /performers/{id}/galleries From c12b8602b6b42205bf41b21c16679e044859a1c0 Mon Sep 17 00:00:00 2001 From: KennyG Date: Wed, 18 Feb 2026 10:41:32 -0500 Subject: [PATCH 5/9] Enhance Stash ID icon functionality for performer cards - Updated `maybeAddStashIDIcon` to fetch performer data via GraphQL for stash IDs. - Added error handling for fetching stash ID icons. - Introduced a new asynchronous function `fetchPerformerStashIDs` to retrieve stash IDs. --- plugins/cjCardTweaks/cjCardTweaks.js | 61 ++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/plugins/cjCardTweaks/cjCardTweaks.js b/plugins/cjCardTweaks/cjCardTweaks.js index 948e4d5a..977bf377 100644 --- a/plugins/cjCardTweaks/cjCardTweaks.js +++ b/plugins/cjCardTweaks/cjCardTweaks.js @@ -213,7 +213,9 @@ maybeAddFileCount(card, stashData, isContentCard); maybeAddDimensionToBanner(card); if (cardClass === "performer-card") { - maybeAddStashIDIcon(card, stashData); + maybeAddStashIDIcon(card, stashData).catch((err) => { + console.warn("[cjCardTweaks] Error adding stash ID icon", err); + }); } }); } @@ -278,13 +280,54 @@ oldBanner.remove(); } + /** + * Fetch performer data via GraphQL to get stash_ids + * + * @param {number} performerId - Performer ID (integer from local database) + * @returns {Promise} Performer data with stash_ids, or null if not found + */ + async function fetchPerformerStashIDs(performerId) { + const query = ` + query FindPerformer($id: ID!) { + findPerformer(id: $id) { + id + stash_ids { + endpoint + stash_id + updated_at + } + } + } + `; + + try { + const response = await fetch("/graphql", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + query, + variables: { id: performerId }, + }), + }); + + const result = await response.json(); + if (result.data && result.data.findPerformer) { + return result.data.findPerformer; + } + return null; + } catch (err) { + console.warn("[cjCardTweaks] Failed to fetch performer stash_ids", err); + return null; + } + } + /** * Add Stash ID icon to performer cards that have stash IDs * * @param {Element} card - Card element from cards list. * @param {Object} stashData - Data fetched from the GraphQL interceptor. e.g. stash.performers. */ - function maybeAddStashIDIcon(card, stashData) { + async function maybeAddStashIDIcon(card, stashData) { if (!SETTINGS.stashIDIcon) return; // Verify this function was not run twice on the same card @@ -294,13 +337,17 @@ const link = card.querySelector(".thumbnail-section > a"); if (!link) return; - const id = new URL(link.href).pathname.split("/").pop(); - const data = stashData[id]; + // Extract performer ID from URL (e.g., /performers/123 -> "123") + const performerIdStr = new URL(link.href).pathname.split("/").pop(); + const performerId = parseInt(performerIdStr, 10); + if (!performerId || isNaN(performerId)) return; - if (!data) return; + // Fetch performer data via GraphQL to get stash_ids + const performer = await fetchPerformerStashIDs(performerId); + if (!performer) return; - // Check if performer has stash IDs (stash_ids or stashIDs) - const stashIDs = data.stash_ids || data.stashIDs || []; + // Check if performer has stash IDs + const stashIDs = performer.stash_ids || []; if (!Array.isArray(stashIDs) || stashIDs.length === 0) return; // Box icon SVG From 2aad260cef31fd2f01fcab8d0870b7701f638273 Mon Sep 17 00:00:00 2001 From: KennyG Date: Wed, 18 Feb 2026 10:51:27 -0500 Subject: [PATCH 6/9] Bump --- plugins/cjCardTweaks/cjCardTweaks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/cjCardTweaks/cjCardTweaks.yml b/plugins/cjCardTweaks/cjCardTweaks.yml index 3fd2ad3f..4063ab1b 100644 --- a/plugins/cjCardTweaks/cjCardTweaks.yml +++ b/plugins/cjCardTweaks/cjCardTweaks.yml @@ -25,6 +25,6 @@ settings: description: "Tweaks performer cards to use a traditional profile design." type: BOOLEAN stashIDIcon: - displayName: Stash ID icon + displayName: Stash ID icon_up description: "Adds a Stash ID icon to the performer cards." type: BOOLEAN \ No newline at end of file From 846732cf4f9b31c10271f84bc9897f288f8f5d48 Mon Sep 17 00:00:00 2001 From: KennyG Date: Wed, 18 Feb 2026 11:57:47 -0500 Subject: [PATCH 7/9] Creates Stash ID icon functionality for performer cards - Updated `maybeAddStashIDIcon` to improve the logic for adding Stash ID icons. - Introduced a MutationObserver to dynamically handle card-popovers and fetch stash IDs. - Enhanced error handling and updated CSS styles for better display of Stash ID counts. - Adjusted settings description for clarity. --- plugins/cjCardTweaks/cjCardTweaks.js | 171 +++++++++++++++++--------- plugins/cjCardTweaks/cjCardTweaks.yml | 2 +- 2 files changed, 116 insertions(+), 57 deletions(-) diff --git a/plugins/cjCardTweaks/cjCardTweaks.js b/plugins/cjCardTweaks/cjCardTweaks.js index 977bf377..2faa84cc 100644 --- a/plugins/cjCardTweaks/cjCardTweaks.js +++ b/plugins/cjCardTweaks/cjCardTweaks.js @@ -44,7 +44,7 @@ const RATING_BANNER_3D_STYLE = ".grid-card{overflow:unset}.detail-group .rating-banner-3d,.rating-banner{display:none}.grid-card:hover .rating-banner-3d{opacity:0;transition:opacity .5s}.rating-banner-3d{height:110px;left:-6px;overflow:hidden;position:absolute;top:-6px;width:110px}.rating-banner-3d span{box-shadow:0 5px 4px rgb(0 0 0 / 50%);position:absolute;display:block;width:170px;padding:10px 5px 10px 0;background-color:#ff6a07;color:#fff;font:700 1rem/1 Lato,sans-serif;text-shadow:0 1px 1px rgba(0,0,0,.2);text-transform:uppercase;text-align:center;letter-spacing:1px;right:-20px;top:24px;transform:rotate(-45deg)}.rating-banner-3d::before{top:0;right:0;position:absolute;z-index:-1;content:'';display:block;border:5px solid #a34405;border-top-color:transparent;border-left-color:transparent}.rating-banner-3d::after{bottom:0;left:0;position:absolute;z-index:-1;content:'';display:block;border:5px solid #963e04}"; const STASH_ID_ICON_STYLE = - ".stash-id-icon{position:absolute;top:0.3rem;left:0.5rem;z-index:10;width:1.2rem;height:1.2rem;background-color:#007bff;border:2px solid #fff;border-radius:3px;box-shadow:0 2px 4px rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center}.stash-id-icon svg{width:0.8rem;height:0.8rem;fill:#fff}"; + ".stash-id-count{display:inline-flex;align-items:center;flex-direction:row}.stash-id-count-number{display:inline-block;margin-right:0.25rem}.stash-id-icon{display:inline-flex;align-items:center}.stash-id-icon svg{width:0.875rem;height:0.875rem;fill:currentColor;color:#fff}"; /** * Element to inject custom CSS styles. @@ -213,9 +213,34 @@ maybeAddFileCount(card, stashData, isContentCard); maybeAddDimensionToBanner(card); if (cardClass === "performer-card") { - maybeAddStashIDIcon(card, stashData).catch((err) => { - console.warn("[cjCardTweaks] Error adding stash ID icon", err); - }); + maybeAddStashIDIcon(card, stashData); + + // Also set up a MutationObserver to watch for card-popovers being added + if (SETTINGS.stashIDIcon && !card.querySelector(".stash-id-count")) { + const observer = new MutationObserver((mutations) => { + const cardPopovers = card.querySelector(".card-popovers.btn-group") || + card.querySelector(".card-popovers") || + card.querySelector('[role="group"].btn-group'); + if (cardPopovers && !cardPopovers.querySelector(".stash-id-count")) { + const link = card.querySelector(".thumbnail-section > a"); + if (link) { + const id = new URL(link.href).pathname.split("/").pop(); + const idNum = parseInt(id, 10); + // Query GraphQL for stash IDs + queryStashIDs(card, id, idNum); + observer.disconnect(); + } + } + }); + + observer.observe(card, { + childList: true, + subtree: true + }); + + // Disconnect after 5 seconds to avoid memory leaks + setTimeout(() => observer.disconnect(), 5000); + } } }); } @@ -281,12 +306,35 @@ } /** - * Fetch performer data via GraphQL to get stash_ids + * Add Stash ID count and icon to performer cards in the card-popovers btn-group * - * @param {number} performerId - Performer ID (integer from local database) - * @returns {Promise} Performer data with stash_ids, or null if not found + * @param {Element} card - Card element from cards list. + * @param {Object} stashData - Data fetched from the GraphQL interceptor. e.g. stash.performers. + */ + function maybeAddStashIDIcon(card, stashData) { + if (!SETTINGS.stashIDIcon) return; + + // Verify this function was not run twice on the same card + const existingCount = card.querySelector(".stash-id-count"); + if (existingCount) return; + + const link = card.querySelector(".thumbnail-section > a"); + if (!link) return; + + const id = new URL(link.href).pathname.split("/").pop(); + const idNum = parseInt(id, 10); + + // Query GraphQL for stash IDs + queryStashIDs(card, id, idNum); + } + + /** + * Query GraphQL for performer stash IDs + * @param {Element} card - Card element + * @param {string} id - Performer ID as string + * @param {number} idNum - Performer ID as number */ - async function fetchPerformerStashIDs(performerId) { + async function queryStashIDs(card, id, idNum) { const query = ` query FindPerformer($id: ID!) { findPerformer(id: $id) { @@ -294,68 +342,79 @@ stash_ids { endpoint stash_id - updated_at } } } `; - + + const variables = { + id: idNum + }; + try { - const response = await fetch("/graphql", { - method: "POST", - headers: { "Content-Type": "application/json" }, + const response = await fetch('/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, body: JSON.stringify({ - query, - variables: { id: performerId }, - }), + query: query, + variables: variables + }) }); - + const result = await response.json(); - if (result.data && result.data.findPerformer) { - return result.data.findPerformer; + + if (result.errors) return; + + const performer = result.data?.findPerformer; + if (!performer) return; + + const stashIDs = performer.stash_ids || []; + const stashIDCount = Array.isArray(stashIDs) ? stashIDs.length : 0; + + // Find card-popovers and add button + const cardPopovers = card.querySelector(".card-popovers.btn-group") || + card.querySelector(".card-popovers") || + card.querySelector('[role="group"].btn-group'); + + if (cardPopovers && !cardPopovers.querySelector(".stash-id-count")) { + addStashIDButton(cardPopovers, stashIDCount); + } + } catch (error) { + // On error, show 0 count + const cardPopovers = card.querySelector(".card-popovers.btn-group") || + card.querySelector(".card-popovers") || + card.querySelector('[role="group"].btn-group'); + + if (cardPopovers && !cardPopovers.querySelector(".stash-id-count")) { + addStashIDButton(cardPopovers, 0); } - return null; - } catch (err) { - console.warn("[cjCardTweaks] Failed to fetch performer stash_ids", err); - return null; } } /** - * Add Stash ID icon to performer cards that have stash IDs - * - * @param {Element} card - Card element from cards list. - * @param {Object} stashData - Data fetched from the GraphQL interceptor. e.g. stash.performers. + * Helper function to add the stash ID button to the card-popovers */ - async function maybeAddStashIDIcon(card, stashData) { - if (!SETTINGS.stashIDIcon) return; - - // Verify this function was not run twice on the same card - const existingIcon = card.querySelector(".stash-id-icon"); - if (existingIcon) return; - - const link = card.querySelector(".thumbnail-section > a"); - if (!link) return; - - // Extract performer ID from URL (e.g., /performers/123 -> "123") - const performerIdStr = new URL(link.href).pathname.split("/").pop(); - const performerId = parseInt(performerIdStr, 10); - if (!performerId || isNaN(performerId)) return; - - // Fetch performer data via GraphQL to get stash_ids - const performer = await fetchPerformerStashIDs(performerId); - if (!performer) return; - - // Check if performer has stash IDs - const stashIDs = performer.stash_ids || []; - if (!Array.isArray(stashIDs) || stashIDs.length === 0) return; - - // Box icon SVG - const boxIconSVG = ``; - - const el = createElementFromHTML( - `${boxIconSVG}` + function addStashIDButton(cardPopovers, stashIDCount) { + // Check if already added + if (cardPopovers.querySelector(".stash-id-count")) return; + + // Box-open icon SVG (StashApp logo style - open box) + const boxIconSVG = ``; + + // Create a wrapper div similar to the tag-count structure + const wrapper = document.createElement("div"); + + // Create button with count FIRST, then icon (as requested) + const button = createElementFromHTML( + `` ); - link.parentElement.appendChild(el); + + wrapper.appendChild(button); + cardPopovers.appendChild(wrapper); } })(); diff --git a/plugins/cjCardTweaks/cjCardTweaks.yml b/plugins/cjCardTweaks/cjCardTweaks.yml index 4063ab1b..3fd2ad3f 100644 --- a/plugins/cjCardTweaks/cjCardTweaks.yml +++ b/plugins/cjCardTweaks/cjCardTweaks.yml @@ -25,6 +25,6 @@ settings: description: "Tweaks performer cards to use a traditional profile design." type: BOOLEAN stashIDIcon: - displayName: Stash ID icon_up + displayName: Stash ID icon description: "Adds a Stash ID icon to the performer cards." type: BOOLEAN \ No newline at end of file From 2567444c674a6a6d0d3567f5409eb5cc9790ac5e Mon Sep 17 00:00:00 2001 From: KennyG Date: Wed, 18 Feb 2026 12:01:40 -0500 Subject: [PATCH 8/9] Refine Stash ID button display logic in performer cards - Updated `maybeAddStashIDIcon` to only show the Stash ID button if the count is greater than 0. - Modified error handling to silently fail without displaying a count when an error occurs. --- plugins/cjCardTweaks/cjCardTweaks.js | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/plugins/cjCardTweaks/cjCardTweaks.js b/plugins/cjCardTweaks/cjCardTweaks.js index 2faa84cc..beae48f6 100644 --- a/plugins/cjCardTweaks/cjCardTweaks.js +++ b/plugins/cjCardTweaks/cjCardTweaks.js @@ -373,23 +373,19 @@ const stashIDs = performer.stash_ids || []; const stashIDCount = Array.isArray(stashIDs) ? stashIDs.length : 0; - // Find card-popovers and add button - const cardPopovers = card.querySelector(".card-popovers.btn-group") || - card.querySelector(".card-popovers") || - card.querySelector('[role="group"].btn-group'); - - if (cardPopovers && !cardPopovers.querySelector(".stash-id-count")) { - addStashIDButton(cardPopovers, stashIDCount); + // Only show if count is greater than 0 + if (stashIDCount > 0) { + // Find card-popovers and add button + const cardPopovers = card.querySelector(".card-popovers.btn-group") || + card.querySelector(".card-popovers") || + card.querySelector('[role="group"].btn-group'); + + if (cardPopovers && !cardPopovers.querySelector(".stash-id-count")) { + addStashIDButton(cardPopovers, stashIDCount); + } } } catch (error) { - // On error, show 0 count - const cardPopovers = card.querySelector(".card-popovers.btn-group") || - card.querySelector(".card-popovers") || - card.querySelector('[role="group"].btn-group'); - - if (cardPopovers && !cardPopovers.querySelector(".stash-id-count")) { - addStashIDButton(cardPopovers, 0); - } + // On error, don't show anything (silent fail) } } From 56c6eb7f72a96e50a59bdb16b382b399c1fa48fb Mon Sep 17 00:00:00 2001 From: KennyG Date: Wed, 18 Feb 2026 12:46:04 -0500 Subject: [PATCH 9/9] Add Stash ID icon description to README for performer cards - Updated README.md to include details about the new Stash ID icon feature. - Described the icon's appearance, tooltip functionality, and its purpose in identifying performers linked to external Stash databases. --- plugins/cjCardTweaks/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/cjCardTweaks/README.md b/plugins/cjCardTweaks/README.md index 8de12288..f68c5235 100644 --- a/plugins/cjCardTweaks/README.md +++ b/plugins/cjCardTweaks/README.md @@ -20,3 +20,8 @@ Adds an additional dimension to the rating banners. ![unnamed_2](https://github.com/user-attachments/assets/f505417d-ed0c-40c4-9c78-647081a41307) Modify the performer cards to use a traditional profile design + +### Stash ID icon +![unnamed_3](https://github.com/user-attachments/assets/181fe3cd-b3e5-437d-8ded-7e48f2c0e446) + +Adds a box icon to performer cards that have one or more Stash IDs (GUIDs) attached. The icon appears in the top-left corner of the performer card thumbnail and displays a tooltip showing the count of Stash IDs when hovered. This helps quickly identify performers that are linked to external Stash databases.