a57bba9ea0
Replace the bordered hero screenshot card with an elegant nested device mockup cluster; wire iPad home shots into the lang/theme matrix.
1373 lines
52 KiB
HTML
1373 lines
52 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-Hans" data-theme="system">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>OSGKeyboard — 开口即文字 · iOS & Mac 语音输入</title>
|
|
<meta name="description" content="免费开源的跨端语音输入。本地识别、BYOK 润色、个性词库。iPhone、iPad、Mac — 开口即文字。">
|
|
<meta name="keywords" content="voice keyboard, dictation, BYOK, open source, privacy, 语音输入, 听写, OSGKeyboard">
|
|
<meta name="theme-color" content="#0a0a0b" media="(prefers-color-scheme: dark)">
|
|
<meta name="theme-color" content="#f7f7f5" media="(prefers-color-scheme: light)">
|
|
<link rel="canonical" href="https://hkgood.github.io/OSGKeyboard/">
|
|
<link rel="icon" href="assets/app-icon.png" type="image/png">
|
|
<link rel="apple-touch-icon" href="assets/app-icon.png">
|
|
<meta property="og:type" content="website">
|
|
<meta property="og:title" content="OSGKeyboard — 开口即文字">
|
|
<meta property="og:description" content="免费 · 跨端 · 开源 · 隐私优先 · BYOK。语音留在本机,AI 用你自己的 Key。">
|
|
<meta property="og:url" content="https://hkgood.github.io/OSGKeyboard/">
|
|
<meta property="og:image" content="https://hkgood.github.io/OSGKeyboard/assets/screenshots/zh/dark/mac-dashboard.png">
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "SoftwareApplication",
|
|
"name": "OSGKeyboard",
|
|
"operatingSystem": "iOS, iPadOS, macOS",
|
|
"applicationCategory": "UtilitiesApplication",
|
|
"url": "https://hkgood.github.io/OSGKeyboard/",
|
|
"image": "https://hkgood.github.io/OSGKeyboard/assets/app-icon.png",
|
|
"description": "Free open-source voice input for iPhone, iPad, and Mac. On-device recognition, BYOK AI polish, personal dictionary.",
|
|
"softwareVersion": "0.5.4",
|
|
"isAccessibleForFree": true,
|
|
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" },
|
|
"author": { "@type": "Organization", "name": "OSGKeyboard" }
|
|
}
|
|
</script>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700&family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,300,0,0&display=swap" rel="stylesheet">
|
|
<style>
|
|
:root {
|
|
--bg: #f7f7f5;
|
|
--bg-elevated: #ffffff;
|
|
--bg-soft: #efefec;
|
|
--text: #121214;
|
|
--text-2: #5a5a62;
|
|
--text-3: #8e8e96;
|
|
--line: rgba(18, 18, 20, 0.10);
|
|
--line-strong: rgba(18, 18, 20, 0.16);
|
|
--accent: #2f9a52;
|
|
--accent-soft: rgba(47, 154, 82, 0.12);
|
|
--accent-glow: rgba(47, 154, 82, 0.28);
|
|
--nav: rgba(247, 247, 245, 0.78);
|
|
--shadow: 0 28px 80px rgba(18, 18, 20, 0.12);
|
|
--radius: 20px;
|
|
--max: 1120px;
|
|
--font: "Outfit", -apple-system, BlinkMacSystemFont, "SF Pro Text", "PingFang SC", "Hiragino Sans GB", "Noto Sans SC", sans-serif;
|
|
}
|
|
|
|
html[data-theme="dark"] {
|
|
--bg: #0a0a0b;
|
|
--bg-elevated: #131316;
|
|
--bg-soft: #18181b;
|
|
--text: #f4f4f2;
|
|
--text-2: #a8a8b0;
|
|
--text-3: #6f6f78;
|
|
--line: rgba(255, 255, 255, 0.09);
|
|
--line-strong: rgba(255, 255, 255, 0.14);
|
|
--accent: #4cc46e;
|
|
--accent-soft: rgba(76, 196, 110, 0.14);
|
|
--accent-glow: rgba(76, 196, 110, 0.32);
|
|
--nav: rgba(10, 10, 11, 0.78);
|
|
--shadow: 0 28px 80px rgba(0, 0, 0, 0.45);
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
html[data-theme="system"] {
|
|
--bg: #0a0a0b;
|
|
--bg-elevated: #131316;
|
|
--bg-soft: #18181b;
|
|
--text: #f4f4f2;
|
|
--text-2: #a8a8b0;
|
|
--text-3: #6f6f78;
|
|
--line: rgba(255, 255, 255, 0.09);
|
|
--line-strong: rgba(255, 255, 255, 0.14);
|
|
--accent: #4cc46e;
|
|
--accent-soft: rgba(76, 196, 110, 0.14);
|
|
--accent-glow: rgba(76, 196, 110, 0.32);
|
|
--nav: rgba(10, 10, 11, 0.78);
|
|
--shadow: 0 28px 80px rgba(0, 0, 0, 0.45);
|
|
}
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
html { scroll-behavior: smooth; }
|
|
body {
|
|
margin: 0;
|
|
font-family: var(--font);
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
line-height: 1.55;
|
|
-webkit-font-smoothing: antialiased;
|
|
overflow-x: hidden;
|
|
}
|
|
a { color: inherit; text-decoration: none; }
|
|
img { max-width: 100%; display: block; }
|
|
button { font: inherit; color: inherit; }
|
|
|
|
.material-symbols-outlined {
|
|
font-variation-settings: 'FILL' 0, 'wght' 300, 'GRAD' 0, 'opsz' 24;
|
|
line-height: 1;
|
|
user-select: none;
|
|
}
|
|
|
|
.wrap {
|
|
width: min(100% - 2.5rem, var(--max));
|
|
margin-inline: auto;
|
|
}
|
|
|
|
/* —— Nav —— */
|
|
.nav {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 40;
|
|
background: var(--nav);
|
|
backdrop-filter: blur(18px) saturate(1.4);
|
|
-webkit-backdrop-filter: blur(18px) saturate(1.4);
|
|
border-bottom: 1px solid var(--line);
|
|
}
|
|
.nav-inner {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
min-height: 64px;
|
|
gap: 1rem;
|
|
}
|
|
.brand {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.7rem;
|
|
font-weight: 600;
|
|
letter-spacing: -0.02em;
|
|
}
|
|
.brand-mark {
|
|
width: 72px;
|
|
height: 22px;
|
|
color: var(--accent);
|
|
}
|
|
.brand-name { font-size: 1rem; }
|
|
.nav-actions { display: flex; align-items: center; gap: 0.35rem; }
|
|
.icon-btn, .pill-toggle button {
|
|
border: 1px solid var(--line);
|
|
background: transparent;
|
|
border-radius: 999px;
|
|
cursor: pointer;
|
|
transition: background 0.2s, border-color 0.2s, color 0.2s;
|
|
}
|
|
.icon-btn {
|
|
width: 36px;
|
|
height: 36px;
|
|
display: grid;
|
|
place-items: center;
|
|
color: var(--text-2);
|
|
}
|
|
.icon-btn:hover { color: var(--text); border-color: var(--line-strong); }
|
|
.icon-btn .material-symbols-outlined { font-size: 20px; }
|
|
.pill-toggle {
|
|
display: inline-flex;
|
|
padding: 2px;
|
|
border-radius: 999px;
|
|
border: 1px solid var(--line);
|
|
}
|
|
.pill-toggle button {
|
|
border: 0;
|
|
padding: 0.28rem 0.65rem;
|
|
font-size: 0.78rem;
|
|
font-weight: 600;
|
|
color: var(--text-3);
|
|
background: transparent;
|
|
}
|
|
.pill-toggle button.active {
|
|
background: var(--text);
|
|
color: var(--bg);
|
|
}
|
|
.nav-link {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.3rem;
|
|
padding: 0.4rem 0.7rem;
|
|
border-radius: 999px;
|
|
font-size: 0.85rem;
|
|
font-weight: 500;
|
|
color: var(--text-2);
|
|
}
|
|
.nav-link:hover { color: var(--text); }
|
|
.nav-link .material-symbols-outlined { font-size: 18px; }
|
|
|
|
/* —— Buttons —— */
|
|
.btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.45rem;
|
|
padding: 0.85rem 1.35rem;
|
|
border-radius: 999px;
|
|
font-weight: 600;
|
|
font-size: 0.95rem;
|
|
border: 1px solid transparent;
|
|
transition: transform 0.18s ease, background 0.18s, border-color 0.18s;
|
|
}
|
|
.btn:hover { transform: translateY(-1px); }
|
|
.btn .material-symbols-outlined { font-size: 20px; }
|
|
.btn-primary { background: var(--accent); color: #fff; }
|
|
.btn-ghost {
|
|
background: transparent;
|
|
border-color: var(--line-strong);
|
|
color: var(--text);
|
|
}
|
|
.btn-ghost:hover { background: var(--bg-soft); }
|
|
|
|
/* —— Hero —— */
|
|
.hero {
|
|
position: relative;
|
|
padding: 4.5rem 0 2rem;
|
|
overflow: hidden;
|
|
}
|
|
.hero::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: -20% 10% auto;
|
|
height: 70%;
|
|
background:
|
|
radial-gradient(ellipse at 30% 40%, var(--accent-soft), transparent 55%),
|
|
radial-gradient(ellipse at 75% 20%, rgba(47, 154, 82, 0.08), transparent 50%);
|
|
pointer-events: none;
|
|
z-index: 0;
|
|
}
|
|
.hero .wrap { position: relative; z-index: 1; }
|
|
.hero-copy {
|
|
max-width: 42rem;
|
|
margin: 0 auto 3rem;
|
|
text-align: center;
|
|
}
|
|
.eyebrow {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.45rem;
|
|
margin-bottom: 1.1rem;
|
|
font-size: 0.8rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.04em;
|
|
text-transform: uppercase;
|
|
color: var(--accent);
|
|
}
|
|
.eyebrow .material-symbols-outlined { font-size: 16px; }
|
|
h1 {
|
|
margin: 0 0 1rem;
|
|
font-size: clamp(2.6rem, 7vw, 4.4rem);
|
|
line-height: 1.02;
|
|
letter-spacing: -0.045em;
|
|
font-weight: 700;
|
|
}
|
|
.lead {
|
|
margin: 0 auto 1.75rem;
|
|
max-width: 34rem;
|
|
font-size: 1.12rem;
|
|
color: var(--text-2);
|
|
}
|
|
.hero-cta {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
gap: 0.7rem;
|
|
margin-bottom: 1.4rem;
|
|
}
|
|
.hero-meta {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
gap: 0.6rem 1.2rem;
|
|
font-size: 0.86rem;
|
|
color: var(--text-3);
|
|
}
|
|
.hero-meta span {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.3rem;
|
|
}
|
|
.hero-meta .material-symbols-outlined { font-size: 16px; color: var(--accent); }
|
|
|
|
/* —— Hero device family (Mac + iPad + iPhone) —— */
|
|
.hero-stage {
|
|
position: relative;
|
|
margin-top: 2.75rem;
|
|
max-width: 1080px;
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
}
|
|
.device-cluster {
|
|
position: relative;
|
|
width: 100%;
|
|
aspect-ratio: 16 / 10;
|
|
min-height: 280px;
|
|
transform: translateY(16px);
|
|
opacity: 0;
|
|
animation: rise-in 1s cubic-bezier(0.22, 1, 0.36, 1) 0.12s forwards;
|
|
--device-shell: #1c1c1e;
|
|
--device-shell-2: #2c2c2e;
|
|
--device-edge: rgba(255, 255, 255, 0.14);
|
|
--device-shadow: 0 28px 60px rgba(0, 0, 0, 0.28);
|
|
}
|
|
html[data-theme="light"] .device-cluster {
|
|
--device-shell: #d8d8dc;
|
|
--device-shell-2: #c4c4c8;
|
|
--device-edge: rgba(0, 0, 0, 0.12);
|
|
--device-shadow: 0 24px 50px rgba(18, 18, 20, 0.16);
|
|
}
|
|
@media (prefers-color-scheme: light) {
|
|
html[data-theme="system"] .device-cluster {
|
|
--device-shell: #d8d8dc;
|
|
--device-shell-2: #c4c4c8;
|
|
--device-edge: rgba(0, 0, 0, 0.12);
|
|
--device-shadow: 0 24px 50px rgba(18, 18, 20, 0.16);
|
|
}
|
|
}
|
|
|
|
.device {
|
|
position: absolute;
|
|
box-sizing: border-box;
|
|
}
|
|
.device img {
|
|
display: block;
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
object-position: top center;
|
|
}
|
|
|
|
/* MacBook — back right */
|
|
.device-mac {
|
|
right: 0;
|
|
top: 4%;
|
|
width: 72%;
|
|
z-index: 1;
|
|
filter: drop-shadow(var(--device-shadow));
|
|
}
|
|
.mac-lid {
|
|
border-radius: 10px 10px 0 0;
|
|
background: linear-gradient(180deg, var(--device-shell-2), var(--device-shell));
|
|
padding: 1.4% 1.4% 0.9%;
|
|
border: 1px solid var(--device-edge);
|
|
border-bottom: none;
|
|
}
|
|
.mac-bezel {
|
|
position: relative;
|
|
border-radius: 6px 6px 2px 2px;
|
|
overflow: hidden;
|
|
background: #000;
|
|
aspect-ratio: 16 / 10;
|
|
}
|
|
.mac-notch {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 18%;
|
|
height: 3.2%;
|
|
max-height: 12px;
|
|
background: #0a0a0b;
|
|
border-radius: 0 0 7px 7px;
|
|
z-index: 2;
|
|
}
|
|
.mac-screen {
|
|
position: absolute;
|
|
inset: 0;
|
|
overflow: hidden;
|
|
}
|
|
.mac-base {
|
|
height: 10px;
|
|
margin: 0 -1.5% 0;
|
|
border-radius: 0 0 10px 10px;
|
|
background: linear-gradient(180deg, var(--device-shell), var(--device-shell-2));
|
|
border: 1px solid var(--device-edge);
|
|
border-top: none;
|
|
position: relative;
|
|
}
|
|
.mac-base::after {
|
|
content: "";
|
|
position: absolute;
|
|
left: 50%;
|
|
top: 2px;
|
|
transform: translateX(-50%);
|
|
width: 16%;
|
|
height: 4px;
|
|
border-radius: 0 0 4px 4px;
|
|
background: rgba(0, 0, 0, 0.22);
|
|
}
|
|
|
|
/* iPad — center */
|
|
.device-ipad {
|
|
left: 22%;
|
|
top: 18%;
|
|
width: 36%;
|
|
z-index: 2;
|
|
filter: drop-shadow(0 22px 40px rgba(0, 0, 0, 0.22));
|
|
}
|
|
.ipad-bezel {
|
|
border-radius: 18px;
|
|
background: linear-gradient(160deg, var(--device-shell-2), var(--device-shell));
|
|
padding: 2.2%;
|
|
border: 1px solid var(--device-edge);
|
|
}
|
|
.ipad-screen {
|
|
border-radius: 10px;
|
|
overflow: hidden;
|
|
background: #000;
|
|
aspect-ratio: 3 / 4;
|
|
}
|
|
|
|
/* iPhone — front left */
|
|
.device-phone {
|
|
left: 4%;
|
|
bottom: 2%;
|
|
width: 20%;
|
|
z-index: 3;
|
|
filter: drop-shadow(0 18px 32px rgba(0, 0, 0, 0.26));
|
|
}
|
|
.phone-bezel {
|
|
position: relative;
|
|
border-radius: 22px;
|
|
background: linear-gradient(160deg, var(--device-shell-2), var(--device-shell));
|
|
padding: 3.2%;
|
|
border: 1px solid var(--device-edge);
|
|
}
|
|
.phone-island {
|
|
position: absolute;
|
|
top: 5.5%;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 32%;
|
|
height: 3.8%;
|
|
border-radius: 20px;
|
|
background: #0a0a0b;
|
|
z-index: 2;
|
|
}
|
|
.phone-screen {
|
|
border-radius: 16px;
|
|
overflow: hidden;
|
|
background: #000;
|
|
aspect-ratio: 9 / 19.5;
|
|
}
|
|
|
|
@media (max-width: 720px) {
|
|
.device-cluster { aspect-ratio: 5 / 4; }
|
|
.device-mac { width: 78%; right: -2%; top: 0; }
|
|
.device-ipad { width: 42%; left: 14%; top: 22%; }
|
|
.device-phone { width: 26%; left: 2%; bottom: 0; }
|
|
.phone-bezel { border-radius: 18px; }
|
|
.phone-screen { border-radius: 12px; }
|
|
.ipad-bezel { border-radius: 14px; }
|
|
}
|
|
|
|
@keyframes rise-in {
|
|
to { transform: translateY(0); opacity: 1; }
|
|
}
|
|
|
|
/* —— Reveal —— */
|
|
.reveal {
|
|
opacity: 0;
|
|
transform: translateY(28px);
|
|
transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
|
|
}
|
|
.reveal.in { opacity: 1; transform: none; }
|
|
.reveal-delay-1 { transition-delay: 0.08s; }
|
|
.reveal-delay-2 { transition-delay: 0.16s; }
|
|
.reveal-delay-3 { transition-delay: 0.24s; }
|
|
.reveal-delay-4 { transition-delay: 0.32s; }
|
|
|
|
/* —— Sections —— */
|
|
section.block { padding: 5rem 0; }
|
|
.block-head {
|
|
max-width: 38rem;
|
|
margin: 0 auto 2.75rem;
|
|
text-align: center;
|
|
}
|
|
.block-head h2 {
|
|
margin: 0 0 0.65rem;
|
|
font-size: clamp(1.7rem, 3.5vw, 2.35rem);
|
|
letter-spacing: -0.03em;
|
|
font-weight: 700;
|
|
}
|
|
.block-head p {
|
|
margin: 0;
|
|
color: var(--text-2);
|
|
font-size: 1.05rem;
|
|
}
|
|
|
|
/* —— Diff pillars —— */
|
|
.pillars {
|
|
display: grid;
|
|
grid-template-columns: repeat(5, 1fr);
|
|
gap: 1rem;
|
|
}
|
|
@media (max-width: 960px) {
|
|
.pillars { grid-template-columns: repeat(3, 1fr); }
|
|
}
|
|
@media (max-width: 620px) {
|
|
.pillars { grid-template-columns: 1fr 1fr; }
|
|
}
|
|
.pillar {
|
|
padding: 1.35rem 1.15rem;
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
background: var(--bg-elevated);
|
|
transition: border-color 0.25s, transform 0.25s;
|
|
}
|
|
.pillar:hover {
|
|
border-color: var(--accent);
|
|
transform: translateY(-3px);
|
|
}
|
|
.pillar .material-symbols-outlined {
|
|
font-size: 26px;
|
|
color: var(--accent);
|
|
margin-bottom: 0.85rem;
|
|
}
|
|
.pillar h3 {
|
|
margin: 0 0 0.35rem;
|
|
font-size: 1rem;
|
|
letter-spacing: -0.015em;
|
|
}
|
|
.pillar p {
|
|
margin: 0;
|
|
font-size: 0.88rem;
|
|
color: var(--text-2);
|
|
line-height: 1.45;
|
|
}
|
|
|
|
/* —— Story rows —— */
|
|
/* Never force aspect-ratio + object-fit:cover on product shots — that crops/squashes. */
|
|
.story {
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
|
|
gap: clamp(1.5rem, 4vw, 3rem);
|
|
align-items: center;
|
|
padding: 2.75rem 0;
|
|
}
|
|
.story.story--phone {
|
|
grid-template-columns: minmax(0, 1fr) minmax(220px, 300px);
|
|
}
|
|
.story.story--media-first {
|
|
grid-template-columns: minmax(0, 1.08fr) minmax(0, 0.92fr);
|
|
}
|
|
.story.story--media-first.story--phone {
|
|
grid-template-columns: minmax(220px, 300px) minmax(0, 1fr);
|
|
}
|
|
.story.story--media-first .story-copy { order: 2; }
|
|
.story.story--media-first .story-media { order: 1; }
|
|
@media (max-width: 860px) {
|
|
.story,
|
|
.story.story--phone,
|
|
.story.story--media-first,
|
|
.story.story--media-first.story--phone {
|
|
grid-template-columns: 1fr;
|
|
gap: 1.35rem;
|
|
}
|
|
.story.story--media-first .story-copy,
|
|
.story.story--media-first .story-media { order: initial; }
|
|
.story .story-media { order: -1; }
|
|
}
|
|
.story-copy .kicker {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.35rem;
|
|
font-size: 0.8rem;
|
|
font-weight: 600;
|
|
color: var(--accent);
|
|
margin-bottom: 0.7rem;
|
|
}
|
|
.story-copy .kicker .material-symbols-outlined { font-size: 18px; }
|
|
.story-copy h3 {
|
|
margin: 0 0 0.7rem;
|
|
font-size: clamp(1.4rem, 2.6vw, 1.85rem);
|
|
letter-spacing: -0.025em;
|
|
line-height: 1.2;
|
|
}
|
|
.story-copy p {
|
|
margin: 0;
|
|
color: var(--text-2);
|
|
font-size: 1.02rem;
|
|
}
|
|
.shot {
|
|
margin: 0;
|
|
border-radius: 16px;
|
|
overflow: hidden;
|
|
border: 1px solid var(--line);
|
|
background: var(--bg-elevated);
|
|
box-shadow: var(--shadow);
|
|
line-height: 0;
|
|
}
|
|
.shot img {
|
|
width: 100%;
|
|
height: auto;
|
|
display: block;
|
|
}
|
|
.shot--phone {
|
|
border-radius: 28px;
|
|
max-width: 300px;
|
|
width: 100%;
|
|
margin-inline: auto;
|
|
}
|
|
|
|
/* —— Device strip —— */
|
|
.device-strip {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: flex-end;
|
|
justify-content: center;
|
|
gap: 1.5rem 1.75rem;
|
|
}
|
|
.device-card {
|
|
margin: 0;
|
|
border-radius: 16px;
|
|
overflow: hidden;
|
|
border: 1px solid var(--line);
|
|
background: var(--bg-elevated);
|
|
box-shadow: var(--shadow);
|
|
line-height: 0;
|
|
}
|
|
.device-card img {
|
|
width: 100%;
|
|
height: auto;
|
|
display: block;
|
|
}
|
|
.device-card.mac {
|
|
flex: 1 1 520px;
|
|
max-width: 720px;
|
|
}
|
|
.device-card.phone {
|
|
flex: 0 0 auto;
|
|
width: min(260px, 46vw);
|
|
border-radius: 26px;
|
|
}
|
|
.device-caption {
|
|
margin-top: 0.65rem;
|
|
font-size: 0.8rem;
|
|
font-weight: 600;
|
|
color: var(--text-3);
|
|
text-align: center;
|
|
line-height: 1.3;
|
|
}
|
|
.device-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
.device-item.mac {
|
|
flex: 1 1 520px;
|
|
max-width: 720px;
|
|
}
|
|
.device-item.mac .device-card { width: 100%; }
|
|
|
|
/* —— Compare —— */
|
|
.compare {
|
|
border-radius: calc(var(--radius) + 4px);
|
|
border: 1px solid var(--line);
|
|
background:
|
|
linear-gradient(180deg, var(--accent-soft), transparent 42%),
|
|
var(--bg-elevated);
|
|
padding: 2rem 1.5rem;
|
|
overflow: hidden;
|
|
}
|
|
.compare-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: 1.25rem;
|
|
}
|
|
@media (max-width: 800px) {
|
|
.compare-grid { grid-template-columns: 1fr 1fr; }
|
|
}
|
|
.compare-item {
|
|
padding: 0.5rem;
|
|
}
|
|
.compare-item .material-symbols-outlined {
|
|
font-size: 24px;
|
|
color: var(--accent);
|
|
margin-bottom: 0.6rem;
|
|
}
|
|
.compare-item h3 {
|
|
margin: 0 0 0.3rem;
|
|
font-size: 0.98rem;
|
|
}
|
|
.compare-item p {
|
|
margin: 0;
|
|
font-size: 0.88rem;
|
|
color: var(--text-2);
|
|
}
|
|
|
|
/* —— Steps —— */
|
|
.steps {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 1.5rem;
|
|
counter-reset: step;
|
|
}
|
|
@media (max-width: 720px) {
|
|
.steps { grid-template-columns: 1fr; }
|
|
}
|
|
.step {
|
|
position: relative;
|
|
padding: 1.5rem 1.25rem 1.5rem 1.5rem;
|
|
border-top: 1px solid var(--line-strong);
|
|
}
|
|
.step::before {
|
|
counter-increment: step;
|
|
content: "0" counter(step);
|
|
display: block;
|
|
font-size: 0.78rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.06em;
|
|
color: var(--accent);
|
|
margin-bottom: 0.85rem;
|
|
}
|
|
.step h3 {
|
|
margin: 0 0 0.4rem;
|
|
font-size: 1.05rem;
|
|
}
|
|
.step p {
|
|
margin: 0;
|
|
color: var(--text-2);
|
|
font-size: 0.93rem;
|
|
}
|
|
|
|
/* —— Closing —— */
|
|
.closing {
|
|
padding: 5rem 0 4rem;
|
|
text-align: center;
|
|
position: relative;
|
|
}
|
|
.closing::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 10% 20% 20%;
|
|
background: radial-gradient(circle, var(--accent-soft), transparent 65%);
|
|
pointer-events: none;
|
|
}
|
|
.closing .wrap { position: relative; }
|
|
.closing .brand-mark {
|
|
width: 110px;
|
|
height: 34px;
|
|
margin: 0 auto 1.4rem;
|
|
color: var(--accent);
|
|
}
|
|
.closing h2 {
|
|
margin: 0 0 0.75rem;
|
|
font-size: clamp(1.8rem, 4vw, 2.6rem);
|
|
letter-spacing: -0.035em;
|
|
}
|
|
.closing p {
|
|
margin: 0 auto 1.75rem;
|
|
max-width: 32rem;
|
|
color: var(--text-2);
|
|
}
|
|
.closing-cta {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
gap: 0.7rem;
|
|
}
|
|
|
|
/* —— Footer —— */
|
|
footer {
|
|
border-top: 1px solid var(--line);
|
|
padding: 2rem 0 2.75rem;
|
|
font-size: 0.85rem;
|
|
color: var(--text-3);
|
|
}
|
|
.footer-inner {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: space-between;
|
|
gap: 1rem;
|
|
align-items: center;
|
|
}
|
|
.footer-links {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 1.1rem;
|
|
}
|
|
.footer-links a { color: var(--text-2); }
|
|
.footer-links a:hover { color: var(--text); }
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*, *::before, *::after {
|
|
animation: none !important;
|
|
transition: none !important;
|
|
}
|
|
.reveal { opacity: 1; transform: none; }
|
|
.device-cluster { opacity: 1; transform: none; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header class="nav">
|
|
<div class="wrap nav-inner">
|
|
<a class="brand" href="#top" aria-label="OSGKeyboard">
|
|
<svg class="brand-mark" viewBox="36 367 952 291" aria-hidden="true">
|
|
<g fill="currentColor">
|
|
<path d="M627.511 512.419C627.511 448.783 576.127 397.197 512.741 397.197C449.355 397.197 397.971 448.783 397.971 512.419C397.971 576.054 449.355 627.641 512.741 627.641V637.837C443.746 637.837 387.814 581.686 387.814 512.419C387.814 443.152 443.746 387 512.741 387C581.736 387 637.668 443.152 637.668 512.419C637.668 581.686 581.736 637.837 512.741 637.837V627.641C576.127 627.641 627.511 576.054 627.511 512.419Z"/>
|
|
<path d="M56.2632 512.419C56.2632 581.686 112.195 637.837 181.19 637.837C250.185 637.837 306.117 581.686 306.117 512.419C306.117 443.152 250.185 387 181.19 387C112.195 387 56.2632 443.152 56.2632 512.419Z"/>
|
|
<path d="M399.618 562.094L399.618 552.257L512.74 552.257L512.74 562.094L399.618 562.094Z"/>
|
|
<path d="M512.741 483.398V473.561H625.864V483.398H512.741Z"/>
|
|
<path d="M843.308 387.981C910.987 387.981 966.093 441.8 968.171 508.975H843.308V518.812H968.096C965.014 585.066 910.324 637.836 843.308 637.836C774.313 637.836 718.381 581.904 718.381 512.909C718.381 443.914 774.312 387.981 843.308 387.981ZM968.234 518.812H968.096C968.187 516.855 968.234 514.888 968.234 512.909C968.234 511.593 968.211 510.281 968.171 508.975H968.234V518.812Z"/>
|
|
</g>
|
|
</svg>
|
|
<span class="brand-name">OSGKeyboard</span>
|
|
</a>
|
|
<div class="nav-actions">
|
|
<div class="pill-toggle" role="group" aria-label="Language">
|
|
<button type="button" data-lang="en">EN</button>
|
|
<button type="button" class="active" data-lang="zh">中文</button>
|
|
</div>
|
|
<button type="button" class="icon-btn" id="themeToggle" aria-label="Toggle theme">
|
|
<span class="material-symbols-outlined" data-theme-icon>contrast</span>
|
|
</button>
|
|
<a class="nav-link" href="https://github.com/hkgood/OSGKeyboard" rel="noopener">
|
|
<span class="material-symbols-outlined" aria-hidden="true">code</span>
|
|
<span data-i18n="nav.github">GitHub</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main id="top">
|
|
<!-- Hero -->
|
|
<section class="hero">
|
|
<div class="wrap">
|
|
<div class="hero-copy">
|
|
<div class="eyebrow">
|
|
<span class="material-symbols-outlined" aria-hidden="true">graphic_eq</span>
|
|
<span data-i18n="hero.eyebrow">Free · Open · Cross-platform</span>
|
|
</div>
|
|
<h1 data-i18n="hero.title">开口即文字。</h1>
|
|
<p class="lead" data-i18n="hero.lead">免费开源的跨端语音输入。本地识别,自带 API Key 润色,避免重复订阅。iPhone、iPad、Mac — 说完即落字。</p>
|
|
<div class="hero-cta">
|
|
<a class="btn btn-primary" href="https://github.com/hkgood/OSGKeyboard/releases" rel="noopener">
|
|
<span class="material-symbols-outlined" aria-hidden="true">download</span>
|
|
<span data-i18n="hero.cta.primary">立即获取</span>
|
|
</a>
|
|
<a class="btn btn-ghost" href="https://github.com/hkgood/OSGKeyboard" rel="noopener">
|
|
<span class="material-symbols-outlined" aria-hidden="true">terminal</span>
|
|
<span data-i18n="hero.cta.secondary">查看源码</span>
|
|
</a>
|
|
</div>
|
|
<div class="hero-meta">
|
|
<span><span class="material-symbols-outlined" aria-hidden="true">lock</span><span data-i18n="hero.meta1">默认本地识别</span></span>
|
|
<span><span class="material-symbols-outlined" aria-hidden="true">key</span><span data-i18n="hero.meta2">BYOK 不重复付费</span></span>
|
|
<span><span class="material-symbols-outlined" aria-hidden="true">devices</span><span data-i18n="hero.meta3">iOS · iPad · Mac</span></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="hero-stage" aria-label="OSGKeyboard on Mac, iPad, and iPhone">
|
|
<div class="device-cluster">
|
|
<div class="device device-mac">
|
|
<div class="mac-lid">
|
|
<div class="mac-bezel">
|
|
<div class="mac-notch" aria-hidden="true"></div>
|
|
<div class="mac-screen">
|
|
<img data-shot="mac-dashboard" src="assets/screenshots/zh/dark/mac-dashboard.png" width="1440" height="900" alt="OSGKeyboard on Mac" loading="eager">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mac-base" aria-hidden="true"></div>
|
|
</div>
|
|
<div class="device device-ipad">
|
|
<div class="ipad-bezel">
|
|
<div class="ipad-screen">
|
|
<img data-shot="ipad-home" src="assets/screenshots/zh/light/ipad-home.png" width="800" height="1066" alt="OSGKeyboard on iPad" loading="eager">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="device device-phone">
|
|
<div class="phone-bezel">
|
|
<div class="phone-island" aria-hidden="true"></div>
|
|
<div class="phone-screen">
|
|
<img data-shot="iphone-home" src="assets/screenshots/zh/light/iphone-home.png" width="552" height="1200" alt="OSGKeyboard on iPhone" loading="eager">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Differentiation -->
|
|
<section class="block" id="why">
|
|
<div class="wrap">
|
|
<div class="block-head reveal">
|
|
<h2 data-i18n="diff.title">和闭源订阅工具不一样</h2>
|
|
<p data-i18n="diff.subtitle">我们把差异化写进产品设计,而不是定价页。</p>
|
|
</div>
|
|
<div class="pillars">
|
|
<article class="pillar reveal">
|
|
<span class="material-symbols-outlined" aria-hidden="true">volunteer_activism</span>
|
|
<h3 data-i18n="diff.free.t">免费</h3>
|
|
<p data-i18n="diff.free.b">核心功能全部开放,无需订阅解锁。</p>
|
|
</article>
|
|
<article class="pillar reveal reveal-delay-1">
|
|
<span class="material-symbols-outlined" aria-hidden="true">devices</span>
|
|
<h3 data-i18n="diff.cross.t">跨端</h3>
|
|
<p data-i18n="diff.cross.b">iPhone、iPad、Mac 同源体验,词库可同步。</p>
|
|
</article>
|
|
<article class="pillar reveal reveal-delay-2">
|
|
<span class="material-symbols-outlined" aria-hidden="true">code</span>
|
|
<h3 data-i18n="diff.open.t">开源</h3>
|
|
<p data-i18n="diff.open.b">源码可审计。你信任的是代码,不是口号。</p>
|
|
</article>
|
|
<article class="pillar reveal reveal-delay-3">
|
|
<span class="material-symbols-outlined" aria-hidden="true">shield_lock</span>
|
|
<h3 data-i18n="diff.privacy.t">隐私</h3>
|
|
<p data-i18n="diff.privacy.b">默认本机识别,录音不上云,无追踪器。</p>
|
|
</article>
|
|
<article class="pillar reveal reveal-delay-4">
|
|
<span class="material-symbols-outlined" aria-hidden="true">vpn_key</span>
|
|
<h3 data-i18n="diff.byok.t">BYOK</h3>
|
|
<p data-i18n="diff.byok.b">带上你自己的 API Key,避免平台重复收费。</p>
|
|
</article>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Product stories -->
|
|
<section class="block" id="product" style="padding-top: 1rem;">
|
|
<div class="wrap">
|
|
<div class="block-head reveal">
|
|
<h2 data-i18n="product.title">开口之后,全链路都在你掌控</h2>
|
|
<p data-i18n="product.subtitle">从键盘到桌面,从词库到设置 — 真实产品界面。</p>
|
|
</div>
|
|
|
|
<div class="story reveal">
|
|
<div class="story-copy">
|
|
<div class="kicker">
|
|
<span class="material-symbols-outlined" aria-hidden="true">laptop_mac</span>
|
|
<span data-i18n="story.mac.k">macOS</span>
|
|
</div>
|
|
<h3 data-i18n="story.mac.t">首页听写,边说边落字</h3>
|
|
<p data-i18n="story.mac.b">红色录音态实时显示转写。本地 ASR 就绪时,文字出现在听写区,也可插入前台 App 光标处。</p>
|
|
</div>
|
|
<div class="story-media">
|
|
<figure class="shot">
|
|
<img data-shot="mac-recording" src="assets/screenshots/zh/dark/mac-recording.png" width="1440" height="900" alt="Mac dictation recording" loading="lazy">
|
|
</figure>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="story story--phone story--media-first reveal">
|
|
<div class="story-copy">
|
|
<div class="kicker">
|
|
<span class="material-symbols-outlined" aria-hidden="true">smartphone</span>
|
|
<span data-i18n="story.ios.k">iPhone</span>
|
|
</div>
|
|
<h3 data-i18n="story.ios.t">自定义键盘:绿就绪,红录音</h3>
|
|
<p data-i18n="story.ios.b">主 App 可试键盘;系统设置添加后,任意 App 切换到 OSGKeyboard 即可听写。「本地」标识默认不上云。</p>
|
|
</div>
|
|
<div class="story-media">
|
|
<figure class="shot shot--phone">
|
|
<img data-shot="iphone-keyboard_recording" src="assets/screenshots/zh/light/iphone-keyboard_recording.png" width="552" height="1200" alt="iPhone keyboard recording" loading="lazy">
|
|
</figure>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="story reveal">
|
|
<div class="story-copy">
|
|
<div class="kicker">
|
|
<span class="material-symbols-outlined" aria-hidden="true">menu_book</span>
|
|
<span data-i18n="story.dict.k">个性词库</span>
|
|
</div>
|
|
<h3 data-i18n="story.dict.t">个性词库,让识别更懂你</h3>
|
|
<p data-i18n="story.dict.b">人名、产品名、专业术语按类整理。AI 润色时原样保留,避免被「改坏」。</p>
|
|
</div>
|
|
<div class="story-media">
|
|
<figure class="shot">
|
|
<img data-shot="mac-dictionary" src="assets/screenshots/zh/dark/mac-dictionary.png" width="1440" height="900" alt="Mac personal dictionary" loading="lazy">
|
|
</figure>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="story story--media-first reveal">
|
|
<div class="story-copy">
|
|
<div class="kicker">
|
|
<span class="material-symbols-outlined" aria-hidden="true">shield_lock</span>
|
|
<span data-i18n="story.settings.k">隐私 · 本地引擎</span>
|
|
</div>
|
|
<h3 data-i18n="story.settings.t">默认本地识别,高隐私低延迟</h3>
|
|
<p data-i18n="story.settings.b">设置里可选本地或云端引擎。截图中本地识别已勾选;云端与 API Key 仅在你主动配置时使用。</p>
|
|
</div>
|
|
<div class="story-media">
|
|
<figure class="shot">
|
|
<img data-shot="mac-settings" src="assets/screenshots/zh/dark/mac-settings.png" width="1440" height="900" alt="Mac settings local ASR" loading="lazy">
|
|
</figure>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Cross-device gallery -->
|
|
<section class="block" style="padding-top: 0;">
|
|
<div class="wrap">
|
|
<div class="block-head reveal">
|
|
<h2 data-i18n="gallery.title">跨端同一套体验</h2>
|
|
<p data-i18n="gallery.subtitle">Mac 历史归档,iPhone 用量与键盘 — 完整窗口,不做裁切。</p>
|
|
</div>
|
|
<div class="device-strip reveal">
|
|
<div class="device-item mac">
|
|
<figure class="device-card mac">
|
|
<img data-shot="mac-history" src="assets/screenshots/zh/dark/mac-history.png" width="1440" height="900" alt="Mac history" loading="lazy">
|
|
</figure>
|
|
<div class="device-caption" data-i18n="gallery.cap.mac">Mac · 听写历史</div>
|
|
</div>
|
|
<div class="device-item">
|
|
<figure class="device-card phone">
|
|
<img data-shot="iphone-home" src="assets/screenshots/zh/light/iphone-home.png" width="552" height="1200" alt="iPhone home" loading="lazy">
|
|
</figure>
|
|
<div class="device-caption" data-i18n="gallery.cap.home">iPhone · 首页统计</div>
|
|
</div>
|
|
<div class="device-item">
|
|
<figure class="device-card phone">
|
|
<img data-shot="iphone-keyboard_idle" src="assets/screenshots/zh/light/iphone-keyboard_idle.png" width="552" height="1200" alt="iPhone keyboard idle" loading="lazy">
|
|
</figure>
|
|
<div class="device-caption" data-i18n="gallery.cap.kb">iPhone · 键盘待机</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Compare -->
|
|
<section class="block" id="byok">
|
|
<div class="wrap">
|
|
<div class="block-head reveal">
|
|
<h2 data-i18n="compare.title">为什么选 BYOK</h2>
|
|
<p data-i18n="compare.subtitle">很多工具把 AI 用量包进订阅。你已经有 Key 时,不该再付一遍。</p>
|
|
</div>
|
|
<div class="compare reveal">
|
|
<div class="compare-grid">
|
|
<div class="compare-item">
|
|
<span class="material-symbols-outlined" aria-hidden="true">payments</span>
|
|
<h3 data-i18n="compare.1.t">避免重复消费</h3>
|
|
<p data-i18n="compare.1.b">用已有的 DeepSeek / OpenAI 额度,不另开「听写订阅」。</p>
|
|
</div>
|
|
<div class="compare-item">
|
|
<span class="material-symbols-outlined" aria-hidden="true">visibility</span>
|
|
<h3 data-i18n="compare.2.t">费用透明</h3>
|
|
<p data-i18n="compare.2.b">账单在服务商侧,OSGKeyboard 不经手你的模型费用。</p>
|
|
</div>
|
|
<div class="compare-item">
|
|
<span class="material-symbols-outlined" aria-hidden="true">swap_horiz</span>
|
|
<h3 data-i18n="compare.3.t">随时换模型</h3>
|
|
<p data-i18n="compare.3.b">OpenAI 兼容端点即可接入;本地模型也可选。</p>
|
|
</div>
|
|
<div class="compare-item">
|
|
<span class="material-symbols-outlined" aria-hidden="true">encrypted</span>
|
|
<h3 data-i18n="compare.4.t">Key 在本机</h3>
|
|
<p data-i18n="compare.4.b">存入系统 Keychain,不经过我们的服务器。</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Steps -->
|
|
<section class="block" id="start">
|
|
<div class="wrap">
|
|
<div class="block-head reveal">
|
|
<h2 data-i18n="steps.title">三步,开始第一句话</h2>
|
|
<p data-i18n="steps.subtitle">设置一次,之后在任意输入处开口即写。</p>
|
|
</div>
|
|
<div class="steps">
|
|
<div class="step reveal">
|
|
<h3 data-i18n="step1.t">安装并授权</h3>
|
|
<p data-i18n="step1.b">iOS 添加键盘并开启完全访问;Mac 授予麦克风与辅助功能。</p>
|
|
</div>
|
|
<div class="step reveal reveal-delay-1">
|
|
<h3 data-i18n="step2.t">选引擎,可选填 Key</h3>
|
|
<p data-i18n="step2.b">本地识别开箱即用;需要 AI 润色时填入你自己的 API Key。</p>
|
|
</div>
|
|
<div class="step reveal reveal-delay-2">
|
|
<h3 data-i18n="step3.t">开口说话</h3>
|
|
<p data-i18n="step3.b">切换到 OSGKeyboard,或按住 Option。润色文字落入光标处。</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Closing -->
|
|
<section class="closing">
|
|
<div class="wrap reveal">
|
|
<svg class="brand-mark" viewBox="36 367 952 291" aria-hidden="true">
|
|
<g fill="currentColor">
|
|
<path d="M627.511 512.419C627.511 448.783 576.127 397.197 512.741 397.197C449.355 397.197 397.971 448.783 397.971 512.419C397.971 576.054 449.355 627.641 512.741 627.641V637.837C443.746 637.837 387.814 581.686 387.814 512.419C387.814 443.152 443.746 387 512.741 387C581.736 387 637.668 443.152 637.668 512.419C637.668 581.686 581.736 637.837 512.741 637.837V627.641C576.127 627.641 627.511 576.054 627.511 512.419Z"/>
|
|
<path d="M56.2632 512.419C56.2632 581.686 112.195 637.837 181.19 637.837C250.185 637.837 306.117 581.686 306.117 512.419C306.117 443.152 250.185 387 181.19 387C112.195 387 56.2632 443.152 56.2632 512.419Z"/>
|
|
<path d="M399.618 562.094L399.618 552.257L512.74 552.257L512.74 562.094L399.618 562.094Z"/>
|
|
<path d="M512.741 483.398V473.561H625.864V483.398H512.741Z"/>
|
|
<path d="M843.308 387.981C910.987 387.981 966.093 441.8 968.171 508.975H843.308V518.812H968.096C965.014 585.066 910.324 637.836 843.308 637.836C774.313 637.836 718.381 581.904 718.381 512.909C718.381 443.914 774.312 387.981 843.308 387.981ZM968.234 518.812H968.096C968.187 516.855 968.234 514.888 968.234 512.909C968.234 511.593 968.211 510.281 968.171 508.975H968.234V518.812Z"/>
|
|
</g>
|
|
</svg>
|
|
<h2 data-i18n="close.title">开口即文字。其余交给你。</h2>
|
|
<p data-i18n="close.body">免费、跨端、开源、隐私优先。语音留在本机,AI 用你自己的 Key。</p>
|
|
<div class="closing-cta">
|
|
<a class="btn btn-primary" href="https://github.com/hkgood/OSGKeyboard/releases" rel="noopener">
|
|
<span class="material-symbols-outlined" aria-hidden="true">download</span>
|
|
<span data-i18n="close.cta">立即获取</span>
|
|
</a>
|
|
<a class="btn btn-ghost" href="privacy/">
|
|
<span class="material-symbols-outlined" aria-hidden="true">policy</span>
|
|
<span data-i18n="close.privacy">隐私政策</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<footer>
|
|
<div class="wrap footer-inner">
|
|
<span data-i18n="footer.copy">© OSGKeyboard · v0.5.4 · 源码可见</span>
|
|
<span class="footer-links">
|
|
<a href="https://github.com/hkgood/OSGKeyboard" rel="noopener" data-i18n="footer.source">Source</a>
|
|
<a href="privacy/" data-i18n="footer.privacy">Privacy</a>
|
|
<a href="https://github.com/hkgood/OSGKeyboard/blob/main/LICENSE" rel="noopener" data-i18n="footer.license">License</a>
|
|
</span>
|
|
</div>
|
|
</footer>
|
|
|
|
<script>
|
|
const copy = {
|
|
en: {
|
|
"nav.github": "GitHub",
|
|
"hero.eyebrow": "Free · Open · Cross-platform",
|
|
"hero.title": "Speak it. It's typed.",
|
|
"hero.lead": "Free, open-source voice input across iPhone, iPad, and Mac. On-device recognition. Bring your own API key — no double-billing.",
|
|
"hero.cta.primary": "Get it now",
|
|
"hero.cta.secondary": "View source",
|
|
"hero.meta1": "On-device by default",
|
|
"hero.meta2": "BYOK — no double pay",
|
|
"hero.meta3": "iOS · iPad · Mac",
|
|
"diff.title": "Not another closed subscription",
|
|
"diff.subtitle": "The differences are designed in — not gated behind a paywall.",
|
|
"diff.free.t": "Free",
|
|
"diff.free.b": "Every core feature is available. No subscription unlock.",
|
|
"diff.cross.t": "Cross-platform",
|
|
"diff.cross.b": "iPhone, iPad, and Mac — one product language, optional sync.",
|
|
"diff.open.t": "Open source",
|
|
"diff.open.b": "Audit the code. Trust the repo, not the marketing.",
|
|
"diff.privacy.t": "Privacy",
|
|
"diff.privacy.b": "On-device ASR by default. No trackers. Audio stays local.",
|
|
"diff.byok.t": "BYOK",
|
|
"diff.byok.b": "Bring your own API key. Avoid paying twice for AI.",
|
|
"product.title": "After you speak, you stay in control",
|
|
"product.subtitle": "From keyboard to desktop — real product UI.",
|
|
"story.mac.k": "macOS",
|
|
"story.mac.t": "Dictate on the home screen — live transcript",
|
|
"story.mac.b": "Red recording state streams text into the dictation area. With local ASR ready, insert at the frontmost app cursor when you choose.",
|
|
"story.ios.k": "iPhone",
|
|
"story.ios.t": "Custom keyboard: green ready, red recording",
|
|
"story.ios.b": "Try it in the main app; after adding the keyboard in Settings, switch to OSGKeyboard in any app. The Local chip means audio stays on-device by default.",
|
|
"story.dict.k": "Personal dictionary",
|
|
"story.dict.t": "Terms the AI must never rewrite",
|
|
"story.dict.b": "Names, products, and jargon — grouped and preserved verbatim during polish.",
|
|
"story.settings.k": "Privacy · local engine",
|
|
"story.settings.t": "Local recognition by default",
|
|
"story.settings.b": "Choose local or cloud in Settings. This shot shows local ASR selected; cloud engines and API keys only when you opt in.",
|
|
"gallery.title": "One experience across devices",
|
|
"gallery.subtitle": "Mac history, iPhone stats and keyboard — full frames, never cropped.",
|
|
"gallery.cap.mac": "Mac · History",
|
|
"gallery.cap.home": "iPhone · Home stats",
|
|
"gallery.cap.kb": "iPhone · Keyboard idle",
|
|
"compare.title": "Why BYOK matters",
|
|
"compare.subtitle": "Many tools fold AI usage into a subscription. If you already have a key, you shouldn't pay again.",
|
|
"compare.1.t": "No double billing",
|
|
"compare.1.b": "Use your existing DeepSeek / OpenAI credits — skip a dictation tax.",
|
|
"compare.2.t": "Transparent cost",
|
|
"compare.2.b": "Billing stays with your provider. We never touch model fees.",
|
|
"compare.3.t": "Swap models freely",
|
|
"compare.3.b": "Any OpenAI-compatible endpoint — or keep it fully local.",
|
|
"compare.4.t": "Keys stay on-device",
|
|
"compare.4.b": "Stored in Keychain. Never routed through our servers.",
|
|
"steps.title": "Three steps to your first line",
|
|
"steps.subtitle": "Set up once. Dictate anywhere after.",
|
|
"step1.t": "Install & authorize",
|
|
"step1.b": "Add the iOS keyboard with Full Access; grant mic + Accessibility on Mac.",
|
|
"step2.t": "Pick engine, optional key",
|
|
"step2.b": "Local ASR works out of the box. Add your API key when you want AI polish.",
|
|
"step3.t": "Start talking",
|
|
"step3.b": "Switch to OSGKeyboard, or hold Option. Polished text lands at the cursor.",
|
|
"close.title": "Speak it. The rest is yours.",
|
|
"close.body": "Free, cross-platform, open, privacy-first. Voice stays local. AI uses your key.",
|
|
"close.cta": "Get it now",
|
|
"close.privacy": "Privacy policy",
|
|
"footer.copy": "© OSGKeyboard · v0.5.4 · source available",
|
|
"footer.source": "Source",
|
|
"footer.privacy": "Privacy",
|
|
"footer.license": "License"
|
|
},
|
|
zh: {
|
|
"nav.github": "GitHub",
|
|
"hero.eyebrow": "免费 · 开源 · 跨端",
|
|
"hero.title": "开口即文字。",
|
|
"hero.lead": "免费开源的跨端语音输入。本地识别,自带 API Key 润色,避免重复订阅。iPhone、iPad、Mac — 说完即落字。",
|
|
"hero.cta.primary": "立即获取",
|
|
"hero.cta.secondary": "查看源码",
|
|
"hero.meta1": "默认本地识别",
|
|
"hero.meta2": "BYOK 不重复付费",
|
|
"hero.meta3": "iOS · iPad · Mac",
|
|
"diff.title": "和闭源订阅工具不一样",
|
|
"diff.subtitle": "我们把差异化写进产品设计,而不是定价页。",
|
|
"diff.free.t": "免费",
|
|
"diff.free.b": "核心功能全部开放,无需订阅解锁。",
|
|
"diff.cross.t": "跨端",
|
|
"diff.cross.b": "iPhone、iPad、Mac 同源体验,词库可同步。",
|
|
"diff.open.t": "开源",
|
|
"diff.open.b": "源码可审计。你信任的是代码,不是口号。",
|
|
"diff.privacy.t": "隐私",
|
|
"diff.privacy.b": "默认本机识别,录音不上云,无追踪器。",
|
|
"diff.byok.t": "BYOK",
|
|
"diff.byok.b": "带上你自己的 API Key,避免平台重复收费。",
|
|
"product.title": "开口之后,全链路都在你掌控",
|
|
"product.subtitle": "从键盘到桌面,从词库到设置 — 真实产品界面。",
|
|
"story.mac.k": "macOS",
|
|
"story.mac.t": "首页听写,边说边落字",
|
|
"story.mac.b": "红色录音态实时显示转写。本地 ASR 就绪时,文字出现在听写区,也可插入前台 App 光标处。",
|
|
"story.ios.k": "iPhone",
|
|
"story.ios.t": "自定义键盘:绿就绪,红录音",
|
|
"story.ios.b": "主 App 可试键盘;系统设置添加后,任意 App 切换到 OSGKeyboard 即可听写。「本地」标识默认不上云。",
|
|
"story.dict.k": "个性词库",
|
|
"story.dict.t": "个性词库,让识别更懂你",
|
|
"story.dict.b": "人名、产品名、专业术语按类整理。AI 润色时原样保留,避免被「改坏」。",
|
|
"story.settings.k": "隐私 · 本地引擎",
|
|
"story.settings.t": "默认本地识别,高隐私低延迟",
|
|
"story.settings.b": "设置里可选本地或云端引擎。截图中本地识别已勾选;云端与 API Key 仅在你主动配置时使用。",
|
|
"gallery.title": "跨端同一套体验",
|
|
"gallery.subtitle": "Mac 历史归档,iPhone 用量与键盘 — 完整窗口,不做裁切。",
|
|
"gallery.cap.mac": "Mac · 听写历史",
|
|
"gallery.cap.home": "iPhone · 首页统计",
|
|
"gallery.cap.kb": "iPhone · 键盘待机",
|
|
"compare.title": "为什么选 BYOK",
|
|
"compare.subtitle": "很多工具把 AI 用量包进订阅。你已经有 Key 时,不该再付一遍。",
|
|
"compare.1.t": "避免重复消费",
|
|
"compare.1.b": "用已有的 DeepSeek / OpenAI 额度,不另开「听写订阅」。",
|
|
"compare.2.t": "费用透明",
|
|
"compare.2.b": "账单在服务商侧,OSGKeyboard 不经手你的模型费用。",
|
|
"compare.3.t": "随时换模型",
|
|
"compare.3.b": "OpenAI 兼容端点即可接入;本地模型也可选。",
|
|
"compare.4.t": "Key 在本机",
|
|
"compare.4.b": "存入系统 Keychain,不经过我们的服务器。",
|
|
"steps.title": "三步,开始第一句话",
|
|
"steps.subtitle": "设置一次,之后在任意输入处开口即写。",
|
|
"step1.t": "安装并授权",
|
|
"step1.b": "iOS 添加键盘并开启完全访问;Mac 授予麦克风与辅助功能。",
|
|
"step2.t": "选引擎,可选填 Key",
|
|
"step2.b": "本地识别开箱即用;需要 AI 润色时填入你自己的 API Key。",
|
|
"step3.t": "开口说话",
|
|
"step3.b": "切换到 OSGKeyboard,或按住 Option。润色文字落入光标处。",
|
|
"close.title": "开口即文字。其余交给你。",
|
|
"close.body": "免费、跨端、开源、隐私优先。语音留在本机,AI 用你自己的 Key。",
|
|
"close.cta": "立即获取",
|
|
"close.privacy": "隐私政策",
|
|
"footer.copy": "© OSGKeyboard · v0.5.4 · 源码可见",
|
|
"footer.source": "源代码",
|
|
"footer.privacy": "隐私",
|
|
"footer.license": "许可协议"
|
|
}
|
|
};
|
|
|
|
function applyLang(lang) {
|
|
const strings = copy[lang] || copy.zh;
|
|
document.documentElement.lang = lang === "zh" ? "zh-Hans" : "en";
|
|
document.querySelectorAll("[data-i18n]").forEach((el) => {
|
|
const value = strings[el.getAttribute("data-i18n")];
|
|
if (value != null) el.textContent = value;
|
|
});
|
|
document.querySelectorAll(".pill-toggle button[data-lang]").forEach((btn) => {
|
|
btn.classList.toggle("active", btn.dataset.lang === lang);
|
|
});
|
|
try { localStorage.setItem("osg-lang", lang); } catch (_) {}
|
|
applyShots(lang);
|
|
}
|
|
|
|
function resolvedTheme() {
|
|
const theme = document.documentElement.getAttribute("data-theme") || "system";
|
|
if (theme === "light" || theme === "dark") return theme;
|
|
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
}
|
|
|
|
function shotURL(name, langOverride) {
|
|
let lang = langOverride;
|
|
if (!lang) {
|
|
try { lang = localStorage.getItem("osg-lang"); } catch (_) {}
|
|
}
|
|
if (!lang) lang = document.documentElement.lang.startsWith("zh") ? "zh" : "en";
|
|
const theme = resolvedTheme();
|
|
return `assets/screenshots/${lang}/${theme}/${name}.png`;
|
|
}
|
|
|
|
function applyShots(langOverride) {
|
|
document.querySelectorAll("img[data-shot]").forEach((img) => {
|
|
const name = img.getAttribute("data-shot");
|
|
if (!name) return;
|
|
const next = shotURL(name, langOverride);
|
|
if (img.getAttribute("src") !== next) img.setAttribute("src", next);
|
|
});
|
|
}
|
|
|
|
function applyTheme(theme) {
|
|
document.documentElement.setAttribute("data-theme", theme);
|
|
const icon = document.querySelector("[data-theme-icon]");
|
|
if (icon) {
|
|
icon.textContent = theme === "dark" ? "light_mode" : theme === "light" ? "dark_mode" : "contrast";
|
|
}
|
|
try { localStorage.setItem("osg-theme", theme); } catch (_) {}
|
|
applyShots();
|
|
}
|
|
|
|
function cycleTheme() {
|
|
const current = document.documentElement.getAttribute("data-theme") || "system";
|
|
const next = current === "system" ? "light" : current === "light" ? "dark" : "system";
|
|
applyTheme(next);
|
|
}
|
|
|
|
document.querySelectorAll(".pill-toggle button[data-lang]").forEach((btn) => {
|
|
btn.addEventListener("click", () => applyLang(btn.dataset.lang));
|
|
});
|
|
document.getElementById("themeToggle")?.addEventListener("click", cycleTheme);
|
|
|
|
const savedLang = (() => { try { return localStorage.getItem("osg-lang"); } catch (_) { return null; } })();
|
|
const savedTheme = (() => { try { return localStorage.getItem("osg-theme"); } catch (_) { return null; } })();
|
|
applyLang(savedLang || ((navigator.language || "").startsWith("zh") ? "zh" : "en"));
|
|
applyTheme(savedTheme || "system");
|
|
|
|
try {
|
|
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
|
|
if ((document.documentElement.getAttribute("data-theme") || "system") === "system") {
|
|
applyShots();
|
|
}
|
|
});
|
|
} catch (_) {}
|
|
|
|
// Scroll reveal
|
|
const reveals = document.querySelectorAll(".reveal");
|
|
if ("IntersectionObserver" in window) {
|
|
const io = new IntersectionObserver((entries) => {
|
|
entries.forEach((entry) => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add("in");
|
|
io.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, { rootMargin: "0px 0px -8% 0px", threshold: 0.12 });
|
|
reveals.forEach((el) => io.observe(el));
|
|
} else {
|
|
reveals.forEach((el) => el.classList.add("in"));
|
|
}
|
|
|
|
// Soft parallax after hero entrance animation settles
|
|
const stage = document.querySelector(".device-cluster");
|
|
if (stage && !window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
let ready = false;
|
|
setTimeout(() => { ready = true; }, 1100);
|
|
window.addEventListener("scroll", () => {
|
|
if (!ready) return;
|
|
const y = Math.min(window.scrollY * 0.06, 28);
|
|
stage.style.transform = `translateY(${y}px)`;
|
|
}, { passive: true });
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|