hfviewer / space-shell.js
hfviewer automation
Sync Space from hfviewer-22514889f757
0ae3824
Raw
History Blame Contribute Delete
63 kB
(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 = '<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);
// 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, "&amp;").replace(/"/g, "&quot;");
};
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");
// 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", '<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) { /* 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 = '<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) { /* 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 });
})();