| (function () { |
| "use strict"; |
|
|
| var CARD_PREFIX = "https://hfviewer.com/models/cards/"; |
| var API_PREFIX = "https://hfviewer.com/api/graph-preview.svg"; |
| var WEBSITE_ORIGIN = "https://hfviewer.com"; |
| var HUGGING_FACE_ORIGIN = "https://huggingface.co"; |
| var HOME_MESSAGE_TYPE = "hfviewer-space-home-requested"; |
| var HEADER_STATE_MESSAGE_TYPE = "hfviewer-space-header-state"; |
| var HEADER_ACTION_MESSAGE_TYPE = "hfviewer-space-header-action"; |
| var HEADER_ACTION_RESULT_MESSAGE_TYPE = "hfviewer-space-header-action-result"; |
| var COOKIE_CONSENT_MESSAGE_TYPE = "hfviewer-space-cookie-consent"; |
| var SPACE_BOOKMARK_STATE_URL = WEBSITE_ORIGIN + "/api/hf_space_bookmarks/state"; |
| var SPACE_BOOKMARK_TOGGLE_URL = WEBSITE_ORIGIN + "/api/hf_space_bookmarks/toggle"; |
| var SPACE_WATCHLIST_STATE_URL = WEBSITE_ORIGIN + "/api/hf_space_watchlist/state"; |
| var SPACE_WATCHLIST_TOGGLE_URL = WEBSITE_ORIGIN + "/api/hf_space_watchlist/toggle"; |
| var COMPACT_EDIT_README_REVEAL_DELAY_MS = 420; |
| var COOKIE_CONSENT_STORAGE_KEY = "hfviewer_space_cookie_consent_v1"; |
| var VIEWER_RETRY_DELAY_MS = 12000; |
| var VIEWER_FAILURE_DELAY_MS = 18000; |
| var viewerRetryTimer = 0; |
| var viewerRetryCount = 0; |
| var viewerLoadSequence = 0; |
| var viewerRetryMutationExpected = false; |
| var compactHeaderState = null; |
| var compactDiffMenu = null; |
| var compactTooltip = null; |
| var compactTooltipTarget = null; |
| var compactEmbedFeedbackTimer = 0; |
| var compactEditReadmeRevealTimer = 0; |
| var compactEmbedCopySequence = 0; |
| var compactEditReadmeRepoId = ""; |
| var compactBookmarkRepoId = ""; |
| var compactBookmarkPressed = null; |
| var compactBookmarkSyncSequence = 0; |
| var compactBookmarkSyncingRepoId = ""; |
| var nativeSpaceWatchedOwners = new Set(); |
| var nativeSpaceWatchStateSequence = 0; |
| var nativeSpaceWatchStateLoaded = false; |
| var nativeSpaceWatchOperations = new Map(); |
|
|
| function normalizeNativeSpaceWatchOwner(value) { |
| var owner = String(value || "").trim().replace(/^\/+|\/+$/g, ""); |
| return /^[A-Za-z0-9][A-Za-z0-9._-]{0,95}$/.test(owner) ? owner : ""; |
| } |
|
|
| function nativeSpaceWatchOwnerForControl(button) { |
| var explicit = normalizeNativeSpaceWatchOwner(button?.dataset?.watchOrg); |
| if (explicit) return explicit; |
| if (button?.dataset?.action === "compare-watch") { |
| return normalizeNativeSpaceWatchOwner(String(compactHeaderState?.repoId || "").split("/")[0]); |
| } |
| var cardOwner = normalizeNativeSpaceWatchOwner( |
| button?.closest?.(".hf-watchlist-card")?.querySelector?.(".hf-watchlist-card__name")?.textContent |
| ); |
| if (cardOwner) return cardOwner; |
| var panelTitle = button?.closest?.(".hfnx-notify")?.querySelector?.(".hfnx-notify__title")?.textContent || ""; |
| var panelMatch = String(panelTitle).match(/^Get notified on\s+(.+?)\s+releases$/i); |
| if (panelMatch) return normalizeNativeSpaceWatchOwner(panelMatch[1]); |
| var label = String( |
| button?.querySelector?.(".hfnx-watchcta__label,.pill__label,.sx-action-label")?.textContent || |
| button?.textContent || "" |
| ).trim(); |
| var match = label.match(/^(?:Watch|Watching|Adding|Removing)\s+(.+?)(?:\s+releases)?(?:\s*[\u2713…])?$/i); |
| return normalizeNativeSpaceWatchOwner(match?.[1]); |
| } |
|
|
| function setNativeSpaceWatchControl(button, owner, watched, pending) { |
| if (!button || !owner) return; |
| button.dataset.spaceWatchOrg = owner; |
| button.classList.toggle("is-watching", watched && !pending); |
| button.classList.toggle("is-busy", !!pending); |
| button.disabled = !!pending; |
| button.setAttribute("aria-pressed", watched ? "true" : "false"); |
| var verb = pending ? (watched ? "Adding " : "Removing ") : (watched ? "Watching " : "Watch "); |
| var suffix = pending ? "…" : watched ? " ✓" : ""; |
| var label = verb + owner + suffix; |
| var labelNode = button.querySelector(".hfnx-watchcta__label,.pill__label,.sx-action-label"); |
| if (button.matches(".hf-watchlist-card__watch")) { |
| button.textContent = pending |
| ? (watched ? "Adding…" : "Removing…") |
| : watched ? "Watching ✓" : "Watch releases"; |
| } else if (button.matches("#owner-gallery-watch-btn")) { |
| if (labelNode) labelNode.textContent = pending |
| ? (watched ? "Adding…" : "Removing…") |
| : watched ? "Watching " + owner + " ✓" : "Watch " + owner + " releases"; |
| } else if (button.matches('[data-action="compare-watch"]')) { |
| if (labelNode) labelNode.textContent = pending |
| ? (watched ? "Adding…" : "Removing…") |
| : watched ? "Watching " + owner + " ✓" : "Watch " + owner + " releases"; |
| } else if (labelNode) { |
| labelNode.textContent = label; |
| } |
| } |
|
|
| function syncNativeSpaceWatchControls(root) { |
| if (!window.hfviewerSpaceAuth?.isAuthenticated?.()) return; |
| var controls = []; |
| if (root?.matches?.(".hfnx-watchcta,.hf-watchlist-card__watch,#owner-gallery-watch-btn,[data-action='compare-watch']")) { |
| controls.push(root); |
| } |
| root?.querySelectorAll?.(".hfnx-watchcta,.hf-watchlist-card__watch,#owner-gallery-watch-btn,[data-action='compare-watch']") |
| .forEach(function (button) { controls.push(button); }); |
| controls.forEach(function (button) { |
| var owner = nativeSpaceWatchOwnerForControl(button); |
| if (!owner) return; |
| var pending = nativeSpaceWatchOperations.has(owner.toLowerCase()); |
| setNativeSpaceWatchControl( |
| button, |
| owner, |
| nativeSpaceWatchedOwners.has(owner.toLowerCase()), |
| pending |
| ); |
| if (!nativeSpaceWatchStateLoaded && !pending) { |
| button.disabled = true; |
| button.classList.add("is-busy"); |
| button.setAttribute("aria-busy", "true"); |
| } else { |
| button.removeAttribute("aria-busy"); |
| } |
| }); |
| } |
|
|
| async function loadNativeSpaceWatchlistState() { |
| if (!window.hfviewerSpaceAuth?.isAuthenticated?.()) return; |
| var sequence = ++nativeSpaceWatchStateSequence; |
| nativeSpaceWatchStateLoaded = false; |
| syncNativeSpaceWatchControls(document); |
| try { |
| var result = await window.hfviewerSpaceAuth.postJson(SPACE_WATCHLIST_STATE_URL, {}); |
| if (sequence !== nativeSpaceWatchStateSequence) return; |
| nativeSpaceWatchedOwners = new Set( |
| (Array.isArray(result?.orgs) ? result.orgs : []).map(function (owner) { |
| return String(owner || "").toLowerCase(); |
| }) |
| ); |
| nativeSpaceWatchStateLoaded = true; |
| syncNativeSpaceWatchControls(document); |
| } catch (error) { |
| console.warn("Failed to read the native Space release watchlist", error); |
| document.querySelectorAll( |
| ".hfnx-watchcta,.hf-watchlist-card__watch,#owner-gallery-watch-btn,[data-action='compare-watch']" |
| ).forEach(function (button) { |
| button.dataset.sxTooltip = "Could not load watchlist — reload to try again"; |
| }); |
| } |
| } |
|
|
| async function toggleNativeSpaceWatchControl(button, owner, watched) { |
| var key = owner.toLowerCase(); |
| if (!nativeSpaceWatchStateLoaded || nativeSpaceWatchOperations.has(key)) return; |
| |
| |
| nativeSpaceWatchStateSequence += 1; |
| nativeSpaceWatchOperations.set(key, true); |
| setNativeSpaceWatchControl(button, owner, watched, true); |
| try { |
| var result = await window.hfviewerSpaceAuth.postJson(SPACE_WATCHLIST_TOGGLE_URL, { |
| org: owner, |
| watched: watched, |
| placement: button.matches(".hfnx-watchcta") ? "landing_search" : |
| button.matches("#owner-gallery-watch-btn") ? "owner_page" : |
| button.matches('[data-action="compare-watch"]') ? "compare_header" : "watchlist_page", |
| }); |
| nativeSpaceWatchedOwners = new Set( |
| (Array.isArray(result?.orgs) ? result.orgs : []).map(function (entry) { |
| return String(entry || "").toLowerCase(); |
| }) |
| ); |
| window.hfviewerAnalyticsBridge?.capture?.( |
| watched ? "hfnx_watchlist_subscribed" : "hfnx_watchlist_unwatched", |
| { |
| product_surface: "hf_space", |
| source_page: "hf_space", |
| native_space_auth: true, |
| org: owner, |
| placement: button.matches(".hfnx-watchcta") ? "landing_search" : "space_native_watch", |
| } |
| ); |
| } catch (error) { |
| console.warn("Failed to update the native Space release watchlist", error); |
| button.dataset.sxTooltip = "Could not update watchlist — try again"; |
| } finally { |
| nativeSpaceWatchOperations.delete(key); |
| syncNativeSpaceWatchControls(document); |
| } |
| } |
|
|
| function handleNativeSpaceWatchClick(event) { |
| var button = event.target.closest?.( |
| ".hfnx-watchcta,.hf-watchlist-card__watch,#owner-gallery-watch-btn,[data-action='compare-watch']" |
| ); |
| if (!button) return; |
| var owner = nativeSpaceWatchOwnerForControl(button); |
| if (!owner) return; |
| if (!window.hfviewerSpaceAuth?.isAuthenticated?.()) { |
| event.preventDefault(); |
| event.stopImmediatePropagation(); |
| window.hfviewerSpaceAuth?.requestLogin?.(button, { |
| title: "Sign in to watch " + owner, |
| description: "Continue with Hugging Face to get notified about new " + owner + " releases.", |
| intent: "watch_" + owner.toLowerCase(), |
| }); |
| return; |
| } |
| if (!nativeSpaceWatchStateLoaded) { |
| event.preventDefault(); |
| event.stopImmediatePropagation(); |
| return; |
| } |
| var watched = nativeSpaceWatchedOwners.has(owner.toLowerCase()); |
| |
| |
| |
| |
| |
| |
| if (watched && button.matches(".hfnx-watchcta")) { |
| event.preventDefault(); |
| event.stopImmediatePropagation(); |
| setNativeSpaceWatchControl(button, owner, true, false); |
| return; |
| } |
| event.preventDefault(); |
| event.stopImmediatePropagation(); |
| void toggleNativeSpaceWatchControl(button, owner, !watched); |
| } |
|
|
| function normalizeSpaceCookieConsent(value) { |
| if (!value || typeof value !== "object") return null; |
| var outcome = String(value.outcome || ""); |
| if (!/^(accepted|declined|partial)$/.test(outcome)) return null; |
| return { |
| outcome: outcome, |
| analyticsAllowed: value.analyticsAllowed === true, |
| adsAllowed: value.adsAllowed === true, |
| }; |
| } |
|
|
| function readSpaceCookieConsent() { |
| try { |
| return normalizeSpaceCookieConsent( |
| JSON.parse(window.localStorage.getItem(COOKIE_CONSENT_STORAGE_KEY) || "null") |
| ); |
| } catch (error) { |
| return null; |
| } |
| } |
|
|
| function writeSpaceCookieConsent(value) { |
| var consent = normalizeSpaceCookieConsent(value); |
| if (!consent) return false; |
| try { |
| window.localStorage.setItem(COOKIE_CONSENT_STORAGE_KEY, JSON.stringify(consent)); |
| return true; |
| } catch (error) { |
| return false; |
| } |
| } |
|
|
| function applySpaceCookieConsentToViewerUrl(value) { |
| var url = value instanceof URL ? new URL(value.href) : new URL(String(value || ""), WEBSITE_ORIGIN); |
| var consent = readSpaceCookieConsent(); |
| if (!consent || url.origin !== WEBSITE_ORIGIN) return url; |
| if (url.searchParams.get("space_interaction") !== "embedded_viewer") return url; |
| url.searchParams.set("space_cookie_consent", consent.outcome); |
| url.searchParams.set("space_cookie_consent_analytics", consent.analyticsAllowed ? "1" : "0"); |
| url.searchParams.set("space_cookie_consent_ads", consent.adsAllowed ? "1" : "0"); |
| return url; |
| } |
|
|
| window.__hfSpaceApplyCookieConsentToViewerUrl = applySpaceCookieConsentToViewerUrl; |
|
|
| function applyStoredSpaceCookieConsentToFrame(markMutationExpected) { |
| var frame = document.getElementById("sx-viewer-frame"); |
| var current = frame?.getAttribute("src") || ""; |
| if (!frame || !current) return false; |
| try { |
| var next = applySpaceCookieConsentToViewerUrl(current).toString(); |
| if (next === new URL(current, window.location.href).toString()) return false; |
| if (markMutationExpected) viewerRetryMutationExpected = true; |
| frame.src = next; |
| return true; |
| } catch (error) { |
| return false; |
| } |
| } |
|
|
| function handleSpaceCookieConsent(event) { |
| var frame = document.getElementById("sx-viewer-frame"); |
| if ( |
| event.origin !== WEBSITE_ORIGIN || |
| !frame || |
| event.source !== frame.contentWindow || |
| event.data?.type !== COOKIE_CONSENT_MESSAGE_TYPE || |
| event.data?.version !== 1 || |
| event.data?.userInitiated !== true |
| ) return; |
| writeSpaceCookieConsent({ |
| outcome: event.data.outcome, |
| analyticsAllowed: event.data.analyticsAllowed, |
| adsAllowed: event.data.adsAllowed, |
| }); |
| } |
|
|
| var COMPACT_DISCUSS_ICON = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M21 12a8 8 0 0 1-8 8H7l-4 2 1.6-4A9 9 0 1 1 21 12z"/></svg>'; |
| var COMPACT_INSTALL_ICON = '<span class="sx-compact-logo-icon" aria-hidden="true"></span>'; |
| var COMPACT_BACK_ICON = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M19 12H5"/><path d="m12 19-7-7 7-7"/></svg>'; |
| var COMPACT_WATCH_ICON = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9a6 6 0 0 1 12 0c0 5 2 6 2 6H4s2-1 2-6"/><path d="M10.5 19a1.8 1.8 0 0 0 3 0"/></svg>'; |
| var COMPACT_EDIT_ICON = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M13.5 6.5l4 4M4 20l4.5-1 10-10a2.8 2.8 0 0 0-4-4l-10 10z"/><path d="M13 6l4 4"/></svg>'; |
|
|
| function installCompactHeaderStyles() { |
| if (document.getElementById("sx-compact-header-styles")) return; |
| var style = document.createElement("style"); |
| style.id = "sx-compact-header-styles"; |
| style.textContent = |
| "#sx-header{gap:8px;padding:6px 12px;overflow:visible}" + |
| "#sx-header .sx-brand{flex:0 0 auto;gap:7px}" + |
| "#sx-header .sx-brand span{display:none!important}" + |
| "#sx-header .sx-logo{width:22px;height:22px}" + |
| "#sx-back{height:34px;padding:0 10px;border-radius:8px;font-size:12px;white-space:nowrap}" + |
| ".sx-model-actions{display:none;align-items:center;gap:5px;margin-left:auto}" + |
| "body.sx-viewer-open .sx-model-actions{display:flex}" + |
| ".sx-compact-action{height:32px;min-width:32px;display:inline-flex;align-items:center;justify-content:center;gap:5px;padding:0 9px;border:1px solid #352b47;border-radius:8px;background:#1b1626;color:#ded7eb;font:600 11.5px/1 Inter,system-ui,sans-serif;text-decoration:none;cursor:pointer;white-space:nowrap}" + |
| ".sx-compact-action[hidden]{display:none!important}" + |
| ".sx-action-icon[hidden]{display:none!important}" + |
| ".sx-compact-action:hover,.sx-compact-action:focus-visible{border-color:#645378;background:#241d31;color:#fff;outline:none}" + |
| ".sx-compact-action svg{width:14px;height:14px;display:block;fill:none;stroke:currentColor;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}" + |
| ".sx-compact-logo-icon{position:relative;display:block;width:16px;height:16px;overflow:hidden;border:1px solid rgba(255,255,255,.26);border-radius:5px;background:linear-gradient(135deg,#9cff63,#88d7ff);box-shadow:inset 0 1px 0 rgba(255,255,255,.16)}" + |
| ".sx-compact-logo-icon::after{content:'';position:absolute;left:2px;top:2px;width:8px;height:8px;border-radius:50%;background:radial-gradient(circle,rgba(255,255,255,.38),rgba(255,255,255,0) 70%)}" + |
| ".sx-action-icon{display:inline-grid;place-items:center;flex:0 0 auto}" + |
| ".sx-compact-action.is-bookmarked{color:#9cff63;border-color:rgba(156,255,99,.42);background:rgba(156,255,99,.08)}" + |
| ".sx-compact-action.is-busy{pointer-events:none;opacity:.72}" + |
| ".sx-compact-action.is-copy-feedback .sx-action-label{display:inline}" + |
| ".sx-compact-action--spawned{animation:sx-compact-action-spawn 260ms cubic-bezier(.2,.85,.25,1.2) both}" + |
| "@keyframes sx-compact-action-spawn{from{opacity:0;transform:translateX(-9px) scale(.88)}to{opacity:1;transform:none}}" + |
| ".sx-compact-action[data-action='compare-watch']{border-color:rgba(156,255,99,.3);background:rgba(156,255,99,.07);color:#dfffd0}" + |
| ".sx-compact-action[data-action='compare-install']{border-color:rgba(136,215,255,.3);background:rgba(136,215,255,.08)}" + |
| ".sx-compact-action--primary{border-color:rgba(156,255,99,.35);background:rgba(156,255,99,.09);color:#dfffd0}" + |
| ".sx-diff-counts{display:inline-flex;flex-direction:column;align-items:flex-end;font-size:8px;line-height:.82;font-weight:800}" + |
| ".sx-diff-plus{color:#68dc87}.sx-diff-minus{color:#ff6d77}" + |
| "#sx-open{margin-left:0;height:32px;box-sizing:border-box;padding:0 10px;border-radius:8px;font-size:11.5px}" + |
| "#sx-auth{position:relative;display:none;align-items:center;flex:0 0 auto}" + |
| "body.sx-viewer-open #sx-auth:not([hidden]){display:flex}" + |
| "#sx-auth[hidden]{display:none!important}" + |
| "#sx-auth .space-native-auth-wrap{display:flex;position:relative;align-items:center}" + |
| "#sx-auth .hfnx-auth__chip{height:32px;box-sizing:border-box;display:inline-flex!important;align-items:center;gap:7px;padding:0 9px 0 4px;border:1px solid #352b47;border-radius:8px;background:#1b1626;color:#ded7eb;font:600 11.5px/1 Inter,system-ui,sans-serif;cursor:pointer;white-space:nowrap}" + |
| "#sx-auth .hfnx-auth__chip:hover,#sx-auth .hfnx-auth__chip:focus-visible{border-color:#645378;background:#241d31;color:#fff;outline:none}" + |
| "#sx-auth .hfnx-auth__avatar{width:24px;height:24px;flex:0 0 24px;border-radius:50%;object-fit:cover}" + |
| "#sx-auth .hfnx-auth__avatar--letter{display:inline-flex;align-items:center;justify-content:center;font-size:11px}" + |
| "body #sx-auth .space-native-auth-menu{position:absolute;display:none!important;top:calc(100% + 8px);right:0;z-index:5200;min-width:168px;padding:6px;border:1px solid rgba(255,255,255,.12);border-radius:12px;background:rgba(13,17,29,.98);box-shadow:0 18px 48px rgba(0,0,0,.42)}" + |
| "body #sx-auth .space-native-auth-menu.is-open{display:grid!important;gap:2px}" + |
| "#sx-auth .space-native-auth-menu .hfnx-auth__item{display:block;width:100%;box-sizing:border-box;padding:9px 14px;border:0;border-radius:10px;background:transparent;color:#c5d0e3;font:500 13.5px/1.2 Inter,system-ui,sans-serif;text-align:left;text-decoration:none;cursor:pointer}" + |
| "#sx-auth .space-native-auth-menu .hfnx-auth__item:hover,#sx-auth .space-native-auth-menu .hfnx-auth__item:focus-visible{background:rgba(255,255,255,.06);color:#f5f8ff;outline:none}" + |
| ".sx-compact-diff-menu{position:fixed;z-index:5000;top:47px;right:58px;min-width:220px;padding:6px;border:1px solid #3b304d;border-radius:10px;background:#171220;box-shadow:0 14px 34px rgba(0,0,0,.42)}" + |
| ".sx-compact-diff-menu[hidden]{display:none}" + |
| ".sx-compact-diff-option{width:100%;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 10px;border:0;border-radius:7px;background:transparent;color:#e9e3f2;font:600 12px/1.25 Inter,system-ui,sans-serif;text-align:left;cursor:pointer}" + |
| ".sx-compact-diff-option:hover{background:#241d31}" + |
| ".sx-compact-tooltip{position:fixed;z-index:5100;left:0;top:0;max-width:min(240px,calc(100vw - 20px));padding:7px 9px;border:1px solid rgba(174,163,194,.22);border-radius:8px;background:linear-gradient(180deg,rgba(35,28,48,.98),rgba(20,16,29,.98));color:#f1ebf8;font:600 11px/1.25 Inter,system-ui,sans-serif;letter-spacing:.01em;white-space:nowrap;box-shadow:0 10px 28px rgba(0,0,0,.42),inset 0 1px 0 rgba(255,255,255,.055);pointer-events:none;opacity:0;transform:translateY(-3px);transition:opacity 110ms ease,transform 110ms ease}" + |
| ".sx-compact-tooltip::before{content:'';position:absolute;top:-4px;left:var(--sx-tooltip-arrow,50%);width:7px;height:7px;border-left:1px solid rgba(174,163,194,.22);border-top:1px solid rgba(174,163,194,.22);background:#231c30;transform:translateX(-50%) rotate(45deg)}" + |
| ".sx-compact-tooltip.is-visible{opacity:1;transform:translateY(0)}" + |
| "@media(prefers-reduced-motion:reduce){.sx-compact-tooltip{transition:none}.sx-compact-action--spawned{animation:none}}" + |
| "@media(max-width:900px){.sx-action-label{display:none}.sx-compact-action{padding:0;min-width:32px}.sx-compact-action--primary .sx-action-label{display:inline}.sx-compact-action--primary{padding:0 7px}}" + |
| ".space-native-auth-login{display:inline-flex!important;align-items:center;gap:6px}" + |
| ".space-native-auth-login__hf-logo{display:block;width:17px;height:16px;object-fit:contain}" + |
| "@media(max-width:640px){#sx-header{padding:6px 8px;gap:5px}#sx-header .sx-brand strong{display:none}#sx-back{width:32px;min-width:32px;padding:0;font-size:0}#sx-back::before{content:'←';font-size:16px}.sx-model-actions{margin-left:auto}.sx-compact-action[data-action='report'],.sx-compact-action[data-action='hugging-face']{display:none!important}#sx-open{width:32px;min-width:32px;padding:0}.sx-open-label{display:none}#sx-auth .hfnx-auth__chip{max-width:122px}#sx-auth .hfnx-auth__chip>span:last-child{overflow:hidden;text-overflow:ellipsis}.sx-compact-diff-menu{right:8px;left:8px;min-width:0}}"; |
| document.head.appendChild(style); |
| } |
|
|
| function compactAction(action, label, icon, tooltipLabel, options) { |
| var isLink = options?.link === true; |
| var button = document.createElement(isLink ? "a" : "button"); |
| if (!isLink) button.type = "button"; |
| button.className = "sx-compact-action"; |
| button.dataset.action = action; |
| button.dataset.sxTooltip = tooltipLabel || label; |
| button.setAttribute("aria-label", label); |
| button.innerHTML = '<span class="sx-action-icon">' + icon + '</span><span class="sx-action-label"></span>'; |
| button.querySelector(".sx-action-label").textContent = label; |
| return button; |
| } |
|
|
| function hideCompactTooltip() { |
| compactTooltipTarget = null; |
| compactTooltip?.classList.remove("is-visible"); |
| } |
|
|
| function positionCompactTooltip(target) { |
| if (!compactTooltip || compactTooltipTarget !== target || !target?.isConnected) return; |
| var rect = target.getBoundingClientRect(); |
| var width = compactTooltip.offsetWidth || 120; |
| var margin = 10; |
| var left = Math.max(margin, Math.min(rect.left + rect.width / 2 - width / 2, window.innerWidth - width - margin)); |
| var top = rect.bottom + 8; |
| var arrow = Math.max(9, Math.min(width - 9, rect.left + rect.width / 2 - left)); |
| compactTooltip.style.left = Math.round(left) + "px"; |
| compactTooltip.style.top = Math.round(top) + "px"; |
| compactTooltip.style.setProperty("--sx-tooltip-arrow", Math.round(arrow) + "px"); |
| } |
|
|
| function showCompactTooltip(target) { |
| var label = String(target?.dataset?.sxTooltip || "").trim(); |
| if (!label) return; |
| if (!compactTooltip) { |
| compactTooltip = document.createElement("div"); |
| compactTooltip.className = "sx-compact-tooltip"; |
| compactTooltip.setAttribute("role", "tooltip"); |
| document.body.appendChild(compactTooltip); |
| } |
| compactTooltipTarget = target; |
| compactTooltip.textContent = label; |
| positionCompactTooltip(target); |
| compactTooltip.classList.add("is-visible"); |
| window.requestAnimationFrame(function () { positionCompactTooltip(target); }); |
| } |
|
|
| function installCompactHeaderTooltips(header, back, open) { |
| back.dataset.sxTooltip = "Back to all models"; |
| open.dataset.sxTooltip = "Open on full website"; |
| header.addEventListener("pointerover", function (event) { |
| var target = event.target.closest?.("[data-sx-tooltip]"); |
| if (!target || !header.contains(target) || target.contains(event.relatedTarget)) return; |
| showCompactTooltip(target); |
| }); |
| header.addEventListener("pointerout", function (event) { |
| var target = event.target.closest?.("[data-sx-tooltip]"); |
| if (!target || target.contains(event.relatedTarget)) return; |
| hideCompactTooltip(); |
| }); |
| header.addEventListener("focusin", function (event) { |
| var target = event.target.closest?.("[data-sx-tooltip]"); |
| if (target && header.contains(target)) showCompactTooltip(target); |
| }); |
| header.addEventListener("focusout", function (event) { |
| if (!event.target.closest?.("[data-sx-tooltip]")?.contains(event.relatedTarget)) hideCompactTooltip(); |
| }); |
| window.addEventListener("resize", hideCompactTooltip); |
| window.addEventListener("scroll", hideCompactTooltip, true); |
| } |
|
|
| function setCompactActionLabel(button, label) { |
| var labelElement = button?.querySelector(".sx-action-label"); |
| if (labelElement) labelElement.textContent = label; |
| } |
|
|
| function compactModelBookmarkTarget() { |
| var repoId = String(compactHeaderState?.repoId || "").trim(); |
| if (!repoId || repoId.split("/").length !== 2) return null; |
| return { |
| target_type: "model", |
| repo_id: repoId, |
| title: repoId, |
| url: "/" + repoId, |
| }; |
| } |
|
|
| function renderCompactBookmarkState(repoId, pressed) { |
| var button = document.querySelector('#sx-model-actions [data-action="bookmark"]'); |
| if (!button || repoId !== String(compactHeaderState?.repoId || "")) return; |
| compactBookmarkRepoId = repoId; |
| compactBookmarkPressed = !!pressed; |
| button.classList.toggle("is-bookmarked", compactBookmarkPressed); |
| button.setAttribute("aria-pressed", compactBookmarkPressed ? "true" : "false"); |
| button.setAttribute("aria-label", compactBookmarkPressed ? "Remove bookmark" : "Bookmark graph"); |
| button.dataset.sxTooltip = compactBookmarkPressed ? "Remove bookmark" : "Bookmark graph"; |
| } |
|
|
| async function syncCompactSpaceBookmark() { |
| var target = compactModelBookmarkTarget(); |
| if (!target || !window.hfviewerSpaceAuth?.isAuthenticated?.()) return; |
| if (compactBookmarkRepoId === target.repo_id || compactBookmarkSyncingRepoId === target.repo_id) return; |
| var sequence = ++compactBookmarkSyncSequence; |
| compactBookmarkSyncingRepoId = target.repo_id; |
| try { |
| var result = await window.hfviewerSpaceAuth.postJson(SPACE_BOOKMARK_STATE_URL, target); |
| if (sequence !== compactBookmarkSyncSequence) return; |
| renderCompactBookmarkState(target.repo_id, result?.bookmarked === true); |
| } catch (error) { |
| console.warn("Failed to read the native Space bookmark", error); |
| } finally { |
| if (compactBookmarkSyncingRepoId === target.repo_id) compactBookmarkSyncingRepoId = ""; |
| } |
| } |
|
|
| async function toggleCompactSpaceBookmark(button) { |
| var target = compactModelBookmarkTarget(); |
| if (!target || button.classList.contains("is-busy")) return; |
| var wasPressed = |
| compactBookmarkRepoId === target.repo_id |
| ? compactBookmarkPressed === true |
| : button.getAttribute("aria-pressed") === "true"; |
| button.classList.add("is-busy"); |
| try { |
| var result = await window.hfviewerSpaceAuth.postJson(SPACE_BOOKMARK_TOGGLE_URL, { |
| ...target, |
| action: wasPressed ? "remove" : "add", |
| }); |
| renderCompactBookmarkState(target.repo_id, result?.bookmarked === true); |
| window.hfviewerAnalyticsBridge?.capture?.("space_graph_bookmark_toggled", { |
| product_surface: "hf_space", |
| source_page: "hf_space", |
| surface: "space_compact_header", |
| model_repo_id: target.repo_id, |
| repo_id: target.repo_id, |
| bookmarked: result?.bookmarked === true, |
| }); |
| } catch (error) { |
| console.warn("Failed to update the native Space bookmark", error); |
| if (!window.hfviewerSpaceAuth?.isAuthenticated?.()) { |
| renderCompactBookmarkState(target.repo_id, false); |
| window.hfviewerSpaceAuth?.requestLogin?.(button, { |
| title: "Sign in to bookmark this graph", |
| description: "Continue with Hugging Face to save it to your hfviewer bookmarks.", |
| intent: "bookmark_graph", |
| }); |
| } |
| } finally { |
| button.classList.remove("is-busy"); |
| } |
| } |
|
|
| function resetCompactEmbedControls(repoId) { |
| window.clearTimeout(compactEmbedFeedbackTimer); |
| window.clearTimeout(compactEditReadmeRevealTimer); |
| compactEmbedFeedbackTimer = 0; |
| compactEditReadmeRevealTimer = 0; |
| var embed = document.querySelector('#sx-model-actions [data-action="embed"]'); |
| var edit = document.querySelector('#sx-model-actions [data-action="edit-readme"]'); |
| if (embed) { |
| embed.classList.remove("is-busy", "is-copy-feedback"); |
| setCompactActionLabel(embed, "Embed"); |
| } |
| if (edit && compactEditReadmeRepoId !== repoId) { |
| edit.hidden = true; |
| edit.classList.remove("sx-compact-action--spawned"); |
| edit.removeAttribute("href"); |
| compactEditReadmeRepoId = ""; |
| } |
| } |
|
|
| function revealCompactEditReadme(repoId) { |
| var edit = document.querySelector('#sx-model-actions [data-action="edit-readme"]'); |
| if (!edit || repoId.split("/").length !== 2) return; |
| var wasHidden = edit.hidden || compactEditReadmeRepoId !== repoId; |
| edit.href = HUGGING_FACE_ORIGIN + "/" + repoId.split("/").map(encodeURIComponent).join("/") + "/edit/main/README.md"; |
| edit.target = "_blank"; |
| edit.rel = "noopener noreferrer"; |
| edit.hidden = false; |
| compactEditReadmeRepoId = repoId; |
| if (wasHidden) { |
| edit.classList.remove("sx-compact-action--spawned"); |
| void edit.offsetWidth; |
| edit.classList.add("sx-compact-action--spawned"); |
| } |
| } |
|
|
| function copyCompactEmbedSnippet(value) { |
| var snippet = String(value || ""); |
| if (!snippet) return Promise.resolve(false); |
|
|
| |
| |
| |
| |
| var textarea = document.createElement("textarea"); |
| textarea.value = snippet; |
| textarea.setAttribute("readonly", ""); |
| textarea.style.position = "fixed"; |
| textarea.style.left = "-9999px"; |
| textarea.style.opacity = "0"; |
| document.body.appendChild(textarea); |
| textarea.focus(); |
| textarea.select(); |
| textarea.setSelectionRange(0, textarea.value.length); |
| var copied = false; |
| try { |
| copied = document.execCommand("copy"); |
| } catch (error) { |
| copied = false; |
| } |
| textarea.remove(); |
| if (copied) return Promise.resolve(true); |
|
|
| |
| |
| if (navigator.clipboard?.writeText) { |
| try { |
| return Promise.resolve(navigator.clipboard.writeText(snippet)).then( |
| function () { return true; }, |
| function () { return false; } |
| ); |
| } catch (error) { |
| return Promise.resolve(false); |
| } |
| } |
| return Promise.resolve(false); |
| } |
|
|
| function buildCompactModelCardSnippet(repoId) { |
| var normalized = String(repoId || "").trim(); |
| if (normalized.split("/").length !== 2) return ""; |
| var path = normalized.split("/").map(encodeURIComponent).join("/"); |
| var slug = normalized |
| .toLowerCase() |
| .replace(/[^a-z0-9]+/g, "-") |
| .replace(/^-+|-+$/g, "") || "model"; |
| var viewer = new URL("/" + path, WEBSITE_ORIGIN); |
| viewer.searchParams.set("utm_source", "huggingface"); |
| viewer.searchParams.set("utm_medium", "embedded_model_card"); |
| viewer.searchParams.set("utm_campaign", slug + "_card"); |
| viewer.searchParams.set("utm_content", "embedded_card_open_viewer"); |
| viewer.searchParams.set("from", "embedded-model-card"); |
| var card = new URL("/api/card.svg", WEBSITE_ORIGIN); |
| card.searchParams.set("source", normalized); |
| card.searchParams.set("granularity", "auto"); |
| var escapeAttribute = function (value) { |
| return String(value).replace(/&/g, "&").replace(/"/g, """); |
| }; |
| return [ |
| '<a href="' + escapeAttribute(viewer.href) + '" target="_blank" rel="noopener">', |
| " <img", |
| ' src="' + escapeAttribute(card.href) + '"', |
| ' alt="Architecture graph for ' + escapeAttribute(normalized) + '. Open in hfviewer"', |
| ' width="100%"', |
| " />", |
| "</a>", |
| ].join("\n"); |
| } |
|
|
| function buildCompactFrameUrl(href) { |
| var frame = document.getElementById("sx-viewer-frame"); |
| if (!frame || !href) return null; |
| var target; |
| var current; |
| try { |
| target = new URL(href, WEBSITE_ORIGIN); |
| current = new URL(frame.src || WEBSITE_ORIGIN, WEBSITE_ORIGIN); |
| } catch (error) { |
| return null; |
| } |
| [ |
| "space_interaction", |
| "space_cookie_consent", |
| "space_cookie_consent_analytics", |
| "space_cookie_consent_ads", |
| "tracking_actor", |
| "tracking_actor_label", |
| "space_journey_id", |
| "source_page", |
| ].forEach(function (key) { |
| var value = current.searchParams.get(key); |
| if (value) target.searchParams.set(key, value); |
| }); |
| target.searchParams.set("space_interaction", "embedded_viewer"); |
| return applySpaceCookieConsentToViewerUrl(target); |
| } |
|
|
| function openCompactFrame(href) { |
| var frame = document.getElementById("sx-viewer-frame"); |
| var target = buildCompactFrameUrl(href); |
| if (!frame || !target) return false; |
| frame.src = target.href; |
| return true; |
| } |
|
|
| function openCompactDiff(href) { |
| return openCompactFrame(href); |
| } |
|
|
| function openCompactModel(repoId) { |
| var normalized = String(repoId || "").replace(/^\/+|\/+$/g, ""); |
| return normalized ? openCompactFrame("/" + normalized) : false; |
| } |
|
|
| function finishCompactEmbedCopy(succeeded, repoId, copySequence) { |
| if ( |
| copySequence !== compactEmbedCopySequence || |
| repoId !== String(compactHeaderState?.repoId || "") |
| ) return; |
| var embed = document.querySelector('#sx-model-actions [data-action="embed"]'); |
| if (!embed) return; |
| embed.classList.remove("is-busy"); |
| setCompactActionLabel(embed, succeeded ? "Copied" : "Copy failed"); |
| window.clearTimeout(compactEditReadmeRevealTimer); |
| compactEditReadmeRevealTimer = 0; |
| if (succeeded && compactEditReadmeRepoId !== repoId) { |
| compactEditReadmeRevealTimer = window.setTimeout(function () { |
| compactEditReadmeRevealTimer = 0; |
| if ( |
| copySequence === compactEmbedCopySequence && |
| repoId === String(compactHeaderState?.repoId || "") |
| ) revealCompactEditReadme(repoId); |
| }, COMPACT_EDIT_README_REVEAL_DELAY_MS); |
| } |
| window.clearTimeout(compactEmbedFeedbackTimer); |
| compactEmbedFeedbackTimer = window.setTimeout(function () { |
| if (copySequence !== compactEmbedCopySequence) return; |
| compactEmbedFeedbackTimer = 0; |
| embed.classList.remove("is-copy-feedback"); |
| setCompactActionLabel(embed, "Embed"); |
| }, succeeded ? 2600 : 1800); |
| } |
|
|
| function handleCompactHeaderActionResult(event) { |
| var frame = document.getElementById("sx-viewer-frame"); |
| if ( |
| event.origin !== WEBSITE_ORIGIN || |
| !frame || |
| event.source !== frame.contentWindow || |
| event.data?.type !== HEADER_ACTION_RESULT_MESSAGE_TYPE || |
| event.data?.version !== 1 || |
| event.data?.action !== "embed" |
| ) return; |
| var repoId = String(compactHeaderState?.repoId || ""); |
| finishCompactEmbedCopy(event.data?.ok === true, repoId, compactEmbedCopySequence); |
| } |
|
|
| function installCompactViewerHeader() { |
| var header = document.getElementById("sx-header"); |
| var back = document.getElementById("sx-back"); |
| var open = document.getElementById("sx-open"); |
| if (!header || !back || !open || document.getElementById("sx-model-actions")) return; |
| installCompactHeaderStyles(); |
| header.querySelector(".sx-brand span")?.remove(); |
|
|
| var actions = document.createElement("div"); |
| actions.id = "sx-model-actions"; |
| actions.className = "sx-model-actions"; |
| var embedAction = compactAction("embed", "Embed", '<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="2"/><path d="M4 16H3a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v1"/></svg>', "Embed on model card"); |
| |
| |
| |
| |
| embedAction.disabled = true; |
| var editReadmeAction = compactAction("edit-readme", "Edit README.md", COMPACT_EDIT_ICON, "Go to edit page", { link: true }); |
| editReadmeAction.hidden = true; |
| actions.append( |
| compactAction("report", "Report", '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M21 15a4 4 0 0 1-4 4H8l-5 3V7a4 4 0 0 1 4-4h10a4 4 0 0 1 4 4z"/><path d="M12 7v5M12 15.5h.01"/></svg>', "Report graph issue"), |
| compactAction("hugging-face", "Hugging Face", '<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M8.5 10h.01M15.5 10h.01M8.5 14c2.1 2 4.9 2 7 0"/></svg>', "Go to HF model", { link: true }), |
| embedAction, |
| editReadmeAction, |
| compactAction("bookmark", "Bookmark", '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 3h12v18l-6-4-6 4z"/></svg>', "Bookmark graph"), |
| compactAction("primary", "Discuss", COMPACT_DISCUSS_ICON, "Discuss model", { link: true }) |
| ); |
| var compareBack = compactAction("compare-back", "Back to model", COMPACT_BACK_ICON, "Back to model"); |
| var compareWatch = compactAction("compare-watch", "Watch releases", COMPACT_WATCH_ICON, "Watch releases"); |
| var compareInstall = compactAction("compare-install", "Install extension", COMPACT_INSTALL_ICON, "Install Chrome extension", { link: true }); |
| compareBack.hidden = true; |
| compareWatch.hidden = true; |
| compareInstall.hidden = true; |
| actions.append(compareBack, compareWatch, compareInstall); |
| header.insertBefore(actions, open); |
| var compactAuth = document.createElement("div"); |
| compactAuth.id = "sx-auth"; |
| compactAuth.hidden = true; |
| open.insertAdjacentElement("afterend", compactAuth); |
| installCompactHeaderTooltips(header, back, open); |
| compactDiffMenu = document.createElement("div"); |
| compactDiffMenu.className = "sx-compact-diff-menu"; |
| compactDiffMenu.hidden = true; |
| document.body.appendChild(compactDiffMenu); |
|
|
| actions.addEventListener("click", function (event) { |
| var button = event.target.closest("[data-action]"); |
| if (!button) return; |
| var action = button.dataset.action; |
| if (action === "edit-readme" && button.hasAttribute("href")) { |
| try { |
| window.hfviewerAnalyticsBridge?.capture?.("space_model_card_readme_edit_opened", { |
| product_surface: "hf_space", |
| source_page: "hf_space", |
| surface: "space_compact_header", |
| placement: "after_embed_copy", |
| model_repo_id: compactHeaderState?.repoId || "", |
| repo_id: compactHeaderState?.repoId || "", |
| href: button.href, |
| }); |
| } catch (error) { } |
| return; |
| } |
| if (action === "embed") { |
| event.preventDefault(); |
| var repoId = String(compactHeaderState?.repoId || ""); |
| var copySequence = ++compactEmbedCopySequence; |
| window.clearTimeout(compactEmbedFeedbackTimer); |
| window.clearTimeout(compactEditReadmeRevealTimer); |
| compactEditReadmeRevealTimer = 0; |
| setCompactActionLabel(button, "Copying…"); |
| button.classList.add("is-busy", "is-copy-feedback"); |
| compactEmbedFeedbackTimer = window.setTimeout(function () { |
| compactEmbedFeedbackTimer = 0; |
| button.classList.remove("is-busy", "is-copy-feedback"); |
| setCompactActionLabel(button, "Embed"); |
| }, 6000); |
| var snippet = String( |
| compactHeaderState?.actions?.embed?.snippet || |
| buildCompactModelCardSnippet(compactHeaderState?.repoId) |
| ); |
| if (!snippet) { |
| finishCompactEmbedCopy(false, repoId, copySequence); |
| return; |
| } |
| void copyCompactEmbedSnippet(snippet).then(function (copied) { |
| finishCompactEmbedCopy(copied, repoId, copySequence); |
| try { |
| window.hfviewerAnalyticsBridge?.capture?.( |
| copied ? "space_model_card_embed_copied" : "space_model_card_embed_copy_failed", |
| { |
| product_surface: "hf_space", |
| source_page: "hf_space", |
| surface: "space_compact_header", |
| model_repo_id: compactHeaderState?.repoId || "", |
| repo_id: compactHeaderState?.repoId || "", |
| } |
| ); |
| } catch (error) { } |
| }); |
| return; |
| } |
| if (action === "bookmark") { |
| event.preventDefault(); |
| if (window.hfviewerSpaceAuth?.isAuthenticated?.()) { |
| void toggleCompactSpaceBookmark(button); |
| } else { |
| renderCompactBookmarkState(String(compactHeaderState?.repoId || ""), false); |
| window.hfviewerSpaceAuth?.requestLogin?.(button, { |
| title: "Sign in to bookmark this graph", |
| description: "Continue with Hugging Face to save it to your hfviewer bookmarks.", |
| intent: "bookmark_graph", |
| }); |
| } |
| return; |
| } |
| if (action === "compare-back") { |
| event.preventDefault(); |
| openCompactModel(compactHeaderState?.repoId); |
| return; |
| } |
| if (action === "primary" && compactHeaderState?.actions?.primary?.mode === "diff") { |
| var diffOptions = compactHeaderState.actions.primary.options || []; |
| event.preventDefault(); |
| if (diffOptions.length > 1) { |
| compactDiffMenu.hidden = !compactDiffMenu.hidden; |
| } else if (diffOptions.length === 1) { |
| openCompactDiff(diffOptions[0].href); |
| } |
| return; |
| } |
| if (action === "hugging-face" && button.hasAttribute("href")) { |
| var hfLinkProperties = { |
| product_surface: "hf_space", |
| source_page: "hf_space", |
| surface: "space_compact_header", |
| placement: "space_compact_header", |
| model_repo_id: compactHeaderState?.repoId || "", |
| repo_id: compactHeaderState?.repoId || "", |
| href: button.href, |
| }; |
| try { |
| window.hfviewerAnalyticsBridge?.capture?.("open_huggingface_link", hfLinkProperties); |
| window.hfviewerAnalyticsBridge?.capture?.("external_huggingface_opened", hfLinkProperties); |
| } catch (error) { } |
| return; |
| } |
| if ( |
| action === "compare-install" && |
| button.hasAttribute("href") |
| ) { |
| try { |
| window.hfviewerAnalyticsBridge?.capture?.("chrome_extension_install_clicked", { |
| product_surface: "hf_space", |
| source_page: "hf_space", |
| surface: "space_compact_header", |
| placement: "space_compact_compare_header", |
| destination: "chrome_web_store", |
| extension_id: "mmadlggmpkpiockpjfepaohcllbnakej", |
| href: button.href, |
| cta_label: "Install Chrome extension", |
| }); |
| } catch (error) { } |
| return; |
| } |
| if ( |
| action === "primary" && |
| compactHeaderState?.actions?.primary?.mode === "install" && |
| button.hasAttribute("href") |
| ) { |
| try { |
| window.hfviewerAnalyticsBridge?.capture?.("chrome_extension_install_clicked", { |
| product_surface: "hf_space", |
| source_page: "hf_space", |
| surface: "space_compact_header", |
| placement: "space_compact_header", |
| destination: "chrome_web_store", |
| extension_id: "mmadlggmpkpiockpjfepaohcllbnakej", |
| model_repo_id: compactHeaderState?.repoId || "", |
| href: button.href, |
| cta_label: "Install Chrome extension", |
| }); |
| } catch (error) { } |
| return; |
| } |
| if ( |
| action === "primary" && |
| compactHeaderState?.actions?.primary?.mode === "discuss" && |
| button.hasAttribute("href") |
| ) { |
| try { |
| window.hfviewerAnalyticsBridge?.capture?.("discord_link_clicked", { |
| product_surface: "hf_space", |
| source_page: "hf_space", |
| surface: "space_compact_header", |
| model_repo_id: compactHeaderState?.repoId || "", |
| href: button.href, |
| }); |
| } catch (error) { } |
| return; |
| } |
| event.preventDefault(); |
| postHeaderAction(action); |
| }); |
| document.addEventListener("click", function (event) { |
| if (!compactDiffMenu?.hidden && !compactDiffMenu.contains(event.target) && !event.target.closest("[data-action='primary']")) { |
| compactDiffMenu.hidden = true; |
| } |
| }); |
| } |
|
|
| function postHeaderAction(action, extra) { |
| var frame = document.getElementById("sx-viewer-frame"); |
| if (!frame?.contentWindow) return; |
| frame.contentWindow.postMessage(Object.assign({ |
| type: HEADER_ACTION_MESSAGE_TYPE, |
| version: 1, |
| action: action, |
| }, extra || {}), WEBSITE_ORIGIN); |
| } |
|
|
| function renderCompactHeader(state) { |
| var previousRepoId = String(compactHeaderState?.repoId || ""); |
| compactHeaderState = state; |
| var actions = document.getElementById("sx-model-actions"); |
| if (!actions) return; |
| var pageMode = state.viewMode || "model"; |
| if (previousRepoId !== String(state.repoId || "")) { |
| compactEmbedCopySequence += 1; |
| compactBookmarkSyncSequence += 1; |
| compactBookmarkRepoId = ""; |
| compactBookmarkPressed = null; |
| compactBookmarkSyncingRepoId = ""; |
| resetCompactEmbedControls(String(state.repoId || "")); |
| } |
| ["report", "embed", "bookmark", "primary"].forEach(function (name) { |
| var button = actions.querySelector('[data-action="' + name + '"]'); |
| if (!button) return; |
| var actionState = state.actions?.[name] || {}; |
| button.hidden = pageMode !== "model" || actionState.visible === false; |
| if (name === "embed") { |
| button.disabled = !String( |
| actionState.snippet || buildCompactModelCardSnippet(state.repoId) |
| ); |
| } |
| if (name === "bookmark") { |
| if ( |
| window.hfviewerSpaceAuth?.isAuthenticated?.() && |
| compactBookmarkRepoId === String(state.repoId || "") && |
| compactBookmarkPressed !== null |
| ) { |
| renderCompactBookmarkState(String(state.repoId || ""), compactBookmarkPressed); |
| } else { |
| |
| |
| |
| var nativePressed = window.hfviewerSpaceAuth?.isAuthenticated?.() |
| ? !!actionState.pressed |
| : false; |
| button.classList.toggle("is-bookmarked", nativePressed); |
| button.setAttribute("aria-pressed", nativePressed ? "true" : "false"); |
| } |
| } |
| }); |
| var editReadme = actions.querySelector('[data-action="edit-readme"]'); |
| if (editReadme && pageMode !== "model") editReadme.hidden = true; |
| var huggingFace = actions.querySelector('[data-action="hugging-face"]'); |
| if (huggingFace) huggingFace.hidden = pageMode !== "model"; |
| if (huggingFace && pageMode === "model" && state.huggingFaceHref) { |
| huggingFace.href = new URL(state.huggingFaceHref, HUGGING_FACE_ORIGIN).href; |
| huggingFace.target = "_blank"; |
| huggingFace.rel = "noopener noreferrer"; |
| } |
| ["compare-back", "compare-watch", "compare-install"].forEach(function (name) { |
| var button = actions.querySelector('[data-action="' + name + '"]'); |
| if (!button) return; |
| var stateKey = name === "compare-back" |
| ? "compareBack" |
| : name === "compare-watch" |
| ? "compareWatch" |
| : "compareInstall"; |
| var actionState = state.actions?.[stateKey] || {}; |
| button.hidden = pageMode === "model" || actionState.visible !== true; |
| var label = String(actionState.label || button.getAttribute("aria-label") || "").trim(); |
| if (label) { |
| button.querySelector(".sx-action-label").textContent = label; |
| button.setAttribute("aria-label", label); |
| if (name === "compare-watch") button.dataset.sxTooltip = label; |
| } |
| if (name === "compare-install" && actionState.href) { |
| button.href = new URL(actionState.href, WEBSITE_ORIGIN).href; |
| button.target = "_blank"; |
| button.rel = "noopener noreferrer"; |
| } |
| }); |
| var open = document.getElementById("sx-open"); |
| if (open && state.pagePath) { |
| open.href = new URL(state.pagePath, WEBSITE_ORIGIN).href; |
| open.dataset.embeddedPagePath = state.pagePath; |
| window.__SX_CURRENT_EMBEDDED_PATH = state.pagePath; |
| } |
| var primary = actions.querySelector('[data-action="primary"]'); |
| var primaryState = state.actions?.primary || {}; |
| if (primary) { |
| var label = primaryState.label || (primaryState.mode === "diff" ? "Diff" : "Discuss"); |
| primary.querySelector(".sx-action-label").textContent = label; |
| var primaryIcon = primary.querySelector(".sx-action-icon"); |
| primaryIcon.hidden = primaryState.mode === "diff"; |
| primaryIcon.innerHTML = primaryState.mode === "install" |
| ? COMPACT_INSTALL_ICON |
| : primaryState.mode === "discuss" |
| ? COMPACT_DISCUSS_ICON |
| : ""; |
| primary.setAttribute("aria-label", label); |
| primary.dataset.sxTooltip = primaryState.mode === "install" |
| ? "Install Chrome extension" |
| : primaryState.mode === "diff" |
| ? "View model diff" |
| : "Discuss model"; |
| primary.classList.toggle("sx-compact-action--primary", primaryState.mode === "diff"); |
| if ( |
| (primaryState.mode === "install" || primaryState.mode === "discuss") && |
| primaryState.href |
| ) { |
| primary.href = new URL(primaryState.href, WEBSITE_ORIGIN).href; |
| primary.target = "_blank"; |
| primary.rel = primaryState.mode === "install" ? "noopener noreferrer" : "noopener"; |
| } else { |
| primary.removeAttribute("href"); |
| primary.removeAttribute("target"); |
| primary.removeAttribute("rel"); |
| } |
| primary.querySelector(".sx-diff-counts")?.remove(); |
| if (primaryState.mode === "diff") { |
| var totals = (primaryState.options || []).reduce(function (sum, option) { |
| sum.plus += Number(option.plus || 0); |
| sum.minus += Number(option.minus || 0); |
| return sum; |
| }, { plus: 0, minus: 0 }); |
| var counts = document.createElement("span"); |
| counts.className = "sx-diff-counts"; |
| counts.innerHTML = '<span class="sx-diff-plus"></span><span class="sx-diff-minus"></span>'; |
| counts.querySelector(".sx-diff-plus").textContent = "+" + totals.plus; |
| counts.querySelector(".sx-diff-minus").textContent = "−" + totals.minus; |
| primary.prepend(counts); |
| } |
| } |
| if (pageMode === "model" && window.hfviewerSpaceAuth?.isAuthenticated?.()) { |
| void syncCompactSpaceBookmark(); |
| } |
| compactDiffMenu.replaceChildren(); |
| (primaryState.options || []).forEach(function (option) { |
| var button = document.createElement("button"); |
| button.type = "button"; |
| button.className = "sx-compact-diff-option"; |
| button.innerHTML = '<span class="sx-diff-option-label"></span><span class="sx-diff-counts"><span class="sx-diff-plus"></span><span class="sx-diff-minus"></span></span>'; |
| button.querySelector(".sx-diff-option-label").textContent = option.otherLabel; |
| button.querySelector(".sx-diff-plus").textContent = "+" + Number(option.plus || 0); |
| button.querySelector(".sx-diff-minus").textContent = "−" + Number(option.minus || 0); |
| button.addEventListener("click", function () { |
| compactDiffMenu.hidden = true; |
| openCompactDiff(option.href); |
| }); |
| compactDiffMenu.appendChild(button); |
| }); |
| syncNativeSpaceWatchControls(actions); |
| } |
|
|
| function handleCompactHeaderState(event) { |
| var frame = document.getElementById("sx-viewer-frame"); |
| if ( |
| event.origin !== WEBSITE_ORIGIN || |
| !frame || |
| event.source !== frame.contentWindow || |
| event.data?.type !== HEADER_STATE_MESSAGE_TYPE || |
| event.data?.version !== 1 |
| ) return; |
| renderCompactHeader(event.data); |
| } |
|
|
| function replaceSpaceCookieWording(root) { |
| var walker = document.createTreeWalker(root || document.body, NodeFilter.SHOW_TEXT); |
| var node; |
| while ((node = walker.nextNode())) { |
| var parentTag = node.parentElement?.tagName; |
| if (parentTag === "SCRIPT" || parentTag === "STYLE") continue; |
| if (node.nodeValue?.includes("This website stores cookies on your computer")) { |
| node.nodeValue = node.nodeValue.replaceAll( |
| "This website stores cookies on your computer", |
| "This space stores cookies on your computer" |
| ); |
| } |
| } |
| } |
|
|
| function modelFromLocation() { |
| return new URLSearchParams(window.location.search).get("model") || ""; |
| } |
|
|
| var lastParentModel = modelFromLocation(); |
|
|
| function parentQueryString() { |
| var params = new URLSearchParams(window.location.search); |
| Array.from(params.keys()).forEach(function (key) { |
| if (key.startsWith("__")) params.delete(key); |
| }); |
| var query = params.toString(); |
| return query ? "?" + query : ""; |
| } |
|
|
| function syncModelToHuggingFaceUrl() { |
| var model = modelFromLocation(); |
| if (model === lastParentModel) return; |
| lastParentModel = model; |
| if (window.parent === window) return; |
| window.parent.postMessage({ |
| queryString: parentQueryString(), |
| hash: window.location.hash || "", |
| }, HUGGING_FACE_ORIGIN); |
| } |
|
|
| function syncAfterHistoryChange(methodName) { |
| var original = window.history[methodName]; |
| if (typeof original !== "function") return; |
| window.history[methodName] = function () { |
| var result = original.apply(this, arguments); |
| syncModelToHuggingFaceUrl(); |
| return result; |
| }; |
| } |
|
|
| function repositoryFromImage(image) { |
| var alt = String(image.getAttribute("alt") || "").trim(); |
| var suffix = " architecture graph"; |
| if (alt.endsWith(suffix)) return alt.slice(0, -suffix.length).trim(); |
|
|
| var card = image.closest("[data-model]"); |
| if (!card) return ""; |
| try { |
| var url = new URL(String(card.dataset.model || ""), window.location.href); |
| return url.pathname.replace(/^\/+|\/+$/g, ""); |
| } catch (error) { |
| return ""; |
| } |
| } |
|
|
| function restoreCard(image) { |
| if (!(image instanceof HTMLImageElement)) return; |
| if (image.dataset.spaceCardFallback === "1") return; |
| if (!String(image.currentSrc || image.src || "").startsWith(CARD_PREFIX)) return; |
| var repository = repositoryFromImage(image); |
| if (!repository || repository.split("/").length !== 2) return; |
|
|
| image.dataset.spaceCardFallback = "1"; |
| var query = new URLSearchParams({ |
| source: repository, |
| source_page: "hf-space", |
| }); |
| image.src = API_PREFIX + "?" + query.toString(); |
| } |
|
|
| function removeSpaceOnlyProductFooter(root) { |
| if (!(root instanceof Element || root instanceof Document)) return; |
| if (root.matches?.("section.products-section[data-page='landing']")) { |
| root.remove(); |
| return; |
| } |
| root |
| .querySelectorAll?.("section.products-section[data-page='landing']") |
| .forEach(function (section) { section.remove(); }); |
| } |
|
|
| document.addEventListener("error", function (event) { |
| restoreCard(event.target); |
| }, true); |
|
|
| function repairAlreadyFailedImages(root) { |
| var images = (root || document).querySelectorAll("img"); |
| images.forEach(function (image) { |
| if (image.complete && image.naturalWidth === 0) restoreCard(image); |
| }); |
| } |
|
|
| function returnToSpaceLanding(event) { |
| var frame = document.getElementById("sx-viewer-frame"); |
| if ( |
| event.origin !== WEBSITE_ORIGIN || |
| !frame || |
| event.source !== frame.contentWindow || |
| event.data?.type !== HOME_MESSAGE_TYPE || |
| event.data?.version !== 1 |
| ) return; |
| document.getElementById("sx-back")?.click(); |
| window.scrollTo({ top: 0 }); |
| } |
|
|
| function viewerElements() { |
| return { |
| overlay: document.getElementById("sx-viewer-overlay"), |
| frame: document.getElementById("sx-viewer-frame"), |
| loading: document.getElementById("sx-viewer-loading"), |
| }; |
| } |
|
|
| function clearViewerRetryTimer() { |
| if (viewerRetryTimer) window.clearTimeout(viewerRetryTimer); |
| viewerRetryTimer = 0; |
| } |
|
|
| function showViewerLoadingMessage(message, retryable) { |
| var elements = viewerElements(); |
| if (!elements.loading) return; |
| elements.loading.classList.remove("hidden"); |
| elements.loading.replaceChildren(); |
| var text = document.createElement("span"); |
| text.textContent = message; |
| elements.loading.appendChild(text); |
| if (!retryable) return; |
| var button = document.createElement("button"); |
| button.type = "button"; |
| button.textContent = "Retry"; |
| button.style.cssText = |
| "display:block;margin:14px auto 0;padding:8px 16px;border:1px solid #625276;" + |
| "border-radius:10px;background:#241b31;color:#fff;font:inherit;cursor:pointer"; |
| button.addEventListener("click", function () { |
| retryViewerFrame("manual"); |
| }); |
| elements.loading.appendChild(button); |
| } |
|
|
| function retryViewerFrame(reason) { |
| var elements = viewerElements(); |
| if (!elements.frame || !elements.frame.src) return; |
| clearViewerRetryTimer(); |
| viewerRetryCount += 1; |
| viewerLoadSequence += 1; |
| showViewerLoadingMessage("Loading architecture graph\u2026", false); |
| try { |
| var url = new URL(elements.frame.src); |
| url.searchParams.set("space_retry", String(viewerRetryCount)); |
| url.searchParams.set("space_load_nonce", String(Date.now())); |
| viewerRetryMutationExpected = true; |
| elements.frame.src = url.toString(); |
| } catch (error) { |
| viewerRetryMutationExpected = true; |
| elements.frame.src = elements.frame.src; |
| } |
| scheduleViewerRecovery(viewerLoadSequence, VIEWER_FAILURE_DELAY_MS); |
| try { |
| window.hfviewerAnalyticsBridge?.capture?.("space_embedded_viewer_retried", { |
| product_surface: "hf_space", |
| retry_reason: reason, |
| retry_count: viewerRetryCount, |
| }); |
| } catch (error) { } |
| } |
|
|
| function scheduleViewerRecovery(sequence, delay) { |
| clearViewerRetryTimer(); |
| viewerRetryTimer = window.setTimeout(function () { |
| viewerRetryTimer = 0; |
| var elements = viewerElements(); |
| if ( |
| sequence !== viewerLoadSequence || |
| !elements.overlay?.classList.contains("open") || |
| elements.loading?.classList.contains("hidden") |
| ) return; |
| if (viewerRetryCount === 0) { |
| retryViewerFrame("timeout"); |
| return; |
| } |
| showViewerLoadingMessage( |
| "The graph is taking longer than expected.", |
| true |
| ); |
| }, delay); |
| } |
|
|
| function armViewerRecovery() { |
| var elements = viewerElements(); |
| if (!elements.frame || !elements.loading) return; |
| viewerRetryCount = 0; |
| viewerLoadSequence += 1; |
| showViewerLoadingMessage("Loading architecture graph\u2026", false); |
| scheduleViewerRecovery(viewerLoadSequence, VIEWER_RETRY_DELAY_MS); |
| } |
|
|
| function installViewerRecovery() { |
| var elements = viewerElements(); |
| if (!elements.frame || !elements.loading) return; |
| elements.frame.addEventListener("load", function () { |
| clearViewerRetryTimer(); |
| elements.loading.classList.add("hidden"); |
| }); |
| new MutationObserver(function (records) { |
| if (records.some(function (record) { return record.attributeName === "src"; })) { |
| if (viewerRetryMutationExpected) { |
| viewerRetryMutationExpected = false; |
| return; |
| } |
| if (applyStoredSpaceCookieConsentToFrame(true)) return; |
| armViewerRecovery(); |
| } |
| }).observe(elements.frame, { attributes: true, attributeFilter: ["src"] }); |
| new MutationObserver(function () { |
| if ( |
| elements.overlay?.classList.contains("open") && |
| !elements.loading.classList.contains("hidden") && |
| !viewerRetryTimer && |
| elements.frame.src |
| ) { |
| retryViewerFrame("reopen"); |
| } |
| }).observe(elements.overlay, { attributes: true, attributeFilter: ["class"] }); |
| if (elements.frame.getAttribute("src")) armViewerRecovery(); |
| } |
|
|
| removeSpaceOnlyProductFooter(document); |
| installCompactViewerHeader(); |
| replaceSpaceCookieWording(document.body); |
| repairAlreadyFailedImages(document); |
| syncAfterHistoryChange("replaceState"); |
| syncAfterHistoryChange("pushState"); |
| window.addEventListener("popstate", syncModelToHuggingFaceUrl); |
| window.addEventListener("message", returnToSpaceLanding); |
| window.addEventListener("message", handleCompactHeaderState); |
| window.addEventListener("message", handleCompactHeaderActionResult); |
| window.addEventListener("message", handleSpaceCookieConsent); |
| document.addEventListener("click", handleNativeSpaceWatchClick, true); |
| window.addEventListener("hfviewer-space-auth-changed", function (event) { |
| compactBookmarkSyncSequence += 1; |
| compactBookmarkRepoId = ""; |
| compactBookmarkPressed = null; |
| compactBookmarkSyncingRepoId = ""; |
| if (event.detail?.authenticated) { |
| void syncCompactSpaceBookmark(); |
| void loadNativeSpaceWatchlistState(); |
| } else { |
| nativeSpaceWatchStateSequence += 1; |
| nativeSpaceWatchStateLoaded = false; |
| nativeSpaceWatchedOwners = new Set(); |
| nativeSpaceWatchOperations.clear(); |
| var bookmark = document.querySelector('#sx-model-actions [data-action="bookmark"]'); |
| if (bookmark) { |
| bookmark.classList.remove("is-bookmarked"); |
| bookmark.setAttribute("aria-pressed", "false"); |
| bookmark.setAttribute("aria-label", "Bookmark graph"); |
| bookmark.dataset.sxTooltip = "Bookmark graph"; |
| } |
| } |
| }); |
| applyStoredSpaceCookieConsentToFrame(false); |
| installViewerRecovery(); |
| new MutationObserver(function (records) { |
| records.forEach(function (record) { |
| record.addedNodes.forEach(function (node) { |
| if (!(node instanceof Element)) return; |
| replaceSpaceCookieWording(node); |
| removeSpaceOnlyProductFooter(node); |
| if (node instanceof HTMLImageElement) restoreCard(node); |
| repairAlreadyFailedImages(node); |
| syncNativeSpaceWatchControls(node); |
| }); |
| }); |
| }).observe(document.body, { childList: true, subtree: true }); |
| })(); |
|
|