pearsonkyle commited on
Commit
4e233fb
·
verified ·
1 Parent(s): 26a8d24

Add a button to search for similar cards to each card face when you mouse over the faces. Append cards to the list use the /search endpoint and then remove duplicates

Browse files
Files changed (1) hide show
  1. index.html +105 -21
index.html CHANGED
@@ -1002,18 +1002,20 @@
1002
  <div class="card-3d-container mb-4">
1003
  <div class="card-3d" id="card-3d">
1004
  <div class="card-face">
1005
- <div id="card-image" class="w-full h-full bg-gray-900 flex items-center justify-center">
1006
  <div class="text-center glass-card p-8">
1007
  <i class="fas fa-cube text-5xl text-white/30 mb-4"></i>
1008
  <p class="text-white/60">Initializing...</p>
1009
  </div>
 
 
 
1010
  </div>
1011
  <div id="face-indicator" class="absolute top-4 right-4 price-tag hidden">Face 1/2</div>
1012
  </div>
1013
  </div>
1014
  </div>
1015
-
1016
- <!-- Controls -->
1017
  <div class="flex gap-2 mb-4">
1018
  <button id="random-btn" class="glass-button">
1019
  <i class="fas fa-shuffle mr-2"></i>Random
@@ -1613,7 +1615,6 @@
1613
  // Update gallery header back to "Similar Cards"
1614
  $('gallery-header').textContent = 'Similar Cards';
1615
  }
