mirror of
https://github.com/harvard-edge/cs249r_book.git
synced 2026-11-09 10:39:41 -06:00
219 lines
8.5 KiB
HTML
219 lines
8.5 KiB
HTML
<script>
|
|
// Inject subtitle + framework version chip under sidebar logo.
|
|
// The chip's version literal is sourced from tinytorch/settings.ini and
|
|
// kept in sync by the tinytorch-publish-live workflow's UPDATE_VERSION
|
|
// step on each release. Update marker for sed: TINYTORCH_VERSION_CHIP.
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const sidebarLogo = document.querySelector('.sidebar-logo');
|
|
if (!sidebarLogo) return;
|
|
|
|
// The logo <img> sits inside Quarto's <a class="sidebar-logo-link"> wrapper.
|
|
// Inserting our subtitle as a sibling of the <img> would nest the subtitle's
|
|
// link INSIDE that anchor — invalid HTML, so the browser fires the outer logo
|
|
// link (./index.html) instead of navigating to the textbook. Insert AFTER the
|
|
// logo anchor, as a child of .sidebar-header, so our links are not nested.
|
|
const logoLink = sidebarLogo.closest('.sidebar-logo-link') || sidebarLogo;
|
|
const anchorHost = logoLink.parentElement;
|
|
|
|
// 1. Existing subtitle — what TinyTorch IS in relation to the textbook.
|
|
const subtitle = document.createElement('div');
|
|
subtitle.className = 'sidebar-subtitle';
|
|
subtitle.innerHTML = 'A Build-It-Yourself Companion to the<br><a href="https://mlsysbook.ai" style="color: #D4740C; text-decoration: none;">Machine Learning Systems</a> textbook';
|
|
anchorHost.insertBefore(subtitle, logoLink.nextSibling);
|
|
|
|
// 2. Framework version chip — dynamically themed by version hash so each
|
|
// release cycle gets an assigned, distinctive high-contrast accent.
|
|
const chipWrap = document.createElement('div');
|
|
chipWrap.className = 'tinytorch-version-chip-wrap';
|
|
|
|
const chip = document.createElement('a');
|
|
chip.className = 'tinytorch-version-chip';
|
|
chip.href = 'https://github.com/harvard-edge/cs249r_book/releases?q=tinytorch';
|
|
chip.target = '_blank';
|
|
chip.rel = 'noopener';
|
|
chip.innerHTML = '<span class="tinytorch-version-dot"></span><span class="tinytorch-version-fire">🔥 </span><span class="tinytorch-chip-label">TinyTorch </span><span class="tinytorch-version-tag">v0.2.2</span><span class="tinytorch-version-arrow"> →</span>'; // TINYTORCH_VERSION_CHIP
|
|
|
|
// Curated palette of WCAG AA-compliant accent colors
|
|
const PALETTE = [
|
|
{ name: 'amber', light: '#D4740C', bg: 'rgba(212, 116, 12, 0.08)', bgHover: 'rgba(212, 116, 12, 0.18)', dark: '#F59E0B', darkBg: 'rgba(245, 158, 11, 0.12)', darkBgHover: 'rgba(245, 158, 11, 0.22)' },
|
|
{ name: 'emerald', light: '#059669', bg: 'rgba(5, 150, 105, 0.08)', bgHover: 'rgba(5, 150, 105, 0.18)', dark: '#10B981', darkBg: 'rgba(16, 185, 129, 0.12)', darkBgHover: 'rgba(16, 185, 129, 0.22)' },
|
|
{ name: 'cobalt', light: '#2563EB', bg: 'rgba(37, 99, 235, 0.08)', bgHover: 'rgba(37, 99, 235, 0.18)', dark: '#60A5FA', darkBg: 'rgba(96, 165, 250, 0.12)', darkBgHover: 'rgba(96, 165, 250, 0.22)' },
|
|
{ name: 'violet', light: '#7C3AED', bg: 'rgba(124, 58, 237, 0.08)', bgHover: 'rgba(124, 58, 237, 0.18)', dark: '#A78BFA', darkBg: 'rgba(167, 139, 250, 0.12)', darkBgHover: 'rgba(167, 139, 250, 0.22)' },
|
|
{ name: 'rose', light: '#E11D48', bg: 'rgba(225, 29, 72, 0.08)', bgHover: 'rgba(225, 29, 72, 0.18)', dark: '#FB7185', darkBg: 'rgba(251, 113, 133, 0.12)', darkBgHover: 'rgba(251, 113, 133, 0.22)' },
|
|
{ name: 'cyan', light: '#0891B2', bg: 'rgba(8, 145, 178, 0.08)', bgHover: 'rgba(8, 145, 178, 0.18)', dark: '#22D3EE', darkBg: 'rgba(34, 211, 238, 0.12)', darkBgHover: 'rgba(34, 211, 238, 0.22)' },
|
|
{ name: 'coral', light: '#EA580C', bg: 'rgba(234, 88, 12, 0.08)', bgHover: 'rgba(234, 88, 12, 0.18)', dark: '#FB923C', darkBg: 'rgba(251, 146, 60, 0.12)', darkBgHover: 'rgba(251, 146, 60, 0.22)' },
|
|
{ name: 'purple', light: '#9333EA', bg: 'rgba(147, 51, 234, 0.08)', bgHover: 'rgba(147, 51, 234, 0.18)', dark: '#C084FC', darkBg: 'rgba(192, 132, 252, 0.12)', darkBgHover: 'rgba(192, 132, 252, 0.22)' },
|
|
];
|
|
|
|
function applyTheme(verText) {
|
|
const versionMatch = (verText || '').match(/v?(\d+\.\d+\.\d+)/);
|
|
const versionStr = versionMatch ? versionMatch[1] : '0.1.16';
|
|
let hash = 0;
|
|
for (let i = 0; i < versionStr.length; i++) {
|
|
hash = ((hash << 5) - hash) + versionStr.charCodeAt(i);
|
|
hash |= 0;
|
|
}
|
|
const theme = PALETTE[Math.abs(hash) % PALETTE.length];
|
|
|
|
chip.style.setProperty('--pill-color', theme.light);
|
|
chip.style.setProperty('--pill-bg', theme.bg);
|
|
chip.style.setProperty('--pill-bg-hover', theme.bgHover);
|
|
chip.style.setProperty('--pill-dark-color', theme.dark);
|
|
chip.style.setProperty('--pill-dark-bg', theme.darkBg);
|
|
chip.style.setProperty('--pill-dark-bg-hover', theme.darkBgHover);
|
|
}
|
|
|
|
applyTheme(chip.innerText);
|
|
|
|
chipWrap.appendChild(chip);
|
|
subtitle.parentNode.insertBefore(chipWrap, subtitle.nextSibling);
|
|
|
|
// Live version revalidation: fetches release manifest with cache busting to
|
|
// ensure Safari and browsers serving cached HTML/bfcache update dynamically.
|
|
function refreshLiveVersion() {
|
|
fetch('/tinytorch/release-manifest.json?_t=' + Date.now(), { cache: 'no-store' })
|
|
.then(function(res) { return res.ok ? res.json() : null; })
|
|
.then(function(manifest) {
|
|
if (!manifest || !manifest.releaseId) return;
|
|
const liveTag = 'v' + manifest.releaseId.replace(/^v/, '');
|
|
const tagEl = chip.querySelector('.tinytorch-version-tag');
|
|
if (tagEl && tagEl.textContent !== liveTag) {
|
|
tagEl.textContent = liveTag;
|
|
applyTheme(liveTag);
|
|
}
|
|
})
|
|
.catch(function() {});
|
|
}
|
|
|
|
refreshLiveVersion();
|
|
|
|
window.addEventListener('pageshow', function(evt) {
|
|
if (evt.persisted) {
|
|
refreshLiveVersion();
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
<style>
|
|
.sidebar-subtitle {
|
|
text-align: center;
|
|
font-size: 0.8rem;
|
|
color: #6b7280;
|
|
line-height: 1.4;
|
|
padding: 0 1rem 0.5rem 1rem;
|
|
margin-top: 0.5rem;
|
|
}
|
|
.sidebar-subtitle a:hover {
|
|
color: #f97316 !important;
|
|
}
|
|
|
|
/* Version chip — divider above + dynamic release-colored pill */
|
|
.tinytorch-version-chip-wrap {
|
|
container-type: inline-size;
|
|
text-align: center;
|
|
margin: 0 0.5rem;
|
|
padding: 0.4rem 0.2rem 0.65rem;
|
|
border-top: 1px solid #e5e7eb;
|
|
}
|
|
.tinytorch-version-chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
max-width: 100%;
|
|
box-sizing: border-box;
|
|
padding: 0.16rem 0.5rem;
|
|
border: 1px solid var(--pill-color, #D4740C);
|
|
border-radius: 999px;
|
|
background: var(--pill-bg, rgba(212, 116, 12, 0.08));
|
|
color: var(--pill-color, #D4740C) !important;
|
|
font-size: 0.70rem;
|
|
font-weight: 600;
|
|
text-decoration: none !important;
|
|
white-space: nowrap;
|
|
letter-spacing: 0.01em;
|
|
transition: all 0.2s ease;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.tinytorch-version-chip:hover {
|
|
background: var(--pill-bg-hover, rgba(212, 116, 12, 0.18));
|
|
color: var(--pill-color, #D4740C) !important;
|
|
text-decoration: none;
|
|
}
|
|
.tinytorch-version-dot {
|
|
display: inline-block;
|
|
flex-shrink: 0;
|
|
width: 6px;
|
|
height: 6px;
|
|
margin-right: 5px;
|
|
border-radius: 50%;
|
|
background-color: var(--pill-color, #D4740C);
|
|
box-shadow: 0 0 6px var(--pill-color, #D4740C);
|
|
animation: pulse-dot 2.5s infinite ease-in-out;
|
|
}
|
|
.tinytorch-version-fire {
|
|
flex-shrink: 0;
|
|
margin-right: 2px;
|
|
}
|
|
.tinytorch-chip-label {
|
|
white-space: nowrap;
|
|
}
|
|
.tinytorch-version-tag {
|
|
white-space: nowrap;
|
|
}
|
|
.tinytorch-version-arrow {
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
@container (max-width: 215px) {
|
|
.tinytorch-version-chip {
|
|
font-size: 0.66rem;
|
|
padding: 0.14rem 0.42rem;
|
|
}
|
|
}
|
|
|
|
@container (max-width: 185px) {
|
|
.tinytorch-chip-label {
|
|
display: none;
|
|
}
|
|
.tinytorch-version-chip {
|
|
font-size: 0.68rem;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 1200px) {
|
|
.tinytorch-version-chip-wrap {
|
|
margin: 0 0.25rem;
|
|
}
|
|
}
|
|
@media (max-width: 1050px) {
|
|
.tinytorch-chip-label {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
@keyframes pulse-dot {
|
|
0% { transform: scale(0.9); opacity: 0.75; }
|
|
50% { transform: scale(1.3); opacity: 1; box-shadow: 0 0 8px var(--pill-color, #D4740C); }
|
|
100% { transform: scale(0.9); opacity: 0.75; }
|
|
}
|
|
|
|
/* Dark-mode overrides */
|
|
[data-bs-theme="dark"] .tinytorch-version-chip-wrap {
|
|
border-top-color: #454d55;
|
|
}
|
|
[data-bs-theme="dark"] .tinytorch-version-chip {
|
|
border-color: var(--pill-dark-color, #F59E0B);
|
|
color: var(--pill-dark-color, #F59E0B) !important;
|
|
background: var(--pill-dark-bg, rgba(245, 158, 11, 0.12));
|
|
}
|
|
[data-bs-theme="dark"] .tinytorch-version-chip:hover {
|
|
background: var(--pill-dark-bg-hover, rgba(245, 158, 11, 0.22));
|
|
color: var(--pill-dark-color, #F59E0B) !important;
|
|
}
|
|
[data-bs-theme="dark"] .tinytorch-version-dot {
|
|
background-color: var(--pill-dark-color, #F59E0B);
|
|
box-shadow: 0 0 6px var(--pill-dark-color, #F59E0B);
|
|
}
|
|
</style>
|