From 11a50b4d2365f00d32bf344cfe54f616cf56b2ef Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 8 Nov 2026 01:10:00 +0000 Subject: [PATCH 1/3] =?UTF-8?q?docs:=20=E5=AE=98=E7=BD=91=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E6=B7=B1=E8=89=B2=E7=A7=91=E6=8A=80=E9=A3=8E=E5=B9=B6?= =?UTF-8?q?=E5=8A=A0=E5=85=A5=20GSAP=20=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 配色改为近黑底 + 紫青渐变,默认深色,浅色仍可切换;token 全部通过 WCAG AA 检查 - 样式重做:玻璃质感卡片、首屏极光与网格背景、渐变标题、终端窗口栏、鼠标跟随高光 - 新增 js/motion.js:Lenis 平滑滚动、首屏逐行/逐字入场、插画分层拼装与连线流动、 滚动入场、条形图生长与数字滚动、终端逐行打出、卡片 3D 倾斜、按钮磁吸、导航滑块、 阅读进度条、页眉滚动收起、页脚大字标 - 主题切换用 View Transitions 圆形展开;同站跳转加跨页面转场 - GSAP 3.15.1 与 Lenis 1.3.27 放在 docs/js/vendor 本地加载,由 sync_docs_chrome.py 写入各页 - 无脚本、脚本加载失败、减少动态效果时都退回静态页面,内容不会被藏住 - check_design_layer.py 放宽为:@keyframes 内与白名单里的装饰伪元素可以用 opacity, 静态内容仍然不允许 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01SdLhesahBsuQSbGntwoQFN --- docs/css/style.css | 897 ++++++++++++++++++++++------ docs/css/tokens.css | 287 +++++---- docs/index.html | 15 +- docs/js/main.js | 31 +- docs/js/motion.js | 470 +++++++++++++++ docs/js/theme-init.js | 24 +- docs/js/vendor/LICENSE-lenis.txt | 9 + docs/js/vendor/README.md | 15 + docs/js/vendor/ScrollTrigger.min.js | 11 + docs/js/vendor/SplitText.min.js | 11 + docs/js/vendor/gsap.min.js | 11 + docs/js/vendor/lenis.min.js | 5 + docs/ota/index.html | 13 +- docs/ota/releases.html | 13 +- docs/pyspos.html | 13 +- docs/requirements.html | 13 +- docs/spaceos6.html | 13 +- docs/spaceos7.html | 13 +- docs/spaceos8-download.html | 13 +- docs/spaceos8.html | 13 +- docs/spaceos9-download.html | 13 +- docs/spaceos9.html | 13 +- tools/checks/check_design_layer.py | 30 +- tools/sync_docs_chrome.py | 13 + 24 files changed, 1596 insertions(+), 363 deletions(-) create mode 100644 docs/js/motion.js create mode 100644 docs/js/vendor/LICENSE-lenis.txt create mode 100644 docs/js/vendor/README.md create mode 100644 docs/js/vendor/ScrollTrigger.min.js create mode 100644 docs/js/vendor/SplitText.min.js create mode 100644 docs/js/vendor/gsap.min.js create mode 100644 docs/js/vendor/lenis.min.js diff --git a/docs/css/style.css b/docs/css/style.css index 4c7062f..b2c0e90 100644 --- a/docs/css/style.css +++ b/docs/css/style.css @@ -1,8 +1,11 @@ /* ============================================================ GoutouStdio 站点样式(与 tokens.css 配合使用) - 版式:顶部导航 + 大标题首屏 + 分区正文 + 站点地图页脚。 - 没有动画和装饰图形,层次靠字号、留白和扁平色块。 + 风格:深色科技风。近黑底、玻璃质感卡片、紫→青渐变点缀、 + 首屏极光与网格背景、鼠标跟随高光。 + 动画由 js/motion.js(GSAP + ScrollTrigger + Lenis)驱动; + 这里只放静态外观、悬停过渡、@keyframes 和无脚本时的兜底。 组件只引用 --pg-*,不写死颜色与 4px 以上的尺寸(CI 会检查)。 + opacity 只出现在 @keyframes 和白名单里的装饰伪元素上(CI 会检查)。 ============================================================ */ /* ---------- 基础 ---------- */ @@ -15,8 +18,18 @@ html { scroll-padding-top: calc(var(--pg-header-h) + var(--pg-space-4)); } html[data-theme="dark"] { color-scheme: dark; } +@media (prefers-reduced-motion: no-preference) { + html { scroll-behavior: smooth; } +} + +/* Lenis 接管滚动时要求原生平滑滚动关闭 */ +html.lenis, html.lenis body { height: auto; scroll-behavior: auto; } +.lenis.lenis-stopped { overflow: clip; } +.lenis.lenis-smooth iframe { pointer-events: none; } +.lenis [data-lenis-prevent] { overscroll-behavior: contain; } body { + position: relative; margin: 0; font-family: var(--pg-font-sans); font-size: var(--pg-text-md); @@ -24,22 +37,39 @@ body { color: var(--pg-fg-1); background: var(--pg-bg-page); -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-feature-settings: "cv11", "ss01"; + text-rendering: optimizeLegibility; +} + +/* 全站颗粒质感:纯装饰的噪点层,不含文字,不接收指针事件 */ +body::after { + content: ""; + position: fixed; + inset: 0; + z-index: 100; + pointer-events: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); + opacity: var(--pg-noise-strength); } img { display: block; max-width: 100%; height: auto; } a { color: var(--pg-accent); - text-underline-offset: 0.2em; + text-decoration-thickness: var(--pg-hairline); + text-underline-offset: 0.22em; + transition: color var(--pg-dur-1) ease, text-decoration-color var(--pg-dur-1) ease; } a:hover { color: var(--pg-accent-hover); } :focus-visible { - outline: 2px solid var(--pg-focus); - outline-offset: 2px; + outline: var(--pg-stroke) solid var(--pg-focus); + outline-offset: 0.1875rem; + border-radius: var(--pg-radius-sm); } -::selection { background: var(--pg-accent-soft); } +::selection { color: var(--pg-on-ink); background: var(--pg-brand); } [hidden] { display: none !important; } @@ -59,11 +89,11 @@ a:hover { color: var(--pg-accent-hover); } position: absolute; left: var(--pg-space-3); top: -4rem; - z-index: 60; - padding: var(--pg-space-2) var(--pg-space-3); + z-index: 120; + padding: var(--pg-space-2) var(--pg-space-4); background: var(--pg-ink); color: var(--pg-on-ink); - border-radius: var(--pg-radius); + border-radius: var(--pg-radius-pill); } .skip-link:focus { top: var(--pg-space-2); color: var(--pg-on-ink); } @@ -76,45 +106,83 @@ a:hover { color: var(--pg-accent-hover); } .nowrap { white-space: nowrap; } +/* 滚动条:细、低调,跟随主题 */ +html { scrollbar-color: var(--pg-line-strong) var(--pg-bg-page); scrollbar-width: thin; } + /* ---------- 标题 ---------- */ h1, h2, h3, h4 { margin: 0; - font-family: var(--pg-font-serif); - font-weight: 400; + font-family: var(--pg-font-display); + font-weight: 650; line-height: var(--pg-leading-display); + letter-spacing: var(--pg-tracking-display); color: var(--pg-fg-1); text-wrap: balance; } -h1 { font-size: var(--pg-text-hero); letter-spacing: -0.01em; } +h1 { font-size: var(--pg-text-hero); font-weight: 700; } h2 { font-size: var(--pg-text-2xl); } -h3 { font-size: var(--pg-text-xl); line-height: var(--pg-leading-tight); } +h3 { font-size: var(--pg-text-xl); line-height: var(--pg-leading-tight); letter-spacing: -0.02em; } h4 { font-family: var(--pg-font-sans); font-size: var(--pg-text-md); font-weight: 700; line-height: var(--pg-leading-tight); + letter-spacing: 0; } p, ul, ol, dl, pre, table, figure { margin: 0; } ul, ol { padding-left: var(--pg-space-5); } li + li { margin-top: var(--pg-space-1); } +li::marker { color: var(--pg-fg-3); } strong { font-weight: 700; } .lede { max-width: var(--pg-measure); font-size: var(--pg-text-lg); + line-height: 1.7; color: var(--pg-fg-2); + text-wrap: pretty; } .small { font-size: var(--pg-text-sm); color: var(--pg-fg-3); } +/* 渐变文字:首屏标题用冷灰渐变,.grad 用品牌渐变。 + 脚本拆字后,渐变改由每一行 / 每个字自己承担,避免父元素的裁切留下静止的重影。 */ +.hero h1, +.hero h1.is-split .pg-line, +.hero h1.is-split .pg-char { + background: var(--pg-grad-text); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + -webkit-text-fill-color: transparent; +} +.hero h1.is-split { background: none; } +.hero h1 .grad, +.hero h1.is-split .grad { + background: var(--pg-grad-brand); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + -webkit-text-fill-color: transparent; +} +.pg-line-mask { padding-bottom: 0.08em; margin-bottom: -0.08em; } + /* ---------- 页眉 ---------- */ .site-header { position: sticky; top: 0; z-index: 50; - background: var(--pg-bg-page); - border-bottom: 1px solid var(--pg-line-1); -} + background: var(--pg-bg-glass); + border-bottom: var(--pg-hairline) solid var(--pg-line-glass); + -webkit-backdrop-filter: saturate(1.6) blur(var(--pg-blur)); + backdrop-filter: saturate(1.6) blur(var(--pg-blur)); + view-transition-name: site-header; + transition: transform var(--pg-dur-3) var(--pg-ease-out), + box-shadow var(--pg-dur-2) ease, background-color var(--pg-dur-2) ease; +} +.site-header.is-scrolled { box-shadow: var(--pg-shadow); } +.site-header.is-hidden { transform: translateY(-100%); } + .header-inner { display: flex; align-items: center; @@ -122,26 +190,31 @@ strong { font-weight: 700; } min-height: var(--pg-header-h); } .brand { - font-family: var(--pg-font-serif); - font-size: var(--pg-text-xl); + display: inline-flex; + align-items: center; + gap: var(--pg-space-2); + font-family: var(--pg-font-display); + font-size: var(--pg-text-lg); + font-weight: 700; + letter-spacing: -0.02em; line-height: 1; color: var(--pg-fg-1); text-decoration: none; white-space: nowrap; } -.brand { - display: inline-flex; - align-items: center; - gap: var(--pg-space-2); -} .brand:hover { color: var(--pg-fg-1); } -.brand-mark { width: 1.75rem; height: 1.75rem; flex: none; } +.brand-mark { width: 1.75rem; height: 1.75rem; flex: none; overflow: visible; } +.brand-mark rect { transform-box: fill-box; transform-origin: left center; transition: transform var(--pg-dur-3) var(--pg-ease-spring); } .brand-mark .m1 { fill: var(--pg-fg-1); } .brand-mark .m2 { fill: var(--pg-brand); } -.brand-mark .m3 { fill: var(--pg-line-strong); } +.brand-mark .m3 { fill: var(--pg-brand-2); } +.brand:hover .m1 { transform: scaleX(0.72); } +.brand:hover .m2 { transform: scaleX(0.86); transition-delay: 40ms; } +.brand:hover .m3 { transform: scaleX(0.6); transition-delay: 80ms; } -.site-nav { margin-left: auto; } +.site-nav { position: relative; margin-left: auto; } .site-nav > ul { + position: relative; display: flex; flex-wrap: wrap; align-items: center; @@ -153,24 +226,48 @@ strong { font-weight: 700; } .site-nav li + li { margin-top: 0; } .site-nav a, .site-nav summary { + position: relative; + z-index: 1; display: block; padding: var(--pg-space-2) var(--pg-space-3); font-size: var(--pg-text-sm); + font-weight: 500; color: var(--pg-fg-2); text-decoration: none; - border-radius: var(--pg-radius); + border-radius: var(--pg-radius-pill); cursor: pointer; + transition: color var(--pg-dur-1) ease, background-color var(--pg-dur-1) ease; } .site-nav a:hover, -.site-nav summary:hover { color: var(--pg-fg-1); background: var(--pg-bg-sunken); } +.site-nav summary:hover { color: var(--pg-fg-1); background: var(--pg-accent-soft); } +.site-nav.has-indicator > ul > li > a:hover, +.site-nav.has-indicator > ul > li > details > summary:hover { background: transparent; } .site-nav a[aria-current="page"], -.site-nav .menu.is-current > summary { - color: var(--pg-fg-1); - font-weight: 600; - text-decoration: underline; - text-decoration-color: var(--pg-brand); - text-decoration-thickness: 2px; - text-underline-offset: 0.4em; +.site-nav .menu.is-current > summary { color: var(--pg-fg-1); font-weight: 650; } +.site-nav > ul > li > a[aria-current="page"]::after, +.site-nav .menu.is-current > summary::after { + content: ""; + position: absolute; + left: 50%; + bottom: 0.125rem; + width: 1rem; + height: var(--pg-stroke); + margin-left: -0.5rem; + background: var(--pg-grad-brand); + border-radius: var(--pg-radius-pill); +} +/* 跟随鼠标滑动的导航高亮块,由脚本插入 */ +.nav-indicator { + position: absolute; + top: 0; + left: 0; + z-index: 0; + height: 0; + pointer-events: none; + background: var(--pg-accent-soft); + border: var(--pg-hairline) solid var(--pg-line-glass); + border-radius: var(--pg-radius-pill); + visibility: hidden; } /* 下拉菜单:原生 details,没有脚本也能点开 */ @@ -178,25 +275,31 @@ strong { font-weight: 700; } .menu > summary { list-style: none; } .menu > summary::-webkit-details-marker { display: none; } .menu > summary { display: flex; align-items: center; gap: var(--pg-space-1); } -.menu > summary .chev { width: 1em; height: 1em; vertical-align: 0; } +.menu > summary .chev { width: 1em; height: 1em; vertical-align: 0; transition: transform var(--pg-dur-2) var(--pg-ease-spring); } .menu[open] > summary .chev { transform: rotate(180deg); } -.menu > ul a { display: flex; align-items: center; gap: var(--pg-space-3); } -.menu > ul a .icon { color: var(--pg-fg-3); vertical-align: 0; } -.site-nav a .ext { width: 0.9em; height: 0.9em; margin-left: var(--pg-space-1); vertical-align: -0.1em; color: var(--pg-fg-3); } +.menu > ul a { display: flex; align-items: center; gap: var(--pg-space-3); border-radius: var(--pg-radius); } +.menu > ul a .icon { color: var(--pg-fg-3); vertical-align: 0; transition: color var(--pg-dur-1) ease, transform var(--pg-dur-2) var(--pg-ease-spring); } +.menu > ul a:hover .icon { color: var(--pg-accent); transform: scale(1.12); } +.site-nav a .ext { width: 0.9em; height: 0.9em; margin-left: var(--pg-space-1); vertical-align: -0.1em; color: var(--pg-fg-3); transition: transform var(--pg-dur-2) var(--pg-ease-out); } +.site-nav a:hover .ext { transform: translate(0.125rem, -0.125rem); } .menu > ul { position: absolute; - top: calc(100% + var(--pg-space-1)); - left: 0; + top: calc(100% + var(--pg-space-2)); + left: 50%; z-index: 55; - min-width: 13rem; + min-width: 14rem; margin: 0; padding: var(--pg-space-2); list-style: none; background: var(--pg-bg-raised); - border: 1px solid var(--pg-line-2); - border-radius: var(--pg-radius); - box-shadow: var(--pg-shadow); + border: var(--pg-hairline) solid var(--pg-line-2); + border-radius: var(--pg-radius-lg); + box-shadow: var(--pg-shadow-lg); + transform: translateX(-50%); + transform-origin: top center; } +.menu[open] > ul { animation: pg-pop var(--pg-dur-2) var(--pg-ease-out) both; } +.menu > ul li + li { margin-top: 0.125rem; } .menu > ul a { white-space: nowrap; } .header-actions { display: flex; align-items: center; gap: var(--pg-space-2); } @@ -204,63 +307,169 @@ strong { font-weight: 700; } display: inline-flex; align-items: center; justify-content: center; + width: 2.25rem; min-height: 2.25rem; - padding: 0 var(--pg-space-3); + padding: 0; font: inherit; font-size: var(--pg-text-sm); color: var(--pg-fg-2); - background: transparent; - border: 1px solid var(--pg-line-strong); - border-radius: var(--pg-radius); + background: var(--pg-bg-card); + border: var(--pg-hairline) solid var(--pg-line-strong); + border-radius: var(--pg-radius-pill); cursor: pointer; + transition: color var(--pg-dur-1) ease, border-color var(--pg-dur-1) ease, + transform var(--pg-dur-2) var(--pg-ease-spring), box-shadow var(--pg-dur-2) ease; } -.icon-btn:hover { color: var(--pg-fg-1); background: var(--pg-bg-sunken); } +.icon-btn:hover { color: var(--pg-fg-1); border-color: var(--pg-accent); box-shadow: var(--pg-glow); } +.icon-btn:active { transform: scale(0.92); } .nav-toggle { display: none; } -.icon-btn { width: 2.25rem; padding: 0; } .icon-btn .icon { vertical-align: 0; } .icon-sun { display: none; } body.theme-dark .icon-sun { display: inline; } body.theme-dark .icon-moon { display: none; } +.icon-sun, .icon-moon { animation: pg-spin-in var(--pg-dur-3) var(--pg-ease-spring) both; } .nav-toggle .icon-close { display: none; } .nav-toggle[aria-expanded="true"] .icon-menu { display: none; } .nav-toggle[aria-expanded="true"] .icon-close { display: inline; } +/* 阅读进度条,由脚本插入 */ +.scroll-progress { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 70; + height: var(--pg-stroke); + background: var(--pg-grad-brand); + transform: scaleX(0); + transform-origin: left center; + pointer-events: none; +} + /* ---------- 首屏 ---------- */ -.hero { padding-block: var(--pg-space-9) var(--pg-space-8); } +.hero { + position: relative; + isolation: isolate; + /* 极光比首屏宽,只在水平方向裁掉;竖直方向放开,让它延伸到半透明页眉底下 */ + overflow-x: clip; + padding-block: var(--pg-space-9) var(--pg-space-8); +} +/* 极光:三团径向渐变缓慢旋转;--ax/--ay 由脚本按鼠标位置写入做视差 */ +.hero::before { + content: ""; + position: absolute; + z-index: -2; + top: calc(var(--pg-header-h) * -1 - 10rem); + left: -15%; + right: -15%; + bottom: -6rem; + pointer-events: none; + background: + radial-gradient(38% 46% at 22% 32%, var(--pg-aurora-1), transparent 70%), + radial-gradient(32% 42% at 78% 22%, var(--pg-aurora-2), transparent 70%), + radial-gradient(28% 36% at 58% 74%, var(--pg-aurora-3), transparent 70%); + translate: calc(var(--ax, 0) * 2.5rem) calc(var(--ay, 0) * 2rem); + animation: pg-aurora var(--pg-dur-aurora) ease-in-out infinite alternate; +} +/* 网格:从顶部向下淡出 */ +.hero::after { + content: ""; + position: absolute; + z-index: -1; + top: calc(var(--pg-header-h) * -1); + left: 0; + right: 0; + bottom: 0; + pointer-events: none; + background-image: + linear-gradient(var(--pg-grid) 1px, transparent 1px), + linear-gradient(90deg, var(--pg-grid) 1px, transparent 1px); + background-size: 3.5rem 3.5rem; + background-position: center top; + -webkit-mask-image: radial-gradient(ellipse 75% 70% at 50% 0%, black 20%, transparent 75%); + mask-image: radial-gradient(ellipse 75% 70% at 50% 0%, black 20%, transparent 75%); +} .hero .lede { margin-top: var(--pg-space-5); } .hero .actions { margin-top: var(--pg-space-6); } .hero-split { display: grid; - grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); + grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr); gap: var(--pg-space-7); align-items: center; } -.hero-compact { padding-block: var(--pg-space-8) var(--pg-space-6); } -.hero-compact h1 { font-size: clamp(2.25rem, 4.2vw, 3rem); } -.hero-badge { margin-bottom: var(--pg-space-3); } -.hero-art { min-width: 0; } +.hero-compact { padding-block: var(--pg-space-8) var(--pg-space-7); } +.hero-compact h1, .hero-split h1 { font-size: var(--pg-text-page); } +.hero-split:has(.hero-art-svg) h1 { font-size: var(--pg-text-hero); } +.hero-badge { margin-bottom: var(--pg-space-4); } +.hero-art { position: relative; min-width: 0; } +/* 首屏截图背后的光晕 */ +.hero-art::before, +.hero-art-svg::before { + content: ""; + position: absolute; + z-index: -1; + inset: 10% 5%; + pointer-events: none; + background: radial-gradient(closest-side, var(--pg-glow-clay), transparent); +} + +/* 未开始动画前先藏住首屏,脚本接管后由 GSAP 控制;脚本 3 秒内没跑起来会自动撤掉这层 */ +html.motion:not(.motion-ready) .hero :is(h1, .lede, .actions, .hero-badge, .spec, figure) { visibility: hidden; } /* 版本、架构、许可等关键事实,一行排开 */ .spec { display: grid; - grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: var(--pg-space-4) var(--pg-space-5); - margin-top: var(--pg-space-6); + margin-top: var(--pg-space-7); padding-top: var(--pg-space-5); - border-top: 1px solid var(--pg-line-2); + border-top: var(--pg-hairline) solid var(--pg-line-2); } -.spec dt { font-size: var(--pg-text-xs); color: var(--pg-fg-3); } -.spec dd { margin: 0; font-size: var(--pg-text-sm); overflow-wrap: anywhere; } +.spec dt { + font-family: var(--pg-font-mono); + font-size: var(--pg-text-2xs); + letter-spacing: var(--pg-tracking-label); + text-transform: uppercase; + color: var(--pg-fg-3); +} +.spec dd { margin: var(--pg-space-1) 0 0; font-size: var(--pg-text-sm); font-weight: 600; overflow-wrap: anywhere; } /* ---------- 分区 ---------- */ .section { - padding-block: var(--pg-space-8); - border-top: 1px solid var(--pg-line-1); + position: relative; + padding-block: var(--pg-space-9); + /* 入场前的位移和 3D 翻转可能超出屏幕宽度,横向裁掉,免得窄屏出现横向滚动 */ + overflow-x: clip; +} +/* 分区顶部一条中间亮、两端淡出的渐变细线 */ +.section::before { + content: ""; + position: absolute; + top: 0; + left: 50%; + width: min(var(--pg-container), 100%); + height: 1px; + background: var(--pg-grad-line); + transform: translateX(-50%); + pointer-events: none; } .section-tint { background: var(--pg-bg-sunken); } -.section-head { max-width: var(--pg-measure); margin-bottom: var(--pg-space-6); } -.section-head .lede { margin-top: var(--pg-space-3); } -.section-head h2 + .lede { margin-top: var(--pg-space-3); } +.section-head { max-width: var(--pg-measure); margin-bottom: var(--pg-space-7); } +.section-head h2 + .lede { margin-top: var(--pg-space-4); } + +/* 小节标题上方一小段品牌渐变短线 */ +.section-head h2::before, +.split > h2::before, +.archive-title::before { + content: ""; + display: block; + width: 2.25rem; + height: 0.1875rem; + margin-bottom: var(--pg-space-4); + background: var(--pg-grad-brand); + border-radius: var(--pg-radius-pill); +} +.archive-title::before { display: inline-block; width: 1.25rem; margin: 0 var(--pg-space-2) 0 0; vertical-align: middle; } /* 左边小节标题,右边正文 */ .split { @@ -269,60 +478,141 @@ body.theme-dark .icon-moon { display: none; } gap: var(--pg-space-7); align-items: start; } -.split > h2 { font-size: var(--pg-text-xl); line-height: var(--pg-leading-tight); } +.split > h2 { + position: sticky; + top: calc(var(--pg-header-h) + var(--pg-space-5)); + font-size: var(--pg-text-xl); + line-height: var(--pg-leading-tight); + letter-spacing: -0.02em; +} .split > div { min-width: 0; } .stack > * + * { margin-top: var(--pg-space-4); } -.stack > h3 { margin-top: var(--pg-space-6); } +.stack > h3 { margin-top: var(--pg-space-7); } .stack > h3:first-child { margin-top: 0; } .prose { max-width: var(--pg-measure); } -/* ---------- 项目色块 ---------- */ +/* ---------- 通用卡片外观 ---------- */ +.tile, +.flow > li, +.release, +.table-wrap, +.notice { + background: var(--pg-bg-card); + border: var(--pg-hairline) solid var(--pg-line-glass); + box-shadow: inset 0 1px 0 var(--pg-line-hi); +} + +/* 鼠标跟随高光:脚本给卡片加 .spot 并写入 --mx/--my。 + ::before 是内部柔光,::after 是只照亮描边的光圈,两者都是纯装饰。 */ +.spot { position: relative; isolation: isolate; } +.spot::before, +.spot::after { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; + pointer-events: none; + opacity: 0; + transition: opacity var(--pg-dur-2) ease; +} +.spot::before { + z-index: -1; + background: radial-gradient(22rem circle at var(--mx, 50%) var(--my, 50%), var(--pg-spot), transparent 70%); +} +.spot::after { + z-index: 1; + padding: var(--pg-hairline); + background: radial-gradient(16rem circle at var(--mx, 50%) var(--my, 50%), var(--pg-brand), transparent 70%); + -webkit-mask: linear-gradient(black 0 0) content-box, linear-gradient(black 0 0); + -webkit-mask-composite: xor; + mask: linear-gradient(black 0 0) content-box exclude, linear-gradient(black 0 0); +} +.spot:hover::before, +.spot:hover::after { opacity: 1; } + +/* ---------- 项目卡片 ---------- */ .tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: var(--pg-space-4); + gap: var(--pg-space-5); + perspective: 75rem; } .tile { + --glow: var(--pg-glow-clay); display: flex; flex-direction: column; gap: var(--pg-space-3); min-width: 0; padding: var(--pg-space-6); - background: var(--pg-bg-raised); - border-radius: var(--pg-radius-lg); + overflow: hidden; + border-radius: var(--pg-radius-xl); + transform-style: preserve-3d; + transition: border-color var(--pg-dur-2) ease, box-shadow var(--pg-dur-2) ease; } +.tile:hover { border-color: var(--pg-line-strong); box-shadow: var(--pg-shadow-lg); } .tile h3 { font-size: var(--pg-text-2xl); } .tile p { color: var(--pg-fg-2); } -.tile .status { margin: 0; font-size: var(--pg-text-sm); color: var(--pg-fg-2); } +.tile .status { display: flex; flex-wrap: wrap; align-items: center; gap: var(--pg-space-2); margin: 0; font-size: var(--pg-text-sm); color: var(--pg-fg-2); } .tile-links { display: flex; flex-wrap: wrap; - gap: var(--pg-space-1) var(--pg-space-4); + gap: var(--pg-space-1) var(--pg-space-5); margin-top: auto; - padding-top: var(--pg-space-3); + padding-top: var(--pg-space-4); + font-size: var(--pg-text-sm); font-weight: 600; + border-top: var(--pg-hairline) solid var(--pg-line-glass); } -.tile a { color: var(--pg-fg-1); } +.tile a { color: var(--pg-fg-1); text-decoration: none; } .tile a:hover { color: var(--pg-accent-hover); } -.tile-clay { background: var(--pg-tile-clay); } -.tile-sky { background: var(--pg-tile-sky); } -.tile-sage { background: var(--pg-tile-sage); } +.tile-links a { display: inline-flex; align-items: center; gap: var(--pg-space-1); } +.tile-links a .icon { transition: transform var(--pg-dur-2) var(--pg-ease-spring); } +.tile-links a:hover .icon { transform: translateY(0.1875rem); } +.tile-clay { --glow: var(--pg-glow-clay); background: radial-gradient(120% 70% at 100% 0%, var(--glow), transparent 55%), var(--pg-tile-clay); } +.tile-sky { --glow: var(--pg-glow-sky); background: radial-gradient(120% 70% at 100% 0%, var(--glow), transparent 55%), var(--pg-tile-sky); } +.tile-sage { --glow: var(--pg-glow-sage); background: radial-gradient(120% 70% at 100% 0%, var(--glow), transparent 55%), var(--pg-tile-sage); } -.archive-title { margin: var(--pg-space-8) 0 var(--pg-space-3); } +.archive-title { margin: var(--pg-space-8) 0 var(--pg-space-4); font-size: var(--pg-text-xl); } +.archive-title .icon { color: var(--pg-fg-3); } /* ---------- 行列表:历史、存档、版本 ---------- */ .rows { margin: 0; padding: 0; list-style: none; } .rows > li { + position: relative; display: grid; grid-template-columns: 8rem minmax(0, 1fr); gap: var(--pg-space-4); margin: 0; - padding: var(--pg-space-4) 0; - border-top: 1px solid var(--pg-line-2); + padding: var(--pg-space-5) 0; + border-top: var(--pg-hairline) solid var(--pg-line-2); + transition: background-color var(--pg-dur-2) ease; +} +.rows > li:last-child { border-bottom: var(--pg-hairline) solid var(--pg-line-2); } +.rows > li::before { + content: ""; + position: absolute; + top: -1px; + left: 0; + width: 100%; + height: 1px; + background: var(--pg-grad-brand); + transform: scaleX(0); + transform-origin: left center; + transition: transform var(--pg-dur-3) var(--pg-ease-out); +} +.rows > li:hover::before { transform: scaleX(1); } +.rows .key { + font-family: var(--pg-font-display); + font-size: var(--pg-text-lg); + font-weight: 650; + letter-spacing: -0.01em; + line-height: var(--pg-leading-tight); + transition: color var(--pg-dur-2) ease; } -.rows > li:last-child { border-bottom: 1px solid var(--pg-line-2); } -.rows .key { font-family: var(--pg-font-serif); font-size: var(--pg-text-lg); line-height: var(--pg-leading-tight); } +.rows > li:hover .key { color: var(--pg-accent); } +.rows .key a { color: inherit; } +.rows p { color: var(--pg-fg-2); } .rows-wide > li { grid-template-columns: 12rem minmax(0, 1fr); } .rows p + p { margin-top: var(--pg-space-2); } @@ -332,58 +622,89 @@ code, kbd, pre { font-size: 0.875em; } code { - padding: 0.1em 0.35em; - background: var(--pg-bg-sunken); - border: 1px solid var(--pg-line-1); - border-radius: var(--pg-radius); + padding: 0.1em 0.4em; + background: var(--pg-accent-soft); + border: var(--pg-hairline) solid var(--pg-line-glass); + border-radius: var(--pg-radius-sm); overflow-wrap: anywhere; } -.section-tint code { background: var(--pg-bg-raised); } pre { position: relative; - padding: var(--pg-space-4) var(--pg-space-5); - line-height: 1.65; - background: var(--pg-bg-sunken); - border: 1px solid var(--pg-line-2); - border-radius: var(--pg-radius); + padding: var(--pg-space-5); + line-height: 1.7; + color: var(--pg-term-fg); + background: var(--pg-term-bg); + border: var(--pg-hairline) solid var(--pg-line-glass); + border-radius: var(--pg-radius-lg); + box-shadow: inset 0 1px 0 var(--pg-line-hi), var(--pg-shadow); overflow-x: auto; } -.section-tint pre { background: var(--pg-bg-raised); } pre code { padding: 0; + color: inherit; background: none; border: 0; font-size: inherit; overflow-wrap: normal; } +/* 终端:顶部窗口栏和三个圆点 */ pre.terminal { - color: var(--pg-term-fg); - background: var(--pg-term-bg); - border-color: var(--pg-term-bg); + padding-top: calc(var(--pg-space-7) + var(--pg-space-1)); border-radius: var(--pg-radius-lg); } +pre.terminal::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2.25rem; + background: + radial-gradient(circle at 1.125rem 50%, var(--pg-term-red) 0.3125rem, transparent 0.34rem), + radial-gradient(circle at 2.125rem 50%, var(--pg-term-yellow) 0.3125rem, transparent 0.34rem), + radial-gradient(circle at 3.125rem 50%, var(--pg-term-green) 0.3125rem, transparent 0.34rem), + var(--pg-term-bar); + border-bottom: var(--pg-hairline) solid var(--pg-line-glass); +} +pre.terminal > code { display: block; } pre.terminal .dim { color: var(--pg-term-dim); } pre.terminal .prompt { color: var(--pg-term-accent); user-select: none; } +.term-caret { + display: inline-block; + width: 0.55em; + height: 1.1em; + margin-left: 0.15em; + vertical-align: -0.2em; + background: var(--pg-term-accent); + animation: pg-blink 1.1s steps(1) infinite; +} .copy-btn { position: absolute; - top: var(--pg-space-2); - right: var(--pg-space-2); - padding: var(--pg-space-1) var(--pg-space-2); + top: var(--pg-space-3); + right: var(--pg-space-3); + padding: var(--pg-space-1) var(--pg-space-3); font: inherit; font-family: var(--pg-font-sans); font-size: var(--pg-text-xs); line-height: 1.4; - color: var(--pg-fg-2); - background: var(--pg-bg-raised); - border: 1px solid var(--pg-line-strong); - border-radius: var(--pg-radius); + color: var(--pg-term-fg); + background: var(--pg-term-bar); + border: var(--pg-hairline) solid var(--pg-line-strong); + border-radius: var(--pg-radius-pill); cursor: pointer; + transition: border-color var(--pg-dur-1) ease, transform var(--pg-dur-2) var(--pg-ease-spring); } -.copy-btn:hover { color: var(--pg-fg-1); } -pre:has(.copy-btn) { padding-right: var(--pg-space-8); } +.copy-btn:hover { border-color: var(--pg-term-accent); } +.copy-btn:active { transform: scale(0.94); } +.copy-btn.is-done { color: var(--pg-term-accent); border-color: var(--pg-term-accent); animation: pg-bump var(--pg-dur-3) var(--pg-ease-spring); } +pre:has(.copy-btn) { padding-right: var(--pg-space-9); } /* ---------- 表格 ---------- */ -.table-wrap { overflow-x: auto; } +.table-wrap { + overflow-x: auto; + padding: var(--pg-space-1) var(--pg-space-5); + border-radius: var(--pg-radius-lg); +} table { width: 100%; border-collapse: collapse; @@ -393,57 +714,71 @@ th, td { padding: var(--pg-space-3) var(--pg-space-4) var(--pg-space-3) 0; text-align: left; vertical-align: top; - border-bottom: 1px solid var(--pg-line-2); + border-bottom: var(--pg-hairline) solid var(--pg-line-2); } +tbody tr:last-child > * { border-bottom: 0; } +tbody tr { transition: background-color var(--pg-dur-1) ease; } +tbody tr:hover { background: var(--pg-accent-soft); } thead th { white-space: nowrap; - font-size: var(--pg-text-xs); + font-family: var(--pg-font-mono); + font-size: var(--pg-text-2xs); font-weight: 600; + letter-spacing: var(--pg-tracking-label); + text-transform: uppercase; color: var(--pg-fg-3); - border-bottom: 1px solid var(--pg-line-strong); + border-bottom: var(--pg-hairline) solid var(--pg-line-strong); } -tbody th { font-weight: 600; white-space: nowrap; } -td.num { text-align: right; font-family: var(--pg-font-mono); white-space: nowrap; } +tbody th { font-weight: 650; white-space: nowrap; } +td { color: var(--pg-fg-2); } +td.num { text-align: right; font-family: var(--pg-font-mono); font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--pg-fg-1); } /* ---------- 提示块与徽标 ---------- */ -.notice { - padding: var(--pg-space-4) var(--pg-space-5); - background: var(--pg-bg-sunken); - border: 1px solid var(--pg-line-2); - border-radius: var(--pg-radius); -} .notice { display: flex; align-items: flex-start; gap: var(--pg-space-3); + padding: var(--pg-space-4) var(--pg-space-5); + border-radius: var(--pg-radius-lg); } .notice > div { min-width: 0; } .notice > div > * + * { margin-top: var(--pg-space-2); } .notice-icon { margin-top: 0.2em; color: var(--pg-accent); } .notice-warn .notice-icon { color: var(--pg-warn-fg); } .notice-title { font-weight: 700; } -.notice-info { background: var(--pg-accent-soft); border-color: var(--pg-accent-soft); } +.notice-info { background: var(--pg-accent-soft); border-color: var(--pg-line-glass); } .notice-warn { background: var(--pg-warn-bg); border-color: var(--pg-warn-line); } -.section-tint .notice { background: var(--pg-bg-raised); } -.section-tint .notice-warn { background: var(--pg-warn-bg); } .badge { - display: inline-block; - padding: 0 var(--pg-space-2); + display: inline-flex; + align-items: center; + gap: var(--pg-space-1); + padding: 0 var(--pg-space-3); font-family: var(--pg-font-sans); font-size: var(--pg-text-xs); font-weight: 600; - line-height: 1.7; + line-height: 1.75; white-space: nowrap; color: var(--pg-mute-fg); background: var(--pg-mute-bg); - border: 1px solid var(--pg-mute-line); + border: var(--pg-hairline) solid var(--pg-mute-line); border-radius: var(--pg-radius-pill); vertical-align: middle; } .badge-ok { color: var(--pg-ok-fg); background: var(--pg-ok-bg); border-color: var(--pg-ok-line); } .badge-warn { color: var(--pg-warn-fg); background: var(--pg-warn-bg); border-color: var(--pg-warn-line); } .badge-bad { color: var(--pg-bad-fg); background: var(--pg-bad-bg); border-color: var(--pg-bad-line); } +/* 状态徽标前的呼吸圆点(只给没有图标的徽标) */ +.badge:is(.badge-ok, .badge-warn):not(:has(.icon))::before { + content: ""; + flex: none; + width: 0.375rem; + height: 0.375rem; + background: currentColor; + border-radius: 50%; + box-shadow: 0 0 0 0 currentColor; + animation: pg-ping 2.4s var(--pg-ease-out) infinite; +} /* ---------- 按钮 ---------- */ .actions { @@ -452,32 +787,58 @@ td.num { text-align: right; font-family: var(--pg-font-mono); white-space: nowra gap: var(--pg-space-3); } .button { - display: inline-block; + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + overflow: hidden; padding: var(--pg-space-3) var(--pg-space-5); font-size: var(--pg-text-sm); - font-weight: 600; + font-weight: 650; line-height: var(--pg-leading-tight); text-decoration: none; color: var(--pg-on-ink); background: var(--pg-ink); - border: 1px solid var(--pg-ink); - border-radius: var(--pg-radius); + border: var(--pg-hairline) solid var(--pg-ink); + border-radius: var(--pg-radius-pill); + box-shadow: var(--pg-shadow); + transition: background-color var(--pg-dur-1) ease, border-color var(--pg-dur-2) ease, + box-shadow var(--pg-dur-2) ease; } -.button:hover { color: var(--pg-on-ink); background: var(--pg-ink-hover); border-color: var(--pg-ink-hover); } +/* 悬停时一道斜光扫过 */ +.button::after { + content: ""; + position: absolute; + inset: 0; + pointer-events: none; + background: linear-gradient(105deg, transparent 30%, var(--pg-shine) 50%, transparent 70%); + transform: translateX(-110%); + transition: transform var(--pg-dur-3) var(--pg-ease-out); +} +.button:hover::after { transform: translateX(110%); } +.button:hover { color: var(--pg-on-ink); background: var(--pg-ink-hover); border-color: var(--pg-ink-hover); box-shadow: var(--pg-glow); } +.button:active { box-shadow: var(--pg-shadow); } .button-secondary { color: var(--pg-fg-1); - background: transparent; + background: var(--pg-bg-card); border-color: var(--pg-line-strong); + box-shadow: none; + -webkit-backdrop-filter: blur(var(--pg-blur)); + backdrop-filter: blur(var(--pg-blur)); } -.button-secondary:hover { color: var(--pg-fg-1); background: var(--pg-bg-sunken); border-color: var(--pg-line-strong); } +.button-secondary:hover { color: var(--pg-fg-1); background: var(--pg-accent-soft); border-color: var(--pg-accent); } +.button .icon { margin-right: var(--pg-space-2); vertical-align: -0.2em; transition: transform var(--pg-dur-2) var(--pg-ease-spring); } +.button:hover .icon { transform: translateY(0.125rem); } /* ---------- 图片 ---------- */ figure img { - border: 1px solid var(--pg-line-2); + border: var(--pg-hairline) solid var(--pg-line-glass); border-radius: var(--pg-radius-lg); + box-shadow: var(--pg-shadow-lg); } +.img-frame { overflow: hidden; border-radius: var(--pg-radius-lg); } figcaption { - margin-top: var(--pg-space-2); + margin-top: var(--pg-space-3); font-size: var(--pg-text-sm); color: var(--pg-fg-3); } @@ -488,18 +849,20 @@ figcaption { flex-wrap: wrap; align-items: center; gap: var(--pg-space-2) var(--pg-space-4); - margin-bottom: var(--pg-space-4); + margin-bottom: var(--pg-space-5); } .release-tools button[aria-pressed] { padding: var(--pg-space-1) var(--pg-space-4); font: inherit; font-size: var(--pg-text-sm); color: var(--pg-fg-2); - background: transparent; - border: 1px solid var(--pg-line-strong); + background: var(--pg-bg-card); + border: var(--pg-hairline) solid var(--pg-line-strong); border-radius: var(--pg-radius-pill); cursor: pointer; + transition: color var(--pg-dur-1) ease, background-color var(--pg-dur-2) ease, border-color var(--pg-dur-2) ease; } +.release-tools button[aria-pressed]:hover { color: var(--pg-fg-1); border-color: var(--pg-accent); } .release-tools button[aria-pressed="true"] { color: var(--pg-on-ink); background: var(--pg-ink); @@ -507,33 +870,46 @@ figcaption { } .release-tools input { min-width: 14rem; - padding: var(--pg-space-1) var(--pg-space-3); + padding: var(--pg-space-2) var(--pg-space-4); font: inherit; font-size: var(--pg-text-sm); color: var(--pg-fg-1); - background: var(--pg-bg-raised); - border: 1px solid var(--pg-line-strong); - border-radius: var(--pg-radius); + background: var(--pg-bg-card); + border: var(--pg-hairline) solid var(--pg-line-strong); + border-radius: var(--pg-radius-pill); + transition: border-color var(--pg-dur-1) ease, box-shadow var(--pg-dur-2) ease; } -.release-count { margin-bottom: var(--pg-space-3); font-size: var(--pg-text-sm); color: var(--pg-fg-3); } +.release-tools input:focus { outline: none; border-color: var(--pg-accent); box-shadow: var(--pg-glow); } +.release-count { margin-bottom: var(--pg-space-4); font-size: var(--pg-text-sm); color: var(--pg-fg-3); } .release { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: var(--pg-space-5); - padding: var(--pg-space-5) 0; - border-top: 1px solid var(--pg-line-2); + padding: var(--pg-space-6); + border-radius: var(--pg-radius-xl); } +.release + .release { margin-top: var(--pg-space-4); } .release h3 { font-size: var(--pg-text-xl); } -.release-meta { margin-top: var(--pg-space-1); font-size: var(--pg-text-sm); color: var(--pg-fg-3); } +.release-meta { margin-top: var(--pg-space-1); font-family: var(--pg-font-mono); font-size: var(--pg-text-xs); color: var(--pg-fg-3); } .release > div > * + * { margin-top: var(--pg-space-3); } /* ---------- 页脚 ---------- */ .site-footer { + position: relative; + overflow: hidden; padding-block: var(--pg-space-8) var(--pg-space-5); background: var(--pg-bg-sunken); - border-top: 1px solid var(--pg-line-1); font-size: var(--pg-text-sm); } +.site-footer::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 1px; + background: var(--pg-grad-line); +} .footer-grid { display: grid; grid-template-columns: 1.6fr repeat(4, minmax(0, 1fr)); @@ -542,15 +918,23 @@ figcaption { .footer-about p { margin-top: var(--pg-space-3); max-width: 22rem; color: var(--pg-fg-3); } .footer-title { margin: 0 0 var(--pg-space-3); - font-family: var(--pg-font-sans); - font-size: var(--pg-text-sm); - font-weight: 700; + font-family: var(--pg-font-mono); + font-size: var(--pg-text-2xs); + font-weight: 600; + letter-spacing: var(--pg-tracking-label); + text-transform: uppercase; line-height: var(--pg-leading-tight); + color: var(--pg-fg-3); } .site-footer ul { margin: 0; padding: 0; list-style: none; } .site-footer li + li { margin-top: var(--pg-space-2); } .site-footer a { color: var(--pg-fg-2); text-decoration: none; } -.site-footer a:hover { color: var(--pg-accent-hover); text-decoration: underline; } +.site-footer a:hover { color: var(--pg-fg-1); } +.site-footer nav a { + background: linear-gradient(currentColor, currentColor) left bottom / 0 1px no-repeat; + transition: color var(--pg-dur-1) ease, background-size var(--pg-dur-2) var(--pg-ease-out); +} +.site-footer nav a:hover { background-size: 100% 1px; } .footer-base { display: flex; flex-wrap: wrap; @@ -558,12 +942,31 @@ figcaption { gap: var(--pg-space-2) var(--pg-space-5); margin-top: var(--pg-space-7); padding-top: var(--pg-space-4); - border-top: 1px solid var(--pg-line-2); + border-top: var(--pg-hairline) solid var(--pg-line-2); font-size: var(--pg-text-xs); color: var(--pg-fg-3); } .footer-base p { margin: 0; } - +/* 页脚底部的超大字标,由脚本插入,aria-hidden,纯装饰 */ +.footer-mark { + margin-top: var(--pg-space-6); + font-family: var(--pg-font-display); + font-size: clamp(3.5rem, 15vw, 13rem); + font-weight: 800; + letter-spacing: -0.05em; + line-height: 0.8; + text-align: center; + white-space: nowrap; + user-select: none; + pointer-events: none; + background: var(--pg-grad-brand); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + -webkit-text-fill-color: transparent; + -webkit-mask-image: linear-gradient(180deg, black 10%, transparent 95%); + mask-image: linear-gradient(180deg, black 10%, transparent 95%); +} /* ---------- 图标 ---------- */ .sprite { position: absolute; width: 0; height: 0; overflow: hidden; } @@ -579,6 +982,7 @@ figcaption { vertical-align: -0.25em; } .icon-box { + position: relative; display: inline-grid; place-items: center; flex: none; @@ -586,33 +990,51 @@ figcaption { height: 3rem; color: var(--pg-fg-1); background: var(--pg-bg-raised); + border: var(--pg-hairline) solid var(--pg-line-glass); border-radius: var(--pg-radius); + box-shadow: inset 0 1px 0 var(--pg-line-hi), var(--pg-shadow); } -.icon-box .icon { width: 1.625rem; height: 1.625rem; vertical-align: 0; } -.section-tint .icon-box { background: var(--pg-bg-page); } -.button .icon { margin-right: var(--pg-space-2); vertical-align: -0.2em; } -.badge .icon { width: 1em; height: 1em; margin-right: var(--pg-space-1); vertical-align: -0.15em; } -.tile-icon { margin-bottom: var(--pg-space-2); } +/* 图标框外圈一层渐变描边 */ +.icon-box::before { + content: ""; + position: absolute; + inset: calc(var(--pg-hairline) * -1); + padding: var(--pg-hairline); + border-radius: inherit; + background: linear-gradient(140deg, var(--pg-brand), transparent 45%, transparent 60%, var(--pg-brand-2)); + -webkit-mask: linear-gradient(black 0 0) content-box, linear-gradient(black 0 0); + -webkit-mask-composite: xor; + mask: linear-gradient(black 0 0) content-box exclude, linear-gradient(black 0 0); + pointer-events: none; +} +.icon-box .icon { width: 1.5rem; height: 1.5rem; vertical-align: 0; transition: transform var(--pg-dur-3) var(--pg-ease-spring); } +.tile:hover .icon-box .icon, +.points > li:hover .icon-box .icon, +.flow > li:hover .icon-box .icon { transform: scale(1.14) rotate(-6deg); } +.button .icon { margin-right: var(--pg-space-2); } +.badge .icon { width: 1em; height: 1em; vertical-align: -0.15em; } +.tile-icon { margin-bottom: var(--pg-space-3); } .split-head { display: flex; flex-direction: column; gap: var(--pg-space-3); align-items: flex-start; } .icon-line { display: flex; align-items: baseline; gap: var(--pg-space-3); } -.icon-line .icon { position: relative; top: 0.25em; color: var(--pg-fg-3); } +.icon-line .icon { position: relative; top: 0.25em; color: var(--pg-accent); } /* ---------- 要点条:每页顶部的四件事 ---------- */ .points { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); - gap: var(--pg-space-5); + gap: var(--pg-space-6); margin: 0; padding: 0; list-style: none; } .points > li { display: flex; flex-direction: column; gap: var(--pg-space-2); margin: 0; } -.points .icon-box { margin-bottom: var(--pg-space-1); background: var(--pg-bg-sunken); } -.points + .notice { margin-top: var(--pg-space-6); } +.points .icon-box { margin-bottom: var(--pg-space-2); color: var(--pg-accent); } +.points + .notice { margin-top: var(--pg-space-7); } .points strong { - font-family: var(--pg-font-serif); + font-family: var(--pg-font-display); font-size: var(--pg-text-lg); - font-weight: 400; + font-weight: 650; + letter-spacing: -0.01em; line-height: var(--pg-leading-tight); } .points p { font-size: var(--pg-text-sm); color: var(--pg-fg-2); } @@ -636,12 +1058,12 @@ figcaption { min-width: 0; margin: 0; padding: var(--pg-space-5); - background: var(--pg-bg-raised); - border: 1px solid var(--pg-line-2); border-radius: var(--pg-radius-lg); + transition: border-color var(--pg-dur-2) ease, translate var(--pg-dur-3) var(--pg-ease-out); } -.section-tint .flow > li { background: var(--pg-bg-page); } -.flow > li + li::before { +.flow > li:hover { border-color: var(--pg-line-strong); translate: 0 -0.25rem; } +/* 卡片之间的箭头:挂在 .flow-top 上,好让卡片本身的 ::before/::after 留给鼠标高光 */ +.flow > li + li > .flow-top::before { content: ""; position: absolute; top: 50%; @@ -649,16 +1071,24 @@ figcaption { width: 0.6rem; height: 0.6rem; margin-top: -0.3rem; - border-top: 2px solid var(--pg-line-strong); - border-right: 2px solid var(--pg-line-strong); + border-top: var(--pg-stroke) solid var(--pg-brand); + border-right: var(--pg-stroke) solid var(--pg-brand); transform: rotate(45deg); } .flow-top { display: flex; align-items: center; justify-content: space-between; } -.flow-top .icon-box { background: var(--pg-bg-sunken); } -.section-tint .flow-top .icon-box { background: var(--pg-bg-sunken); } -.flow-n { font-family: var(--pg-font-serif); font-size: var(--pg-text-xl); color: var(--pg-brand); } +.flow-n { + font-family: var(--pg-font-mono); + font-size: var(--pg-text-xl); + font-weight: 700; + background: var(--pg-grad-brand); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + -webkit-text-fill-color: transparent; +} .flow-title { font-weight: 700; line-height: var(--pg-leading-tight); } .flow p { font-size: var(--pg-text-sm); color: var(--pg-fg-2); } +.flow p.flow-title { color: var(--pg-fg-1); } .flow code { font-size: var(--pg-text-xs); } .flow > li > a { margin-top: auto; padding-top: var(--pg-space-2); font-size: var(--pg-text-sm); font-weight: 600; } .flow > li > code { margin-top: auto; } @@ -672,27 +1102,42 @@ figcaption { list-style: none; } .layers > li { + position: relative; display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--pg-space-1) var(--pg-space-4); margin: 0; - padding: var(--pg-space-3) var(--pg-space-4); - background: var(--pg-bg-sunken); + padding: var(--pg-space-3) var(--pg-space-5); + overflow: hidden; + background: var(--pg-bg-card); + border: var(--pg-hairline) solid var(--pg-line-glass); border-radius: var(--pg-radius); + transition: translate var(--pg-dur-3) var(--pg-ease-out), border-color var(--pg-dur-2) ease; +} +.layers > li::before { + content: ""; + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 0.1875rem; + background: var(--layer, var(--pg-line-strong)); } +.layers > li:hover { translate: 0.375rem 0; border-color: var(--pg-line-strong); } .layers strong { font-weight: 700; } .layers span { font-size: var(--pg-text-sm); color: var(--pg-fg-2); } -.layers .layer-user { background: var(--pg-tile-sky); } -.layers .layer-compat { background: var(--pg-tile-sage); } -.layers .layer-lib { background: var(--pg-tile-sage); } -.layers .layer-kernel { background: var(--pg-tile-clay); } +.layers .layer-user { --layer: var(--pg-ref-cyan); background: var(--pg-tile-sky); } +.layers .layer-compat { --layer: var(--pg-ref-emerald); background: var(--pg-tile-sage); } +.layers .layer-lib { --layer: var(--pg-ref-emerald); background: var(--pg-tile-sage); } +.layers .layer-kernel { --layer: var(--pg-ref-violet); background: var(--pg-tile-clay); } +.layers .layer-boot { --layer: var(--pg-ref-pink); } .layers .layer-abi { background: transparent; - border: 2px dashed var(--pg-line-strong); + border: var(--pg-stroke) dashed var(--pg-line-strong); } -.section-tint .layers .layer-boot { background: var(--pg-bg-raised); } +.layers .layer-abi::before { display: none; } /* ---------- 条形图:表格里的数量对比 ---------- */ .bar { @@ -701,7 +1146,7 @@ figcaption { height: 0.375rem; margin: 0 0 var(--pg-space-1); overflow: hidden; - background: var(--pg-line-1); + background: var(--pg-line-2); border-radius: var(--pg-radius-pill); } .num .bar { margin-left: auto; } @@ -709,26 +1154,74 @@ figcaption { display: block; width: var(--w); height: 100%; - background: var(--pg-brand); + background: var(--pg-grad-brand); + border-radius: inherit; + transform-origin: left center; } /* ---------- 插画 ---------- */ -.art { display: block; width: 100%; max-width: 30rem; height: auto; margin-inline: auto; } +.art { display: block; width: 100%; max-width: 30rem; height: auto; margin-inline: auto; overflow: visible; } .art text { font-family: var(--pg-font-sans); fill: var(--pg-fg-1); } .art .art-sub { fill: var(--pg-fg-2); } .art .art-note { fill: var(--pg-fg-3); } +.art rect { stroke: var(--pg-line-glass); stroke-width: 1; } .art-l1 { fill: var(--pg-tile-sky); } .art-l2 { fill: var(--pg-tile-clay); } .art-l3 { fill: var(--pg-tile-sage); } +.art rect.art-l2 { stroke: var(--pg-brand); stroke-width: 1.5; } .art-slot { fill: var(--pg-bg-raised); stroke: var(--pg-line-strong); stroke-width: 2; } -.art-slot-on { fill: var(--pg-tile-clay); stroke: var(--pg-brand); stroke-width: 2; } +.art rect.art-slot { stroke: var(--pg-line-strong); stroke-width: 2; } +.art rect.art-slot-on { fill: var(--pg-tile-clay); stroke: var(--pg-brand); stroke-width: 2; } .art-line { fill: none; stroke: var(--pg-fg-3); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; } -.art-brand { fill: none; stroke: var(--pg-brand); stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; } -.art-ico { fill: none; stroke: var(--pg-fg-2); stroke-width: 1.1; stroke-linecap: round; stroke-linejoin: round; } +.art-brand { fill: none; stroke: var(--pg-brand-2); stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; } +.art-ico { fill: none; stroke: var(--pg-accent); stroke-width: 1.1; stroke-linecap: round; stroke-linejoin: round; } .stack .art { max-width: 34rem; margin-inline: 0; } -.hero-art-svg { min-width: 0; } +.hero-art-svg { position: relative; min-width: 0; } .hero-art-svg figcaption { max-width: 30rem; margin-inline: auto; } +/* ---------- 关键帧 ---------- */ +@keyframes pg-aurora { + 0% { transform: rotate(0deg) scale(1); } + 50% { transform: rotate(6deg) scale(1.08); } + 100% { transform: rotate(-4deg) scale(1.02); } +} +@keyframes pg-pop { + from { opacity: 0; transform: translateX(-50%) translateY(-0.375rem) scale(0.97); } + to { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); } +} +@keyframes pg-spin-in { + from { opacity: 0; transform: rotate(-90deg) scale(0.5); } + to { opacity: 1; transform: rotate(0deg) scale(1); } +} +@keyframes pg-ping { + 0% { box-shadow: 0 0 0 0 currentColor; } + 70%, 100% { box-shadow: 0 0 0 0.375rem transparent; } +} +@keyframes pg-blink { + 0%, 50% { opacity: 1; } + 50.01%, 100% { opacity: 0; } +} +@keyframes pg-bump { + 0% { transform: scale(1); } + 40% { transform: scale(1.12); } + 100% { transform: scale(1); } +} +@keyframes pg-vt-out { + to { opacity: 0; transform: translateY(-0.75rem) scale(0.985); } +} +@keyframes pg-vt-in { + from { opacity: 0; transform: translateY(1.25rem) scale(1.01); } +} + +/* ---------- 跨页面转场(Chrome / Edge / Safari 18.2+;其他浏览器照常跳转) ---------- */ +@view-transition { navigation: auto; } +::view-transition-old(root) { animation: var(--pg-dur-2) var(--pg-ease-in-out) both pg-vt-out; } +::view-transition-new(root) { animation: var(--pg-dur-3) var(--pg-ease-out) both pg-vt-in; } +/* 切换主题时由脚本用圆形展开动画,替换掉默认的淡入淡出 */ +html.vt-theme .site-header { view-transition-name: none; } +html.vt-theme::view-transition-old(root), +html.vt-theme::view-transition-new(root) { animation: none; mix-blend-mode: normal; } + /* ---------- 窄屏 ---------- */ @media (max-width: 62rem) { .tiles { grid-template-columns: minmax(0, 1fr); } @@ -740,19 +1233,21 @@ figcaption { @media (max-width: 56rem) { .container { padding-inline: var(--pg-space-4); } - .hero { padding-block: var(--pg-space-7) var(--pg-space-6); } + .hero { padding-block: var(--pg-space-8) var(--pg-space-7); } .hero-split, .split, .release { grid-template-columns: minmax(0, 1fr); gap: var(--pg-space-5); } - .section { padding-block: var(--pg-space-7); } + .split > h2 { position: static; } + .section { padding-block: var(--pg-space-8); } .rows > li, .rows-wide > li { grid-template-columns: minmax(0, 1fr); gap: var(--pg-space-1); } .points { grid-template-columns: minmax(0, 1fr); } .flow, .flow-4, .flow-5 { --cols: 1; gap: var(--pg-space-6); } - .flow > li + li::before { + .flow > li + li > .flow-top::before { top: -1.05rem; left: 50%; margin: 0 0 0 -0.3rem; transform: rotate(135deg); } .hero-art-svg { max-width: 24rem; } + .release { padding: var(--pg-space-5); } /* 没有脚本时,导航直接在页眉里折行,仍然可用。 */ .header-inner { flex-wrap: wrap; padding-block: var(--pg-space-2); } @@ -771,10 +1266,26 @@ figcaption { border: 0; background: transparent; padding-block: 0; + transform: none; + animation: none; + } + .site-nav > ul > li > a[aria-current="page"]::after, + .site-nav .menu.is-current > summary::after { left: var(--pg-space-3); margin-left: 0; } +} + +/* 用户要求减少动态效果时,关掉所有装饰性动画与过渡 */ +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; } + .hero::before { animation: none; translate: none; } + @view-transition { navigation: none; } } @media print { - .site-header, .copy-btn, .skip-link, .site-footer { display: none; } + .site-header, .copy-btn, .skip-link, .site-footer, .scroll-progress { display: none; } + body::after, .hero::before, .hero::after { display: none; } .section { padding-block: var(--pg-space-5); } } diff --git a/docs/css/tokens.css b/docs/css/tokens.css index e4ffa0b..606f74d 100644 --- a/docs/css/tokens.css +++ b/docs/css/tokens.css @@ -3,47 +3,59 @@ 只放值与语义映射,不放组件样式。组件只准引用 --pg-*。 三层:原始值(:root)→ 语义(按用途命名)→ 主题覆写。 尺寸一律用 rem,便于跟随用户的字号设置。 - 配色:暖米色底、近黑暖灰字、陶土色点缀。 + 配色:深色科技风。近黑蓝底、冷灰字、紫→青渐变做品牌色; + 默认深色,浅色作为可切换的备选主题。 ============================================================ */ :root { /* ---- 原始色板(主题无关) ---- */ - --pg-ref-ivory: #faf9f5; - --pg-ref-ivory-dark: #f0eee6; + --pg-ref-void: #05060a; + --pg-ref-night: #0a0c13; + --pg-ref-snow: #f6f7fb; --pg-ref-white: #ffffff; - --pg-ref-slate: #141413; - --pg-ref-clay: #d97757; - --pg-ref-clay-deep: #a64b2a; - --pg-ref-clay-darker: #84391f; + --pg-ref-violet: #7c6cff; + --pg-ref-violet-deep: #4b3fd6; + --pg-ref-cyan: #22d3ee; + --pg-ref-cyan-deep: #0b6f86; + --pg-ref-emerald: #34d399; + --pg-ref-pink: #f472b6; /* 终端面板:两个主题下固定同一套深色 */ - --pg-term-bg: #1f1e1d; - --pg-term-fg: #f0eee6; - --pg-term-dim: #b0aea5; - --pg-term-accent: #eba589; - - /* ---- 字体:标题用衬线,正文用无衬线,代码用等宽 ---- - 衬线栈里 Georgia 管拉丁字母;中文在 macOS 落到宋体,其余系统落到雅黑。 */ - --pg-font-serif: Georgia, "Source Serif 4", "Songti SC", "Noto Serif CJK SC", - "Source Han Serif SC", "Microsoft YaHei", "PingFang SC", serif; - --pg-font-sans: system-ui, -apple-system, "Segoe UI", "PingFang SC", + --pg-term-bg: #07080d; + --pg-term-fg: #e8eaf2; + --pg-term-dim: #9198ab; + --pg-term-accent: #67e3f6; + --pg-term-bar: #12141d; + --pg-term-red: #ff5f57; + --pg-term-yellow: #febc2e; + --pg-term-green: #28c840; + + /* ---- 字体:标题与正文都用无衬线,代码与标签用等宽 ---- */ + --pg-font-display: "Inter Display", Inter, system-ui, -apple-system, "Segoe UI", + "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", + "Noto Sans SC", sans-serif; + --pg-font-sans: Inter, system-ui, -apple-system, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "Noto Sans SC", sans-serif; - --pg-font-mono: ui-monospace, "SF Mono", Menlo, Consolas, + --pg-font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; /* ---- 字阶与行高 ---- */ + --pg-text-2xs: 0.75rem; --pg-text-xs: 0.8125rem; --pg-text-sm: 0.9375rem; --pg-text-md: 1rem; --pg-text-lg: 1.1875rem; --pg-text-xl: 1.5rem; - --pg-text-2xl: 2.125rem; - --pg-text-hero: clamp(2.5rem, 5.4vw, 4rem); + --pg-text-2xl: clamp(1.875rem, 3.2vw, 2.625rem); + --pg-text-hero: clamp(2.75rem, 6.6vw, 5.5rem); + --pg-text-page: clamp(2.5rem, 5vw, 4rem); - --pg-leading-body: 1.8; + --pg-leading-body: 1.75; --pg-leading-tight: 1.3; - --pg-leading-display: 1.18; + --pg-leading-display: 1.08; + --pg-tracking-display: -0.035em; + --pg-tracking-label: 0.14em; /* ---- 间距与尺寸 ---- */ --pg-space-1: 0.25rem; @@ -53,110 +65,171 @@ --pg-space-5: 1.5rem; --pg-space-6: 2rem; --pg-space-7: 3rem; - --pg-space-8: 4.5rem; - --pg-space-9: 6rem; + --pg-space-8: 5rem; + --pg-space-9: 7.5rem; - --pg-radius: 0.5rem; - --pg-radius-lg: 1rem; + --pg-radius-sm: 0.375rem; + --pg-radius: 0.625rem; + --pg-radius-lg: 1.125rem; + --pg-radius-xl: 1.5rem; --pg-radius-pill: 6rem; --pg-header-h: 4.25rem; - --pg-container: 72rem; - --pg-measure: 42rem; + --pg-container: 75rem; + --pg-measure: 44rem; + --pg-hairline: 0.0625rem; + --pg-stroke: 0.125rem; + --pg-blur: 1rem; + --pg-blur-lg: 1.5rem; + + /* ---- 动效:时长与缓动 ---- */ + --pg-dur-1: 160ms; + --pg-dur-2: 320ms; + --pg-dur-3: 640ms; + --pg-dur-aurora: 28s; + --pg-ease-out: cubic-bezier(0.16, 1, 0.3, 1); + --pg-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); + /* 弹簧:刚度 180、阻尼 14 预先算好的曲线;不支持 linear() 的浏览器整条声明失效,回到 ease */ + --pg-ease-spring: linear(0, 0.009, 0.035 2.1%, 0.141 4.4%, 0.723 12.9%, 0.938 16.7%, + 1.017, 1.077, 1.121, 1.149 24.3%, 1.159, 1.163 27%, 1.154, 1.129 32.8%, + 1.051 39.6%, 1.017 43.1%, 0.991, 0.977 51%, 0.975 57.1%, 0.997 69.8%, 1.003 76.9%, 1); } -/* ---- 语义层:亮色(默认) ---- */ +/* ---- 语义层:亮色(备选) ---- */ :root, body:not(.theme-dark) { color-scheme: light; - --pg-bg-page: var(--pg-ref-ivory); + --pg-bg-page: var(--pg-ref-snow); --pg-bg-raised: var(--pg-ref-white); - --pg-bg-sunken: var(--pg-ref-ivory-dark); + --pg-bg-sunken: #edeff5; + --pg-bg-glass: rgb(255 255 255 / 0.72); + --pg-bg-card: rgb(255 255 255 / 0.8); - --pg-fg-1: var(--pg-ref-slate); - --pg-fg-2: #3d3d3a; - --pg-fg-3: #5e5d59; + --pg-fg-1: #0b0d14; + --pg-fg-2: #3b4152; + --pg-fg-3: #575e72; - --pg-line-1: #e8e6dc; - --pg-line-2: #d6d3c5; + --pg-line-1: #e4e7ef; + --pg-line-2: #d5d9e4; /* 控件边界:按钮、输入框的描边,3.0+ 对比度。 line-1/line-2 只做装饰性分隔线,不承载信息。 */ - --pg-line-strong: #8a877d; - - --pg-accent: var(--pg-ref-clay-deep); - --pg-accent-hover: var(--pg-ref-clay-darker); - --pg-accent-soft: #f6e3d8; - --pg-brand: var(--pg-ref-clay); - - /* 主按钮:深色底、米色字 */ - --pg-ink: var(--pg-ref-slate); - --pg-ink-hover: #3d3d3a; - --pg-on-ink: var(--pg-ref-ivory); - - /* 首页项目色块:三种扁平底色 */ - --pg-tile-clay: #f4e1d6; - --pg-tile-sky: #dfe8f1; - --pg-tile-sage: #e5e9d9; - - --pg-ok-fg: #065f46; - --pg-ok-bg: #e9f5ef; - --pg-ok-line: #9fd2bb; - --pg-warn-fg: #8a3d0c; - --pg-warn-bg: #fbf0dc; - --pg-warn-line: #e0be85; + --pg-line-strong: #838a9e; + --pg-line-glass: rgb(11 13 20 / 0.08); + --pg-line-hi: rgb(255 255 255 / 0.9); + + --pg-accent: var(--pg-ref-violet-deep); + --pg-accent-hover: #3a2fb8; + --pg-accent-soft: #ecebff; + --pg-brand: var(--pg-ref-violet); + --pg-brand-2: var(--pg-ref-cyan-deep); + --pg-grad-brand: linear-gradient(120deg, var(--pg-ref-violet-deep) 0%, var(--pg-ref-violet) 45%, var(--pg-ref-cyan-deep) 100%); + --pg-grad-text: linear-gradient(180deg, #0b0d14 30%, #4a5066 100%); + --pg-grad-line: linear-gradient(90deg, transparent, rgb(75 63 214 / 0.5), rgb(11 111 134 / 0.5), transparent); + + /* 主按钮:深色底、白字 */ + --pg-ink: #0b0d14; + --pg-ink-hover: #262a38; + --pg-on-ink: var(--pg-ref-white); + + /* 首页项目卡片:三种带色调的底 */ + --pg-tile-clay: #f1efff; + --pg-tile-sky: #eaf7fb; + --pg-tile-sage: #eaf7f1; + --pg-glow-clay: rgb(124 108 255 / 0.32); + --pg-glow-sky: rgb(34 211 238 / 0.32); + --pg-glow-sage: rgb(52 211 153 / 0.32); + + /* 首屏极光与网格背景(纯装饰) */ + --pg-aurora-1: rgb(124 108 255 / 0.22); + --pg-aurora-2: rgb(34 211 238 / 0.18); + --pg-aurora-3: rgb(244 114 182 / 0.12); + --pg-grid: rgb(11 13 20 / 0.06); + --pg-spot: rgb(124 108 255 / 0.12); + --pg-noise-strength: 0.03; + --pg-shine: rgb(255 255 255 / 0.28); + + --pg-ok-fg: #05603f; + --pg-ok-bg: #e6f6ef; + --pg-ok-line: #9fd8bf; + --pg-warn-fg: #844006; + --pg-warn-bg: #fdf3e1; + --pg-warn-line: #e8c58c; --pg-bad-fg: #991b1b; - --pg-bad-bg: #fbecec; - --pg-bad-line: #e6a8a8; - --pg-mute-fg: #3d3d3a; - --pg-mute-bg: var(--pg-ref-ivory-dark); - --pg-mute-line: #d6d3c5; - - --pg-focus: var(--pg-ref-clay-deep); - --pg-shadow: 0 0.5rem 1.5rem rgb(20 20 19 / 0.14); + --pg-bad-bg: #fdecec; + --pg-bad-line: #eaa9a9; + --pg-mute-fg: #3b4152; + --pg-mute-bg: #edeff5; + --pg-mute-line: #d5d9e4; + + --pg-focus: var(--pg-ref-violet-deep); + --pg-shadow: 0 0.75rem 2rem rgb(11 13 20 / 0.1), 0 0.125rem 0.375rem rgb(11 13 20 / 0.06); + --pg-shadow-lg: 0 2rem 4rem rgb(11 13 20 / 0.14); + --pg-glow: 0 0 0 0.0625rem rgb(75 63 214 / 0.18), 0 0.5rem 2rem rgb(124 108 255 / 0.22); } -/* ---- 语义层:暗色 ---- */ +/* ---- 语义层:暗色(默认) ---- */ body.theme-dark { color-scheme: dark; - --pg-bg-page: #1f1e1d; - --pg-bg-raised: #2a2927; - --pg-bg-sunken: #181716; - - --pg-fg-1: #faf9f5; - --pg-fg-2: #cfccc1; - --pg-fg-3: #a5a297; - - --pg-line-1: #34322f; - --pg-line-2: #45433f; - --pg-line-strong: #7b7970; - - --pg-accent: #e8926f; - --pg-accent-hover: #f1ae92; - --pg-accent-soft: #3a2a22; - --pg-brand: var(--pg-ref-clay); - - --pg-ink: #faf9f5; - --pg-ink-hover: #e0ddd2; - --pg-on-ink: #141413; - - --pg-tile-clay: #3a2b24; - --pg-tile-sky: #232c36; - --pg-tile-sage: #272c20; - - --pg-ok-fg: #7dd3ae; - --pg-ok-bg: #10251d; - --pg-ok-line: #1f4a3a; - --pg-warn-fg: #f0c57c; - --pg-warn-bg: #2a2113; - --pg-warn-line: #57431f; - --pg-bad-fg: #f1a3a3; - --pg-bad-bg: #2b1517; + --pg-bg-page: var(--pg-ref-void); + --pg-bg-raised: #0e1018; + --pg-bg-sunken: #090b11; + --pg-bg-glass: rgb(10 12 19 / 0.66); + --pg-bg-card: rgb(17 19 29 / 0.6); + + --pg-fg-1: #f1f3f9; + --pg-fg-2: #b6bccc; + --pg-fg-3: #8a91a5; + + --pg-line-1: #161923; + --pg-line-2: #222632; + --pg-line-strong: #5d6477; + --pg-line-glass: rgb(255 255 255 / 0.08); + --pg-line-hi: rgb(255 255 255 / 0.1); + + --pg-accent: #a99fff; + --pg-accent-hover: #cdc7ff; + --pg-accent-soft: #15142c; + --pg-brand: var(--pg-ref-violet); + --pg-brand-2: var(--pg-ref-cyan); + --pg-grad-brand: linear-gradient(120deg, var(--pg-ref-violet) 0%, #a78bfa 40%, var(--pg-ref-cyan) 100%); + --pg-grad-text: linear-gradient(180deg, #ffffff 30%, #8f96aa 100%); + --pg-grad-line: linear-gradient(90deg, transparent, rgb(124 108 255 / 0.7), rgb(34 211 238 / 0.7), transparent); + + --pg-ink: #f1f3f9; + --pg-ink-hover: #ffffff; + --pg-on-ink: #05060a; + + --pg-tile-clay: #120f24; + --pg-tile-sky: #0b1820; + --pg-tile-sage: #0b1a16; + --pg-glow-clay: rgb(124 108 255 / 0.45); + --pg-glow-sky: rgb(34 211 238 / 0.4); + --pg-glow-sage: rgb(52 211 153 / 0.4); + + --pg-aurora-1: rgb(124 108 255 / 0.3); + --pg-aurora-2: rgb(34 211 238 / 0.18); + --pg-aurora-3: rgb(244 114 182 / 0.14); + --pg-grid: rgb(255 255 255 / 0.045); + --pg-spot: rgb(124 108 255 / 0.16); + --pg-noise-strength: 0.05; + --pg-shine: rgb(124 108 255 / 0.3); + + --pg-ok-fg: #6ee7b7; + --pg-ok-bg: #0a1f18; + --pg-ok-line: #1b4a39; + --pg-warn-fg: #f6c87a; + --pg-warn-bg: #211a0c; + --pg-warn-line: #54421d; + --pg-bad-fg: #f5a3a3; + --pg-bad-bg: #261215; --pg-bad-line: #5c2629; - --pg-mute-fg: #cfccc1; - --pg-mute-bg: #2a2927; - --pg-mute-line: #45433f; - - --pg-focus: #e8926f; - --pg-shadow: 0 0.5rem 1.5rem rgb(0 0 0 / 0.5); + --pg-mute-fg: #c3c8d6; + --pg-mute-bg: #12141d; + --pg-mute-line: #2a2e3b; + + --pg-focus: #a99fff; + --pg-shadow: 0 0.75rem 2rem rgb(0 0 0 / 0.5), 0 0.125rem 0.375rem rgb(0 0 0 / 0.4); + --pg-shadow-lg: 0 2rem 5rem rgb(0 0 0 / 0.6); + --pg-glow: 0 0 0 0.0625rem rgb(124 108 255 / 0.35), 0 0.5rem 2.5rem rgb(124 108 255 / 0.35); } diff --git a/docs/index.html b/docs/index.html index 7ec34b3..2b32ed3 100644 --- a/docs/index.html +++ b/docs/index.html @@ -6,14 +6,14 @@ GoutouStdio - - + + - +
-

几个学生,
在写操作系统。

+

几个学生,
在写操作系统。

GoutouStdio 是学生自发组织的开源小组,2022 年开始做操作系统。现在维护两个项目:用 Python 写的 PySpOS,和用 Rust 写内核的 SpaceOS 9。

了解 PySpOS @@ -239,7 +239,12 @@
- + + + + + + diff --git a/docs/js/main.js b/docs/js/main.js index 3d558fa..d9013cb 100644 --- a/docs/js/main.js +++ b/docs/js/main.js @@ -1,5 +1,6 @@ /* 站点交互:主题切换、导航菜单、代码块复制。 - 三项都是渐进增强,脚本失效时页面内容与导航仍然可用。 */ + 三项都是渐进增强,脚本失效时页面内容与导航仍然可用。 + 滚动与入场动画在 motion.js 里,和这里互不依赖。 */ (function () { "use strict"; @@ -11,9 +12,10 @@ function saveTheme(theme) { try { localStorage.setItem(KEY, theme); } catch (e) { /* 隐私模式下忽略 */ } } - function preferredTheme() { - if (window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches) return "light"; - return "dark"; + // 站点默认暗色,访客手动切到浅色才用浅色(与 theme-init.js 一致)。 + function preferredTheme() { return "dark"; } + function reducedMotion() { + return window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches; } function initTheme() { @@ -31,8 +33,24 @@ } apply(readTheme() || preferredTheme(), false); if (!btn) return; + // 支持 View Transitions 的浏览器里,新主题从按钮位置以圆形展开;其余浏览器直接切换。 btn.addEventListener("click", function () { - apply(document.body.classList.contains("theme-dark") ? "light" : "dark", true); + var next = document.body.classList.contains("theme-dark") ? "light" : "dark"; + if (!document.startViewTransition || reducedMotion()) { apply(next, true); return; } + var rect = btn.getBoundingClientRect(); + var x = rect.left + rect.width / 2; + var y = rect.top + rect.height / 2; + var r = Math.hypot(Math.max(x, window.innerWidth - x), Math.max(y, window.innerHeight - y)); + var root = document.documentElement; + root.classList.add("vt-theme"); + var vt = document.startViewTransition(function () { apply(next, true); }); + vt.ready.then(function () { + root.animate( + { clipPath: ["circle(0 at " + x + "px " + y + "px)", "circle(" + r + "px at " + x + "px " + y + "px)"] }, + { duration: 560, easing: "cubic-bezier(0.65, 0, 0.35, 1)", pseudoElement: "::view-transition-new(root)" } + ); + }).catch(function () { /* 被打断时主题已经切好,忽略 */ }); + vt.finished.then(function () { root.classList.remove("vt-theme"); }, function () { root.classList.remove("vt-theme"); }); }); window.addEventListener("storage", function (e) { if (e.key === KEY) apply(e.newValue || preferredTheme(), false); @@ -102,8 +120,9 @@ var text = copy.textContent.replace(/\n$/, ""); function done(ok) { btn.textContent = ok ? "已复制" : "复制失败"; + btn.classList.toggle("is-done", ok); status.textContent = ok ? "已复制到剪贴板" : "复制失败,请手动选择文本"; - window.setTimeout(function () { btn.textContent = "复制"; }, 1600); + window.setTimeout(function () { btn.textContent = "复制"; btn.classList.remove("is-done"); }, 1600); } if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(text).then(function () { done(true); }, function () { done(false); }); diff --git a/docs/js/motion.js b/docs/js/motion.js new file mode 100644 index 0000000..6961ee5 --- /dev/null +++ b/docs/js/motion.js @@ -0,0 +1,470 @@ +/* 站点动画:GSAP + ScrollTrigger + SplitText + Lenis(都在 js/vendor,本地加载)。 + 全部是渐进增强: + - 访客要求减少动态效果,或者任何一个库没加载上,就什么都不做,页面保持静态完整; + - 没有脚本时 theme-init.js 不会打上 motion 标记,内容不会被藏起来; + - 动画只改 transform / opacity / clip-path,不改文字内容(数字滚动结束后与原文一致)。 */ +(function () { + "use strict"; + + var root = document.documentElement; + var gsap = window.gsap; + var ScrollTrigger = window.ScrollTrigger; + var SplitText = window.SplitText; + var mm = window.matchMedia ? function (q) { return window.matchMedia(q).matches; } : function () { return false; }; + + if (!gsap || !ScrollTrigger || mm("(prefers-reduced-motion: reduce)")) { + root.classList.remove("motion"); + return; + } + gsap.registerPlugin(ScrollTrigger); + if (SplitText) gsap.registerPlugin(SplitText); + + var finePointer = mm("(hover: hover) and (pointer: fine)"); + var $ = function (sel, ctx) { return (ctx || document).querySelector(sel); }; + var $$ = function (sel, ctx) { return Array.prototype.slice.call((ctx || document).querySelectorAll(sel)); }; + + gsap.defaults({ ease: "power3.out", duration: 0.9 }); + + /* ---------- 平滑滚动 ---------- */ + var lenis = null; + function initLenis() { + if (!window.Lenis) return; + // 锚点跳转交给 Lenis,它会读取 html 的 scroll-padding-top,避开吸顶页眉。 + lenis = new window.Lenis({ lerp: 0.1, wheelMultiplier: 1, anchors: true }); + lenis.on("scroll", ScrollTrigger.update); + gsap.ticker.add(function (time) { lenis.raf(time * 1000); }); + gsap.ticker.lagSmoothing(0); + } + + /* ---------- 阅读进度条与页眉 ---------- */ + function initChrome() { + var bar = document.createElement("div"); + bar.className = "scroll-progress"; + bar.setAttribute("aria-hidden", "true"); + document.body.appendChild(bar); + gsap.to(bar, { scaleX: 1, ease: "none", scrollTrigger: { start: 0, end: "max", scrub: 0.3 } }); + + var header = $(".site-header"); + var nav = $("#site-nav"); + if (!header) return; + // 往下滚时收起页眉,往上滚立刻出现;菜单展开或焦点在页眉里时不收。 + ScrollTrigger.create({ + start: 0, + end: "max", + onUpdate: function (self) { + var y = self.scroll(); + header.classList.toggle("is-scrolled", y > 8); + var busy = (nav && nav.classList.contains("open")) || header.querySelector("details[open]") || + header.contains(document.activeElement); + header.classList.toggle("is-hidden", self.direction === 1 && y > 320 && !busy); + } + }); + } + + /* ---------- 导航:跟随鼠标的高亮块、窄屏菜单展开 ---------- */ + function initNav() { + var nav = $("#site-nav"); + if (!nav) return; + var list = $(":scope > ul", nav); + var items = $$(":scope > ul > li > a, :scope > ul > li > details > summary", nav); + + var media = gsap.matchMedia(); + media.add("(min-width: 56.01rem) and (hover: hover)", function () { + var pill = document.createElement("span"); + pill.className = "nav-indicator"; + pill.setAttribute("aria-hidden", "true"); + list.prepend(pill); + nav.classList.add("has-indicator"); + var shown = false; + function moveTo(el) { + var vars = { x: el.offsetLeft, y: el.offsetTop, width: el.offsetWidth, height: el.offsetHeight }; + if (!shown) { gsap.set(pill, vars); shown = true; } + gsap.to(pill, Object.assign({ autoAlpha: 1, duration: 0.45, ease: "expo.out", overwrite: true }, vars)); + } + function hide() { shown = false; gsap.to(pill, { autoAlpha: 0, duration: 0.25, overwrite: true }); } + items.forEach(function (el) { + el.addEventListener("mouseenter", function () { moveTo(el); }); + el.addEventListener("focus", function () { moveTo(el); }); + }); + list.addEventListener("mouseleave", hide); + return function () { pill.remove(); nav.classList.remove("has-indicator"); }; + }); + + // 窄屏:菜单打开时逐项落下 + new MutationObserver(function () { + if (nav.classList.contains("open")) { + gsap.from($$(":scope > ul > li", nav), { y: -10, autoAlpha: 0, duration: 0.45, stagger: 0.04, ease: "power2.out" }); + } + }).observe(nav, { attributes: true, attributeFilter: ["class"] }); + } + + /* ---------- 文字拆分 ---------- */ + // 带
的标题按行包一层遮罩,逐行从下方升起;行内的 .grad 渐变不受影响。 + function splitLines(el) { + var groups = [[]]; + Array.prototype.slice.call(el.childNodes).forEach(function (n) { + if (n.nodeName === "BR") groups.push([]); + else groups[groups.length - 1].push(n); + }); + el.textContent = ""; + return groups.map(function (nodes) { + var mask = document.createElement("span"); + var line = document.createElement("span"); + mask.className = "pg-line-mask"; + line.className = "pg-line"; + mask.style.display = "block"; + mask.style.overflow = "clip"; + line.style.display = "block"; + nodes.forEach(function (n) { line.appendChild(n); }); + mask.appendChild(line); + el.appendChild(mask); + return line; + }); + } + + function splitChars(el) { + if (!SplitText) return null; + var split = SplitText.create(el, { type: "words,chars", wordsClass: "pg-word", charsClass: "pg-char" }); + return split.chars; + } + + /* ---------- 插画:分层拼装、连线描边、虚线流动 ---------- */ + function prepStroke(path) { + var len = 0; + try { len = path.getTotalLength(); } catch (e) { len = 0; } + if (!len) return 0; + gsap.set(path, { strokeDasharray: len, strokeDashoffset: len }); + return len; + } + + function artTimeline(svg) { + var tl = gsap.timeline({ paused: true }); + var blocks = $$("rect", svg); + var texts = $$("text", svg); + var strokes = $$(".art-brand, .art-line", svg); + var icons = $$(".art-ico", svg); + tl.from(blocks, { y: -28, autoAlpha: 0, scale: 0.94, transformOrigin: "50% 50%", duration: 0.9, stagger: 0.14, ease: "back.out(1.4)" }, 0) + .from(texts, { x: -12, autoAlpha: 0, duration: 0.6, stagger: 0.04 }, 0.25) + .from(icons, { scale: 0, autoAlpha: 0, transformOrigin: "50% 50%", duration: 0.7, stagger: 0.12, ease: "back.out(2)" }, 0.45); + strokes.forEach(function (p, i) { + if (!prepStroke(p)) return; + tl.to(p, { strokeDashoffset: 0, duration: 0.8, ease: "power2.inOut" }, 0.55 + i * 0.12); + }); + // 描完之后变成流动的虚线,表示数据在层与层之间传递 + tl.call(function () { + strokes.forEach(function (p) { + gsap.set(p, { strokeDasharray: "6 7", strokeDashoffset: 0 }); + gsap.to(p, { strokeDashoffset: -26, duration: 1.4, ease: "none", repeat: -1 }); + }); + icons.forEach(function (g, i) { + gsap.to(g, { y: -3, duration: 1.8 + i * 0.3, ease: "sine.inOut", yoyo: true, repeat: -1 }); + }); + }); + return tl; + } + + /* ---------- 终端:逐行打出 ---------- */ + function typeTerminal(pre) { + var code = $("code", pre) || pre; + var lines = Math.max(1, code.textContent.replace(/\n$/, "").split("\n").length); + var caret = document.createElement("span"); + caret.className = "term-caret"; + caret.setAttribute("aria-hidden", "true"); + gsap.set(code, { display: "block", clipPath: "inset(0% 0% 100% 0%)" }); + return gsap.to(code, { + clipPath: "inset(0% 0% 0% 0%)", + duration: Math.min(2.4, lines * 0.11), + ease: "steps(" + lines + ")", + paused: true, + onComplete: function () { code.appendChild(caret); gsap.set(code, { clearProps: "clipPath" }); } + }); + } + + /* ---------- 首屏入场 ---------- */ + function initHero() { + var hero = $(".hero"); + if (!hero) return; + var tl = gsap.timeline({ defaults: { ease: "expo.out", duration: 1.1 }, delay: 0.05 }); + + var badge = $(".hero-badge", hero); + if (badge) tl.from(badge, { y: 16, autoAlpha: 0, duration: 0.8 }, 0); + + var h1 = $("h1", hero); + if (h1) { + if (h1.querySelector("br")) { + h1.classList.add("is-split"); + tl.from(splitLines(h1), { yPercent: 110, rotation: 2, duration: 1.2, stagger: 0.12 }, 0.08); + } else { + var chars = splitChars(h1); + if (chars && chars.length) { + h1.classList.add("is-split"); + tl.from(chars, { + yPercent: 90, rotationX: -80, autoAlpha: 0, transformOrigin: "50% 100% -0.5em", + duration: 1, stagger: { each: 0.03, from: "start" } + }, 0.08); + } else { + tl.from(h1, { y: 30, autoAlpha: 0 }, 0.08); + } + } + } + + var lede = $(".lede", hero); + if (lede) tl.from(lede, { y: 24, autoAlpha: 0 }, 0.4); + var acts = $$(".actions > *", hero); + if (acts.length) tl.from(acts, { y: 20, autoAlpha: 0, stagger: 0.08 }, 0.55); + var spec = $$(".spec > div", hero); + if (spec.length) tl.from(spec, { y: 16, autoAlpha: 0, stagger: 0.06 }, 0.65); + + var fig = $("figure", hero); + if (fig) { + var svg = $("svg.art", fig); + if (svg) { + var art = artTimeline(svg); + tl.from(fig, { autoAlpha: 0, duration: 0.4 }, 0.2); + tl.add(function () { art.play(); }, 0.25); + var cap = $("figcaption", fig); + if (cap) tl.from(cap, { y: 10, autoAlpha: 0, duration: 0.8 }, 1.2); + } else { + tl.from(fig, { y: 50, scale: 0.95, rotationX: 10, transformPerspective: 1200, autoAlpha: 0, duration: 1.4 }, 0.25); + var term = $("pre.terminal", fig); + if (term) { + var typing = typeTerminal(term); + tl.add(function () { typing.play(); }, 0.8); + } + // 往下滚时截图比文字移得快一点,拉出前后景深 + var img = $("img", fig); + if (img) { + gsap.to(fig, { + yPercent: -10, ease: "none", + scrollTrigger: { trigger: hero, start: "top top", end: "bottom top", scrub: true } + }); + } + } + } + + // 极光随鼠标轻微偏移 + if (finePointer) { + var ax = gsap.quickTo(hero, "--ax", { duration: 1.6, ease: "power3" }); + var ay = gsap.quickTo(hero, "--ay", { duration: 1.6, ease: "power3" }); + hero.addEventListener("pointermove", function (e) { + var r = hero.getBoundingClientRect(); + ax((e.clientX - r.left) / r.width - 0.5); + ay((e.clientY - r.top) / r.height - 0.5); + }); + hero.addEventListener("pointerleave", function () { ax(0); ay(0); }); + } + } + + /* ---------- 滚动入场 ---------- */ + function reveal(targets, from, opts) { + targets = targets.filter(Boolean); + if (!targets.length) return; + opts = opts || {}; + gsap.set(targets, Object.assign({ autoAlpha: 0 }, from)); + ScrollTrigger.batch(targets, { + start: opts.start || "top 88%", + once: true, + onEnter: function (batch) { + var to = { autoAlpha: 1, x: 0, y: 0, scale: 1, rotationX: 0, rotation: 0, stagger: opts.stagger || 0.09, + duration: opts.duration || 1, ease: opts.ease || "expo.out", overwrite: true, + clearProps: "transform,opacity,visibility" }; + gsap.to(batch, to); + if (opts.onEnter) opts.onEnter(batch); + } + }); + } + + // 去掉祖先已经在列表里的元素,避免父子叠加两次动画 + function topmost(els) { + return els.filter(function (el) { + return !els.some(function (other) { return other !== el && other.contains(el); }); + }); + } + + function popIcons(batch) { + var icons = batch.map(function (el) { return $(".icon-box", el); }).filter(Boolean); + if (icons.length) { + gsap.fromTo(icons, { scale: 0.5, rotation: -20 }, + { scale: 1, rotation: 0, duration: 0.9, stagger: 0.09, ease: "back.out(2.2)", delay: 0.15, clearProps: "transform" }); + } + } + + function initReveals() { + var main = $("main"); + if (!main) return; + var heroEl = $(".hero", main); + var notHero = function (el) { return !(heroEl && heroEl.contains(el)); }; + + // 小节标题逐字浮现 + reveal($$(".archive-title", main), { x: -20 }); + $$(".section-head h2, .split > h2", main).filter(notHero).forEach(function (h) { + var chars = splitChars(h); + if (!chars || !chars.length) { reveal([h], { y: 30 }); return; } + gsap.from(chars, { + yPercent: 80, autoAlpha: 0, rotationX: -60, transformOrigin: "50% 100%", duration: 0.9, + stagger: 0.025, ease: "expo.out", + scrollTrigger: { trigger: h, start: "top 88%", once: true } + }); + }); + reveal($$(".section-head .lede", main).filter(notHero), { y: 24 }); + + reveal($$(".tile", main), { y: 70, rotationX: -14, transformOrigin: "50% 100%" }, + { stagger: 0.12, duration: 1.2, onEnter: popIcons }); + reveal($$(".points > li", main), { y: 40 }, { stagger: 0.1, onEnter: popIcons }); + reveal($$(".flow > li", main), { y: 40, scale: 0.96 }, { stagger: 0.12, onEnter: popIcons }); + reveal($$(".rows > li", main), { x: -30 }, { stagger: 0.07 }); + reveal($$(".layers > li", main), { x: 50 }, { stagger: 0.07 }); + + var general = topmost($$([ + ".notice", ".table-wrap", "main pre", ".stack > p", ".stack > ul", ".stack > ol", ".stack > dl", + ".stack > .actions", ".stack > h3", "figure", ".prose > *", ".release-tools" + ].join(","), main).filter(notHero).filter(function (el) { + return !el.closest(".tile, .points, .flow, .rows, .layers"); + })); + reveal(general, { y: 36 }, { stagger: 0.08 }); + + // 插画:进入视口后再拼装 + $$("svg.art", main).filter(notHero).forEach(function (svg) { + var tl = artTimeline(svg); + ScrollTrigger.create({ trigger: svg, start: "top 80%", once: true, onEnter: function () { tl.play(); } }); + }); + + // 不在首屏的终端也逐行打出 + $$("pre.terminal", main).filter(notHero).forEach(function (pre) { + var t = typeTerminal(pre); + ScrollTrigger.create({ trigger: pre, start: "top 80%", once: true, onEnter: function () { t.play(); } }); + }); + + // 首屏以外的大图:遮罩展开 + 轻微缩放 + $$("figure img", main).filter(notHero).forEach(function (img) { + gsap.fromTo(img, { clipPath: "inset(8% 6% 8% 6% round 1.125rem)", scale: 1.06 }, { + clipPath: "inset(0% 0% 0% 0% round 1.125rem)", scale: 1, duration: 1.6, ease: "expo.out", + clearProps: "clipPath,transform", + scrollTrigger: { trigger: img, start: "top 85%", once: true } + }); + }); + } + + /* ---------- 数据:条形图生长、数字滚动 ---------- */ + function initData() { + $$(".table-wrap").forEach(function (wrap) { + var bars = $$(".bar > i", wrap); + var nums = $$("td.num", wrap); + if (!bars.length && !nums.length) return; + gsap.set(bars, { scaleX: 0 }); + ScrollTrigger.create({ + trigger: wrap, start: "top 82%", once: true, + onEnter: function () { + gsap.to(bars, { scaleX: 1, duration: 1.6, stagger: 0.08, ease: "expo.out", delay: 0.2 }); + nums.forEach(function (td, i) { + var node = td.lastChild; + if (!node || node.nodeType !== 3) return; + var text = node.nodeValue.trim(); + if (!/^\d{1,3}(,\d{3})*$|^\d+$/.test(text)) return; + var end = parseInt(text.replace(/,/g, ""), 10); + var grouped = text.indexOf(",") !== -1; + var obj = { v: 0 }; + gsap.to(obj, { + v: end, duration: 1.6, delay: 0.2 + i * 0.08, ease: "expo.out", + onUpdate: function () { + var n = Math.round(obj.v); + node.nodeValue = grouped ? n.toLocaleString("en-US") : String(n); + }, + onComplete: function () { node.nodeValue = text; } + }); + }); + } + }); + }); + } + + /* ---------- 卡片:鼠标跟随高光、3D 倾斜;按钮:磁吸 ---------- */ + function initPointer() { + if (!finePointer) return; + function track(el) { + el.classList.add("spot"); + el.addEventListener("pointermove", function (e) { + var r = el.getBoundingClientRect(); + el.style.setProperty("--mx", (e.clientX - r.left) + "px"); + el.style.setProperty("--my", (e.clientY - r.top) + "px"); + }); + } + $$(".tile, .flow > li, .release, .table-wrap").forEach(track); + window.pgTrackSpot = track; + + $$(".tile").forEach(function (tile) { + var rx = gsap.quickTo(tile, "rotationX", { duration: 0.6, ease: "power3" }); + var ry = gsap.quickTo(tile, "rotationY", { duration: 0.6, ease: "power3" }); + gsap.set(tile, { transformPerspective: 900 }); + tile.addEventListener("pointermove", function (e) { + var r = tile.getBoundingClientRect(); + ry(((e.clientX - r.left) / r.width - 0.5) * 8); + rx(-((e.clientY - r.top) / r.height - 0.5) * 8); + }); + tile.addEventListener("pointerleave", function () { rx(0); ry(0); }); + }); + + $$(".button").forEach(function (btn) { + var mx = gsap.quickTo(btn, "x", { duration: 0.5, ease: "power3" }); + var my = gsap.quickTo(btn, "y", { duration: 0.5, ease: "power3" }); + btn.addEventListener("pointermove", function (e) { + var r = btn.getBoundingClientRect(); + mx((e.clientX - r.left - r.width / 2) * 0.22); + my((e.clientY - r.top - r.height / 2) * 0.35); + }); + btn.addEventListener("pointerleave", function () { + gsap.to(btn, { x: 0, y: 0, duration: 0.9, ease: "elastic.out(1, 0.4)", overwrite: true }); + }); + }); + } + + /* ---------- 版本历史:脚本渲染出来的列表也有入场 ---------- */ + function initDynamicLists() { + var host = $("#version-history"); + if (!host) return; + new MutationObserver(function () { + var items = $$(":scope > .release", host).slice(0, 12); + if (!items.length) return; + if (window.pgTrackSpot) items.forEach(window.pgTrackSpot); + gsap.fromTo(items, { y: 28, autoAlpha: 0 }, { + y: 0, autoAlpha: 1, duration: 0.7, stagger: 0.05, ease: "expo.out", overwrite: true, + clearProps: "transform,opacity,visibility" + }); + }).observe(host, { childList: true }); + } + + /* ---------- 页脚:超大字标随滚动升起 ---------- */ + function initFooter() { + var footer = $(".site-footer"); + if (!footer) return; + var mark = document.createElement("div"); + mark.className = "footer-mark"; + mark.setAttribute("aria-hidden", "true"); + mark.textContent = "GoutouStdio"; + footer.appendChild(mark); + gsap.from(mark, { + yPercent: 45, autoAlpha: 0, ease: "none", + scrollTrigger: { trigger: footer, start: "top bottom", end: "bottom bottom", scrub: 0.6 } + }); + reveal($$(".footer-grid > *", footer), { y: 24 }, { stagger: 0.07, start: "top 95%" }); + } + + function init() { + initLenis(); + initChrome(); + initNav(); + initHero(); + root.classList.add("motion-ready"); + initReveals(); + initData(); + initPointer(); + initDynamicLists(); + initFooter(); + // 图片和字体到齐后重新量一次触发位置 + window.addEventListener("load", function () { ScrollTrigger.refresh(); }); + if (document.fonts && document.fonts.ready) document.fonts.ready.then(function () { ScrollTrigger.refresh(); }); + } + + if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", init); + else init(); +})(); diff --git a/docs/js/theme-init.js b/docs/js/theme-init.js index d96046b..a689057 100644 --- a/docs/js/theme-init.js +++ b/docs/js/theme-init.js @@ -1,24 +1,28 @@ /* 主题预初始化:放在 的第一行,正文绘制前就定好主题,避免闪一下。 约定:body.theme-dark = 暗色,无类名 = 亮色。 - 没有保存过选择时跟随系统;系统也没给出偏好时用暗色。 - 同时给 打上 js 标记,窄屏侧栏据此才收成抽屉;没有脚本时导航照常显示。 */ + 站点默认暗色;只有访客手动切到浅色并保存过,才用浅色。 + 同时给 打上 js 标记,窄屏侧栏据此才收成抽屉;没有脚本时导航照常显示。 + 访客没有要求减少动态效果时再打上 motion 标记:首屏内容先藏起来,等 motion.js 接管后播放入场动画。 + motion.js 3 秒内没有接管(脚本加载失败、被拦截),就撤掉标记,内容照常显示。 */ (function () { var KEY = "pg-theme"; var root = document.documentElement; var saved = null; try { saved = localStorage.getItem(KEY); } catch (e) { saved = null; } - var theme; - if (saved === "light" || saved === "dark") { - theme = saved; - } else if (window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches) { - theme = "light"; - } else { - theme = "dark"; - } + var theme = saved === "light" ? "light" : "dark"; root.classList.add("js"); root.dataset.theme = theme; + + var reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches; + if (!reduce) { + root.classList.add("motion"); + window.setTimeout(function () { + if (!root.classList.contains("motion-ready")) root.classList.remove("motion"); + }, 3000); + } + var apply = function () { document.body.classList.toggle("theme-dark", theme === "dark"); }; if (document.body) apply(); else document.addEventListener("DOMContentLoaded", apply); diff --git a/docs/js/vendor/LICENSE-lenis.txt b/docs/js/vendor/LICENSE-lenis.txt new file mode 100644 index 0000000..4960877 --- /dev/null +++ b/docs/js/vendor/LICENSE-lenis.txt @@ -0,0 +1,9 @@ +The MIT License + +Copyright (c) 2024 darkroom.engineering + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/docs/js/vendor/README.md b/docs/js/vendor/README.md new file mode 100644 index 0000000..c24c526 --- /dev/null +++ b/docs/js/vendor/README.md @@ -0,0 +1,15 @@ +# 第三方动画库 + +官网动画用到的库,原样从 npm 包的 `dist/` 复制过来,只去掉了 sourceMappingURL 注释。 +放在本地而不是 CDN,是为了国内访问稳定,也让页面不依赖外部服务。 +加载顺序由 `tools/sync_docs_chrome.py` 的 `VENDOR_SCRIPTS` 决定。 + +| 文件 | 来源 | 版本 | 许可 | +|---|---|---|---| +| `gsap.min.js` | npm `gsap` | 3.15.1 | GSAP Standard "No Charge" License, | +| `ScrollTrigger.min.js` | npm `gsap` | 3.15.1 | 同上 | +| `SplitText.min.js` | npm `gsap` | 3.15.1 | 同上 | +| `lenis.min.js` | npm `lenis` | 1.3.27 | MIT,见 `LICENSE-lenis.txt` | + +升级时:`npm pack gsap lenis`,解包后把同名文件覆盖进来,删掉末尾的 sourceMappingURL 注释, +更新上表,再运行 `python tools/sync_docs_chrome.py` 刷新各页面引用里的版本哈希。 diff --git a/docs/js/vendor/ScrollTrigger.min.js b/docs/js/vendor/ScrollTrigger.min.js new file mode 100644 index 0000000..4c89ae3 --- /dev/null +++ b/docs/js/vendor/ScrollTrigger.min.js @@ -0,0 +1,11 @@ +/*! + * ScrollTrigger 3.15.1 + * https://gsap.com + * + * @license Copyright 2026, GreenSock. All rights reserved. + * Subject to the terms at https://gsap.com/standard-license. + * @author: Jack Doyle, jack@greensock.com + */ + +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).window=e.window||{})}(this,function(e){"use strict";function _defineProperties(e,t){for(var r=0;r=Math.abs(r)?t:r}function P(){(Ae=Se.core.globals().ScrollTrigger)&&Ae.core&&function _integrate(){var e=Ae.core,r=e.bridge||{},t=e._scrollers,n=e._proxies;t.push.apply(t,Ie),n.push.apply(n,Le),Ie=t,Le=n,o=function _bridge(e,t){return r[e](t)}}()}function Q(e){return Se=e||r(),!Te&&Se&&"undefined"!=typeof document&&document.body&&(Ce=window,ke=(Me=document).documentElement,Ee=Me.body,t=[Ce,Me,ke,Ee],Se.utils.clamp,Be=Se.core.context||function(){},Oe="onpointerenter"in Ee?"pointer":"mouse",Pe=k.isTouch=Ce.matchMedia&&Ce.matchMedia("(hover: none), (pointer: coarse)").matches?1:"ontouchstart"in Ce||0=i,n=Math.abs(t)>=i;T&&(r||n)&&T(se,e,t,me,ye),r&&(m&&0Math.abs(t)?"x":"y",oe=!0),"y"!==ae&&(me[2]+=e,se._vx.update(e,!0)),"x"!==ae&&(ye[2]+=t,se._vy.update(t,!0)),n?ee=ee||requestAnimationFrame(lf):lf()}function of(e){if(!jf(e,1)){var t=(e=N(e,s)).clientX,r=e.clientY,n=t-se.x,i=r-se.y,o=se.isDragging;se.x=t,se.y=r,(o||(n||i)&&(Math.abs(se.startX-t)>=a||Math.abs(se.startY-r)>=a))&&(re=re||(o?2:1),o||(se.isDragging=!0),nf(n,i))}}function rf(e){return e.touches&&1=e)return a[n];return a[n-1]}for(n=a.length,e+=r;n--;)if(a[n]<=e)return a[n];return a[0]}:function(e,t,r){void 0===r&&(r=.001);var n=o(e);return!t||Math.abs(n-e)r&&(n*=t/100),e=e.substr(0,r-1)),e=n+(e in X?X[e]*t:~e.indexOf("%")?parseFloat(e)*t/100:parseFloat(e)||0)}return e}function Eb(e,t,r,n,i,o,a,s){var l=i.startColor,c=i.endColor,u=i.fontSize,f=i.indent,d=i.fontWeight,p=Ue.createElement("div"),g=Ma(r)||"fixed"===z(r,"pinType"),h=-1!==e.indexOf("scroller"),v=g?He:"IFRAME"===r.tagName?r.contentDocument.body:r,b=-1!==e.indexOf("start"),m=b?l:c,y="border-color:"+m+";font-size:"+u+";color:"+m+";font-weight:"+d+";pointer-events:none;white-space:nowrap;font-family:sans-serif,Arial;z-index:1000;padding:4px 8px;border-width:0;border-style:solid;";return y+="position:"+((h||s)&&g?"fixed;":"absolute;"),!h&&!s&&g||(y+=(n===Xe?D:I)+":"+(o+parseFloat(f))+"px;"),a&&(y+="box-sizing:border-box;text-align:left;width:"+a.offsetWidth+"px;"),p._isStart=b,p.setAttribute("class","gsap-marker-"+e+(t?" marker-"+t:"")),p.style.cssText=y,p.innerText=t||0===t?e+"-"+t:e,v.children[0]?v.insertBefore(p,v.children[0]):v.appendChild(p),p._offset=p["offset"+n.op.d2],U(p,0,n,b),p}function Jb(){return 34He.clientWidth)||(Ie.cache++,v?T=T||requestAnimationFrame(K):K(),lt||W("scrollStart"),lt=st())}function Lb(){y=Je.innerWidth,m=Je.innerHeight}function Mb(e){Ie.cache++,!0!==e&&(je||h||Ue.fullscreenElement||Ue.webkitFullscreenElement||b&&y===Je.innerWidth&&!(Math.abs(Je.innerHeight-m)>.25*Je.innerHeight))||c.restart(!0)}function Pb(){return yb(re,"scrollEnd",Pb)||Et(!0)}function Sb(e){for(var t=0;tt,n=e._startClamp&&e.start>=t;(r||n)&&e.setPositions(n?t-1:e.start,r?Math.max(n?t:e.start+1,t):e.end,!0)}),_b(!1),tt=0,r.forEach(function(e){return e&&e.render&&e.render(-1)}),Ie.forEach(function(e){Ua(e)&&(e.smooth&&requestAnimationFrame(function(){return e.target.style.scrollBehavior="smooth"}),e.rec&&e(e.rec))}),Vb(_,1),c.pause(),kt++,K(nt=2),Ct.forEach(function(e){return Ua(e.vars.onRefresh)&&e.vars.onRefresh(e)}),nt=re.isRefreshing=!1,W("refresh")}else xb(re,"scrollEnd",Pb)},j=0,Pt=1,K=function _updateAll(e){if(2===e||!nt&&!et){re.isUpdating=!0,ot&&ot.update(0);var t=Ct.length,r=st(),n=50<=r-R,i=t&&Ct[0].scroll();if(Pt=i=Ra(be,he)){if(oe&&Ae()&&!de)for(o=oe.parentNode;o&&o!==He;)o._pinOffset&&(B-=o._pinOffset,I-=o._pinOffset),o=o.parentNode}else i=nb(ae),s=he===Xe,a=Ae(),K=parseFloat(j(he.a))+w,!y&&1=I})},Te.update=function(e,t,r){if(!de||r||e){var n,i,o,a,s,l,c,u=!0===nt?re:Te.scroll(),f=e?0:(u-B)/q,d=f<0?0:1u+(u-D)/(st()-Ge)*E&&(d=.9999)),d!==p&&Te.enabled){if(a=(s=(n=Te.isActive=!!d&&d<1)!=(!!p&&p<1))||!!d!=!!p,Te.direction=p=Ra(be,he),fe)if(e||!n&&!l)qc(ae,G);else{var g=wt(ae,!0),h=u-B;qc(ae,He,g.top+(he===Xe?h:0)+_t,g.left+(he===Xe?0:h)+_t)}Ot(n||l?W:V),Z&&d<1&&n||b(K+(1!==d||l?0:$))}}else b(Ja(K+$*d));!ue||A.tween||je||at||te.restart(!0),C&&(s||ce&&d&&(d<1||!rt))&&We(C.targets).forEach(function(e){return e.classList[n||ce?"add":"remove"](C.className)}),!T||ve||e||T(Te),a&&!je?(ve&&(c&&("complete"===o?O.pause().totalProgress(1):"reset"===o?O.restart(!0).pause():"restart"===o?O.restart(!0):O[o]()),T&&T(Te)),!s&&rt||(M&&s&&Ya(Te,M),xe[i]&&Ya(Te,xe[i]),ce&&(1===d?Te.kill(!1,1):xe[i]=0),s||xe[i=1===d?1:3]&&Ya(Te,xe[i])),pe&&!n&&Math.abs(Te.getVelocity())>(Va(pe)?pe:2500)&&(Xa(Te.callbackAnimation),ee?ee.progress(1):Xa(O,"reverse"===o?1:!d,1))):ve&&T&&!je&&T(Te)}if(x){var v=de?u/de.duration()*(de._caScrollDist||0):u;y(v+(X._isFlipped?1:0)),x(v)}S&&S(-u/de.duration()*(de._caScrollDist||0))}},Te.enable=function(e,t){Te.enabled||(Te.enabled=!0,xb(be,"resize",Mb),me||xb(be,"scroll",Kb),Ce&&xb(ScrollTrigger,"refreshInit",Ce),!1!==e&&(Te.progress=Oe=0,R=D=Ee=Ae()),!1!==t&&Te.refresh())},Te.getTween=function(e){return e&&A?A.tween:ee},Te.setPositions=function(e,t,r,n){if(de){var i=de.scrollTrigger,o=de.duration(),a=i.end-i.start;e=i.start+a*e/o,t=i.start+a*t/o}Te.refresh(!1,!1,{start:Ea(e,r&&!!Te._startClamp),end:Ea(t,r&&!!Te._endClamp)},n),Te.update()},Te.adjustPinSpacing=function(e){if(Q&&e){var t=Q.indexOf(he.d)+1;Q[t]=parseFloat(Q[t])+e+_t,Q[1]=parseFloat(Q[1])+e+_t,Ot(Q)}},Te.disable=function(e,t){if(!1!==e&&Te.revert(!0,!0),Te.enabled&&(Te.enabled=Te.isActive=!1,t||ee&&ee.pause(),re=0,n&&(n.uncache=1),Ce&&yb(ScrollTrigger,"refreshInit",Ce),te&&(te.pause(),A.tween&&A.tween.kill()&&(A.tween=0)),!me)){for(var r=Ct.length;r--;)if(Ct[r].scroller===be&&Ct[r]!==Te)return;yb(be,"resize",Mb),me||yb(be,"scroll",Kb)}},Te.kill=function(e,t){Te.disable(e,t),ee&&!t&&ee.kill(),a&&delete Mt[a];var r=Ct.indexOf(Te);0<=r&&Ct.splice(r,1),r===$e&&0o&&(b()>o?a.progress(1)&&b(o):a.resetTo("scrollY",o))}Wa(e)||(e={}),e.preventDefault=e.isNormalizer=e.allowClicks=!0,e.type||(e.type="wheel,touch"),e.debounce=!!e.debounce,e.id=e.id||"normalizer";var n,o,l,i,a,c,u,s,f=e.normalizeScrollX,t=e.momentum,r=e.allowNestedScroll,d=e.onRelease,p=J(e.target)||qe,g=Fe.core.globals().ScrollSmoother,h=g&&g.get(),v=E&&(e.content&&J(e.content)||h&&!1!==e.content&&!h.smooth()&&h.content()),b=L(p,Xe),m=L(p,Ne),y=1,x=(k.isTouch&&Je.visualViewport?Je.visualViewport.scale*Je.visualViewport.width:Je.outerWidth)/Je.innerWidth,_=0,w=Ua(t)?function(){return t(n)}:function(){return t||2.8},S=zc(p,e.type,!0,r),T=Ia,C=Ia;return v&&Fe.set(v,{y:"+=0"}),e.ignoreCheck=function(e){return E&&"touchmove"===e.type&&function ignoreDrag(){if(i){requestAnimationFrame(Jq);var e=Ja(n.deltaY/2),t=C(b.v-e);if(v&&t!==b.v+b.offset){b.offset=t-b.v;var r=Ja((parseFloat(v&&v._gsap.y)||0)-b.offset);v.style.transform="matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, "+r+", 0, 1)",v._gsap.y=r+"px",b.cacheID=Ie.cache,K()}return!0}b.offset&&Nq(),i=!0}()||1.05=o||o-1<=r)&&Fe.to({},{onUpdate:Tq,duration:i})}else s.restart(!0);d&&d(e)},e.onWheel=function(){a._ts&&a.pause(),1e3e?"string"==typeof e?s(document.querySelectorAll(e)):"length"in e?Array.from(e).reduce((e,t)=>("string"==typeof t?e.push(...s(t)):e.push(t),e),[]):[e]:[],o=e=>s(e).filter(e=>e&&1===e.nodeType),a=[],h=function(){},d={add:e=>e()},p=/\s+/g,c=new RegExp("\\p{RI}\\p{RI}|\\p{Emoji}(\\p{EMod}|\\u{FE0F}\\u{20E3}?|[\\u{E0020}-\\u{E007E}]+\\u{E007F})?(\\u{200D}\\p{Emoji}(\\p{EMod}|\\u{FE0F}\\u{20E3}?|[\\u{E0020}-\\u{E007E}]+\\u{E007F})?)*|.","gu"),u={left:0,top:0,width:0,height:0},f=(e,t)=>{for(;++t{e.innerHTML=t,i?e.setAttribute("aria-label",i):e.removeAttribute("aria-label"),n?e.setAttribute("aria-hidden",n):e.removeAttribute("aria-hidden")},g=(e,t)=>{if(t){let i,n,l,r,s=new Set(e.join("").match(t)||a),o=e.length;if(s.size)for(;--o>-1;)for(l of(n=e[o],s))if(l.startsWith(n)&&l.length>n.length){for(i=0,r=n;l.startsWith(r+=e[o+ ++i])&&r.length"inline"===window.getComputedStyle(e).display&&(e.style.display="inline-block"),y=(e,t,i)=>t.insertBefore("string"==typeof e?document.createTextNode(e):e,i),x=(e,t,i)=>{let n=t[e+"sClass"]||"",{tag:l="div",aria:r="auto",propIndex:s=!1}=t,o="line"===e?"block":"inline-block",a=n.indexOf("++")>-1,h=t=>{let h=document.createElement(l),d=i.length+1;return n&&(h.className=n+(a?" "+n+d:"")),s&&h.style.setProperty("--"+e,d+""),"none"!==r&&h.setAttribute("aria-hidden","true"),"span"!==l&&(h.style.position="relative",h.style.display=o),h.textContent=t,i.push(h),h};return a&&(n=n.replace("++","")),h.collection=i,h},b=(e,t,i,n,l,s,o,h,d,c)=>{var u;let f,m,x,v,C,E,S,T,A,N,_,R,k,O,W,B,j,F,I=Array.from(e.childNodes),L=0,{wordDelimiter:z,reduceWhiteSpace:M=!0,prepareText:H}=t,D=e.getBoundingClientRect(),P=D,q=!M&&"pre"===window.getComputedStyle(e).whiteSpace.substring(0,3),G=s&&c&&"rtl"===window.getComputedStyle(e).direction,J=0,K=i.collection;for("object"==typeof z?(x=z.delimiter||z,m=z.replaceWith||""):m=""===z?"":z||" ",f=" "!==m;L-1?(E=K[K.length-1],E.appendChild(document.createTextNode(n?"":B))):(E=i(n?"":B),y(E,e,v),J&&1===A&&!S&&E.insertBefore(J,E.firstChild)),n){if(_=r?g([...r.segment(B)].map(e=>e.segment),d):B.match(h)||a,d){let e=B.match(d);e&&e.some(e=>_.indexOf(e)<0)&&(_=B.match(h)||a)}for(F=0;F<_.length;F++)E.appendChild(" "===_[F]?document.createTextNode(" "):n(_[F]))}if(s&&c){if(W=v.textContent=W.substring(B.length+1,W.length),N=E.getBoundingClientRect(),N.top>P.top&&(G?N.left>=P.left:N.left<=P.left)){for(R=e.cloneNode(),k=e.childNodes[0];k&&k!==E;)O=k,k=k.nextSibling,R.appendChild(O);e.parentNode.insertBefore(R,e),l&&w(R)}P=N}(A=C.length?" ":f&&" "===B.slice(-1)?" "+m:m,e,v)}else y(m,e,v);e.removeChild(v),J=0}else 1===v.nodeType&&(o&&o.indexOf(v)>-1?(K.indexOf(v.previousSibling)>-1&&K[K.length-1].appendChild(v),J=v):(b(v,t,i,n,l,s,o,h,d,!0),J=0),l&&w(v))};const v=class e{constructor(e,t){this.isSplit=!1,n||C.register(window.gsap),this.elements=o(e),this.chars=[],this.words=[],this.lines=[],this.masks=[],this.vars=t,this.elements.forEach(e=>{var i;!1!==t.overwrite&&(null==(i=e[l])||i._data.orig.filter(({element:t})=>t===e).forEach(m)),e[l]=this}),this._split=()=>this.isSplit&&this.split(this.vars);let i=[],r=()=>{let e,t=i.length;for(;t--;){e=i[t];let n=e.element.offsetWidth;if(n!==e.width)return e.width=n,void this._split()}};this._data={orig:i,obs:"undefined"!=typeof ResizeObserver&&new ResizeObserver(()=>{clearTimeout(this._data.t),this._data.t=setTimeout(r,200)})},h(this),this.split(t)}split(e){return(this._ctx||d).add(()=>{this.isSplit&&this.revert(),this.vars=e=e||this.vars||{};let t,{type:n="chars,words,lines",aria:l="auto",deepSlice:r=!0,smartWrap:a,onSplit:h,autoSplit:d=!1,specialChars:p,mask:m}=this.vars,g=n.indexOf("lines")>-1,w=n.indexOf("chars")>-1,y=n.indexOf("words")>-1,v=w&&!y&&!g,C=p&&("push"in p?new RegExp("(?:"+p.join("|")+")","gu"):p),E=C?new RegExp(C.source+"|"+c.source,"gu"):c,S=!!e.ignore&&o(e.ignore),{orig:T,animTime:A,obs:N}=this._data;(w||y||g)&&(this.elements.forEach((t,i)=>{T[i]={element:t,html:t.innerHTML,ariaL:t.getAttribute("aria-label"),ariaH:t.getAttribute("aria-hidden")},"auto"===l?t.setAttribute("aria-label",(t.textContent||"").trim()):"hidden"===l&&t.setAttribute("aria-hidden","true");let n,o,h,d,p=[],c=[],m=[],A=w?x("char",e,p):null,N=x("word",e,c);if(b(t,e,N,A,v,r&&(g||v),S,E,C,!1),g){let i,l,r=s(t.childNodes),o=((e,t,i,n)=>{let l=x("line",i,n),r=window.getComputedStyle(e).textAlign||"left";return(i,n)=>{let s=l("");for(s.style.textAlign=r,e.insertBefore(s,t[i]);i1===e.nodeType?e.getBoundingClientRect():u),c=u;for(n=0;nc.top&&(d?l.left+l.width>c.left+1:l.left{var t;return null==(t=e.parentNode)?void 0:t.removeChild(e)})}if(!y){for(n=0;n{let t=e.cloneNode();return e.replaceWith(t),t.appendChild(e),e.className&&(t.className=e.className.trim().split(" ").map(e=>e+"-mask").join(" ")),t.style.overflow="clip",t.style.verticalAlign="top",t}))),this.isSplit=!0,i&&g&&d&&i.addEventListener("loadingdone",this._split),(t=h&&h(this))&&t.totalTime&&(this._data.anim=A?t.totalTime(A):t),g&&d&&this.elements.forEach((e,t)=>{T[t].width=e.offsetWidth,N&&N.observe(e)})}),this}kill(){let{obs:e,t:t}=this._data;e&&e.disconnect(),clearTimeout(t),null==i||i.removeEventListener("loadingdone",this._split)}revert(){var e,t;if(this.isSplit){let{orig:i,anim:n}=this._data;this.kill(),i.forEach(m),this.chars.length=this.words.length=this.lines.length=i.length=this.masks.length=0,this.isSplit=!1,n&&(this._data.animTime=n.totalTime(),n.revert()),null==(t=(e=this.vars).onRevert)||t.call(e,this)}return this}static create(t,i){return new e(t,i)}static register(e){t=t||e||window.gsap,t&&(s=t.utils.toArray,h=t.core.context||h),!n&&window.innerWidth>0&&(i=document.fonts,n=!0)}};v.version="3.15.1";let C=v;e.SplitText=C,e.default=C;if (typeof(window)==="undefined"||window!==e){Object.defineProperty(e,"__esModule",{value:!0})} else {delete e.default}})); + diff --git a/docs/js/vendor/gsap.min.js b/docs/js/vendor/gsap.min.js new file mode 100644 index 0000000..c6d5f85 --- /dev/null +++ b/docs/js/vendor/gsap.min.js @@ -0,0 +1,11 @@ +/*! + * GSAP 3.15.1 + * https://gsap.com + * + * @license Copyright 2026, GreenSock. All rights reserved. + * Subject to the terms at https://gsap.com/standard-license. + * @author: Jack Doyle, jack@greensock.com + */ + +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t=t||self).window=t.window||{})}(this,function(e){"use strict";function _inheritsLoose(t,e){t.prototype=Object.create(e.prototype),(t.prototype.constructor=t).__proto__=e}function _assertThisInitialized(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function r(t){return"string"==typeof t}function s(t){return"function"==typeof t}function t(t){return"number"==typeof t}function u(t){return void 0===t}function v(t){return"object"==typeof t}function w(t){return!1!==t}function x(){return"undefined"!=typeof window}function y(t){return s(t)||r(t)}function R(t){return(i=bt(t,ht))&&Fe}function S(t,e){return console.warn("Invalid property",t,"set to",e,"Missing plugin? gsap.registerPlugin()")}function T(t,e){return!e&&console.warn(t)}function U(t,e){return t&&(ht[t]=e)&&i&&(i[t]=e)||ht}function V(){return 0}function ga(t){var e,r,i=t[0];if(v(i)||s(i)||(t=[t]),!(e=(i._gsap||{}).harness)){for(r=yt.length;r--&&!yt[r].targetTest(i););e=yt[r]}for(r=t.length;r--;)t[r]&&(t[r]._gsap||(t[r]._gsap=new qt(t[r],e)))||t.splice(r,1);return t}function ha(t){return t._gsap||ga(Pt(t))[0]._gsap}function ia(t,e,r){return(r=t[e])&&s(r)?t[e]():u(r)&&t.getAttribute&&t.getAttribute(e)||r}function ja(t,e){return(t=t.split(",")).forEach(e)||t}function ka(t){return Math.round(1e5*t)/1e5||0}function la(t){return Math.round(1e7*t)/1e7||0}function ma(t,e){var r=e.charAt(0),i=parseFloat(e.substr(2));return t=parseFloat(t),"+"===r?t+i:"-"===r?t-i:"*"===r?t*i:t/i}function na(t,e){for(var r=e.length,i=0;t.indexOf(e[i])<0&&++ia;)s=s._prev;return s?(e._next=s._next,s._next=e):(e._next=t[r],t[r]=e),e._next?e._next._prev=e:t[i]=e,e._prev=s,e.parent=e._dp=t,e}function Ba(t,e,r,i){void 0===r&&(r="_first"),void 0===i&&(i="_last");var n=e._prev,a=e._next;n?n._next=a:t[r]===e&&(t[r]=a),a?a._prev=n:t[i]===e&&(t[i]=n),e._next=e._prev=e.parent=null}function Ca(t,e){t.parent&&(!e||t.parent.autoRemoveChildren)&&t.parent.remove&&t.parent.remove(t),t._act=0}function Da(t,e){if(t&&(!e||e._end>t._dur||e._start<0))for(var r=t;r;)r._dirty=1,r=r.parent;return t}function Fa(t,e,r,i){return t._startAt&&(L?t._startAt.revert(ft):t.vars.immediateRender&&!t.vars.autoRevert||t._startAt.render(e,!0,i))}function Ha(t){return t._repeat?wt(t._tTime,t=t.duration()+t._rDelay)*t:0}function Ja(t,e){return(t-e._start)*e._ts+(0<=e._ts?0:e._dirty?e.totalDuration():e._tDur)}function Ka(t){return t._end=la(t._start+(t._tDur/Math.abs(t._ts||t._rts||X)||0))}function La(t,e){var r=t._dp;return r&&r.smoothChildTiming&&t._ts&&(t._start=la(r._time-(0X)&&e.render(r,!0)),Da(t,e)._dp&&t._initted&&t._time>=t._dur&&t._ts){if(t._dur=e._time)?e._last?e._last._start:e._time:i||e!==I?Ot(e,i,r):e._time)+r._delay),r._end=la(r._start+(r.totalDuration()/Math.abs(r.timeScale())||0)),Aa(e,r,"_first","_last",e._sort?"_start":0),xt(r)||(e._recent=r),n||Ma(e,r),e._ts<0&&La(e,e._tTime),e}function Oa(t,e){return(ht.ScrollTrigger||S("scrollTrigger",e))&&ht.ScrollTrigger.create(e,t)}function Pa(t,e,r,i,n){return $t(t,e,n),t._initted?!r&&t._pt&&!L&&(t._dur&&!1!==t.vars.lazy||!t._dur&&t.vars.lazy)&&f!==Lt.frame?(pt.push(t),t._lazy=[n,i],1):void 0:1}function Ua(t,e,r,i){var n=t._repeat,a=la(e)||0,s=t._tTime/t._tDur;return s&&!i&&(t._time*=a/t._dur),t._dur=a,t._tDur=n?n<0?1e10:la(a*(n+1)+t._rDelay*n):a,0(n=Math.abs(n))&&(a=i,o=n);return a}function wb(t){return Ca(t),t.scrollTrigger&&t.scrollTrigger.kill(!!L),t.progress()<1&&At(t,"onInterrupt"),t}function zb(t){if(t)if(t=!t.name&&t.default||t,x()||t.headless){var e=t.name,r=s(t),i=e&&!r&&t.init?function(){this._props=[]}:t,n={init:V,render:_e,add:Ht,kill:Te,modifier:ve,rawVars:0},a={targetTest:0,get:0,getSetter:ue,aliases:{},register:0};if(It(),t!==i){if(mt[e])return;ta(i,ta(xa(t,n),a)),bt(i.prototype,bt(n,xa(t,a))),mt[i.prop=e]=i,t.targetTest&&(yt.push(i),dt[e]=1),e=("css"===e?"CSS":e.charAt(0).toUpperCase()+e.substr(1))+"Plugin"}U(e,i),t.register&&t.register(Fe,i,we)}else Dt.push(t)}function Cb(t,e,r){return(6*(t+=t<0?1:1>16,e>>8&zt,e&zt]:0:Rt.black;if(!p){if(","===e.substr(-1)&&(e=e.substr(0,e.length-1)),Rt[e])p=Rt[e];else if("#"===e.charAt(0)){if(e.length<6&&(e="#"+(n=e.charAt(1))+n+(a=e.charAt(2))+a+(s=e.charAt(3))+s+(5===e.length?e.charAt(4)+e.charAt(4):"")),9===e.length)return[(p=parseInt(e.substr(1,6),16))>>16,p>>8&zt,p&zt,parseInt(e.substr(7),16)/255];p=[(e=parseInt(e.substr(1),16))>>16,e>>8&zt,e&zt]}else if("hsl"===e.substr(0,3))if(p=d=e.match(rt),r){if(~e.indexOf("="))return p=e.match(it),i&&p.length<4&&(p[3]=1),p}else o=+p[0]%360/360,u=p[1]/100,n=2*(h=p[2]/100)-(a=h<=.5?h*(u+1):h+u-h*u),3=q?u.endTime(!1):t._dur;return r(e)&&(isNaN(e)||e in o)?(a=e.charAt(0),s="%"===e.substr(-1),n=e.indexOf("="),"<"===a||">"===a?(0<=n&&(e=e.replace(/=/,"")),("<"===a?u._start:u.endTime(0<=u._repeat))+(parseFloat(e.substr(1))||0)*(s?(n<0?u:i).totalDuration()/100:1)):n<0?(e in o||(o[e]=h),o[e]):(a=parseFloat(e.charAt(n-1)+e.substr(n+1)),s&&i&&(a=a/100*(K(i)?i[0]:i).totalDuration()),1=r&&te)return i;i=i._next}else for(i=t._last;i&&i._start>=r;){if("isPause"===i.data&&i._start=n._start)&&n._ts&&h!==n){if(n.parent!==this)return this.render(t,e,r);if(n.render(0=this.totalDuration()||!v&&_)&&(f!==this._start&&Math.abs(l)===Math.abs(this._ts)||this._lock||(!t&&g||!(v===m&&0=i&&(a instanceof te?e&&n.push(a):(r&&n.push(a),t&&n.push.apply(n,a.getChildren(!0,e,r)))),a=a._next;return n},e.getById=function getById(t){for(var e=this.getChildren(1,1,1),r=e.length;r--;)if(e[r].vars.id===t)return e[r]},e.remove=function remove(t){return r(t)?this.removeLabel(t):s(t)?this.killTweensOf(t):(t.parent===this&&Ba(this,t),t===this._recent&&(this._recent=this._last),Da(this))},e.totalTime=function totalTime(t,e){return arguments.length?(this._forcing=1,!this._dp&&this._ts&&(this._start=la(Lt.time-(0r:!r||s.isActive())&&n.push(s):(i=s.getTweensOf(a,r)).length&&n.push.apply(n,i),s=s._next;return n},e.tweenTo=function tweenTo(t,e){e=e||{};var r,i=this,n=Ot(i,t),a=e.startAt,s=e.onStart,o=e.onStartParams,u=e.immediateRender,h=te.to(i,ta({ease:e.ease||"none",lazy:!1,immediateRender:!1,time:n,overwrite:"auto",duration:e.duration||Math.abs((n-(a&&"time"in a?a.time:i._time))/i.timeScale())||X,onStart:function onStart(){if(i.pause(),!r){var t=e.duration||Math.abs((n-(a&&"time"in a?a.time:i._time))/i.timeScale());h._dur!==t&&Ua(h,t,0,1).render(h._time,!0,!0),r=1}s&&s.apply(h,o||[])}},e));return u?h.render(0):h},e.tweenFromTo=function tweenFromTo(t,e,r){return this.tweenTo(e,ta({startAt:{time:Ot(this,t)}},r))},e.recent=function recent(){return this._recent},e.nextLabel=function nextLabel(t){return void 0===t&&(t=this._time),ub(this,Ot(this,t))},e.previousLabel=function previousLabel(t){return void 0===t&&(t=this._time),ub(this,Ot(this,t),1)},e.currentLabel=function currentLabel(t){return arguments.length?this.seek(t,!0):this.previousLabel(this._time+X)},e.shiftChildren=function shiftChildren(t,e,r){void 0===r&&(r=0);var i,n=this._first,a=this.labels;for(t=la(t);n;)n._start>=r&&(n._start+=t,n._end+=t),n=n._next;if(e)for(i in a)a[i]>=r&&(a[i]+=t);return Da(this)},e.invalidate=function invalidate(t){var e=this._first;for(this._lock=0;e;)e.invalidate(t),e=e._next;return i.prototype.invalidate.call(this,t)},e.clear=function clear(t){void 0===t&&(t=!0);for(var e,r=this._first;r;)e=r._next,this.remove(r),r=e;return this._dp&&(this._time=this._tTime=this._pTime=0),t&&(this.labels={}),Da(this)},e.totalDuration=function totalDuration(t){var e,r,i,n=0,a=this,s=a._last,o=q;if(arguments.length)return a.timeScale((a._repeat<0?a.duration():a.totalDuration())/(a.reversed()?-t:t));if(a._dirty){for(i=a.parent;s;)e=s._prev,s._dirty&&s.totalDuration(),o<(r=s._start)&&a._sort&&s._ts&&!a._lock?(a._lock=1,Na(a,s,r-s._delay,1)._lock=0):o=r,r<0&&s._ts&&(n-=r,(!i&&!a._dp||i&&i.smoothChildTiming)&&(a._start+=la(r/a._ts),a._time-=r,a._tTime-=r),a.shiftChildren(-r,!1,-Infinity),o=0),s._end>n&&s._ts&&(n=s._end),s=e;Ua(a,a===I&&a._time>n?a._time:n,1,1),a._dirty=0}return a._tDur},Timeline.updateRoot=function updateRoot(t){if(I._ts&&(qa(I,Ja(t,I)),f=Lt.frame),Lt.frame>=vt){vt+=Y.autoSleep||120;var e=I._first;if((!e||!e._ts)&&Y.autoSleep&&Lt._listeners.length<2){for(;e&&!e._ts;)e=e._next;e||Lt.sleep()}}},Timeline}(Xt);ta(Gt.prototype,{_lock:0,_hasPause:0,_forcing:0});function cc(t,e,i,n,a,o){var u,h,l,f;if(mt[t]&&!1!==(u=new mt[t]).init(a,u.rawVars?e[t]:function _processVars(t,e,i,n,a){if(s(t)&&(t=Qt(t,a,e,i,n)),!v(t)||t.style&&t.nodeType||K(t)||J(t))return r(t)?Qt(t,a,e,i,n):t;var o,u={};for(o in t)u[o]=Qt(t[o],a,e,i,n);return u}(e[t],n,a,o,i),i,n,o)&&(i._pt=h=new we(i._pt,a,t,0,1,u.render,u,0,u.priority),i!==c))for(l=i._ptLookup[i._targets.indexOf(a)],f=u._props.length;f--;)l[u._props[f]]=h;return u}function ic(t,r,e,i){var n,a,s=r.ease||i||"power1.inOut";if(K(r))a=e[t]||(e[t]=[]),r.forEach(function(t,e){return a.push({t:e/(r.length-1)*100,v:t,e:s})});else for(n in r)a=e[n]||(e[n]=[]),"ease"===n||a.push({t:parseFloat(t),v:r[n],e:s})}var Zt,Wt,Ht=function _addPropTween(t,e,i,n,a,o,u,h,l,f){s(n)&&(n=n(a||0,t,o));var c,d=t[e],p="get"!==i?i:s(d)?l?t[e.indexOf("set")||!s(t["get"+e.substr(3)])?e:"get"+e.substr(3)](l):t[e]():d,_=s(d)?l?se:ae:ie;if(r(n)&&(~n.indexOf("random(")&&(n=rb(n)),"="===n.charAt(1)&&(!(c=ma(p,n)+(_a(p)||0))&&0!==c||(n=c))),!f||p!==n||Wt)return isNaN(p*n)||""===n?(d||e in t||S(e,n),function _addComplexStringPropTween(t,e,r,i,n,a,s){var o,u,h,l,f,c,d,p,_=new we(this._pt,t,e,0,1,pe,null,n),m=0,g=0;for(_.b=r,_.e=i,r+="",(d=~(i+="").indexOf("random("))&&(i=rb(i)),a&&(a(p=[r,i],t,e),r=p[0],i=p[1]),u=r.match(at)||[];o=at.exec(i);)l=o[0],f=i.substring(m,o.index),h?h=(h+1)%5:"rgba("===f.substr(-5)&&(h=1),l!==u[g++]&&(c=parseFloat(u[g-1])||0,_._pt={_next:_._pt,p:f||1===g?f:",",s:c,c:"="===l.charAt(1)?ma(c,l)-c:parseFloat(l)-c,m:h&&h<4?Math.round:0},m=at.lastIndex);return _.c=m")}),s.duration();else{for(l in u={},k)"ease"===l||"easeEach"===l||ic(l,k[l],u,k.easeEach);for(l in u)for(A=u[l].sort(function(t,e){return t.t-e.t}),o=R=0;o=t._tDur||e<0)&&t.ratio===u&&(u&&Ca(t,1),r||L||(At(t,u?"onComplete":"onReverseComplete",!0),t._prom&&t._prom()))}else t._zTime||(t._zTime=e)}(this,t,e,r);return this},e.targets=function targets(){return this._targets},e.invalidate=function invalidate(t){return t&&this.vars.runBackwards||(this._startAt=0),this._pt=this._op=this._onUpdate=this._lazy=this.ratio=0,this._ptLookup=[],this.timeline&&this.timeline.invalidate(t),E.prototype.invalidate.call(this,t)},e.resetTo=function resetTo(t,e,r,i,n){d||Lt.wake(),this._ts||this.play();var a,s=Math.min(this._dur,(this._dp._time-this._start)*this._ts);return this._initted||$t(this,s),a=this._ease(s/this._dur),function _updatePropTweens(t,e,r,i,n,a,s,o){var u,h,l,f,c=(t._pt&&t._ptCache||(t._ptCache={}))[e];if(!c)for(c=t._ptCache[e]=[],l=t._ptLookup,f=t._targets.length;f--;){if((u=l[f][e])&&u.d&&u.d._pt)for(u=u.d._pt;u&&u.p!==e&&u.fp!==e;)u=u._next;if(!u)return Wt=1,t.vars[e]="+=0",$t(t,s),Wt=0,o?T(e+" not eligible for reset. Try splitting into individual properties"):1;c.push(u)}for(f=c.length;f--;)(u=(h=c[f])._pt||h).s=!i&&0!==i||n?u.s+(i||0)+a*u.c:i,u.c=r-u.s,h.e&&(h.e=ka(r)+_a(h.e)),h.b&&(h.b=u.s+_a(h.b))}(this,t,e,r,i,a,s,n)?this.resetTo(t,e,r,i,1):(La(this,0),this.parent||Aa(this._dp,this,"_first","_last",this._dp._sort?"_start":0),this.render(0))},e.kill=function kill(t,e){if(void 0===e&&(e="all"),!(t||e&&"all"!==e))return this._lazy=this._pt=0,this.parent?wb(this):this.scrollTrigger&&this.scrollTrigger.kill(!!L),this;if(this.timeline){var i=this.timeline.totalDuration();return this.timeline.killTweensOf(t,e,Zt&&!0!==Zt.vars.overwrite)._first||wb(this),this.parent&&i!==this.timeline.totalDuration()&&Ua(this,this._dur*this.timeline._tDur/i,0,1),this}var n,a,s,o,u,h,l,f=this._targets,c=t?Pt(t):f,d=this._ptLookup,p=this._pt;if((!e||"all"===e)&&function _arraysMatch(t,e){for(var r=t.length,i=r===e.length;i&&r--&&t[r]===e[r];);return r<0}(f,c))return"all"===e&&(this._pt=0),wb(this);for(n=this._op=this._op||[],"all"!==e&&(r(e)&&(u={},ja(e,function(t){return u[t]=1}),e=u),e=function _addAliasesToVars(t,e){var r,i,n,a,s=t[0]?ha(t[0]).harness:0,o=s&&s.aliases;if(!o)return e;for(i in r=bt({},e),o)if(i in r)for(n=(a=o[i].split(",")).length;n--;)r[a[n]]=r[i];return r}(f,e)),l=f.length;l--;)if(~c.indexOf(f[l]))for(u in a=d[l],"all"===e?(n[l]=e,o=a,s={}):(s=n[l]=n[l]||{},o=e),o)(h=a&&a[u])&&("kill"in h.d&&!0!==h.d.kill(u)||Ba(this,h,"_pt"),delete a[u]),"all"!==s&&(s[u]=1);return this._initted&&!this._pt&&p&&wb(this),this},Tween.to=function to(t,e,r){return new Tween(t,e,r)},Tween.from=function from(t,e){return Ya(1,arguments)},Tween.delayedCall=function delayedCall(t,e,r,i){return new Tween(e,0,{immediateRender:!1,lazy:!1,overwrite:!1,delay:t,onComplete:e,onReverseComplete:e,onCompleteParams:r,onReverseCompleteParams:r,callbackScope:i})},Tween.fromTo=function fromTo(t,e,r){return Ya(2,arguments)},Tween.set=function set(t,e){return e.duration=0,e.repeatDelay||(e.repeat=0),new Tween(t,e)},Tween.killTweensOf=function killTweensOf(t,e,r){return I.killTweensOf(t,e,r)},Tween}(Xt);ta(te.prototype,{_targets:[],_lazy:0,_startAt:0,_op:0,_onInit:0}),ja("staggerTo,staggerFrom,staggerFromTo",function(r){te[r]=function(){var t=new Gt,e=Ct.call(arguments,0);return e.splice("staggerFromTo"===r?5:4,0,0),t[r].apply(t,e)}});function qc(t,e,r){return t.setAttribute(e,r)}function yc(t,e,r,i){i.mSet(t,e,i.m.call(i.tween,r,i.mt),i)}var ie=function _setterPlain(t,e,r){return t[e]=r},ae=function _setterFunc(t,e,r){return t[e](r)},se=function _setterFuncWithParam(t,e,r,i){return t[e](i.fp,r)},ue=function _getSetter(t,e){return s(t[e])?ae:u(t[e])&&t.setAttribute?qc:ie},fe=function _renderPlain(t,e){return e.set(e.t,e.p,Math.round(1e6*(e.s+e.c*t))/1e6,e)},de=function _renderBoolean(t,e){return e.set(e.t,e.p,!!(e.s+e.c*t),e)},pe=function _renderComplexString(t,e){var r=e._pt,i="";if(!t&&e.b)i=e.b;else if(1===t&&e.e)i=e.e;else{for(;r;)i=r.p+(r.m?r.m(r.s+r.c*t):Math.round(1e4*(r.s+r.c*t))/1e4)+i,r=r._next;i+=e.c}e.set(e.t,e.p,i,e)},_e=function _renderPropTweens(t,e){for(var r=e._pt;r;)r.r(t,r.d),r=r._next},ve=function _addPluginModifier(t,e,r,i){for(var n,a=this._pt;a;)n=a._next,a.p===i&&a.modifier(t,e,r),a=n},Te=function _killPropTweensOf(t){for(var e,r,i=this._pt;i;)r=i._next,i.p===t&&!i.op||i.op===t?Ba(this,i,"_pt"):i.dep||(e=1),i=r;return!e},be=function _sortPropTweensByPriority(t){for(var e,r,i,n,a=t._pt;a;){for(e=a._next,r=i;r&&r.pr>a.pr;)r=r._next;(a._prev=r?r._prev:n)?a._prev._next=a:i=a,(a._next=r)?r._prev=a:n=a,a=e}t._pt=i},we=(PropTween.prototype.modifier=function modifier(t,e,r){this.mSet=this.mSet||this.set,this.set=yc,this.m=t,this.mt=r,this.tween=e},PropTween);function PropTween(t,e,r,i,n,a,s,o,u){this.t=e,this.s=i,this.c=n,this.p=r,this.r=a||fe,this.d=s||this,this.set=o||ie,this.pr=u||0,(this._next=t)&&(t._prev=this)}ja(Tt+"parent,duration,ease,delay,overwrite,runBackwards,startAt,yoyo,immediateRender,repeat,repeatDelay,data,paused,reversed,lazy,callbackScope,stringFilter,id,yoyoEase,stagger,inherit,repeatRefresh,keyframes,autoRevert,scrollTrigger,easeReverse",function(t){return dt[t]=1}),ht.TweenMax=ht.TweenLite=te,ht.TimelineLite=ht.TimelineMax=Gt,I=new Gt({sortChildren:!1,defaults:j,autoRemoveChildren:!0,id:"root",smoothChildTiming:!0}),Y.stringFilter=Ib;function Gc(t){return(Oe[t]||Me).map(function(t){return t()})}function Hc(){var t=Date.now(),o=[];2=1;let i=n?1:this.easing(r);this.value=this.from+(this.to-this.from)*i}else this.lerp?(this.value=r(this.value,this.to,this.lerp*60,e),Math.round(this.value)===Math.round(this.to)&&(this.value=this.to,n=!0)):(this.value=this.to,n=!0);n&&this.stop(),this.onUpdate?.(this.value,n)}stop(){this.isRunning=!1}fromTo(e,t,{lerp:n,duration:r,easing:i,onStart:a,onUpdate:o}){this.from=this.value=e,this.to=t,this.lerp=n,this.duration=r,this.easing=i,this.currentTime=0,this.isRunning=!0,a?.(),this.onUpdate=o}};function o(e,t){let n;return function(...r){clearTimeout(n),n=setTimeout(()=>{n=void 0,e.apply(this,r)},t)}}var s=class{width=0;height=0;scrollHeight=0;scrollWidth=0;debouncedResize;wrapperResizeObserver;contentResizeObserver;constructor(e,t,{autoResize:n=!0,debounce:r=250}={}){this.wrapper=e,this.content=t,n&&(this.debouncedResize=o(this.resize,r),this.wrapper instanceof Window?window.addEventListener(`resize`,this.debouncedResize):(this.wrapperResizeObserver=new ResizeObserver(this.debouncedResize),this.wrapperResizeObserver.observe(this.wrapper)),this.contentResizeObserver=new ResizeObserver(this.debouncedResize),this.contentResizeObserver.observe(this.content)),this.resize()}destroy(){this.wrapperResizeObserver?.disconnect(),this.contentResizeObserver?.disconnect(),this.wrapper===window&&this.debouncedResize&&window.removeEventListener(`resize`,this.debouncedResize)}resize=()=>{this.onWrapperResize(),this.onContentResize()};onWrapperResize=()=>{this.wrapper instanceof Window?(this.width=window.innerWidth,this.height=window.innerHeight):(this.width=this.wrapper.clientWidth,this.height=this.wrapper.clientHeight)};onContentResize=()=>{this.wrapper instanceof Window?(this.scrollHeight=this.content.scrollHeight,this.scrollWidth=this.content.scrollWidth):(this.scrollHeight=this.wrapper.scrollHeight,this.scrollWidth=this.wrapper.scrollWidth)};get limit(){return{x:this.scrollWidth-this.width,y:this.scrollHeight-this.height}}},c=class{events={};emit(e,...t){let n=this.events[e]||[];for(let e=0,r=n.length;e{this.events[e]=this.events[e]?.filter(e=>t!==e)}}off(e,t){this.events[e]=this.events[e]?.filter(e=>t!==e)}destroy(){this.events={}}};let l={passive:!1};function u(e,t){return e===1?16.666666666666668:e===2?t:1}var d=class{touchStart={x:0,y:0};lastDelta={x:0,y:0};window={width:0,height:0};emitter=new c;constructor(e,t={wheelMultiplier:1,touchMultiplier:1}){this.element=e,this.options=t,window.addEventListener(`resize`,this.onWindowResize),this.onWindowResize(),this.element.addEventListener(`wheel`,this.onWheel,l),this.element.addEventListener(`touchstart`,this.onTouchStart,l),this.element.addEventListener(`touchmove`,this.onTouchMove,l),this.element.addEventListener(`touchend`,this.onTouchEnd,l)}on(e,t){return this.emitter.on(e,t)}destroy(){this.emitter.destroy(),window.removeEventListener(`resize`,this.onWindowResize),this.element.removeEventListener(`wheel`,this.onWheel,l),this.element.removeEventListener(`touchstart`,this.onTouchStart,l),this.element.removeEventListener(`touchmove`,this.onTouchMove,l),this.element.removeEventListener(`touchend`,this.onTouchEnd,l)}onTouchStart=e=>{let{clientX:t,clientY:n}=e.targetTouches?e.targetTouches[0]:e;this.touchStart.x=t,this.touchStart.y=n,this.lastDelta={x:0,y:0},this.emitter.emit(`scroll`,{deltaX:0,deltaY:0,event:e})};onTouchMove=e=>{let{clientX:t,clientY:n}=e.targetTouches?e.targetTouches[0]:e,r=-(t-this.touchStart.x)*this.options.touchMultiplier,i=-(n-this.touchStart.y)*this.options.touchMultiplier;this.touchStart.x=t,this.touchStart.y=n,this.lastDelta={x:r,y:i},this.emitter.emit(`scroll`,{deltaX:r,deltaY:i,event:e})};onTouchEnd=e=>{this.emitter.emit(`scroll`,{deltaX:this.lastDelta.x,deltaY:this.lastDelta.y,event:e})};onWheel=e=>{let{deltaX:t,deltaY:n,deltaMode:r}=e,i=u(r,this.window.width),a=u(r,this.window.height);t*=i,n*=a,t*=this.options.wheelMultiplier,n*=this.options.wheelMultiplier,this.emitter.emit(`scroll`,{deltaX:t,deltaY:n,event:e})};onWindowResize=()=>{this.window={width:window.innerWidth,height:window.innerHeight}}};let f=e=>Math.min(1,1.001-2**(-10*e));var p=class{_isScrolling=!1;_isStopped=!1;_isLocked=!1;_preventNextNativeScrollEvent=!1;_resetVelocityTimeout=null;_rafId=null;_isDraggingSelection=!1;reducedMotionMediaQuery=window.matchMedia(`(prefers-reduced-motion: reduce)`);isTouching;isIos;time=0;userData={};lastVelocity=0;velocity=0;direction=0;options;targetScroll;animatedScroll;animate=new a;emitter=new c;dimensions;virtualScroll;constructor({wrapper:t=window,content:n=document.documentElement,eventsTarget:r=t,smoothWheel:i=!0,syncTouch:a=!1,syncTouchLerp:o=.075,touchInertiaExponent:c=1.7,duration:l,easing:u,lerp:p=.1,infinite:m=!1,orientation:h=`vertical`,gestureOrientation:g=h===`horizontal`?`both`:`vertical`,touchMultiplier:_=1,wheelMultiplier:v=1,autoResize:y=!0,prevent:b,virtualScroll:x,overscroll:S=!0,autoRaf:C=!1,anchors:w=!1,autoToggle:T=!1,allowNestedScroll:E=!1,__experimental__naiveDimensions:D=!1,naiveDimensions:O=D,stopInertiaOnNavigate:k=!1,respectReducedMotion:A=!0}={}){window.lenisVersion=e,window.lenis||(window.lenis={}),window.lenis.version=e,h===`horizontal`&&(window.lenis.horizontal=!0),a===!0&&(window.lenis.touch=!0),this.isIos=/(iPad|iPhone|iPod)/g.test(navigator.userAgent),(!t||t===document.documentElement)&&(t=window),typeof l==`number`&&typeof u!=`function`?u=f:typeof u==`function`&&typeof l!=`number`&&(l=1),this.options={wrapper:t,content:n,eventsTarget:r,smoothWheel:i,syncTouch:a,syncTouchLerp:o,touchInertiaExponent:c,duration:l,easing:u,lerp:p,infinite:m,gestureOrientation:g,orientation:h,touchMultiplier:_,wheelMultiplier:v,autoResize:y,prevent:b,virtualScroll:x,overscroll:S,autoRaf:C,anchors:w,autoToggle:T,allowNestedScroll:E,naiveDimensions:O,stopInertiaOnNavigate:k,respectReducedMotion:A},this.dimensions=new s(t,n,{autoResize:y}),this.updateClassName(),this.targetScroll=this.animatedScroll=this.actualScroll,this.options.wrapper.addEventListener(`scroll`,this.onNativeScroll),this.options.wrapper.addEventListener(`scrollend`,this.onScrollEnd,{capture:!0}),(this.options.anchors||this.options.stopInertiaOnNavigate)&&this.options.wrapper.addEventListener(`click`,this.onClick),this.options.wrapper.addEventListener(`pointerdown`,this.onPointerDown),this.virtualScroll=new d(r,{touchMultiplier:_,wheelMultiplier:v}),this.virtualScroll.on(`scroll`,this.onVirtualScroll),this.options.autoToggle&&(this.checkOverflow(),this.rootElement.addEventListener(`transitionend`,this.onTransitionEnd)),this.options.autoRaf&&(this._rafId=requestAnimationFrame(this.raf))}destroy(){this.emitter.destroy(),this._resetVelocityTimeout!==null&&(clearTimeout(this._resetVelocityTimeout),this._resetVelocityTimeout=null),this.options.wrapper.removeEventListener(`scroll`,this.onNativeScroll),this.options.wrapper.removeEventListener(`scrollend`,this.onScrollEnd,{capture:!0}),this.options.wrapper.removeEventListener(`pointerdown`,this.onPointerDown),(this.options.anchors||this.options.stopInertiaOnNavigate)&&this.options.wrapper.removeEventListener(`click`,this.onClick),this.virtualScroll.destroy(),this.dimensions.destroy(),this.cleanUpClassName(),this._rafId&&cancelAnimationFrame(this._rafId)}on(e,t){return this.emitter.on(e,t)}off(e,t){return this.emitter.off(e,t)}onScrollEnd=e=>{e instanceof CustomEvent||(this.isScrolling===`smooth`||this.isScrolling===!1)&&e.stopPropagation()};dispatchScrollendEvent=()=>{this.options.wrapper.dispatchEvent(new CustomEvent(`scrollend`,{bubbles:this.options.wrapper===window,detail:{lenisScrollEnd:!0}}))};get overflow(){let e=this.isHorizontal?`overflow-x`:`overflow-y`;return getComputedStyle(this.rootElement)[e]}checkOverflow(){[`hidden`,`clip`].includes(this.overflow)?this.internalStop():this.internalStart()}onTransitionEnd=e=>{e.propertyName?.includes(`overflow`)&&e.target===this.rootElement&&this.checkOverflow()};setScroll(e){this.isHorizontal?this.options.wrapper.scrollTo({left:e,behavior:`instant`}):this.options.wrapper.scrollTo({top:e,behavior:`instant`})}onClick=e=>{let t=e.composedPath().filter(e=>e instanceof HTMLAnchorElement&&e.href).map(e=>new URL(e.href)),n=new URL(window.location.href);if(this.options.anchors){let e=t.find(e=>n.host===e.host&&n.pathname===e.pathname&&e.hash);if(e){let t=typeof this.options.anchors==`object`&&this.options.anchors?this.options.anchors:void 0,n=decodeURIComponent(e.hash);this.scrollTo(n,t);return}}if(this.options.stopInertiaOnNavigate&&t.some(e=>n.host===e.host&&n.pathname!==e.pathname)){this.reset();return}};onPointerDown=e=>{e.button===1&&this.reset()};isTouchOnSelectionHandle(e){let t=window.getSelection();if(!t||t.isCollapsed||t.rangeCount===0)return!1;let n=e.targetTouches[0]??e.changedTouches[0];if(!n)return!1;let r=t.getRangeAt(0).getClientRects();if(r.length===0)return!1;let i=r[0],a=r[r.length-1],o=Math.hypot(n.clientX-i.left,n.clientY-i.top)<=40,s=Math.hypot(n.clientX-a.right,n.clientY-a.bottom)<=40;return o||s}onVirtualScroll=e=>{if(typeof this.options.virtualScroll==`function`&&this.options.virtualScroll(e)===!1)return;let{deltaX:t,deltaY:n,event:r}=e;if(this.emitter.emit(`virtual-scroll`,{deltaX:t,deltaY:n,event:r}),r.ctrlKey||r.lenisStopPropagation)return;let i=r.type.includes(`touch`),a=r.type.includes(`wheel`);if(i&&this.isIos&&(r.type===`touchstart`&&(this._isDraggingSelection=this.isTouchOnSelectionHandle(r)),this._isDraggingSelection)){r.type===`touchend`&&(this._isDraggingSelection=!1);return}this.isTouching=r.type===`touchstart`||r.type===`touchmove`;let o=t===0&&n===0;if(this.options.syncTouch&&i&&r.type===`touchstart`&&o&&!this.isStopped&&!this.isLocked){this.reset();return}let s=this.options.gestureOrientation===`vertical`&&n===0||this.options.gestureOrientation===`horizontal`&&t===0;if(o||s)return;let c=r.composedPath();c=c.slice(0,c.indexOf(this.rootElement));let l=this.options.prevent,u=Math.abs(t)>=Math.abs(n)?`horizontal`:`vertical`;if(c.find(e=>e instanceof HTMLElement&&(typeof l==`function`&&l?.(e)||e.hasAttribute?.(`data-lenis-prevent`)||u===`vertical`&&e.hasAttribute?.(`data-lenis-prevent-vertical`)||u===`horizontal`&&e.hasAttribute?.(`data-lenis-prevent-horizontal`)||i&&e.hasAttribute?.(`data-lenis-prevent-touch`)||a&&e.hasAttribute?.(`data-lenis-prevent-wheel`)||this.options.allowNestedScroll&&this.hasNestedScroll(e,{deltaX:t,deltaY:n}))))return;if(this.isStopped||this.isLocked){r.cancelable&&r.preventDefault();return}if(!(this.options.syncTouch&&i||this.options.smoothWheel&&a)){this.isScrolling=`native`,this.animate.stop(),r.lenisStopPropagation=!0;return}let d=n;this.options.gestureOrientation===`both`?d=Math.abs(n)>Math.abs(t)?n:t:this.options.gestureOrientation===`horizontal`&&(d=t),(!this.options.overscroll||this.options.infinite||this.options.wrapper!==window&&this.limit>0&&(this.animatedScroll>0&&this.animatedScroll0||this.animatedScroll===this.limit&&n<0))&&(r.lenisStopPropagation=!0),r.cancelable&&r.preventDefault();let f=i&&this.options.syncTouch,p=i&&r.type===`touchend`;p&&(d=Math.sign(d)*Math.abs(this.velocity)**this.options.touchInertiaExponent),this.scrollTo(this.targetScroll+d,{programmatic:!1,...f?{lerp:p?this.options.syncTouchLerp:1}:{lerp:this.options.lerp,duration:this.options.duration,easing:this.options.easing}})};resize(){this.dimensions.resize(),this.animatedScroll=this.targetScroll=this.actualScroll,this.emit()}emit(){this.emitter.emit(`scroll`,this)}onNativeScroll=()=>{if(this._resetVelocityTimeout!==null&&(clearTimeout(this._resetVelocityTimeout),this._resetVelocityTimeout=null),this._preventNextNativeScrollEvent){this._preventNextNativeScrollEvent=!1;return}if(this.isScrolling===!1||this.isScrolling===`native`){let e=this.animatedScroll;this.animatedScroll=this.targetScroll=this.actualScroll,this.lastVelocity=this.velocity,this.velocity=this.animatedScroll-e,this.direction=Math.sign(this.animatedScroll-e),this.isStopped||(this.isScrolling=`native`),this.emit(),this.velocity!==0&&(this._resetVelocityTimeout=setTimeout(()=>{this.lastVelocity=this.velocity,this.velocity=0,this.isScrolling=!1,this.emit()},400))}};reset(){this.isLocked=!1,this.isScrolling=!1,this.animatedScroll=this.targetScroll=this.actualScroll,this.lastVelocity=this.velocity=0,this.animate.stop()}start(){if(this.isStopped){if(this.options.autoToggle){this.rootElement.style.removeProperty(`overflow`);return}this.internalStart()}}internalStart(){this.isStopped&&(this.reset(),this.isStopped=!1,this.emit())}stop(){if(!this.isStopped){if(this.options.autoToggle){this.rootElement.style.setProperty(`overflow`,`clip`);return}this.internalStop()}}internalStop(){this.isStopped||(this.reset(),this.isStopped=!0,this.emit())}raf=e=>{let t=e-(this.time||e);this.time=e,this.animate.advance(t*.001),this.options.autoRaf&&(this._rafId=requestAnimationFrame(this.raf))};scrollTo(e,{offset:n=0,immediate:r=!1,lock:i=!1,programmatic:a=!0,lerp:o=a?this.options.lerp:void 0,duration:s=a?this.options.duration:void 0,easing:c=a?this.options.easing:void 0,onStart:l,onComplete:u,force:d=!1,userData:p}={}){if(this.prefersReducedMotion&&(a?r=!0:(o=1,s=void 0,c=void 0)),(this.isStopped||this.isLocked)&&!d)return;let m=e,h=n;if(typeof m==`string`&&[`top`,`left`,`start`,`#`].includes(m))m=0;else if(typeof m==`string`&&[`bottom`,`right`,`end`].includes(m))m=this.limit;else{let e=null;if(typeof m==`string`?(e=m.startsWith(`#`)?document.getElementById(m.slice(1)):document.querySelector(m),e||(m===`#top`?m=0:console.warn(`Lenis: Target not found`,m))):m instanceof Element&&(e=m),e){if(this.options.wrapper!==window){let e=this.rootElement.getBoundingClientRect();h-=this.isHorizontal?e.left:e.top}let t=e.getBoundingClientRect(),n=getComputedStyle(e),r=this.isHorizontal?Number.parseFloat(n.scrollMarginLeft):Number.parseFloat(n.scrollMarginTop),i=getComputedStyle(this.rootElement),a=this.isHorizontal?Number.parseFloat(i.scrollPaddingLeft):Number.parseFloat(i.scrollPaddingTop);m=(this.isHorizontal?t.left:t.top)+this.animatedScroll-(Number.isNaN(r)?0:r)-(Number.isNaN(a)?0:a)}}if(typeof m==`number`){if(m+=h,this.options.infinite){if(a){this.targetScroll=this.animatedScroll=this.scroll;let e=m-this.animatedScroll;e>this.limit/2?m-=this.limit:e<-this.limit/2&&(m+=this.limit)}}else m=t(0,m,this.limit);if(m===this.targetScroll){l?.(this),u?.(this);return}if(this.userData=p??{},r){this.animatedScroll=this.targetScroll=m,this.setScroll(this.scroll),this.reset(),this.preventNextNativeScrollEvent(),this.emit(),u?.(this),this.userData={},requestAnimationFrame(()=>{this.dispatchScrollendEvent()});return}a||(this.targetScroll=m),typeof s==`number`&&typeof c!=`function`?c=f:typeof c==`function`&&typeof s!=`number`&&(s=1),this.animate.fromTo(this.animatedScroll,m,{duration:s,easing:c,lerp:o,onStart:()=>{i&&(this.isLocked=!0),this.isScrolling=`smooth`,l?.(this)},onUpdate:(e,t)=>{this.isScrolling=`smooth`,this.lastVelocity=this.velocity,this.velocity=e-this.animatedScroll,this.direction=Math.sign(this.velocity),this.animatedScroll=e,this.setScroll(this.scroll),a&&(this.targetScroll=e),t||this.emit(),t&&(this.reset(),this.emit(),u?.(this),this.userData={},requestAnimationFrame(()=>{this.dispatchScrollendEvent()}),this.preventNextNativeScrollEvent())}})}}preventNextNativeScrollEvent(){this._preventNextNativeScrollEvent=!0,requestAnimationFrame(()=>{this._preventNextNativeScrollEvent=!1})}hasNestedScroll(e,{deltaX:t,deltaY:n}){let r=Date.now();e._lenis||={};let i=e._lenis,a,o,s,c,l,u,d,f,p,m;if(r-(i.time??0)>2e3){i.time=Date.now();let t=window.getComputedStyle(e);if(i.computedStyle=t,a=[`auto`,`overlay`,`scroll`].includes(t.overflowX),o=[`auto`,`overlay`,`scroll`].includes(t.overflowY),l=[`auto`].includes(t.overscrollBehaviorX),u=[`auto`].includes(t.overscrollBehaviorY),i.hasOverflowX=a,i.hasOverflowY=o,!(a||o))return!1;d=e.scrollWidth,f=e.scrollHeight,p=e.clientWidth,m=e.clientHeight,s=d>p,c=f>m,i.isScrollableX=s,i.isScrollableY=c,i.scrollWidth=d,i.scrollHeight=f,i.clientWidth=p,i.clientHeight=m,i.hasOverscrollBehaviorX=l,i.hasOverscrollBehaviorY=u}else s=i.isScrollableX,c=i.isScrollableY,a=i.hasOverflowX,o=i.hasOverflowY,d=i.scrollWidth,f=i.scrollHeight,p=i.clientWidth,m=i.clientHeight,l=i.hasOverscrollBehaviorX,u=i.hasOverscrollBehaviorY;if(!(a&&s||o&&c))return!1;let h=Math.abs(t)>=Math.abs(n)?`horizontal`:`vertical`,g,_,v,y,b,x;if(h===`horizontal`)g=Math.round(e.scrollLeft),_=d-p,v=t,y=a,b=s,x=l;else if(h===`vertical`)g=Math.round(e.scrollTop),_=f-m,v=n,y=o,b=c,x=u;else return!1;return!x&&(g>=_||g<=0)?!0:(v>0?g<_:g>0)&&y&&b}get rootElement(){return this.options.wrapper===window?document.documentElement:this.options.wrapper}get limit(){return this.options.naiveDimensions?this.isHorizontal?this.rootElement.scrollWidth-this.rootElement.clientWidth:this.rootElement.scrollHeight-this.rootElement.clientHeight:this.dimensions.limit[this.isHorizontal?`x`:`y`]}get isHorizontal(){return this.options.orientation===`horizontal`}get actualScroll(){let e=this.options.wrapper;return this.isHorizontal?e.scrollX??e.scrollLeft:e.scrollY??e.scrollTop}get scroll(){return this.options.infinite?i(this.animatedScroll,this.limit):this.animatedScroll}get progress(){return this.limit===0?1:this.scroll/this.limit}get isScrolling(){return this._isScrolling}set isScrolling(e){this._isScrolling!==e&&(this._isScrolling=e,this.updateClassName())}get isStopped(){return this._isStopped}set isStopped(e){this._isStopped!==e&&(this._isStopped=e,this.updateClassName())}get isLocked(){return this._isLocked}set isLocked(e){this._isLocked!==e&&(this._isLocked=e,this.updateClassName())}get isSmooth(){return this.isScrolling===`smooth`}get prefersReducedMotion(){return this.options.respectReducedMotion&&this.reducedMotionMediaQuery.matches}get className(){let e=`lenis`;return this.options.autoToggle&&(e+=` lenis-autoToggle`),this.isStopped&&(e+=` lenis-stopped`),this.isLocked&&(e+=` lenis-locked`),this.isScrolling&&(e+=` lenis-scrolling`),this.isScrolling===`smooth`&&(e+=` lenis-smooth`),e}updateClassName(){this.cleanUpClassName(),this.className.split(` `).forEach(e=>{this.rootElement.classList.add(e)})}cleanUpClassName(){for(let e of Array.from(this.rootElement.classList))(e===`lenis`||e.startsWith(`lenis-`))&&this.rootElement.classList.remove(e)}};globalThis.Lenis=p,globalThis.Lenis.prototype=p.prototype})(); diff --git a/docs/ota/index.html b/docs/ota/index.html index 6367492..dbfe393 100644 --- a/docs/ota/index.html +++ b/docs/ota/index.html @@ -6,14 +6,14 @@ PySpOS 更新包 | GoutouStdio - - + + - +
- + + + + + + + - + + + + + + + - + + + + + + diff --git a/docs/requirements.html b/docs/requirements.html index 42d0f09..4500231 100644 --- a/docs/requirements.html +++ b/docs/requirements.html @@ -6,14 +6,14 @@ 配置要求 | GoutouStdio - - + + - + - + + + + + + diff --git a/docs/spaceos6.html b/docs/spaceos6.html index 5ef6cfa..8b84347 100644 --- a/docs/spaceos6.html +++ b/docs/spaceos6.html @@ -6,14 +6,14 @@ SpaceOS 6(已停更) | GoutouStdio - - + + - + - + + + + + + diff --git a/docs/spaceos7.html b/docs/spaceos7.html index d4e10c2..e75453f 100644 --- a/docs/spaceos7.html +++ b/docs/spaceos7.html @@ -6,14 +6,14 @@ SpaceOS 7(已停更) | GoutouStdio - - + + - + - + + + + + + diff --git a/docs/spaceos8-download.html b/docs/spaceos8-download.html index dafefe4..fa88f9b 100644 --- a/docs/spaceos8-download.html +++ b/docs/spaceos8-download.html @@ -6,14 +6,14 @@ 下载 SpaceOS 8 | GoutouStdio - - + + - + - + + + + + + diff --git a/docs/spaceos8.html b/docs/spaceos8.html index 8b03481..af7baa9 100644 --- a/docs/spaceos8.html +++ b/docs/spaceos8.html @@ -6,14 +6,14 @@ SpaceOS 8 | GoutouStdio - - + + - + - + + + + + + diff --git a/docs/spaceos9-download.html b/docs/spaceos9-download.html index cd82948..d954d72 100644 --- a/docs/spaceos9-download.html +++ b/docs/spaceos9-download.html @@ -6,14 +6,14 @@ 下载 SpaceOS 9 | GoutouStdio - - + + - + - + + + + + + diff --git a/docs/spaceos9.html b/docs/spaceos9.html index 23cca39..ff00511 100644 --- a/docs/spaceos9.html +++ b/docs/spaceos9.html @@ -6,14 +6,14 @@ SpaceOS 9 | GoutouStdio - - + + - + - + + + + + + diff --git a/tools/checks/check_design_layer.py b/tools/checks/check_design_layer.py index dffd4e6..6884360 100644 --- a/tools/checks/check_design_layer.py +++ b/tools/checks/check_design_layer.py @@ -24,8 +24,18 @@ # Site-wide paper grain: a purely decorative pseudo-element carrying a noise background, no text in it, # and pointer-events: none, so it takes no part in interaction. 'body::after', + # Pointer-follow highlight on cards: two empty pseudo-elements (inner glow and lit border) that fade + # in on hover. They hold no text and take no pointer events; the card content never changes opacity. + '.spot::before', + '.spot::after', + '.spot:hover::before', + '.spot:hover::after', } +# opacity is allowed inside @keyframes: an animation only passes through a translucent state on its way +# in or out, and every animation is switched off under prefers-reduced-motion. A resting state that is +# dimmed with opacity is still caught by the rule below. + # Read a CSS file as UTF-8 text. def read(p): @@ -80,10 +90,26 @@ def check_token_refs(all_css, defined, label): return missing -# Flag opacity or filter applied to whole content blocks, whitelist aside. +# Remove every @keyframes block, braces balanced, so its frames are not read as selectors. +def strip_keyframes(css): + out, i = [], 0 + for m in re.finditer(r"@(?:-webkit-)?keyframes\b[^{]*\{", css): + if m.start() < i: + continue + out.append(css[i:m.start()]) + depth, j = 1, m.end() + while j < len(css) and depth: + depth += {"{": 1, "}": -1}.get(css[j], 0) + j += 1 + i = j + out.append(css[i:]) + return "".join(out) + + +# Flag opacity or filter applied to whole content blocks, whitelist and @keyframes aside. def check_no_opacity_on_content(css, name): bad = [] - body = strip_comments(css) + body = strip_keyframes(strip_comments(css)) for m in re.finditer(r'([^{}]+)\{([^{}]*)\}', body): raw_sel, decl = m.group(1).strip(), m.group(2) if not raw_sel or raw_sel.startswith('@'): diff --git a/tools/sync_docs_chrome.py b/tools/sync_docs_chrome.py index 447a31e..a54c504 100644 --- a/tools/sync_docs_chrome.py +++ b/tools/sync_docs_chrome.py @@ -119,6 +119,15 @@ BLOCKS = ("head", "chrome", "foot") +# Third-party animation libraries, loaded in this order before js/motion.js. Versions and licences +# are listed in docs/js/vendor/README.md. +VENDOR_SCRIPTS = [ + "js/vendor/gsap.min.js", + "js/vendor/ScrollTrigger.min.js", + "js/vendor/SplitText.min.js", + "js/vendor/lenis.min.js", +] + # Every page in docs/ and docs/ota/, as paths relative to docs/ with forward slashes. def page_paths(): @@ -261,6 +270,10 @@ def render_foot(page): " ", f' ', ] + # Animation stack, served from docs/js/vendor so the site never depends on a CDN. + # motion.js does nothing when any of these fail to load or the visitor asks for reduced motion. + for rel in VENDOR_SCRIPTS + ["js/motion.js"]: + out.append(f' ') return "\n".join(out) From bfc1c87f80ea3ae16caf8a1dea6bca73cfcd0835 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 8 Nov 2026 01:44:58 +0000 Subject: [PATCH 2/3] =?UTF-8?q?docs:=20=E6=89=8B=E6=9C=BA=E7=AB=AF?= =?UTF-8?q?=E8=A1=A8=E6=A0=BC=E6=94=B9=E4=B8=BA=E5=A0=86=E5=8F=A0=E5=8D=A1?= =?UTF-8?q?=E7=89=87=EF=BC=8C=E7=89=B9=E6=80=A7=E5=88=97=E8=A1=A8=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E7=BC=96=E5=8F=B7=E5=8D=A1=E7=89=87=E7=BD=91=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 表格在 48rem 以下每行变成一张卡片,行标题在上,值前面补列名(main.js 把表头写进 data-label), 不再把说明列挤成几个字宽,徽标也不再被裁掉 - .rows-wide 特性列表改为带 01/02 编号的卡片网格:桌面两列,手机一列 - 中文排版:text-autospace、text-spacing-trim、line-break: strict,段落 text-wrap: pretty 避免末行孤字 - 行内代码缩小一号并用 box-decoration-break: clone,折行时两段都保留完整圆角 - 配置要求页左侧图标标题与其他页的小节标题一样吸顶 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01SdLhesahBsuQSbGntwoQFN --- docs/css/style.css | 115 +++++++++++++++++++++++++++++++++++- docs/index.html | 4 +- docs/js/main.js | 15 +++++ docs/ota/index.html | 4 +- docs/ota/releases.html | 4 +- docs/pyspos.html | 4 +- docs/requirements.html | 4 +- docs/spaceos6.html | 4 +- docs/spaceos7.html | 4 +- docs/spaceos8-download.html | 4 +- docs/spaceos8.html | 4 +- docs/spaceos9-download.html | 4 +- docs/spaceos9.html | 4 +- 13 files changed, 149 insertions(+), 25 deletions(-) diff --git a/docs/css/style.css b/docs/css/style.css index b2c0e90..b5444b8 100644 --- a/docs/css/style.css +++ b/docs/css/style.css @@ -40,8 +40,15 @@ body { -moz-osx-font-smoothing: grayscale; font-feature-settings: "cv11", "ss01"; text-rendering: optimizeLegibility; + /* 中西文之间自动留出间距、行首行尾标点挤压、避免标点出现在行首 */ + text-autospace: normal; + text-spacing-trim: normal; + line-break: strict; } +/* 段落最后一行不要只剩一两个字 */ +p, li, td, dd, figcaption { text-wrap: pretty; } + /* 全站颗粒质感:纯装饰的噪点层,不含文字,不接收指针事件 */ body::after { content: ""; @@ -486,6 +493,8 @@ html.motion:not(.motion-ready) .hero :is(h1, .lede, .actions, .hero-badge, .spec letter-spacing: -0.02em; } .split > div { min-width: 0; } +.split > .split-head { position: sticky; top: calc(var(--pg-header-h) + var(--pg-space-5)); } +.split-head h2 { font-size: var(--pg-text-xl); line-height: var(--pg-leading-tight); letter-spacing: -0.02em; } .stack > * + * { margin-top: var(--pg-space-4); } .stack > h3 { margin-top: var(--pg-space-7); } @@ -613,7 +622,49 @@ html.motion:not(.motion-ready) .hero :is(h1, .lede, .actions, .hero-badge, .spec .rows > li:hover .key { color: var(--pg-accent); } .rows .key a { color: inherit; } .rows p { color: var(--pg-fg-2); } -.rows-wide > li { grid-template-columns: 12rem minmax(0, 1fr); } +/* 特性列表:带编号的卡片网格,标题在上、说明在下 */ +.rows-wide { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: var(--pg-space-4); + counter-reset: feat; +} +.rows-wide > li, +.rows-wide > li:last-child { + display: flex; + flex-direction: column; + gap: var(--pg-space-2); + padding: var(--pg-space-5); + background: var(--pg-bg-card); + border: var(--pg-hairline) solid var(--pg-line-glass); + border-radius: var(--pg-radius-lg); + box-shadow: inset 0 1px 0 var(--pg-line-hi); + counter-increment: feat; + transition: border-color var(--pg-dur-2) ease, translate var(--pg-dur-3) var(--pg-ease-out), + box-shadow var(--pg-dur-2) ease; +} +.rows-wide > li:hover { border-color: var(--pg-line-strong); translate: 0 -0.1875rem; box-shadow: var(--pg-shadow); } +/* 编号:01、02……,等宽字体、品牌渐变 */ +.rows-wide > li::before, +.rows-wide > li:hover::before { + content: counter(feat, decimal-leading-zero); + position: static; + width: auto; + height: auto; + font-family: var(--pg-font-mono); + font-size: var(--pg-text-xs); + font-weight: 700; + letter-spacing: var(--pg-tracking-label); + line-height: 1; + background: var(--pg-grad-brand); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + -webkit-text-fill-color: transparent; + transform: none; +} +.rows-wide .key { font-size: var(--pg-text-lg); } +.rows-wide p { font-size: var(--pg-text-sm); line-height: 1.8; } .rows p + p { margin-top: var(--pg-space-2); } /* ---------- 代码 ---------- */ @@ -622,11 +673,15 @@ code, kbd, pre { font-size: 0.875em; } code { - padding: 0.1em 0.4em; + padding: 0.08em 0.38em; + font-size: 0.84em; background: var(--pg-accent-soft); border: var(--pg-hairline) solid var(--pg-line-glass); border-radius: var(--pg-radius-sm); overflow-wrap: anywhere; + /* 折行时每一段都保留完整的圆角和内边距 */ + -webkit-box-decoration-break: clone; + box-decoration-break: clone; } pre { position: relative; @@ -1235,7 +1290,10 @@ html.vt-theme::view-transition-new(root) { animation: none; mix-blend-mode: norm .container { padding-inline: var(--pg-space-4); } .hero { padding-block: var(--pg-space-8) var(--pg-space-7); } .hero-split, .split, .release { grid-template-columns: minmax(0, 1fr); gap: var(--pg-space-5); } - .split > h2 { position: static; } + .split > h2, .split > .split-head { position: static; } + .rows-wide { grid-template-columns: minmax(0, 1fr); gap: var(--pg-space-3); } + .rows-wide > li, .rows-wide > li:last-child { padding: var(--pg-space-4) var(--pg-space-5); } + .lede { font-size: 1.0625rem; } .section { padding-block: var(--pg-space-8); } .rows > li, .rows-wide > li { grid-template-columns: minmax(0, 1fr); gap: var(--pg-space-1); } .points { grid-template-columns: minmax(0, 1fr); } @@ -1273,6 +1331,57 @@ html.vt-theme::view-transition-new(root) { animation: none; mix-blend-mode: norm .site-nav .menu.is-current > summary::after { left: var(--pg-space-3); margin-left: 0; } } +/* 手机上表格改成一行一张卡片:行标题在上,每个值前面补上列名(由 main.js 写进 data-label)。 + 这些表每一行彼此独立,不需要跨行横向比较,所以堆叠比横向滚动更好读。 */ +@media (max-width: 48rem) { + .table-wrap { + padding: 0; + overflow: visible; + background: none; + border: 0; + border-radius: 0; + box-shadow: none; + } + .table-wrap :is(table, tbody, tr, th, td) { display: block; width: auto; } + .table-wrap thead { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip-path: inset(50%); + white-space: nowrap; + } + .table-wrap tr { + padding: var(--pg-space-4) var(--pg-space-5); + background: var(--pg-bg-card); + border: var(--pg-hairline) solid var(--pg-line-glass); + border-radius: var(--pg-radius-lg); + box-shadow: inset 0 1px 0 var(--pg-line-hi); + } + .table-wrap tr + tr { margin-top: var(--pg-space-3); } + .table-wrap tbody tr:hover { background: var(--pg-bg-card); } + .table-wrap :is(th, td), + .table-wrap tbody tr:last-child > * { padding: 0; border: 0; text-align: left; white-space: normal; } + .table-wrap tbody th { font-size: var(--pg-text-md); line-height: var(--pg-leading-tight); } + .table-wrap tbody th + td, + .table-wrap td + td { margin-top: var(--pg-space-2); } + .table-wrap td:empty { display: none; } + .table-wrap td[data-label]::before { + content: attr(data-label); + display: block; + margin-bottom: 0.125rem; + font-family: var(--pg-font-mono); + font-size: var(--pg-text-2xs); + font-weight: 600; + letter-spacing: var(--pg-tracking-label); + text-transform: uppercase; + color: var(--pg-fg-3); + } + .table-wrap .badge { white-space: normal; } + .table-wrap td.num { text-align: left; } + .num .bar { margin-left: 0; } +} + /* 用户要求减少动态效果时,关掉所有装饰性动画与过渡 */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { diff --git a/docs/index.html b/docs/index.html index 2b32ed3..87bc0b0 100644 --- a/docs/index.html +++ b/docs/index.html @@ -7,7 +7,7 @@ GoutouStdio - + @@ -239,7 +239,7 @@ - + diff --git a/docs/js/main.js b/docs/js/main.js index d9013cb..bd3d98a 100644 --- a/docs/js/main.js +++ b/docs/js/main.js @@ -132,10 +132,25 @@ }); } + // 手机上表格会堆叠成卡片,每个单元格前要显示列名;这里把表头文字抄到 data-label。 + function initTableLabels() { + document.querySelectorAll("main table").forEach(function (table) { + var heads = table.querySelectorAll("thead th"); + if (!heads.length) return; + table.querySelectorAll("tbody tr").forEach(function (tr) { + Array.prototype.forEach.call(tr.children, function (cell, i) { + var label = heads[i] && heads[i].textContent.trim(); + if (cell.tagName === "TD" && label) cell.setAttribute("data-label", label); + }); + }); + }); + } + function init() { initTheme(); initMenu(); initCopy(); + initTableLabels(); } if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", init); else init(); diff --git a/docs/ota/index.html b/docs/ota/index.html index dbfe393..986860b 100644 --- a/docs/ota/index.html +++ b/docs/ota/index.html @@ -7,7 +7,7 @@ PySpOS 更新包 | GoutouStdio - + @@ -224,7 +224,7 @@ - + diff --git a/docs/ota/releases.html b/docs/ota/releases.html index b2ab740..7dba6a1 100644 --- a/docs/ota/releases.html +++ b/docs/ota/releases.html @@ -7,7 +7,7 @@ PySpOS 版本历史 | GoutouStdio - + @@ -154,7 +154,7 @@ - + diff --git a/docs/pyspos.html b/docs/pyspos.html index 8b923f6..22b8a50 100644 --- a/docs/pyspos.html +++ b/docs/pyspos.html @@ -7,7 +7,7 @@ PySpOS | GoutouStdio - + @@ -289,7 +289,7 @@ - + diff --git a/docs/requirements.html b/docs/requirements.html index 4500231..f806bc5 100644 --- a/docs/requirements.html +++ b/docs/requirements.html @@ -7,7 +7,7 @@ 配置要求 | GoutouStdio - + @@ -223,7 +223,7 @@ - + diff --git a/docs/spaceos6.html b/docs/spaceos6.html index 8b84347..813e2fe 100644 --- a/docs/spaceos6.html +++ b/docs/spaceos6.html @@ -7,7 +7,7 @@ SpaceOS 6(已停更) | GoutouStdio - + @@ -189,7 +189,7 @@ - + diff --git a/docs/spaceos7.html b/docs/spaceos7.html index e75453f..c784bb5 100644 --- a/docs/spaceos7.html +++ b/docs/spaceos7.html @@ -7,7 +7,7 @@ SpaceOS 7(已停更) | GoutouStdio - + @@ -198,7 +198,7 @@ - + diff --git a/docs/spaceos8-download.html b/docs/spaceos8-download.html index fa88f9b..a7e9047 100644 --- a/docs/spaceos8-download.html +++ b/docs/spaceos8-download.html @@ -7,7 +7,7 @@ 下载 SpaceOS 8 | GoutouStdio - + @@ -239,7 +239,7 @@ - + diff --git a/docs/spaceos8.html b/docs/spaceos8.html index af7baa9..ebebee6 100644 --- a/docs/spaceos8.html +++ b/docs/spaceos8.html @@ -7,7 +7,7 @@ SpaceOS 8 | GoutouStdio - + @@ -343,7 +343,7 @@ - + diff --git a/docs/spaceos9-download.html b/docs/spaceos9-download.html index d954d72..0e6b01f 100644 --- a/docs/spaceos9-download.html +++ b/docs/spaceos9-download.html @@ -7,7 +7,7 @@ 下载 SpaceOS 9 | GoutouStdio - + @@ -232,7 +232,7 @@ - + diff --git a/docs/spaceos9.html b/docs/spaceos9.html index ff00511..b58e6bd 100644 --- a/docs/spaceos9.html +++ b/docs/spaceos9.html @@ -7,7 +7,7 @@ SpaceOS 9 | GoutouStdio - + @@ -245,7 +245,7 @@ - + From 96c65f4948e9448f44efbdffb8fcbc67534024b1 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 8 Nov 2026 02:02:08 +0000 Subject: [PATCH 3/3] =?UTF-8?q?docs:=20=E9=95=BF=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E6=8A=98=E5=8F=A0=E3=80=81=E6=A8=AA=E6=BB=91?= =?UTF-8?q?=E5=92=8C=E6=9C=AC=E9=A1=B5=E7=9B=AE=E5=BD=95=EF=BC=8C=E4=BB=8B?= =?UTF-8?q?=E7=BB=8D=E9=A1=B5=E5=A4=A7=E5=B9=85=E7=BC=A9=E7=9F=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 小节多的页面在首屏下方加吸顶的本页目录,滚动时高亮当前小节,页眉收起时贴顶 - 特性卡片:电脑上先显示 4 张,其余用「展开其余 N 项」按钮展开; 手机上改为横向滑动的卡片带,下方有进度条和「当前 / 总数」 - 超过 5 行的表格先显示 4 行;配置要求页的表格标了 data-nofold,不折叠 - 手机表格卡片:状态徽标和数字列挪到标题右边,说明列不再重复显示列名 - 窄屏代码块的复制按钮移到代码上方,不再压住长命令 - 全部是渐进增强:没有脚本时内容全部平铺 - 手机上 PySpOS 页从约 10.7 屏缩到 7.6 屏,SpaceOS 8 页从 16.3 屏缩到 11.7 屏 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01SdLhesahBsuQSbGntwoQFN --- docs/css/style.css | 164 +++++++++++++++++++++++++++++++++++- docs/index.html | 6 +- docs/js/main.js | 144 ++++++++++++++++++++++++++++++- docs/js/motion.js | 10 +++ docs/ota/index.html | 6 +- docs/ota/releases.html | 6 +- docs/pyspos.html | 6 +- docs/requirements.html | 12 +-- docs/spaceos6.html | 6 +- docs/spaceos7.html | 6 +- docs/spaceos8-download.html | 6 +- docs/spaceos8.html | 6 +- docs/spaceos9-download.html | 6 +- docs/spaceos9.html | 6 +- 14 files changed, 351 insertions(+), 39 deletions(-) diff --git a/docs/css/style.css b/docs/css/style.css index b5444b8..461676b 100644 --- a/docs/css/style.css +++ b/docs/css/style.css @@ -1331,6 +1331,142 @@ html.vt-theme::view-transition-new(root) { animation: none; mix-blend-mode: norm .site-nav .menu.is-current > summary::after { left: var(--pg-space-3); margin-left: 0; } } + +/* ---------- 长内容:折叠按钮、横滑卡片、本页目录 ---------- */ +/* 折叠:表格只露前 4 行、特性卡片在电脑上只露前 4 张,底部渐隐提示下面还有 */ +table.is-collapsed tbody > tr:nth-child(n+5) { display: none; } +.rows-wide.is-collapsed > li:nth-child(n+5) { display: none; } +.table-wrap:has(table.is-collapsed), +.rows-wide.is-collapsed { + -webkit-mask-image: linear-gradient(180deg, black 65%, transparent); + mask-image: linear-gradient(180deg, black 65%, transparent); +} +.fold-btn { + display: flex; + align-items: center; + justify-content: center; + gap: var(--pg-space-2); + width: 100%; + margin-top: var(--pg-space-3); + padding: var(--pg-space-3) var(--pg-space-4); + font: inherit; + font-size: var(--pg-text-sm); + font-weight: 600; + color: var(--pg-fg-1); + background: var(--pg-bg-card); + border: var(--pg-hairline) dashed var(--pg-line-strong); + border-radius: var(--pg-radius-lg); + cursor: pointer; + transition: border-color var(--pg-dur-2) ease, background-color var(--pg-dur-2) ease; +} +.fold-btn:hover { border-color: var(--pg-accent); border-style: solid; background: var(--pg-accent-soft); } +.fold-btn .icon { transition: transform var(--pg-dur-2) var(--pg-ease-spring); } +.fold-btn[aria-expanded="true"] .icon { transform: rotate(180deg); } +.swipe-meta { display: none; } +/* 列表直接放在两栏 .split 里时,按钮跟着列表待在右栏,并收紧和列表之间的行距 */ +@media (min-width: 56.01rem) { + .split > .fold-btn { grid-column: 2; margin-top: calc(var(--pg-space-7) * -1 + var(--pg-space-3)); } +} + +/* 本页目录:首屏下方吸顶;页眉收起时贴到最顶上 */ +.page-toc { + position: sticky; + top: var(--pg-header-h); + z-index: 40; + background: var(--pg-bg-glass); + border-block: var(--pg-hairline) solid var(--pg-line-glass); + -webkit-backdrop-filter: saturate(1.6) blur(var(--pg-blur)); + backdrop-filter: saturate(1.6) blur(var(--pg-blur)); + transition: top var(--pg-dur-3) var(--pg-ease-out); +} +.site-header.is-hidden ~ main .page-toc { top: 0; } +html.has-toc { scroll-padding-top: calc(var(--pg-header-h) + 3.5rem); } +.page-toc ul { + display: flex; + gap: var(--pg-space-1); + max-width: var(--pg-container); + margin: 0 auto; + padding: var(--pg-space-2) var(--pg-space-5); + overflow-x: auto; + list-style: none; + scrollbar-width: none; +} +.page-toc ul::-webkit-scrollbar { display: none; } +.page-toc li { flex: none; margin: 0; } +.page-toc a { + display: block; + padding: var(--pg-space-1) var(--pg-space-3); + font-size: var(--pg-text-xs); + font-weight: 500; + white-space: nowrap; + color: var(--pg-fg-2); + text-decoration: none; + border: var(--pg-hairline) solid transparent; + border-radius: var(--pg-radius-pill); + transition: color var(--pg-dur-1) ease, background-color var(--pg-dur-2) ease, border-color var(--pg-dur-2) ease; +} +.page-toc a:hover { color: var(--pg-fg-1); background: var(--pg-accent-soft); } +.page-toc a[aria-current] { + color: var(--pg-fg-1); + font-weight: 650; + background: var(--pg-accent-soft); + border-color: var(--pg-line-strong); +} +.page-toc + .section::before { display: none; } + +/* 手机:特性卡片改成横向滑动,露出下一张的边缘提示还能滑 */ +@media (max-width: 48rem) { + .rows-wide, + .rows-wide.is-collapsed { + grid-template-columns: none; + grid-auto-flow: column; + grid-auto-columns: 84%; + margin-inline: calc(var(--pg-space-4) * -1); + padding: 0 var(--pg-space-4) var(--pg-space-1); + overflow-x: auto; + overscroll-behavior-x: contain; + scroll-snap-type: x mandatory; + scroll-padding-inline: var(--pg-space-4); + scrollbar-width: none; + -webkit-mask-image: none; + mask-image: none; + } + .rows-wide::-webkit-scrollbar { display: none; } + .rows-wide > li { scroll-snap-align: start; align-self: start; } + .split > .swipe-meta { margin-top: calc(var(--pg-space-5) * -1 + var(--pg-space-3)); } + .rows-wide.is-collapsed > li:nth-child(n+5) { display: flex; } + .fold-btn.fold-cards { display: none; } + .swipe-meta { + display: flex; + align-items: center; + gap: var(--pg-space-3); + margin-top: var(--pg-space-3); + font-family: var(--pg-font-mono); + font-size: var(--pg-text-2xs); + color: var(--pg-fg-3); + } + .swipe-meta[hidden] { display: none; } + .swipe-track { + position: relative; + flex: 1; + height: 0.1875rem; + overflow: hidden; + background: var(--pg-line-2); + border-radius: var(--pg-radius-pill); + } + .swipe-track i { + position: absolute; + top: 0; + bottom: 0; + left: 0; + background: var(--pg-grad-brand); + border-radius: inherit; + } + .page-toc ul { padding-inline: var(--pg-space-4); } + /* 窄屏代码块:复制按钮放到代码上方一条,不再压住长命令 */ + pre:has(.copy-btn) { padding-top: calc(var(--pg-space-7) + var(--pg-space-1)); padding-right: var(--pg-space-5); } +} + /* 手机上表格改成一行一张卡片:行标题在上,每个值前面补上列名(由 main.js 写进 data-label)。 这些表每一行彼此独立,不需要跨行横向比较,所以堆叠比横向滚动更好读。 */ @media (max-width: 48rem) { @@ -1378,8 +1514,34 @@ html.vt-theme::view-transition-new(root) { animation: none; mix-blend-mode: norm color: var(--pg-fg-3); } .table-wrap .badge { white-space: normal; } + /* 只有一个状态徽标的格子不再单占一行:挪到卡片标题右边,也不再显示列名 */ + .table-wrap tbody tr:has(> th) { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + column-gap: var(--pg-space-3); + align-items: start; + } + .table-wrap tbody tr:has(> th) > * { grid-column: 1 / -1; } + .table-wrap tbody tr:has(> th) > th { grid-column: 1; grid-row: 1; } + .table-wrap tbody tr > td:has(> .badge:only-child) { + grid-column: 2; + grid-row: 1; + margin-top: 0; + } + .table-wrap td:has(> .badge:only-child)::before { display: none; } + /* 数字列(行数等)同样放到标题右边,条形图缩短后跟在数字上方 */ + .table-wrap tbody tr > td.num { + grid-column: 2; + grid-row: 1; + margin-top: 0; + text-align: right; + } + .table-wrap td.num::before { display: none; } + .table-wrap td.num .bar { width: 4.5rem; margin-left: auto; } + .table-wrap tbody tr > th + td + td.num, + .table-wrap tbody tr > th + td:has(+ td.num) { margin-top: var(--pg-space-2); } + .table-wrap tbody tr > th + td:has(> .badge:only-child) + td { margin-top: var(--pg-space-2); } .table-wrap td.num { text-align: left; } - .num .bar { margin-left: 0; } } /* 用户要求减少动态效果时,关掉所有装饰性动画与过渡 */ diff --git a/docs/index.html b/docs/index.html index 87bc0b0..7506733 100644 --- a/docs/index.html +++ b/docs/index.html @@ -7,7 +7,7 @@ GoutouStdio - + @@ -239,12 +239,12 @@ - + - + diff --git a/docs/js/main.js b/docs/js/main.js index bd3d98a..bcfb01d 100644 --- a/docs/js/main.js +++ b/docs/js/main.js @@ -137,20 +137,160 @@ document.querySelectorAll("main table").forEach(function (table) { var heads = table.querySelectorAll("thead th"); if (!heads.length) return; - table.querySelectorAll("tbody tr").forEach(function (tr) { + var rows = table.querySelectorAll("tbody tr"); + // 文字最长的那一列就是说明本身,不需要再标列名,免得每张卡片都多一行「说明」「作用」 + var totals = []; + rows.forEach(function (tr) { + Array.prototype.forEach.call(tr.children, function (cell, i) { + totals[i] = (totals[i] || 0) + cell.textContent.trim().length; + }); + }); + var main = totals.indexOf(Math.max.apply(null, totals)); + rows.forEach(function (tr) { Array.prototype.forEach.call(tr.children, function (cell, i) { var label = heads[i] && heads[i].textContent.trim(); - if (cell.tagName === "TD" && label) cell.setAttribute("data-label", label); + if (cell.tagName === "TD" && label && i !== main) cell.setAttribute("data-label", label); }); }); }); } + + /* ---------- 长内容:折叠、横滑、本页目录 ---------- + 三项都只在有脚本时生效;没有脚本时内容全部平铺,一个字都不会被藏起来。 */ + + var FOLD_ROWS = 4; // 表格超过 FOLD_ROWS + 1 行时,先显示 FOLD_ROWS 行 + var FOLD_CARDS = 4; // 特性卡片超过 FOLD_CARDS 张时,电脑上先显示 FOLD_CARDS 张 + var uid = 0; + + function makeFold(list, items, keep, extraClass) { + if (items.length <= keep + 1) return; + if (!list.id) list.id = "fold-" + (++uid); + var rest = items.length - keep; + var btn = document.createElement("button"); + btn.type = "button"; + btn.className = "fold-btn" + (extraClass ? " " + extraClass : ""); + btn.setAttribute("aria-controls", list.id); + function set(open) { + list.classList.toggle("is-collapsed", !open); + btn.setAttribute("aria-expanded", String(open)); + btn.innerHTML = (open ? "收起" : "展开其余 " + rest + " 项") + + ''; + } + set(false); + btn.addEventListener("click", function () { + var open = list.classList.contains("is-collapsed"); + set(open); + if (open) { + if (window.pgMotion) window.pgMotion.show(items.slice(keep)); + } else { + // 收起时把按钮带回视野,免得停在一大片空白里 + var top = list.getBoundingClientRect().top; + if (top < 0) list.scrollIntoView({ block: "start" }); + } + if (window.ScrollTrigger) window.ScrollTrigger.refresh(); + }); + var anchor = list.closest(".table-wrap") || list; + anchor.insertAdjacentElement("afterend", btn); + } + + function initFold() { + document.querySelectorAll("main .table-wrap:not([data-nofold]) table").forEach(function (table) { + var rows = Array.prototype.slice.call(table.querySelectorAll("tbody > tr")); + makeFold(table, rows, FOLD_ROWS); + }); + document.querySelectorAll("main .rows-wide:not([data-nofold])").forEach(function (list) { + var items = Array.prototype.slice.call(list.children); + // 手机上这类列表改为横滑卡片,不需要折叠按钮(CSS 在窄屏隐藏 .fold-cards) + makeFold(list, items, FOLD_CARDS, "fold-cards"); + }); + } + + // 手机上特性卡片横向滑动:下方一条进度条和「当前 / 总数」 + function initCarousel() { + document.querySelectorAll("main .rows-wide").forEach(function (list) { + var total = list.children.length; + if (total < 2) return; + var meta = document.createElement("div"); + meta.className = "swipe-meta"; + meta.setAttribute("aria-hidden", "true"); + meta.innerHTML = ''; + list.insertAdjacentElement("afterend", meta); + var thumb = meta.querySelector("i"); + var count = meta.querySelector(".swipe-count"); + function update() { + var max = list.scrollWidth - list.clientWidth; + if (max <= 1) { meta.hidden = true; return; } + meta.hidden = false; + var ratio = list.clientWidth / list.scrollWidth; + var p = list.scrollLeft / max; + thumb.style.width = (ratio * 100) + "%"; + thumb.style.transform = "translateX(" + (p * (1 / ratio - 1) * 100) + "%)"; + var card = list.children[0].getBoundingClientRect().width || 1; + var idx = Math.min(total, Math.round(list.scrollLeft / card) + 1); + count.textContent = "左右滑动 · " + idx + " / " + total; + } + list.addEventListener("scroll", update, { passive: true }); + window.addEventListener("resize", update); + update(); + }); + } + + // 本页目录:小节多的页面,在首屏下方放一条吸顶的目录,滚动时高亮当前小节 + function initToc() { + var main = document.querySelector("main"); + if (!main) return; + var sections = Array.prototype.slice.call(main.querySelectorAll(":scope > section.section[id]")).filter(function (s) { + return s.querySelector("h2"); + }); + if (sections.length < 4) return; + var nav = document.createElement("nav"); + nav.className = "page-toc"; + nav.setAttribute("aria-label", "本页目录"); + var ul = document.createElement("ul"); + var links = sections.map(function (s) { + var li = document.createElement("li"); + var a = document.createElement("a"); + a.href = "#" + s.id; + a.textContent = s.querySelector("h2").textContent.trim(); + li.appendChild(a); + ul.appendChild(li); + return a; + }); + nav.appendChild(ul); + var hero = main.querySelector(":scope > .hero"); + if (hero) hero.insertAdjacentElement("afterend", nav); + else main.prepend(nav); + document.documentElement.classList.add("has-toc"); + + if (!("IntersectionObserver" in window)) return; + var current = null; + function activate(a) { + if (current === a) return; + if (current) current.removeAttribute("aria-current"); + current = a; + if (!a) return; + a.setAttribute("aria-current", "true"); + // 只横向滚动目录本身,不带动页面 + var left = a.offsetLeft - (ul.clientWidth - a.offsetWidth) / 2; + ul.scrollTo({ left: Math.max(0, left), behavior: "smooth" }); + } + var io = new IntersectionObserver(function (entries) { + entries.forEach(function (e) { + if (e.isIntersecting) activate(links[sections.indexOf(e.target)]); + }); + }, { rootMargin: "-40% 0px -55% 0px" }); + sections.forEach(function (s) { io.observe(s); }); + } + function init() { initTheme(); initMenu(); initCopy(); initTableLabels(); + initToc(); + initFold(); + initCarousel(); } if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", init); else init(); diff --git a/docs/js/motion.js b/docs/js/motion.js index 6961ee5..1a9ccf5 100644 --- a/docs/js/motion.js +++ b/docs/js/motion.js @@ -449,6 +449,16 @@ reveal($$(".footer-grid > *", footer), { y: 24 }, { stagger: 0.07, start: "top 95%" }); } + // main.js 展开折叠内容后调用:新露出的条目依次浮现 + window.pgMotion = { + show: function (items) { + gsap.fromTo(items, { y: 24, autoAlpha: 0 }, { + y: 0, autoAlpha: 1, duration: 0.7, stagger: 0.05, ease: "expo.out", overwrite: true, + clearProps: "transform,opacity,visibility" + }); + } + }; + function init() { initLenis(); initChrome(); diff --git a/docs/ota/index.html b/docs/ota/index.html index 986860b..ad9e692 100644 --- a/docs/ota/index.html +++ b/docs/ota/index.html @@ -7,7 +7,7 @@ PySpOS 更新包 | GoutouStdio - + @@ -224,12 +224,12 @@ - + - + + - + + - + diff --git a/docs/requirements.html b/docs/requirements.html index f806bc5..549df5e 100644 --- a/docs/requirements.html +++ b/docs/requirements.html @@ -7,7 +7,7 @@ 配置要求 | GoutouStdio - + @@ -88,7 +88,7 @@

配置要求

PySpOS 3.2.0

纯用户态,不直接操作硬件,能跑 Python 的机器就能跑。

-
+
@@ -110,7 +110,7 @@

配置要求

SpaceOS 9 公开预览

目前只在 QEMU 上验证过。

-
+
Python3.10 或更高。CI 覆盖 3.10、3.12、3.14。
@@ -139,7 +139,7 @@

配置要求

表里“已验证”是实际跑过的组合,“默认关闭”的需要自己在 make menuconfig 里打开。

-
+
虚拟机x86_64 QEMU,推荐 Q35 机型和 VirtIO GPU 2D。
@@ -223,12 +223,12 @@ - + - + diff --git a/docs/spaceos6.html b/docs/spaceos6.html index 813e2fe..c5ea896 100644 --- a/docs/spaceos6.html +++ b/docs/spaceos6.html @@ -7,7 +7,7 @@ SpaceOS 6(已停更) | GoutouStdio - + @@ -189,12 +189,12 @@ - + - + diff --git a/docs/spaceos7.html b/docs/spaceos7.html index c784bb5..b56f7cc 100644 --- a/docs/spaceos7.html +++ b/docs/spaceos7.html @@ -7,7 +7,7 @@ SpaceOS 7(已停更) | GoutouStdio - + @@ -198,12 +198,12 @@ - + - + diff --git a/docs/spaceos8-download.html b/docs/spaceos8-download.html index a7e9047..0284059 100644 --- a/docs/spaceos8-download.html +++ b/docs/spaceos8-download.html @@ -7,7 +7,7 @@ 下载 SpaceOS 8 | GoutouStdio - + @@ -239,12 +239,12 @@ - + - + diff --git a/docs/spaceos8.html b/docs/spaceos8.html index ebebee6..a9e16a2 100644 --- a/docs/spaceos8.html +++ b/docs/spaceos8.html @@ -7,7 +7,7 @@ SpaceOS 8 | GoutouStdio - + @@ -343,12 +343,12 @@ - + - + diff --git a/docs/spaceos9-download.html b/docs/spaceos9-download.html index 0e6b01f..cc763ae 100644 --- a/docs/spaceos9-download.html +++ b/docs/spaceos9-download.html @@ -7,7 +7,7 @@ 下载 SpaceOS 9 | GoutouStdio - + @@ -232,12 +232,12 @@ - + - + diff --git a/docs/spaceos9.html b/docs/spaceos9.html index b58e6bd..61436be 100644 --- a/docs/spaceos9.html +++ b/docs/spaceos9.html @@ -7,7 +7,7 @@ SpaceOS 9 | GoutouStdio - + @@ -245,12 +245,12 @@ - + - +
硬件要求状态