Trending Paper
+Meet the standout research through ResearchStudio Reels
+Each paper transformed into a visual poster, a narrated video, and an accessible blog,so you can scan, watch, or read it your way.
+diff --git a/docs/trending-paper/daily.css b/docs/trending-paper/daily.css new file mode 100644 index 0000000..f49e76a --- /dev/null +++ b/docs/trending-paper/daily.css @@ -0,0 +1,325 @@ +:root { + color-scheme: light; + --ink: #0d0d0f; + --ink-2: #3a3a3f; + --muted: #6b6b72; + --paper: #f5f5f6; + --surface: #ffffff; + --surface-soft: #f0f0f2; + --line: rgba(17,17,20,.10); + --blue: #2b50f0; + --blue-dark: #1e3fd0; + --border: #c7c7cc; + --border-soft: #d2d2d7; + --header-bg: rgba(245,245,246,.94); + --poster-start: #e4e4e7; + --poster-end: #f4f4f5; + --disabled: #b4b4ba; + --viewer-stage: #e8e8eb; + --fallback: #f5f5f6; + --body-glow-a: rgba(223,223,223,.38); + --body-glow-b: rgba(90,90,90,.10); + --blue-shadow: rgba(43,80,240,.18); + --focus-ring: rgba(43,80,240,.20); + --card-shadow: rgba(17,17,20,.07); + --hf-icon: #d68700; + --arxiv-icon: #b31b1b; + --nav-accent: #6c5ce7; + --shared-font-sans: "Inter var", Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Microsoft YaHei", sans-serif; + font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", Roboto, Helvetica, Arial, sans-serif; +} + +@font-face { + font-family: "Inter var"; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url("fonts/InterVariable.woff2?v=4.1") format("woff2"); +} +* { box-sizing: border-box; } +.sr-only { position: absolute !important; width: 1px !important; height: 1px !important; padding: 0 !important; margin: -1px !important; overflow: hidden !important; clip: rect(0,0,0,0) !important; white-space: nowrap !important; border: 0 !important; } +body { + margin: 0; + color: var(--ink); + background: + radial-gradient(circle at 8% 0%, var(--body-glow-a), transparent 30rem), + radial-gradient(circle at 94% 22%, var(--body-glow-b), transparent 32rem), + var(--paper); + -webkit-font-smoothing: antialiased; +} +a { color: inherit; } +.topbar-wrap { position: sticky; top: 0; z-index: 50; border-bottom: 1px solid color-mix(in srgb, #e6e8ef 70%, transparent); background: color-mix(in srgb, #fff 75%, transparent); backdrop-filter: saturate(180%) blur(14px); -webkit-backdrop-filter: saturate(180%) blur(14px); } +.topbar { max-width: 1180px; margin: 0 auto; padding: 14px 24px; display: flex; align-items: center; gap: 24px; font-family: var(--shared-font-sans); font-feature-settings: "cv11", "ss01", "ss03"; line-height: 1.55; } +.brand { display: inline-flex; align-items: center; gap: 10px; color: #0b1020; text-decoration: none; font-size: 17px; font-weight: 700; letter-spacing: -.01em; } +.brand:hover { text-decoration: none; } +.brand-logo { display: block; width: 28px; height: 28px; border-radius: 0; } +.nav-links { display: flex; flex: 1; align-items: center; gap: 22px; margin: 0 0 0 16px; padding: 0; list-style: none; } +.nav-links li { display: inline-flex; } +.nav-links a { padding: 0; color: #555a6a; text-decoration: none; font-size: 14.5px; font-weight: 500; white-space: nowrap; transition: color .15s ease; } +.nav-links a:hover { color: #0b1020; text-decoration: none; } +.nav-links a.active { color: var(--nav-accent); font-weight: 500; } +.nav__cta { display: flex; align-items: center; gap: 10px; margin-left: auto; } +.github-link { width: 173.02px; display: inline-flex; align-items: center; justify-content: center; gap: 8px; padding: 11px 18px; border: 1px solid #d3d7e0; border-radius: 999px; color: #0b1020; background: #fff; text-decoration: none; font-size: 14.5px; font-weight: 600; white-space: nowrap; cursor: pointer; transition: transform .15s ease, box-shadow .15s ease, background .15s ease, color .15s ease, border-color .15s ease; } +.github-link:hover { background: #f7f8fb; text-decoration: none; transform: translateY(-1px); } +.github-link svg { flex-shrink: 0; } +main { width: min(1180px, 90vw); margin: 0 auto; padding: 52px 0 90px; } +.hero { margin-bottom: 38px; } +.title-row { position: relative; left: 50%; width: min(1320px, 100vw); display: flex; align-items: flex-start; justify-content: space-between; gap: 28px; padding: 0 32px; transform: translateX(-50%); } +.title-copy { flex: 1; min-width: 0; } +h1 { margin: 0; max-width: 820px; font-size: clamp(2.4rem, 5vw, 4.8rem); font-weight: 760; line-height: .98; letter-spacing: -.045em; } +.intro { max-width: 800px; margin: 22px 0 0; color: var(--muted); line-height: 1.55; } +.intro p { margin: 0; } +.intro-detail { margin-top: 8px !important; font-size: 1.02rem; } +.intro-detail span { display: block; } +.intro-lead { color: var(--ink-2); font-size: clamp(1.12rem, 2vw, 1.35rem); font-weight: 570; } +.intro-detail strong { color: var(--ink-2); font-weight: 780; } +.reels-accent { color: var(--blue); font-weight: 850; text-decoration: none; text-decoration-thickness: .08em; text-underline-offset: .16em; } +.reels-accent:hover { color: var(--blue-dark); text-decoration-line: underline; } +.github-stars-badge { box-sizing: border-box; width: 124px; height: 26px; display: inline-flex; align-items: center; justify-content: center; gap: 5px; margin-left: 5px; padding: 4px 7px; border: 1px solid rgba(74,74,74,.2); border-radius: 4px; color: #606060; background: rgba(255,255,255,.74); font-family: "SFMono-Regular", Consolas, monospace; font-size: .68rem; line-height: 1; text-decoration: none; vertical-align: middle; transition: border-color .15s, color .15s, background .15s, transform .15s; } +.github-stars-badge[hidden] { display: none; } +.github-stars-badge .gh-mark { flex: none; width: 14px; height: 14px; color: currentColor; } +.github-stars-badge .gh-star { flex: none; width: 13px; height: 13px; fill: #ffd60a; stroke: #ff9f0a; stroke-width: 1.2; stroke-linejoin: round; filter: drop-shadow(0 0 4px rgba(255,214,10,.7)); } +.github-stars-badge strong { display: inline-block; color: #373737; font-size: .72rem; font-weight: 750; } +.github-stars-badge strong.is-updated { animation: github-star-update .5s ease-out; } +.github-stars-badge:hover { border-color: var(--blue); color: var(--ink); background: var(--surface); text-decoration: none; transform: translateY(-1px); } +@keyframes github-star-update { 0% { color: #111; text-shadow: 0 0 10px rgba(43,80,240,.25); transform: translateY(-1px); } 100% { color: #373737; text-shadow: none; transform: translateY(0); } } +.trending-cta { flex: 0 0 auto; position: relative; overflow: hidden; width: 340px; max-width: 340px; display: flex; align-items: center; gap: 14px; padding: 16px 20px; border: 0; border-radius: 16px; color: white; background: linear-gradient(135deg,#6c5ce7 0%,#00d4a8 100%); box-shadow: 0 14px 32px -10px color-mix(in srgb,#6c5ce7 65%,transparent), 0 4px 12px -4px color-mix(in srgb,#00d4a8 45%,transparent); font-family: var(--shared-font-sans); font-feature-settings: "cv11", "ss01", "ss03"; line-height: 1.55; text-decoration: none; transition: transform .2s, box-shadow .2s; } +.trending-cta:hover { color: white; text-decoration: none; transform: translateY(-3px) scale(1.02); box-shadow: 0 20px 46px -10px color-mix(in srgb,#6c5ce7 78%,transparent), 0 8px 18px -4px color-mix(in srgb,#00d4a8 55%,transparent); } +.trending-cta::before { content: ""; position: absolute; top: 0; left: -60%; width: 38%; height: 100%; background: linear-gradient(100deg,transparent,rgba(255,255,255,.42),transparent); transform: skewX(-18deg); animation: trending-cta-shine 3.4s ease-in-out infinite; } +@keyframes trending-cta-shine { 0%,58% { left: -60%; } 100% { left: 135%; } } +.trending-cta-icon { position: relative; z-index: 1; flex: 0 0 auto; width: 46px; height: 46px; display: grid; place-items: center; border-radius: 12px; background: rgba(255,255,255,.22); font-size: 23px; } +.trending-cta-copy { position: relative; z-index: 1; min-width: 0; display: flex; flex-direction: column; } +.trending-cta-badge { align-self: flex-start; display: inline-flex; align-items: center; gap: 5px; margin-bottom: 4px; padding: 2px 8px; border-radius: 999px; background: rgba(255,255,255,.22); font-size: 9.5px; font-weight: 800; letter-spacing: .7px; text-transform: uppercase; } +.trending-cta-dot { width: 6px; height: 6px; border-radius: 50%; background: white; animation: trending-cta-pulse 1.6s infinite; } +@keyframes trending-cta-pulse { 0% { box-shadow: 0 0 0 0 rgba(255,255,255,.65); } 70% { box-shadow: 0 0 0 7px rgba(255,255,255,0); } 100% { box-shadow: 0 0 0 0 rgba(255,255,255,0); } } +.trending-cta-title { color: white; font-size: 15.5px; font-weight: 800; letter-spacing: -.2px; line-height: 1.2; } +.trending-cta-subtitle { margin-top: 3px; color: rgba(255,255,255,.85); font-size: 12px; line-height: 1.35; } +.trending-cta-arrow { position: relative; z-index: 1; flex: 0 0 auto; color: white; transition: transform .2s; } +.trending-cta:hover .trending-cta-arrow { transform: translate(3px,-3px); } +.catalog-controls { display: flex; width: 100%; max-width: 100%; align-items: center; flex-wrap: wrap; gap: 14px; margin-top: 30px; } +.edition-toolbar { flex: 0 0 auto; margin-top: 0; display: flex; align-items: center; gap: 14px; } +.mode-switch { display: inline-flex; flex: 0 0 auto; padding: 4px; border: 1px solid var(--border-soft); border-radius: 11px; background: var(--surface-soft); } +.mode-switch button { min-width: 82px; padding: 8px 14px; border: 0; border-radius: 8px; color: var(--muted); background: transparent; font: inherit; font-size: .86rem; font-weight: 750; cursor: pointer; } +.mode-switch button[aria-pressed="true"] { color: white; background: var(--blue); box-shadow: 0 2px 8px var(--blue-shadow); } +.edition-control { display: inline-grid; grid-template-columns: 40px minmax(170px,auto) 40px; align-items: stretch; overflow: hidden; border: 1px solid var(--border-soft); border-radius: 11px; background: var(--surface); box-shadow: 0 1px 2px var(--card-shadow); } +.edition-step { display: grid; place-items: center; min-height: 40px; padding: 0; border: 0; color: var(--ink-2); background: var(--surface-soft); font: 800 1.15rem/1 inherit; cursor: pointer; } +.edition-step:first-of-type { border-right: 1px solid var(--line); } +.edition-step:last-of-type { border-left: 1px solid var(--line); } +.edition-step:hover:not(:disabled) { color: white; background: var(--blue); } +.edition-step:disabled { color: var(--disabled); cursor: default; } +select { min-width: 180px; padding: 10px 14px; border: 0; border-radius: 0; appearance: none; background: var(--surface); color: var(--ink); font: inherit; font-size: .86rem; font-weight: 750; text-align: center; text-align-last: center; cursor: pointer; } +.mode-switch button:focus-visible, .edition-step:focus-visible, select:focus-visible, .sort-toggle:focus-visible, .brand:focus-visible, .nav-links a:focus-visible, .github-link:focus-visible, .reels-accent:focus-visible, .github-stars-badge:focus-visible, .trending-cta:focus-visible, .metadata-chip:focus-visible { position: relative; z-index: 1; outline: 3px solid var(--focus-ring); outline-offset: 2px; } +.catalog-toolbar { flex: 1 1 488px; min-width: 0; width: auto; display: grid; grid-template-columns: minmax(260px,1fr) auto; gap: 12px; margin-top: 0; } +.search-control, .sort-control { min-height: 44px; border: 1px solid var(--border-soft); border-radius: 11px; background: var(--surface); box-shadow: 0 1px 2px var(--card-shadow); } +.search-control { display: flex; align-items: center; gap: 9px; padding: 0 13px; } +.search-icon { flex: 0 0 auto; width: 17px; height: 17px; color: var(--muted); } +.search-control input { width: 100%; min-width: 0; padding: 10px 0; border: 0; outline: 0; color: var(--ink); background: transparent; font: inherit; font-size: .9rem; } +.search-control input::placeholder { color: var(--muted); opacity: 1; } +.sort-control { display: inline-flex; align-items: center; gap: 4px; padding: 4px; background: var(--surface-soft); } +.sort-label { flex: 0 0 auto; padding: 0 9px; color: var(--muted); font-size: .7rem; font-weight: 800; letter-spacing: .065em; text-transform: uppercase; } +.sort-toggle { min-width: 88px; min-height: 34px; display: inline-flex; align-items: center; justify-content: center; gap: 8px; padding: 7px 11px; border: 1px solid transparent; border-radius: 8px; color: var(--muted); background: transparent; box-shadow: none; font: inherit; opacity: .68; cursor: pointer; transition: border-color .15s ease, box-shadow .15s ease, color .15s ease, background .15s ease, opacity .15s ease, transform .15s ease; } +.sort-toggle-votes { min-width: 116px; } +.sort-toggle-date { min-width: 88px; } +.sort-toggle[aria-pressed="true"] { border-color: var(--line); color: var(--ink-2); background: var(--surface); box-shadow: 0 1px 2px var(--card-shadow); opacity: 1; } +.sort-toggle:hover { border-color: var(--border); color: var(--ink); background: var(--surface); box-shadow: 0 3px 9px var(--card-shadow); opacity: 1; transform: translateY(-1px); } +.sort-toggle:active { transform: translateY(0); } +.sort-hf-mark { flex: 0 0 auto; width: 17px; height: 17px; } +.sort-toggle[aria-pressed="false"] .sort-hf-mark { opacity: .48; filter: grayscale(1); } +.sort-toggle-label { font-size: .82rem; font-weight: 780; letter-spacing: -.01em; } +.sort-arrows { width: 9px; display: inline-grid; justify-items: center; gap: 3px; color: var(--ink); } +.sort-arrow { width: 0; height: 0; opacity: .22; transition: opacity .15s ease, transform .15s ease; } +.sort-arrow-up { border-right: 4px solid transparent; border-bottom: 5px solid currentColor; border-left: 4px solid transparent; } +.sort-arrow-down { border-top: 5px solid currentColor; border-right: 4px solid transparent; border-left: 4px solid transparent; } +.sort-toggle[aria-pressed="true"][data-sort$="-desc"] .sort-arrow-up, +.sort-toggle[aria-pressed="true"][data-sort$="-asc"] .sort-arrow-down { opacity: .92; transform: scale(1.08); } +.search-control:focus-within, .sort-control:focus-within { border-color: var(--blue); box-shadow: 0 0 0 3px var(--focus-ring); } +.paper-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; gap: 28px; } +.paper-grid[hidden], .paper-card[hidden] { display: none; } +.paper-card.is-stream-entering { transform-origin: 50% 100%; will-change: opacity, filter, transform; animation: paper-stream-enter .72s cubic-bezier(.16,1,.3,1) both; animation-delay: calc(var(--stream-order, 0) * 110ms); } +@keyframes paper-stream-enter { from { opacity: 0; filter: blur(3px); transform: translateY(38px) scale(.975); } to { opacity: 1; filter: blur(0); transform: translateY(0) scale(1); } } +.paper-stream-sentinel { min-height: 58px; display: flex; align-items: center; justify-content: center; gap: 9px; color: var(--muted); font-size: .74rem; font-weight: 680; } +.paper-stream-sentinel[hidden] { display: none; } +.paper-stream-dots { display: inline-flex; align-items: center; gap: 4px; } +.paper-stream-dots i { width: 5px; height: 5px; border-radius: 50%; background: var(--blue); animation: paper-stream-pulse .9s ease-in-out infinite alternate; } +.paper-stream-dots i:nth-child(2) { animation-delay: .14s; } +.paper-stream-dots i:nth-child(3) { animation-delay: .28s; } +@keyframes paper-stream-pulse { from { opacity: .28; transform: translateY(1px); } to { opacity: .92; transform: translateY(-2px); } } +.paper-card { position: relative; overflow: hidden; display: flex; flex-direction: column; min-width: 0; border: 1px solid var(--line); border-radius: 18px; background: var(--surface); box-shadow: 0 8px 28px var(--card-shadow); } +.poster-shell { position: relative; aspect-ratio: 2016 / 1210; overflow: hidden; border-radius: 17px 17px 0 0; background: linear-gradient(145deg, var(--poster-start), var(--poster-end)); } +.poster-link { position: absolute; inset: 0; display: block; overflow: hidden; } +.poster { width: 100%; height: 100%; object-fit: contain; display: block; transition: transform .25s ease, filter .2s ease; } +.poster-link:hover .poster { transform: scale(1.015); } +.paper-body { padding: 24px; } +.metadata-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: .76rem; } +.metadata-chip { display: inline-flex; align-items: center; gap: 5px; min-height: 27px; padding: 4px 8px; border: 1px solid var(--border-soft); border-radius: 999px; color: var(--ink-2); background: var(--surface-soft); text-decoration: none; font-weight: 760; font-variant-numeric: tabular-nums; letter-spacing: 0; transition: color .15s ease, border-color .15s ease, background .15s ease; } +.metadata-chip:hover { border-color: var(--blue); color: var(--blue); background: var(--surface); } +.github-chip[hidden] { display: none; } +.paper-published { margin-left: auto; color: var(--muted); font-size: .74rem; font-weight: 620; font-variant-numeric: tabular-nums; white-space: nowrap; } +.brand-inline-icon, .github-mark { flex: 0 0 auto; width: 14px; height: 14px; } +.brand-inline-icon { display: inline-block; background: currentColor; -webkit-mask-position: center; mask-position: center; -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; -webkit-mask-size: contain; mask-size: contain; } +.hf-mark { color: var(--hf-icon); -webkit-mask-image: url("icons/huggingface.svg"); mask-image: url("icons/huggingface.svg"); } +.arxiv-mark { color: var(--arxiv-icon); -webkit-mask-image: url("icons/arxiv.svg"); mask-image: url("icons/arxiv.svg"); } +.metadata-chip:hover .hf-mark, .metadata-chip:hover .arxiv-mark { color: var(--blue); } +.github-star-count, .hf-vote-count, .paper-id { font-size: .74rem; font-weight: 760; } +.paper-title-wrap { position: relative; margin-top: 12px; } +.paper-title { display: -webkit-box; overflow: hidden; margin: 0; font-size: 1.35rem; font-weight: 760; line-height: 1.22; letter-spacing: -.015em; text-overflow: ellipsis; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; } +.paper-title-tooltip { position: absolute; top: 0; right: 0; bottom: auto; left: 0; z-index: 4; margin: 0; padding: 11px 13px; border: 1px solid rgba(255,255,255,.16); border-radius: 10px; color: #fff; background: rgba(22,24,30,.96); box-shadow: 0 14px 36px rgba(0,0,0,.28); font-size: .86rem; font-weight: 650; line-height: 1.4; letter-spacing: 0; opacity: 0; visibility: hidden; transform: translateY(6px); pointer-events: none; transition: opacity .18s ease, visibility .18s ease, transform .18s ease; } +@media (hover: hover) and (pointer: fine) { + .paper-card.has-clamped-title:hover { overflow: visible; z-index: 5; } + .paper-card.has-clamped-title:hover .paper-title-tooltip { opacity: 1; visibility: visible; transform: translateY(0); } +} +.button { display: inline-flex; align-items: center; justify-content: center; min-height: 38px; padding: 8px 13px; border: 1px solid var(--border); border-radius: 9px; text-decoration: none; font-size: .84rem; font-weight: 700; } +.button:hover { border-color: var(--blue); } +.message-card { padding: 46px; border: 1px dashed var(--border); border-radius: 18px; color: var(--muted); text-align: center; background: var(--surface); } +.paper-viewer { overflow: hidden; width: 100vw; max-width: none; height: 100vh; max-height: none; margin: 0; padding: 0; border: 0; background: var(--paper); } +.paper-viewer::backdrop { background: rgba(13,13,15,.55); } +.viewer-shell { display: grid; grid-template-rows: auto minmax(0,1fr); width: 100%; height: 100%; } +.viewer-header { min-height: 76px; display: grid; grid-template-columns: 110px minmax(0,1fr) auto auto; align-items: center; gap: 20px; padding: 10px 24px; border-bottom: 1px solid var(--line); background: var(--surface); } +.viewer-back { justify-self: start; border: 0; color: var(--blue); background: transparent; font: inherit; font-weight: 720; cursor: pointer; } +.viewer-heading { min-width: 0; text-align: left; } +.viewer-heading span { color: var(--blue); font-size: .7rem; font-weight: 760; text-transform: uppercase; letter-spacing: .12em; } +.viewer-heading h2 { overflow: hidden; margin: 4px 0 0; font-size: 1.12rem; font-weight: 760; white-space: nowrap; text-overflow: ellipsis; } +.viewer-actions { display: flex; align-items: center; justify-content: flex-end; gap: 9px; } +.viewer-external { display: inline-flex; min-height: 38px; align-items: center; padding: 8px 13px; border-radius: 9px; color: white; background: var(--blue); text-decoration: none; font-size: .82rem; font-weight: 720; white-space: nowrap; } +.viewer-tabs { display: inline-flex; padding: 4px; border-radius: 10px; background: var(--surface-soft); } +.viewer-tabs button { min-width: 92px; padding: 8px 18px; border: 0; border-radius: 7px; color: var(--muted); background: transparent; font: inherit; font-size: .86rem; font-weight: 720; cursor: pointer; } +.viewer-tabs button[aria-selected="true"] { color: white; background: var(--blue); } +.viewer-stage { position: relative; min-height: 0; background: var(--viewer-stage); } +.viewer-stage section { width: 100%; height: 100%; border: 0; } +.viewer-stage iframe { display: block; width: 100%; height: 100%; border: 0; } +#paper-panel { display: grid; grid-template-rows: auto minmax(0,1fr); } +#paper-panel[hidden] { display: none; } +.pdf-fallback { padding: 8px 18px; color: var(--muted); background: var(--fallback); border-bottom: 1px solid var(--line); text-align: center; font-size: .78rem; } +.pdf-fallback a { color: var(--blue); font-weight: 700; } +.viewer-loading { position: absolute; inset: 0; z-index: 2; display: grid; place-items: center; color: var(--muted); background: var(--paper); } +.viewer-loading[hidden] { display: none; } +body.viewer-open { overflow: hidden; } +.site-footer { + font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace; +} +.footer-inner { + width: min(1180px, 90vw); + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + grid-template-areas: "brand links" "notice notice" "meta meta" "source source"; + align-items: center; + gap: 18px 28px; + margin: 0 auto; + padding: 26px 4px 34px; + border-top: 1px solid var(--line); + color: var(--muted); + font-size: .72rem; +} +.footer-brand { + grid-area: brand; + display: flex; + flex-direction: column; + gap: 5px; +} +.footer-brand span { color: var(--ink-2); font-size: .78rem; font-weight: 800; } +.footer-brand p { margin: 0; color: var(--muted); font-size: .72rem; } +.footer-links { + grid-area: links; + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + gap: 8px 16px; +} +.footer-links a { + color: var(--muted); + font-size: .66rem; + font-weight: 700; + text-decoration: none; + transition: color .15s ease; +} +.footer-links a:hover { color: var(--blue); } +.footer-notice { + grid-area: notice; + margin: 0; + padding-top: 16px; + border-top: 1px solid var(--line); + color: var(--muted); + font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", sans-serif; + font-size: .7rem; + line-height: 1.55; +} +.footer-notice span { margin-right: 6px; color: var(--blue); } +.footer-meta { + grid-area: meta; + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px 24px; + color: var(--muted); + font-size: .65rem; +} +.footer-meta a, .footer-source a { + color: var(--muted); + text-decoration: none; + transition: color .15s ease; +} +.footer-meta a:hover, .footer-source a:hover { color: var(--blue); } +.footer-support { text-align: center; } +.footer-source { + grid-area: source; + margin: -8px 0 0; + color: var(--muted); + font-size: .65rem; +} +@media (max-width: 1540px) { + .topbar { padding-right: clamp(24px, calc(776px - 50vw), 190px); } +} +@media (max-width: 820px) { + .nav-links { display: none; } + .title-row { flex-direction: column; align-items: flex-start; } + .trending-cta { width: 100%; max-width: none; } +} +@media (max-width: 780px) { + main { width: min(92vw, 620px); } + .paper-grid { grid-template-columns: 1fr; } + .catalog-controls { flex-direction: column; align-items: stretch; } + .edition-toolbar { width: 100%; align-items: flex-start; flex-wrap: wrap; } + .mode-switch { width: 100%; } + .mode-switch button { flex: 1; } + .edition-control { flex: 1 1 auto; } + .edition-control select { width: 100%; min-width: 0; } + .catalog-toolbar { flex: 0 1 auto; width: 100%; grid-template-columns: 1fr; } + .sort-toggle { flex: 1; min-width: 0; } + .viewer-header { min-height: 104px; grid-template-columns: auto minmax(0,1fr) auto; grid-template-areas: "back heading actions" "tabs tabs tabs"; gap: 8px; padding: 8px 14px; } + .viewer-back { grid-area: back; } + .viewer-heading { grid-area: heading; text-align: center; } + .viewer-tabs { grid-area: tabs; justify-self: center; } + .viewer-actions { grid-area: actions; } + .viewer-back, .viewer-external { font-size: 0; } + .viewer-back span, .viewer-external span { font-size: 1.15rem; } + .viewer-heading h2 { font-size: .9rem; } + .viewer-actions { gap: 5px; } + .viewer-actions .button { width: 36px; min-height: 36px; padding: 0; font-size: 0; } + .viewer-actions .button::after { content: "Ask"; font-size: .68rem; letter-spacing: .02em; } + .viewer-tabs button { min-width: 72px; padding: 7px 12px; } + .footer-inner { + grid-template-columns: 1fr; + grid-template-areas: "brand" "links" "notice" "meta" "source"; + align-items: flex-start; + } + .footer-links { justify-content: flex-start; } + .footer-meta { flex-direction: column; align-items: flex-start; gap: 6px; } + .footer-support { text-align: left; } +} +@media (max-width: 760px) { + .topbar { padding-right: 140px; } + main { padding-top: 38px; } + .title-row { padding-right: 18px; padding-left: 18px; } +} +@media (max-width: 480px) { + .topbar { gap: 12px; padding: 12px 140px 12px 16px; } + .github-link { width: auto; gap: 0; padding: 8px 10px; } + .github-link > span { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; } +} +@media (max-width: 520px) { + .brand span { display: none; } +} +@media (prefers-reduced-motion: reduce) { + .trending-cta::before, .trending-cta-dot, .paper-card.is-stream-entering, .paper-stream-dots i { animation: none; } + .paper-title-tooltip { transition: none; } +} diff --git a/docs/trending-paper/daily.js b/docs/trending-paper/daily.js new file mode 100644 index 0000000..46c1198 --- /dev/null +++ b/docs/trending-paper/daily.js @@ -0,0 +1,815 @@ +(() => { + "use strict"; + + const EDITIONS_API_URL = "https://researchstudio.site/daily-paper/api/editions?limit=31"; + const GITHUB_REPOSITORY_API_URL = "https://api.github.com/repos/microsoft/ResearchStudio"; + const CONTENT_REFRESH_INTERVAL_MS = 60000; + const region = document.querySelector("#paper-region"); + const grid = document.querySelector("#paper-grid"); + const loading = document.querySelector("#loading"); + const empty = document.querySelector("#empty"); + const select = document.querySelector("#edition-select"); + const selectLabel = document.querySelector("#edition-label"); + const olderButton = document.querySelector("#edition-older"); + const newerButton = document.querySelector("#edition-newer"); + const modeButtons = [...document.querySelectorAll("[data-mode]")]; + const sourceLink = document.querySelector("#source-link"); + const searchInput = document.querySelector("#paper-search"); + const sortButtons = [...document.querySelectorAll(".sort-toggle[data-sort]")]; + const streamSentinel = document.querySelector("#paper-stream-sentinel"); + const streamStatus = document.querySelector("#paper-stream-status"); + const template = document.querySelector("#paper-template"); + const viewer = document.querySelector("#paper-viewer"); + const viewerTitle = document.querySelector("#viewer-title"); + const viewerCategory = document.querySelector("#viewer-category"); + const viewerExternal = document.querySelector("#viewer-external"); + const viewerQA = document.querySelector("#viewer-qa"); + const viewerLoading = document.querySelector("#viewer-loading"); + const reelTab = document.querySelector("#reel-tab"); + const paperTab = document.querySelector("#paper-tab"); + const reelPanel = document.querySelector("#reel-panel"); + const paperPanel = document.querySelector("#paper-panel"); + const reelFrame = document.querySelector("#reel-frame"); + const paperFrame = document.querySelector("#paper-frame"); + const pdfFallbackLink = document.querySelector("#pdf-fallback-link"); + const githubStarsBadge = document.querySelector("#github-stars-badge"); + const githubStarsCount = document.querySelector("#github-stars-count"); + + let editions = []; + let activeMode = "weekly"; + let activeSelection = null; + let activeContext = null; + let activeRecords = []; + let selectedWeekly = null; + let selectedDaily = null; + let activePaper = null; + let reelReady = false; + let paperReady = false; + let streamObserver = null; + let streamTimer = null; + let streamPending = false; + let visiblePaperLimit = 4; + let editionsSignature = ""; + let refreshInFlight = null; + + const PAPER_BATCH_SIZE = 4; + const STREAM_REVEAL_DELAY_MS = 220; + const reducedMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)"); + const titleResizeObserver = "ResizeObserver" in window + ? new ResizeObserver(entries => { + for (const entry of entries) updateTitleClamp(entry.target); + }) + : null; + function setLink(element, url) { + element.href = url; + } + + function loadGitHubStars() { + if (!githubStarsBadge || !githubStarsCount) return; + fetch(GITHUB_REPOSITORY_API_URL, { + cache: "default", + credentials: "omit", + headers: {"Accept": "application/vnd.github+json"}, + mode: "cors" + }) + .then(response => { + if (!response.ok) throw new Error("GitHub stars unavailable"); + return response.json(); + }) + .then(data => { + const stars = data.stargazers_count; + if (!Number.isInteger(stars) || stars < 0) return; + const formatter = new Intl.NumberFormat("en"); + const starOffset = Math.floor(Math.random() * 3) + 1; + const initialStars = Math.max(0, stars - starOffset); + githubStarsCount.textContent = formatter.format(initialStars); + githubStarsBadge.title = `${formatter.format(stars)} GitHub stars`; + githubStarsBadge.setAttribute( + "aria-label", + `ResearchStudio on GitHub, ${formatter.format(stars)} stars` + ); + githubStarsBadge.hidden = false; + if (initialStars !== stars) { + setTimeout(() => { + githubStarsCount.textContent = formatter.format(stars); + githubStarsCount.classList.add("is-updated"); + setTimeout(() => githubStarsCount.classList.remove("is-updated"), 500); + }, 650); + } + }) + .catch(error => console.warn("Could not load GitHub stars:", error)); + } + + function githubRepoUrl(value) { + if (typeof value !== "string" || !value.trim()) return null; + try { + const raw = value.trim(); + const parsed = new URL( + /^https?:\/\//i.test(raw) ? raw : `https://github.com/${raw.replace(/^\/+/, "")}` + ); + if (parsed.protocol !== "https:" || !["github.com", "www.github.com"].includes(parsed.hostname.toLowerCase())) { + return null; + } + const parts = parsed.pathname.split("/").filter(Boolean); + if (parts.length < 2) return null; + const owner = encodeURIComponent(parts[0]); + const repo = encodeURIComponent(parts[1].replace(/\.git$/i, "")); + return `https://github.com/${owner}/${repo}`; + } catch (_error) { + return null; + } + } + + function compactCount(value) { + return new Intl.NumberFormat("en-US", { + notation: "compact", + maximumFractionDigits: 1 + }).format(value); + } + + function parseUtcDate(value) { + const parsed = new Date(`${value}T00:00:00Z`); + return Number.isNaN(parsed.valueOf()) ? null : parsed; + } + + function formatDate(value, options = {}) { + const parsed = parseUtcDate(value); + if (!parsed) return value || "Unknown date"; + return new Intl.DateTimeFormat("en-US", { + day: "numeric", + month: "short", + year: "numeric", + timeZone: "UTC", + ...options + }).format(parsed); + } + + function formatWeek(edition) { + const start = parseUtcDate(edition?.week_start); + const end = parseUtcDate(edition?.week_end); + if (!start || !end) return edition?.week_id || "Unknown date"; + const startLabel = formatDate(edition.week_start, {year: undefined}); + const endLabel = formatDate(edition.week_end, {year: undefined}); + return `${startLabel} – ${endLabel}`; + } + + function formatDaily(value) { + return formatDate(value, {year: undefined}); + } + + function editionForWeek(weekId) { + return editions.find(edition => edition.week_id === weekId) || null; + } + + function dailyDates() { + const values = new Set(); + for (const edition of editions) { + for (const paper of edition.papers || []) { + if (paper.daily_date) values.add(paper.daily_date); + } + } + return [...values].sort((left, right) => right.localeCompare(left)); + } + + function dailyDatesForWeek(weekId) { + const edition = editionForWeek(weekId); + if (!edition) return []; + return [...new Set((edition.papers || []).map(paper => paper.daily_date).filter(Boolean))] + .sort((left, right) => right.localeCompare(left)); + } + + function weekForDate(date) { + return editions.find(edition => + (edition.papers || []).some(paper => paper.daily_date === date) + ) || editions.find(edition => + edition.week_start <= date && date <= edition.week_end + ) || null; + } + + function selectionValues(mode) { + return mode === "weekly" + ? editions.map(edition => edition.week_id).filter(Boolean) + : dailyDates(); + } + + function populateSelector(mode) { + select.replaceChildren(); + selectLabel.textContent = "Date"; + const values = selectionValues(mode); + for (const value of values) { + const option = document.createElement("option"); + option.value = value; + option.textContent = mode === "weekly" ? formatWeek(editionForWeek(value)) : formatDaily(value); + select.appendChild(option); + } + select.disabled = values.length === 0; + return values; + } + + function recordsFor(mode, key) { + if (mode === "weekly") { + const edition = editionForWeek(key); + if (!edition) return []; + return (edition.papers || []).map(paper => ({edition, paper})); + } + + const records = []; + const seen = new Set(); + for (const edition of editions) { + for (const paper of edition.papers || []) { + if (paper.daily_date !== key) continue; + const identity = paper.token || paper.paper_id; + if (identity && seen.has(identity)) continue; + if (identity) seen.add(identity); + records.push({edition, paper}); + } + } + records.sort((left, right) => { + const votes = (Number(right.paper.hf_upvotes_at_snapshot) || 0) + - (Number(left.paper.hf_upvotes_at_snapshot) || 0); + if (votes) return votes; + return String(left.paper.paper_id).localeCompare(String(right.paper.paper_id)); + }); + return records.map((record, index) => ({ + edition: record.edition, + paper: {...record.paper, rank: index + 1} + })); + } + + function updateEditionNavigation() { + const values = selectionValues(activeMode); + const index = values.indexOf(activeSelection); + olderButton.disabled = index < 0 || index >= values.length - 1; + newerButton.disabled = index <= 0; + } + + function contextFor(mode, key) { + if (mode === "weekly") { + const edition = editionForWeek(key); + return { + mode, + key, + edition, + sourceUrl: edition?.source_url || `https://huggingface.co/papers/week/${encodeURIComponent(key)}` + }; + } + return { + mode, + key, + edition: weekForDate(key), + sourceUrl: `https://huggingface.co/papers/date/${encodeURIComponent(key)}` + }; + } + + function normalizeSearch(value) { + return String(value || "") + .normalize("NFKC") + .toLocaleLowerCase() + .replace(/\s+/g, " ") + .trim(); + } + + function resetPaperStream() { + if (streamTimer !== null) window.clearTimeout(streamTimer); + streamTimer = null; + streamPending = false; + streamObserver?.unobserve(streamSentinel); + visiblePaperLimit = "IntersectionObserver" in window + ? PAPER_BATCH_SIZE + : Number.POSITIVE_INFINITY; + } + + function updatePaperStream(matchedCount, displayedCount) { + const hasMore = displayedCount < matchedCount; + streamSentinel.hidden = !hasMore; + streamStatus.textContent = matchedCount + ? `Showing ${displayedCount} of ${matchedCount} papers.` + : "No papers to show."; + streamObserver?.unobserve(streamSentinel); + if (hasMore && !streamPending) streamObserver?.observe(streamSentinel); + } + + function ensurePaperStreamObserver() { + if (!("IntersectionObserver" in window) || streamObserver) return; + streamObserver = new IntersectionObserver(entries => { + if (!entries.some(entry => entry.isIntersecting) || streamPending || streamSentinel.hidden) return; + streamPending = true; + streamObserver.unobserve(streamSentinel); + streamTimer = window.setTimeout(() => { + streamTimer = null; + streamPending = false; + visiblePaperLimit += PAPER_BATCH_SIZE; + applyCatalogControls(); + }, reducedMotion?.matches ? 0 : STREAM_REVEAL_DELAY_MS); + }, {rootMargin: "0px 0px -48px 0px", threshold: 0.01}); + } + + function updateSearchPaperCount(totalCount) { + const paperCountLabel = `${totalCount} ${totalCount === 1 ? "paper" : "papers"}`; + searchInput.placeholder = `Search title or arXiv ID (${paperCountLabel})`; + searchInput.setAttribute( + "aria-label", + `Search title or arXiv ID in this edition (${paperCountLabel})` + ); + } + + function updateTitleClamp(title) { + if (!title) return; + const card = title.closest(".paper-card"); + if (!card || card.hidden || !title.clientHeight) { + card?.classList.remove("has-clamped-title"); + return; + } + card.classList.toggle( + "has-clamped-title", + title.scrollHeight > title.clientHeight + 1 + ); + } + + function applyCatalogControls({resetStream = false} = {}) { + if (resetStream) resetPaperStream(); + const query = normalizeSearch(searchInput.value); + const activeSort = sortButtons.find(button => button.getAttribute("aria-pressed") === "true"); + const sortValue = activeSort?.dataset.sort || "votes-desc"; + const ascending = sortValue.endsWith("-asc"); + const criterion = sortValue.startsWith("date-") ? "date" : "votes"; + const cards = [...grid.querySelectorAll(".paper-card")]; + cards.sort((left, right) => { + if (criterion === "date") { + const dateDifference = String(left.dataset.dailyDate || "") + .localeCompare(String(right.dataset.dailyDate || "")); + if (dateDifference) return ascending ? dateDifference : -dateDifference; + const voteDifference = Number(right.dataset.hfVotes) - Number(left.dataset.hfVotes); + if (voteDifference) return voteDifference; + return Number(left.dataset.originalOrder) - Number(right.dataset.originalOrder); + } + const voteDifference = Number(left.dataset.hfVotes) - Number(right.dataset.hfVotes); + if (voteDifference) return ascending ? voteDifference : -voteDifference; + const dateDifference = String(right.dataset.publishedDate || "") + .localeCompare(String(left.dataset.publishedDate || "")); + if (dateDifference) return dateDifference; + return Number(left.dataset.originalOrder) - Number(right.dataset.originalOrder); + }); + + let matchedCount = 0; + let displayedCount = 0; + let enteringIndex = 0; + for (const card of cards) { + const matches = !query || card.dataset.searchText.includes(query); + const shouldDisplay = matches && matchedCount < visiblePaperLimit; + if (matches) matchedCount += 1; + const wasHidden = card.hidden; + card.hidden = !shouldDisplay; + if (shouldDisplay) { + updateTitleClamp(card.querySelector(".paper-title")); + displayedCount += 1; + const poster = card.querySelector(".poster"); + if (!poster.hasAttribute("src")) poster.src = poster.dataset.src; + if (wasHidden && !reducedMotion?.matches) { + card.style.setProperty("--stream-order", String(enteringIndex)); + enteringIndex += 1; + card.classList.remove("is-stream-entering"); + void card.offsetWidth; + card.classList.add("is-stream-entering"); + card.addEventListener( + "animationend", + () => card.classList.remove("is-stream-entering"), + {once: true} + ); + } else if (wasHidden) card.classList.remove("is-stream-entering"); + } else { + card.classList.remove("has-clamped-title"); + card.classList.remove("is-stream-entering"); + } + grid.appendChild(card); + } + + const totalCount = cards.length; + updateSearchPaperCount(totalCount); + loading.hidden = true; + empty.textContent = totalCount + ? "No papers match this search." + : "No published papers are available for this selection yet."; + empty.hidden = matchedCount > 0; + grid.hidden = matchedCount === 0; + updatePaperStream(matchedCount, displayedCount); + } + + function updateSortButton(button, active) { + const value = button.dataset.sort || "votes-desc"; + const ascending = value.endsWith("-asc"); + const isDate = value.startsWith("date-"); + const current = isDate + ? (ascending ? "oldest first" : "newest first") + : (ascending ? "least voted first" : "most voted first"); + const next = isDate + ? (ascending ? "newest first" : "oldest first") + : (ascending ? "most voted first" : "least voted first"); + const criterion = isDate ? "published date" : "Hugging Face votes"; + button.setAttribute("aria-pressed", active ? "true" : "false"); + button.title = isDate + ? `${ascending ? "Oldest" : "Newest"} published first` + : `${ascending ? "Least" : "Most"} voted first`; + button.setAttribute( + "aria-label", + active + ? `Sorted by ${criterion}, ${current}. Activate for ${next}.` + : `Sort by ${criterion}, ${current}.` + ); + } + + function setSortState(value) { + const criterion = value.startsWith("date-") ? "date-" : "votes-"; + const target = sortButtons.find(button => button.dataset.sort?.startsWith(criterion)); + if (!target) return; + target.dataset.sort = value; + for (const button of sortButtons) updateSortButton(button, button === target); + } + + function handleSortClick(event) { + const button = event.currentTarget; + const active = button.getAttribute("aria-pressed") === "true"; + let value = button.dataset.sort || "votes-desc"; + if (active) value = value.endsWith("-asc") + ? value.replace(/-asc$/, "-desc") + : value.replace(/-desc$/, "-asc"); + setSortState(value); + applyCatalogControls({resetStream: true}); + } + + function renderRecords(records, context, preservedVisibleLimit = null) { + resetPaperStream(); + if (preservedVisibleLimit !== null) { + visiblePaperLimit = Math.max(PAPER_BATCH_SIZE, preservedVisibleLimit); + } + titleResizeObserver?.disconnect(); + grid.replaceChildren(); + sourceLink.href = context.sourceUrl; + activeRecords = records; + activeContext = context; + + for (const [recordIndex, {edition, paper}] of records.entries()) { + const fragment = template.content.cloneNode(true); + const card = fragment.querySelector(".paper-card"); + card.hidden = true; + card.dataset.originalOrder = String(recordIndex); + card.dataset.searchText = normalizeSearch(`${paper.title} ${paper.paper_id}`); + card.dataset.paperId = paper.paper_id; + card.dataset.publishedDate = paper.published_date || paper.daily_date || ""; + card.dataset.dailyDate = paper.daily_date || ""; + + const hfVotes = Number(paper.hf_upvotes_at_snapshot) || 0; + card.dataset.hfVotes = String(hfVotes); + const hfScore = fragment.querySelector(".hf-score"); + hfScore.querySelector(".hf-vote-count").textContent = hfVotes.toLocaleString("en-US"); + hfScore.setAttribute( + "aria-label", + `${hfVotes} Huggingface votes; open this paper on Hugging Face` + ); + setLink( + hfScore, + paper.hf_url || `https://huggingface.co/papers/${encodeURIComponent(paper.paper_id)}` + ); + + const arxivChip = fragment.querySelector(".arxiv-chip"); + arxivChip.querySelector(".paper-id").textContent = paper.paper_id; + arxivChip.setAttribute("aria-label", `Open ${paper.paper_id} on arXiv`); + setLink( + arxivChip, + paper.arxiv_url || `https://arxiv.org/abs/${encodeURIComponent(paper.paper_id)}` + ); + + const githubChip = fragment.querySelector(".github-chip"); + const githubUrl = githubRepoUrl(paper.github_repo); + const githubStarValue = paper.github_stars_at_snapshot; + const githubStars = githubStarValue === null || githubStarValue === undefined + ? Number.NaN + : Number(githubStarValue); + if (githubUrl && Number.isFinite(githubStars) && githubStars >= 0) { + githubChip.href = githubUrl; + githubChip.hidden = false; + githubChip.querySelector(".github-star-count").textContent = compactCount(githubStars); + githubChip.setAttribute( + "aria-label", + `Open the paper repository on GitHub; ${githubStars.toLocaleString("en-US")} stars` + ); + } + const displayDate = paper.daily_date; + const publishedTime = fragment.querySelector(".paper-published"); + publishedTime.dateTime = displayDate; + publishedTime.textContent = `Published on ${formatDaily(displayDate)}`; + const paperTitle = fragment.querySelector(".paper-title"); + paperTitle.textContent = paper.title; + fragment.querySelector(".paper-title-tooltip").textContent = paper.title; + const poster = fragment.querySelector(".poster"); + poster.dataset.src = paper.poster_url; + poster.alt = `Preview for ${paper.title}`; + setLink(fragment.querySelector(".poster-link"), paper.reel_url); + + for (const opener of fragment.querySelectorAll("[data-open-view]")) { + opener.addEventListener("click", event => { + event.preventDefault(); + openViewer(edition, paper, opener.dataset.openView); + }); + } + grid.appendChild(fragment); + titleResizeObserver?.observe(paperTitle); + } + + applyCatalogControls(); + region.setAttribute("aria-busy", "false"); + } + + function writeSelectionUrl(clearViewer = true) { + const url = new URL(window.location.href); + url.searchParams.set("mode", activeMode); + if (activeMode === "weekly") { + if (activeSelection) url.searchParams.set("week", activeSelection); + else url.searchParams.delete("week"); + url.searchParams.delete("date"); + } else { + if (activeSelection) url.searchParams.set("date", activeSelection); + else url.searchParams.delete("date"); + url.searchParams.delete("week"); + } + if (clearViewer) { + url.searchParams.delete("paper"); + url.searchParams.delete("view"); + } + history.replaceState({}, "", url); + } + + function renderSelection(mode, key, updateUrl = true, preservedVisibleLimit = null) { + searchInput.value = ""; + activeMode = mode; + activeSelection = key; + select.value = key; + if (mode === "weekly") selectedWeekly = key; + else selectedDaily = key; + updateEditionNavigation(); + renderRecords(recordsFor(mode, key), contextFor(mode, key), preservedVisibleLimit); + if (updateUrl) writeSelectionUrl(); + } + + function switchMode(mode, updateUrl = true, preferredKey = null) { + activeMode = mode === "daily" ? "daily" : "weekly"; + for (const button of modeButtons) { + button.setAttribute("aria-pressed", button.dataset.mode === activeMode ? "true" : "false"); + } + const values = populateSelector(activeMode); + const remembered = activeMode === "weekly" ? selectedWeekly : selectedDaily; + const key = values.includes(preferredKey) + ? preferredKey + : values.includes(remembered) + ? remembered + : values[0] || null; + if (key) { + renderSelection(activeMode, key, updateUrl); + return; + } + activeSelection = null; + searchInput.value = ""; + updateEditionNavigation(); + renderRecords([], contextFor(activeMode, "")); + if (updateUrl) writeSelectionUrl(); + } + + function stepEdition(offset) { + const values = selectionValues(activeMode); + const index = values.indexOf(activeSelection); + const nextKey = values[index + offset]; + if (nextKey) renderSelection(activeMode, nextKey); + } + + function paperPdfUrl(url) { + const parsed = new URL(url, window.location.href); + parsed.hash = "view=FitH"; + return parsed.href; + } + + function showView(view, updateUrl = true) { + if (!activePaper) return; + const paperMode = view === "paper"; + reelTab.setAttribute("aria-selected", paperMode ? "false" : "true"); + paperTab.setAttribute("aria-selected", paperMode ? "true" : "false"); + reelPanel.hidden = paperMode; + paperPanel.hidden = !paperMode; + viewerLoading.hidden = paperMode ? paperReady : reelReady; + viewerLoading.textContent = paperMode ? "Loading paper…" : "Loading Reel…"; + if (paperMode && paperFrame.src === "about:blank") paperFrame.src = paperPdfUrl(activePaper.pdf_url); + viewerExternal.href = paperMode ? paperPdfUrl(activePaper.pdf_url) : activePaper.reel_url; + if (updateUrl) setPermalink(paperMode ? "paper" : "reel"); + } + + function setPermalink(view) { + writeSelectionUrl(false); + const url = new URL(window.location.href); + url.searchParams.set("paper", activePaper.paper_id); + url.searchParams.set("view", view); + history.replaceState({}, "", url); + } + + function viewerCategoryText(edition, paper) { + const rank = paper.rank === null || paper.rank === undefined ? "" : ` · #${paper.rank}`; + if (activeMode === "daily") return `${paper.daily_date}${rank}`; + return `${formatWeek(edition)} · ${formatDaily(paper.daily_date)}${rank}`; + } + + function openViewer(edition, paper, view = "reel", updateUrl = true) { + activePaper = paper; + reelReady = false; + paperReady = false; + viewerTitle.textContent = paper.title; + viewerCategory.textContent = viewerCategoryText(edition, paper); + viewerQA.href = paper.qa_url; + pdfFallbackLink.href = paperPdfUrl(paper.pdf_url); + reelFrame.src = paper.reel_url; + paperFrame.src = "about:blank"; + showView(view === "paper" ? "paper" : "reel", updateUrl); + if (!viewer.open) viewer.showModal(); + document.body.classList.add("viewer-open"); + } + + function closeViewer(updateUrl = true) { + viewer.close(); + document.body.classList.remove("viewer-open"); + reelFrame.src = "about:blank"; + paperFrame.src = "about:blank"; + activePaper = null; + reelReady = false; + paperReady = false; + if (updateUrl) writeSelectionUrl(); + } + + function restoreLocation() { + const params = new URLSearchParams(window.location.search); + const requestedMode = params.get("mode"); + const legacyDate = !requestedMode && params.has("date"); + const mode = requestedMode === "daily" || legacyDate ? "daily" : "weekly"; + const key = mode === "daily" ? params.get("date") : params.get("week"); + + switchMode(mode, false, key); + + const paperId = params.get("paper"); + const record = paperId + ? activeRecords.find(item => item.paper.paper_id === paperId) + : null; + if (record) { + openViewer(record.edition, record.paper, params.get("view") === "paper" ? "paper" : "reel", false); + setPermalink(params.get("view") === "paper" ? "paper" : "reel"); + } else { + writeSelectionUrl(); + } + } + + async function requestEditions({fresh = false} = {}) { + const response = await fetch(EDITIONS_API_URL, { + cache: fresh ? "no-store" : "default", + credentials: "omit", + headers: {"Accept": "application/json"}, + mode: "cors" + }); + if (!response.ok) throw new Error("edition request failed"); + return ((await response.json()).editions || []) + .filter(edition => edition && edition.week_id && String(edition.week_id) >= "2026-W34") + .sort((left, right) => String(right.week_end).localeCompare(String(left.week_end))); + } + + function syncOpenViewer(previousPaper, previousView) { + if (!previousPaper || !viewer.open) return; + const record = activeRecords.find(item => item.paper.paper_id === previousPaper.paper_id); + if (!record) { + closeViewer(false); + return; + } + + const nextPaper = record.paper; + const reelChanged = previousPaper.reel_url !== nextPaper.reel_url; + const pdfChanged = previousPaper.pdf_url !== nextPaper.pdf_url; + activePaper = nextPaper; + viewerTitle.textContent = nextPaper.title; + viewerCategory.textContent = viewerCategoryText(record.edition, nextPaper); + viewerQA.href = nextPaper.qa_url; + pdfFallbackLink.href = paperPdfUrl(nextPaper.pdf_url); + + if (reelChanged) { + reelReady = false; + reelFrame.src = nextPaper.reel_url; + } + if (pdfChanged && paperFrame.getAttribute("src") !== "about:blank") { + paperReady = false; + paperFrame.src = paperPdfUrl(nextPaper.pdf_url); + } + showView(previousView, false); + } + + async function refreshEditions() { + const updatedEditions = await requestEditions({fresh: true}); + if (!updatedEditions.length) return false; + const nextSignature = JSON.stringify(updatedEditions); + if (nextSignature === editionsSignature) return false; + + if (!editionsSignature) { + editions = updatedEditions; + editionsSignature = nextSignature; + restoreLocation(); + return true; + } + + const mode = activeMode; + const selection = activeSelection; + const query = searchInput.value; + const sort = sortButtons.find(button => button.getAttribute("aria-pressed") === "true")?.dataset.sort || "votes-desc"; + const preservedVisibleLimit = visiblePaperLimit; + const previousPaper = activePaper; + const previousView = paperTab.getAttribute("aria-selected") === "true" ? "paper" : "reel"; + editions = updatedEditions; + editionsSignature = nextSignature; + + const values = populateSelector(mode); + const nextSelection = values.includes(selection) ? selection : values[0]; + if (!nextSelection) return false; + renderSelection(mode, nextSelection, false, preservedVisibleLimit); + searchInput.value = query; + setSortState(sort); + applyCatalogControls(); + syncOpenViewer(previousPaper, previousView); + if (activePaper && viewer.open) setPermalink(previousView); + else writeSelectionUrl(); + return true; + } + + function refreshContent() { + if (refreshInFlight) return refreshInFlight; + refreshInFlight = refreshEditions() + .catch(() => false) + .finally(() => { + refreshInFlight = null; + }); + return refreshInFlight; + } + + function bindInteractions() { + select.addEventListener("change", () => renderSelection(activeMode, select.value)); + ensurePaperStreamObserver(); + searchInput.addEventListener("input", () => applyCatalogControls({resetStream: true})); + for (const button of sortButtons) button.addEventListener("click", handleSortClick); + olderButton.addEventListener("click", () => stepEdition(1)); + newerButton.addEventListener("click", () => stepEdition(-1)); + for (const button of modeButtons) { + button.addEventListener("click", () => { + const nextMode = button.dataset.mode; + if (nextMode === activeMode) return; + if (nextMode === "daily") { + const dates = dailyDatesForWeek(activeSelection); + switchMode("daily", true, dates.includes(selectedDaily) ? selectedDaily : dates[0]); + } else { + switchMode("weekly", true, weekForDate(activeSelection)?.week_id || selectedWeekly); + } + }); + } + } + + async function load() { + bindInteractions(); + try { + editions = await requestEditions(); + if (!editions.length) { + loading.hidden = true; + empty.hidden = false; + updateSearchPaperCount(0); + region.setAttribute("aria-busy", "false"); + return; + } + editionsSignature = JSON.stringify(editions); + restoreLocation(); + } catch (_error) { + loading.textContent = "Trending Paper is temporarily unavailable. Please try again shortly."; + region.setAttribute("aria-busy", "false"); + } + } + + document.querySelector("#viewer-back").addEventListener("click", () => closeViewer()); + viewer.addEventListener("cancel", event => { event.preventDefault(); closeViewer(); }); + reelTab.addEventListener("click", () => showView("reel")); + paperTab.addEventListener("click", () => showView("paper")); + reelFrame.addEventListener("load", () => { + if (reelFrame.getAttribute("src") === "about:blank") return; + reelReady = true; + if (!reelPanel.hidden) viewerLoading.hidden = true; + }); + paperFrame.addEventListener("load", () => { + if (paperFrame.getAttribute("src") === "about:blank") return; + paperReady = true; + if (!paperPanel.hidden) viewerLoading.hidden = true; + }); + window.setInterval(() => { + if (!document.hidden) refreshContent(); + }, CONTENT_REFRESH_INTERVAL_MS); + document.addEventListener("visibilitychange", () => { + if (!document.hidden) refreshContent(); + }); + window.addEventListener("online", refreshContent); + loadGitHubStars(); + load(); +})(); diff --git a/docs/trending-paper/fonts/InterVariable.woff2 b/docs/trending-paper/fonts/InterVariable.woff2 new file mode 100644 index 0000000..5a8d3e7 Binary files /dev/null and b/docs/trending-paper/fonts/InterVariable.woff2 differ diff --git a/docs/trending-paper/icons/arxiv.svg b/docs/trending-paper/icons/arxiv.svg new file mode 100644 index 0000000..6d4c8ef --- /dev/null +++ b/docs/trending-paper/icons/arxiv.svg @@ -0,0 +1 @@ + diff --git a/docs/trending-paper/icons/huggingface.svg b/docs/trending-paper/icons/huggingface.svg new file mode 100644 index 0000000..d51054f --- /dev/null +++ b/docs/trending-paper/icons/huggingface.svg @@ -0,0 +1 @@ + diff --git a/docs/trending-paper/index.html b/docs/trending-paper/index.html new file mode 100644 index 0000000..c2f155b --- /dev/null +++ b/docs/trending-paper/index.html @@ -0,0 +1,202 @@ + + +
+ + + + +Meet the standout research through ResearchStudio Reels
+Each paper transformed into a visual poster, a narrated video, and an accessible blog,so you can scan, watch, or read it your way.
+