1616
-
1617
  function displayCardFace(card, faceIndex) {
1618
  const hasFaces = card.card_faces?.length > 1;
1619
  const face = hasFaces ? card.card_faces[faceIndex] : card;
@@ -1621,10 +1622,21 @@
1621
 
1622
  // Update image
1623
  if (imageUris?.large) {
1624
- $('card-image').innerHTML = `<img src="${imageUris.large}" alt="${face.name}" class="w-full h-full object-contain">`;
 
 
 
 
 
 
 
 
 
 
 
 
1625
  }
1626
-
1627
- // Update face navigation
1628
  if (hasFaces) {
1629
  $('face-nav').classList.remove('hidden');
1630
  $('face-indicator').classList.remove('hidden');
@@ -1856,13 +1868,19 @@
1856
  // Final cleaning
1857
  return cardStr.replace(/, ,/g, ',');
1858
  }
1859
-
1860
- async function fetchSimilarCards(card) {
1861
  try {
1862
- // Build the query string using the same logic as Python
1863
- const query = buildCardQueryString(card);
 
 
 
 
 
 
 
1864
  currentGalleryQuery = query;
1865
- console.log('Query string:', query); // Debug logging
1866
 
1867
  // Build URL with gallery color filters
1868
  let url = `https://api.deck.doctor/v1/mtg/search?q=${encodeURIComponent(query)}&topk=12&price_threshold=0`;
@@ -1878,25 +1896,91 @@
1878
  if (data?.length > 0) {
1879
  // Filter out the current card more robustly
1880
  const filteredResults = data.filter(([c]) => {
1881
- // Check multiple identifiers to ensure we exclude the current card
1882
  return c.id !== card.id &&
1883
  c.name !== card.name &&
1884
  (!c.scryfall_uri || c.scryfall_uri !== card.scryfall_uri);
1885
- }).slice(0, 11); // Take at most 11 cards after filtering
 
 
 
 
 
1886
 
1887
- displayGallery(filteredResults);
1888
- $('gallery-section').classList.remove('hidden');
 
 
1889
 
1890
- // Update header if filters are active
1891
- const colorFilterText = galleryColors.size > 0 ? ` (${Array.from(galleryColors).join('')})` : '';
1892
- $('gallery-header').textContent = `Similar Cards${colorFilterText}`;
 
 
 
 
 
 
 
 
 
 
 
1893
  }
1894
  } catch (error) {
1895
  console.error('Gallery error:', error);
1896
  }
1897
  }
1898
-
1899
- function displayGallery(cards) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1900
  $('gallery').innerHTML = cards.map(([card, score]) => `
1901
  <div class="gallery-item">
1902
  <img src="${card.image_uris?.normal || ''}" alt="${card.name}" class="w-full h-full object-cover">
 
1002
  <div class="card-3d-container mb-4">
1003
  <div class="card-3d" id="card-3d">
1004
  <div class="card-face">
1005
+ <div id="card-image" class="w-full h-full bg-gray-900 flex items-center justify-center relative">
1006
  <div class="text-center glass-card p-8">
1007
  <i class="fas fa-cube text-5xl text-white/30 mb-4"></i>
1008
  <p class="text-white/60">Initializing...</p>
1009
  </div>
1010
+ <button id="find-similar-btn" class="absolute bottom-4 left-1/2 transform -translate-x-1/2 glass-button hidden" onclick="findSimilarForCurrentFace()">
1011
+ <i class="fas fa-search mr-2"></i>Find Similar
1012
+ </button>
1013
  </div>
1014
  <div id="face-indicator" class="absolute top-4 right-4 price-tag hidden">Face 1/2</div>
1015
  </div>
1016
  </div>
1017
  </div>
1018
+ <!-- Controls -->
 
1019
  <div class="flex gap-2 mb-4">
1020
  <button id="random-btn" class="glass-button">
1021
  <i class="fas fa-shuffle mr-2"></i>Random
 
1615
  // Update gallery header back to "Similar Cards"
1616
  $('gallery-header').textContent = 'Similar Cards';
1617
  }
 
1618
  function displayCardFace(card, faceIndex) {
1619
  const hasFaces = card.card_faces?.length > 1;
1620
  const face = hasFaces ? card.card_faces[faceIndex] : card;
 
1622
 
1623
  // Update image
1624
  if (imageUris?.large) {
1625
+ $('card-image').innerHTML = `
1626
+ <img src="${imageUris.large}" alt="${face.name}" class="w-full h-full object-contain">
1627
+ <button id="find-similar-btn" class="absolute bottom-4 left-1/2 transform -translate-x-1/2 glass-button opacity-0 transition-opacity duration-200"
1628
+ onclick="findSimilarForCurrentFace()">
1629
+ <i class="fas fa-search mr-2"></i>Find Similar
1630
+ </button>
1631
+ `;
1632
+
1633
+ // Add hover effect to show the button
1634
+ const img = $('card-image').querySelector('img');
1635
+ const btn = $('card-image').querySelector('button');
1636
+ img.addEventListener('mouseenter', () => btn.classList.remove('opacity-0'));
1637
+ img.addEventListener('mouseleave', () => btn.classList.add('opacity-0'));
1638
  }
1639
+ // Update face navigation
 
1640
  if (hasFaces) {
1641
  $('face-nav').classList.remove('hidden');
1642
  $('face-indicator').classList.remove('hidden');
 
1868
  // Final cleaning
1869
  return cardStr.replace(/, ,/g, ',');
1870
  }
1871
+ async function fetchSimilarCards(card, faceIndex = null) {
 
1872
  try {
1873
+ let query;
1874
+ if (faceIndex !== null && card.card_faces?.[faceIndex]) {
1875
+ // Build query for specific face
1876
+ query = buildCardQueryString(card.card_faces[faceIndex]);
1877
+ } else {
1878
+ // Build query for whole card
1879
+ query = buildCardQueryString(card);
1880
+ }
1881
+
1882
  currentGalleryQuery = query;
1883
+ console.log('Query string:', query);
1884
 
1885
  // Build URL with gallery color filters
1886
  let url = `https://api.deck.doctor/v1/mtg/search?q=${encodeURIComponent(query)}&topk=12&price_threshold=0`;
 
1896
  if (data?.length > 0) {
1897
  // Filter out the current card more robustly
1898
  const filteredResults = data.filter(([c]) => {
 
1899
  return c.id !== card.id &&
1900
  c.name !== card.name &&
1901
  (!c.scryfall_uri || c.scryfall_uri !== card.scryfall_uri);
1902
+ });
1903
+
1904
+ // Get existing gallery cards
1905
+ const existingCards = Array.from($('gallery').querySelectorAll('.gallery-item'))
1906
+ .map(el => el.querySelector('h4')?.textContent)
1907
+ .filter(Boolean);
1908
 
1909
+ // Filter out duplicates
1910
+ const uniqueResults = filteredResults.filter(([c]) =>
1911
+ !existingCards.includes(c.name)
1912
+ ).slice(0, 11); // Limit to 11 new cards
1913
 
1914
+ if (uniqueResults.length > 0) {
1915
+ // Append new cards to gallery
1916
+ const currentGallery = $('gallery').innerHTML;
1917
+ const newCards = uniqueResults.map(([card, score]) => createGalleryCard(card, score)).join('');
1918
+ $('gallery').innerHTML = currentGallery + newCards;
1919
+
1920
+ if ($('gallery-section').classList.contains('hidden')) {
1921
+ $('gallery-section').classList.remove('hidden');
1922
+ }
1923
+
1924
+ // Update header if filters are active
1925
+ const colorFilterText = galleryColors.size > 0 ? ` (${Array.from(galleryColors).join('')})` : '';
1926
+ $('gallery-header').textContent = `Similar Cards${colorFilterText}`;
1927
+ }
1928
  }
1929
  } catch (error) {
1930
  console.error('Gallery error:', error);
1931
  }
1932
  }
