From 8974fc4e74f13e563e42cf2c5c7250efca5a59df Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 7 Nov 2026 00:53:44 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=AE=98=E7=BD=91=E6=94=B9=E4=B8=BA=20?= =?UTF-8?q?Anthropic=20=E9=A3=8E=E6=A0=BC=E7=9A=84=E6=9A=96=E8=89=B2?= =?UTF-8?q?=E7=89=88=E5=BC=8F=E5=B9=B6=E9=87=8D=E5=86=99=E6=96=87=E6=A1=88?= =?UTF-8?q?=EF=BC=8C=E6=89=80=E6=9C=89=E9=A1=B5=E9=9D=A2=E8=A1=A5=E4=B8=8A?= =?UTF-8?q?=20SpaceOS=209=20=E8=8F=9C=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 版式 - 暖米色底、近黑暖灰字、陶土色点缀;标题用衬线,正文用无衬线 - 顶部导航 + 下拉菜单(下载、存档)、首屏大标题、分区留白、扁平色块 - 页脚改为站点地图;去掉 3D 架构图、发光、滚动动效和卡片图标 - 配色值参考第三方整理的 Anthropic 色板,不是官方数据,用对比度检查兜底 文案 - 去掉口号式标题和空泛形容,改用版本号、路径、命令和边界条件 - 保留所有“已验证 / 未验证”的标注 - 存档页的提示改为指向 PySpOS 和 SpaceOS 9(原来只写 PySpOS) - 配置要求页补上 SpaceOS 9 菜单缺失的根因与修复 - 8 个页面各抄一份导航,新增 SpaceOS 9 时漏改 - 新增 tools/sync_docs_chrome.py:页眉、菜单、页脚、CSS/JS 版本号只在这里定义, 由页面里的标记区间生成;check_docs_html 校验各页与它一致,并断言每页都有 SpaceOS 9 入口 顺带修复 - check_docs_html 不认识首页的 SpaceOS 9,CI 的 Docs 任务和 tests/web 已经是红的 - ota/releases.html 的兜底变量名与 build_update.py 约定不一致,同步一直在静默跳过 - 版本历史页的更新内容改成按行渲染的列表 Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01SdLheqvbhbobVxS1NvgLpw --- docs/css/style.css | 1689 ++++++------------------- docs/css/tokens.css | 257 ++-- docs/index.html | 387 ++---- docs/js/main.js | 350 ++--- docs/js/theme-init.js | 34 +- docs/ota/index.html | 283 +++-- docs/ota/releases.html | 347 ++--- docs/pyspos.html | 406 +++--- docs/requirements.html | 342 +++-- docs/spaceos6.html | 279 ++-- docs/spaceos7.html | 305 ++--- docs/spaceos8-download.html | 298 +++-- docs/spaceos8.html | 506 ++++---- docs/spaceos9-download.html | 276 ++-- docs/spaceos9.html | 300 +++-- tools/checks/check_docs_html.py | 80 +- tools/checks/check_tokens_contrast.py | 20 +- tools/sync_docs_chrome.py | 293 +++++ 18 files changed, 2705 insertions(+), 3747 deletions(-) create mode 100644 tools/sync_docs_chrome.py diff --git a/docs/css/style.css b/docs/css/style.css index a186102..32afb74 100644 --- a/docs/css/style.css +++ b/docs/css/style.css @@ -1,1425 +1,574 @@ /* ============================================================ GoutouStdio 站点样式(与 tokens.css 配合使用) - 分层:基础 → 排印 → 版式 → 页眉 → 页头 → 通用组件 → - 内容组件 → 终端窗 → 反白带 → 页脚 → 响应式。 - 组件引用 --pg-* token;动效保持正文可读。 + 版式:顶部导航 + 大标题首屏 + 分区正文 + 站点地图页脚。 + 没有动画和装饰图形,层次靠字号、留白和扁平色块。 + 组件只引用 --pg-*,不写死颜色与 4px 以上的尺寸(CI 会检查)。 ============================================================ */ -/* ---------- 1. 基础 ---------- */ +/* ---------- 基础 ---------- */ *, *::before, *::after { box-sizing: border-box; } -* { margin: 0; } - html { - font-size: 16px; - scroll-behavior: smooth; -webkit-text-size-adjust: 100%; + scroll-padding-top: calc(var(--pg-header-h) + var(--pg-space-4)); } +html[data-theme="dark"] { color-scheme: dark; } body { + margin: 0; font-family: var(--pg-font-sans); + font-size: var(--pg-text-md); line-height: var(--pg-leading-body); color: var(--pg-fg-1); background: var(--pg-bg-page); - min-height: 100vh; -webkit-font-smoothing: antialiased; - text-rendering: optimizeLegibility; - counter-reset: pg-sec; } - img { display: block; max-width: 100%; height: auto; } -a { color: var(--pg-accent); text-decoration: none; } -a:hover { text-decoration: underline; } +a { + color: var(--pg-accent); + text-underline-offset: 0.2em; +} +a:hover { color: var(--pg-accent-hover); } :focus-visible { outline: 2px solid var(--pg-focus); outline-offset: 2px; - border-radius: 2px; } ::selection { background: var(--pg-accent-soft); } -.skip-link { - position: absolute; - left: var(--pg-space-4); - top: -100px; - z-index: 3000; - padding: var(--pg-space-2) var(--pg-space-4); - background: var(--pg-ink); - color: var(--pg-bg-page); - border-radius: var(--pg-radius-sm); - font-size: var(--pg-text-sm); - font-weight: 600; -} -.skip-link:focus { top: var(--pg-space-3); } - -/* ---------- 2. 排印角色 ---------- */ -.prose a, -.feature-body a, -.index-desc a, -.index-links a, -.notice-body a, -.timeline a { - padding: 0 1px; - margin: 0 -1px; - border-radius: var(--pg-space-1); - text-decoration: underline; - text-decoration-thickness: 1px; - text-underline-offset: 3px; - transition: background-color var(--pg-duration) var(--pg-ease); -} -.prose a:hover, -.feature-body a:hover, -.index-links a:hover, -.notice-body a:hover { - text-decoration-thickness: 1px; - background: var(--pg-accent-soft); - text-decoration-color: var(--pg-accent-hover); -} +[hidden] { display: none !important; } -/* 外链标记:正文区统一 ↗,按钮/导航/页眉品牌/邮件除外 */ -.prose a[target="_blank"]::after, -.feature-body a[target="_blank"]::after, -.index-desc a[target="_blank"]::after, -.index-links a[target="_blank"]::after, -.notice-body a[target="_blank"]::after, -.footer-col a[target="_blank"]::after { - content: "\2197"; - display: inline-block; - margin-left: 2px; - font-size: 0.8em; - text-decoration: none; +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip-path: inset(50%); + white-space: nowrap; + border: 0; } -code, -.cell-note code { - font-family: var(--pg-font-mono); - font-size: 0.85em; -} -.section-lede code, -.prose code, -.feature-body code, -.index-desc code, -.notice-body code, -.fact code, -.term-note code { - color: var(--pg-accent); - overflow-wrap: anywhere; +.skip-link { + position: absolute; + left: var(--pg-space-3); + top: -4rem; + z-index: 60; + padding: var(--pg-space-2) var(--pg-space-3); + background: var(--pg-ink); + color: var(--pg-on-ink); + border-radius: var(--pg-radius); } +.skip-link:focus { top: var(--pg-space-2); color: var(--pg-on-ink); } -/* ---------- 3. 版式 ---------- */ -.wrap { - max-width: var(--pg-width); +.container { + width: 100%; + max-width: var(--pg-container); margin-inline: auto; padding-inline: var(--pg-space-5); } -.site-main { padding-bottom: var(--pg-space-8); } -.site-main [id] { scroll-margin-top: calc(var(--pg-header-h) + var(--pg-space-5)); } - -.section { padding-block: var(--pg-space-8); counter-increment: pg-sec; } -.section + .section { border: none; } - -.section-head { max-width: 46rem; margin-bottom: var(--pg-space-6); } -.section-head.spaced { margin-top: var(--pg-space-7); } - -.eyebrow { - display: block; - margin-bottom: var(--pg-space-2); - font-family: var(--pg-font-mono); - font-size: var(--pg-text-xs); - letter-spacing: 0.08em; - text-transform: uppercase; - color: var(--pg-fg-3); -} -/* 章节自动编号:01 / 产品 —— 版式自带,不用手写 */ -.section .eyebrow::before { - content: counter(pg-sec, decimal-leading-zero) " / "; - color: var(--pg-accent); -} +.nowrap { white-space: nowrap; } -.section-title { - font-family: var(--pg-font-display); - font-weight: 700; - font-size: var(--pg-text-2xl); - line-height: 1.2; - letter-spacing: -0.005em; +/* ---------- 标题 ---------- */ +h1, h2, h3, h4 { + margin: 0; + font-family: var(--pg-font-serif); + font-weight: 400; + line-height: var(--pg-leading-display); color: var(--pg-fg-1); text-wrap: balance; } -body.theme-dark .section-title { font-weight: 600; } +h1 { font-size: var(--pg-text-hero); letter-spacing: -0.01em; } +h2 { font-size: var(--pg-text-2xl); } +h3 { font-size: var(--pg-text-xl); line-height: var(--pg-leading-tight); } +h4 { + font-family: var(--pg-font-sans); + font-size: var(--pg-text-md); + font-weight: 700; + line-height: var(--pg-leading-tight); +} -.section-lede { margin-top: var(--pg-space-2); color: var(--pg-fg-2); } +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); } +strong { font-weight: 700; } -/* ---------- 4. 页眉 ---------- */ +.lede { + max-width: var(--pg-measure); + font-size: var(--pg-text-lg); + color: var(--pg-fg-2); +} +.small { font-size: var(--pg-text-sm); color: var(--pg-fg-3); } + +/* ---------- 页眉 ---------- */ .site-header { position: sticky; top: 0; - z-index: 1000; - background: var(--pg-bg-raised); - border-bottom: 0; + z-index: 50; + background: var(--pg-bg-page); + border-bottom: 1px solid var(--pg-line-1); } -.site-header-inner { +.header-inner { display: flex; align-items: center; - gap: var(--pg-space-3); - height: var(--pg-header-h); + gap: var(--pg-space-4); + min-height: var(--pg-header-h); } - .brand { - display: flex; - align-items: center; - gap: var(--pg-space-2); + font-family: var(--pg-font-serif); + font-size: var(--pg-text-xl); + line-height: 1; color: var(--pg-fg-1); - font-weight: 700; + text-decoration: none; white-space: nowrap; } -.brand:hover { text-decoration: none; } -.brand-mark { - display: grid; - place-items: center; - width: var(--pg-brand-mark); - height: var(--pg-brand-mark); - border-radius: var(--pg-radius-sm); - background: var(--pg-ref-indigo-700); - color: var(--pg-ref-paper); - font-family: var(--pg-font-display); - font-size: 1.05rem; - font-weight: 600; - line-height: 1; -} -.brand-name { - font-family: var(--pg-font-display); - font-weight: 600; -} -.brand-name small { - display: block; - font-family: var(--pg-font-sans); - font-size: var(--pg-text-xs); - font-weight: 400; - line-height: 1.2; - color: var(--pg-fg-3); -} +.brand:hover { color: var(--pg-fg-1); } .site-nav { margin-left: auto; } -.site-nav ul { +.site-nav > ul { display: flex; + flex-wrap: wrap; align-items: center; - gap: 2px; - list-style: none; + gap: var(--pg-space-1); + margin: 0; padding: 0; + list-style: none; } -.site-nav a { +.site-nav li + li { margin-top: 0; } +.site-nav a, +.site-nav summary { display: block; padding: var(--pg-space-2) var(--pg-space-3); - border-radius: var(--pg-radius-sm); - color: var(--pg-fg-2); font-size: var(--pg-text-sm); - font-weight: 500; - white-space: nowrap; -} -.site-nav a:hover { - color: var(--pg-fg-1); - background: var(--pg-bg-sunken); + color: var(--pg-fg-2); text-decoration: none; + border-radius: var(--pg-radius); + cursor: pointer; } -.site-nav a[aria-current="page"] { - color: var(--pg-accent); - background: var(--pg-accent-soft); - font-weight: 700; +.site-nav a:hover, +.site-nav summary:hover { color: var(--pg-fg-1); background: var(--pg-bg-sunken); } +.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; } -.header-actions { - display: flex; - align-items: center; - gap: var(--pg-space-2); - margin-left: var(--pg-space-2); +/* 下拉菜单:原生 details,没有脚本也能点开 */ +.menu { position: relative; } +.menu > summary { list-style: none; } +.menu > summary::-webkit-details-marker { display: none; } +.menu > summary::after { content: " ▾"; font-size: var(--pg-text-xs); } +.menu > ul { + position: absolute; + top: calc(100% + var(--pg-space-1)); + left: 0; + z-index: 55; + min-width: 13rem; + 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); } +.menu > ul a { white-space: nowrap; } + +.header-actions { display: flex; align-items: center; gap: var(--pg-space-2); } .icon-btn { - display: inline-grid; - place-items: center; - width: var(--pg-icon-btn); - height: var(--pg-icon-btn); - padding: 0; - border: 1px solid var(--pg-line-2); - border-radius: var(--pg-radius-sm); - background: var(--pg-bg-raised); + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 2.25rem; + padding: 0 var(--pg-space-3); + font: inherit; + font-size: var(--pg-text-sm); color: var(--pg-fg-2); - font-size: var(--pg-text-md); + background: transparent; + border: 1px solid var(--pg-line-strong); + border-radius: var(--pg-radius); cursor: pointer; } -.icon-btn:hover { color: var(--pg-fg-1); border-color: var(--pg-line-2); } +.icon-btn:hover { color: var(--pg-fg-1); background: var(--pg-bg-sunken); } .nav-toggle { display: none; } -/* ---------- 5. 页头 ---------- */ -.page-head { - position: relative; - overflow: clip; - padding-block: var(--pg-space-9) var(--pg-space-8); - border-bottom: 0; - background: radial-gradient(ellipse at 85% 20%, var(--pg-accent-soft), transparent 65%); -} -/* 蓝图网:只在页头用,28px 格、7% 浓度,底部与两侧淡出。 - 注意是画线的 linear-gradient,不是颜色渐变;mask 保证它 - 绝不跟正文抢注意力,看不见就是它最成功的状态。 */ -.page-head::before { - content: ""; - position: absolute; - inset: 0; - pointer-events: none; - background-image: - linear-gradient(color-mix(in srgb, var(--pg-accent) 5%, transparent) 1px, transparent 1px), - linear-gradient(90deg, color-mix(in srgb, var(--pg-accent) 5%, transparent) 1px, transparent 1px); - background-size: var(--pg-blueprint) var(--pg-blueprint); - background-position: center top; - mask-image: radial-gradient(ellipse 100% 90% at 50% 0%, black 25%, transparent 78%); - -webkit-mask-image: radial-gradient(ellipse 100% 90% at 50% 0%, black 25%, transparent 78%); -} -.page-head > * { position: relative; } -.page-head h1 { - max-width: 44rem; - font-family: var(--pg-font-display); - font-weight: 700; - font-size: var(--pg-text-hero); - line-height: var(--pg-leading-display); - letter-spacing: -0.045em; - text-wrap: balance; -} -body.theme-dark .page-head h1 { font-weight: 600; } -.page-head .lede { - max-width: 44rem; - margin-top: var(--pg-space-5); - font-size: var(--pg-text-md); - color: var(--pg-fg-2); -} -.lede { max-width: 44rem; } - -.page-head-grid { +/* ---------- 首屏 ---------- */ +.hero { padding-block: var(--pg-space-9) var(--pg-space-8); } +.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, 1fr) minmax(0, 1fr); + grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); 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; } -.actions { - display: flex; - flex-wrap: wrap; - gap: var(--pg-space-3); - margin-top: var(--pg-space-5); -} -.page-head .meta-row, -.meta-row { - display: flex; - flex-wrap: wrap; - gap: var(--pg-space-2) var(--pg-space-4); - margin-top: var(--pg-space-4); - font-family: var(--pg-font-mono); - font-size: var(--pg-text-xs); - color: var(--pg-fg-3); +/* 版本、架构、许可等关键事实,一行排开 */ +.spec { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); + gap: var(--pg-space-4) var(--pg-space-5); + margin-top: var(--pg-space-6); + padding-top: var(--pg-space-5); + border-top: 1px 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; } -/* ---------- 6. 按钮与徽标 ---------- */ -.btn { - display: inline-flex; - align-items: center; - gap: var(--pg-space-2); - min-height: var(--pg-control-h); - padding: var(--pg-space-3) var(--pg-space-5); - border: 1px solid transparent; - border-radius: var(--pg-radius-sm); - font-family: var(--pg-font-sans); - font-size: var(--pg-text-sm); - font-weight: 600; - line-height: 1.4; - cursor: pointer; - transition: - background-color var(--pg-duration) var(--pg-ease), - border-color var(--pg-duration) var(--pg-ease), - color var(--pg-duration) var(--pg-ease); -} -.btn:hover { text-decoration: none; } -/* 主要操作用品牌色,与普通阅读链接形成清晰层级。 */ -.btn-primary { background: var(--pg-ref-indigo-700); color: var(--pg-ref-paper); box-shadow: var(--pg-glow); } -.btn-primary:hover { background: var(--pg-ref-indigo-600); } -.btn-secondary { - background: var(--pg-bg-raised); - border-color: var(--pg-line-2); - color: var(--pg-fg-1); +/* ---------- 分区 ---------- */ +.section { + padding-block: var(--pg-space-8); + border-top: 1px solid var(--pg-line-1); } -.btn-secondary:hover { border-color: var(--pg-fg-3); background: var(--pg-bg-sunken); } +.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); } -.badge { - display: inline-flex; - align-items: center; - gap: var(--pg-radius-sm); - padding: 2px var(--pg-space-2); - border: 1px solid var(--pg-mute-line); - border-radius: var(--pg-radius-pill); - background: var(--pg-mute-bg); - color: var(--pg-mute-fg); - font-size: var(--pg-text-xs); - font-weight: 600; - line-height: 1.6; - white-space: nowrap; +/* 左边小节标题,右边正文 */ +.split { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); + gap: var(--pg-space-7); + align-items: start; } -.badge-ok { background: var(--pg-ok-bg); border-color: var(--pg-ok-line); color: var(--pg-ok-fg); } -.badge-warn { background: var(--pg-warn-bg); border-color: var(--pg-warn-line); color: var(--pg-warn-fg); } -.badge-info { background: var(--pg-accent-soft); border-color: var(--pg-line-2); color: var(--pg-accent); } +.split > h2 { font-size: var(--pg-text-xl); line-height: var(--pg-leading-tight); } +.split > div { min-width: 0; } -/* ---------- 7. 提示块 ---------- */ -.notice { +.stack > * + * { margin-top: var(--pg-space-4); } +.stack > h3 { margin-top: var(--pg-space-6); } +.stack > h3:first-child { margin-top: 0; } +.prose { max-width: var(--pg-measure); } + +/* ---------- 项目色块 ---------- */ +.tiles { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: var(--pg-space-4); +} +.tile { display: flex; + flex-direction: column; gap: var(--pg-space-3); - padding: var(--pg-space-4) var(--pg-space-5); - margin-bottom: var(--pg-space-5); - border-left: 3px solid var(--pg-mute-line); -} -.notice-warn { border-left-color: var(--pg-warn-fg); } -.notice-info { border-left-color: var(--pg-accent); } -.notice-icon { flex: none; font-size: var(--pg-text-lg); line-height: 1.4; } -.notice-warn .notice-icon, .notice-warn .notice-title { color: var(--pg-warn-fg); } -.notice-info .notice-icon, .notice-info .notice-title { color: var(--pg-accent); } -.notice-title { font-weight: 700; font-size: var(--pg-text-md); line-height: var(--pg-leading-tight); margin-bottom: var(--pg-space-1); } -.notice-body { color: var(--pg-fg-2); font-size: var(--pg-text-sm); } -.notice-body > * + * { margin-top: var(--pg-space-2); } -.notice-body p { margin: 0; } - -.check-list { list-style: none; margin: var(--pg-space-2) 0 0; padding: 0; display: grid; gap: var(--pg-space-1); } -.check-list li { padding-left: 1.5em; position: relative; font-size: var(--pg-text-sm); color: var(--pg-fg-2); } -.check-list li::before { content: "✓"; position: absolute; left: 0; color: var(--pg-ok-fg); font-weight: 700; } - -.table-scroll { overflow-x: auto; margin-inline: calc(var(--pg-space-5) * -1); padding-inline: var(--pg-space-5); } -table.data { width: 100%; background: var(--pg-bg-raised); border: 1px solid var(--pg-line-1); border-collapse: collapse; font-size: var(--pg-text-sm); } -table.data caption { text-align: left; padding-bottom: var(--pg-space-3); color: var(--pg-fg-3); } -table.data th, table.data td { - padding: var(--pg-space-3) var(--pg-space-4); - border-bottom: 0; - text-align: left; - vertical-align: baseline; + min-width: 0; + padding: var(--pg-space-6); + background: var(--pg-bg-raised); + border-radius: var(--pg-radius-lg); } -table.data thead th { - background: var(--pg-bg-sunken); - font-size: var(--pg-text-xs); - text-transform: uppercase; - letter-spacing: 0.06em; - color: var(--pg-fg-3); +.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-links { + display: flex; + flex-wrap: wrap; + gap: var(--pg-space-1) var(--pg-space-4); + margin-top: auto; + padding-top: var(--pg-space-3); font-weight: 600; - border-bottom: 1px solid var(--pg-line-2); - white-space: nowrap; } -table.data tbody th { font-weight: 600; white-space: nowrap; } -table.data td { color: var(--pg-fg-2); } -table.data td.num { - font-family: var(--pg-font-mono); - font-size: var(--pg-text-xs); - text-align: right; - white-space: nowrap; -} -table.data tbody tr:last-child th, table.data tbody tr:last-child td { border-bottom: none; } +.tile a { color: var(--pg-fg-1); } +.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); } -.facts { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr)); - gap: var(--pg-space-5) var(--pg-space-6); - margin-top: var(--pg-space-4); - padding-block: var(--pg-space-5); - border-top: 0; - border-bottom: 0; -} -.facts + .prose { margin-top: var(--pg-space-4); } -.fact { min-width: 0; } -.fact dt { font-size: var(--pg-text-xs); color: var(--pg-fg-3); margin-bottom: var(--pg-space-1); } -.fact dd { margin: 0; font-size: var(--pg-text-sm); overflow-wrap: anywhere; } -.fact dd.mono { font-family: var(--pg-font-mono); font-size: var(--pg-text-xs); } +.archive-title { margin: var(--pg-space-8) 0 var(--pg-space-3); } -.timeline { list-style: none; margin: var(--pg-space-5) 0 0; padding: 0; display: grid; } -.timeline li { +/* ---------- 行列表:历史、存档、版本 ---------- */ +.rows { margin: 0; padding: 0; list-style: none; } +.rows > li { display: grid; - grid-template-columns: 4.5rem 1fr; + grid-template-columns: 8rem minmax(0, 1fr); gap: var(--pg-space-4); - padding-block: var(--pg-space-4); - border-top: 0; -} -.timeline li:last-child { border-bottom: 0; } -.timeline-year { font-family: var(--pg-font-mono); font-size: var(--pg-text-sm); color: var(--pg-fg-3); } -.timeline-title { font-weight: 700; font-size: var(--pg-text-md); line-height: var(--pg-leading-tight); } -.timeline p { margin-top: var(--pg-space-1); color: var(--pg-fg-2); font-size: var(--pg-text-sm); } - -/* ---------- 10. 步骤 / 正文 / 代码 / 配图 ---------- */ -.steps { list-style: none; counter-reset: step; margin: var(--pg-space-4) 0 0; padding: 0; display: grid; gap: var(--pg-space-3); } -.steps li { - counter-increment: step; - display: flex; - gap: var(--pg-space-3); - align-items: baseline; - padding-block: var(--pg-space-4); - border-top: 0; - font-size: var(--pg-text-sm); - color: var(--pg-fg-2); -} -.steps li::before { - content: counter(step); - flex: none; - width: 1.5rem; - height: 1.5rem; - display: inline-grid; - place-items: center; - color: var(--pg-accent); - font-family: var(--pg-font-mono); - font-size: var(--pg-text-xs); - transform: translateY(0.25rem); + margin: 0; + padding: var(--pg-space-4) 0; + border-top: 1px solid var(--pg-line-2); } +.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-wide > li { grid-template-columns: 12rem minmax(0, 1fr); } +.rows p + p { margin-top: var(--pg-space-2); } -.prose { max-width: 46rem; color: var(--pg-fg-2); } -.prose > * + * { margin-top: var(--pg-space-3); } -.prose h2 { font-size: var(--pg-text-xl); color: var(--pg-fg-1); margin-top: var(--pg-space-6); } -.prose h3 { font-size: var(--pg-text-lg); color: var(--pg-fg-1); margin-top: var(--pg-space-5); } -.prose ul, .prose ol { padding-left: 1.4em; } -.prose li + li { margin-top: var(--pg-space-1); } - -pre.code { - margin-top: var(--pg-space-3); - padding: var(--pg-space-3) var(--pg-space-5); - border-left: 2px solid var(--pg-accent); - color: var(--pg-fg-1); +/* ---------- 代码 ---------- */ +code, kbd, pre { font-family: var(--pg-font-mono); - font-size: var(--pg-text-sm); - line-height: 1.7; - overflow-x: auto; + font-size: 0.875em; } -pre.code .c { color: var(--pg-fg-3); } - -figure.shot { margin: var(--pg-space-5) 0 0; } -figure.shot img { - +code { + padding: 0.1em 0.35em; + background: var(--pg-bg-sunken); + border: 1px solid var(--pg-line-1); + border-radius: var(--pg-radius); + overflow-wrap: anywhere; } -figure.shot figcaption, -figure.shot-frame figcaption { - margin-top: var(--pg-space-2); - font-size: var(--pg-text-sm); - color: var(--pg-fg-3); +.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); + overflow-x: auto; } -figure.shot-frame { margin: var(--pg-space-5) 0 0; } - -/* ---------- 11. 终端窗与截图窗 ---------- */ -.winbar { - display: flex; - align-items: center; - justify-content: space-between; - gap: var(--pg-space-3); - padding-block: var(--pg-space-3); - border-bottom: 1px solid var(--pg-line-2); +.section-tint pre { background: var(--pg-bg-raised); } +pre code { + padding: 0; + background: none; + border: 0; + font-size: inherit; + overflow-wrap: normal; } -.winbar-title { - display: flex; - align-items: center; - gap: var(--pg-space-3); - min-width: 0; - font-family: var(--pg-font-mono); - font-size: var(--pg-text-xs); - color: var(--pg-fg-3); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; +pre.terminal { + color: var(--pg-term-fg); + background: var(--pg-term-bg); + border-color: var(--pg-term-bg); + border-radius: var(--pg-radius-lg); } - +pre.terminal .dim { color: var(--pg-term-dim); } +pre.terminal .prompt { color: var(--pg-term-accent); user-select: none; } .copy-btn { - flex: none; - font-family: var(--pg-font-mono); - font-size: var(--pg-text-xs); - color: var(--pg-accent); - background: transparent; - border: 0; - border-radius: var(--pg-radius-sm); + position: absolute; + top: var(--pg-space-2); + right: var(--pg-space-2); padding: var(--pg-space-1) var(--pg-space-2); + 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); cursor: pointer; - transition: border-color var(--pg-duration) var(--pg-ease); } -.copy-btn:hover { text-decoration: underline; text-underline-offset: 3px; } +.copy-btn:hover { color: var(--pg-fg-1); } +pre:has(.copy-btn) { padding-right: var(--pg-space-8); } -.term { - border-top: 1px solid var(--pg-line-2); - overflow: hidden; - min-width: 0; -} -.term-body { - padding-block: var(--pg-space-5); - font-family: var(--pg-font-mono); - font-size: 0.8rem; - line-height: 1.85; - color: var(--pg-fg-1); - overflow-x: auto; +/* ---------- 表格 ---------- */ +.table-wrap { overflow-x: auto; } +table { + width: 100%; + border-collapse: collapse; + font-size: var(--pg-text-sm); } -.t-line { white-space: pre-wrap; overflow-wrap: anywhere; } -/* 提示符不进选区:三击复制只拿命令 */ -.t-p { color: var(--pg-accent); padding-right: 0.6em; user-select: none; } -.t-cmd { color: var(--pg-fg-1); font-weight: 500; } -.t-out { color: var(--pg-fg-3); } -.t-ok { color: var(--pg-ok-fg); } -.t-hl { color: var(--pg-accent); } -.t-cursor { - display: inline-block; - width: 0.55em; - height: 1.1em; - background: var(--pg-accent); - vertical-align: text-bottom; - animation: t-blink 1.1s steps(1) infinite; +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); } -@keyframes t-blink { 50% { visibility: hidden; } } - -.term-note { - margin-top: var(--pg-space-2); - font-family: var(--pg-font-mono); +thead th { + white-space: nowrap; font-size: var(--pg-text-xs); + font-weight: 600; color: var(--pg-fg-3); + border-bottom: 1px 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; } -.frame { - - overflow: hidden; +/* ---------- 提示块与徽标 ---------- */ +.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); } -.frame img { width: 100%; height: auto; } +.notice > * + * { margin-top: var(--pg-space-2); } +.notice-title { font-weight: 700; } +.notice-info { background: var(--pg-accent-soft); border-color: var(--pg-accent-soft); } +.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); } -/* ---------- 12. 反白带 / 宣言 / 联系 ---------- */ -.band { - background: var(--pg-band-bg); - color: var(--pg-band-fg); - padding-block: var(--pg-space-6); -} -.band .eyebrow { color: var(--pg-band-mute); } -.band .eyebrow::before { color: inherit; } -.band h2 { - font-family: var(--pg-font-display); - font-weight: 700; - font-size: var(--pg-text-2xl); - line-height: 1.2; - letter-spacing: -0.005em; - text-wrap: balance; -} -.band .band-lede { margin-top: var(--pg-space-2); color: var(--pg-band-mute); max-width: 44rem; } -.band .actions { margin-top: var(--pg-space-5); } -.band .btn-primary { background: var(--pg-band-fg); color: var(--pg-band-bg); } -.band .btn-primary:hover { background: color-mix(in srgb, var(--pg-band-fg) 85%, var(--pg-band-bg)); } -.band .btn-secondary { border-color: var(--pg-band-line); color: var(--pg-band-fg); } -.band .btn-secondary:hover { background: transparent; border-color: var(--pg-band-fg); } -.band code { - font-family: var(--pg-font-mono); - font-size: 0.85em; - padding: 0.1em 0.35em; - border: 1px solid var(--pg-band-line); - border-radius: var(--pg-radius-sm); - overflow-wrap: anywhere; +.badge { + display: inline-block; + padding: 0 var(--pg-space-2); + font-family: var(--pg-font-sans); + font-size: var(--pg-text-xs); + font-weight: 600; + line-height: 1.7; + white-space: nowrap; + color: var(--pg-mute-fg); + background: var(--pg-mute-bg); + border: 1px 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); } -.stat-band ul { - list-style: none; - margin: var(--pg-space-5) 0 0; - padding: var(--pg-space-5) 0 0; - border-top: 1px solid var(--pg-band-line); - display: grid; - gap: var(--pg-space-4); - grid-template-columns: repeat(auto-fit, minmax(min(100%, 10rem), 1fr)); -} -.stat-num { - font-family: var(--pg-font-display); - font-size: 2.25rem; - line-height: 1.1; - font-weight: 700; - letter-spacing: -0.01em; +/* ---------- 按钮 ---------- */ +.actions { + display: flex; + flex-wrap: wrap; + gap: var(--pg-space-3); } -.stat-label { - margin-top: var(--pg-space-1); - font-family: var(--pg-font-mono); - font-size: var(--pg-text-xs); - color: var(--pg-band-mute); +.button { + display: inline-block; + padding: var(--pg-space-3) var(--pg-space-5); + font-size: var(--pg-text-sm); + font-weight: 600; + 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); } - -/* 宣言:工作室的人声时刻,一页只用一次。 - 不用竖线——用大引号 + 留白跟正文拉开距离。 */ -.pullquote { - max-width: 44rem; - margin: 0 0 var(--pg-space-7); - padding: 0; - border: none; - font-family: var(--pg-font-display); - font-size: var(--pg-text-2xl); - line-height: 1.4; - font-weight: 700; - letter-spacing: -0.005em; +.button:hover { color: var(--pg-on-ink); background: var(--pg-ink-hover); border-color: var(--pg-ink-hover); } +.button-secondary { color: var(--pg-fg-1); - text-wrap: balance; + background: transparent; + border-color: var(--pg-line-strong); } -.pullquote::before { - content: "\201C"; - display: block; - font-size: 3.5rem; - line-height: 0.8; - margin-bottom: var(--pg-space-3); - color: var(--pg-accent); +.button-secondary:hover { color: var(--pg-fg-1); background: var(--pg-bg-sunken); border-color: var(--pg-line-strong); } + +/* ---------- 图片 ---------- */ +figure img { + border: 1px solid var(--pg-line-2); + border-radius: var(--pg-radius-lg); } -.pullquote cite { - display: block; +figcaption { margin-top: var(--pg-space-2); - font-family: var(--pg-font-mono); - font-style: normal; - font-size: var(--pg-text-xs); + font-size: var(--pg-text-sm); color: var(--pg-fg-3); } -/* 联系大 CTA:邮箱就是标题 */ -.mail-big { - display: block; - margin-top: var(--pg-space-4); - font-family: var(--pg-font-display); - font-size: clamp(1.5rem, 4vw + 0.5rem, 2.5rem); - font-weight: 700; - letter-spacing: -0.01em; - color: var(--pg-band-fg); - overflow-wrap: anywhere; +/* ---------- 版本历史(ota/releases.html 的脚本使用) ---------- */ +.release-tools { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--pg-space-2) var(--pg-space-4); + margin-bottom: var(--pg-space-4); } -.mail-big:hover { color: var(--pg-band-fg); } - -/* ---------- 13. 上下页 ---------- */ -.pager { - display: grid; - grid-template-columns: 1fr 1fr; - gap: var(--pg-space-4); - margin-top: var(--pg-space-7); - padding-top: var(--pg-space-5); - border-top: 0; +.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); + border-radius: var(--pg-radius-pill); + cursor: pointer; } -.pager a { display: block; } -.pager a:hover { text-decoration: none; } -.pager-next { text-align: right; } -.pager-dir { - display: block; - font-family: var(--pg-font-mono); - font-size: var(--pg-text-xs); - letter-spacing: 0.08em; - text-transform: uppercase; - color: var(--pg-fg-3); +.release-tools button[aria-pressed="true"] { + color: var(--pg-on-ink); + background: var(--pg-ink); + border-color: var(--pg-ink); } -.pager-name { - display: block; - line-height: var(--pg-leading-tight); - margin-top: var(--pg-space-1); - font-family: var(--pg-font-display); - font-size: var(--pg-text-lg); - font-weight: 700; +.release-tools input { + min-width: 14rem; + padding: var(--pg-space-1) var(--pg-space-3); + 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); } -.pager a:hover .pager-name { - color: var(--pg-accent); - text-decoration: underline; - text-underline-offset: 3px; +.release-count { margin-bottom: var(--pg-space-3); 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); } +.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 > div > * + * { margin-top: var(--pg-space-3); } -/* ---------- 14. 页脚 ---------- */ -.site-footer { border-top: 0; padding-block: var(--pg-space-7); background: var(--pg-bg-sunken); } +/* ---------- 页脚 ---------- */ +.site-footer { + 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); +} .footer-grid { display: grid; - gap: var(--pg-space-5); - grid-template-columns: 1.2fr repeat(3, 1fr); + grid-template-columns: 1.6fr repeat(4, minmax(0, 1fr)); + gap: var(--pg-space-6); } -.footer-brand { display: flex; gap: var(--pg-space-2); align-items: center; font-weight: 700; } -.footer-brand .brand-name { font-size: var(--pg-text-md); } -.footer-note { margin-top: var(--pg-space-2); font-size: var(--pg-text-sm); color: var(--pg-fg-3); max-width: 22rem; } -.footer-col h2 { - font-size: var(--pg-text-xs); - text-transform: uppercase; - letter-spacing: 0.06em; - color: var(--pg-fg-3); - margin-bottom: var(--pg-space-2); +.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; + line-height: var(--pg-leading-tight); } -.footer-col ul { list-style: none; padding: 0; display: grid; gap: var(--pg-space-1); } -.footer-col a { color: var(--pg-fg-2); font-size: var(--pg-text-sm); } -.footer-col a:hover { color: var(--pg-accent); text-decoration: underline; text-underline-offset: 3px; } +.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; } .footer-base { - margin-top: var(--pg-space-5); - padding-top: var(--pg-space-4); - border-top: 0; display: flex; flex-wrap: wrap; - gap: var(--pg-space-2) var(--pg-space-4); + justify-content: space-between; + 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); font-size: var(--pg-text-xs); color: var(--pg-fg-3); } +.footer-base p { margin: 0; } -/* ---------- 15. 响应式 ---------- */ -@media (max-width: 1100px) { - .footer-grid { grid-template-columns: 1fr 1fr; } - .site-header-inner { gap: var(--pg-space-2); } - .site-nav a { padding-inline: var(--pg-space-2); } +/* ---------- 窄屏 ---------- */ +@media (max-width: 62rem) { + .tiles { grid-template-columns: minmax(0, 1fr); } + .footer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .footer-about { grid-column: 1 / -1; } } -@media (max-width: 960px) { - .wrap { padding-inline: var(--pg-space-4); } - .page-head { padding-block: var(--pg-space-6) var(--pg-space-5); } +@media (max-width: 56rem) { + .container { padding-inline: var(--pg-space-4); } + .hero { padding-block: var(--pg-space-7) var(--pg-space-6); } + .hero-split, .split, .release { grid-template-columns: minmax(0, 1fr); gap: var(--pg-space-5); } + .section { padding-block: var(--pg-space-7); } + .rows > li, .rows-wide > li { grid-template-columns: minmax(0, 1fr); gap: var(--pg-space-1); } + /* 没有脚本时,导航直接在页眉里折行,仍然可用。 */ + .header-inner { flex-wrap: wrap; padding-block: var(--pg-space-2); } + .site-nav { order: 3; width: 100%; margin-left: 0; } .header-actions { margin-left: auto; } - .page-head-grid { grid-template-columns: 1fr; gap: var(--pg-space-6); } - .nav-toggle { display: inline-grid; } - .site-nav { - position: fixed; - inset: calc(var(--pg-header-h) + 1px) 0 auto 0; - background: var(--pg-bg-page); - border-bottom: 0; - padding: var(--pg-space-3) var(--pg-space-4) var(--pg-space-4); - max-height: calc(100dvh - var(--pg-header-h)); - overflow-y: auto; - box-shadow: var(--pg-shadow); - transform: translateY(calc(var(--pg-space-2) * -1)); - visibility: hidden; - transition: - transform var(--pg-duration) var(--pg-ease), - visibility 0s var(--pg-duration); - } - .site-nav.open { - transform: none; - visibility: visible; - transition: - transform var(--pg-duration) var(--pg-ease); - } - .site-nav ul { flex-direction: column; align-items: stretch; gap: var(--pg-space-1); } - .site-nav a { padding: var(--pg-space-3); } - - .section { padding-block: var(--pg-space-6); } - .timeline li { grid-template-columns: 1fr; gap: var(--pg-space-1); } - .footer-grid { grid-template-columns: 1fr; } - .pager { grid-template-columns: 1fr; } - .pager-next { text-align: left; } - .btn { justify-content: center; } - .page-head .actions .btn, .band .actions .btn { flex: 1 1 12rem; } -} - -@media (prefers-reduced-motion: reduce) { - html { scroll-behavior: auto; } - *, *::before, *::after { transition: none !important; } - .t-cursor { animation: none; } -} - -/* 正文始终可见,进入视口时错峰归位;无 JS 时维持原版式。 */ -.scroll-reveal { - transform: translateY(var(--pg-reveal-distance)); - transition: transform var(--pg-reveal-duration) var(--pg-ease) var(--pg-reveal-delay); -} -.scroll-reveal.is-revealed { transform: none; } -.reading-progress { - position: fixed; - inset: 0 0 auto; - height: 2px; - z-index: 1100; - background: var(--pg-accent); - transform: scaleX(var(--pg-scroll-progress)); - transform-origin: left; - pointer-events: none; -} -.site-header { transition: box-shadow var(--pg-duration) var(--pg-ease); } -.site-header.is-scrolled { box-shadow: 0 2px var(--pg-space-4) var(--pg-line-1); } - -@media (prefers-reduced-motion: reduce), print { - .scroll-reveal { transform: none; transition: none; } - .desktop-evidence img { animation: none; transform: none; } - .reading-progress { display: none; } -} - -/* 首页:短标题、产品入口与会话演示组成同一首屏。 */ -.hero-home .eyebrow { margin-bottom: var(--pg-space-5); color: var(--pg-accent); } -.hero-home h1 { font-size: clamp(2.5rem, 4.8vw, 4.35rem); letter-spacing: -0.05em; } -.hero-accent { color: var(--pg-accent); } -.hero-foot { display: flex; flex-wrap: wrap; gap: var(--pg-space-3) var(--pg-space-5); margin-top: var(--pg-space-6); padding-top: var(--pg-space-4); border-top: 0; font-size: var(--pg-text-xs); color: var(--pg-fg-3); } -.hero-foot strong { font-weight: 600; color: var(--pg-fg-1); margin-right: var(--pg-space-1); } -.section-jump { display: flex; flex-wrap: wrap; gap: var(--pg-space-2); margin-top: var(--pg-space-5); } -.section-jump a { font-size: var(--pg-text-sm); color: var(--pg-fg-2); padding-block: var(--pg-space-2); border-bottom: 1px solid var(--pg-line-2); margin-right: var(--pg-space-3); } -.section-jump a:hover { border-color: var(--pg-accent); color: var(--pg-accent); text-decoration: none; } -.page-head > .eyebrow { color: var(--pg-accent); font-weight: 600; } -.index-links a { display: inline-flex; align-items: center; min-height: var(--pg-control-h); } -.notice > div { min-width: 0; } -.section:has(> .wrap > .band) { background: var(--pg-band-bg); } -.band .btn-primary { box-shadow: none; } - -@media (max-width: 600px) { - .page-head h1 { font-size: clamp(2rem, 8vw, 2.75rem); } - .hero-home h1 { font-size: clamp(2.2rem, 9vw, 3.1rem); } - .page-head .actions .btn { flex: 1 1 100%; } - .hero-foot { gap: var(--pg-space-2) var(--pg-space-4); } - .term-body { padding-block: var(--pg-space-4); } - .band { padding-block: var(--pg-space-5); } - .table-scroll { margin-inline: calc(var(--pg-space-4) * -1); padding-inline: var(--pg-space-4); } -} - -.hero-demo { min-width: 0; } -.hero-demo .term-note { margin-top: var(--pg-space-3); } - -/* 项目内容:标题、说明、源码入口。 */ -.project-index { display: flex; flex-wrap: wrap; align-items: center; gap: var(--pg-space-5); padding-block: var(--pg-space-5); } -.project-index .eyebrow { margin: 0; margin-right: var(--pg-space-5); } -.project-index a { color: var(--pg-fg-1); font-weight: 600; } -.project-index .archive-link { margin-left: auto; color: var(--pg-fg-3); font-size: var(--pg-text-sm); font-weight: 400; } -.project-story { border-top: 0; } -.editorial-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr); gap: var(--pg-space-8); } -.story-heading { min-width: 0; } -.story-title { font-size: clamp(2.5rem, 4.5vw, 4rem); line-height: 1.1; letter-spacing: -0.05em; font-weight: 700; } -.title-period { color: var(--pg-accent); } -.story-tagline { margin-top: var(--pg-space-5); font-size: var(--pg-text-2xl); line-height: 1.45; letter-spacing: -0.025em; } -.project-status { display: flex; flex-wrap: wrap; align-items: center; gap: var(--pg-space-3); margin-top: var(--pg-space-5); font-family: var(--pg-font-mono); font-size: var(--pg-text-xs); color: var(--pg-fg-3); } -.story-links { display: grid; justify-items: start; gap: var(--pg-space-2); margin-top: var(--pg-space-5); font-size: var(--pg-text-sm); font-weight: 600; } -.story-links a { min-height: 2rem; } -.story-copy { min-width: 0; color: var(--pg-fg-2); } -.story-lead { font-size: var(--pg-text-xl); line-height: 1.6; color: var(--pg-fg-1); margin-bottom: var(--pg-space-4); } -.story-details { margin-top: var(--pg-space-5); border-top: 0; } -.story-details > div { display: grid; grid-template-columns: 4rem minmax(0, 1fr); gap: var(--pg-space-4); padding-block: var(--pg-space-3); border-bottom: 0; font-size: var(--pg-text-sm); } -.story-details dt { color: var(--pg-fg-3); font-family: var(--pg-font-mono); font-size: var(--pg-text-xs); } -.source-session { margin-top: var(--pg-space-6); } -.session-label { display: flex; align-items: center; justify-content: space-between; gap: var(--pg-space-3); font-family: var(--pg-font-mono); font-size: var(--pg-text-xs); color: var(--pg-fg-3); margin-bottom: var(--pg-space-3); } -.source-session .copy-btn { color: var(--pg-accent); border: 0; padding: 0; text-decoration: underline; text-underline-offset: 3px; } -.session-transcript { font-family: var(--pg-font-mono); font-size: var(--pg-text-xs); line-height: 2; overflow-wrap: anywhere; } -.session-transcript .t-p { color: var(--pg-accent); } -.session-transcript .t-cmd { color: var(--pg-fg-1); } - - - - - - - - -.desktop-evidence { margin-top: var(--pg-space-7); } -.desktop-evidence img { width: 100%; border-radius: 0; } -.desktop-evidence figcaption { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--pg-space-3); padding-top: var(--pg-space-3); font-size: var(--pg-text-xs); color: var(--pg-fg-3); } -.desktop-evidence figcaption > span { color: var(--pg-fg-1); } -.archive-index > div { display: grid; grid-template-columns: 1fr auto; gap: var(--pg-space-1) var(--pg-space-4); padding-block: var(--pg-space-4); border-top: 0; } -.archive-index > div > a { color: var(--pg-fg-1); font-size: var(--pg-text-lg); font-weight: 600; } -.archive-index > div > span:not(.badge) { color: var(--pg-fg-3); font-size: var(--pg-text-sm); grid-row: 2; } -.archive-index .badge { grid-column: 2; grid-row: 1; align-self: center; } -.archive-index > p { border-top: 0; padding-top: var(--pg-space-4); color: var(--pg-fg-2); font-size: var(--pg-text-sm); } -@media (max-width: 960px) { - .editorial-grid { grid-template-columns: minmax(0, 1fr); gap: var(--pg-space-6); } - .story-links { display: flex; flex-wrap: wrap; gap: var(--pg-space-5); } -} -@media (max-width: 600px) { - .project-index .eyebrow { width: 100%; } - .project-index .archive-link { margin-left: 0; } - - - -} - -/* ---------- 16. Systems Lab / 全站视觉与交互 ---------- */ -[hidden] { display: none !important; } -.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; } -.demo-caption { display: flex; align-items: center; justify-content: space-between; gap: var(--pg-space-3); font-family: var(--pg-font-mono); font-size: 0.6rem; letter-spacing: 0.07em; color: var(--pg-fg-3); margin-bottom: var(--pg-space-3); } -.demo-caption > span:first-child { display: flex; align-items: center; gap: var(--pg-space-2); } -.site-header { - background: color-mix(in srgb, var(--pg-bg-page) 88%, transparent); - backdrop-filter: blur(var(--pg-backdrop-blur)); - -webkit-backdrop-filter: blur(var(--pg-backdrop-blur)); -} -.site-header-inner { max-width: 88rem; } -.brand-mark { border-radius: 30%; transform: rotate(-7deg); transition: transform var(--pg-motion-slow) var(--pg-ease); } -.brand:hover .brand-mark { transform: rotate(0); } -.brand-name { letter-spacing: -0.035em; } -.brand-name small { letter-spacing: 0.06em; margin-top: var(--pg-space-1); font-size: 0.65rem; } -.site-nav a { position: relative; background: transparent; border-radius: 0; padding-block: var(--pg-space-5); } -.site-nav a[aria-current="page"] { background: transparent; } -.site-nav a::before { content: ""; position: absolute; inset: auto var(--pg-space-3) -1px; height: 2px; background: var(--pg-accent); transform: scaleX(0); transform-origin: left; transition: transform var(--pg-motion-slow) var(--pg-ease); } -.site-nav a[aria-current="page"]::before, .site-nav a:hover::before { transform: scaleX(1); } -.site-nav a:hover { background: transparent; color: var(--pg-accent); } -.icon-btn { background: transparent; border-color: var(--pg-line-1); transition: transform var(--pg-motion-slow) var(--pg-ease), background var(--pg-duration); } -.icon-btn:hover { background: var(--pg-bg-sunken); transform: translateY(-2px); } -.motion-toggle { font-size: var(--pg-text-sm); } -.page-head { padding-block: var(--pg-space-8); background: transparent; } -.page-head::before { background-size: calc(var(--pg-blueprint) * 1.5) calc(var(--pg-blueprint) * 1.5); mask-image: linear-gradient(black, transparent); -webkit-mask-image: linear-gradient(black, transparent); } -.page-head h1 { font-size: clamp(2.75rem, 4.8vw, 4.5rem); line-height: 1.16; letter-spacing: -0.055em; } -.page-head .lede { max-width: 40rem; line-height: 1.9; } -.page-head > .eyebrow, .page-head-grid > div > .eyebrow { display: flex; align-items: center; gap: var(--pg-space-3); font-size: 0.7rem; letter-spacing: 0.1em; } -.page-head > .eyebrow::before, .page-head-grid > div > .eyebrow::before { content: ""; width: 1.25rem; height: 1px; background: currentColor; } -.hero-home { padding-block: var(--pg-space-8); border-bottom: 0; } -.hero-home h1 { font-size: clamp(3.2rem, 5vw, 4.8rem); } -.hero-home .page-head-grid { grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); gap: var(--pg-space-7); } -.hero-home .lede { max-width: 31rem; } -.hero-home .actions { margin-top: var(--pg-space-6); } -.hero-foot { margin-top: var(--pg-space-7); } -.hero-accent { display: inline-block; position: relative; } -.hero-accent::after { content: ""; position: absolute; inset: auto 0 -0.1em; height: 0.065em; background: var(--pg-accent); transform: scaleX(1); transform-origin: left; } -.btn { min-height: 3rem; border-radius: var(--pg-radius-pill); padding-inline: var(--pg-space-5); transition: transform var(--pg-motion-slow) var(--pg-ease), box-shadow var(--pg-motion-slow) var(--pg-ease), background var(--pg-duration), border-color var(--pg-duration); } -.btn:hover { transform: translateY(-3px); } -.btn:active { transform: translateY(0) scale(0.98); } -.btn-primary { box-shadow: none; } -.btn-primary:hover { box-shadow: var(--pg-glow); } -.btn > span { transition: transform var(--pg-motion-slow) var(--pg-ease); } -.btn:hover > span { transform: translate(2px, -2px); } -.section { padding-block: var(--pg-space-9); } -.section-title { font-size: clamp(1.75rem, 3vw, 2.75rem); letter-spacing: -0.045em; line-height: 1.3; } -.section-head { margin-bottom: var(--pg-space-7); } -.section-head .eyebrow, .story-heading .eyebrow { margin-bottom: var(--pg-space-5); } -.project-index { padding: var(--pg-space-4) var(--pg-space-5); border: none; background: var(--pg-bg-sunken); border-radius: var(--pg-radius-pill); } -.project-index a { display: inline-flex; align-items: center; gap: var(--pg-space-5); } -.project-index a span { transition: transform var(--pg-motion-slow) var(--pg-ease); } -.project-index a:hover { text-decoration: none; color: var(--pg-accent); } -.project-index a:hover span { transform: translateY(3px); } -.section-jump { gap: var(--pg-space-3) var(--pg-space-5); margin-top: var(--pg-space-7); padding-top: var(--pg-space-5); border-top: 0; } -.section-jump a { position: relative; margin-right: 0; font-size: var(--pg-text-xs); border: none; } -.section-jump a::after { content: ""; position: absolute; inset: auto 0 0; height: 2px; background: var(--pg-accent); transform: scaleX(0); transform-origin: left; transition: transform var(--pg-motion-slow) var(--pg-ease); } -.section-jump a:hover::after, .section-jump a.is-current::after { transform: scaleX(1); } -.story-title { font-size: clamp(3rem, 6vw, 5.5rem); } -.story-tagline { font-size: clamp(1.4rem, 2.5vw, 2rem); } -.editorial-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr); gap: var(--pg-space-9); } -.story-copy { padding-top: var(--pg-space-2); } -.story-details > div { padding-block: var(--pg-space-4); transition: padding var(--pg-motion-slow) var(--pg-ease), background var(--pg-duration); } -.story-details > div:hover { padding-inline: var(--pg-space-3); background: var(--pg-bg-sunken); } -.story-links a, .command-step > a { display: inline-flex; align-items: center; gap: var(--pg-space-5); } -.source-session { background: var(--pg-term-bg); border: 1px solid var(--pg-term-line); border-radius: var(--pg-radius-md); overflow: hidden; } -.session-label { padding: var(--pg-space-3) var(--pg-space-5); border-bottom: 1px solid var(--pg-term-line); color: var(--pg-term-dim); margin-bottom: 0; } -.source-session .copy-btn { color: var(--pg-term-blue); } -.session-transcript { padding: var(--pg-space-5); color: var(--pg-term-fg); } -.session-transcript .t-p { color: var(--pg-term-green); } -.session-transcript .t-cmd { color: var(--pg-term-fg); } -.term { border: 1px solid var(--pg-term-line); background: var(--pg-term-bg); border-radius: var(--pg-radius-md); box-shadow: var(--pg-shadow-float); } -.term .winbar { padding: var(--pg-space-3) var(--pg-space-5); background: var(--pg-term-bar); border-color: var(--pg-term-line); } -.term .winbar-title { color: var(--pg-term-dim); } -.term .winbar-title::before { content: "● ● ●"; color: var(--pg-term-dim); font-size: 0.65rem; letter-spacing: 0.12em; } -.term .copy-btn { color: var(--pg-term-blue); } -.term-body { padding: var(--pg-space-5); color: var(--pg-term-fg); } -.term .t-p, .term .t-ok { color: var(--pg-term-green); } -.term .t-cmd { color: var(--pg-term-fg); } -.term .t-out { color: var(--pg-term-dim); } -.term .t-hl { color: var(--pg-term-blue); } -.term .t-cursor { background: var(--pg-term-green); } -.fact { transition: background var(--pg-duration); } -.fact:hover { background: var(--pg-bg-sunken); } -.desktop-evidence { perspective: 75rem; overflow-x: clip; overflow-y: visible; } -.desktop-evidence img { border: 1px solid var(--pg-line-2); border-radius: var(--pg-radius-md); box-shadow: var(--pg-shadow-float); } -.desktop-evidence figcaption { padding-top: var(--pg-space-5); } -.proof-label { display: flex; align-items: center; justify-content: space-between; gap: var(--pg-space-3); padding: var(--pg-space-3) var(--pg-space-5); background: var(--pg-bg-sunken); border: 1px solid var(--pg-line-1); border-bottom: 0; border-radius: var(--pg-radius-md) var(--pg-radius-md) 0 0; font-family: var(--pg-font-mono); font-size: var(--pg-text-xs); color: var(--pg-fg-3); } -.proof-label + img { border-radius: 0 0 var(--pg-radius-md) var(--pg-radius-md); } -.archive-index > div { transition: padding var(--pg-motion-slow) var(--pg-ease), background var(--pg-duration); } -.archive-index > div:hover { padding-inline: var(--pg-space-4); background: var(--pg-bg-sunken); } -.timeline { position: relative; } -.timeline li { position: relative; padding-block: var(--pg-space-5); transition: padding var(--pg-motion-slow) var(--pg-ease), background var(--pg-duration); } -.timeline li:hover { background: var(--pg-bg-sunken); padding-inline: var(--pg-space-4); } -.timeline-year { font-size: clamp(1.5rem, 3vw, 2.5rem); letter-spacing: -0.04em; } -.band { padding-block: var(--pg-space-7); } -.band h2 { font-size: clamp(2rem, 4vw, 3.5rem); letter-spacing: -0.04em; } -.band .btn-secondary { background: transparent; } -.mail-big { font-size: clamp(1.5rem, 4vw, 3rem); } -.pager a { padding: var(--pg-space-5); background: var(--pg-bg-sunken); border: 1px solid var(--pg-line-1); border-radius: var(--pg-radius-md); transition: transform var(--pg-motion-slow) var(--pg-ease), border-color var(--pg-duration); } -.pager a:hover { transform: translateY(-3px); border-color: var(--pg-accent); } -.site-footer { padding-block: var(--pg-space-8); } -.footer-col h2 { margin-bottom: var(--pg-space-5); } -.footer-col ul { gap: var(--pg-space-3); } -.footer-base { margin-top: var(--pg-space-7); } - -/* 首页架构台:CSS 立体平面,内容与控制独立于装饰。 */ -.architecture-lab { min-width: 0; border: 1px solid var(--pg-line-2); border-radius: var(--pg-radius-lg); background: var(--pg-bg-raised); box-shadow: var(--pg-shadow-float); overflow: hidden; } -.lab-bar { display: flex; align-items: center; justify-content: space-between; gap: var(--pg-space-3); padding: var(--pg-space-4) var(--pg-space-5); border-bottom: 0; font-family: var(--pg-font-mono); font-size: 0.65rem; letter-spacing: 0.04em; color: var(--pg-fg-3); } -.lab-bar > span:first-child { display: flex; align-items: center; gap: var(--pg-space-2); color: var(--pg-fg-1); } -.status-dot { display: inline-block; width: 0.4rem; height: 0.4rem; border-radius: 50%; background: var(--pg-accent); box-shadow: var(--pg-stroke-glow); } -.stack-view { position: relative; height: 19.5rem; overflow: hidden; perspective: 60rem; background-image: radial-gradient(var(--pg-line-2) 1px, transparent 1px); background-size: 1.25rem 1.25rem; } -.stack-assembly { position: absolute; inset: 0; transform: rotateX(var(--pg-stack-tilt-x)) rotateY(var(--pg-stack-tilt-y)); transition: transform var(--pg-motion-slow) var(--pg-ease); } -.stack-plane { position: absolute; left: 17%; width: 66%; height: 8rem; padding: var(--pg-space-4) var(--pg-space-5); border: 1px solid var(--pg-line-strong); border-radius: var(--pg-radius-sm); background: var(--pg-bg-sunken); box-shadow: 0 0.6rem 0 var(--pg-line-1), var(--pg-shadow-float); transform: rotateX(48deg) rotateZ(-28deg); transform-style: preserve-3d; transition: background var(--pg-motion-slow), border-color var(--pg-motion-slow), translate var(--pg-motion-slow) var(--pg-ease); } -.plane-app { top: 2rem; z-index: 3; } -.plane-kernel { top: 6rem; z-index: 2; } -.plane-runtime { top: 10rem; z-index: 1; } -.plane-id { display: block; font-family: var(--pg-font-mono); font-size: 0.65rem; color: var(--pg-fg-3); letter-spacing: 0.08em; } -.stack-plane strong { display: block; margin-top: var(--pg-space-2); font-family: var(--pg-font-mono); font-size: 0.95rem; font-weight: 500; } -.plane-nodes { display: block; margin-top: var(--pg-space-3); color: var(--pg-accent); font-size: var(--pg-text-xs); letter-spacing: 0.15em; } -.stack-plane[data-selected="true"] { background: var(--pg-ref-indigo-700); border-color: var(--pg-ref-indigo-200); color: var(--pg-ref-paper); translate: 0 -0.5rem; } -.stack-plane[data-selected="true"] .plane-id, .stack-plane[data-selected="true"] .plane-nodes { color: var(--pg-ref-paper); } -.stack-coordinate { position: absolute; bottom: var(--pg-space-3); font-family: var(--pg-font-mono); font-size: 0.5rem; letter-spacing: 0.1em; color: var(--pg-fg-3); } -.coord-left { left: var(--pg-space-5); } -.coord-right { right: var(--pg-space-5); } -.stack-orbit { position: absolute; left: 6%; top: 10%; width: 88%; height: 80%; border: 1px solid var(--pg-line-1); border-radius: 50%; transform: rotate(-25deg); } -.orbit-two { width: 72%; left: 14%; height: 65%; top: 18%; } -.layer-controls { display: grid; grid-template-columns: repeat(3, 1fr); border-block: 1px solid var(--pg-line-1); } -.layer-choice { position: relative; padding: var(--pg-space-3); border: none; background: var(--pg-bg-sunken); font-family: var(--pg-font-mono); font-size: 0.65rem; color: var(--pg-fg-3); cursor: pointer; transition: background var(--pg-duration), color var(--pg-duration); } -.layer-choice + .layer-choice { border-left: 1px solid var(--pg-line-1); } -.layer-choice span { margin-left: var(--pg-space-2); font-family: var(--pg-font-sans); font-size: var(--pg-text-xs); } -.layer-choice[aria-pressed="true"] { background: var(--pg-bg-raised); color: var(--pg-accent); } -.layer-choice::after { content: ""; position: absolute; inset: auto 0 0; height: 2px; background: var(--pg-accent); transform: scaleX(0); transition: transform var(--pg-motion-slow) var(--pg-ease); } -.layer-choice[aria-pressed="true"]::after { transform: scaleX(1); } -.layer-choice:hover { color: var(--pg-accent); } -.lab-description { padding: var(--pg-space-4) var(--pg-space-5); min-height: 7.1rem; } -.lab-description code { font-size: 0.65rem; color: var(--pg-accent); } -.lab-description p { color: var(--pg-fg-2); font-size: var(--pg-text-xs); margin-top: var(--pg-space-2); } -.lab-source { display: flex; justify-content: space-between; padding: var(--pg-space-3) var(--pg-space-5); border-top: 0; color: var(--pg-fg-3); font-size: 0.7rem; } -.lab-source:hover { color: var(--pg-accent); text-decoration: none; background: var(--pg-bg-sunken); } - -/* 滚动叙事:右侧保持自然阅读,左侧流程随位置推进。 */ -.command-story { background: var(--pg-bg-sunken); border-top: 0; } -.command-layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--pg-space-9); align-items: start; } -.command-sticky { position: sticky; top: calc(var(--pg-header-h) + var(--pg-space-6)); } -.command-sticky > .eyebrow { margin-bottom: var(--pg-space-5); } -.execution-map { max-width: 24rem; margin-top: var(--pg-space-6); padding: var(--pg-space-5); background: var(--pg-bg-page); border: 1px solid var(--pg-line-2); border-radius: var(--pg-radius-md); } -.execution-top { display: flex; justify-content: space-between; font-family: var(--pg-font-mono); font-size: 0.6rem; letter-spacing: 0.08em; color: var(--pg-fg-3); margin-bottom: var(--pg-space-4); } -.execution-node { display: grid; grid-template-columns: 1.5rem minmax(0, 1fr) auto; align-items: center; gap: var(--pg-space-2); padding: var(--pg-space-3); border: 1px solid var(--pg-line-1); border-radius: var(--pg-radius-sm); transition: transform var(--pg-motion-slow) var(--pg-ease), background var(--pg-motion-slow), border-color var(--pg-duration); } -.execution-node > span { font-family: var(--pg-font-mono); font-size: 0.6rem; color: var(--pg-fg-3); } -.execution-node strong { font-size: var(--pg-text-sm); font-weight: 500; } -.execution-node code { color: var(--pg-fg-3); font-size: 0.55rem; } -.execution-node.is-active { border-color: var(--pg-accent); background: var(--pg-accent-soft); transform: translateX(var(--pg-space-1)); } -.execution-node.is-active strong, .execution-node.is-active > span { color: var(--pg-accent); } -.execution-wire { height: var(--pg-space-3); width: 1px; background: var(--pg-line-2); margin-left: var(--pg-space-5); } -.execution-track { height: 2px; background: var(--pg-line-1); margin-top: var(--pg-space-5); overflow: hidden; } -.execution-track > span { display: block; height: 100%; background: var(--pg-accent); transform: scaleX(var(--pg-section-progress)); transform-origin: left; } -.command-step { display: flex; flex-direction: column; justify-content: center; min-height: 27rem; padding-block: var(--pg-space-7); border-top: 1px solid var(--pg-line-2); } -.step-number { font-family: var(--pg-font-mono); font-size: var(--pg-text-xs); letter-spacing: 0.07em; color: var(--pg-fg-3); } -.command-step.is-active .step-number { color: var(--pg-accent); } -.command-step h3 { font-size: clamp(1.4rem, 2.3vw, 2rem); font-weight: 600; letter-spacing: -0.035em; line-height: 1.4; margin-block: var(--pg-space-5); } -.command-step p { color: var(--pg-fg-2); font-size: var(--pg-text-sm); line-height: 2; max-width: 30rem; } -.command-step > code { font-size: var(--pg-text-xs); color: var(--pg-fg-3); margin-top: var(--pg-space-5); overflow-wrap: anywhere; } -.command-step > a { align-self: start; margin-top: var(--pg-space-5); font-size: var(--pg-text-sm); } -.space-preview { margin: 0; min-width: 0; } -.space-preview .frame { border-radius: var(--pg-radius-md); border: 1px solid var(--pg-line-2); box-shadow: var(--pg-shadow-float); transform: perspective(60rem) rotateY(-7deg) rotateX(3deg); transition: transform var(--pg-stack-enter) var(--pg-ease); } -.space-preview:hover .frame { transform: perspective(60rem) rotateY(0) rotateX(0); } -.space-preview figcaption { display: flex; justify-content: space-between; gap: var(--pg-space-3); margin-top: var(--pg-space-5); font-family: var(--pg-font-mono); color: var(--pg-fg-3); font-size: 0.65rem; } -.space-preview .winbar { background: var(--pg-bg-sunken); padding: var(--pg-space-3) var(--pg-space-4); } -.hero-space .page-head-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); } -.back-top { position: fixed; right: var(--pg-space-5); bottom: var(--pg-space-5); z-index: 900; display: grid; place-items: center; width: 2.75rem; height: 2.75rem; background: var(--pg-bg-raised); color: var(--pg-accent); border: 1px solid var(--pg-line-2); box-shadow: var(--pg-shadow); border-radius: 50%; transform: translateY(5rem); visibility: hidden; transition: transform var(--pg-motion-slow) var(--pg-ease), visibility var(--pg-motion-slow); } -.back-top.is-visible { visibility: visible; transform: none; } -.back-top:hover { text-decoration: none; background: var(--pg-accent-soft); } -.reading-progress { height: 3px; } - -/* 首屏的组成顺序清晰可见,正文不用透明度。 */ -@media (prefers-reduced-motion: no-preference) { - html:not([data-motion="off"]) body:not(.motion-paused) .hero-home h1, html:not([data-motion="off"]) body:not(.motion-paused) .page-head-grid > div > h1 { animation: lab-title-in 1s var(--pg-ease) both; } - html:not([data-motion="off"]) body:not(.motion-paused) .hero-accent::after { animation: lab-underline 1.3s var(--pg-ease) both; } - html:not([data-motion="off"]) body:not(.motion-paused) .stack-plane { animation: stack-arrive var(--pg-stack-enter) var(--pg-ease) both; } - html:not([data-motion="off"]) body:not(.motion-paused) .plane-app { animation-delay: 160ms; } - html:not([data-motion="off"]) body:not(.motion-paused) .plane-kernel { animation-delay: 80ms; } - html:not([data-motion="off"]) body:not(.motion-paused) .term .t-line { animation: terminal-arrive 800ms var(--pg-ease) both; } - html:not([data-motion="off"]) body:not(.motion-paused) .term .t-line:nth-child(3n) { animation-delay: 100ms; } - html:not([data-motion="off"]) body:not(.motion-paused) .term .t-line:nth-child(3n + 1) { animation-delay: 200ms; } -} -@keyframes lab-title-in { from { transform: translateY(var(--pg-space-5)); } to { transform: none; } } -@keyframes lab-underline { from { transform: scaleX(0); } to { transform: scaleX(1); } } -@keyframes stack-arrive { from { transform: rotateX(20deg) rotateZ(-10deg) translateY(var(--pg-space-7)); } to { transform: rotateX(48deg) rotateZ(-28deg); } } -@keyframes terminal-arrive { from { transform: translateX(var(--pg-space-5)); } to { transform: none; } } -@supports (animation-timeline: view()) { - @media (prefers-reduced-motion: no-preference) { - html:not([data-motion="off"]) body:not(.motion-paused) .desktop-evidence img { animation: lab-image-unfold linear both; animation-timeline: view(); animation-range: entry 0% cover 45%; transform-origin: center top; } + /* 有脚本时收进菜单,由页眉按钮展开。 */ + html.js .nav-toggle { display: inline-flex; } + html.js .site-nav { display: none; } + html.js .site-nav.open { display: block; } + html.js .site-nav > ul { flex-direction: column; align-items: stretch; padding-bottom: var(--pg-space-3); } + html.js .menu > ul { + position: static; + min-width: 0; + box-shadow: none; + border: 0; + background: transparent; + padding-block: 0; } } -@keyframes lab-image-unfold { from { transform: perspective(75rem) rotateX(7deg) scale(0.94); } to { transform: perspective(75rem) rotateX(0) scale(1); } } -@media (min-width: 961px) { - .project-story .story-heading { position: sticky; top: calc(var(--pg-header-h) + var(--pg-space-7)); align-self: start; } -} -@media (max-width: 1100px) { - .site-nav a { padding-inline: var(--pg-space-2); } - .brand-name small { display: none; } - .hero-home .page-head-grid, .hero-space .page-head-grid { gap: var(--pg-space-6); } -} -@media (max-width: 960px) { - .page-head { padding-block: var(--pg-space-7); } - .hero-home .page-head-grid, .hero-space .page-head-grid { grid-template-columns: minmax(0, 1fr); gap: var(--pg-space-7); } - .hero-home .lede { max-width: 40rem; } - .architecture-lab { width: 100%; max-width: 36rem; justify-self: center; } - .stack-view { height: 19rem; } - .site-nav a { padding: var(--pg-space-3); } - .site-nav a::before { inset: 0 auto 0 0; height: auto; width: 2px; transform: scaleY(0); } - .site-nav a[aria-current="page"]::before, .site-nav a:hover::before { transform: scaleY(1); } - .section { padding-block: var(--pg-space-8); } - .editorial-grid { grid-template-columns: minmax(0, 1fr); gap: var(--pg-space-6); } - .command-layout { gap: var(--pg-space-6); } - .command-sticky { top: calc(var(--pg-header-h) + var(--pg-space-5)); } - .execution-node { grid-template-columns: 1.25rem minmax(0, 1fr); } - .execution-node code { grid-column: 2; } - .space-preview { max-width: 42rem; } - .space-preview .frame { transform: none; } - .hero-home .actions .btn { flex: 0 1 auto; } -} -@media (max-width: 600px) { - .page-head h1 { font-size: clamp(2.1rem, 8vw, 3rem); } - .hero-home h1 { font-size: clamp(2.5rem, 10.3vw, 3.75rem); } - .page-head .lede { font-size: var(--pg-text-sm); } - .hero-home .eyebrow { font-size: 0.6rem; letter-spacing: 0.04em; } - .hero-home .actions { margin-top: var(--pg-space-5); } - .hero-home .actions .btn { flex: 1 1 auto; padding-inline: var(--pg-space-4); } - .hero-foot { margin-top: var(--pg-space-5); } - .hero-foot span:last-child { width: 100%; } - .architecture-lab { border-radius: var(--pg-radius-md); } - .stack-view { height: 17rem; } - .stack-plane { left: 14%; width: 72%; height: 7rem; padding: var(--pg-space-3); } - .plane-app { top: 1.5rem; } - .plane-kernel { top: 5.5rem; } - .plane-runtime { top: 9.5rem; } - .stack-plane strong { font-size: 0.8rem; } - .plane-nodes { margin-top: var(--pg-space-2); } - .lab-bar { padding-inline: var(--pg-space-4); font-size: 0.55rem; } - .lab-description { padding-inline: var(--pg-space-4); } - .command-layout { grid-template-columns: minmax(0, 1fr); } - .command-sticky { position: static; } - .execution-map { max-width: none; } - .execution-node { grid-template-columns: 1.5rem minmax(0, 1fr) auto; } - .execution-node code { grid-column: auto; } - .command-step { min-height: 20rem; padding-block: var(--pg-space-6); } - .section { padding-block: var(--pg-space-7); } - .section-head { margin-bottom: var(--pg-space-6); } - .story-title { font-size: 3.25rem; } - .source-session .session-label { padding-inline: var(--pg-space-4); gap: var(--pg-space-2); font-size: 0.6rem; } - .session-transcript, .term-body { padding: var(--pg-space-4); font-size: 0.65rem; } - .term .winbar { padding-inline: var(--pg-space-4); } - .term .winbar-title::before { display: none; } - .term .winbar-title { font-size: 0.65rem; } - .space-preview figcaption { flex-wrap: wrap; } - .project-index { gap: var(--pg-space-3) var(--pg-space-5); } - .project-index a { gap: var(--pg-space-3); } - .site-footer { padding-block: var(--pg-space-7); } - .back-top { right: var(--pg-space-4); bottom: var(--pg-space-4); } -} -body.motion-paused *, body.motion-paused *::before, body.motion-paused *::after { animation: none !important; transition: none !important; } -body.motion-paused { --pg-stack-tilt-x: 0deg !important; --pg-stack-tilt-y: 0deg !important; } -body.motion-paused .scroll-reveal, body.motion-paused .desktop-evidence img, body.motion-paused .space-preview .frame { transform: none; } -html:has(body.motion-paused) { scroll-behavior: auto; } -@media (prefers-reduced-motion: reduce), print { - *, *::before, *::after { animation: none !important; transition: none !important; } - .scroll-reveal, .desktop-evidence img, .space-preview .frame, .stack-assembly { transform: none; } - .btn:hover, .icon-btn:hover, .pager a:hover, .execution-node.is-active { transform: none; } - .stack-plane[data-selected="true"] { translate: none; } - .back-top { display: none; } -} -@media print { - .site-header, .back-top, .layer-controls { display: none; } - .command-sticky, .project-story .story-heading { position: static; } - .page-head, .section { padding-block: var(--pg-space-5); } - .term, .architecture-lab, .desktop-evidence img { box-shadow: none; } -} -/* ---------- 17. 内容布局:产品、文档、下载与存档 ---------- */ -.page-head { border: none; } -.page-head::before { background-image: none; } -.hero-home::before { background-image: radial-gradient(var(--pg-line-2) 1px, transparent 1px); background-size: 1.25rem 1.25rem; mask-image: linear-gradient(90deg, transparent 45%, black); -webkit-mask-image: linear-gradient(90deg, transparent 45%, black); } -.hero-home { padding-bottom: var(--pg-space-7); } -.hero-foot { padding-top: 0; } -.section .eyebrow::before { content: none; } -.content-section:nth-of-type(even) { background: color-mix(in srgb, var(--pg-bg-sunken) 45%, var(--pg-bg-page)); } -.feature-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--pg-space-5); list-style: none; margin: 0; padding: 0; } -.feature-card { position: relative; min-width: 0; padding: var(--pg-space-6); background: var(--pg-bg-raised); border: 1px solid transparent; border-radius: var(--pg-radius-lg); transition: transform var(--pg-motion-slow) var(--pg-ease), box-shadow var(--pg-motion-slow) var(--pg-ease), border-color var(--pg-duration); } -.feature-card:hover { transform: translateY(-5px); box-shadow: var(--pg-shadow-float); border-color: var(--pg-line-2); } -.feature-icon { display: inline-grid; place-items: center; width: 3rem; height: 3rem; background: var(--pg-accent-soft); color: var(--pg-accent); border-radius: var(--pg-radius-md); margin-bottom: var(--pg-space-5); } -.feature-title { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--pg-space-2); font-size: var(--pg-text-lg); font-weight: 600; line-height: 1.5; letter-spacing: -0.025em; } -.feature-meta { display: block; flex-basis: 100%; font-family: var(--pg-font-mono); font-size: 0.65rem; font-weight: 400; letter-spacing: 0; color: var(--pg-fg-3); } -.feature-body { color: var(--pg-fg-2); font-size: var(--pg-text-sm); line-height: 1.9; margin-top: var(--pg-space-3); } -.feature-body p + p, .feature-body > * + * { margin-top: var(--pg-space-3); } -.feature-body code { overflow-wrap: anywhere; } -.feature-body a { text-decoration: underline; text-underline-offset: 3px; } -.card-warn .feature-icon { background: var(--pg-warn-bg); color: var(--pg-warn-fg); } -.card-ok .feature-meta { color: var(--pg-ok-fg); } -.feature-mosaic { grid-template-columns: repeat(3, minmax(0, 1fr)); } -.page-pyspos .section-features .feature-card:first-child { grid-column: span 2; background: var(--pg-accent-soft); } -.page-pyspos .section-features .feature-card:nth-child(2) { grid-row: span 2; background: var(--pg-term-bg); color: var(--pg-term-fg); } -.page-pyspos .section-features .feature-card:nth-child(2) .feature-body { color: var(--pg-term-dim); } -.page-pyspos .section-features .feature-card:nth-child(2) .feature-body a { color: var(--pg-term-blue); } -.page-pyspos .section-features .feature-card:nth-child(2)::after { content: "APP → SURFACE → COMPOSITOR"; display: block; padding: var(--pg-space-5) var(--pg-space-3); margin-top: var(--pg-space-6); border: 1px solid var(--pg-term-line); border-radius: var(--pg-radius-md); color: var(--pg-term-blue); font-family: var(--pg-font-mono); font-size: 0.6rem; } -.page-pyspos .section-tech .feature-mosaic { grid-template-columns: repeat(2, minmax(0, 1fr)); } -.page-pyspos .section-tech .feature-card:last-child { grid-column: span 2; } -.page-pyspos .section-tech .feature-icon { float: right; margin: 0 0 var(--pg-space-4) var(--pg-space-4); } -.overview-layout { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); gap: var(--pg-space-7); align-items: start; } -.overview-copy { font-size: var(--pg-text-md); line-height: 1.95; } -.overview-summary { background: var(--pg-bg-sunken); padding: var(--pg-space-5); border-radius: var(--pg-radius-lg); } -.facts { border: none; padding: 0; gap: var(--pg-space-3); margin-top: var(--pg-space-5); } -.fact { background: var(--pg-bg-raised); padding: var(--pg-space-5); border-radius: var(--pg-radius-md); } -.fact:hover { background: var(--pg-accent-soft); } -.fact dt { font-size: 0.65rem; margin-bottom: var(--pg-space-3); letter-spacing: 0.04em; } -.fact dd { font-size: var(--pg-text-md); font-weight: 500; } -.overview-summary .facts { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 0; } -.overview-summary .fact { background: transparent; padding: var(--pg-space-3); } -.overview-summary .fact dd { font-size: var(--pg-text-sm); } -pre.code { background: var(--pg-bg-sunken); border: none; border-radius: var(--pg-radius-md); padding: var(--pg-space-5); font-size: var(--pg-text-xs); } -.notice { border: none; padding: var(--pg-space-5); border-radius: var(--pg-radius-md); background: var(--pg-bg-sunken); } -.notice-warn { background: var(--pg-warn-bg); } -.notice-info { background: var(--pg-accent-soft); } -.notice-icon { display: grid; place-items: center; width: 1.6rem; height: 1.6rem; border: 1px solid currentColor; border-radius: 50%; font-size: var(--pg-text-sm); } -.notice .notice-title { line-height: 1.6; } -.notice-body { line-height: 1.9; } -.section-jump { border: none; gap: var(--pg-space-2); } -.section-jump a { padding: var(--pg-space-2) var(--pg-space-4); background: var(--pg-bg-sunken); border-radius: var(--pg-radius-pill); } -.section-jump a::after { display: none; } -.section-jump a:hover, .section-jump a.is-current { background: var(--pg-accent-soft); color: var(--pg-accent); } -.project-story { border: none; padding-block: var(--pg-space-6); } -.project-story > .editorial-grid { background: var(--pg-bg-sunken); border-radius: var(--pg-radius-lg); padding: var(--pg-space-7); gap: var(--pg-space-7); } -.project-story[data-product="SpaceOS 8"] > .editorial-grid { background: var(--pg-accent-soft); } -.project-story .story-heading { position: static; } -.story-details { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--pg-space-3); border: none; } -.story-details > div { display: block; border: none; padding: var(--pg-space-3); border-radius: var(--pg-radius-md); background: var(--pg-bg-page); font-size: var(--pg-text-xs); } -.story-details dt { color: var(--pg-accent); margin-bottom: var(--pg-space-2); } -.story-details > div:hover { padding: var(--pg-space-3); background: var(--pg-bg-raised); } -.command-story { background: transparent; } -.command-layout { padding: var(--pg-space-7); background: var(--pg-bg-sunken); border-radius: var(--pg-radius-lg); gap: var(--pg-space-7); } -.command-steps { display: grid; gap: var(--pg-space-5); } -.command-step { background: var(--pg-bg-page); border: none; border-radius: var(--pg-radius-lg); padding: var(--pg-space-6); min-height: 25rem; transition: background var(--pg-motion-slow); } -.command-step.is-active { background: var(--pg-bg-raised); } -.archive-index { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--pg-space-4); } -.archive-index > div { display: flex; flex-direction: column; gap: var(--pg-space-3); padding: var(--pg-space-5); border: none; border-radius: var(--pg-radius-lg); background: var(--pg-bg-sunken); } -.archive-index > div:hover { padding: var(--pg-space-5); background: var(--pg-accent-soft); } -.archive-index .badge { align-self: start; } -.archive-index > p { grid-column: 1 / -1; padding: 0; border: none; } -.timeline { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--pg-space-4); } -.timeline li { display: flex; flex-direction: column; align-items: start; gap: var(--pg-space-5); border: none; padding: var(--pg-space-5); background: var(--pg-bg-sunken); border-radius: var(--pg-radius-lg); } -.timeline li:last-child { border: none; } -.timeline li:hover { padding: var(--pg-space-5); background: var(--pg-accent-soft); } -.timeline-year { color: var(--pg-accent); } -.timeline-title { line-height: 1.5; font-size: var(--pg-text-sm); } -.timeline p { font-size: var(--pg-text-xs); } -.band { padding: var(--pg-space-7); border-radius: var(--pg-radius-lg); } -.section:has(> .wrap > .band) { background: transparent; } -.pager { border: none; } -.pager a { border: none; padding: var(--pg-space-6); } -.table-scroll { border-radius: var(--pg-radius-lg); margin-inline: 0; padding: 0; } -table.data { border: none; } -table.data td, table.data th { border: none; padding: var(--pg-space-4) var(--pg-space-5); } -table.data tbody tr:nth-child(even) { background: var(--pg-bg-sunken); } -.metric-exhibits { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--pg-space-5); } -.metric-exhibit { min-width: 0; padding: var(--pg-space-6); border-radius: var(--pg-radius-lg); background: var(--pg-bg-raised); } -.metric-exhibit:first-child { background: var(--pg-accent-soft); } -.metric-exhibit dt { font-size: var(--pg-text-sm); color: var(--pg-fg-2); } -.metric-exhibit dd { font-size: clamp(1.5rem, 2.7vw, 2.5rem); font-weight: 600; letter-spacing: -0.05em; margin-block: var(--pg-space-4); overflow-wrap: anywhere; } -.metric-exhibit p { color: var(--pg-fg-3); font-size: var(--pg-text-xs); line-height: 1.8; } -.metric-exhibit:nth-last-child(-n + 2) dd { font-family: var(--pg-font-mono); font-size: var(--pg-text-xl); letter-spacing: -0.04em; } -.document-layout { display: grid; grid-template-columns: 13rem minmax(0, 1fr); gap: var(--pg-space-7); align-items: start; } -.document-sidebar { position: sticky; top: calc(var(--pg-header-h) + var(--pg-space-6)); padding-top: var(--pg-space-6); } -.document-sidebar > .eyebrow { padding-inline: var(--pg-space-4); margin-bottom: var(--pg-space-4); } -.page-toc { display: grid; gap: var(--pg-space-2); } -.page-toc a { padding: var(--pg-space-3) var(--pg-space-4); font-size: var(--pg-text-xs); color: var(--pg-fg-2); border-radius: var(--pg-radius-sm); transition: background var(--pg-duration); } -.page-toc a:hover, .page-toc a.is-current { background: var(--pg-accent-soft); color: var(--pg-accent); text-decoration: none; } -.sidebar-next { display: block; font-size: var(--pg-text-xs); margin: var(--pg-space-5) var(--pg-space-4); } -.document-content { min-width: 0; } -.document-content .section { padding-block: var(--pg-space-6) var(--pg-space-7); background: transparent; } -.document-content .section > .wrap, .document-content > .wrap { padding-inline: 0; } -.document-content .section-title { font-size: clamp(1.5rem, 2.4vw, 2.25rem); } -.product-choices { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--pg-space-5); margin-top: var(--pg-space-7); } -.product-choice { display: flex; align-items: center; gap: var(--pg-space-5); background: var(--pg-bg-raised); padding: var(--pg-space-6); border-radius: var(--pg-radius-lg); color: var(--pg-fg-1); transition: transform var(--pg-motion-slow) var(--pg-ease), box-shadow var(--pg-duration); } -.product-choice:hover { transform: translateY(-5px); box-shadow: var(--pg-shadow-float); text-decoration: none; } -.product-choice h2 { font-size: var(--pg-text-xl); font-weight: 600; letter-spacing: -0.035em; } -.product-choice p { font-size: var(--pg-text-xs); color: var(--pg-fg-3); margin-top: var(--pg-space-2); } -.product-choice .eyebrow { font-size: 0.6rem; } -.product-choice > span:last-child { margin-left: auto; color: var(--pg-accent); } -.choice-mark { display: grid; place-items: center; flex: none; width: 3.5rem; height: 3.5rem; background: var(--pg-accent-soft); color: var(--pg-accent); border-radius: var(--pg-radius-md); font-family: var(--pg-font-mono); font-weight: 700; font-size: var(--pg-text-xl); } -.choice-kernel { background: var(--pg-accent-soft); } -.choice-kernel .choice-mark { background: var(--pg-bg-raised); } -.requirement-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } -.requirement-grid .feature-card { padding: var(--pg-space-5); } -.requirement-grid .feature-icon { margin-bottom: var(--pg-space-4); } -.archive-hero-grid { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: var(--pg-space-7); align-items: center; } -.archive-art { display: grid; justify-items: center; padding: var(--pg-space-6); border-radius: var(--pg-radius-lg); background: var(--pg-bg-sunken); font-family: var(--pg-font-mono); color: var(--pg-fg-3); font-size: 0.6rem; letter-spacing: 0.15em; } -.archive-art strong { font-size: clamp(7rem, 15vw, 12rem); font-family: var(--pg-font-display); font-weight: 400; line-height: 1.15; letter-spacing: -0.1em; color: var(--pg-accent); } -.archive-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } -.archive-layout .overview-layout { grid-template-columns: minmax(0, 1fr); } -.archive-layout .overview-summary .facts { grid-template-columns: repeat(3, minmax(0, 1fr)); } -.archive-layout .timeline { grid-template-columns: repeat(2, minmax(0, 1fr)); } -.download-hero-grid { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: var(--pg-space-8); align-items: center; } -.download-file-card, .image-file-card { padding: var(--pg-space-6); border-radius: var(--pg-radius-lg); background: var(--pg-bg-raised); box-shadow: var(--pg-shadow-float); min-width: 0; } -.download-file-card .section-head { margin-bottom: var(--pg-space-5); } -.download-file-card .section-title { font-size: var(--pg-text-xl); display: flex; flex-wrap: wrap; align-items: center; gap: var(--pg-space-3); } -.download-file-card .facts { grid-template-columns: repeat(3, minmax(0, 1fr)); } -.download-file-card .fact { padding: var(--pg-space-3); background: var(--pg-bg-sunken); } -.download-file-card .fact dd { font-size: 0.7rem; } -.download-file-card .fact:last-child { grid-column: 1 / -1; } -.download-file-card .actions .btn { width: 100%; justify-content: center; } -.image-file-card { display: flex; flex-direction: column; align-items: center; gap: var(--pg-space-4); background: var(--pg-accent-soft); } -.image-file-card .eyebrow { font-size: 0.6rem; } -.image-file-card h2 { font-size: var(--pg-text-2xl); letter-spacing: -0.04em; font-weight: 600; } -.image-file-card h2 span { font-family: var(--pg-font-mono); font-size: var(--pg-text-sm); color: var(--pg-fg-3); margin-left: var(--pg-space-2); } -.image-file-card p { font-family: var(--pg-font-mono); font-size: var(--pg-text-xs); color: var(--pg-fg-3); } -.image-file-card > a:last-child { font-size: var(--pg-text-xs); } -.disc-art { width: 8rem; height: 8rem; display: grid; place-items: center; border-radius: 50%; background: conic-gradient(from 30deg, var(--pg-bg-raised), var(--pg-line-2), var(--pg-bg-raised), var(--pg-accent-soft), var(--pg-bg-raised)); border: 1px solid var(--pg-line-2); box-shadow: var(--pg-shadow-float); } -.disc-art span { display: grid; place-items: center; width: 3rem; height: 3rem; border-radius: 50%; background: var(--pg-ref-indigo-700); color: var(--pg-ref-paper); font-family: var(--pg-font-mono); font-size: var(--pg-text-xs); } -.download-workspace { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--pg-space-5); align-items: start; } -.package-metadata .facts { grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 0; } -.package-metadata .fact:last-child { grid-column: 1 / -1; } -.checksum-panel { padding: var(--pg-space-6); background: var(--pg-bg-raised); border-radius: var(--pg-radius-lg); } -.checksum-panel h3 { font-size: var(--pg-text-xl); letter-spacing: -0.035em; margin-bottom: var(--pg-space-5); } -.checksum-panel .prose { font-size: var(--pg-text-sm); } -.steps { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--pg-space-4); } -.steps li { display: block; padding: var(--pg-space-5); border: none; background: var(--pg-bg-raised); border-radius: var(--pg-radius-md); line-height: 1.9; } -.steps li::before { display: grid; background: var(--pg-accent-soft); width: 2rem; height: 2rem; border-radius: 50%; transform: none; margin-bottom: var(--pg-space-3); } -.compatibility-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } -.release-toolbar { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: var(--pg-space-4); margin-bottom: var(--pg-space-5); } -.release-filters { display: flex; padding: var(--pg-space-1); gap: var(--pg-space-1); border-radius: var(--pg-radius-pill); background: var(--pg-bg-sunken); } -.release-filters button { border: none; background: transparent; color: var(--pg-fg-2); padding: var(--pg-space-3) var(--pg-space-4); border-radius: var(--pg-radius-pill); font: inherit; font-size: var(--pg-text-xs); cursor: pointer; transition: background var(--pg-duration), color var(--pg-duration); } -.release-filters button[aria-pressed="true"] { background: var(--pg-bg-raised); color: var(--pg-accent); box-shadow: var(--pg-shadow); } -.release-search input { min-width: 15rem; padding: var(--pg-space-3) var(--pg-space-4); font: inherit; font-size: var(--pg-text-xs); color: var(--pg-fg-1); background: var(--pg-bg-raised); border: 1px solid var(--pg-line-strong); border-radius: var(--pg-radius-pill); } -.release-count { color: var(--pg-fg-3); font-family: var(--pg-font-mono); font-size: var(--pg-text-xs); margin-bottom: var(--pg-space-5); } -.release-feed { display: grid; gap: var(--pg-space-5); padding: 0; list-style: none; } -.release-card { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: var(--pg-space-7); padding: var(--pg-space-7); border-radius: var(--pg-radius-lg); background: var(--pg-bg-raised); } -.release-card:first-child { background: var(--pg-accent-soft); } -.release-card .feature-title { align-content: start; font-size: var(--pg-text-2xl); } -.release-card .feature-title .badge { font-size: 0.65rem; } -.release-card .feature-meta { margin-top: var(--pg-space-3); } -.release-card .feature-body { margin-top: 0; } -.release-card .feature-body > p:last-child > a { display: inline-flex; padding: var(--pg-space-2) var(--pg-space-4); background: var(--pg-bg-sunken); border-radius: var(--pg-radius-pill); text-decoration: none; } -.release-card code { font-size: 0.65rem; } -.release-empty { padding: var(--pg-space-7); border-radius: var(--pg-radius-lg); background: var(--pg-bg-sunken); text-align: center; color: var(--pg-fg-2); } -@media (max-width: 1100px) { - .feature-mosaic { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .page-pyspos .section-features .feature-card:nth-child(2) { grid-row: auto; } - .timeline { grid-template-columns: repeat(3, minmax(0, 1fr)); } - .document-layout { grid-template-columns: 11rem minmax(0, 1fr); gap: var(--pg-space-5); } - .download-hero-grid { gap: var(--pg-space-6); } - .overview-layout { gap: var(--pg-space-5); } -} -@media (max-width: 960px) { - .overview-layout { grid-template-columns: minmax(0, 1fr); } - .document-layout { grid-template-columns: minmax(0, 1fr); } - .document-sidebar { position: static; padding: 0; } - .page-toc { display: flex; flex-wrap: wrap; } - .page-toc a { background: var(--pg-bg-sunken); border-radius: var(--pg-radius-pill); } - .document-sidebar > .eyebrow, .sidebar-next { display: none; } - .archive-hero-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 0.65fr); gap: var(--pg-space-5); } - .download-hero-grid { grid-template-columns: minmax(0, 1fr); gap: var(--pg-space-7); } - .download-file-card, .image-file-card { width: 100%; max-width: 42rem; } - .project-story > .editorial-grid { padding: var(--pg-space-6); } - .metric-exhibits { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .command-layout { padding: var(--pg-space-6); gap: var(--pg-space-5); } - .command-step { padding: var(--pg-space-5); } - .product-choices { gap: var(--pg-space-4); } - .product-choice { padding: var(--pg-space-5); gap: var(--pg-space-3); } - .release-card { gap: var(--pg-space-5); padding: var(--pg-space-6); } -} -@media (max-width: 600px) { - .feature-grid, .feature-mosaic, .requirement-grid, .archive-grid, .compatibility-grid, .page-pyspos .section-tech .feature-mosaic { grid-template-columns: minmax(0, 1fr); gap: var(--pg-space-4); } - .page-pyspos .section-features .feature-card:first-child, .page-pyspos .section-tech .feature-card:last-child { grid-column: auto; } - .feature-card { padding: var(--pg-space-5); border-radius: var(--pg-radius-md); } - .feature-icon { width: 2.5rem; height: 2.5rem; margin-bottom: var(--pg-space-4); } - .feature-title { font-size: var(--pg-text-md); } - .project-index { border-radius: var(--pg-radius-md); } - .project-story > .editorial-grid { padding: var(--pg-space-5); gap: var(--pg-space-5); } - .story-details { grid-template-columns: minmax(0, 1fr); } - .story-details > div { display: grid; grid-template-columns: 3rem minmax(0, 1fr); gap: var(--pg-space-2); } - .story-details dt { margin: 0; } - .command-layout { padding: var(--pg-space-5); } - .command-step { min-height: 20rem; } - .archive-index { grid-template-columns: minmax(0, 1fr); } - .timeline { grid-template-columns: minmax(0, 1fr); } - .timeline li { display: grid; grid-template-columns: 4rem minmax(0, 1fr); gap: var(--pg-space-4); } - .timeline-year { font-size: var(--pg-text-xl); } - .band { padding: var(--pg-space-5); } - .product-choices { grid-template-columns: minmax(0, 1fr); margin-top: var(--pg-space-6); } - .document-content .section { padding-block: var(--pg-space-5); } - .archive-hero-grid { grid-template-columns: minmax(0, 1fr); } - .archive-art { display: none; } - .archive-layout .overview-summary .facts { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .archive-layout .timeline { grid-template-columns: minmax(0, 1fr); } - .download-workspace { grid-template-columns: minmax(0, 1fr); } - .download-file-card, .image-file-card, .checksum-panel { padding: var(--pg-space-5); } - .download-file-card .facts { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .metric-exhibits { gap: var(--pg-space-3); } - .metric-exhibit { padding: var(--pg-space-4); border-radius: var(--pg-radius-md); } - .metric-exhibit dd { font-size: var(--pg-text-xl); } - .metric-exhibit:nth-last-child(-n + 2) dd { font-size: var(--pg-text-md); } - .steps { grid-template-columns: minmax(0, 1fr); } - .release-toolbar { align-items: stretch; } - .release-filters { width: 100%; justify-content: space-between; } - .release-filters button { padding-inline: var(--pg-space-3); } - .release-search, .release-search input { width: 100%; min-width: 0; } - .release-card { grid-template-columns: minmax(0, 1fr); padding: var(--pg-space-5); gap: var(--pg-space-5); } - .fact { padding: var(--pg-space-4); } -} -@media (prefers-reduced-motion: reduce), print { - .feature-card:hover, .product-choice:hover { transform: none; } - .document-sidebar { position: static; } +@media print { + .site-header, .copy-btn, .skip-link, .site-footer { display: none; } + .section { padding-block: var(--pg-space-5); } } diff --git a/docs/css/tokens.css b/docs/css/tokens.css index 72679ef..e4ffa0b 100644 --- a/docs/css/tokens.css +++ b/docs/css/tokens.css @@ -2,95 +2,66 @@ GoutouStdio 站点 token(唯一事实来源) 只放值与语义映射,不放组件样式。组件只准引用 --pg-*。 三层:原始值(:root)→ 语义(按用途命名)→ 主题覆写。 + 尺寸一律用 rem,便于跟随用户的字号设置。 + 配色:暖米色底、近黑暖灰字、陶土色点缀。 ============================================================ */ :root { /* ---- 原始色板(主题无关) ---- */ - --pg-ref-indigo-700: #2454c6; - --pg-ref-indigo-600: #1d46ab; - --pg-ref-indigo-200: #9fbeff; - - --pg-ref-ink-950: #0c1424; - --pg-ref-ink-900: #121e32; - - --pg-ref-paper: #ffffff; - --pg-ref-paper-dim: #edf2f8; - --pg-ref-line-light: #dce4ef; - --pg-ref-line-dark: #28374e; - - /* ---- 字体(展示 / 正文 / 等宽,三族封顶) ---- */ - --pg-font-display: "Inter", -apple-system, blinkmacsystemfont, "Segoe UI", - "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif; - --pg-font-sans: -apple-system, blinkmacsystemfont, "Segoe UI", - "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", - "Noto Sans CJK SC", arial, sans-serif; - --pg-font-mono: ui-monospace, sfmono-regular, menlo, consolas, + --pg-ref-ivory: #faf9f5; + --pg-ref-ivory-dark: #f0eee6; + --pg-ref-white: #ffffff; + --pg-ref-slate: #141413; + --pg-ref-clay: #d97757; + --pg-ref-clay-deep: #a64b2a; + --pg-ref-clay-darker: #84391f; + + /* 终端面板:两个主题下固定同一套深色 */ + --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", + "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", + "Noto Sans SC", sans-serif; + --pg-font-mono: ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; /* ---- 字阶与行高 ---- */ - --pg-text-xs: 0.75rem; - --pg-text-sm: 0.875rem; + --pg-text-xs: 0.8125rem; + --pg-text-sm: 0.9375rem; --pg-text-md: 1rem; - --pg-text-lg: 1.125rem; - --pg-text-xl: 1.375rem; - --pg-text-2xl: 1.75rem; - --pg-text-hero: clamp(2.25rem, 4.3vw, 3.85rem); + --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-leading-display: 1.18; + --pg-leading-body: 1.8; --pg-leading-tight: 1.3; - --pg-leading-body: 1.75; - - /* ---- 尺度 ---- */ - --pg-space-1: 4px; - --pg-space-2: 8px; - --pg-space-3: 12px; - --pg-space-4: 16px; - --pg-space-5: 24px; - --pg-space-6: 32px; - --pg-space-7: 48px; - --pg-space-8: 72px; - --pg-space-9: 96px; - --pg-control-h: 44px; - --pg-glow: 0 12px 30px rgb(36 84 198 / 0.18); - - --pg-radius-sm: 8px; - --pg-radius-md: 16px; - --pg-radius-lg: 24px; - --pg-radius-pill: 999px; - - /* 组件固定尺寸(字面值,主题无关) */ - --pg-header-h: 72px; - --pg-icon-btn: 40px; - --pg-brand-mark: 38px; - --pg-blueprint: 48px; - - --pg-width: 78rem; - - --pg-duration: 160ms; - --pg-ease: cubic-bezier(0.16, 1, 0.3, 1); - --pg-reveal-duration: 850ms; - --pg-reveal-delay: 0ms; - --pg-reveal-distance: 40px; - --pg-scroll-progress: 0; - --pg-section-progress: 0; - --pg-stack-tilt-x: 0deg; - --pg-stack-tilt-y: 0deg; - --pg-stack-enter: 1.2s; - --pg-signal-duration: 3s; - --pg-stroke-glow: 0 0 24px rgb(36 84 198 / 0.25); - --pg-shadow-float: 0 24px 60px rgb(8 18 45 / 0.16); - --pg-motion-slow: 500ms; - --pg-backdrop-blur: 18px; - - /* ---- 终端窗:双主题下固定同一套深色 ---- */ - --pg-term-bg: #0c1729; - --pg-term-bar: #122039; - --pg-term-line: #2b3c55; - --pg-term-fg: #e6edf3; - --pg-term-dim: #a1b1c9; - --pg-term-green: #7ee787; - --pg-term-blue: #79c0ff; - --pg-term-btn-line: #607592; + --pg-leading-display: 1.18; + + /* ---- 间距与尺寸 ---- */ + --pg-space-1: 0.25rem; + --pg-space-2: 0.5rem; + --pg-space-3: 0.75rem; + --pg-space-4: 1rem; + --pg-space-5: 1.5rem; + --pg-space-6: 2rem; + --pg-space-7: 3rem; + --pg-space-8: 4.5rem; + --pg-space-9: 6rem; + + --pg-radius: 0.5rem; + --pg-radius-lg: 1rem; + --pg-radius-pill: 6rem; + --pg-header-h: 4.25rem; + --pg-container: 72rem; + --pg-measure: 42rem; } /* ---- 语义层:亮色(默认) ---- */ @@ -98,80 +69,80 @@ body:not(.theme-dark) { color-scheme: light; - --pg-bg-page: #f8fafd; - --pg-bg-raised: var(--pg-ref-paper); - --pg-bg-sunken: var(--pg-ref-paper-dim); - --pg-bg-code: var(--pg-ref-ink-900); - --pg-fg-code-dim: #a2a2ad; + --pg-bg-page: var(--pg-ref-ivory); + --pg-bg-raised: var(--pg-ref-white); + --pg-bg-sunken: var(--pg-ref-ivory-dark); + + --pg-fg-1: var(--pg-ref-slate); + --pg-fg-2: #3d3d3a; + --pg-fg-3: #5e5d59; - --pg-fg-1: #18243b; - --pg-fg-2: #4d5e76; - --pg-fg-3: #5f6e84; - --pg-fg-on-ink: #ffffff; - --pg-fg-on-code: #e8e8ea; + --pg-line-1: #e8e6dc; + --pg-line-2: #d6d3c5; + /* 控件边界:按钮、输入框的描边,3.0+ 对比度。 + line-1/line-2 只做装饰性分隔线,不承载信息。 */ + --pg-line-strong: #8a877d; - --pg-line-1: var(--pg-ref-line-light); - --pg-line-2: #c5d1e1; - /* 控件边界:按钮/窗框/步骤块的描边,3.0+ 对比度。 - line-1/line-2 只做纯装饰分隔线,不承载信息。 */ - --pg-line-strong: #78899f; + --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-ink-950); - --pg-ink-hover: #25354d; + /* 主按钮:深色底、米色字 */ + --pg-ink: var(--pg-ref-slate); + --pg-ink-hover: #3d3d3a; + --pg-on-ink: var(--pg-ref-ivory); - --pg-accent: var(--pg-ref-indigo-700); - --pg-accent-hover: var(--pg-ref-indigo-600); - --pg-accent-soft: #eaf0ff; + /* 首页项目色块:三种扁平底色 */ + --pg-tile-clay: #f4e1d6; + --pg-tile-sky: #dfe8f1; + --pg-tile-sage: #e5e9d9; --pg-ok-fg: #065f46; --pg-ok-bg: #e9f5ef; - --pg-ok-line: #bfe3d2; - --pg-warn-fg: #92400e; - --pg-warn-bg: #fbf3e4; - --pg-warn-line: #ecd3a8; + --pg-ok-line: #9fd2bb; + --pg-warn-fg: #8a3d0c; + --pg-warn-bg: #fbf0dc; + --pg-warn-line: #e0be85; --pg-bad-fg: #991b1b; --pg-bad-bg: #fbecec; - --pg-bad-line: #f0c4c4; - --pg-mute-fg: #4d5e76; - --pg-mute-bg: var(--pg-ref-paper-dim); - --pg-mute-line: var(--pg-ref-line-light); - - --pg-focus: var(--pg-ref-indigo-600); - --pg-shadow: 0 2px 6px rgb(24 43 79 / 0.04), 0 20px 60px rgb(24 43 79 / 0.12); - - /* 反白带:极性翻转,全站只此一处用反色 */ - --pg-band-bg: var(--pg-ref-ink-950); - --pg-band-fg: #f4f4f2; - --pg-band-mute: #a9a9b3; - --pg-band-line: #33333c; + --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); } /* ---- 语义层:暗色 ---- */ body.theme-dark { color-scheme: dark; - --pg-bg-page: var(--pg-ref-ink-950); - --pg-bg-raised: var(--pg-ref-ink-900); - --pg-bg-sunken: #091120; - --pg-bg-code: #0c0c0f; - --pg-fg-code-dim: #95a7c3; + --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-fg-1: #eef3fc; - --pg-fg-2: #b6c5dc; - --pg-fg-3: #95a7c3; - --pg-fg-on-ink: #0c1424; - --pg-fg-on-code: #e8e8ea; + --pg-line-1: #34322f; + --pg-line-2: #45433f; + --pg-line-strong: #7b7970; - --pg-line-1: var(--pg-ref-line-dark); - --pg-line-2: #354862; - --pg-line-strong: #687e9c; + --pg-accent: #e8926f; + --pg-accent-hover: #f1ae92; + --pg-accent-soft: #3a2a22; + --pg-brand: var(--pg-ref-clay); - --pg-ink: #f2f2f5; - --pg-ink-hover: #ffffff; + --pg-ink: #faf9f5; + --pg-ink-hover: #e0ddd2; + --pg-on-ink: #141413; - --pg-accent: var(--pg-ref-indigo-200); - --pg-accent-hover: #dde4ff; - --pg-accent-soft: #172b4c; + --pg-tile-clay: #3a2b24; + --pg-tile-sky: #232c36; + --pg-tile-sage: #272c20; --pg-ok-fg: #7dd3ae; --pg-ok-bg: #10251d; @@ -182,16 +153,10 @@ body.theme-dark { --pg-bad-fg: #f1a3a3; --pg-bad-bg: #2b1517; --pg-bad-line: #5c2629; - --pg-mute-fg: #b6c5dc; - --pg-mute-bg: #17253b; - --pg-mute-line: var(--pg-ref-line-dark); - - --pg-focus: var(--pg-ref-indigo-200); - --pg-shadow: 0 1px 2px rgb(0 0 0 / 0.4), 0 12px 32px rgb(0 0 0 / 0.45); - - /* 暗色主题下使用稍亮的深蓝面,维持阅读对比度。 */ - --pg-band-bg: #172842; - --pg-band-fg: #eef3fc; - --pg-band-mute: #b6c5dc; - --pg-band-line: #354862; + --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); } diff --git a/docs/index.html b/docs/index.html index 538a40f..617c012 100644 --- a/docs/index.html +++ b/docs/index.html @@ -3,293 +3,186 @@ - - GoutouStdio — PySpOS 与 SpaceOS - - - - + + GoutouStdio + + + + + - + + + - - -
-
-
-
- 独立学生技术工作室 · 始于 2022 -

把好奇,
写成操作系统。

-

从 Python 中的进程调度,到 UEFI 上的自研内核。我们是一群学生,把对操作系统的好奇,写成能运行、能阅读、能继续探索的开源项目。

- -
- 02 个活跃项目 - 100% 开放源码 - Python / C 从模拟到内核 -
-
-
-
INSIDE PYSPOS架构示意 / 001
- - -
shell / applications

从一条命令开始。Shell 解析输入,独立应用拥有自己的界面与逻辑。

- 每一层,都有源码可循 -
-
-
- - - -
-
-
- 把原理拆开看 -

一次 Enter,
系统里发生了什么?

-

以外部程序的执行路径为例,
沿着源码,走进系统内部。

- -
-
-
01 / INPUT

先理解,你想做什么。

Shell 将输入解析成命令与参数,再交给对应的处理模块。管道、重定向和后台作业,从这里开始。

src/shell/dispatch.py查看 Shell 模块
-
02 / PROCESS

给程序,一个独立的进程。

外部应用通过 fork / exec 运行。进程有自己的身份与生命周期,调度器用虚拟运行时间和截止期安排执行。

src/forkexec.py · src/process.py了解进程与调度
-
03 / EXECUTION

指令运行,系统开始回应。

ELF 程序由 Unicorn 执行 x86_64 指令;系统调用转发到模拟层。你可以观察执行、修改实现,再运行一次。

Unicorn Engine → syscall探索 ELF 执行引擎
+ +
+
+
+

几个学生写的操作系统。

+

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

+
-
-
-
- Python / 用户态模拟 -

PySpOS.

- -

维护中 v3.2.0 RC1 · MIT

-

阅读项目介绍 ↗下载与更新 ↗

-
-
-

一个可以运行,也可以逐行理解的模拟操作系统。

-

在 Python 中实现进程调度、内存映射、文件系统和引导流程。应用运行在真实子进程中,ELF 由 Unicorn 执行 x86 指令;改动源码后,你可以直接观察系统行为的变化。

-
-
调度
EEVDF 虚拟截止期与 nice 权重
-
进程
真实 fork / exec、作业控制与信号
-
引导
OOBE、A/B 槽位与签名验证
-
-
-
从这里开始 / Python 3.10+
-
$ git clone https://github.com/ViudiraTech/PySpOS.git
$ cd PySpOS
$ pip install -r requirements.txt
$ python launcher.py
-
+
+
+

项目

+
+
+

PySpOS

+

维护中 v3.2.0 RC1 · MIT

+

用 Python 写的模拟操作系统。有进程调度、文件系统和引导流程,能执行 x86_64 的 ELF 程序。

+ +
+
+

SpaceOS 9

+

公开预览 x86_64

+

内核 SpaceKernel 3.0 用 Rust 写成,实现 Linux ABI。自带 PID 1(systemg)和 C 库(splibc),能运行动态链接的 BusyBox。

+ +
+
+

SpaceOS 8

+

抢先体验 x86_64 · Apache-2.0

+

上一代项目。基于 Uinxed 内核(C 写的 UNIX-like 内核),带自研桌面 SPUI 和一批系统程序。

+ +
-
-
-
-
-
- D / SpaceKernel 3.0 -

SpaceOS 9.

- -

公开预览 x86_64 · Linux ABI

-

阅读项目介绍 ↗获取镜像 ↗

-
-
-

从 Rust 内核一直走到能交互的 shell。

-

SpaceKernel 3.0 保留 Linux ABI,加入 FAT32 根盘、PID 1 systemg、splibc 用户态与动态 BusyBox;VFS 使用 Linux 语义的 dentry/inode cache。x86_64 镜像已在 QEMU/UEFI 实跑,aarch64/riscv64 通过编译检查。

-
-
内核
SpaceKernel 3.0(SPK)· Rust · soft-float
-
用户态
splibc、systemg、sh · 动态 musl/BusyBox
-
验证
三架构 cargo check · x86_64 SELFTEST_OK / QEMU 启动
-
-
-
-
- -
-
-
- C / 自研内核与用户态 -

SpaceOS 8.

- -

抢先体验 x86_64 · Apache-2.0

-

阅读项目介绍 ↗获取镜像 ↗

-
-
-

在真实硬件上,让自己写的内核接管系统。

-

基于 Uinxed 自研 UNIX-like 内核,配一整套自研用户态。从窗口合成器到浏览器,一层一层实现。内核已在实体服务器上跑通,OpenJDK 与 DOOM 已能运行,图形与部分 ABI 仍在补齐。

-
-
内核
Uinxed 1.0.0-alpha.1 · 约 18.1 万行
-
用户态
SPUI 桌面、系统程序 · 约 29.2 万行
-
兼容
Linux 系统调用与 X11 兼容层
-
-
+

存档

+
    +
  • + SpaceOS 7 +

    已停更 完全自研的混合内核,2026 年启动,做到约一半。

    +
  • +
  • + SpaceOS 6 +

    已停更 基于 Uinxed-Kernel 的桌面系统,带 SPUI 1.0,2024 年发布。

    +
  • +
-
-
RUNTIME EVIDENCE / 真实运行UINXED × MINECRAFT
- Uinxed 内核下运行 Minecraft,世界渲染与游戏界面正常 -
一次真实的运行记录 Uinxed + Alpine Linux + Weston · QEMU/KVM Q35。窗口、输入与渲染正常。
-
-
-
-
历史项目

走过的路线,
也留在这里。

-
-
SpaceOS 6 2024 · UEFI 的第一次正式尝试已停更
-
SpaceOS 7 2026 · 完全自研内核的探索已停更
-

历史源码与说明继续保留。当前维护的项目是 PySpOS 与 SpaceOS 9;SpaceOS 8 保留为上一代自研内核项目。

+
+
+

运行 PySpOS

+
+

需要 Python 3.10 或更高版本。

+
git clone https://github.com/ViudiraTech/PySpOS.git
+cd PySpOS
+pip install -r requirements.txt
+python launcher.py
+

各平台的要求见配置要求,功能和模块见 PySpOS 介绍。

-
-
-
- 发展历程 -

从 MP4 方案到自研内核

-
-
    -
  1. - 2022 -
    狗头 OS

    工作室创立,从网上狗头 OS 项目起步的“MP4 架构”折腾,攒下第一批关注者。

    -
  2. -
  3. - 2023 -
    互相学习

    开始读别人的代码,过程中和原作者认识,互相学习。

    -
  4. -
  5. - 2024 -
    VimdropOS 与第一版 SpaceOS

    基于 ViudiraTech 的 VimdropOS 做出第一版 SpaceOS,开启新篇章。

    -
  6. -
  7. - 2025 -
    UEFI 与 SpaceOS 6

    深入研究 UEFI,基于 Uinxed-Kernel 研发正式的 SpaceOS。

    -
  8. -
  9. - 2026 -
    SpaceOS 7 自研与 SpaceOS 8

    UEFI 完全搞懂之后走全栈自研;随后把方向收回内核与桌面可用的 SpaceOS 8。

    -
  10. -
+
+
+

历史

+
    +
  • 2022

    工作室成立。从网上的“狗头 OS”项目开始,做“MP4 架构”的实验,有了第一批关注者。

  • +
  • 2023

    开始读别人的代码,认识了原作者,互相交流。

  • +
  • 2024

    基于 ViudiraTech 的 VimdropOS 做出第一版 SpaceOS。

  • +
  • 2025

    深入研究 UEFI,基于 Uinxed-Kernel 开发正式的 SpaceOS。

  • +
  • 2026

    UEFI 研究完成后尝试全部自研(SpaceOS 7),做到一半停下;之后回到内核与桌面,做出 SpaceOS 8。10 月公开 SpaceOS 9 预览,内核换成 Rust 写的 SpaceKernel 3.0。

  • +
-
-
-
- 联系 -

有想法,直接写信

-

合作、报错、投简历式的自我介绍都可以。我们是学生,回得慢,但每封都读。

- goutoustdio@outlook.com - +
+
+

联系

+
+

合作、反馈问题或自我介绍都可以发邮件到 goutoustdio@outlook.com。我们是学生,回复可能比较慢。

+

代码在 GitHub:ViudiraTech,PySpOS 仓库是 ViudiraTech/PySpOS。

-
- + - - + + diff --git a/docs/js/main.js b/docs/js/main.js index da44723..ea8b0e6 100644 --- a/docs/js/main.js +++ b/docs/js/main.js @@ -1,327 +1,121 @@ -/* 站点交互:主题、导航和渐进增强的滚动动效。无 JS 依赖。 */ +/* 站点交互:主题切换、导航菜单、代码块复制。 + 三项都是渐进增强,脚本失效时页面内容与导航仍然可用。 */ (function () { "use strict"; var KEY = "pg-theme"; - var ICON_MOON = - ''; - var ICON_SUN = - ''; function readTheme() { - try { - return localStorage.getItem(KEY); - } catch (e) { - return null; - } + try { return localStorage.getItem(KEY); } catch (e) { return null; } } - function saveTheme(theme) { - try { - localStorage.setItem(KEY, theme); - } catch (e) { - /* 隐私模式下直接忽略 */ - } + try { localStorage.setItem(KEY, theme); } catch (e) { /* 隐私模式下忽略 */ } } - function preferredTheme() { - if (window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches) { - return "light"; - } + if (window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches) return "light"; return "dark"; } - function paintToggle(btn, theme) { - var dark = theme !== "light"; - btn.innerHTML = dark ? ICON_MOON : ICON_SUN; - btn.setAttribute("aria-pressed", String(dark)); - btn.setAttribute("aria-label", dark ? "切换到亮色主题" : "切换到暗色主题"); - } - - function applyTheme(theme, persist) { - var normalized = theme === "light" ? "light" : "dark"; - document.body.classList.toggle("theme-dark", normalized === "dark"); - document.documentElement.dataset.theme = normalized; - var btn = document.getElementById("theme-toggle"); - if (btn) paintToggle(btn, normalized); - if (persist) saveTheme(normalized); - } - function initTheme() { - applyTheme(readTheme() || preferredTheme(), !readTheme()); var btn = document.getElementById("theme-toggle"); + function apply(theme, persist) { + var dark = theme !== "light"; + document.body.classList.toggle("theme-dark", dark); + document.documentElement.dataset.theme = dark ? "dark" : "light"; + if (btn) { + btn.textContent = dark ? "主题:深色" : "主题:浅色"; + btn.setAttribute("aria-label", dark ? "当前为深色主题,点击切换到浅色" : "当前为浅色主题,点击切换到深色"); + } + if (persist) saveTheme(dark ? "dark" : "light"); + } + apply(readTheme() || preferredTheme(), false); if (!btn) return; btn.addEventListener("click", function () { - applyTheme(document.body.classList.contains("theme-dark") ? "light" : "dark", true); + apply(document.body.classList.contains("theme-dark") ? "light" : "dark", true); }); window.addEventListener("storage", function (e) { - if (e.key === KEY || e.key === null) applyTheme(e.newValue || preferredTheme(), false); + if (e.key === KEY) apply(e.newValue || preferredTheme(), false); }); } - function initCopy() { - var status = document.createElement("span"); - status.className = "sr-only"; - status.setAttribute("role", "status"); - document.body.appendChild(status); - document.addEventListener("click", function (e) { - var btn = e.target.closest(".copy-btn"); - if (!btn) return; - var box = btn.dataset.copy && document.getElementById(btn.dataset.copy); - if (!box) return; - var cmds = Array.prototype.map.call( - box.querySelectorAll(".t-cmd"), - function (el) { return el.textContent; } - ).join("\n"); - var done = function (ok) { - btn.textContent = ok ? "已复制" : "复制失败"; - status.textContent = ok ? "命令已复制到剪贴板" : "复制失败,请手动选择命令复制"; - window.setTimeout(function () { btn.textContent = "复制命令"; }, 1600); - }; - if (navigator.clipboard && navigator.clipboard.writeText) { - navigator.clipboard.writeText(cmds).then(function () { done(true); }, function () { done(false); }); - } else { - done(false); - } - }); - } + function initMenu() { + var toggle = document.getElementById("nav-toggle"); + var menu = document.getElementById("site-nav"); + var drops = Array.prototype.slice.call(document.querySelectorAll(".site-nav details.menu")); - function initMotionPreference() { - var actions = document.querySelector(".header-actions"); - if (!actions) return; - var preference = null; - try { preference = localStorage.getItem("pg-motion"); } catch (e) { /* optional storage */ } - var reduced = window.matchMedia("(prefers-reduced-motion: reduce)"); - var button = document.createElement("button"); - button.className = "icon-btn motion-toggle"; - button.type = "button"; - actions.insertBefore(button, document.getElementById("nav-toggle")); - function paint() { - var paused = preference === "off" || reduced.matches; - document.body.classList.toggle("motion-paused", paused); - document.documentElement.dataset.motion = paused ? "off" : "on"; - button.textContent = paused ? "▷" : "Ⅱ"; - button.setAttribute("aria-pressed", String(paused)); - button.setAttribute("aria-label", reduced.matches ? "已按系统设置减少动效" : (paused ? "开启动效" : "减少动效")); - button.title = reduced.matches ? "已遵循系统的减少动态效果设置" : (paused ? "开启动效" : "减少动效"); - button.disabled = reduced.matches; + function closeDrops(except) { + drops.forEach(function (d) { if (d !== except) d.removeAttribute("open"); }); } - button.addEventListener("click", function () { - preference = document.body.classList.contains("motion-paused") ? "on" : "off"; - try { localStorage.setItem("pg-motion", preference); } catch (e) { /* optional storage */ } - paint(); - }); - reduced.addEventListener("change", paint); - paint(); - } - - function initLab() { - var lab = document.querySelector(".architecture-lab"); - if (!lab) return; - var controls = lab.querySelector(".layer-controls"); - var description = lab.querySelector(".lab-description"); - var layers = { - app: ["shell / applications", "从一条命令开始。Shell 解析输入,独立应用拥有自己的界面与逻辑。"], - kernel: ["process.py / EEVDF", "进程、调度、权限与系统调用。沿着 Python 源码,看到系统如何作出决定。"], - runtime: ["Python 3.10+ / host OS", "以 Python 与宿主系统作为运行基础。应用通过真实子进程执行,ELF 指令由执行引擎处理。"] - }; - controls.hidden = false; - controls.addEventListener("click", function (event) { - var button = event.target.closest("[data-layer]"); - if (!button) return; - var key = button.dataset.layer; - controls.querySelectorAll("button").forEach(function (item) { - item.setAttribute("aria-pressed", String(item === button)); - }); - lab.querySelectorAll("[data-plane]").forEach(function (plane) { - plane.dataset.selected = String(plane.dataset.plane === key); - }); - description.querySelector("code").textContent = layers[key][0]; - description.querySelector("p").textContent = layers[key][1]; - if (!document.body.classList.contains("motion-paused") && description.animate) { - description.animate([{ transform: "translateY(8px)" }, { transform: "translateY(0)" }], { duration: 450, easing: "cubic-bezier(0.16, 1, 0.3, 1)" }); - } - }); - var view = lab.querySelector(".stack-view"); - var finePointer = window.matchMedia("(hover: hover) and (pointer: fine)"); - var pointerFrame = null; - var pointerX = 0; - var pointerY = 0; - view.addEventListener("pointermove", function (event) { - if (!finePointer.matches || document.body.classList.contains("motion-paused")) return; - var rect = view.getBoundingClientRect(); - pointerX = ((event.clientX - rect.left) / rect.width - 0.5) * 8; - pointerY = ((event.clientY - rect.top) / rect.height - 0.5) * -6; - if (pointerFrame !== null) return; - pointerFrame = window.requestAnimationFrame(function () { - view.style.setProperty("--pg-stack-tilt-x", pointerY + "deg"); - view.style.setProperty("--pg-stack-tilt-y", pointerX + "deg"); - pointerFrame = null; - }); + // 下拉菜单一次只开一个;点到别处或按 Esc 收起。 + drops.forEach(function (d) { + d.addEventListener("toggle", function () { if (d.open) closeDrops(d); }); }); - view.addEventListener("pointerleave", function () { - if (pointerFrame !== null) window.cancelAnimationFrame(pointerFrame); - pointerFrame = null; - view.style.setProperty("--pg-stack-tilt-x", "0deg"); - view.style.setProperty("--pg-stack-tilt-y", "0deg"); + document.addEventListener("click", function (e) { + drops.forEach(function (d) { if (d.open && !d.contains(e.target)) d.removeAttribute("open"); }); }); - } - - function initNav() { - var toggle = document.getElementById("nav-toggle"); - var menu = document.getElementById("site-nav"); - if (!toggle || !menu) return; function setOpen(open) { + if (!toggle || !menu) return; menu.classList.toggle("open", open); toggle.setAttribute("aria-expanded", String(open)); - toggle.textContent = open ? "✕" : "☰"; } - - toggle.addEventListener("click", function () { - setOpen(!menu.classList.contains("open")); - }); - menu.addEventListener("click", function (e) { - if (e.target.closest("a")) setOpen(false); - }); + if (toggle && menu) { + toggle.addEventListener("click", function () { + setOpen(!menu.classList.contains("open")); + }); + document.addEventListener("click", function (e) { + if (menu.classList.contains("open") && !menu.contains(e.target) && !toggle.contains(e.target)) setOpen(false); + }); + } document.addEventListener("keydown", function (e) { - if (e.key === "Escape") setOpen(false); - }); - document.addEventListener("click", function (e) { - if ( - menu.classList.contains("open") && - !menu.contains(e.target) && - !toggle.contains(e.target) - ) { + if (e.key !== "Escape") return; + closeDrops(null); + if (menu && menu.classList.contains("open")) { setOpen(false); + if (toggle) toggle.focus(); } }); } - function initMotion() { - var reduced = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)"); - var header = document.querySelector(".site-header"); - var progress = document.createElement("div"); - progress.className = "reading-progress"; - progress.setAttribute("aria-hidden", "true"); - document.body.appendChild(progress); - var backTop = document.createElement("a"); - backTop.className = "back-top"; - backTop.href = "#main"; - backTop.textContent = "↑"; - backTop.setAttribute("aria-label", "回到页面顶部"); - document.body.appendChild(backTop); - var story = document.querySelector(".command-story"); - var steps = story ? Array.prototype.slice.call(story.querySelectorAll(".command-step")) : []; - var nodes = story ? story.querySelectorAll(".execution-node") : []; - var activeStep = -1; - var sectionLinks = Array.prototype.slice.call(document.querySelectorAll(".section-jump a[href^='#'], .page-toc a[href^='#']")); - var sectionTargets = sectionLinks.map(function (link) { return document.getElementById(link.hash.slice(1)); }); - var pending = false; + function initCopy() { + var status = document.createElement("span"); + status.className = "sr-only"; + status.setAttribute("role", "status"); + document.body.appendChild(status); - function updateProgress() { - pending = false; - var root = document.documentElement; - var distance = root.scrollHeight - root.clientHeight; - var position = Math.max(0, window.scrollY || root.scrollTop); - progress.style.setProperty("--pg-scroll-progress", distance > 0 ? Math.min(1, position / distance) : 0); - if (header) header.classList.toggle("is-scrolled", position > 16); - backTop.classList.toggle("is-visible", position > root.clientHeight); - var currentSection = -1; - sectionTargets.forEach(function (target, i) { - if (target && target.getBoundingClientRect().top <= root.clientHeight * 0.35) currentSection = i; - }); - sectionLinks.forEach(function (link, i) { - link.classList.toggle("is-current", i === currentSection); - if (i === currentSection) link.setAttribute("aria-current", "location"); - else link.removeAttribute("aria-current"); - }); - if (story) { - var middle = root.clientHeight * 0.52; - var nearest = 0; - var best = Infinity; - var centers = steps.map(function (step, index) { - var rect = step.getBoundingClientRect(); - var center = rect.top + rect.height / 2; - if (Math.abs(center - middle) < best) { best = Math.abs(center - middle); nearest = index; } - return center; - }); - var span = centers[centers.length - 1] - centers[0]; - var completed = span > 0 ? Math.max(0, Math.min(1, (middle - centers[0]) / span)) : 0; - story.style.setProperty("--pg-section-progress", completed); - if (nearest !== activeStep) { - activeStep = nearest; - steps.forEach(function (step, i) { step.classList.toggle("is-active", i === nearest); }); - nodes.forEach(function (node, i) { node.classList.toggle("is-active", i === nearest); }); - story.querySelector(".execution-count").textContent = "0" + (nearest + 1) + " / 03"; - } + // 只给可以直接粘贴执行的命令块加复制按钮;输出示例标成 class="output",不加。 + document.querySelectorAll("main pre:not(.output)").forEach(function (pre) { + var btn = document.createElement("button"); + btn.type = "button"; + btn.className = "copy-btn"; + btn.textContent = "复制"; + pre.appendChild(btn); + }); + + document.addEventListener("click", function (e) { + var btn = e.target.closest(".copy-btn"); + if (!btn) return; + var copy = btn.parentElement.cloneNode(true); + var own = copy.querySelector(".copy-btn"); + if (own) own.remove(); + var text = copy.textContent.replace(/\n$/, ""); + function done(ok) { + btn.textContent = ok ? "已复制" : "复制失败"; + status.textContent = ok ? "已复制到剪贴板" : "复制失败,请手动选择文本"; + window.setTimeout(function () { btn.textContent = "复制"; }, 1600); } - } - function queueProgress() { - if (!pending) { - pending = true; - window.requestAnimationFrame(updateProgress); + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(text).then(function () { done(true); }, function () { done(false); }); + } else { + done(false); } - } - window.addEventListener("scroll", queueProgress, { passive: true }); - window.addEventListener("resize", queueProgress); - window.addEventListener("load", queueProgress); - if (window.ResizeObserver) new ResizeObserver(queueProgress).observe(document.body); - updateProgress(); - - if (!window.IntersectionObserver || (reduced && reduced.matches)) return; - var observer = new IntersectionObserver(function (entries) { - entries.forEach(function (entry) { - if (!entry.isIntersecting) return; - entry.target.classList.add("is-revealed"); - observer.unobserve(entry.target); - }); - }, { threshold: 0, rootMargin: "0px 0px -32px 0px" }); - var targets = document.querySelectorAll( - ".page-head-grid > div, .section-head, .story-heading, .story-copy, .system-layer, " + - ".desktop-evidence, .feature-grid > .feature-card, .timeline > li, .archive-index > div, .fact, .command-step" - ); - targets.forEach(function (el) { - // 首屏和锚点目标即时可读;不通过透明度隐藏正文。 - var rect = el.getBoundingClientRect(); - el.classList.add("scroll-reveal"); - var siblings = Array.prototype.filter.call(el.parentElement.children, function (child) { - return child.tagName === el.tagName; - }); - el.style.setProperty("--pg-reveal-delay", (siblings.indexOf(el) % 4) * 60 + "ms"); - if (rect.top < window.innerHeight && rect.bottom > 0) el.classList.add("is-revealed"); - else observer.observe(el); - }); - function revealFocus(event) { - var el = event.target.closest(".scroll-reveal"); - if (el) { el.classList.add("is-revealed"); observer.unobserve(el); } - } - document.addEventListener("focusin", revealFocus); - function revealHash() { - var target = document.getElementById(window.location.hash.slice(1)); - if (!target) return; - var el = target.closest(".scroll-reveal"); - if (el) { el.classList.add("is-revealed"); observer.unobserve(el); } - } - window.addEventListener("hashchange", revealHash); - revealHash(); - if (reduced) reduced.addEventListener("change", function () { - if (!reduced.matches) return; - observer.disconnect(); - targets.forEach(function (el) { el.classList.add("is-revealed"); }); }); } function init() { initTheme(); - initNav(); + initMenu(); initCopy(); - initMotionPreference(); - initLab(); - initMotion(); } 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 0f50604..d96046b 100644 --- a/docs/js/theme-init.js +++ b/docs/js/theme-init.js @@ -1,13 +1,13 @@ -/* 主题预初始化:在正文渲染前确定主题,避免闪烁。 - 约定:body.theme-dark = 暗色(默认),无类名 = 亮色。 */ +/* 主题预初始化:放在 的第一行,正文绘制前就定好主题,避免闪一下。 + 约定:body.theme-dark = 暗色,无类名 = 亮色。 + 没有保存过选择时跟随系统;系统也没给出偏好时用暗色。 + 同时给 打上 js 标记,窄屏侧栏据此才收成抽屉;没有脚本时导航照常显示。 */ (function () { var KEY = "pg-theme"; + var root = document.documentElement; var saved = null; - try { - saved = localStorage.getItem(KEY); - } catch (e) { - saved = null; - } + try { saved = localStorage.getItem(KEY); } catch (e) { saved = null; } + var theme; if (saved === "light" || saved === "dark") { theme = saved; @@ -16,18 +16,10 @@ } else { theme = "dark"; } - document.documentElement.dataset.theme = theme; - var savedMotion = null; - try { savedMotion = localStorage.getItem("pg-motion"); } catch (e) { /* optional storage */ } - var reduced = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches; - document.documentElement.dataset.motion = reduced || savedMotion === "off" ? "off" : "on"; - var apply = function () { - document.body.classList.toggle("theme-dark", theme === "dark"); - document.body.classList.toggle("motion-paused", document.documentElement.dataset.motion === "off"); - }; - if (document.body) { - apply(); - } else { - document.addEventListener("DOMContentLoaded", apply); - } + + root.classList.add("js"); + root.dataset.theme = theme; + var apply = function () { document.body.classList.toggle("theme-dark", theme === "dark"); }; + if (document.body) apply(); + else document.addEventListener("DOMContentLoaded", apply); })(); diff --git a/docs/ota/index.html b/docs/ota/index.html index c1ce9c4..103ec42 100644 --- a/docs/ota/index.html +++ b/docs/ota/index.html @@ -3,170 +3,181 @@ - - OTA 更新下载 — PySpOS | GoutouStdio - - - - + + PySpOS 更新包 | GoutouStdio + + + + + - + + + - - -
-
- PySpOS · OTA 更新 -

下一次启动,
从这里开始。

-

获取最新的 PySpOS 系统更新包,查看版本变化与安装要求。支持云端更新、本地安装及 A/B 槽位回滚。

-
- - 版本历史 -
- -
- - +
+
-
-
-
- 更新日志 -

这次改了什么

-
-
-

apps 升级为真子进程 fork/exec;11 步 OOBE 首次开机向导;EEVDF 调度器对标 Linux 6.6+;终端输入自愈;SpaceConfig v2 与命令注册表。完整条目见版本历史。

+
+
+

最新版本 候选发布版

+
+
+ + + + + + + +
版本3.2.0
发布日期2026-09-24
文件大小245.1 KB
SHA-256d88f7969a6985a447d8d19760bf9a6a807df98878eb75e887cb45ad41cd1bbc8
+
+

版本信息以 version.json 为准。安装前请核对 SHA-256,对不上就不要继续。

+
-
-
-
- 兼容性要求 -

装之前确认

+
+
+

更新内容

+
+
    +
  • apps 升级为真子进程 fork/exec,PID 0/1/2 引导与孤儿收养
  • +
  • 新增 11 步 OOBE 首次开机向导,真实时区(zoneinfo + TZ)与语言
  • +
  • EEVDF 调度器对标 Linux 6.6+,含 nice 权重表与信号状态机
  • +
  • 终端输入自愈:stty sane 恢复 + 输入归一化,修复按 Enter 刷 ^M
  • +
  • 修复子进程 stdio 中继协议不匹配导致输出丢失、后台作业挂死
  • +
  • 新增 SpaceConfig v2、命令注册表与 Tab 补全、真出厂重置
  • +
  • main.py 拆分为 src/shell/*,保留兼容 facade
  • +
  • 更新包补齐 requirements.txt / LICENSE / README.md
  • +
  • CI 改为分层节奏:日常单格,发版才跑全平台矩阵
  • +
-
    -
  • Python

    3.10 或更高版本(云端抓取需要 requests 库)。

  • -
  • 系统

    Windows 7 及以上 / Linux / macOS。

  • -
  • 磁盘

    至少 50 MB 可用空间。

  • -
-
-
-
- 回滚指南 -

更新出问题时回滚

-

回滚会切换到另一个槽位,请确保另一个槽位有有效系统版本,否则可能无法启动。

+
+
+

安装前确认

+
+ + + + + + +
Python3.10 或更高版本。云端抓取需要 requests 库。
系统Windows 7 及以上 / Linux / macOS。
磁盘至少 50 MB 可用空间。
-
    -
  1. 在 PySpOS 中输入 recovery 进入恢复模式
  2. -
  3. 在恢复模式中输入 ota_rollback
  4. -
  5. 确认回滚操作
  6. -
  7. 重启 PySpOS 以应用更改
  8. -
-
-
-
- 下载 -

拿去装吧

-

更新包与校验值以 version.json 为准,装完对不上 SHA-256 就别继续。

- +
+
+

更新出问题时回滚

+
+

回滚会切换到另一个槽位。请确认另一个槽位里有可用的系统版本,否则可能无法启动。

+
    +
  1. 在 PySpOS 中输入 recovery,进入恢复模式。
  2. +
  3. 在恢复模式中输入 ota_rollback。
  4. +
  5. 确认回滚操作。
  6. +
  7. 重启 PySpOS,使更改生效。
  8. +
-
- + - + + - diff --git a/docs/ota/releases.html b/docs/ota/releases.html index 9cbef52..39fd8a5 100644 --- a/docs/ota/releases.html +++ b/docs/ota/releases.html @@ -3,180 +3,210 @@ - - 版本历史 — PySpOS | GoutouStdio - - - - + + PySpOS 版本历史 | GoutouStdio + + + + + - + + + - - -
-
- PySpOS · 版本历史 -

所有历史版本。

-

更新内容、下载链接与 SHA-256 校验值。数据来自 version.json,取不到时用本页内置的静态数据兜底。

-
- 下载最新版 - 了解 PySpOS + +
+
+
+

PySpOS 版本历史

+

所有历史版本的更新内容、下载链接和 SHA-256。数据来自 version.json,读不到时使用页面里内置的数据。

+
-
+ -
-
-
- 历史版本 -

按时间倒序

+
+
+ +

+
+

正在读取版本数据…

-

-
    -
  • -

    正在读取版本数据…

    -
  • -
-
- + - + + - diff --git a/docs/pyspos.html b/docs/pyspos.html index 6615211..361ecbd 100644 --- a/docs/pyspos.html +++ b/docs/pyspos.html @@ -3,276 +3,226 @@ - - PySpOS — Python 模拟操作系统 | GoutouStdio - - - - + + PySpOS | GoutouStdio + + + + + - + + + - - -
-
-
+ +
+
+
- PySpOS · Python 模拟操作系统 -

读得懂的内核,
跑得起来的系统。

-

用 Python 实现进程调度、文件系统与引导流程。运行命令、执行 ELF、修改源码,让操作系统的每个决定都看得见。

+

PySpOS

+

用 Python 写的模拟操作系统。它跑在宿主系统上,自己实现了 shell、进程调度、文件系统、引导流程和 OTA 更新,也能执行 x86_64 的 ELF 程序。

-
- v3.2.0 RC1 - Python 3.10+ - MIT -
-
-
-
SHELL SESSION实际输出节选
-
-
- pyspos — help 与 ps - -
-
-
└─$help
-
PySpOS shell 命令(按分组;help <命令> 查看详情)
-
-
[系统]
-
clear 清屏
-
echo 打印字符串(支持 > 与 >>)
-
help 显示帮助(help <命令> 看详情)
-
…(共 9 组:系统 / 文件 / 进程与作业 / 程序与执行 / 配置与空间 / 更新与恢复 / 首次开机 / 内置应用 / 用户包)
-
└─$ps
-
PID PPID STATE KIND CMD
-
0 0 Sleeping idle swapper/0
-
1 0 Sleeping init init
-
2 1 Running shell pyspos-sh
-
└─$
+ 下载 3.2.0 + 查看源码
+
+
当前版本
v3.2.0 RC1
+
运行环境
Python 3.10+
+
许可
MIT
+
-

格式与 src/commands.py、src/shell/proc_cmds.py、src/process.py 的实际输出一致;PID 0/1/2 为保留进程。

-
+
+
└─$ help
+PySpOS shell 命令(按分组;help <命令> 查看详情)
+
+[系统]
+  clear            清屏
+  echo             打印字符串(支持 > 与 >>)
+  help             显示帮助(help <命令> 看详情)
+  …(共 9 组)
+└─$ ps
+  PID   PPID  STATE     KIND    CMD
+    0      0  Sleeping  idle    swapper/0
+    1      0  Sleeping  init    init
+    2      1  Running   shell   pyspos-sh
+
shell 中 help 与 ps 的输出(节选)。格式取自 src/commands.py、src/shell/proc_cmds.py 和 src/process.py;PID 0、1、2 是保留进程。
+
- -
+ -
-
-
- 介绍 -

从 2022 年的折腾到 v3.2.0

+
+
+

概览

+
+

PySpOS 从 2022 年的“MP4 架构狗头 OS”一路改过来。现在的版本能跑命令、管理文件、执行 ELF 程序;应用是独立进程,调度器参照 Linux 的 EEVDF。

+

3.2.0 重构了内部结构:原先 1217 行的 main.py 拆成 src/shell/ 下的 sys、elf、ota、proc、dispatch 几个模块,原文件留作兼容层。调度在 process.py,进程引擎在 forkexec.py。

+

ELF 默认由 Unicorn Engine 执行真实的 x86_64 指令,系统调用转发给内置的模拟层。没装 unicorn 时退回自研的 SpaceCPU 1 Pro 模拟器,Windows 上也能跑。动态链接(INTERP)和 TLS 还没有实现。

+
+ + + + + + + + + +
当前版本v3.2.0 RC1
项目启动2022
核心语言Python(另有部分 C 测试程序)
升级方式A/B 槽位,可在本地回滚
程序格式ELF(x86_64)、SPF 脚本
测试按子系统分组,在 GitHub Actions 上运行
+
-
-

PySpOS 从 2022 年的“MP4 架构狗头 OS”一路迭代到现在:能跑命令、管文件、执行 ELF 程序,应用是独立进程,调度器对标 Linux。3.2.0 是一次内核级重构——1217 行的 main.py 拆成 src/shell/* 包(sys / elf / ota / proc / dispatch),原文件保留兼容 facade;调度在 process.py,进程引擎在 forkexec.py。

-

A/B 双槽位升级:更新装到非活动槽位,校验通过后切换,失败可回滚到上一个版本。命令走元数据注册表,提供分组 help、help <cmd>、Tab 补全,支持管道与重定向;PATH=apps/ 下的外部程序可直接执行,cmd & 后台作业配 jobs / fg / bg / wait。

-

ELF 执行默认由 Unicorn Engine 跑真实 x86_64 指令,syscall 转发到内置模拟层;缺 unicorn 时降级到自研 SpaceCPU 1 Pro 模拟器,Windows 也能跑。动态链接(INTERP)与 TLS 还在路线图上。

-
-
-
-
- v3.2.0 -

核心特性

-

应用从协作式模拟变成真子进程,调度器对标 Linux 6.6+,并第一次带上完整的首次开机向导。

-
-
    -
  • -
    真子进程
    -

    应用不再是模拟,而是独立 OS 进程。PID 0 swapper、1 init、2 shell 三级引导,孤儿被 init 收养,退出后回收为僵尸再清理。

    -
  • -
  • -
    应用可自绘的窗口 API
    -

    VortexGlass 负责窗口与输入,应用可用 Python SDK 管理多窗口、处理键盘鼠标事件,绘制控件或提交自己的 RGBA 像素画面。记事本、文件浏览器与绘图程序各自持有界面和逻辑,API 与独立应用示例可供继续开发。

    -
  • -
  • -
    EEVDF 调度
    -

    Earliest Eligible Virtual Deadline First,对标 Linux 6.6+:nice 权重表、vruntime 推进、虚拟截止期与信号状态机。

    -
  • -
  • -
    Unicorn ELF 引擎
    -

    默认执行真实 x86_64 指令,syscall 转发到模拟层;没装 unicorn 就降级到自研 SpaceCPU 1 Pro。支持 --stats / --map / --disasm / --strace 观测。

    -
  • -
  • -
    OOBE 首次开机向导
    -

    11 步 curses 向导,语言与时区真实生效(zoneinfo + TZ),完成标记写入 etc/.oobe_done,可随时用 oobe 重跑。

    -
  • -
  • -
    终端可靠性
    -

    TUI 按显示宽度排版(CJK 双宽),剥离完整 ANSI 转义序列。stty sane 自愈 + 输入归一化 + 重试上限,按 Enter 刷 ^M 的问题不再出现。

    -
  • -
  • -
    后台作业
    -

    cmd &、jobs / fg / bg / wait、$!、进程组与会话。前台交互经管道中继,后台作业 stdin 接 /dev/null。

    -
  • -
  • -
    SpaceConfig v2
    -

    .spc 格式:转义、行尾注释、null、列表类型,外加 Schema 校验与 spc_get / set / migrate 工具链。

    -
  • -
  • -
    命令注册表
    -

    元数据驱动的分组 help、help <cmd>、Tab 补全,以及 PATH=apps/ 外部命令解析。

    -
  • -
  • -
    A/B 槽位与回滚
    -

    更新装到非活动槽位,校验通过后切换,失败可回滚。OTA 云端已恢复(goutoustdio.rainyland.top),也可在 Recovery 里从云端选版本装到指定槽位。

    -
  • -
  • -
    真出厂重置
    -

    recovery.erase 清理 8 类数据,含题库历史、shell 历史与全树 __pycache__。

    -
  • -
  • -
    SPF 脚本
    -

    SPF(Space Portable Format)自研脚本格式,支持变量、算术、输入与引入。

    -
  • -
  • -
    MIT 开源
    -

    3.2.0 起采用 MIT 协议。代码在 ViudiraTech/PySpOS,自动化回归测试由 GitHub Actions 把关。

    -
  • +
    +
    +

    3.2.0 的改动

    +
      +
    • 进程

      应用是独立的 OS 进程,不再是协作式模拟。PID 0(swapper)、1(init)、2(shell)依次引导;孤儿进程由 init 收养,退出后先成为僵尸,再被回收。

    • +
    • 调度

      EEVDF(Earliest Eligible Virtual Deadline First),参照 Linux 6.6+:nice 权重表、vruntime、虚拟截止期和信号状态机。

    • +
    • 后台作业

      cmd &、jobs、fg、bg、wait、$!,以及进程组与会话。前台交互经管道中继,后台作业的 stdin 接 /dev/null。

    • +
    • ELF 引擎

      默认用 Unicorn,没装就用 SpaceCPU 1 Pro。可以加 --stats、--map、--disasm、--strace 观察执行过程。

    • +
    • 窗口 API

      VortexGlass 管理窗口和输入。应用可以用 Python SDK 开多个窗口、处理键盘鼠标事件、绘制控件,或直接提交 RGBA 像素。记事本、文件浏览器和绘图程序各自持有界面和逻辑。API 与示例见 vortexglass.md。

    • +
    • 首次开机向导

      共 11 步的 curses 向导。语言和时区设置会实际生效(zoneinfo + TZ),完成标记写在 etc/.oobe_done,可以用 oobe 命令重跑。

    • +
    • 终端

      TUI 按显示宽度排版(CJK 双宽),并去掉完整的 ANSI 转义序列。输入侧有 stty sane 恢复、输入归一化和重试上限,按 Enter 出现 ^M 的问题已修复。

    • +
    • 命令

      命令走元数据注册表:分组 help、help <cmd>、Tab 补全,支持管道和重定向。PATH=apps/ 下的外部程序可以直接执行。

    • +
    • SpaceConfig v2

      .spc 配置格式,支持转义、行尾注释、null 和列表,带 Schema 校验,以及 spc_get / set / migrate 工具。

    • +
    • A/B 槽位与回滚

      更新装进非活动槽位,校验通过后再切换,失败可以回滚。OTA 云端已恢复(goutoustdio.rainyland.top),在 Recovery 里也可以从云端选版本装到指定槽位。

    • +
    • 出厂重置

      recovery.erase 清理 8 类数据,包括题库历史、shell 历史和整棵目录树的 __pycache__。

    • +
    • SPF 脚本

      SPF(Space Portable Format)是自研的脚本格式,支持变量、算术、输入和引入。

    • +
    • 许可与测试

      3.2.0 起使用 MIT 许可,代码在 ViudiraTech/PySpOS。回归测试在 GitHub Actions 上运行。

    -
    -
    +
    +
    - 技术细节 -

    模块速览

    -

    每个模块一句话加一个入口,完整说明在仓库 README 与源码注释里。

    +

    模块

    +

    每个模块只列一行,完整说明在仓库 README 和源码注释里。

    +
    +
    +
    + + + + + + + + + + + + +
    模块文件作用
    主循环src/kernel.py处理用户输入、执行命令、管理应用运行,启动时初始化各子系统。入口是 kernel.loop()。
    SPF 解析src/parse_spf.py解析并执行 SPF 文件,支持 putchar() 与 exit() 系统调用。
    OTA 更新src/ota.py版本检查、下载、验证、安装,云端地址是 goutoustdio.rainyland.top。
    文件系统src/fs.pylist / create / read / delete 等文件操作 API,基于 os 和 shutil 实现。
    配置管理src/btcfg.py管理启动配置(ROOT 权限、启动器锁定状态),存放在 etc/bootcfg.json。
    ELF 运行src/elf_loader/ELF 加载、CPU 模拟和系统调用模拟,支持 x86 / x86_64。用法:run program.elf,可加 --stats / --map / --strace。
    +
    -
      -
    • -
      主循环 src/kernel.py
      -

      处理用户输入、执行命令、管理应用运行,启动时初始化各子系统。

      kernel.loop()  # 主循环
      -
    • -
    • -
      SPF 解析 src/parse_spf.py
      -

      SPF 文件格式的解析与执行,支持 putchar() 与 exit() 系统调用。

      putchar("Hello");  exit(0);
      -
    • -
    • -
      OTA 更新 src/ota.py
      -

      版本检查、下载、验证、安装,云端地址 goutoustdio.rainyland.top。

      compare_versions(v1, v2)  # 版本比较
      -
    • -
    • -
      文件系统 src/fs.py
      -

      list / create / read / delete 等文件操作 API,基于 os 与 shutil 实现。

      fs.create_file("test.txt", "content")
      -
    • -
    • -
      配置管理 src/btcfg.py
      -

      管理启动配置(ROOT 权限、启动器锁定状态),存放在 etc/bootcfg.json。

      btcfg.load_bootcfg()  # 加载配置
      -
    • -
    • -
      ELF 运行 src/elf_loader/
      -

      整合 ELF 加载、CPU 模拟与系统调用模拟。支持 x86 / x86_64,模拟 Linux 系统调用。

      run program.elf  # 附 --stats / --map / --strace
      -
    • -
    -
    -
    -
    - 兼容性 -

    能跑 Python 就能跑

    -
    -
    -
    Windows
    Windows 7 及以上,兼容性最好
    -
    Linux
    主流发行版(Ubuntu / Debian / Fedora 等)
    -
    macOS
    macOS 10.14 及以上
    -
    Python
    3.10+(CI 覆盖 3.10 / 3.12 / 3.14)
    -
    -
    - 前往下载 - 完整配置要求 +
    +
    +

    兼容性

    +
    +
    + + + + + + + +
    WindowsWindows 7 及以上,兼容性最好
    Linux主流发行版(Ubuntu、Debian、Fedora 等)
    macOSmacOS 10.14 及以上
    Python3.10 及以上;CI 覆盖 3.10、3.12、3.14
    +
    +
    -
- + - - + + diff --git a/docs/requirements.html b/docs/requirements.html index d883d5d..7b154c8 100644 --- a/docs/requirements.html +++ b/docs/requirements.html @@ -3,230 +3,196 @@ - + 配置要求 | GoutouStdio - - - - + + + + + - + + + - + +
+
+
+

配置要求

+

PySpOS 是跑在宿主系统上的 Python 程序;SpaceOS 8 和 9 是要自己启动的内核。条件差别大,所以按产品分开写。

+
+
-
-
- 配置要求 -

跑起来需要什么。

-

PySpOS 是跑在宿主系统上的 Python 程序,SpaceOS 8 是要独占机器的真内核——条件差别很大,所以按产品分开列,不做一张通用表。

- -
- -
-
-
- PySpOS 3.2.0 -

模拟操作系统:能跑 Python 就行

+
+
+

PySpOS 3.2.0

+
+

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

+
+ + + + + + + + + +
Python3.10 或更高。CI 覆盖 3.10、3.12、3.14。
系统Windows 7 及以上、Linux 各发行版、macOS。
磁盘约 120 MB。不需要管理员权限。
CPU / 内存双核 CPU、512 MB 内存足够,不需要独立显卡。
终端curses 的首次开机向导需要 80×24 字符的终端。SSH、tmux、Windows Terminal 都可以。
可选依赖unicorn(ELF 引擎,缺失时退回自研模拟器);tzdata(真实时区,Windows 和精简容器镜像需要)。都不是硬性要求。
+
+

PySpOS 介绍 · 下载更新包

-
    -
  • -
    软件要求 维护中
    -
    -

    Python 3.10 或更高(CI 覆盖 3.10 / 3.12 / 3.14)。系统:Windows 7+、Linux 各发行版、macOS。磁盘约 120 MB。无需管理员权限。

    -

    可选依赖:unicorn(ELF 引擎,缺失自动降级到自研模拟器)、tzdata(真实时区,Windows 与精简容器镜像需要)。都不是硬性要求。

    -

    PySpOS 详情 → · 下载

    -
    -
  • -
  • -
    硬件要求 纯用户态,没有硬性门槛
    -
    -

    不直接操作硬件,能跑 Python 的机器就能跑:双核 CPU、512 MB 内存足够,无需独立显卡。

    -

    唯一与显示相关的是终端:curses 的 OOBE 向导需要一个 80×24 字符的终端。SSH、tmux、Windows Terminal 都可以。

    -
    -
  • -
-
-
-
- SpaceOS 8 -

真实内核:这是硬要求

-

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

-
-
- -
-

请用可抛弃的机器与测试盘

-
-

NTFS 写入驱动目前尚未被认为可以信任。在确认稳定之前,不要让它碰重要数据。已在 Dell PowerEdge R410 等实体服务器上验证。

-
+
+
+

SpaceOS 9 公开预览

+
+

目前只在 QEMU 上验证过。

+
+ + + + + + + + +
虚拟机x86_64 QEMU,推荐 Q35 机型和 VirtIO GPU 2D。
固件OVMF UEFI 固件。
内存至少 512 MiB。
未验证真实硬件、真实 GPU、音频、网络、睡眠唤醒。
其他架构aarch64 / riscv64 只通过编译检查,镜像里没有它们的用户态。
+

SpaceOS 9 介绍 · QEMU 启动命令

-
    -
  • -
    处理器 x86_64 必需 · 已验证
    -

    多核可用,SMP 启动会拉起全部核心,并在核间做任务迁移与负载均衡。

    -
  • -
  • -
    内存 最低 128 MB · 512 MB 可启动
    -

    要跑图形桌面并使用 swap,建议 1 GB 以上。

    -
  • -
  • -
    固件 UEFI 已验证 · Legacy 支持
    -

    经 Limine 引导,UEFI 与 Legacy 两种模式都支持,不挑固件实现。

    -
  • -
  • -
    输入 xHCI USB HID 已验证
    -

    PS/2 键盘与鼠标为主路径,提供 Linux 兼容的 evdev;另支持 USB HID(键盘、鼠标、消费类控制器)。PS/2 路径仍在验证中。

    -
  • -
  • -
    显示 真实 GPU 驱动未完成
    -

    DRM/KMS 内核核心,通用 GPU 驱动注册表,内置 VirtIO-GPU 驱动;GOP 帧缓冲控制台(带点阵字体)与软件帧缓冲兜底。未接显卡时可用 GOP 文本控制台。

    -
  • -
  • -
    存储 NTFS 写入不建议用
    -

    IDE/ATA、AHCI(SATA)、NVMe、USB Mass Storage 均有驱动(NVMe 已启用)。默认根文件系统是 tmpfs。

    -
  • -
  • -
    网络 需要指定网卡
    -

    需要 Intel e1000 / e1000e 或 Realtek RTL8139 / RTL8169。协议栈自研,客户端带 DHCP。非该网卡可能无法联网。

    -
  • -
  • -
    音频(可选)SB16 默认关闭
    -

    Sound Blaster 16 与 Intel HD Audio,带 ALSA 兼容的 PCM / control ABI。SB16 需 menuconfig 打开。

    -
  • -
+
+
-
- 构建 -

工具链与默认关闭的功能

-
-
    -
  • -
    构建工具链
    -
    -

    必需:make、gcc(建议 13.3+)、qemu、xorriso。建议:clang-format、clang-tidy。仅 menuconfig 需要 kconfig-frontends 与 libncurses-dev。

    -
    sudo apt install build-essential qemu-system-x86 xorriso libncurses-dev
    -
    -
  • -
  • -
    默认关闭的功能
    -
    -

    有些子系统在 Kconfig 里默认是 n,想用得自己打开:VIRTIO / VIRTIO_GPU(虚拟化设备)、SOUND_SB16(声卡)。用 make menuconfig 打开,并确认虚拟机或实体机上确实有对应设备。新增 GPU 驱动是在 drivers/gpu/gpu_drivers.c 的注册表里加一条记录。

    -
    -
  • -
-
- SpaceOS 8 详情 - 内核源码与构建说明 +
+
+

SpaceOS 8

+
+
+

请用可抛弃的机器和测试盘

+

NTFS 写入驱动目前还不能信任,确认稳定前不要让它碰重要数据。已在 Dell PowerEdge R410 等实体服务器上验证。

+
+

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

+
+ + + + + + + + + + + + + + +
硬件要求状态
处理器x86_64 必需。多核可用,SMP 启动会拉起全部核心,并在核间迁移任务、均衡负载。已验证
内存最低 128 MB,512 MB 可启动。跑图形桌面并使用 swap,建议 1 GB 以上。
固件经 Limine 引导,UEFI 与 Legacy 两种模式都支持,不挑固件实现。UEFI 已验证
输入PS/2 键盘和鼠标是主路径,提供 Linux 兼容的 evdev;另支持 USB HID(键盘、鼠标、消费类控制器)。PS/2 路径仍在验证中。xHCI USB HID 已验证
显示DRM/KMS 内核核心、通用 GPU 驱动注册表、内置 VirtIO-GPU 驱动;GOP 帧缓冲控制台(点阵字体)和软件帧缓冲兜底。没接显卡时可用 GOP 文本控制台。真实 GPU 驱动未完成
存储IDE/ATA、AHCI(SATA)、NVMe、USB Mass Storage 都有驱动(NVMe 已启用)。默认根文件系统是 tmpfs。NTFS 写入不建议用
网络需要 Intel e1000 / e1000e 或 Realtek RTL8139 / RTL8169。协议栈自研,客户端带 DHCP。其他网卡可能无法联网。需要指定网卡
音频(可选)Sound Blaster 16 与 Intel HD Audio,带 ALSA 兼容的 PCM / control ABI。SB16 需要在 menuconfig 里打开。SB16 默认关闭
+
+

构建

+

必需:make、gcc(建议 13.3+)、qemu、xorriso。建议:clang-format、clang-tidy。只有 menuconfig 需要 kconfig-frontends 和 libncurses-dev。

+
sudo apt install build-essential qemu-system-x86 xorriso libncurses-dev
+

默认关闭的功能

+

有些子系统在 Kconfig 里默认是 n,要用得自己打开:VIRTIO / VIRTIO_GPU(虚拟化设备)、SOUND_SB16(声卡)。用 make menuconfig 打开,并确认虚拟机或实体机上有对应设备。新增 GPU 驱动,是在 drivers/gpu/gpu_drivers.c 的注册表里加一条记录。

+

SpaceOS 8 介绍 · 内核源码与构建说明

-
-
-
- 已停更 -

SpaceOS 6 / 7

+
+
+

SpaceOS 6 / 7

+
+

这两个项目已停止开发,这里不再列硬件要求。页面仅作历史存档,配置和构建说明以当时的仓库记录为准。

+

SpaceOS 7 存档 · SpaceOS 6 存档

-
- -
-

这两条路线已停止开发

-
-

原先的通用配置表对它们已经不适用,本页不再列硬件要求。两个页面仅作历史存档,配置与构建说明以当时的仓库记录为准。

-
-
-
-
- -
- + - - + + diff --git a/docs/spaceos6.html b/docs/spaceos6.html index e3b94fd..adc75b4 100644 --- a/docs/spaceos6.html +++ b/docs/spaceos6.html @@ -3,201 +3,162 @@ - - SpaceOS 6(存档)| GoutouStdio - - - - + + SpaceOS 6(已停更) | GoutouStdio + + + + + - + + + - - -
-
- 存档 · 已停止开发 -

SpaceOS 6

-

基于 Uinxed-Kernel 的桌面操作系统:SPUI 1.0 界面、SPF Parser 2.0、SPFS 2.0 文件系统。这是我们第一次真正碰 UEFI 的路线。

-
- 6.0 - 首次发布 · 2024 - x86 + +
+
+
+

已停更

+

SpaceOS 6

+

基于 Uinxed-Kernel 的桌面操作系统:SPUI 1.0 界面、SPF Parser 2.0、SPFS 2.0 文件系统。这是团队第一次接触 UEFI。

+
+
版本
6.0
+
首次发布
2024
+
桌面环境
SPUI 1.0
+
文件系统
SPFS 2.0
+
架构
x86
+
+
-
- -
-
+
+
- -
-

该路线已停止开发,本页仅作历史存档

-
-

不再维护,不再接受 issue、功能请求或移植。请勿基于它继续投入。当前唯一在维护的产物是 PySpOS,代码已迁移至 ViudiraTech/PySpOS。

-
-
+

这个项目已停止开发,本页仅作存档

+

不再维护,不接受 issue、功能请求或移植,请不要基于它继续投入。目前在维护的是 PySpOS 和 SpaceOS 9;PySpOS 的代码在 ViudiraTech/PySpOS。

+
+
-
- 介绍 -

基于 Uinxed-Kernel 的定制桌面系统

+
+
+

背景

+
+

SpaceOS 6 基于 ViudiraTech 开发的 Uinxed-Kernel 深度定制,2024 年开始研发,内核优化、用户界面、文件系统和硬件驱动都是自己做的。

+

Uinxed-Kernel 是 64 位宏内核,运行在 CPU Ring 0,进程调度、内存管理、文件系统和设备驱动都在内核空间。SPUI(SpaceOS User Interface)1.0 是自研的桌面环境。硬件上支持 AHCI / NVMe 存储、SB16 声卡、JPG 硬件加速和分区表,可以多系统部署。

-
-

SpaceOS 6 是基于 Uinxed-Kernel(ViudiraTech 开发)深度定制的桌面操作系统。2024 年开始研发,内核优化、用户界面、文件系统、硬件驱动都是自己做的。

-

Uinxed-Kernel 是 64 位宏内核,运行在 CPU Ring 0,提供进程调度、内存管理、文件系统与设备驱动,所有核心组件都在内核空间。SPUI(SpaceOS User Interface)1.0 是自研桌面环境,界面简洁,适合日常办公和娱乐。硬件上支持 AHCI / NVMe 存储、SB16 声卡、JPG 硬件加速与分区表,可多系统部署。

-
-
-
-
- 特性 -

当时做到的部分

-
-
    -
  • -
    SPUI 1.0 桌面
    -

    自研桌面环境,支持窗口管理、任务栏与开始菜单。

    -
  • -
  • -
    SPF Parser 2.0
    -

    SPF(SpaceOS Format)可执行文件解析与执行,2.0 版带来更好的性能与更多系统调用支持。

    -
  • -
  • -
    JPG 图片解析
    -

    原生支持 JPG 解析与显示,无需第三方库,支持多种尺寸与压缩质量。

    -
  • -
  • -
    SB16 声卡
    -

    完整 SB16 支持,高质量音频输出,PCM 播放与 MIDI 合成。

    -
  • -
  • -
    分区表与存储
    -

    分区表、AHCI、NVMe,多分区管理与引导加载器配置。

    -
  • -
  • -
    SPFS 2.0 文件系统
    -

    自研高性能文件系统,文件权限管理与日志功能,为硬盘存储优化的低延迟、高吞吐读写。

    -
  • +
    +
    +

    做到的部分

    +
      +
    • SPUI 1.0 桌面

      自研桌面环境,支持窗口管理、任务栏和开始菜单。

    • +
    • SPF Parser 2.0

      解析并执行 SPF(SpaceOS Format)可执行文件。2.0 版性能更好,支持更多系统调用。

    • +
    • JPG 图片解析

      原生支持 JPG 解析与显示,不依赖第三方库。

    • +
    • SB16 声卡

      SB16 声卡驱动,支持 PCM 播放和 MIDI 合成。

    • +
    • 分区表与存储

      分区表、AHCI、NVMe,多分区管理和引导加载器配置。

    • +
    • SPFS 2.0

      自研文件系统,带文件权限管理和日志,面向硬盘优化读写延迟和吞吐。

    -
    -
    -
    - 技术规格 -

    内核与子系统

    -
    -
      -
    • -
      Uinxed-Kernel
      -

      ViudiraTech 开发的 64 位宏内核,Ring 0 最高特权级,完整的系统调用、进程调度、内存管理与文件系统支持。

      -
    • -
    • -
      宏内核架构
      -

      进程管理、内存管理、文件系统、设备驱动全部在内核空间,高效的系统性能与低延迟硬件访问。

      -
    • -
    • -
      内存管理
      -

      分页内存管理与内存保护,优化的分配算法,减少碎片化。

      -
    • -
    • -
      存储
      -

      IDE / AHCI / NVMe 接口,MBR 与 GPT 分区表。

      -
    • -
    • -
      音频
      -

      SB16 兼容驱动,DMA 传输,音频混合与音量控制。

      -
    • -
    • -
      当年要求的配置
      -

      x86 处理器(Pentium 及以上),内存最低 64 MB(推荐 128 MB+),至少 500 MB 可用空间,VESA 兼容显卡(800×600)。

      -
    • +
      +
      +

      内核与子系统

      +
        +
      • Uinxed-Kernel

        ViudiraTech 开发的 64 位宏内核,运行在 Ring 0,提供系统调用、进程调度、内存管理和文件系统。

      • +
      • 宏内核架构

        进程管理、内存管理、文件系统和设备驱动都在内核空间。

      • +
      • 内存管理

        分页和内存保护。

      • +
      • 存储

        IDE / AHCI / NVMe 接口,MBR 与 GPT 分区表。

      • +
      • 音频

        SB16 兼容驱动,DMA 传输,音频混合与音量控制。

      • +
      • 当年的配置要求

        x86 处理器(Pentium 及以上),内存最低 64 MB(推荐 128 MB 以上),至少 500 MB 可用空间,VESA 兼容显卡(800×600)。

      - -
- + - - + + diff --git a/docs/spaceos7.html b/docs/spaceos7.html index 5ec9e4e..55db170 100644 --- a/docs/spaceos7.html +++ b/docs/spaceos7.html @@ -3,226 +3,171 @@ - - SpaceOS 7(存档)| GoutouStdio - - - - + + SpaceOS 7(已停更) | GoutouStdio + + + + + - + + + - - -
-
- 存档 · 已停止开发 -

SpaceOS 7

-

纯自研混合内核架构操作系统的尝试:不依赖外部代码,UEFI 引导与内核加载全部自己写。项目做到约一半后停止。

-
- 启动 · 2026 - 完全自研 - 进度约 50% + +
+
+
+

已停更

+

SpaceOS 7

+

尝试完全自研的混合内核操作系统:不依赖外部代码,UEFI 引导和内核加载都自己写。做到约一半后停止。

+
+
启动
2026
+
方式
完全自研
+
进度
约 50%
+
+
-
- -
-
+
+
- -
-

该路线已停止开发,本页仅作历史存档

-
-

不再维护,不再接受 issue、功能请求或移植。请勿基于它继续投入。当前唯一在维护的产物是 PySpOS,代码已迁移至 ViudiraTech/PySpOS。

-
-
+

这个项目已停止开发,本页仅作存档

+

不再维护,不接受 issue、功能请求或移植,请不要基于它继续投入。目前在维护的是 PySpOS 和 SpaceOS 9;PySpOS 的代码在 ViudiraTech/PySpOS。

+
+
-
- 介绍 -

掌握 UEFI 之后的一次从零开始

-
-
-

2026 年,团队完全掌握 UEFI 技术之后启动了这个自研项目:没有外部代码,没有依赖,UEFI 引导、内核加载全部自己写。设计理念是“完全自研、混合内核、极致性能”——混合内核结合微内核和宏内核的优点,既保证稳定性,又提供高性能。

-

做到约一半时停止:用户态没做完,存在 Heisenbug。中间引导和内核完全重构,桌面环境运行在 Ring 3——桌面崩了内核不会跟着炸。

+
+
+

背景

+
+

2026 年,团队研究清楚 UEFI 之后启动了这个项目:没有外部代码,没有依赖,UEFI 引导和内核加载全部自己写。设计目标写的是“完全自研、混合内核、极致性能”,混合内核想兼顾微内核的稳定和宏内核的性能。

+

做到约一半时停止:用户态没做完,还存在 Heisenbug。中间重构过一次引导和内核;桌面环境运行在 Ring 3,桌面崩溃不会拖垮内核。

-
-
-
- 特性 -

做到一半的东西

-
-
    -
  • -
    SPFS 2.0 Ultra 文件系统
    -
    -

    自研旗舰文件系统:Journal 日志、CRC64 数据校验、缓存块机制,原生内置。

    -
      -
    • Journal 日志功能
    • -
    • CRC64 数据校验
    • -
    • 缓存块机制
    • -
    -
    -
  • -
  • -
    内核重构
    -
    -

    中间引导和内核全部手搓:自研引导程序(没用 Limine / GRUB),抛弃 Uinxed-Kernel 转纯自研,伙伴系统 + Slab 分配器,进程间 IPC 与用户态支持。

    -
    -
  • -
  • -
    Ring 3 桌面环境
    -

    桌面跑在用户空间,内核与用户空间隔离,桌面崩溃不影响内核,安全性更高。

    -
  • -
  • -
    SMP 多核
    -

    对称多处理支持:多核调度、同步机制、内存管理与中断处理。

    -
  • -
  • -
    xHCI 控制器
    -

    完整的 xHCI 驱动,USB 3.0 及以上,向后兼容 USB 2.0,多设备与高带宽传输。

    -
  • -
  • -
    空间 API
    -

    自研系统调用接口:丰富的系统调用、高性能 IPC、灵活通信机制与权限控制。

    -
  • +
    +
    +

    做到的部分

    +
      +
    • SPFS 2.0 Ultra

      自研文件系统,带 Journal 日志、CRC64 数据校验和缓存块机制。

    • +
    • 内核重构

      引导程序自己写(没用 Limine / GRUB);放弃 Uinxed-Kernel,改为纯自研;伙伴系统加 Slab 分配器;进程间 IPC 和用户态支持。

    • +
    • Ring 3 桌面

      桌面跑在用户空间,与内核隔离,桌面崩溃不影响内核。

    • +
    • SMP 多核

      多核调度、同步机制、内存管理和中断处理。

    • +
    • xHCI 控制器

      xHCI 驱动,支持 USB 3.0 及以上,向后兼容 USB 2.0。

    • +
    • 空间 API

      自研的系统调用接口,包含系统调用、IPC 和权限控制。

    -
    -
    -
    - 技术细节 -

    内核侧的设计

    -
    -
      -
    • -
      混合内核架构
      -

      微内核组件用户空间化,关键服务跑在内核空间,高效 IPC 与完善的权限管理。

      -
    • -
    • -
      纯手写 UEFI 库
      -

      从零构建 UEFI 库,高半地址内核加载,不依赖任何外部引导代码。

      -
    • -
    • -
      内存管理
      -

      伙伴系统(Buddy System)+ Slab 分配器,4KB 与 2MB 大页,完整虚拟内存与硬件辅助内存保护。

      -
    • -
    • -
      进程调度
      -

      CFS 完全公平调度,实时调度支持,多核负载均衡与节能调度。

      -
    • -
    • -
      设备驱动
      -

      存储(IDE / AHCI / NVMe / USB / SCSI)、显示、网络、音频、输入,以及 xHCI USB 驱动。

      -
    • +
      +
      +

      内核设计

      +
        +
      • 混合内核

        微内核组件放到用户空间,关键服务留在内核空间;有 IPC 和权限管理。

      • +
      • UEFI 库

        从零写的 UEFI 库,高半地址内核加载,不依赖外部引导代码。

      • +
      • 内存管理

        Buddy System 加 Slab 分配器,4 KB 与 2 MB 大页,虚拟内存和硬件辅助的内存保护。

      • +
      • 进程调度

        CFS 完全公平调度,支持实时调度、多核负载均衡与节能调度。

      • +
      • 设备驱动

        存储(IDE / AHCI / NVMe / USB / SCSI)、显示、网络、音频、输入,以及 xHCI USB 驱动。

      -
      -
      -
      - 项目历程 -

      2026 年的几次推进

      -
      -
        -
      1. - 2026 -
        搞懂 UEFI

        UEFI 完全理解,启动完全自研操作系统项目。(用户态没做好)

        -
      2. -
      3. - 2026 -
        混合内核设计

        完成混合内核架构设计,结合微内核和宏内核的优点。

        -
      4. -
      5. - 2026 -
        内核实现

        完成内核基本功能:进程调度、内存管理、中断处理。

        -
      6. -
      7. - 2026 -
        Ring 3 桌面

        实现用户空间桌面环境,提升系统安全性。

        -
      8. +
        +
        +

        进展

        +
          +
        1. 搞懂 UEFI

          理解 UEFI 之后,启动完全自研的操作系统项目。(用户态没做好)

        2. +
        3. 混合内核设计

          完成混合内核的架构设计。

        4. +
        5. 内核实现

          完成内核基本功能:进程调度、内存管理、中断处理。

        6. +
        7. Ring 3 桌面

          实现用户空间的桌面环境。

        - -
- + - - + + diff --git a/docs/spaceos8-download.html b/docs/spaceos8-download.html index 72ffea5..c0aa496 100644 --- a/docs/spaceos8-download.html +++ b/docs/spaceos8-download.html @@ -3,193 +3,187 @@ - - 下载 SpaceOS 8 抢先体验版 | GoutouStdio - - - - + + 下载 SpaceOS 8 | GoutouStdio + + + + + - + + + - - -
-
- SpaceOS 8 · 公开测试 · 第二版 -

下载抢先体验版。

-

第二版公开测试镜像,修复 xHCI 控制器上的 USB HID,USB 键盘与鼠标可以使用。下载前必须先读使用声明,下载后必须核对校验值。

-
- 下载第二版 · 39.7 MiB - 先读使用声明 - 返回介绍 + +
+
+
+

抢先体验 第二版

+

下载 SpaceOS 8

+

第二版公开测试镜像,修复了 xHCI 控制器上的 USB HID,USB 键盘和鼠标可以使用。下载前请读使用声明,下载后核对 SHA-256。

+
+
-
- -
-
-
- -
-

实验版本,不作任何担保

-
-

按“现状”提供。下载即表示你接受自行承担全部风险,并只在虚拟机、测试机或可抛弃磁盘上使用。

-
+
+
+

镜像信息

+
+
+

实验版本,不作担保

+

按“现状”提供。下载即表示你自行承担全部风险,只能在虚拟机、测试机或可抛弃的磁盘上使用。

-
- -
- 当前版本 -

第二版公开测试镜像

-

默认下载第二版。第一版镜像继续保留,仅用于对照和历史存档。可启动的 ISO 镜像,精简掉了一部分 Linux 应用。

-
-
VERIFY YOUR DOWNLOAD

确认镜像完整,再启动。

-

校验命令(二选一):

-
sha256sum Uinxed-x64-slim.iso
-
Get-FileHash .\Uinxed-x64-slim.iso -Algorithm SHA256
-
-
- 下载第二版 - 版本记录 +

默认下载第二版。第一版镜像继续保留,只用于对照。镜像是可启动的 ISO,精简掉了一部分 Linux 应用。

+
+ + + + + + + +
版本第二版(Preview 2),2026-09-25
内核 / 架构Uinxed 1.0.0-alpha.1 / x86_64
文件大小41,625,600 bytes(39.7 MiB)
SHA-25695c7b80eb61a970ebb766199d4ca632093d28211bbc44e24ed5df99666723513
+
+

校验

+

下载后先核对 SHA-256,再写入测试盘或挂给虚拟机。校验命令二选一:

+
sha256sum Uinxed-x64-slim.iso
+
Get-FileHash .\Uinxed-x64-slim.iso -Algorithm SHA256
-
-
-
- 版本记录 -

两个公开版本

+
+
+

版本记录

+
+ + + + + + + + + + + + + + + + + + +
版本日期说明下载
第二版2026-09-25修复 xHCI USB HID,USB 键盘和鼠标可以使用。当前推荐。第二版
第一版2026-09-24首个公开测试镜像,不再作为默认下载。41,572,352 bytes;SHA-256:ad6ad432a41a6b63581386fd54d6dff65e347d6771d19fc159337221a1422ff2第一版
-
    -
  • -
    第二版 · 2026-09-25 当前推荐
    -

    修复 xHCI USB HID,USB 键盘与鼠标可以使用。下载第二版

    -
  • -
  • -
    第一版 · 2026-09-24 历史保留
    -

    首个公开测试镜像,已保留,不再作为默认下载。下载第一版

    文件大小 41,572,352 bytes;SHA-256:ad6ad432a41a6b63581386fd54d6dff65e347d6771d19fc159337221a1422ff2

    -
  • -
-
-
-
- 使用声明 -

下载前必须接受的条款

-

这里不是功能宣传,而是使用边界。下载即表示你已阅读并接受以下全部条款。

-
-
    -
  • -
    按现状提供,不作任何担保 核心条款
    -

    SpaceOS 8 第二版是实验性测试镜像,按“现状”和“可用状态”提供,不作任何明示或暗示担保,包括但不限于适销性、特定用途适用性、准确性、完整性、安全性、稳定性和不侵权。

    -
  • -
  • -
    只能用于可抛弃环境 使用范围
    -

    只能在虚拟机、测试机或可抛弃磁盘上使用。不得在主力设备、生产环境、重要数据分区或唯一数据副本上运行、安装、写入或测试本镜像。

    -
  • -
  • -
    数据丢失与设备损坏风险自担 责任限制
    -

    使用本镜像可能造成数据丢失、系统无法启动、硬件异常或外设不可用。在法律允许的最大范围内,作者和贡献者不对任何直接或间接损失负责。使用前必须自行完整备份。

    -
  • -
  • -
    兼容性与支持范围 第二版边界
    -

    第二版修复 xHCI 控制器上的 USB HID,USB 键盘与鼠标可以使用;但真实 GPU 驱动、虚拟化设备、音频、Linux ABI、NTFS 写入等仍属实验范围,不承诺可用、不承诺修复时间。

    -
  • -
  • -
    版本识别与反馈方式 校验优先
    -

    必须用本页公布的 SHA-256 确认镜像后再使用。反馈属于自愿行为,请勿提交个人数据;是否采纳、是否回复、是否修复,均不作承诺。

    -
  • -
-
- 下载 -

接受条款的话,镜像在这里

-

下载后先核对 SHA-256,确认无误再写入测试盘或挂给虚拟机。

+
+
+

使用声明

+
+

下载即表示你已阅读并接受以下条款。

+
    +
  1. 按现状提供,不作担保。SpaceOS 8 第二版是实验性测试镜像,按“现状”和“可用状态”提供,不作任何明示或暗示的担保,包括但不限于适销性、特定用途适用性、准确性、完整性、安全性、稳定性和不侵权。
  2. +
  3. 只能用于可抛弃的环境。只能在虚拟机、测试机或可抛弃的磁盘上使用。不得在主力设备、生产环境、重要数据分区或唯一的数据副本上运行、安装、写入或测试本镜像。
  4. +
  5. 数据丢失与设备损坏的风险自担。使用本镜像可能造成数据丢失、系统无法启动、硬件异常或外设不可用。在法律允许的最大范围内,作者和贡献者不对任何直接或间接损失负责。使用前请自行完整备份。
  6. +
  7. 兼容性与支持范围。第二版修复了 xHCI 控制器上的 USB HID,USB 键盘和鼠标可以使用;但真实 GPU 驱动、虚拟化设备、音频、Linux ABI、NTFS 写入等仍属实验范围,不承诺可用,也不承诺修复时间。
  8. +
  9. 版本识别与反馈。请用本页公布的 SHA-256 确认镜像后再使用。反馈是自愿的,请不要提交个人数据;是否采纳、回复或修复,均不作承诺。
  10. +
-
- + - - + + diff --git a/docs/spaceos8.html b/docs/spaceos8.html index 7136743..56bc954 100644 --- a/docs/spaceos8.html +++ b/docs/spaceos8.html @@ -3,339 +3,289 @@ - - 第二束光,来了。 | SpaceOS 8 抢先体验 | GoutouStdio - - - - + + SpaceOS 8 | GoutouStdio + + + + + - + + + - - -
-
-
- SpaceOS 8 · 公开测试 · 第二版 slim 镜像 -

第二束光,来了。

-

第一束光是第一版尝鲜镜像:能启动,但 USB 键鼠是坏的,插上去没反应。方向一个字没改,我们又点了一次——第二版 slim 镜像修好了 xHCI USB HID,键鼠真的能用了。已开放下载。

- -
- Uinxed 1.0.0-alpha.1 - x86_64 - Apache-2.0 -
-
-
-
-
Uinxed × Minecraft
- Uinxed 内核与 Alpine Linux 用户态下,Minecraft 在 Weston 桌面中运行的真实记录 -
-
真实运行记录 / QEMU + KVM查看运行环境 ↗
-
- -
- -
-
-
- -
-

抢先体验版,不是正式 release

-
-

构建在自研内核 Uinxed-Kernel 之上,配一套完全自研的用户态。内核已在实体服务器上跑通,桌面能启动,能跑现成的 Linux 软件——OpenJDK 与 DOOM 不是演示,是真的在跑。这一版 slim 镜像修好了 xHCI USB HID,USB 键盘与鼠标终于可用。图形与部分 ABI 仍在补齐,所以它叫抢先体验。

-
+ +
+
+
+
+

抢先体验 上一代

+

SpaceOS 8

+

内核是 C 写的 UNIX-like 内核 Uinxed-Kernel,用户态是一套自研的桌面和系统程序,包括窗口合成器、浏览器和文件管理器。第二版 slim 镜像修好了 xHCI 上的 USB HID,USB 键盘和鼠标现在可以用;第一版里它们插上没有反应。

+ +
+
内核版本
Uinxed 1.0.0-alpha.1
+
架构
x86_64
+
内核仓库许可
Apache-2.0
+
- -
- 规模 -

先说用户态——它比内核更大,也更少见

-

从窗口合成器到浏览器,没有一层是套现成的。下面是这一版的实际规模。

-
-
用户态
约 29.2 万行

SPUI 桌面、合成器、浏览器、系统程序,全部自研

内核
约 18.1 万行

C 从零写的 UNIX-like 单体内核,449 个 C/H 文件

图形界面
约 3.4 万行

合成器 + 自研图形库 libgfx

浏览器
约 2.4 万行

纯 C 的 UxHtml 引擎,含完整 JavaScript 引擎

协议兼容
0–462

Linux 6.12 x86-64 系统调用编号

版本 / 许可
1.0.0-alpha.1

内核仓库许可 Apache-2.0

-
-
- -
-
-
- 验证 -

能不能用,看它扛不扛得住现成的软件

-

一个系统是否真的能用,不看它自己有多少功能,看它能不能扛住别人写好的软件。下面四个程序,没有一个是为我们的内核写的。

-
-
    -
  • -
    OpenJDK 已经真的能跑
    -

    为了它重写了信号处理、TSS / SMP / APIC 与中断路径,并修了 VFS 与页管理——真实 JVM 的 GC 线程、线程挂起与内存映射都依赖这些地方不出错。

    -
  • -
  • -
    DOOM 经自研 X11 兼容层运行
    -

    需要键盘、鼠标、连续动画帧、音频与稳定显示通路,任何一环不行都跑不动。跑得起来说明这套兼容层不是摆设。

    -
  • -
  • -
    systemd 初步跑通
    -

    systemd 对内核 ABI 的要求非常苛刻:一启动就探测大量可选 sysctl、挂载点与命名空间接口,任何一处行为不符就起不来。为此补了 Cgroup v2 与完整的 Linux 命名空间支持。

    -
  • -
  • -
    Minecraft 团队实测
    -

    要跑起来就得扛住真实 JVM 的内存管理、GC 线程调度、mmap 与 futex 行为。运行环境见下方截图。

    -
  • -
-
-
-
- weston-terminal — Minecraft · QEMU/KVM Q35 -
- Uinxed 内核下运行 Minecraft:世界完整渲染,生命值与饥饿条、快捷栏、准星均正常 -
-
- Minecraft 运行中。宿主为 QEMU/KVM Q35,内核 - Uinxed 1.0.0-alpha.1,用户态是 - Alpine Linux v3.24,桌面走 Weston(Wayland), - 终端为 weston-terminal 14.0.2。窗口、输入与渲染均正常。 -
+
+ Minecraft 在 Weston 桌面中运行的截图,世界完整渲染,生命值、饥饿条、快捷栏和准星都正常 +
Minecraft 运行在 Uinxed 内核上:用户态是 Alpine Linux v3.24,桌面是 Weston(Wayland),终端是 weston-terminal 14.0.2,宿主是 QEMU/KVM Q35。窗口、输入和渲染正常。
-
-
-
- 兼容与外观 -

DOOM 是怎么跑起来的

+
+
+
+

抢先体验版,不是正式 release

+

内核已在实体服务器上启动,桌面能运行,也能跑 OpenJDK 和 DOOM 这类现成的 Linux 软件。图形和部分 ABI 还在补,所以只叫抢先体验。

-
    -
  • -
    X11 兼容层 120 个核心协议请求 · 约 4500 行
    -

    从零写的 X11 服务器。目的不是造玩具 X,而是让原生 SPUI 桌面能直接跑 Linux 上的图形软件:窗口映射、绘制、输入与事件、字体、光标、属性与选区,缺一个就有程序起不来。

    -
  • -
  • -
    VortexGlass 34 个素材 · 3 种主题风格
    -

    Vista Aero 风格的毛玻璃窗口装饰,在自研合成器里逐像素画出:34 个素材覆盖四边边框、标题栏按钮的四种状态,以及标志性的 Aero orb。主题可在 tech / win98 / vortexglass 之间切换,由配置服务统一管理,所有应用共享同一套外观。

    -
  • -
-
-
-
- 用户态 -

一整套自研桌面

-

内核是每个 OS 都得有的东西,用户态不是。从窗口合成器到浏览器,中间没有一层是套现成的。

-
-
    -
  • -
    SPUI 桌面 约 4500 行 · 14 个文件
    -

    原生桌面环境:dock、壁纸与窗口管理,dock 上直接启动终端、开始屏幕、文件管理器、活动监视器与文本编辑器。图标缺失时有降级配色兜底,不至于开天窗。

    -
  • -
  • -
    服务与配置系统 init / service / uxconfd
    -

    自研 PID 1 服务管理器:目标里程碑、进程监管与自动重启、僵尸回收;状态输出对齐 systemd 的 [ OK ] Started … 格式,提供 systemctl 风格命令行,经 /tmp/initctl 与 PID 1 通信。配置由 uxconfd 统一管理,uxconf-query 读写,所有应用共享同一套外观与设置。

    -
  • -
  • -
    OOBE 首次开机向导 约 2100 行
    -

    首次开机的设置流程,与 PySpOS 里的向导同一套思路——语言、时区等设置真实生效,而不是走过场。

    -
  • -
  • -
    自研 libc 与工具链 libc / libgfx / imgui
    -

    自己的 C 库、图形库与 GUI 工具包,加上自研 sh。系统不依赖现成的用户态发行版。

    -
  • -
- -
- 应用套件 -

桌面上跑的程序

-

下面每一个都是自研的,不是移植来的。行数是源码规模。

-
-
- +
+
+

规模

+
+
- + - - - - - - - - - - - - - - - + + + + + +
程序做什么行数
部分规模说明
compositor窗口合成器:DRM/KMS、损伤跟踪、Xcursor、窗口装饰12,320
browser浏览器,含 UxHtml 渲染引擎与 JS 引擎25,713
libgfx自研图形库与控件库21,785
settings系统设置3,064
terminal终端仿真器2,869
oobe首次开机向导2,137
editor文本编辑器1,637
displaymgr显示管理器1,489
filemgr文件管理器1,347
sysmon活动监视器1,267
initPID 1 服务管理器1,262
uxconfd配置守护进程873
netmgrd网络管理器761
escalate提权通道685
calc计算器388
用户态约 29.2 万行SPUI 桌面、合成器、浏览器和系统程序,全部自研。
内核约 18.1 万行C 写的 UNIX-like 单体内核,449 个 C/H 文件。
图形界面约 3.4 万行合成器和自研图形库 libgfx。
浏览器约 2.4 万行纯 C 的 UxHtml 引擎,带 JavaScript 引擎。
系统调用编号 0–462Linux 6.12 x86-64 的系统调用编号。
版本 / 许可1.0.0-alpha.1内核仓库的许可是 Apache-2.0。
-
-
-
- 底座 -

Uinxed-Kernel

-

上面的桌面、应用与浏览器最终都跑在这个内核上:C 从零写的 UNIX-like x86_64 单体内核,经 Limine 引导(UEFI 与 Legacy 双模),实现 Linux 6.12 的系统调用 ABI。

+
+
+

现成软件的运行情况

+
+

下面这些程序都不是为这个内核写的。

+
+ + + + + + + + + + +
程序状态备注
OpenJDK能运行为它重写了信号处理、TSS / SMP / APIC 和中断路径,并修了 VFS 与页管理。JVM 的 GC 线程、线程挂起和内存映射都依赖这些地方行为正确。
DOOM能运行经自研的 X11 兼容层运行。需要键盘、鼠标、连续动画帧、音频和稳定的显示通路。
systemd初步跑通systemd 一启动就探测大量可选的 sysctl、挂载点和命名空间接口,任何一处行为不符都会起不来。为此补了 Cgroup v2 和完整的 Linux 命名空间支持。
Minecraft团队实测需要内核扛住真实 JVM 的内存管理、GC 线程调度、mmap 和 futex 行为。运行环境见页首截图。
+
-
    -
  • -
    调度与进程
    -

    EEVDF 调度器,每 CPU 运行队列配红黑树时间线(vruntime / deadline / vlag / weight);SMP 任务迁移与负载均衡、IPI 抢占;两阶段等待队列避免丢失唤醒;互斥量与 futex 优先级继承;cgroups、ptrace、可加载内核模块。

    -
  • -
  • -
    内存管理
    -

    二元 buddy 物理页分配器,四级页表(4 KiB / 2 MiB / 1 GiB);高半区直接映射(HHDM)与 slab 分配器;统一页缓存带 LRU 回收、脏页回写与预读;完整 swap 子系统。

    -
  • -
  • -
    文件系统
    -

    UNIX 风格 VFS,挂载点与 inode 节点;tmpfs 作默认根,另有 procfs、sysfs、devtmpfs、cpio、cgroupfs;实际文件系统覆盖 FAT12/16/32、exFAT、ext2/3/4、NTFS(可写)与 ISO 9660。

    -
  • -
  • -
    网络
    -

    自研协议栈:Ethernet、ARP、IPv4/IPv6、ICMP/ICMPv6、NDP、UDP、TCP;e1000/e1000e 与 RTL8139/8169 网卡驱动;Linux 套接字 ABI、DHCP 客户端,/proc/net 与 /sys/class/net 视图。

    -
  • -
  • -
    驱动
    -

    输入:PS/2 键鼠、evdev、USB HID;存储:IDE/ATA、AHCI、NVMe、USB Mass Storage;显示:DRM/KMS、VirtIO-GPU、GOP 帧缓冲;音频:SB16、Intel HD Audio;总线与平台:PCI/PCIe、USB、I2C、ACPI、HPET、TPM 1.2/2.0。

    -
  • -
  • -
    安全与 IPC
    -

    seccomp 过滤与 no_new_privs;AF_UNIX / AF_NETLINK / AF_INET / AF_INET6;pipe、epoll、eventfd、timerfd、signalfd、memfd、pidfd、POSIX 消息队列与 System V IPC;futex2;inotify;Unix98 PTY 与多虚拟终端。

    -
  • +
+
+ +
+
+

X11 兼容层与窗口主题

+
    +
  • X11 兼容层

    从零写的 X11 服务器,实现了 120 个核心协议请求,约 4500 行。目的是让原生 SPUI 桌面直接运行 Linux 上的图形软件:窗口映射、绘制、输入与事件、字体、光标、属性与选区,缺任何一个都会让部分程序起不来。

  • +
  • VortexGlass

    Vista Aero 风格的毛玻璃窗口装饰,在自研合成器里逐像素绘制。34 个素材覆盖四边边框、标题栏按钮的四种状态和 Aero orb。主题可以在 tech / win98 / vortexglass 之间切换,由配置服务统一管理,所有应用共用一套外观。

-
-
-
- 诚实说明 -

走到哪一步,就写到哪一步

-
-
- -
-

实验性系统,不要拿重要数据冒险

-
-

已跑通:内核在实体服务器上启动;能引导 Alpine Linux 并拉起图形桌面(Weston/Wayland 与 Xfce 两条路径都验证过),终端可用;SPUI 桌面与自研用户态在同一套内核上运行;X11 兼容层能跑 DOOM 这类真实图形程序;真实 JVM(OpenJDK)已经能跑。

-

第二版更新:修复 xHCI 控制器上的 USB HID,USB 键盘与鼠标可用——第一版插上没反应的那两样,现在真的能用了。第一版镜像继续保留,详情与校验值见下载页版本记录。

-

仍在补:GPU 驱动、虚拟化设备、音频,以及 Linux ABI 的相当一部分。PS/2 输入路径、evdev 消费者、poll/epoll 唤醒与 EEVDF 调度器都在验证阶段。systemd 只是初步跑通,离生产可用还有距离。NTFS 写入驱动尚未被认为可以信任,请用可抛弃的机器与测试盘。

-

内核版本串为 Uinxed 1.0.0-alpha.1,已在 Dell PowerEdge R410 等实体服务器上验证。

-
+
+
+

用户态与桌面

+
+

用户态没有套现成的发行版:从窗口合成器到浏览器都是自己写的。

+
    +
  • SPUI 桌面

    约 4500 行,14 个文件。有 dock、壁纸和窗口管理;dock 上可以启动终端、开始屏幕、文件管理器、活动监视器和文本编辑器。图标缺失时有降级配色。

  • +
  • 服务与配置

    init / service / uxconfd。自研的 PID 1 服务管理器,有目标里程碑、进程监管与自动重启、僵尸回收。状态输出对齐 systemd 的 [ OK ] Started … 格式,提供 systemctl 风格的命令行,经 /tmp/initctl 与 PID 1 通信。配置由 uxconfd 统一管理,用 uxconf-query 读写。

  • +
  • 首次开机向导

    约 2100 行。思路和 PySpOS 里的向导相同:语言、时区等设置会实际生效。

  • +
  • libc 与工具链

    libc / libgfx / imgui:自己的 C 库、图形库和 GUI 工具包,另有自研的 sh。

  • +
+

应用

+

全部自研,不是移植来的。行数是源码规模。

+
+ + + + + + + + + + + + + + + + + + + + + +
程序作用行数
compositor窗口合成器:DRM/KMS、损伤跟踪、Xcursor、窗口装饰12,320
browser浏览器,含 UxHtml 渲染引擎与 JS 引擎25,713
libgfx自研图形库与控件库21,785
settings系统设置3,064
terminal终端仿真器2,869
oobe首次开机向导2,137
editor文本编辑器1,637
displaymgr显示管理器1,489
filemgr文件管理器1,347
sysmon活动监视器1,267
initPID 1 服务管理器1,262
uxconfd配置守护进程873
netmgrd网络管理器761
escalate提权通道685
calc计算器388
-
-
-
- 上手 -

构建与运行

-

需要 make、gcc(建议 13.3+)、qemu、xorriso;menuconfig 另需 kconfig-frontends 与 libncurses-dev。

+
+
+

Uinxed-Kernel

+
+

桌面、应用和浏览器都跑在这个内核上:C 从零写的 UNIX-like x86_64 单体内核,经 Limine 引导(UEFI 与 Legacy 双模),实现 Linux 6.12 的系统调用 ABI。

+
+ + + + + + + + + +
调度与进程EEVDF 调度器,每 CPU 运行队列配红黑树时间线(vruntime / deadline / vlag / weight);SMP 任务迁移与负载均衡、IPI 抢占;两阶段等待队列避免丢失唤醒;互斥量与 futex 的优先级继承;cgroups、ptrace、可加载内核模块。
内存管理二元 buddy 物理页分配器,四级页表(4 KiB / 2 MiB / 1 GiB);高半区直接映射(HHDM)和 slab 分配器;统一页缓存,带 LRU 回收、脏页回写和预读;swap 子系统。
文件系统UNIX 风格的 VFS,有挂载点和 inode 节点;tmpfs 作默认根,另有 procfs、sysfs、devtmpfs、cpio、cgroupfs;实际文件系统覆盖 FAT12/16/32、exFAT、ext2/3/4、NTFS(可写)和 ISO 9660。
网络自研协议栈:Ethernet、ARP、IPv4/IPv6、ICMP/ICMPv6、NDP、UDP、TCP;e1000/e1000e 与 RTL8139/8169 网卡驱动;Linux 套接字 ABI、DHCP 客户端,以及 /proc/net 和 /sys/class/net 视图。
驱动输入:PS/2 键鼠、evdev、USB HID。存储:IDE/ATA、AHCI、NVMe、USB Mass Storage。显示:DRM/KMS、VirtIO-GPU、GOP 帧缓冲。音频:SB16、Intel HD Audio。总线与平台:PCI/PCIe、USB、I2C、ACPI、HPET、TPM 1.2/2.0。
安全与 IPCseccomp 过滤和 no_new_privs;AF_UNIX / AF_NETLINK / AF_INET / AF_INET6;pipe、epoll、eventfd、timerfd、signalfd、memfd、pidfd、POSIX 消息队列和 System V IPC;futex2;inotify;Unix98 PTY 和多虚拟终端。
+
-
    -
  • -
    在 QEMU 里跑 最快看到桌面,无需额外硬件
    -
    -

    make run 已自动把串口输出接到终端。想抓内核日志,把 limine.conf 里的 console=tty0 改成 console=ttyS0——屏幕控制台在启动挂死时可能丢日志,串口更可靠。

    -

    源码 · 配置要求 · 抢先体验版

    -
    -
  • -
  • -
    在实体机上跑 已验证 · Dell PowerEdge R410
    -
    -

    先看仓库 README 的 Run on physical hardware 一节,按步骤准备引导介质。再次提醒:请用可抛弃的机器与测试盘,文件系统驱动(尤其 NTFS 写入)目前不适合承载重要数据。

    -
    -
  • -
- -
-
-
-
+
+ +
+
+

构建与运行

+
+

需要 make、gcc(建议 13.3+)、qemu、xorriso;menuconfig 另需 kconfig-frontends 和 libncurses-dev。

+

在 QEMU 里运行

+

不需要额外硬件,最快能看到桌面。make run 已经把串口输出接到终端。要抓内核日志,把 limine.conf 里的 console=tty0 改成 console=ttyS0:启动挂死时屏幕控制台可能丢日志,串口更可靠。

+

在实体机上运行

+

已在 Dell PowerEdge R410 上验证。先看仓库 README 的 Run on physical hardware 一节,按步骤准备引导介质。请用可抛弃的机器和测试盘;文件系统驱动(尤其是 NTFS 写入)目前不适合放重要数据。

+

源码 · 配置要求 · 下载镜像

+
+
+
+
+ + - - + + diff --git a/docs/spaceos9-download.html b/docs/spaceos9-download.html index 5313db0..6ebbf2c 100644 --- a/docs/spaceos9-download.html +++ b/docs/spaceos9-download.html @@ -3,188 +3,180 @@ - - 下载 SpaceOS 9 | SpaceKernel 3.0 | GoutouStdio - - - - + + 下载 SpaceOS 9 | GoutouStdio + + + + + - + + + - - -
-
- SpaceOS 9 · SpaceKernel 3.0 · x86_64 公开预览 -

下载公开预览镜像。

-

可启动的 x86_64 ISO。包含我们自己的内核、FAT32 根盘、systemg PID 1、splibc 与可交互的 BusyBox ash。下载前请先读使用声明,下载后请核对 SHA-256。

-
- 下载镜像 · 69.8 MiB - 先读使用声明 - 返回介绍 + +
+
+
+

公开预览

+

下载 SpaceOS 9

+

x86_64 的可启动 ISO,包含 SpaceKernel 3.0、FAT32 根盘、systemg(PID 1)、splibc 和可交互的 BusyBox ash。下载前请读使用声明,下载后核对 SHA-256。

+
-
+ -
-
-
- -
-

开发预览,不作任何担保

-
-

按“现状”提供。下载即表示你接受自行承担全部风险,并只在虚拟机、测试机或可抛弃磁盘上使用。不要在主力设备或唯一数据副本上运行。

-
+
+
+

镜像信息

+
+
+

开发预览,不作担保

+

按“现状”提供。下载即表示你自行承担全部风险,只能在虚拟机、测试机或可抛弃的磁盘上使用。不要在主力设备或唯一的数据副本上运行。

-
- -
- 当前版本 -

公开预览镜像

-

x86_64 UEFI 可启动 ISO。默认启动 systemg(PID 1),由它拉起控制台 BusyBox ash;镜像内同时保留 splibc 自带的 sh 与测试程序。

-
-
VERIFY YOUR DOWNLOAD

确认镜像完整,再启动。

-

校验命令(二选一):

-
sha256sum SpaceOS-9-x86_64.iso
-
Get-FileHash .\SpaceOS-9-x86_64.iso -Algorithm SHA256
-

也可以直接下载 SHA-256 校验文件。

-
-
- 下载镜像 - 查看源码 +
+ + + + + + + +
版本SpaceOS 9 / SpaceKernel 3.0 #13,2026-10-25
内核 / 架构SpaceKernel 3.0(SPK) / x86_64
文件大小73,175,040 bytes(69.8 MiB)
SHA-2562e6164b0ee92a1e4852f108b2b247e82005013fc10072d9f4a0be3f1eb9ccdb1
+
+

校验

+

下载后先核对 SHA-256,再启动。校验命令二选一:

+
sha256sum SpaceOS-9-x86_64.iso
+
Get-FileHash .\SpaceOS-9-x86_64.iso -Algorithm SHA256
+

也可以下载 SHA-256 校验文件。

-
-
-
- 启动方式 -

在 QEMU / UEFI 上启动

-

需要 x86_64 QEMU、OVMF UEFI 固件和至少 512 MiB 内存。推荐 Q35 机型与 VirtIO GPU 2D。

-
-
    -
  • QEMU 命令 Linux 主机示例
    qemu-system-x86_64 -machine q35 -m 512 -smp 4 \
    +    
    +
    +

    在 QEMU / UEFI 上启动

    +
    +

    需要 x86_64 QEMU、OVMF UEFI 固件和至少 512 MiB 内存。推荐 Q35 机型和 VirtIO GPU 2D。Linux 主机上的命令示例:

    +
    qemu-system-x86_64 -machine q35 -m 512 -smp 4 \
       -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \
    -  -device virtio-gpu-pci -cdrom SpaceOS-9-x86_64.iso

    建议使用图形窗口;串口控制台也可用 -serial stdio。

  • -
  • 启动后 交互验证

    systemg 会启动 console shell。看到 / # 后可执行 uname -a、id、nproc、ls、cat /etc/os-release。退出 shell 会由 systemg 重新拉起。

  • -
  • 已知边界 诚实标注

    真实 GPU、音频、网络与部分 Linux ABI 仍在推进;aarch64/riscv64 用户态未包含在此镜像中。

  • -
+ -device virtio-gpu-pci -cdrom SpaceOS-9-x86_64.iso +

建议用图形窗口;串口控制台也可以,加 -serial stdio。

+

启动后 systemg 会拉起 console shell。看到 / # 后可以执行 uname -a、id、nproc、ls、cat /etc/os-release。退出 shell 后,systemg 会重新拉起它。

+

已知限制:真实 GPU、音频、网络和部分 Linux ABI 还在做;aarch64 / riscv64 的用户态不在这个镜像里。

+
-
-
-
- 使用声明 -

下载前必须接受的条款

-

这里不是功能宣传,而是使用边界。下载即表示你已阅读并接受以下全部条款。

-
-
    -
  • 按现状提供,不作任何担保 核心条款

    SpaceOS 9 公开预览镜像是实验性构建,按“现状”和“可用状态”提供,不作任何明示或暗示担保,包括但不限于适销性、特定用途适用性、准确性、完整性、安全性、稳定性和不侵权。

  • -
  • 只能用于可抛弃环境 使用范围

    只能在虚拟机、测试机或可抛弃磁盘上使用。不得在主力设备、生产环境、重要数据分区或唯一数据副本上运行、安装或写入本镜像。

  • -
  • 数据丢失与设备损坏风险自担 责任限制

    使用本镜像可能造成数据丢失、系统无法启动、硬件异常或外设不可用。在法律允许的最大范围内,作者和贡献者不对任何直接或间接损失负责。使用前必须自行完整备份。

  • -
  • 版本识别与反馈 校验优先

    必须用本页公布的 SHA-256 确认镜像后再使用。反馈请通过 SpaceOS-9 issue tracker;是否采纳或修复不作承诺。

  • -
-
- 下载 -

接受条款的话,镜像在这里

-

下载后先核对 SHA-256,确认无误再挂给虚拟机。镜像内含第三方组件,请同时阅读第三方许可说明。

+
+
+

使用声明

+
+

下载即表示你已阅读并接受以下条款。

+
    +
  1. 按现状提供,不作担保。SpaceOS 9 公开预览镜像是实验性构建,按“现状”和“可用状态”提供,不作任何明示或暗示的担保,包括但不限于适销性、特定用途适用性、准确性、完整性、安全性、稳定性和不侵权。
  2. +
  3. 只能用于可抛弃的环境。只能在虚拟机、测试机或可抛弃的磁盘上使用。不得在主力设备、生产环境、重要数据分区或唯一的数据副本上运行、安装或写入本镜像。
  4. +
  5. 数据丢失与设备损坏的风险自担。使用本镜像可能造成数据丢失、系统无法启动、硬件异常或外设不可用。在法律允许的最大范围内,作者和贡献者不对任何直接或间接损失负责。使用前请自行完整备份。
  6. +
  7. 版本识别与反馈。请用本页公布的 SHA-256 确认镜像后再使用。反馈请提交到 SpaceOS-9 issue tracker;是否采纳或修复,不作承诺。
  8. +
-
-
-
- 第三方组件 -

BusyBox、musl 与 Limine

-

镜像包含 GPL/MIT/BSD 第三方组件。完整许可、版本、校验值与源码获取方式见 THIRD_PARTY_NOTICES.md。

-
+
+
+

第三方组件

+

镜像包含 BusyBox、musl、Limine 等 GPL / MIT / BSD 许可的第三方组件。完整许可、版本、校验值和源码获取方式见 THIRD_PARTY_NOTICES.md。

- -
- + - - + + diff --git a/docs/spaceos9.html b/docs/spaceos9.html index 0574887..228aac5 100644 --- a/docs/spaceos9.html +++ b/docs/spaceos9.html @@ -3,200 +3,196 @@ - - SpaceOS 9 · SpaceKernel 3.0 | GoutouStdio - - - - + + SpaceOS 9 | GoutouStdio + + + + + - + + + - - -
-
-
- SpaceOS 9 · SpaceKernel 3.0 · x86_64 公开预览 -

第九束光,
整条链路。

-

从 Rust 内核、Linux ABI 系统调用、FAT32 根盘,到 PID 1 服务管理器和能交互的 shell。SpaceOS 9 不再只是引导样例,而是一条可以登录、可以运行真实 Linux 程序的完整链路。

- -
- SpaceKernel 3.0(SPK) - x86_64 - Rust - Linux ABI -
-
-
-
-
SpaceOS 9 / SPK 3.0
- SpaceOS 9 标志:青色、紫色与白色的环形数字 9 + +
+
+
+
+

公开预览

+

SpaceOS 9

+

内核是 Rust 写的 SpaceKernel 3.0,实现了 Linux 64 位 ABI,目标是让现成的 Linux 程序直接运行。目前验证过的是 musl 动态链接的 BusyBox。镜像是 x86_64 的可启动 ISO。

+ +
+
内核
SpaceKernel 3.0(SPK),Rust
+
架构
x86_64
+
ABI
Linux 64 位
+
-
项目标志 / ViudiraTech获取预览镜像 ↗
-
- -
+
+ 终端截图:fastfetch 显示 SpaceOS 9 x86_64,内核 SpaceKernel 3.0-spk,shell 为 ash,旁边是彩色的数字 9 字符画 +
在 SpaceOS 9 里运行 fastfetch 的截图。
+
+ + -
-
+
+
- -
-

公开预览,不是“功能清单”

-
-

当前镜像是 x86_64 开发预览。内核自测、三架构编译检查与 QEMU 启动已通过;用户态与驱动覆盖仍在推进。下载前请阅读 使用声明。

-
-
-
-
- 概览 -

内核、ABI 与用户态

-

SpaceOS 9 的目标不是另造一套用户接口,而是尽量把 Linux 现有的进程、文件与终端语义实现完整,让现成程序能直接跑起来。

+

这是公开预览

+

内核自测、三个架构的编译检查和 QEMU 启动已经通过;用户态和驱动覆盖还在补。下载前请读使用声明。

-
-
内核
SpaceKernel 3.0

Rust、soft-float、多架构;x86_64 优先

-
ABI
Linux 64-bit

ELF/PIE/PT_INTERP、syscall、信号、futex、epoll、AF_UNIX

-
根盘
FAT32

Limine 模块 rootfs、只读挂载、/dev /proc /sys /tmp /run

-
PID 1
systemg

服务单元、依赖排序、SIGCHLD 回收与重启

-
用户态
splibc

freestanding C 库、sh、systemg;动态 BusyBox 通过 musl 运行

-
终端
ash + job control

raw termios、poll、控制终端、前台进程组、Ctrl-C / Ctrl-Z

-
-
-
-
- 实现 -

这一版真正做了什么

+
+
+

组成

+
+

SpaceOS 9 没有另造一套用户接口,而是尽量把 Linux 现有的进程、文件和终端语义做完整,让现成程序能直接运行。

+
+ + + + + + + + + +
内核SpaceKernel 3.0。Rust、soft-float、多架构,x86_64 优先。
ABILinux 64 位:ELF / PIE / PT_INTERP、系统调用、信号、futex、epoll、AF_UNIX。
根盘FAT32。Limine 模块提供 rootfs,只读挂载;有 /dev、/proc、/sys、/tmp、/run。
PID 1systemg:服务单元、依赖排序、SIGCHLD 回收与重启。
用户态splibc(freestanding C 库)、sh、systemg。动态链接的 BusyBox 通过 musl 运行。
终端ash,带作业控制:raw termios、poll、控制终端、前台进程组、Ctrl-C / Ctrl-Z。
+
-
    -
  • -
    VFS dentry / inode cache Linux 语义
    -

    正/负 dentry、inode 身份、LRU 回收、路径前缀失效;procfs/sysfs/devtmpfs 强制重新验证,避免设备注销后仍查到旧目录。FAT32 大文件读取同时做了簇链缓存。

    -
  • -
  • -
    动态链接与 BusyBox PT_INTERP
    -

    内核装载 ELF 解释器与 PIE;镜像内提供 musl 动态加载器和 BusyBox standalone shell。ash 可以真正接收键盘/串口输入,并执行 applet。

    -
  • -
  • -
    进程、信号与终端 Linux ABI
    -

    clone 线程、进程组/会话、wait4/waitid、POSIX 信号、futex、epoll、signalfd、timerfd、AF_UNIX;终端支持控制终端、前后台组与 raw 模式。

    -
  • -
  • -
    从 PID 1 到 shell systemg
    -

    systemg 作为 PID 1 解析 /etc/systemg/*.service,按依赖启动服务、回收孤儿进程,并拉起控制台 shell;splibc 提供静态 C 运行环境与自带 sh。

    -
  • +
+
+ +
+
+

这一版新增的部分

+
    +
  • VFS 缓存

    Linux 语义的 dentry / inode 缓存:正、负 dentry,inode 身份,LRU 回收,路径前缀失效。procfs、sysfs、devtmpfs 每次都重新验证,避免设备注销后还查到旧目录。FAT32 的大文件读取另外做了簇链缓存。

  • +
  • 动态链接与 BusyBox

    内核装载 ELF 解释器和 PIE。镜像里有 musl 动态加载器和 BusyBox standalone shell;ash 能接收键盘和串口输入,并执行 applet。

  • +
  • 进程、信号与终端

    clone 线程、进程组与会话、wait4 / waitid、POSIX 信号、futex、epoll、signalfd、timerfd、AF_UNIX。终端支持控制终端、前后台进程组和 raw 模式。

  • +
  • 从 PID 1 到 shell

    systemg 作为 PID 1,解析 /etc/systemg/*.service,按依赖启动服务、回收孤儿进程,并拉起控制台 shell。splibc 提供静态 C 运行环境和自带的 sh。

-
-
-
- 验证边界 -

哪些验证过,哪些还没有

-

这里只写实际跑过的结果;未验收的部分明确标注,不用占位值冒充。

+
+
+

验证情况

+
+ + + + + + + + + +
范围状态内容
x86_64已验证UEFI/QEMU 启动、systemg 作 PID 1、BusyBox 动态加载、ash 交互、uname / id / nproc / ls / cat、VFS dcache 自测、SELFTEST_OK,以及三个架构的 cargo check。
aarch64 / riscv64仅编译检查内核能通过编译检查。用户态、rootfs 和启动链路没有在这个镜像里验证。
真实硬件与 GPU未验证目前只在 QEMU 的 VirtIO GPU 2D 上验证。真实 GPU、音频、网络、睡眠唤醒不在这个镜像的承诺范围内。
-
    -
  • x86_64 实跑 已验收

    UEFI/QEMU 启动、systemg PID 1、BusyBox 动态加载、ash 交互、uname/id/nproc/ls/cat、VFS dcache 自测、SELFTEST_OK 与三架构 cargo check。

  • -
  • aarch64 / riscv64 仅编译检查

    内核可通过编译检查;用户态、rootfs 与启动链路尚未在该镜像中验收。

  • -
  • 真实硬件与 GPU 未验收

    当前验证在 QEMU 的 VirtIO GPU 2D 链路完成;真实 GPU、音频、网络与睡眠唤醒等未纳入本镜像承诺。

  • -
-
-
-
- 源码与许可 -

源码公开,第三方组件各自遵循其许可证

-

SpaceOS 9 的源码位于 ViudiraTech/SpaceOS-9。镜像包含 BusyBox、musl、Limine 等第三方组件;下载页提供第三方许可与源码获取说明。

+
+
+

源码与许可

+
+

SpaceOS 9 的源码在 ViudiraTech/SpaceOS-9。镜像里包含 BusyBox、musl、Limine 等第三方组件,各自遵循自己的许可证,许可与源码获取方式见下载页和 THIRD_PARTY_NOTICES.md。

-
- + - - + + diff --git a/tools/checks/check_docs_html.py b/tools/checks/check_docs_html.py index 84c61a0..68ea83c 100644 --- a/tools/checks/check_docs_html.py +++ b/tools/checks/check_docs_html.py @@ -20,6 +20,9 @@ REPO = str(Path(__file__).resolve().parents[2]) DOCS = os.path.join(REPO, "docs") +sys.path.insert(0, os.path.join(REPO, "tools")) +import sync_docs_chrome # noqa: E402 + VOID = {"area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"} @@ -114,6 +117,39 @@ def handle_endtag(self, tag): self.depth -= 1 +class NoticeCheck(HTMLParser): + """Collect the text and links of the first warning notice on a page.""" + + def __init__(self): + super().__init__(convert_charrefs=True) + self.depth = 0 + self.start = None + self.seen = False + self.text = [] + self.links = [] + + def handle_starttag(self, tag, attrs): + attrs = dict(attrs) + if tag not in VOID: + self.depth += 1 + if not self.seen and "notice-warn" in (attrs.get("class") or "").split(): + self.seen = True + self.start = self.depth + if self.start is not None and tag == "a": + self.links.append(attrs.get("href", "")) + + def handle_data(self, data): + if self.start is not None: + self.text.append(data) + + def handle_endtag(self, tag): + if tag in VOID: + return + if self.start is not None and self.depth == self.start: + self.start = None + self.depth -= 1 + + # Project state checks must keep working when the content layout changes. def check_product_states(): errs = [] @@ -121,7 +157,7 @@ def check_product_states(): if os.path.exists(idx): parser = ProductCheck() parser.feed(Path(idx).read_text(encoding="utf-8")) - expected = {"PySpOS": "active", "SpaceOS 8": "active", + expected = {"PySpOS": "active", "SpaceOS 9": "active", "SpaceOS 8": "active", "SpaceOS 6": "retired", "SpaceOS 7": "retired"} names = [product["name"] for product in parser.products] if sorted(names) != sorted(expected): @@ -133,27 +169,43 @@ def check_product_states(): errs.append(f"项目 {name}: 维护状态 {status} 不正确") if status == "retired" and "已停更" not in text: errs.append(f"项目 {name}: 未明确标注已停更") - if status == "active" and not any(word in text for word in ("维护中", "抢先体验")): + if status == "active" and not any(word in text for word in ("维护中", "公开预览", "抢先体验")): errs.append(f"项目 {name}: 缺少维护状态说明") if not any(link and not link.startswith("#") for link in product["links"]): errs.append(f"项目 {name}: 没有可点击出口") - # The SpaceOS 6/7 detail pages need a retirement notice pointing at PySpOS + # The SpaceOS 6/7 detail pages need a retirement notice that points at the maintained projects for ver in ("6", "7"): p = os.path.join(DOCS, f"spaceos{ver}.html") if not os.path.exists(p): continue - s = open(p, encoding="utf-8").read() - m = re.search(r'
]*>(.*?)
\s*', - s, re.S) - if not m: + notice = NoticeCheck() + notice.feed(open(p, encoding="utf-8").read()) + if not notice.seen: errs.append(f"spaceos{ver}.html: 缺少停更提示块(.notice.notice-warn)") continue - banner = m.group(1) - if "停止开发" not in banner: + if "停止开发" not in "".join(notice.text): errs.append(f"spaceos{ver}.html: 提示块未明确写「停止开发」") - if "pyspos.html" not in banner: + if "pyspos.html" not in notice.links: errs.append(f"spaceos{ver}.html: 提示块未指向当前维护的 PySpOS") + if "spaceos9.html" not in notice.links: + errs.append(f"spaceos{ver}.html: 提示块未指向当前维护的 SpaceOS 9") + return errs + + +# Site-wide rules: shared chrome in sync, SpaceOS 9 reachable everywhere, release fallback intact. +def check_site_rules(pages): + errs = list(sync_docs_chrome.check()) + for p in pages: + rel = os.path.relpath(p, DOCS).replace(os.sep, "/") + c = check_page(p) + if not any(urlsplit(h).path.endswith("spaceos9.html") for h in c.links): + errs.append(f"{rel}: 没有指向 spaceos9.html 的链接,导航里缺 SpaceOS 9") + releases = os.path.join(DOCS, "ota", "releases.html") + if os.path.exists(releases): + # build_update.py rewrites this variable from version.json; without it the sync silently skips + if "const fallbackVersionData = " not in open(releases, encoding="utf-8").read(): + errs.append("ota/releases.html: 缺少 const fallbackVersionData,build_update.py 无法同步兜底数据") return errs @@ -177,6 +229,14 @@ def main(): total += len(c.errs) print("-" * 62) + site_errs = check_site_rules(pages) + if site_errs: + print("站点规则:") + for e in site_errs: + print(f" - {e}") + total += len(site_errs) + else: + print("站点规则: OK(页眉/菜单/页脚与单一来源一致,每页都能到 SpaceOS 9)") state_errs = check_product_states() if state_errs: print("产品状态一致性:") diff --git a/tools/checks/check_tokens_contrast.py b/tools/checks/check_tokens_contrast.py index c4bb5cf..a1e26c6 100644 --- a/tools/checks/check_tokens_contrast.py +++ b/tools/checks/check_tokens_contrast.py @@ -148,7 +148,11 @@ def main(): 'page': {'dark': rd('--pg-bg-page'), 'light': rl('--pg-bg-page')}, 'raised': {'dark': rd('--pg-bg-raised'), 'light': rl('--pg-bg-raised')}, 'sunk': {'dark': rd('--pg-bg-sunken'), 'light': rl('--pg-bg-sunken')}, - 'band': {'dark': rd('--pg-band-bg'), 'light': rl('--pg-band-bg')}, + 'ink': {'dark': rd('--pg-ink'), 'light': rl('--pg-ink')}, + 'soft': {'dark': rd('--pg-accent-soft'), 'light': rl('--pg-accent-soft')}, + 'tile-clay': {'dark': rd('--pg-tile-clay'), 'light': rl('--pg-tile-clay')}, + 'tile-sky': {'dark': rd('--pg-tile-sky'), 'light': rl('--pg-tile-sky')}, + 'tile-sage': {'dark': rd('--pg-tile-sage'), 'light': rl('--pg-tile-sage')}, 'term': {'dark': rd('--pg-term-bg'), 'light': rl('--pg-term-bg')}, } @@ -160,14 +164,22 @@ def main(): ('正文 / 凸起面', '--pg-fg-1', 'raised', 4.5), ('次要 / 凸起面', '--pg-fg-2', 'raised', 4.5), ('次要 / 下沉面', '--pg-fg-2', 'sunk', 4.5), + ('弱化 / 下沉面', '--pg-fg-3', 'sunk', 4.5), + ('链接 accent / 下沉面', '--pg-accent', 'sunk', 4.5), + ('链接 accent / 提示底', '--pg-accent', 'soft', 4.5), + ('主按钮文字 / 按钮底', '--pg-on-ink', 'ink', 4.5), + ('正文 / 黏土色块', '--pg-fg-1', 'tile-clay', 4.5), + ('次要 / 黏土色块', '--pg-fg-2', 'tile-clay', 4.5), + ('正文 / 天蓝色块', '--pg-fg-1', 'tile-sky', 4.5), + ('次要 / 天蓝色块', '--pg-fg-2', 'tile-sky', 4.5), + ('正文 / 鼠尾草色块', '--pg-fg-1', 'tile-sage', 4.5), + ('次要 / 鼠尾草色块', '--pg-fg-2', 'tile-sage', 4.5), # line-1/line-2 are purely decorative separators (WCAG 1.4.11 exempts decoration); # control borders use --pg-line-strong instead, which is what is measured here. ('控件边界 strong / 页面底', '--pg-line-strong', 'page', 3.0), - ('反白带文字 / 带底', '--pg-band-fg', 'band', 4.5), - ('反白带次要 / 带底', '--pg-band-mute', 'band', 4.5), ('终端正文 / 终端底', '--pg-term-fg', 'term', 4.5), ('终端弱化 / 终端底', '--pg-term-dim', 'term', 4.5), - ('终端提示绿 / 终端底', '--pg-term-green', 'term', 3.0), + ('终端提示符 / 终端底', '--pg-term-accent', 'term', 4.5), ] print(f"{'检查项':28} {'暗色':>10} {'亮色':>10} 判定") print('-' * 70) diff --git a/tools/sync_docs_chrome.py b/tools/sync_docs_chrome.py new file mode 100644 index 0000000..a188d54 --- /dev/null +++ b/tools/sync_docs_chrome.py @@ -0,0 +1,293 @@ +''' + * + * sync_docs_chrome.py + * Single source for the docs site chrome: head assets, header menu and footer sitemap. + * + * 2026/11/7 By GoutouStdio + * Copyright (C) 2022-2026 GoutouStdio, based on the MIT license. + * + */ +''' + +# Every page in docs/ carries three marker pairs. This script rewrites what sits between them: +# ... stylesheet links and favicon +# ... theme script, skip link, header and menu +# ... footer sitemap and the site script +# Page content stays hand-written. Adding a page means adding it to the lists below and running: +# python tools/sync_docs_chrome.py rewrite the pages +# python tools/sync_docs_chrome.py --check exit 1 if any page is out of date (CI uses this) + +import hashlib +import re +import sys +from html import escape +from pathlib import Path + +DOCS = Path(__file__).resolve().parents[1] / "docs" + +GITHUB = "https://github.com/ViudiraTech" +MAIL = "mailto:goutoustdio@outlook.com" + +# Plain links in the header, in order. (text, page) +HEADER_LINKS = [ + ("PySpOS", "pyspos.html"), + ("SpaceOS 9", "spaceos9.html"), + ("SpaceOS 8", "spaceos8.html"), + ("配置要求", "requirements.html"), +] + +# Drop-down menus in the header, shown after the plain links. (label, [(text, page)]) +HEADER_MENUS = [ + ("下载", [ + ("PySpOS 更新包", "ota/index.html"), + ("PySpOS 版本历史", "ota/releases.html"), + ("SpaceOS 9 镜像", "spaceos9-download.html"), + ("SpaceOS 8 镜像", "spaceos8-download.html"), + ]), + ("存档", [ + ("SpaceOS 7", "spaceos7.html"), + ("SpaceOS 6", "spaceos6.html"), + ]), +] + +# Footer sitemap columns. A target that starts with http or mailto is external, anything else is a page. +FOOTER = [ + ("项目", [ + ("PySpOS", "pyspos.html"), + ("SpaceOS 9", "spaceos9.html"), + ("SpaceOS 8", "spaceos8.html"), + ]), + ("下载", [ + ("PySpOS 更新包", "ota/index.html"), + ("PySpOS 版本历史", "ota/releases.html"), + ("SpaceOS 9 镜像", "spaceos9-download.html"), + ("SpaceOS 8 镜像", "spaceos8-download.html"), + ]), + ("资料", [ + ("配置要求", "requirements.html"), + ("GitHub", GITHUB), + ("联系我们", "index.html#contact"), + ]), + ("存档", [ + ("SpaceOS 7", "spaceos7.html"), + ("SpaceOS 6", "spaceos6.html"), + ]), +] + +FAVICON = ( + "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E" + "%3Crect width='64' height='64' rx='12' fill='%23d97757'/%3E" + "%3Ctext x='32' y='44' font-size='34' font-weight='700' text-anchor='middle' " + "fill='%23faf9f5' font-family='Georgia,serif'%3EG%3C/text%3E%3C/svg%3E" +) + +BLOCKS = ("head", "chrome", "foot") + + +# Every page in docs/ and docs/ota/, as paths relative to docs/ with forward slashes. +def page_paths(): + pages = sorted(DOCS.glob("*.html")) + sorted((DOCS / "ota").glob("*.html")) + return [p.relative_to(DOCS).as_posix() for p in pages] + + +# Hash a static asset with line endings normalised, so the cache key is the same on every OS. +def asset_version(rel): + data = (DOCS / rel).read_bytes().replace(b"\r\n", b"\n") + return hashlib.sha1(data).hexdigest()[:8] + + +# Path prefix that leads from a page back to docs/. +def root_prefix(page): + return "../" * page.count("/") + + +# True for a target that leaves the site. +def is_external(target): + return target.startswith(("http://", "https://", "mailto:")) + + +# Render one for a header or footer target, marking it current when it is the page itself. +def render_link(page, text, target): + if is_external(target): + extra = ' rel="noopener" target="_blank"' if target.startswith("http") else "" + return f'{escape(text)}' + current = ' aria-current="page"' if target == page else "" + return f'{escape(text)}' + + +# Render the stylesheet links and favicon for one page. +def render_head(page): + root = root_prefix(page) + return "\n".join([ + f' ', + f' ', + f' ', + ]) + + +# Render the header menu items for one page. +def render_menu(page): + out = [] + for text, target in HEADER_LINKS: + out.append(f"
  • {render_link(page, text, target)}
  • ") + for label, items in HEADER_MENUS: + current = " is-current" if any(t == page for _x, t in items) else "" + out.append(f'
  • ") + out.append(f"
  • {render_link(page, 'GitHub', GITHUB)}
  • ") + return "\n".join(out) + + +# Render the theme script, skip link and header for one page. +def render_chrome(page): + root = root_prefix(page) + version = asset_version("js/theme-init.js") + return "\n".join([ + f' ', + ' ', + ' ", + ]) + + +# Render the footer sitemap and the site script for one page. +def render_foot(page): + root = root_prefix(page) + out = [ + '
    ', + ' ", + '
    ', + ' ", + "
    ", + "
    ", + f' ', + ] + return "\n".join(out) + + +RENDERERS = {"head": render_head, "chrome": render_chrome, "foot": render_foot} + + +# Return the page text with every marker pair rewritten, plus the names of pairs that are missing. +def apply_blocks(page, text): + missing = [] + for name in BLOCKS: + pattern = re.compile( + r"[ \t]*.*?" % (name, name), re.S) + if not pattern.search(text): + missing.append(name) + continue + block = " \n%s\n " % (name, RENDERERS[name](page), name) + text = pattern.sub(lambda _m: block, text, count=1) + return text, missing + + +# Read a page, normalising line endings so the comparison is stable on every OS. +def read_page(page): + return (DOCS / page).read_text(encoding="utf-8").replace("\r\n", "\n") + + +# Pages of the site that the header cannot reach, and menu targets that point at no page. +def nav_mismatch(): + linked = {"index.html"} + for _text, target in HEADER_LINKS: + linked.add(target) + for _label, items in HEADER_MENUS: + linked.update(target for _text, target in items) + pages = set(page_paths()) + return sorted(pages - linked), sorted(linked - pages) + + +# List the problems found when comparing every page with what this script would write. +def check(): + problems = [] + unlisted, dangling = nav_mismatch() + for page in unlisted: + problems.append(f"{page}: 不在页眉菜单里,访客从导航找不到它") + for page in dangling: + problems.append(f"菜单指向不存在的页面: {page}") + for page in page_paths(): + text = read_page(page) + new, missing = apply_blocks(page, text) + for name in missing: + problems.append(f"{page}: 缺少 / 标记") + if not missing and new != text: + problems.append(f"{page}: 页眉/菜单/页脚与 tools/sync_docs_chrome.py 不一致," + "请运行 python tools/sync_docs_chrome.py") + return problems + + +# Rewrite every page whose chrome is out of date and return the pages that changed. +def sync(): + changed = [] + for page in page_paths(): + text = read_page(page) + new, missing = apply_blocks(page, text) + if missing: + print(f"[WARN] {page}: 缺少标记 {', '.join(missing)},已跳过") + continue + if new != text: + (DOCS / page).write_text(new, encoding="utf-8", newline="\n") + changed.append(page) + return changed + + +# Entry point: rewrite the pages, or only verify them with --check. +def main(argv=None): + argv = sys.argv[1:] if argv is None else argv + if "--check" in argv: + problems = check() + for line in problems: + print(f"[FAIL] {line}") + if not problems: + print("[ OK ] 所有页面的页眉、菜单与页脚都与单一来源一致") + return 1 if problems else 0 + unlisted, dangling = nav_mismatch() + for page in unlisted: + print(f"[WARN] {page} 不在页眉菜单里,先把它加进 HEADER_LINKS 或 HEADER_MENUS") + for page in dangling: + print(f"[WARN] 菜单指向不存在的页面: {page}") + changed = sync() + for page in changed: + print(f"[sync] {page}") + print(f"完成:{len(changed)} 个页面更新") + return 1 if unlisted or dangling else 0 + + +if __name__ == "__main__": + sys.exit(main())