(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; // Invalidate any older state read before issuing the mutation. Otherwise // a delayed state response can overwrite the newer toggle response. 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()); // The landing search CTA's checked state is an informational confirmation, // not an unwatch action. Preserve that behavior while intercepting its // click before the website-cookie handler can open an OAuth form. The // already-watched click must be intercepted too: letting it bubble would // re-enter the legacy cookie-authenticated handler even though no toggle // is wanted. 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 = ''; var COMPACT_INSTALL_ICON = ''; var COMPACT_BACK_ICON = ''; var COMPACT_WATCH_ICON = ''; var COMPACT_EDIT_ICON = ''; 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 = '' + icon + ''; 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); // Keep the clipboard write in the Space button's own trusted click. A // postMessage into the nested hfviewer iframe loses transient user // activation in Chromium, so both the Clipboard API and execCommand are // rejected there even though the same button works on hfviewer.com. 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); // Modern fallback, invoked synchronously before this function yields so // the browser still associates it with the original click. 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 [ '', " ", "", ].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", '', "Embed on model card"); // The compact controls are installed before the viewer has posted its // model-specific header state. Do not accept an Embed click during that // short gap: the compatibility fallback crosses an iframe boundary and // therefore cannot retain clipboard user activation. 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", '', "Report graph issue"), compactAction("hugging-face", "Hugging Face", '', "Go to HF model", { link: true }), embedAction, editReadmeAction, compactAction("bookmark", "Bookmark", '', "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) { /* navigation must not depend on analytics */ } 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) { /* clipboard feedback must not depend on analytics */ } }); 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) { /* navigation must not depend on analytics */ } 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) { /* navigation must not depend on analytics */ } 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) { /* navigation must not depend on analytics */ } 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) { /* navigation must not depend on analytics */ } 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 { // The embedded website and the Space have separate browser storage. // Never paint the iframe's cookie-session bookmark state as native // Space state when there is no native Space profile beside it. 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 = ''; 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 = ''; 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) { /* recovery must not depend on analytics */ } } 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 }); })();