1933
+
1934
+ function findSimilarForCurrentFace() {
1935
+ if (!currentCard) return;
1936
+ setLoading(true);
1937
+
1938
+ // If card has multiple faces, search for current face
1939
+ if (currentCard.card_faces?.length > 1) {
1940
+ fetchSimilarCards(currentCard, currentFace);
1941
+ } else {
1942
+ fetchSimilarCards(currentCard);
1943
+ }
1944
+
1945
+ setLoading(false);
1946
+ }
1947
+
1948
+ function createGalleryCard(card, score) {
1949
+ return `
1950
+ <div class="gallery-item">
1951
+ <img src="${card.image_uris?.normal || ''}" alt="${card.name}" class="w-full h-full object-cover">
1952
+ ${card.prices?.usd ? `<div class="absolute top-2 left-2 price-tag text-xs">${parseFloat(card.prices.usd).toFixed(2)}</div>` : ''}
1953
+ <div class="gallery-overlay">
1954
+ <div class="gallery-overlay-content">
1955
+ <h4 class="font-semibold text-white line-clamp-2 mb-2">${card.name}</h4>
1956
+ ${card.mana_cost ? `<div class="gallery-mana-cost mb-2">${formatManaCost(card.mana_cost)}</div>` : ''}
1957
+ <p class="text-xs text-white/60 line-clamp-2 mb-2">${card.type_line}</p>
1958
+ ${card.oracle_text ? `<p class="text-xs text-white/50 line-clamp-3 mb-3">${card.oracle_text}</p>` : ''}
1959
+ <p class="text-xs text-white/40 mb-3">${Math.round(score * 100)}% similar</p>
1960
+
1961
+ <div class="gallery-actions">
1962
+ <button onclick="loadCard('${card.id}')" class="gallery-btn gallery-btn-view">
1963
+ <i class="fas fa-eye"></i>
1964
+ View Card
1965
+ </button>
1966
+ ${card.purchase_uris?.tcgplayer ? `
1967
+ <a href="${card.purchase_uris.tcgplayer}" target="_blank" class="gallery-btn">
1968
+ <i class="fas fa-shopping-cart"></i>
1969
+ TCG
1970
+ </a>
1971
+ ` : card.purchase_uris?.cardmarket ? `
1972
+ <a href="${card.purchase_uris.cardmarket}" target="_blank" class="gallery-btn">
1973
+ <i class="fas fa-shopping-cart"></i>
1974
+ CM
1975
+ </a>
1976
+ ` : ''}
1977
+ </div>
1978
+ </div>
1979
+ </div>
1980
+ </div>
1981
+ `;
1982
+ }
1983
+ function displayGallery(cards) {
1984
  $('gallery').innerHTML = cards.map(([card, score]) => `
1985
  <div class="gallery-item">
1986
  <img src="${card.image_uris?.normal || ''}" alt="${card.name}" class="w-full h-full object-cover">