website: dark-first redesign + SFTP 面板截图
CI / typecheck + test + build (windows) (push) Canceled after 0s
Deploy website to GitHub Pages / deploy (push) Canceled after 0s

- 全站深色(#0b0e12 + 终端绿),右上角可切浅色;两套主题共用 CSS 令牌
- 自托管字体:Space Grotesk(展示)+ JetBrains Mono(等宽),不再依赖系统栈
- 「核心能力」改为横向滚动卡片轨道(箭头翻页 / 拖拽 / 键盘方向键)
- 新增 SFTP 文件面板截图(含空白处右键菜单),IP 已打码
- 补 og:image / theme-color / skip-link / 焦点环 / 导航当前位置高亮
This commit is contained in:
Bill committed 2026-10-09 00:46:35 +08:00
1 parent 9e5e959f80
commit 5602949a4f
12 files changed
+841 -179

No files matched your search

+11 -5
View File
@@ -14,9 +14,12 @@ website/
├── index.html # 单页站点:功能介绍 / 下载 / 更新日志 ├── index.html # 单页站点:功能介绍 / 下载 / 更新日志
├── .nojekyll # 让 GitHub Pages 原样托管(跳过 Jekyll 处理) ├── .nojekyll # 让 GitHub Pages 原样托管(跳过 Jekyll 处理)
└── assets/ └── assets/
├── style.css # 全部样式(企业风浅色主题,单一强调色) ├── style.css # 全部样式(深色默认 / 浅色可切换,主题令牌在 :root 与 html[data-theme='light'])
├── fonts/ # 自托管 woff2:Space Grotesk 400/500/600/700、JetBrains Mono 400/500/600
├── shot-terminal.jpg # 主界面截图(三路分屏并行 AI 编程 Agent,含背景图个性化) ├── shot-terminal.jpg # 主界面截图(三路分屏并行 AI 编程 Agent,含背景图个性化)
├── shot-agents.png # 多路 AI Agent 分屏截图(无背景图版) ├── shot-agents.png # 多路 AI Agent 分屏截图(无背景图版)
├── shot-sftp.png # SFTP 文件面板:列头、排序、用户/组、字体缩放
├── shot-sftp-menu.png # 文件列表空白处右键菜单
├── shot-themes.jpg # 主题设置页截图(背景图/不透明度/压暗层) ├── shot-themes.jpg # 主题设置页截图(背景图/不透明度/压暗层)
├── shot-lock.png # 锁屏界面截图 ├── shot-lock.png # 锁屏界面截图
└── icon.png # 应用图标(来自仓库 build/icon.png) └── icon.png # 应用图标(来自仓库 build/icon.png)
@@ -62,7 +65,10 @@ npx serve website
## 设计约定 ## 设计约定
- 浅色企业风,避免营销站常见的霓虹/渐变装饰 - **深色为主**:默认深色(`#0b0e12` 底 + 终端绿强调色),右上角按钮可切浅色;两套主题共用同一份令牌,新增样式一律走 CSS 变量,不要写死颜色
- 强调色仅一个:`#1a7f37`(应用品牌绿 #3fb950 的深色化,保证浅底对比度 ≥ 4.5:1) - 主题选择存 `localStorage['openterminal.theme']`,首帧前由 `index.html` 头部的内联脚本定好,避免闪白/闪黑
- 截图来自真实运行界面,与应用内设置一致 - 全站只用**一个强调色**:深底 `#3fb950`、浅底 `#1a7f37`(对比度 ≥ 4.5:1)
- 中文为主要语言;字体使用系统栈(Segoe UI / 微软雅黑),不加载外部字体 - 字体自托管(`assets/fonts/`),不依赖 Google Fonts:展示字体 Space Grotesk、等宽 JetBrains Mono;中文回落到系统栈
- 「核心能力」是横向滚动卡片轨道(箭头翻页 + 拖拽 + 键盘方向键),不再用三栏等高卡片
- 截图来自真实运行界面,与应用内设置一致;涉及 IP / 主机名等敏感信息的截图必须打码后再放入 `assets/`
- 中文为主要语言;文案保持具体、平实,避免营销套话
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 662 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 KiB

+536 -128
View File
@@ -1,17 +1,140 @@
/* OpenTerminal 官网 - 企业风浅色主题 /* OpenTerminal 官网 —— 双主题 (深色默认 / 浅色)
单一强调色: 品牌绿 #1a7f37 (深化的 #3fb950, 保证浅底对比度) 排版: Space Grotesk (展示) + JetBrains Mono (等宽), 自托管 woff2
无渐变 / 无发光 / 无装饰性动画, 仅保留 hover 与 focus 反馈 */ 单一强调色: 终端绿 (深底 #3fb950, 浅底 #1a7f37) */
@font-face {
font-family: 'Space Grotesk';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('assets/fonts/space-grotesk-400.woff2') format('woff2');
}
@font-face {
font-family: 'Space Grotesk';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('assets/fonts/space-grotesk-500.woff2') format('woff2');
}
@font-face {
font-family: 'Space Grotesk';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('assets/fonts/space-grotesk-600.woff2') format('woff2');
}
@font-face {
font-family: 'Space Grotesk';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('assets/fonts/space-grotesk-700.woff2') format('woff2');
}
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('assets/fonts/jetbrains-mono-400.woff2') format('woff2');
}
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('assets/fonts/jetbrains-mono-500.woff2') format('woff2');
}
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('assets/fonts/jetbrains-mono-600.woff2') format('woff2');
}
/* ---- 主题令牌: 深色为默认 ---- */
:root { :root {
--bg: #ffffff; color-scheme: dark;
--bg-soft: #f7f8f8;
--text: #1c2128; --bg: #0b0e12;
--text-2: #57606a; --bg-alt: #0d1219;
--border: #d9dee3; --surface: #11161e;
--surface-2: #161c26;
--text: #e6ebe6;
--text-2: #99a49c;
--text-3: #6f7a72;
--border: rgba(255, 255, 255, 0.09);
--border-strong: rgba(255, 255, 255, 0.16);
--accent: #3fb950;
--accent-hover: #34a24a;
--accent-text: #4fd067;
--accent-soft: rgba(63, 185, 80, 0.12);
--btn-fg: #06210d;
--overlay-1: rgba(255, 255, 255, 0.03);
--overlay-2: rgba(255, 255, 255, 0.06);
--overlay-3: rgba(255, 255, 255, 0.08);
--overlay-row: rgba(255, 255, 255, 0.02);
--grid-line: rgba(255, 255, 255, 0.03);
--grid-line-soft: rgba(255, 255, 255, 0.022);
--nav-bg: rgba(11, 14, 18, 0.82);
--glow-1: rgba(63, 185, 80, 0.15);
--glow-2: rgba(63, 185, 80, 0.07);
--grain: 0.045;
--shadow-card: 0 1px 0 rgba(255, 255, 255, 0.04) inset, 0 18px 48px -24px rgba(0, 0, 0, 0.9);
--shadow-lift: 0 1px 0 rgba(255, 255, 255, 0.06) inset, 0 28px 64px -28px rgba(0, 0, 0, 1);
--shadow-shot: 0 2px 10px rgba(0, 0, 0, 0.5), 0 36px 88px -32px rgba(0, 0, 0, 0.95);
--display: 'Space Grotesk', 'Segoe UI', 'Microsoft YaHei', sans-serif;
--mono: 'JetBrains Mono', ui-monospace, 'Cascadia Mono', Consolas, monospace;
--radius: 10px;
--radius-lg: 14px;
}
html[data-theme='light'] {
color-scheme: light;
--bg: #fafbf9;
--bg-alt: #f1f4f0;
--surface: #ffffff;
--surface-2: #fbfdfa;
--text: #151a16;
--text-2: #59615a;
--text-3: #8b948c;
--border: #e1e6df;
--border-strong: #cbd3ca;
--accent: #1a7f37; --accent: #1a7f37;
--accent-hover: #116329; --accent-hover: #146227;
--radius: 8px; --accent-text: #1a7f37;
--mono: ui-monospace, "Cascadia Mono", "SF Mono", Consolas, monospace; --accent-soft: rgba(26, 127, 55, 0.09);
--btn-fg: #ffffff;
--overlay-1: rgba(21, 26, 22, 0.03);
--overlay-2: rgba(21, 26, 22, 0.05);
--overlay-3: rgba(21, 26, 22, 0.06);
--overlay-row: rgba(21, 26, 22, 0.02);
--grid-line: rgba(21, 26, 22, 0.05);
--grid-line-soft: rgba(21, 26, 22, 0.035);
--nav-bg: rgba(250, 251, 249, 0.86);
--glow-1: rgba(26, 127, 55, 0.1);
--glow-2: rgba(26, 127, 55, 0.05);
--grain: 0.02;
--shadow-card: 0 1px 2px rgba(23, 32, 24, 0.06), 0 18px 40px -24px rgba(23, 32, 24, 0.22);
--shadow-lift: 0 2px 4px rgba(23, 32, 24, 0.08), 0 28px 56px -26px rgba(23, 32, 24, 0.28);
--shadow-shot: 0 2px 8px rgba(23, 32, 24, 0.12), 0 32px 72px -32px rgba(23, 32, 24, 0.35);
} }
* { box-sizing: border-box; margin: 0; padding: 0; } * { box-sizing: border-box; margin: 0; padding: 0; }
@@ -19,28 +142,63 @@
html { scroll-behavior: smooth; } html { scroll-behavior: smooth; }
body { body {
font-family: "Segoe UI", "Microsoft YaHei", -apple-system, "PingFang SC", "Noto Sans SC", sans-serif; font-family: 'Segoe UI', 'Microsoft YaHei', -apple-system, 'PingFang SC', 'Noto Sans SC', sans-serif;
font-size: 16px; font-size: 16px;
line-height: 1.6; line-height: 1.65;
color: var(--text); color: var(--text);
background: var(--bg); background: var(--bg);
transition: background-color 0.3s ease, color 0.3s ease;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
} }
.container { .container {
max-width: 1120px; max-width: 1180px;
margin: 0 auto; margin: 0 auto;
padding: 0 24px; padding: 0 28px;
} }
.mono { font-family: var(--mono); font-size: 0.92em; } .mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
h1, h2, h3 {
font-family: var(--display);
font-weight: 700;
text-wrap: balance;
color: var(--text);
}
p { text-wrap: pretty; }
img { max-width: 100%; }
a:focus-visible,
button:focus-visible {
outline: 2px solid var(--accent-text);
outline-offset: 3px;
border-radius: 6px;
}
.skip-link {
position: absolute;
left: -9999px;
top: 0;
z-index: 100;
background: var(--accent);
color: var(--btn-fg);
padding: 10px 18px;
font-size: 14px;
font-weight: 600;
}
.skip-link:focus { left: 12px; top: 12px; }
section[id], .site-nav { scroll-margin-top: 76px; }
/* ---- nav ---- */ /* ---- nav ---- */
.site-nav { .site-nav {
position: sticky; position: sticky;
top: 0; top: 0;
z-index: 10; z-index: 30;
background: rgba(255, 255, 255, 0.94); background: var(--nav-bg);
backdrop-filter: blur(14px);
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
} }
@@ -48,58 +206,128 @@ body {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
height: 64px; gap: 16px;
height: 62px;
} }
.brand { .brand {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
font-family: var(--display);
font-weight: 600; font-weight: 600;
font-size: 17px; font-size: 17px;
letter-spacing: -0.01em;
color: var(--text); color: var(--text);
text-decoration: none; text-decoration: none;
flex-shrink: 0;
} }
.nav-links { display: flex; gap: 28px; } .brand img { border-radius: 6px; }
.nav-right { display: flex; align-items: center; gap: 10px; }
.nav-links { display: flex; gap: 2px; }
.nav-links a { .nav-links a {
color: var(--text-2); color: var(--text-2);
text-decoration: none; text-decoration: none;
font-size: 15px; font-size: 14.5px;
padding: 7px 13px;
border-radius: 7px;
transition: color 0.2s ease, background 0.2s ease;
} }
.nav-links a:hover { color: var(--text); } .nav-links a:hover { color: var(--text); background: var(--overlay-2); }
.nav-links a.is-current { color: var(--text); background: var(--accent-soft); }
.theme-toggle {
position: relative;
width: 38px;
height: 38px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 50%;
border: 1px solid var(--border-strong);
background: var(--surface);
color: var(--text-2);
cursor: pointer;
flex-shrink: 0;
transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease, transform 0.15s ease;
}
.theme-toggle:hover { color: var(--accent-text); border-color: var(--accent); transform: translateY(-1px); }
.theme-toggle:active { transform: translateY(0) scale(0.97); }
.theme-toggle svg { position: absolute; transition: opacity 0.25s ease, transform 0.35s ease; }
.theme-toggle .icon-sun { opacity: 0; transform: rotate(-60deg) scale(0.7); }
.theme-toggle .icon-moon { opacity: 1; transform: none; }
html[data-theme='light'] .theme-toggle .icon-sun { opacity: 1; transform: none; }
html[data-theme='light'] .theme-toggle .icon-moon { opacity: 0; transform: rotate(60deg) scale(0.7); }
/* ---- hero ---- */ /* ---- hero ---- */
.hero { .hero {
padding: 72px 0 0; position: relative;
background: var(--bg-soft); overflow: hidden;
padding: 92px 0 0;
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
background:
radial-gradient(1100px 500px at 80% -15%, var(--glow-1), transparent 62%),
radial-gradient(760px 420px at -10% 115%, var(--glow-2), transparent 58%),
var(--bg);
}
.hero::before {
content: "";
position: absolute;
inset: 0;
background-image:
linear-gradient(var(--grid-line) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
background-size: 72px 72px;
mask-image: radial-gradient(900px 620px at 50% 0%, #000 32%, transparent 100%);
pointer-events: none;
}
.hero::after {
content: "";
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
opacity: var(--grain);
pointer-events: none;
} }
.hero-grid { .hero-grid {
position: relative;
z-index: 1;
display: grid; display: grid;
grid-template-columns: 1fr 1.2fr; grid-template-columns: 1fr 1.12fr;
gap: 48px; gap: 56px;
align-items: center; align-items: center;
} }
.hero-kicker { .hero-kicker {
font-size: 14px; font-family: var(--mono);
color: var(--text-2); font-size: 13px;
margin-bottom: 16px; font-weight: 500;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--accent-text);
margin-bottom: 20px;
} }
.hero h1 { .hero h1 {
font-size: 36px; font-size: clamp(34px, 4.6vw, 54px);
line-height: 1.25; line-height: 1.08;
letter-spacing: -0.02em; letter-spacing: -0.03em;
font-weight: 650;
} }
.hero h1 .accent { color: var(--accent-text); }
.hero-sub { .hero-sub {
margin-top: 20px; margin-top: 22px;
max-width: 46ch; max-width: 46ch;
color: var(--text-2); color: var(--text-2);
font-size: 17px; font-size: 17px;
@@ -108,118 +336,251 @@ body {
.hero-actions { .hero-actions {
display: flex; display: flex;
gap: 12px; gap: 12px;
margin-top: 28px; margin-top: 32px;
flex-wrap: wrap; flex-wrap: wrap;
} }
.btn { .btn {
display: inline-block; display: inline-block;
padding: 10px 22px; padding: 11px 24px;
border-radius: var(--radius); border-radius: var(--radius);
font-size: 15px; font-size: 15px;
font-weight: 600; font-weight: 600;
text-decoration: none; text-decoration: none;
border: 1px solid transparent; border: 1px solid transparent;
transition: background 0.2s ease, border-color 0.2s ease, transform 0.15s ease, box-shadow 0.2s ease;
} }
.btn:active { transform: translateY(1px); } .btn:active { transform: translateY(1px) scale(0.99); }
.btn-primary { .btn-primary {
background: var(--accent); background: var(--accent);
color: #ffffff; color: var(--btn-fg);
box-shadow: 0 10px 28px -10px rgba(63, 185, 80, 0.5);
} }
.btn-primary:hover { background: var(--accent-hover); } .btn-primary:hover { background: var(--accent-hover); }
.btn-ghost { .btn-ghost {
color: var(--text); color: var(--text);
border-color: var(--border); border-color: var(--border-strong);
background: var(--bg); background: var(--overlay-1);
} }
.btn-ghost:hover { border-color: var(--accent); background: var(--overlay-2); }
.btn-ghost:hover { border-color: var(--text-2); }
.hero-note { .hero-note {
margin-top: 14px; margin-top: 16px;
font-size: 13px; font-size: 13px;
color: var(--text-2); color: var(--text-2);
} }
.hero-note .mono { color: var(--text); }
.hero-shot { .hero-shot { min-width: 0; }
min-width: 0;
}
.hero-shot img { .hero-shot img {
display: block; display: block;
width: 100%; width: 100%;
height: auto; height: auto;
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: var(--radius); border-radius: 12px;
box-shadow: 0 8px 24px rgba(28, 33, 40, 0.10); box-shadow: var(--shadow-shot);
} }
.hero-shot figcaption { .hero-shot figcaption {
margin-top: 10px; margin-top: 12px;
font-size: 13px; font-size: 13px;
color: var(--text-2); color: var(--text-2);
text-align: center; text-align: center;
} }
.spec-strip { .spec-strip {
position: relative;
z-index: 1;
display: flex; display: flex;
gap: 32px; gap: 40px;
flex-wrap: wrap; flex-wrap: wrap;
padding-top: 40px; padding: 40px 0 48px;
padding-bottom: 40px; margin-top: 48px;
font-size: 14px; font-size: 14px;
color: var(--text-2); color: var(--text-2);
border-top: 1px solid var(--border);
} }
.spec-strip strong { color: var(--text); font-weight: 600; } .spec-strip strong { color: var(--text); font-weight: 600; }
.spec-strip .mono { font-size: 13px; }
/* ---- features ---- */ /* ---- shared section chrome ---- */
.features { padding: 72px 0; } section { position: relative; }
.section-head {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 32px;
}
.section-eyebrow {
font-family: var(--mono);
font-size: 12.5px;
font-weight: 600;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--accent-text);
margin-bottom: 12px;
}
.features h2, .download h2, .changelog h2 { .features h2, .download h2, .changelog h2 {
font-size: 28px; font-size: clamp(26px, 3vw, 34px);
font-weight: 650; font-weight: 700;
letter-spacing: -0.01em; letter-spacing: -0.025em;
} }
.section-sub { .section-sub {
margin-top: 10px; margin-top: 14px;
color: var(--text-2); color: var(--text-2);
max-width: 60ch; max-width: 62ch;
font-size: 16.5px;
} }
.grid-bg::before {
content: "";
position: absolute;
inset: 0;
background-image:
linear-gradient(var(--grid-line-soft) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-line-soft) 1px, transparent 1px);
background-size: 64px 64px;
pointer-events: none;
}
.features > .container { position: relative; z-index: 1; }
/* ---- capability track (horizontal scroll) ---- */
.features { padding: 92px 0 100px; background: var(--bg); }
.cap-controls { display: flex; gap: 8px; flex-shrink: 0; }
.track-btn {
width: 38px;
height: 38px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 50%;
border: 1px solid var(--border-strong);
background: var(--surface);
color: var(--text);
cursor: pointer;
transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease, transform 0.15s ease;
}
.track-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent-text); transform: translateY(-1px); }
.track-btn:active:not(:disabled) { transform: translateY(0) scale(0.97); }
.track-btn:disabled { opacity: 0.35; cursor: default; }
.cap-track {
display: flex;
gap: 20px;
margin-top: 44px;
padding: 4px 28px 22px;
margin-left: -28px;
margin-right: -28px;
overflow-x: auto;
overscroll-behavior-x: contain;
scroll-snap-type: x proximity;
scrollbar-width: none;
cursor: grab;
mask-image: linear-gradient(90deg, transparent 0, #000 28px, #000 calc(100% - 28px), transparent 100%);
}
.cap-track::-webkit-scrollbar { display: none; }
.cap-track.is-dragging { cursor: grabbing; scroll-snap-type: none; }
.cap-card {
flex: 0 0 clamp(292px, 30vw, 366px);
scroll-snap-align: start;
display: flex;
flex-direction: column;
gap: 14px;
padding: 26px 24px 28px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
transition: border-color 0.25s ease, transform 0.25s ease, box-shadow 0.25s ease, background 0.25s ease;
}
.cap-card:hover {
border-color: var(--accent);
background: var(--surface-2);
transform: translateY(-4px);
box-shadow: var(--shadow-lift);
}
.cap-num {
font-family: var(--mono);
font-size: 12.5px;
font-weight: 500;
color: var(--accent-text);
letter-spacing: 0.1em;
}
.cap-card h3 { font-size: 19px; letter-spacing: -0.01em; }
.cap-card ul { list-style: none; display: grid; gap: 9px; margin-top: 2px; }
.cap-card li {
position: relative;
padding-left: 16px;
font-size: 14.5px;
line-height: 1.55;
color: var(--text-2);
}
.cap-card li::before {
content: "";
position: absolute;
left: 0;
top: 0.66em;
width: 5px;
height: 5px;
border-radius: 1px;
background: var(--accent);
}
.cap-hint {
margin-top: 18px;
font-family: var(--mono);
font-size: 12.5px;
color: var(--text-3);
}
/* ---- media rows (screenshot + copy) ---- */
.feature-row { .feature-row {
display: grid; display: grid;
grid-template-columns: 1fr 1fr;
gap: 64px;
margin-top: 48px;
}
.media-row {
grid-template-columns: 7fr 5fr; grid-template-columns: 7fr 5fr;
gap: 60px;
align-items: center; align-items: center;
gap: 56px; margin-top: 84px;
}
.feature-row:first-of-type { margin-top: 76px; }
.feature-copy { min-width: 0; }
.feature-copy h3 { font-size: 21px; letter-spacing: -0.015em; margin-bottom: 14px; }
.eyebrow {
font-family: var(--mono);
font-size: 12.5px;
font-weight: 600;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--accent-text);
margin-bottom: 12px;
} }
.feature-copy h3 { .feature-copy ul { list-style: none; display: grid; gap: 10px; }
font-size: 20px;
font-weight: 650;
margin-bottom: 16px;
}
.feature-copy ul {
list-style: none;
display: grid;
gap: 10px;
}
.feature-copy li { .feature-copy li {
padding-left: 18px;
position: relative; position: relative;
padding-left: 18px;
color: var(--text-2); color: var(--text-2);
font-size: 15px; font-size: 15px;
} }
@@ -229,24 +590,38 @@ body {
position: absolute; position: absolute;
left: 0; left: 0;
top: 0.62em; top: 0.62em;
width: 6px; width: 5px;
height: 2px; height: 5px;
border-radius: 1px;
background: var(--accent); background: var(--accent);
} }
.feature-shot { min-width: 0; }
.feature-shot img { .feature-shot img {
display: block; display: block;
width: 100%; width: 100%;
height: auto; height: auto;
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: var(--radius); border-radius: 12px;
box-shadow: 0 6px 18px rgba(28, 33, 40, 0.08); box-shadow: var(--shadow-shot);
background: var(--surface);
}
.feature-shot img + img { margin-top: 16px; }
.feature-shot img.narrow { width: 78%; }
.feature-shot figcaption {
margin-top: 12px;
font-size: 13px;
color: var(--text-2);
text-align: center;
} }
/* ---- download ---- */ /* ---- download ---- */
.download { .download {
padding: 72px 0; padding: 92px 0 100px;
background: var(--bg-soft); background: var(--bg-alt);
border-top: 1px solid var(--border); border-top: 1px solid var(--border);
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
} }
@@ -254,84 +629,94 @@ body {
.dl-table { .dl-table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
margin-top: 40px; margin-top: 44px;
background: var(--bg); background: var(--surface);
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: var(--radius); border-radius: var(--radius-lg);
overflow: hidden; overflow: hidden;
font-size: 14.5px; font-size: 14.5px;
} }
.dl-table th, .dl-table td { .dl-table th, .dl-table td {
text-align: left; text-align: left;
padding: 14px 18px; padding: 15px 20px;
vertical-align: top; vertical-align: top;
} }
.dl-table thead th { .dl-table thead th {
background: var(--bg-soft); background: var(--accent-soft);
color: var(--text-2); color: var(--text-2);
font-weight: 600; font-family: var(--mono);
font-size: 13px; font-weight: 500;
font-size: 12.5px;
letter-spacing: 0.06em;
} }
.dl-table tbody td { color: var(--text-2); }
.dl-table tbody tr + tr td { border-top: 1px solid var(--border); } .dl-table tbody tr + tr td { border-top: 1px solid var(--border); }
.dl-table tbody tr:hover td { background: var(--overlay-row); }
.dl-table a { color: var(--accent); text-decoration: none; font-weight: 600; } .dl-table a {
.dl-table a:hover { text-decoration: underline; } color: var(--accent-text);
text-decoration: none;
font-weight: 500;
border-bottom: 1px solid transparent;
}
.dl-table a:hover { border-bottom-color: currentColor; }
.dl-table .sep { color: var(--border); margin: 0 8px; } .dl-table .sep { color: var(--border-strong); margin: 0 8px; }
.dl-table .mono { color: var(--text); }
.update-note { margin-top: 40px; max-width: 72ch; } .update-note { margin-top: 44px; max-width: 74ch; }
.update-note h3 { font-size: 16px; margin-bottom: 8px; } .update-note h3 { font-size: 16px; margin-bottom: 8px; }
.update-note p { color: var(--text-2); font-size: 14.5px; } .update-note p { color: var(--text-2); font-size: 14.5px; }
/* ---- changelog ---- */ /* ---- changelog ---- */
.changelog { padding: 72px 0; } .changelog { padding: 92px 0 100px; background: var(--bg); }
.changelog-list { .changelog-list {
display: grid; display: grid;
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
gap: 48px; gap: 40px;
margin-top: 40px; margin-top: 44px;
} }
.changelog-list article {
border-top: 2px solid var(--border);
padding-top: 18px;
transition: border-color 0.25s ease;
}
.changelog-list article:hover { border-top-color: var(--accent); }
.changelog-list article h3 { .changelog-list article h3 {
font-size: 15px; font-family: var(--mono);
font-size: 14.5px;
font-weight: 600; font-weight: 600;
margin-bottom: 12px; margin-bottom: 14px;
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
} }
.changelog-list article h3 .mono { .changelog-list .rel-date { color: var(--text-3); font-weight: 400; font-size: 12.5px; }
color: var(--text);
margin-right: 8px;
}
.changelog-list ul { .changelog-list ul { list-style: none; display: grid; gap: 8px; }
list-style: none;
display: grid;
gap: 8px;
}
.changelog-list li { .changelog-list li { font-size: 14px; color: var(--text-2); }
font-size: 14px;
color: var(--text-2);
}
.changelog-more { .changelog-more { margin-top: 44px; font-size: 14px; color: var(--text-2); }
margin-top: 40px;
font-size: 14px;
color: var(--text-2);
}
.changelog-more a { color: var(--accent); } .changelog-more a { color: var(--accent-text); font-weight: 500; text-decoration: none; }
.changelog-more a:hover { border-bottom: 1px solid currentColor; }
/* ---- footer ---- */ /* ---- footer ---- */
.site-footer { .site-footer {
background: var(--bg-alt);
border-top: 1px solid var(--border); border-top: 1px solid var(--border);
padding: 28px 0; padding: 34px 0;
font-size: 13.5px; font-size: 13.5px;
color: var(--text-2); color: var(--text-2);
} }
@@ -341,24 +726,47 @@ body {
justify-content: space-between; justify-content: space-between;
gap: 16px; gap: 16px;
flex-wrap: wrap; flex-wrap: wrap;
align-items: center;
}
.footer-inner .mono { font-size: 12.5px; color: var(--text-3); }
/* ---- scroll reveal ---- */
@media (prefers-reduced-motion: no-preference) {
.reveal {
opacity: 0;
transform: translateY(14px);
transition: opacity 0.6s ease, transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
.reveal.is-in { opacity: 1; transform: none; }
} }
/* ---- responsive ---- */ /* ---- responsive ---- */
@media (max-width: 960px) { @media (max-width: 1024px) {
.feature-row { grid-template-columns: 1fr; gap: 36px; }
.feature-row .feature-shot { order: -1; }
.feature-shot img.narrow { width: 100%; }
}
@media (max-width: 900px) {
.hero-grid { grid-template-columns: 1fr; gap: 40px; } .hero-grid { grid-template-columns: 1fr; gap: 40px; }
.hero { padding-top: 48px; } .hero { padding-top: 60px; }
.feature-row, .media-row { grid-template-columns: 1fr; gap: 32px; } .section-head { flex-direction: column; align-items: flex-start; gap: 20px; }
.media-row .feature-shot { order: -1; } .changelog-list { grid-template-columns: 1fr; gap: 28px; }
.changelog-list { grid-template-columns: 1fr; gap: 32px; }
} }
@media (max-width: 640px) { @media (max-width: 640px) {
.nav-links { gap: 16px; } .container { padding: 0 20px; }
.nav-links a { font-size: 14px; } .nav-inner { height: 56px; }
.nav-links { gap: 0; }
.nav-links a { font-size: 13.5px; padding: 6px 8px; }
.hero h1 { font-size: 30px; } .hero h1 { font-size: 30px; }
.spec-strip { gap: 16px 24px; }
.cap-track { padding: 4px 20px 20px; margin-left: -20px; margin-right: -20px; }
.dl-table { display: block; overflow-x: auto; white-space: nowrap; } .dl-table { display: block; overflow-x: auto; white-space: nowrap; }
} }
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; } html { scroll-behavior: auto; }
*, *::before, *::after { transition: none !important; }
} }
+294 -46
View File
@@ -6,32 +6,61 @@
<title>OpenTerminal - 本地终端 / SSH / SFTP 桌面终端,开源 Windows 终端工具</title> <title>OpenTerminal - 本地终端 / SSH / SFTP 桌面终端,开源 Windows 终端工具</title>
<meta name="description" content="OpenTerminal 是开源的 Windows 桌面终端:本地终端、SSH 远程会话、SFTP 文件管理、分屏与广播输入、主题定制。MIT 协议,支持自动更新。" /> <meta name="description" content="OpenTerminal 是开源的 Windows 桌面终端:本地终端、SSH 远程会话、SFTP 文件管理、分屏与广播输入、主题定制。MIT 协议,支持自动更新。" />
<link rel="icon" type="image/png" href="assets/icon.png" /> <link rel="icon" type="image/png" href="assets/icon.png" />
<link rel="preload" href="assets/fonts/space-grotesk-600.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="assets/fonts/jetbrains-mono-500.woff2" as="font" type="font/woff2" crossorigin />
<meta property="og:title" content="OpenTerminal - 本地终端 / SSH / SFTP 桌面终端" />
<meta property="og:description" content="开源的 Windows 桌面终端:本地终端、SSH 远程会话、SFTP 文件管理、分屏与广播输入。MIT 协议,支持自动更新。" />
<meta property="og:image" content="assets/shot-terminal.jpg" />
<meta name="theme-color" content="#0b0e12" />
<link rel="stylesheet" href="assets/style.css" /> <link rel="stylesheet" href="assets/style.css" />
<script>
// 主题必须在首帧前定好,避免闪白/闪黑;默认深色,只有用户显式切过才用保存值
(function () {
var theme = 'dark';
try {
if (localStorage.getItem('openterminal.theme') === 'light') theme = 'light';
} catch (e) {}
document.documentElement.dataset.theme = theme;
})();
</script>
</head> </head>
<body> <body>
<a class="skip-link" href="#main">跳到主要内容</a>
<header class="site-nav"> <header class="site-nav">
<div class="container nav-inner"> <div class="container nav-inner">
<a class="brand" href="#top"> <a class="brand" href="#top">
<img src="assets/icon.png" alt="OpenTerminal 图标" width="24" height="24" /> <img src="assets/icon.png" alt="OpenTerminal 图标" width="24" height="24" />
<span>OpenTerminal</span> <span>OpenTerminal</span>
</a> </a>
<nav class="nav-links"> <div class="nav-right">
<a href="#features">功能</a> <nav class="nav-links" aria-label="站内导航">
<a href="#download">下载</a> <a href="#features">功能</a>
<a href="#changelog">更新日志</a> <a href="#download">下载</a>
<a href="https://github.com/billowliu2/OpenTerminal" target="_blank" rel="noopener">GitHub</a> <a href="#changelog">更新日志</a>
</nav> <a href="https://github.com/billowliu2/OpenTerminal" target="_blank" rel="noopener">GitHub</a>
</nav>
<button class="theme-toggle" id="theme-toggle" type="button" aria-label="切换明暗主题" title="切换明暗主题">
<svg class="icon-moon" width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z" />
</svg>
<svg class="icon-sun" width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<circle cx="12" cy="12" r="4" />
<path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4" />
</svg>
</button>
</div>
</div> </div>
</header> </header>
<main id="top"> <main id="main">
<section class="hero"> <section class="hero" id="top">
<div class="container hero-grid"> <div class="container hero-grid">
<div class="hero-copy"> <div class="hero-copy">
<p class="hero-kicker">开源桌面终端 · MIT 协议</p> <p class="hero-kicker">开源桌面终端 · MIT 协议</p>
<h1>本地终端、SSH 与 SFTP<br />装进同一个桌面应用</h1> <h1>本地终端、SSH 与 SFTP<br />装进<span class="accent">同一个</span>桌面应用</h1>
<p class="hero-sub"> <p class="hero-sub">
OpenTerminal 面向开发和运维场景:WebGL 加速的终端渲染、可分屏的 SSH 会话、 OpenTerminal 面向开发和运维场景:WebGL 加速的终端渲染、可分屏的 SSH 会话、
内置 SFTP 文件管理与广播输入。安装即用,支持自动更新。 内置 SFTP 文件管理与广播输入。安装即用,支持自动更新。
@@ -44,7 +73,7 @@
查看源码 查看源码
</a> </a>
</div> </div>
<p class="hero-note">当前版本 v1.0.22 · NSIS exe 安装包,支持应用内自动更新。</p> <p class="hero-note">当前版本 <span class="mono">v1.0.22</span> · NSIS exe 安装包,支持应用内自动更新。</p>
</div> </div>
<figure class="hero-shot"> <figure class="hero-shot">
<img src="assets/shot-terminal.jpg" alt="OpenTerminal 主界面:三路分屏并行运行 Kimi Code、Claude Code 与 Codex" width="1739" height="870" /> <img src="assets/shot-terminal.jpg" alt="OpenTerminal 主界面:三路分屏并行运行 Kimi Code、Claude Code 与 Codex" width="1739" height="870" />
@@ -52,46 +81,131 @@
</figure> </figure>
</div> </div>
<div class="container spec-strip"> <div class="container spec-strip">
<span>当前版本 <strong class="mono">1.0.22</strong></span> <span>当前版本 <strong class="mono">v1.0.22</strong></span>
<span>Windows 10 / 11 · x64</span> <span>Windows 10 / 11 · x64</span>
<span>MIT 开源协议</span> <span>MIT 开源协议</span>
<span>exe 支持应用内自动更新</span> <span>exe 支持应用内自动更新</span>
</div> </div>
</section> </section>
<section class="features" id="features"> <section class="features grid-bg" id="features">
<div class="container"> <div class="container">
<h2>核心能力</h2> <div class="section-head">
<p class="section-sub">按使用场景划分的三组功能,全部能力见仓库 README。</p> <div>
<p class="section-eyebrow">Capabilities</p>
<div class="feature-row"> <h2>核心能力</h2>
<div class="feature-copy"> <p class="section-sub">按使用场景划分的能力组,左右拖动或用箭头浏览;完整清单见仓库 README。</p>
<h3>终端核心</h3>
<ul>
<li>node-pty 本地会话,自动探测 PowerShell / zsh / bash</li>
<li>xterm.js WebGL 渲染,声明 256 色与真彩能力,异常时自动降级</li>
<li>内嵌搜索(Ctrl+F)、命令历史、粘贴风险确认</li>
<li>关键词高亮:22 条内置规则(错误状态、路径、IP、URL 等),支持自定义正则与三档强度</li>
<li>字体、字重、字距、行高、光标样式实时生效</li>
</ul>
</div> </div>
<div class="feature-copy"> <div class="cap-controls">
<h3>SSH 与 SFTP</h3> <button class="track-btn" type="button" data-dir="-1" aria-label="向左浏览能力卡片">
<ul> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 18l-6-6 6-6" /></svg>
<li>ssh2 实现:密码、私钥(文件或粘贴内容)、SSH Agent 三种认证</li> </button>
<li>多路分屏与广播输入,一次操作下发到多台主机</li> <button class="track-btn" type="button" data-dir="1" aria-label="向右浏览能力卡片">
<li>SFTP 文件面板:上传下载、同名覆盖确认、chmod / chown 权限编辑</li> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 6l6 6-6 6" /></svg>
<li>ZMODEM 传输支持;主机指纹校验与首次连接确认</li> </button>
<li>目录记忆:OSC 7 上报工作目录,新会话按上次位置恢复</li>
</ul>
</div> </div>
</div> </div>
<div class="feature-row media-row"> <div class="cap-track" id="cap-track" tabindex="0" aria-label="核心能力,可横向滚动">
<figure class="feature-shot"> <article class="cap-card">
<p class="cap-num">01</p>
<h3>终端核心</h3>
<ul>
<li>node-pty 本地会话,自动探测 PowerShell / zsh / bash</li>
<li>xterm.js WebGL 渲染,声明 256 色与真彩,异常自动降级</li>
<li>内嵌搜索(Ctrl+F)、命令历史、粘贴风险确认</li>
<li>关键词高亮 22 条内置规则,支持自定义正则与三档强度</li>
<li>字体、字重、字距、行高、光标样式实时生效</li>
</ul>
</article>
<article class="cap-card">
<p class="cap-num">02</p>
<h3>SSH 会话</h3>
<ul>
<li>ssh2 实现:密码、私钥(文件或粘贴内容)、SSH Agent</li>
<li>多路分屏与广播输入,一次操作下发到多台主机</li>
<li>主机指纹校验、首次连接确认、变更告警</li>
<li>ZMODEM 传输支持(sz / rz)</li>
<li>OSC 7 上报工作目录,新会话按上次位置恢复</li>
</ul>
</article>
<article class="cap-card">
<p class="cap-num">03</p>
<h3>SFTP 文件面板</h3>
<ul>
<li>列表头列分明:名称、大小、修改时间、权限、用户/组</li>
<li>点击列头排序;文件名解析为真实用户与组名</li>
<li>字体缩放 80%–180%,Ctrl+滚轮或工具栏按钮</li>
<li>上传下载、同名覆盖确认、chmod / chown 编辑</li>
<li>大目录虚拟滚动,上万条目不卡窗口</li>
</ul>
</article>
<article class="cap-card">
<p class="cap-num">04</p>
<h3>工作区与布局</h3>
<ul>
<li>dockview 分屏:任意拆分、拖拽、合并与浮动</li>
<li>布局模板保存与一键套用</li>
<li>启动恢复上次会话布局</li>
<li>界面缩放 50% 至 150%</li>
</ul>
</article>
<article class="cap-card">
<p class="cap-num">05</p>
<h3>主题与外观</h3>
<ul>
<li>主题编辑器:22 个颜色位逐项调整,实时预览</li>
<li>内置 11 套主题,跟随系统明暗自动切换</li>
<li>自定义背景图片、不透明度与压暗层</li>
<li>标签强调色、高亮配色跟随主题</li>
</ul>
</article>
<article class="cap-card">
<p class="cap-num">06</p>
<h3>效率工具</h3>
<ul>
<li>命令库:保存常用命令片段,跨会话发送到指定终端</li>
<li>高亮规则集按主机绑定,不同服务器不同配色</li>
<li>规则导入导出、实时预览与命中统计</li>
<li>会话快照与恢复、终端标签自动命名</li>
</ul>
</article>
<article class="cap-card">
<p class="cap-num">07</p>
<h3>系统监控</h3>
<ul>
<li>CPU、负载、内存、磁盘、网络实时面板</li>
<li>每核占用与磁盘分区明细</li>
<li>运行时长与网络速率曲线</li>
<li>随会话打开,不额外占用通道</li>
</ul>
</article>
<article class="cap-card">
<p class="cap-num">08</p>
<h3>锁屏与安全</h3>
<ul>
<li>Ctrl+L 一键锁屏,支持闲置自动锁定与启动即锁</li>
<li>密码经 scrypt 加盐哈希存储,不落盘明文</li>
<li>输错触发递增冷却;锁定期间禁用菜单与刷新快捷键</li>
<li>锁屏只是遮罩:背后会话与传输不中断</li>
</ul>
</article>
</div>
<p class="cap-hint">← 拖动浏览 · 共 8 组能力</p>
<div class="feature-row">
<figure class="feature-shot reveal">
<img src="assets/shot-agents.png" alt="OpenTerminal 多路分屏运行 Kimi Code、Claude Code、Codex 等 AI 编程 Agent" width="1296" height="870" /> <img src="assets/shot-agents.png" alt="OpenTerminal 多路分屏运行 Kimi Code、Claude Code、Codex 等 AI 编程 Agent" width="1296" height="870" />
</figure> </figure>
<div class="feature-copy"> <div class="feature-copy reveal">
<p class="eyebrow">Workbench</p>
<h3>AI Agent 工作台</h3> <h3>AI Agent 工作台</h3>
<ul> <ul>
<li>多路分屏并行运行 Kimi Code、Claude Code、Codex 等 AI 编程工具</li> <li>多路分屏并行运行 Kimi Code、Claude Code、Codex 等 AI 编程工具</li>
@@ -102,11 +216,31 @@
</div> </div>
</div> </div>
<div class="feature-row media-row"> <div class="feature-row">
<figure class="feature-shot"> <figure class="feature-shot reveal">
<img src="assets/shot-sftp.png" alt="OpenTerminal SFTP 文件面板:列表头、可排序列、完整修改时间与用户/组" width="1282" height="801" />
<img class="narrow" src="assets/shot-sftp-menu.png" alt="OpenTerminal 在文件列表空白处右键弹出同款文件菜单" width="1282" height="801" />
<figcaption>列表空白处右键同样是完整文件菜单,行内右键则作用于所选条目</figcaption>
</figure>
<div class="feature-copy reveal">
<p class="eyebrow">SFTP</p>
<h3>远程文件面板</h3>
<ul>
<li>列表头列分明:名称、大小、修改时间、权限、用户/组,点击列头排序</li>
<li>文件名解析为真实用户与组名(root/root、eveuser/eveuser),不再只有数字</li>
<li>字体缩放 80%–180%:Ctrl+滚轮或工具栏按钮,行高与字号联动并记忆</li>
<li>上传 / 下载、同名覆盖确认、chmod 权限与 chown 属主编辑</li>
<li>大目录虚拟滚动,上万条目不卡窗口</li>
</ul>
</div>
</div>
<div class="feature-row">
<figure class="feature-shot reveal">
<img src="assets/shot-themes.jpg" alt="OpenTerminal 主题设置:背景图片、不透明度、压暗层与主题画廊" width="1739" height="870" /> <img src="assets/shot-themes.jpg" alt="OpenTerminal 主题设置:背景图片、不透明度、压暗层与主题画廊" width="1739" height="870" />
</figure> </figure>
<div class="feature-copy"> <div class="feature-copy reveal">
<p class="eyebrow">Customize</p>
<h3>定制与效率</h3> <h3>定制与效率</h3>
<ul> <ul>
<li>主题编辑器:22 个颜色位逐项调整,实时预览;支持自定义背景图片、不透明度与压暗层</li> <li>主题编辑器:22 个颜色位逐项调整,实时预览;支持自定义背景图片、不透明度与压暗层</li>
@@ -118,11 +252,12 @@
</div> </div>
</div> </div>
<div class="feature-row media-row"> <div class="feature-row">
<figure class="feature-shot"> <figure class="feature-shot reveal">
<img src="assets/shot-lock.png" alt="OpenTerminal 锁屏界面:不透明遮罩与密码解锁" width="1739" height="870" /> <img src="assets/shot-lock.png" alt="OpenTerminal 锁屏界面:不透明遮罩与密码解锁" width="1739" height="870" />
</figure> </figure>
<div class="feature-copy"> <div class="feature-copy reveal">
<p class="eyebrow">Security</p>
<h3>锁屏与安全</h3> <h3>锁屏与安全</h3>
<ul> <ul>
<li>Ctrl+L 一键锁屏,支持闲置自动锁定与启动即锁</li> <li>Ctrl+L 一键锁屏,支持闲置自动锁定与启动即锁</li>
@@ -137,6 +272,7 @@
<section class="download" id="download"> <section class="download" id="download">
<div class="container"> <div class="container">
<p class="section-eyebrow">Download</p>
<h2>下载</h2> <h2>下载</h2>
<p class="section-sub"> <p class="section-sub">
从 GitHub Releases 下载;全部历史版本在同一页面。 从 GitHub Releases 下载;全部历史版本在同一页面。
@@ -179,10 +315,11 @@
<section class="changelog" id="changelog"> <section class="changelog" id="changelog">
<div class="container"> <div class="container">
<p class="section-eyebrow">Changelog</p>
<h2>近期更新</h2> <h2>近期更新</h2>
<div class="changelog-list"> <div class="changelog-list">
<article> <article>
<h3><span class="mono">1.0.22</span> 2026-10-08</h3> <h3><span class="mono">1.0.22</span><span class="rel-date">2026-10-08</span></h3>
<ul> <ul>
<li>修复中文输入法候选窗不跟随光标(v1.0.21 回归)</li> <li>修复中文输入法候选窗不跟随光标(v1.0.21 回归)</li>
<li>不再提供 MSI 安装包,统一使用 NSIS exe</li> <li>不再提供 MSI 安装包,统一使用 NSIS exe</li>
@@ -190,7 +327,7 @@
</ul> </ul>
</article> </article>
<article> <article>
<h3><span class="mono">1.0.21</span> 2026-10-07</h3> <h3><span class="mono">1.0.21</span><span class="rel-date">2026-10-07</span></h3>
<ul> <ul>
<li>背景图体验:任意主题下调透明度均为压暗、文字自动提亮到 WCAG AA、新增背景压暗滑杆</li> <li>背景图体验:任意主题下调透明度均为压暗、文字自动提亮到 WCAG AA、新增背景压暗滑杆</li>
<li>性能:设置热更不再触发全窗格重排,SFTP 大目录虚拟滚动,面板独立错误边界</li> <li>性能:设置热更不再触发全窗格重排,SFTP 大目录虚拟滚动,面板独立错误边界</li>
@@ -198,7 +335,7 @@
</ul> </ul>
</article> </article>
<article> <article>
<h3><span class="mono">1.0.20</span> 2026-10-01</h3> <h3><span class="mono">1.0.20</span><span class="rel-date">2026-10-01</span></h3>
<ul> <ul>
<li>修复中文输入法候选窗不跟随光标</li> <li>修复中文输入法候选窗不跟随光标</li>
<li>锁屏加固:锁定期间移除应用菜单,Ctrl+L 按键判定重构并补测试</li> <li>锁屏加固:锁定期间移除应用菜单,Ctrl+L 按键判定重构并补测试</li>
@@ -220,5 +357,116 @@
</div> </div>
</footer> </footer>
<script>
(function () {
var reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
// 滚动进场
var revealables = document.querySelectorAll('.reveal');
if (reduce || !('IntersectionObserver' in window)) {
revealables.forEach(function (el) { el.classList.add('is-in'); });
} else {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) { e.target.classList.add('is-in'); io.unobserve(e.target); }
});
}, { rootMargin: '0px 0px -8% 0px', threshold: 0.05 });
revealables.forEach(function (el) { io.observe(el); });
}
// 导航当前位置
var links = Array.prototype.slice.call(document.querySelectorAll('.nav-links a[href^="#"]'));
var sections = links
.map(function (a) { return document.querySelector(a.getAttribute('href')); })
.filter(Boolean);
function syncNav() {
var pos = scrollY + 130;
var current = null;
sections.forEach(function (s) { if (s.offsetTop <= pos) current = s; });
links.forEach(function (a) {
a.classList.toggle('is-current', !!current && a.getAttribute('href') === '#' + current.id);
});
}
addEventListener('scroll', syncNav, { passive: true });
syncNav();
// 能力卡片横向轨道:箭头翻页 + 按住拖动
var track = document.getElementById('cap-track');
if (!track) return;
var buttons = document.querySelectorAll('.track-btn');
function step() {
var card = track.querySelector('.cap-card');
return card ? card.getBoundingClientRect().width + 20 : track.clientWidth * 0.8;
}
function syncButtons() {
var max = track.scrollWidth - track.clientWidth - 1;
buttons.forEach(function (b) {
var dir = Number(b.getAttribute('data-dir'));
b.disabled = dir < 0 ? track.scrollLeft <= 0 : track.scrollLeft >= max;
});
}
buttons.forEach(function (b) {
b.addEventListener('click', function () {
track.scrollBy({ left: Number(b.getAttribute('data-dir')) * step(), behavior: reduce ? 'auto' : 'smooth' });
});
});
track.addEventListener('scroll', syncButtons, { passive: true });
addEventListener('resize', syncButtons);
syncButtons();
var dragging = false;
var startX = 0;
var startScroll = 0;
track.addEventListener('pointerdown', function (e) {
if (e.pointerType === 'mouse' && e.button !== 0) return;
dragging = true;
startX = e.clientX;
startScroll = track.scrollLeft;
track.classList.add('is-dragging');
track.setPointerCapture(e.pointerId);
});
track.addEventListener('pointermove', function (e) {
if (!dragging) return;
var delta = e.clientX - startX;
if (Math.abs(delta) > 3) e.preventDefault();
track.scrollLeft = startScroll - delta;
});
function endDrag(e) {
if (!dragging) return;
dragging = false;
track.classList.remove('is-dragging');
if (e && e.pointerId !== undefined && track.hasPointerCapture(e.pointerId)) {
track.releasePointerCapture(e.pointerId);
}
syncButtons();
}
track.addEventListener('pointerup', endDrag);
track.addEventListener('pointercancel', endDrag);
track.addEventListener('dragstart', function (e) { e.preventDefault(); });
track.addEventListener('keydown', function (e) {
if (e.key === 'ArrowRight') { track.scrollBy({ left: step(), behavior: 'smooth' }); e.preventDefault(); }
if (e.key === 'ArrowLeft') { track.scrollBy({ left: -step(), behavior: 'smooth' }); e.preventDefault(); }
});
// 明暗主题切换
var toggle = document.getElementById('theme-toggle');
var meta = document.querySelector('meta[name="theme-color"]');
function applyTheme(theme) {
document.documentElement.dataset.theme = theme;
if (meta) meta.setAttribute('content', theme === 'light' ? '#fafbf9' : '#0b0e12');
if (toggle) toggle.setAttribute('aria-label', theme === 'light' ? '切换到深色主题' : '切换到浅色主题');
}
if (toggle) {
toggle.addEventListener('click', function () {
var next = document.documentElement.dataset.theme === 'light' ? 'dark' : 'light';
applyTheme(next);
try { localStorage.setItem('openterminal.theme', next); } catch (e) {}
});
}
applyTheme(document.documentElement.dataset.theme === 'light' ? 'light' : 'dark');
})();
</script>
</body> </body>
</html> </html>