docs(pages): redesign home as dark product stage with real footage

- Render the new Icon Composer app icon for favicon, nav, and og:image
- Hero uses the three-device visual on a dark stage; hand-drawn mockups
  replaced with on-device videos and real UI cards
- Add 2.1 selling points: personal style, three-tone replies, monthly
  usage calendar, EventKit skills export, guided trials, credit rewards
- Restructure service paths with on-device as the primary trust card
- Self-hosted inline SVG icons; remove dead Three.js assets and logo.svg
- Bump page version references to 2.1.0 (Mac source at 2.2)
This commit is contained in:
Rocky
2026-09-27 19:26:00 +08:00
parent 8c16bf67d8
commit 4912529c13
9 changed files with 1479 additions and 2519 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 524 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 421 KiB

-373
View File
@@ -1,373 +0,0 @@
/*!
* OSGKeyboard hero beams — vanilla Three.js recreation inspired by
* React Bits "Beams" (https://reactbits.dev/backgrounds/beams).
* Parameters match the shared demo preset.
* three.js is MIT; this file is project code.
*/
(function (global) {
"use strict";
const NOISE = `
float random (in vec2 st) {
return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
}
float noise (in vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}
vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}
vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}
vec3 fade(vec3 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);}
float cnoise(vec3 P){
vec3 Pi0 = floor(P);
vec3 Pi1 = Pi0 + vec3(1.0);
Pi0 = mod(Pi0, 289.0);
Pi1 = mod(Pi1, 289.0);
vec3 Pf0 = fract(P);
vec3 Pf1 = Pf0 - vec3(1.0);
vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
vec4 iy = vec4(Pi0.yy, Pi1.yy);
vec4 iz0 = Pi0.zzzz;
vec4 iz1 = Pi1.zzzz;
vec4 ixy = permute(permute(ix) + iy);
vec4 ixy0 = permute(ixy + iz0);
vec4 ixy1 = permute(ixy + iz1);
vec4 gx0 = ixy0 / 7.0;
vec4 gy0 = fract(floor(gx0) / 7.0) - 0.5;
gx0 = fract(gx0);
vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
vec4 sz0 = step(gz0, vec4(0.0));
gx0 -= sz0 * (step(0.0, gx0) - 0.5);
gy0 -= sz0 * (step(0.0, gy0) - 0.5);
vec4 gx1 = ixy1 / 7.0;
vec4 gy1 = fract(floor(gx1) / 7.0) - 0.5;
gx1 = fract(gx1);
vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
vec4 sz1 = step(gz1, vec4(0.0));
gx1 -= sz1 * (step(0.0, gx1) - 0.5);
gy1 -= sz1 * (step(0.0, gy1) - 0.5);
vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
vec4 norm0 = taylorInvSqrt(vec4(dot(g000,g000),dot(g010,g010),dot(g100,g100),dot(g110,g110)));
g000 *= norm0.x; g010 *= norm0.y; g100 *= norm0.z; g110 *= norm0.w;
vec4 norm1 = taylorInvSqrt(vec4(dot(g001,g001),dot(g011,g011),dot(g101,g101),dot(g111,g111)));
g001 *= norm1.x; g011 *= norm1.y; g101 *= norm1.z; g111 *= norm1.w;
float n000 = dot(g000, Pf0);
float n100 = dot(g100, vec3(Pf1.x,Pf0.yz));
float n010 = dot(g010, vec3(Pf0.x,Pf1.y,Pf0.z));
float n110 = dot(g110, vec3(Pf1.xy,Pf0.z));
float n001 = dot(g001, vec3(Pf0.xy,Pf1.z));
float n101 = dot(g101, vec3(Pf1.x,Pf0.y,Pf1.z));
float n011 = dot(g011, vec3(Pf0.x,Pf1.yz));
float n111 = dot(g111, Pf1);
vec3 fade_xyz = fade(Pf0);
vec4 n_z = mix(vec4(n000,n100,n010,n110),vec4(n001,n101,n011,n111),fade_xyz.z);
vec2 n_yz = mix(n_z.xy,n_z.zw,fade_xyz.y);
float n_xyz = mix(n_yz.x,n_yz.y,fade_xyz.x);
return 2.2 * n_xyz;
}
`;
function hexToNormalizedRGB(hex) {
const clean = String(hex).replace("#", "");
const full = clean.length === 3
? clean.split("").map((c) => c + c).join("")
: clean;
return [
parseInt(full.substring(0, 2), 16) / 255,
parseInt(full.substring(2, 4), 16) / 255,
parseInt(full.substring(4, 6), 16) / 255
];
}
function extendMaterial(THREE, BaseMaterial, cfg) {
const physical = THREE.ShaderLib.physical;
const { vertexShader: baseVert, fragmentShader: baseFrag, uniforms: baseUniforms } = physical;
const baseDefines = physical.defines || {};
const uniforms = THREE.UniformsUtils.clone(baseUniforms);
const defaults = new BaseMaterial(cfg.material || {});
if (defaults.color) uniforms.diffuse.value = defaults.color;
if ("roughness" in defaults) uniforms.roughness.value = defaults.roughness;
if ("metalness" in defaults) uniforms.metalness.value = defaults.metalness;
if ("envMap" in defaults) uniforms.envMap.value = defaults.envMap;
if ("envMapIntensity" in defaults) uniforms.envMapIntensity.value = defaults.envMapIntensity;
Object.entries(cfg.uniforms || {}).forEach(([key, u]) => {
uniforms[key] = u !== null && typeof u === "object" && "value" in u ? u : { value: u };
});
let vert = `${cfg.header}\n${cfg.vertexHeader || ""}\n${baseVert}`;
let frag = `${cfg.header}\n${cfg.fragmentHeader || ""}\n${baseFrag}`;
for (const [inc, code] of Object.entries(cfg.vertex || {})) {
vert = vert.replace(inc, `${inc}\n${code}`);
}
for (const [inc, code] of Object.entries(cfg.fragment || {})) {
frag = frag.replace(inc, `${inc}\n${code}`);
}
return new THREE.ShaderMaterial({
defines: { ...baseDefines },
uniforms,
vertexShader: vert,
fragmentShader: frag,
lights: true,
fog: !!(cfg.material && cfg.material.fog)
});
}
function createStackedPlanesBufferGeometry(THREE, n, width, height, spacing, heightSegments) {
const geometry = new THREE.BufferGeometry();
const numVertices = n * (heightSegments + 1) * 2;
const numFaces = n * heightSegments * 2;
const positions = new Float32Array(numVertices * 3);
const indices = new Uint32Array(numFaces * 3);
const uvs = new Float32Array(numVertices * 2);
let vertexOffset = 0;
let indexOffset = 0;
let uvOffset = 0;
const totalWidth = n * width + (n - 1) * spacing;
const xOffsetBase = -totalWidth / 2;
for (let i = 0; i < n; i++) {
const xOffset = xOffsetBase + i * (width + spacing);
const uvXOffset = Math.random() * 300;
const uvYOffset = Math.random() * 300;
for (let j = 0; j <= heightSegments; j++) {
const y = height * (j / heightSegments - 0.5);
positions.set([xOffset, y, 0, xOffset + width, y, 0], vertexOffset * 3);
const uvY = j / heightSegments;
uvs.set([uvXOffset, uvY + uvYOffset, uvXOffset + 1, uvY + uvYOffset], uvOffset);
if (j < heightSegments) {
const a = vertexOffset;
const b = vertexOffset + 1;
const c = vertexOffset + 2;
const d = vertexOffset + 3;
indices.set([a, b, c, c, b, d], indexOffset);
indexOffset += 6;
}
vertexOffset += 2;
uvOffset += 4;
}
}
geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
geometry.setAttribute("uv", new THREE.BufferAttribute(uvs, 2));
geometry.setIndex(new THREE.BufferAttribute(indices, 1));
geometry.computeVertexNormals();
return geometry;
}
/**
* @param {HTMLCanvasElement} canvas
* @param {object} options
*/
function create(canvas, options) {
const THREE = global.THREE;
if (!THREE || !canvas) throw new Error("THREE or canvas missing");
const {
beamWidth = 4.5,
beamHeight = 6,
beamNumber = 26,
lightColor = "#7CFFB2",
speed = 4.5,
noiseIntensity = 2.65,
scale = 0.62,
rotation = 208,
background = "#000000",
reduceMotion = false,
heightSegments = 100
} = options || {};
const scene = new THREE.Scene();
scene.background = new THREE.Color(background);
const camera = new THREE.PerspectiveCamera(30, 1, 0.1, 100);
camera.position.set(0, 0, 20);
const renderer = new THREE.WebGLRenderer({
canvas,
antialias: true,
alpha: false,
powerPreference: "low-power"
});
renderer.setClearColor(background, 1);
renderer.outputColorSpace = THREE.SRGBColorSpace;
const material = extendMaterial(THREE, THREE.MeshStandardMaterial, {
header: `
varying vec3 vEye;
varying float vNoise;
varying vec2 vUv;
varying vec3 vPosition;
uniform float time;
uniform float uSpeed;
uniform float uNoiseIntensity;
uniform float uScale;
${NOISE}`,
vertexHeader: `
float getPos(vec3 pos) {
vec3 noisePos =
vec3(pos.x * 0., pos.y - uv.y, pos.z + time * uSpeed * 3.) * uScale;
return cnoise(noisePos);
}
vec3 getCurrentPos(vec3 pos) {
vec3 newpos = pos;
newpos.z += getPos(pos);
return newpos;
}
vec3 getNormal(vec3 pos) {
vec3 curpos = getCurrentPos(pos);
vec3 nextposX = getCurrentPos(pos + vec3(0.01, 0.0, 0.0));
vec3 nextposZ = getCurrentPos(pos + vec3(0.0, -0.01, 0.0));
vec3 tangentX = normalize(nextposX - curpos);
vec3 tangentZ = normalize(nextposZ - curpos);
return normalize(cross(tangentZ, tangentX));
}`,
vertex: {
"#include <begin_vertex>": "transformed.z += getPos(transformed.xyz);",
"#include <beginnormal_vertex>": "objectNormal = getNormal(position.xyz);"
},
fragment: {
"#include <dithering_fragment>": `
float randomNoise = noise(gl_FragCoord.xy);
gl_FragColor.rgb -= randomNoise / 15. * uNoiseIntensity;`
},
material: { fog: true },
uniforms: {
diffuse: new THREE.Color(...hexToNormalizedRGB("#000000")),
time: { value: 0 },
roughness: 0.3,
metalness: 0.3,
uSpeed: { value: speed },
envMapIntensity: 10,
uNoiseIntensity: { value: noiseIntensity },
uScale: { value: scale }
}
});
const geometry = createStackedPlanesBufferGeometry(
THREE,
beamNumber,
beamWidth,
beamHeight,
0,
heightSegments
);
const mesh = new THREE.Mesh(geometry, material);
const group = new THREE.Group();
group.rotation.z = THREE.MathUtils.degToRad(rotation);
group.add(mesh);
const dir = new THREE.DirectionalLight(lightColor, 1);
dir.position.set(0, 3, 10);
group.add(dir);
scene.add(group);
scene.add(new THREE.AmbientLight(0xffffff, 1));
let playing = !reduceMotion;
let visible = true;
let raf = 0;
let last = performance.now();
let disposed = false;
function resize() {
const parent = canvas.parentElement || canvas;
const rect = parent.getBoundingClientRect();
const w = Math.max(1, Math.floor(rect.width));
const h = Math.max(1, Math.floor(rect.height));
const dpr = Math.min(window.devicePixelRatio || 1, reduceMotion ? 1 : 1.75);
renderer.setPixelRatio(dpr);
renderer.setSize(w, h, false);
camera.aspect = w / h;
camera.updateProjectionMatrix();
}
function frame(now) {
if (disposed) return;
raf = requestAnimationFrame(frame);
if (!playing || !visible || document.hidden) return;
const delta = Math.min(0.05, (now - last) / 1000);
last = now;
material.uniforms.time.value += 0.1 * delta;
renderer.render(scene, camera);
}
function renderOnce() {
renderer.render(scene, camera);
}
resize();
// Seed a bit of displacement so the first frame isn't flat
material.uniforms.time.value = reduceMotion ? 2.4 : 0.2;
renderOnce();
if (!reduceMotion) {
last = performance.now();
raf = requestAnimationFrame(frame);
}
const ro = new ResizeObserver(() => {
resize();
renderOnce();
});
ro.observe(canvas.parentElement || canvas);
return {
setPlaying(next) {
playing = !!next && !reduceMotion;
if (playing && !raf) {
last = performance.now();
raf = requestAnimationFrame(frame);
}
},
setVisible(next) {
visible = !!next;
if (visible && playing && !raf) {
last = performance.now();
raf = requestAnimationFrame(frame);
}
if (visible) renderOnce();
},
setLightColor(hex) {
dir.color.set(hex);
renderOnce();
},
setBackground(hex) {
scene.background = new THREE.Color(hex);
renderer.setClearColor(hex, 1);
renderOnce();
},
resize,
destroy() {
disposed = true;
cancelAnimationFrame(raf);
raf = 0;
ro.disconnect();
geometry.dispose();
material.dispose();
renderer.dispose();
}
};
}
global.OSGBeamsHero = { create };
})(typeof window !== "undefined" ? window : globalThis);
-21
View File
@@ -1,21 +0,0 @@
The MIT License
Copyright © 2010-2023 three.js authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-7
View File
File diff suppressed because one or more lines are too long
-9
View File
@@ -1,9 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="36 367 952 291" fill="none" 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>

Before

Width:  |  Height:  |  Size: 1.3 KiB

+939 -1695
View File
File diff suppressed because it is too large Load Diff
+92 -25
View File
@@ -1,22 +1,28 @@
/*! /*!
* OSGKeyboard website interactions. * OSGKeyboard 主页交互。
* The page remains useful without JavaScript; this file adds language, * 无 JavaScript 时页面内容完整可读;本文件负责:
* theme, and compact mobile navigation controls. * 1. 中 / 英双语切换(data-zh / data-en 系列属性,含视频源)
* 2. 深浅色主题(默认跟随系统,手动选择后持久化)
* 3. 移动端导航开合
* 4. 导航栏滚动状态、滚动显现动效、离屏视频暂停
*/ */
(function () { (function () {
"use strict"; "use strict";
const root = document.documentElement; const root = document.documentElement;
const siteNav = document.getElementById("siteNav");
const languageButton = document.getElementById("languageToggle"); const languageButton = document.getElementById("languageToggle");
const themeButton = document.getElementById("themeToggle"); const themeButton = document.getElementById("themeToggle");
const themeIcon = themeButton?.querySelector(".material-symbols-rounded"); const themeIconUse = document.getElementById("themeIconUse");
const menuButton = document.getElementById("menuToggle"); const menuButton = document.getElementById("menuToggle");
const menuIcon = menuButton?.querySelector(".material-symbols-rounded"); const menuIconUse = document.getElementById("menuIconUse");
const mobileNav = document.getElementById("mobileNav"); const mobileNav = document.getElementById("mobileNav");
const themeColor = document.querySelector('meta[name="theme-color"]'); const themeColor = document.querySelector('meta[name="theme-color"]');
const themeMedia = window.matchMedia("(prefers-color-scheme: dark)"); const themeMedia = window.matchMedia("(prefers-color-scheme: dark)");
const supportedLanguages = new Set(["zh", "en"]); const supportedLanguages = new Set(["zh", "en"]);
/* ---------------- 语言 ---------------- */
function preferredLanguage() { function preferredLanguage() {
const query = new URLSearchParams(window.location.search).get("lang"); const query = new URLSearchParams(window.location.search).get("lang");
if (supportedLanguages.has(query)) return query; if (supportedLanguages.has(query)) return query;
@@ -42,6 +48,7 @@
element.alt = element.dataset[`${lang}Alt`] || ""; element.alt = element.dataset[`${lang}Alt`] || "";
}); });
// 同时覆盖 <img> 与 <video>:切换语言后视频自动重新加载播放
document.querySelectorAll("[data-zh-src][data-en-src]").forEach((element) => { document.querySelectorAll("[data-zh-src][data-en-src]").forEach((element) => {
element.src = element.dataset[`${lang}Src`] || ""; element.src = element.dataset[`${lang}Src`] || "";
}); });
@@ -58,14 +65,15 @@
element.setAttribute("content", element.dataset[`${lang}Content`] || ""); element.setAttribute("content", element.dataset[`${lang}Content`] || "");
}); });
// 实机截图按 语言 + 主题 组合切换
document.querySelectorAll("[data-shot]").forEach((image) => { document.querySelectorAll("[data-shot]").forEach((image) => {
const theme = root.dataset.theme || "light"; const theme = root.dataset.theme || "light";
image.src = `assets/screenshots/${lang}/${theme}/${image.dataset.shot}`; image.src = `assets/screenshots/${lang}/${theme}/${image.dataset.shot}`;
}); });
document.title = lang === "zh" document.title = lang === "zh"
? "OSGKeyboard — 语音输入、中英打字与 AI 助手" ? "OSGKeyboard — 开口即文字"
: "OSGKeyboard — Voice input, bilingual typing, and an AI assistant"; : "OSGKeyboard — Say it. It’s typed.";
if (languageButton) { if (languageButton) {
languageButton.textContent = lang === "zh" ? "EN" : "中文"; languageButton.textContent = lang === "zh" ? "EN" : "中文";
@@ -97,33 +105,21 @@
} }
} }
/* ---------------- 主题 ---------------- */
function storedTheme() { function storedTheme() {
const saved = localStorage.getItem("osg-site-theme"); const saved = localStorage.getItem("osg-site-theme");
return saved === "light" || saved === "dark" ? saved : null; return saved === "light" || saved === "dark" ? saved : null;
} }
function setMenuOpen(isOpen) {
if (!menuButton || !mobileNav) return;
const open = Boolean(isOpen);
menuButton.setAttribute("aria-expanded", String(open));
mobileNav.hidden = !open;
if (menuIcon) menuIcon.textContent = open ? "close" : "menu";
const lang = root.dataset.lang || "zh";
menuButton.setAttribute(
"aria-label",
lang === "zh"
? (open ? "关闭导航" : "打开导航")
: (open ? "Close navigation" : "Open navigation")
);
}
function applyTheme(theme, persist) { function applyTheme(theme, persist) {
const resolved = theme === "dark" ? "dark" : "light"; const resolved = theme === "dark" ? "dark" : "light";
root.dataset.theme = resolved; root.dataset.theme = resolved;
root.style.colorScheme = resolved; root.style.colorScheme = resolved;
if (themeIcon) { // 图标指向「点击后切换到的目标主题」
themeIcon.textContent = resolved === "dark" ? "light_mode" : "dark_mode"; if (themeIconUse) {
themeIconUse.setAttribute("href", resolved === "dark" ? "#i-sun" : "#i-moon");
} }
if (themeButton) { if (themeButton) {
@@ -134,7 +130,8 @@
themeButton.setAttribute("aria-label", label); themeButton.setAttribute("aria-label", label);
} }
themeColor?.setAttribute("content", resolved === "dark" ? "#0c0e0f" : "#eef2f6"); // 深色舞台在各主题下配色一致,theme-color 跟随舞台色
themeColor?.setAttribute("content", "#0e1013");
document.querySelectorAll("[data-shot]").forEach((image) => { document.querySelectorAll("[data-shot]").forEach((image) => {
const lang = root.dataset.lang || "zh"; const lang = root.dataset.lang || "zh";
@@ -144,6 +141,25 @@
if (persist) localStorage.setItem("osg-site-theme", resolved); if (persist) localStorage.setItem("osg-site-theme", resolved);
} }
/* ---------------- 移动端导航 ---------------- */
function setMenuOpen(isOpen) {
if (!menuButton || !mobileNav) return;
const open = Boolean(isOpen);
menuButton.setAttribute("aria-expanded", String(open));
mobileNav.hidden = !open;
if (menuIconUse) menuIconUse.setAttribute("href", open ? "#i-close" : "#i-menu");
const lang = root.dataset.lang || "zh";
menuButton.setAttribute(
"aria-label",
lang === "zh"
? (open ? "关闭导航" : "打开导航")
: (open ? "Close navigation" : "Open navigation")
);
}
/* ---------------- 初始化 ---------------- */
const initialLanguage = preferredLanguage(); const initialLanguage = preferredLanguage();
const initialTheme = storedTheme() || (themeMedia.matches ? "dark" : "light"); const initialTheme = storedTheme() || (themeMedia.matches ? "dark" : "light");
applyLanguage(initialLanguage, false); applyLanguage(initialLanguage, false);
@@ -173,4 +189,55 @@
themeMedia.addEventListener("change", (event) => { themeMedia.addEventListener("change", (event) => {
if (!storedTheme()) applyTheme(event.matches ? "dark" : "light", false); if (!storedTheme()) applyTheme(event.matches ? "dark" : "light", false);
}); });
/* ---------------- 导航栏滚动状态 ---------------- */
function updateNavState() {
siteNav?.classList.toggle("is-scrolled", window.scrollY > 24);
}
updateNavState();
window.addEventListener("scroll", updateNavState, { passive: true });
/* ---------------- 滚动显现与离屏视频暂停 ---------------- */
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduceMotion || !("IntersectionObserver" in window)) {
document.querySelectorAll(".reveal").forEach((element) => {
element.classList.add("is-visible");
});
} else {
const revealObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("is-visible");
revealObserver.unobserve(entry.target);
}
});
},
{ threshold: 0.12, rootMargin: "0px 0px -8% 0px" }
);
document.querySelectorAll(".reveal").forEach((element) => {
revealObserver.observe(element);
});
// 视频滚出视口时暂停,回来时恢复,省电且避免分散注意力
const videoObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
const video = entry.target;
if (entry.isIntersecting) {
video.play().catch(() => {});
} else {
video.pause();
}
});
},
{ threshold: 0.15 }
);
document.querySelectorAll("video[autoplay]").forEach((video) => {
videoObserver.observe(video);
});
}
})(); })();
+412 -353
View File
@@ -3,15 +3,15 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>OSGKeyboard — 语音输入、中英打字与 AI 助手</title> <title>OSGKeyboard — 开口即文字</title>
<meta <meta
name="description" name="description"
content="OSGKeyboard 是一款适用于 iPhone、iPad 和 Mac 的语音与双语输入工具。iOS、iPadOS 版集成听写、中英输入、AI 助手、剪贴板技能和可选的托管积分服务,并默认使用端侧语音识别。" content="OSGKeyboard 是面向 iPhone、iPad 与 Mac 的语音输入工具:一把键盘完成听写、中英打字、AI 助手与剪贴板技能,默认端侧识别,核心功能无需账号。"
data-zh-content="OSGKeyboard 是一款适用于 iPhone、iPad 和 Mac 的语音与双语输入工具。iOS、iPadOS 版集成听写、中英输入、AI 助手、剪贴板技能和可选的托管积分服务,并默认使用端侧语音识别。" data-zh-content="OSGKeyboard 是面向 iPhone、iPad 与 Mac 的语音输入工具:一把键盘完成听写、中英打字、AI 助手与剪贴板技能,默认端侧识别,核心功能无需账号。"
data-en-content="OSGKeyboard brings voice and bilingual input to iPhone, iPad, and Mac. Its custom keyboard for iOS and iPadOS combines dictation, Chinese and English typing, an AI assistant, clipboard skills, and optional managed credits—with on-device recognition by default." data-en-content="OSGKeyboard brings voice and bilingual input to iPhone, iPad, and Mac: dictation, Chinese and English typing, an AI assistant, and clipboard skills in one keyboard — on-device by default, no account required."
> >
<meta name="robots" content="index, follow"> <meta name="robots" content="index, follow">
<meta name="theme-color" content="#eef2f6"> <meta name="theme-color" content="#0e1013">
<meta name="color-scheme" content="light dark"> <meta name="color-scheme" content="light dark">
<link rel="canonical" href="https://hkgood.github.io/OSGKeyboard/"> <link rel="canonical" href="https://hkgood.github.io/OSGKeyboard/">
<link rel="alternate" hreflang="zh-Hans" href="https://hkgood.github.io/OSGKeyboard/"> <link rel="alternate" hreflang="zh-Hans" href="https://hkgood.github.io/OSGKeyboard/">
@@ -29,9 +29,9 @@
> >
<meta <meta
property="og:description" property="og:description"
content="用一把键盘完成听写、中英输入、AI 问答和剪贴板技能。默认使用端侧识别,也可配置自己的 API 密钥或使用托管积分服务。" content="一把键盘,四种输入方式:听写、中英打字、AI 助手与剪贴板技能。默认端侧识别,核心功能无需账号。"
data-zh-content="用一把键盘完成听写、中英输入、AI 问答和剪贴板技能。默认使用端侧识别,也可配置自己的 API 密钥或使用托管积分服务。" data-zh-content="一把键盘,四种输入方式:听写、中英打字、AI 助手与剪贴板技能。默认端侧识别,核心功能无需账号。"
data-en-content="One keyboard for dictation, Chinese and English typing, AI answers, and clipboard skills. On-device by default, with your own API key or optional managed credits." data-en-content="One keyboard, four ways to input: dictation, bilingual typing, an AI assistant, and clipboard skills. On-device by default, no account required."
> >
<meta property="og:image" content="https://hkgood.github.io/OSGKeyboard/assets/app-icon.png"> <meta property="og:image" content="https://hkgood.github.io/OSGKeyboard/assets/app-icon.png">
<meta name="twitter:card" content="summary"> <meta name="twitter:card" content="summary">
@@ -40,6 +40,7 @@
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="assets/site.css"> <link rel="stylesheet" href="assets/site.css">
<script> <script>
// 首屏前确定主题,避免闪烁;无保存偏好时跟随系统
(function () { (function () {
const saved = localStorage.getItem("osg-site-theme"); const saved = localStorage.getItem("osg-site-theme");
const dark = window.matchMedia("(prefers-color-scheme: dark)").matches; const dark = window.matchMedia("(prefers-color-scheme: dark)").matches;
@@ -55,8 +56,8 @@
"name": "OSGKeyboard", "name": "OSGKeyboard",
"operatingSystem": "iOS 26, iPadOS 26, macOS 15", "operatingSystem": "iOS 26, iPadOS 26, macOS 15",
"applicationCategory": "UtilitiesApplication", "applicationCategory": "UtilitiesApplication",
"softwareVersion": "2.0.1", "softwareVersion": "2.1.0",
"description": "A source-available voice and typing keyboard with on-device speech recognition by default, Chinese and English typing, an AI assistant, clipboard skills, providers configured with your own API key, and optional managed credits.", "description": "A source-available voice and typing keyboard with on-device speech recognition by default, Chinese and English typing, an AI assistant with preview-before-insert, clipboard skills, a learned personal style, providers configured with your own API key, and optional managed credits.",
"downloadUrl": "https://apps.apple.com/app/osgkeyboard/id6781553267", "downloadUrl": "https://apps.apple.com/app/osgkeyboard/id6781553267",
"url": "https://hkgood.github.io/OSGKeyboard/", "url": "https://hkgood.github.io/OSGKeyboard/",
"offers": { "offers": {
@@ -66,15 +67,17 @@
}, },
"featureList": [ "featureList": [
"On-device iOS speech recognition by default", "On-device iOS speech recognition by default",
"Unified dictation and AI assistant with preview before insert",
"Voice editing of the last inserted text",
"Chinese full pinyin and double pinyin typing", "Chinese full pinyin and double pinyin typing",
"English autocomplete, autocorrect, and next-word suggestions", "English autocomplete, autocorrect, and next-word suggestions",
"Unified dictation and AI assistant keyboard", "Clipboard history and AI skills with native EventKit export",
"Clipboard history and AI skills", "Learned personal speaking style and personalized replies",
"Voice editing of the last verified insertion", "Monthly usage calendar and personal dictionary",
"Optional Sign in with Apple and managed credits on iOS and iPadOS", "Optional Sign in with Apple and managed credits on iOS and iPadOS",
"Cloud speech and AI with your own API key", "Cloud speech and AI with your own API key",
"Mac Option-key global dictation with local Qwen3 MLX on Apple Silicon", "Mac Option-key global dictation with local Qwen3 MLX on Apple Silicon",
"Personal dictionary and optional iCloud sync" "Optional iCloud sync"
], ],
"license": "https://github.com/hkgood/OSGKeyboard/blob/main/LICENSE" "license": "https://github.com/hkgood/OSGKeyboard/blob/main/LICENSE"
}, },
@@ -112,6 +115,37 @@
</script> </script>
</head> </head>
<body> <body>
<!-- 内联 SVG 图标库:SF 风格描边图标,currentColor 随主题变色,零外部依赖 -->
<svg class="svg-defs" aria-hidden="true" focusable="false">
<defs>
<symbol id="i-menu" viewBox="0 0 24 24"><path d="M4 7h16M4 12h16M4 17h16"/></symbol>
<symbol id="i-close" viewBox="0 0 24 24"><path d="M6 6l12 12M18 6L6 18"/></symbol>
<symbol id="i-moon" viewBox="0 0 24 24"><path d="M20 13.5A8 8 0 1 1 10.5 4a6.6 6.6 0 0 0 9.5 9.5Z"/></symbol>
<symbol id="i-sun" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4.2"/><path d="M12 2.8v2M12 19.2v2M2.8 12h2M19.2 12h2M5.2 5.2l1.4 1.4M17.4 17.4l1.4 1.4M18.8 5.2l-1.4 1.4M6.6 17.4l-1.4 1.4"/></symbol>
<symbol id="i-plane" viewBox="0 0 24 24"><path d="M4.5 12.3 20.5 4l-4.8 16.5-4-5.9-7.2-2.3Z"/><path d="M11.7 14.6 20.5 4"/></symbol>
<symbol id="i-chip" viewBox="0 0 24 24"><rect x="7" y="7" width="10" height="10" rx="2"/><path d="M10 2.8V5M14 2.8V5M10 19v2.2M14 19v2.2M2.8 10H5M2.8 14H5M19 10h2.2M19 14h2.2"/><rect x="10.6" y="10.6" width="2.8" height="2.8"/></symbol>
<symbol id="i-user-off" viewBox="0 0 24 24"><circle cx="10" cy="7.8" r="3.4"/><path d="M4.2 20c.7-3.1 3-4.9 5.8-4.9 1.3 0 2.5.4 3.5 1.1M16.8 16.8l4 4M20.8 16.8l-4 4"/></symbol>
<symbol id="i-code" viewBox="0 0 24 24"><path d="M8.5 8 4 12l4.5 4M15.5 8 20 12l-4.5 4"/></symbol>
<symbol id="i-mic" viewBox="0 0 24 24"><rect x="9" y="3" width="6" height="11" rx="3"/><path d="M5.5 11a6.5 6.5 0 0 0 13 0M12 17.5V21M9 21h6"/></symbol>
<symbol id="i-sparkle" viewBox="0 0 24 24"><path d="M11 4l1.8 4.7L17.5 10.5l-4.7 1.8L11 17l-1.8-4.7L4.5 10.5l4.7-1.8L11 4Z"/><path d="M18 14.5l.9 2.3 2.3.9-2.3.9-.9 2.3-.9-2.3-2.3-.9 2.3-.9.9-2.3Z"/></symbol>
<symbol id="i-keyboard" viewBox="0 0 24 24"><rect x="3" y="6" width="18" height="12" rx="2.5"/><path d="M7 10h.01M11 10h.01M15 10h.01M17.5 10h.01M7 14h.01M17.5 14h.01M9.8 14h4.4"/></symbol>
<symbol id="i-paste" viewBox="0 0 24 24"><rect x="5.5" y="4.5" width="13" height="16" rx="2.5"/><path d="M9 4.8a3 3 0 0 1 6 0"/><path d="M12 10v6.2M9.3 13.6l2.7 2.7 2.7-2.7"/></symbol>
<symbol id="i-translate" viewBox="0 0 24 24"><path d="M4 6.5h9M8.5 4.5v2c0 3.3-2.4 6.6-4.3 8M6 9.8c1 2.3 3.3 4.6 5.7 5.6"/><path d="M13 20l4.3-9 4.3 9M14.5 16.8h5.6"/></symbol>
<symbol id="i-spellcheck" viewBox="0 0 24 24"><path d="M4 15.5 7.6 6l3.6 9.5M5.5 12.5h4.2"/><path d="M13.5 15.8l2.4 2.4 4.6-5"/></symbol>
<symbol id="i-reply" viewBox="0 0 24 24"><path d="M9.5 10.5 4.5 15l5 4.5"/><path d="M4.5 15h9a6.5 6.5 0 0 0 6.5-6.5V6"/></symbol>
<symbol id="i-summarize" viewBox="0 0 24 24"><path d="M5 6.5h14M5 11h14M5 15.5h9M5 20h6"/></symbol>
<symbol id="i-task" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><path d="M8.4 12.3l2.4 2.4 4.8-5"/></symbol>
<symbol id="i-event" viewBox="0 0 24 24"><rect x="4" y="5.5" width="16" height="15" rx="2.5"/><path d="M4 10.2h16M8.5 3.2v3.3M15.5 3.2v3.3"/><path d="M8 14.2h2.4M13.6 14.2H16M8 17.4h2.4"/></symbol>
<symbol id="i-note" viewBox="0 0 24 24"><path d="M6.5 3.5H15l4 4v11a2 2 0 0 1-2 2H6.5a2 2 0 0 1-2-2v-13a2 2 0 0 1 2-2Z"/><path d="M14.8 3.8V8H19"/><path d="M8.5 12.5h7M8.5 16h4.5"/></symbol>
<symbol id="i-nav" viewBox="0 0 24 24"><path d="M12 3.5 18.8 20 12 16.6 5.2 20 12 3.5Z"/></symbol>
<symbol id="i-shield" viewBox="0 0 24 24"><path d="M12 3 5 5.8v5.4c0 4.6 3 7.9 7 9.8 4-1.9 7-5.2 7-9.8V5.8L12 3Z"/><path d="M9 11.9l2.2 2.2 3.9-4.1"/></symbol>
<symbol id="i-key" viewBox="0 0 24 24"><circle cx="8" cy="15.5" r="4.3"/><path d="M11.1 12.4 20 3.5M16.6 7l2.9 2.9M13.9 9.7l2.1 2.1"/></symbol>
<symbol id="i-coin" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><path d="M12 7.3v9.4M15.2 9.2c-.6-.9-1.8-1.4-3.2-1.4-1.7 0-3 .9-3 2.2 0 3 6.1 1.6 6.1 4.4 0 1.4-1.4 2.3-3.1 2.3-1.4 0-2.6-.5-3.2-1.5"/></symbol>
<symbol id="i-terminal" viewBox="0 0 24 24"><rect x="3" y="4.5" width="18" height="15" rx="2.5"/><path d="M7 9.5 10 12l-3 2.5M12.5 14.8H17"/></symbol>
<symbol id="i-arrow-out" viewBox="0 0 24 24"><path d="M7 17 17 7M9.2 7H17v7.8"/></symbol>
</defs>
</svg>
<a <a
class="skip-link" class="skip-link"
href="#main" href="#main"
@@ -119,7 +153,7 @@
data-en="Skip to main content" data-en="Skip to main content"
>跳到主要内容</a> >跳到主要内容</a>
<header class="site-nav"> <header class="site-nav" id="siteNav">
<nav class="nav-shell" aria-label="主导航" data-zh-label="主导航" data-en-label="Primary navigation"> <nav class="nav-shell" aria-label="主导航" data-zh-label="主导航" data-en-label="Primary navigation">
<a class="brand" href="#top" aria-label="OSGKeyboard"> <a class="brand" href="#top" aria-label="OSGKeyboard">
<img src="assets/app-icon.png" width="30" height="30" alt=""> <img src="assets/app-icon.png" width="30" height="30" alt="">
@@ -127,7 +161,7 @@
</a> </a>
<div class="nav-links"> <div class="nav-links">
<a href="#assistant" data-zh="体验" data-en="Experience">体验</a> <a href="#assistant" data-zh="体验" data-en="Experience">体验</a>
<a href="#typing" data-zh="输入" data-en="Typing">输入</a> <a href="#personal" data-zh="个人化" data-en="Personal">个人化</a>
<a href="#privacy" data-zh="隐私" data-en="Privacy">隐私</a> <a href="#privacy" data-zh="隐私" data-en="Privacy">隐私</a>
<a href="#platforms" data-zh="平台" data-en="Platforms">平台</a> <a href="#platforms" data-zh="平台" data-en="Platforms">平台</a>
</div> </div>
@@ -142,43 +176,44 @@
data-zh-label="打开导航" data-zh-label="打开导航"
data-en-label="Open navigation" data-en-label="Open navigation"
> >
<span class="material-symbols-rounded" aria-hidden="true">menu</span> <svg class="i" aria-hidden="true"><use id="menuIconUse" href="#i-menu"/></svg>
</button> </button>
<button class="language-button" id="languageToggle" type="button" aria-label="切换到英文">EN</button> <button class="language-button" id="languageToggle" type="button" aria-label="切换到英文">EN</button>
<button class="icon-button" id="themeToggle" type="button" aria-label="切换到深色模式"> <button class="icon-button" id="themeToggle" type="button" aria-label="切换到深色模式">
<span class="material-symbols-rounded" aria-hidden="true">dark_mode</span> <svg class="i" aria-hidden="true"><use id="themeIconUse" href="#i-moon"/></svg>
</button> </button>
</div> </div>
</nav> </nav>
<nav class="mobile-nav" id="mobileNav" aria-label="移动端导航" data-zh-label="移动端导航" data-en-label="Mobile navigation" hidden> <nav class="mobile-nav" id="mobileNav" aria-label="移动端导航" data-zh-label="移动端导航" data-en-label="Mobile navigation" hidden>
<a href="#assistant" data-zh="体验" data-en="Experience">体验</a> <a href="#assistant" data-zh="体验" data-en="Experience">体验</a>
<a href="#typing" data-zh="输入" data-en="Typing">输入</a> <a href="#personal" data-zh="个人化" data-en="Personal">个人化</a>
<a href="#privacy" data-zh="隐私" data-en="Privacy">隐私</a> <a href="#privacy" data-zh="隐私" data-en="Privacy">隐私</a>
<a href="#platforms" data-zh="平台" data-en="Platforms">平台</a> <a href="#platforms" data-zh="平台" data-en="Platforms">平台</a>
</nav> </nav>
</header> </header>
<main id="main"> <main id="main">
<!-- Hero: one promise, one primary action --> <!-- Hero:深色产品舞台(两种主题下保持一致),文字居中 + 三端同框主视觉 -->
<section class="hero" id="top"> <section class="hero" id="top">
<div class="hero-orb" aria-hidden="true"></div> <div class="hero-glow" aria-hidden="true"></div>
<div class="hero-inner"> <div class="hero-inner">
<p class="launch-pill"> <p class="launch-pill reveal">
<span class="launch-dot" aria-hidden="true"></span> <span class="launch-dot" aria-hidden="true"></span>
<span data-zh="OSGKeyboard 2.0 · 适配 iOS / iPadOS 26" data-en="OSGKeyboard 2.0 · Built for iOS and iPadOS 26"> <span data-zh="OSGKeyboard 2.1 · 为 iOS / iPadOS 26 打造" data-en="OSGKeyboard 2.1 · Built for iOS and iPadOS 26">
OSGKeyboard 2.0 · 适配 iOS / iPadOS 26 OSGKeyboard 2.1 · 为 iOS / iPadOS 26 打造
</span> </span>
</p> </p>
<h1> <h1 class="reveal" style="--d:.06s">
<span data-zh="开口,文字直达输入框。" data-en="From voice to text.">开口,文字直达输入框。</span> <span data-zh="开口即文字。" data-en="Say it. It’s typed.">开口即文字。</span>
<span class="hero-muted" data-zh="无需离开当前 App。" data-en="Without leaving your current app.">无需离开当前 App。</span> <span class="hero-muted" data-zh="一把键盘,四种输入方式。" data-en="One keyboard, four ways to input.">一把键盘,四种输入方式。</span>
</h1> </h1>
<p <p
class="hero-copy" class="hero-copy reveal"
data-zh="在支持第三方键盘的输入框里听写、打字、提问和编辑。OSGKeyboard 把语音识别、中英输入与 AI 助手放进同一个键盘。" style="--d:.12s"
data-en="Dictate, type, ask, and edit in text fields that support custom keyboards. OSGKeyboard brings voice recognition, bilingual typing, and an AI assistant into one keyboard." data-zh="听写、中英打字、AI 助手与剪贴板技能,在同一个输入界面完成。默认端侧识别,核心功能无需账号。"
>在支持第三方键盘的输入框里听写、打字、提问和编辑。OSGKeyboard 把语音识别、中英输入与 AI 助手放进同一个键盘。</p> data-en="Dictation, bilingual typing, an AI assistant, and clipboard skills — all in one keyboard. On-device recognition by default, no account required."
<div class="hero-actions"> >听写、中英打字、AI 助手与剪贴板技能,在同一个输入界面完成。默认端侧识别,核心功能无需账号。</p>
<div class="hero-actions reveal" style="--d:.18s">
<a <a
class="store-badge" class="store-badge"
href="https://apps.apple.com/cn/app/osgkeyboard/id6781553267" href="https://apps.apple.com/cn/app/osgkeyboard/id6781553267"
@@ -197,6 +232,14 @@
height="48" height="48"
> >
</a> </a>
<a
class="ghost-action"
href="https://testflight.apple.com/join/c2Bz4qK9"
rel="noopener"
>
<svg class="i" aria-hidden="true"><use href="#i-plane"/></svg>
<span data-zh="TestFlight 公测" data-en="Join the TestFlight beta">TestFlight 公测</span>
</a>
<a <a
class="text-action" class="text-action"
href="https://github.com/hkgood/OSGKeyboard" href="https://github.com/hkgood/OSGKeyboard"
@@ -204,424 +247,440 @@
data-en="View source" data-en="View source"
>查看源码</a> >查看源码</a>
</div> </div>
<p <ul class="hero-trust reveal" style="--d:.24s" aria-label="产品承诺" data-zh-label="产品承诺" data-en-label="Product promises">
class="hero-note" <li><svg class="i" aria-hidden="true"><use href="#i-chip"/></svg><span data-zh="端侧优先" data-en="On-device first">端侧优先</span></li>
data-zh="iOS / iPadOS 26+ · 核心功能无需 OSG 账号 · 默认端侧识别" <li><svg class="i" aria-hidden="true"><use href="#i-user-off"/></svg><span data-zh="核心功能无需账号" data-en="No account required">核心功能无需账号</span></li>
data-en="iOS and iPadOS 26+ · No OSG account required for core features · On-device recognition by default" <li><svg class="i" aria-hidden="true"><use href="#i-code"/></svg><span data-zh="源码可见" data-en="Source available">源码可见</span></li>
>iOS / iPadOS 26+ · 核心功能无需 OSG 账号 · 默认端侧识别</p>
</div>
<a
class="scroll-cue"
href="#assistant"
aria-label="看看它如何工作"
data-zh-label="看看它如何工作"
data-en-label="See how it works"
>
<span data-zh="看看它如何工作" data-en="See how it works">看看它如何工作</span>
<span class="material-symbols-rounded" aria-hidden="true">south</span>
</a>
</section>
<!-- Unified assistant: demonstrate the core interaction before listing features -->
<section class="section product-section" id="assistant">
<div class="section-inner">
<div class="center-heading">
<p class="eyebrow" data-zh="语音与 AI 助手" data-en="Voice and AI assistant">语音与 AI 助手</p>
<h2 data-zh="说出想法,预览后再插入。" data-en="Speak your thought, preview the result, then insert it.">说出想法,预览后再插入。</h2>
<p
data-zh="轻点麦克风开始听写,长按直接向 AI 提问。结果会先供你预览,确认后再插入,让每一步都清晰可控。"
data-en="Tap the microphone to dictate, or hold it to ask AI. Preview the result before inserting it, so every step stays clear and under your control."
>轻点麦克风开始听写,长按直接向 AI 提问。结果会先供你预览,确认后再插入,让每一步都清晰可控。</p>
</div>
<div class="product-stage">
<div class="stage-bar">
<span class="stage-dots" aria-hidden="true"><i></i><i></i><i></i></span>
<span data-zh="支持第三方键盘的 App · 交互示意与实机画面" data-en="App with custom-keyboard support · Interaction illustration and real keyboard screenshot">支持第三方键盘的 App · 交互示意与实机画面</span>
<span class="stage-status" data-zh="本地优先" data-en="Local first">本地优先</span>
</div>
<div class="stage-content">
<div class="conversation">
<span class="conversation-label" data-zh="当前输入" data-en="Current input">当前输入</span>
<div class="message-bubble">
<span data-zh="帮我把刚才的内容整理清楚,但保留我的语气。" data-en="Make that clearer, but keep it in my voice.">
帮我把刚才的内容整理清楚,但保留我的语气。
</span>
</div>
<div class="insert-preview">
<span class="preview-kicker" data-zh="准备插入" data-en="Ready to insert">准备插入</span>
<p
data-zh="已理清内容,同时保留原有语气。"
data-en="The message is clearer while preserving your original tone."
>已理清内容,同时保留原有语气。</p>
<span class="text-cursor" aria-hidden="true"></span>
</div>
</div>
<figure
class="keyboard-capture"
aria-label="OSGKeyboard 自定义键盘实机画面"
data-zh-label="OSGKeyboard 自定义键盘实机画面"
data-en-label="Actual OSGKeyboard custom keyboard"
>
<img
src="assets/screenshots/zh/light/iphone-keyboard_recording.png"
data-shot="iphone-keyboard_recording.png"
data-zh-alt="OSGKeyboard 在 iPhone 输入框中进行语音输入"
data-en-alt="OSGKeyboard dictating into an iPhone text field"
alt="OSGKeyboard 在 iPhone 输入框中进行语音输入"
>
<figcaption data-zh="iPhone 实机键盘画面" data-en="Actual OSGKeyboard on iPhone">iPhone 实机键盘画面</figcaption>
</figure>
</div>
</div>
<div class="gesture-grid">
<article>
<span class="gesture-number">01</span>
<span class="material-symbols-rounded" aria-hidden="true">mic</span>
<h3 data-zh="说出需求" data-en="Say what you need">说出需求</h3>
<p data-zh="轻点听写,长按提问。" data-en="Tap to dictate, hold to ask.">轻点听写,长按提问。</p>
</article>
<article>
<span class="gesture-number">02</span>
<span class="material-symbols-rounded" aria-hidden="true">auto_fix_high</span>
<h3 data-zh="理解并处理" data-en="Understand and process">理解并处理</h3>
<p data-zh="润色文字、回答问题,或修改上次输入。" data-en="Polish text, answer a question, or revise the last input.">润色文字、回答问题,或修改上次输入。</p>
</article>
<article>
<span class="gesture-number">03</span>
<span class="material-symbols-rounded" aria-hidden="true">text_fields</span>
<h3 data-zh="确认后插入" data-en="Preview, then insert">确认后插入</h3>
<p data-zh="仅当光标位置和输入上下文未变化时,才会自动插入。" data-en="Auto-insert only if the cursor position and surrounding context have not changed.">仅当光标位置和输入上下文未变化时,才会自动插入。</p>
</article>
</div>
</div>
</section>
<!-- Voice scenario -->
<section class="section scene-section">
<div class="section-inner scene-layout">
<div class="scene-copy">
<p class="eyebrow" data-zh="语音输入" data-en="Voice input">语音输入</p>
<h2 data-zh="开口即文字,不打断思路。" data-en="Speak naturally. Keep your train of thought.">开口即文字,不打断思路。</h2>
<p
data-zh="完成系统授权后即可使用端侧识别。个人词典与上下文词语提示有助于提高名字、术语和常用表达的识别准确率。"
data-en="Start with on-device recognition—no account or cloud setup required. Hotwords, a personal dictionary, and contextual corrections improve recognition of names, terminology, and phrases you use often."
>完成系统授权后即可使用端侧识别。个人词典与上下文词语提示有助于提高名字、术语和常用表达的识别准确率。</p>
<ul class="quiet-list">
<li data-zh="默认使用 Apple 端侧语音识别" data-en="Apple on-device recognition by default">默认使用 Apple 端侧语音识别</li>
<li data-zh="云端识别与 AI 润色均为可选" data-en="Cloud recognition and AI polishing are always optional">云端识别与 AI 润色均为可选</li>
<li data-zh="可用语音修改最近一次由 OSGKeyboard 插入的文字" data-en="Revise the most recent text inserted by OSGKeyboard using your voice">可用语音修改最近一次由 OSGKeyboard 插入的文字</li>
</ul> </ul>
</div> </div>
<div class="voice-stage"> <figure class="hero-devices reveal" style="--d:.3s">
<div class="voice-meta"> <img
<span data-zh="端侧 · Apple 语音识别" data-en="On-device · Apple Speech Recognition">端侧 · Apple 语音识别</span> src="assets/hero-devices.png"
<span class="live-indicator"><i></i><span data-zh="听写中" data-en="Listening">听写中</span></span> width="1600"
</div> height="716"
<div class="transcript-card"> data-zh-alt="OSGKeyboard 在 iPad、Mac 与 iPhone 上同时使用"
<span class="transcript-before" data-zh="今天下午和产品团队确认一下" data-en="This afternoon, review the launch plan">今天下午和产品团队确认一下</span> data-en-alt="OSGKeyboard running on iPad, Mac, and iPhone at the same time"
<strong data-zh="新版本上线计划。" data-en="with the product team.">新版本上线计划。</strong> alt="OSGKeyboard 在 iPad、Mac 与 iPhone 上同时使用"
</div> fetchpriority="high"
<div class="waveform" aria-hidden="true"> >
<i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i> </figure>
</div> </section>
<div class="voice-footer">
<span data-zh="已识别 20 个字" data-en="10 words recognized">已识别 20 个字</span> <!-- 四种输入方式:锚点速览条 -->
<span data-zh="完成" data-en="Done">完成</span> <section class="section ways-section" id="ways">
</div> <div class="section-inner">
<div class="ways-grid">
<a class="way-card reveal" href="#assistant">
<svg class="i" aria-hidden="true"><use href="#i-mic"/></svg>
<h3 data-zh="语音听写" data-en="Dictation">语音听写</h3>
<p data-zh="轻点麦克风,端侧识别实时上屏。" data-en="Tap the mic; on-device recognition types as you speak.">轻点麦克风,端侧识别实时上屏。</p>
</a>
<a class="way-card reveal" href="#assistant" style="--d:.06s">
<svg class="i" aria-hidden="true"><use href="#i-sparkle"/></svg>
<h3 data-zh="长按问 AI" data-en="Hold to ask AI">长按问 AI</h3>
<p data-zh="回答先预览,确认再插入。" data-en="Answers stream into a preview — insert when ready.">回答先预览,确认再插入。</p>
</a>
<a class="way-card reveal" href="#typing" style="--d:.12s">
<svg class="i" aria-hidden="true"><use href="#i-keyboard"/></svg>
<h3 data-zh="中英打字" data-en="Bilingual typing">中英打字</h3>
<p data-zh="全拼双拼 + 英文补全纠错。" data-en="Full and double pinyin, plus English completion and correction.">全拼双拼 + 英文补全纠错。</p>
</a>
<a class="way-card reveal" href="#skills" style="--d:.18s">
<svg class="i" aria-hidden="true"><use href="#i-paste"/></svg>
<h3 data-zh="剪贴板技能" data-en="Clipboard skills">剪贴板技能</h3>
<p data-zh="复制之后,直接行动。" data-en="Copy, then take action.">复制之后,直接行动。</p>
</a>
</div> </div>
</div> </div>
</section> </section>
<!-- Chinese and English typing --> <!-- 语音 + AI 助手:实机视频为视觉主体 -->
<section class="section typing-section" id="typing"> <section class="section feature-section" id="assistant">
<div class="section-inner"> <div class="section-inner split-layout">
<div class="center-heading compact"> <div class="feature-copy reveal">
<p class="eyebrow" data-zh="中文与英文输入" data-en="Chinese and English typing">中文与英文输入</p> <p class="eyebrow" data-zh="语音 + AI 助手" data-en="Voice + AI assistant">语音 + AI 助手</p>
<h2 data-zh="不方便说话时,也能顺手打字。" data-en="When speaking is not convenient, typing stays effortless.">不方便说话时,也能顺手打字。</h2> <h2 data-zh="先预览,再插入。" data-en="Preview first. Insert when ready.">先预览,再插入。</h2>
</div>
<div class="typing-stories">
<article class="typing-story">
<div class="typing-visual chinese-visual">
<span class="typing-label" data-zh="全拼 / 双拼" data-en="Full pinyin / double pinyin">全拼 / 双拼</span>
<div class="composition-row"><strong>jintian</strong><span class="text-cursor" aria-hidden="true"></span></div>
<div class="candidate-row">
<span class="selected">今天</span><span>今天的</span><span>近天</span><span>金田</span>
</div>
<div class="mini-keyboard" aria-hidden="true">
<span>q</span><span>w</span><span>e</span><span>r</span><span>t</span><span>y</span><span>u</span><span>i</span><span>o</span><span>p</span>
</div>
</div>
<div class="story-copy">
<span class="story-index">01</span>
<h3 data-zh="全拼、双拼,输入方式由你选。" data-en="Choose full pinyin or double pinyin.">全拼、双拼,输入方式由你选。</h3>
<p <p
data-zh="支持全拼、自然码、小鹤、微软与搜狗双拼,同时提供候选分页、数字选词、滑动选词与中英混输。" data-zh="轻点麦克风开始听写,长按直接向 AI 提问。回答流式显示在键盘里,确认后才进入输入框——每一步都在你的掌控之中。"
data-en="Full pinyin plus Ziranma, Xiaohe, Microsoft, and Sogou double pinyin—with candidate paging, number selection, swiping, and mixed Chinese-English input." data-en="Tap the mic to dictate, or hold it to ask AI. Answers stream into the keyboard and reach the text field only when you confirm — every step stays under your control."
>支持全拼、自然码、小鹤、微软与搜狗双拼,同时提供候选分页、数字选词、滑动选词与中英混输。</p> >轻点麦克风开始听写,长按直接向 AI 提问。回答流式显示在键盘里,确认后才进入输入框——每一步都在你的掌控之中。</p>
</div> <ol class="step-list">
</article> <li>
<article class="typing-story reverse"> <span class="step-index">01</span>
<div class="typing-visual english-visual">
<span class="typing-label">English</span>
<div class="suggestion-row"><span>Meet</span><span class="selected">Meeting</span><span>Meetings</span></div>
<div class="english-line">Let's schedule a meetign<span class="correction-mark"></span></div>
<div class="correction-chip"><span class="material-symbols-rounded">spellcheck</span><span>meeting</span></div>
</div>
<div class="story-copy">
<span class="story-index">02</span>
<h3 data-zh="英文输入,同样顺手。" data-en="English typing that gets out of your way.">英文输入,同样顺手。</h3>
<p
data-zh="支持前缀补全、自动纠错、下一词预测、撤销自动纠错和句首自动大写,中英文切换也能保持流畅。"
data-en="Prefix completion, autocorrect, next-word prediction, candidate undo, and sentence capitalization keep bilingual typing fluid."
>支持前缀补全、自动纠错、下一词预测、撤销自动纠错和句首自动大写,中英文切换也能保持流畅。</p>
</div>
</article>
</div>
</div>
</section>
<!-- Clipboard and skill scenarios -->
<section class="section skills-section" id="skills">
<div class="section-inner">
<div class="scene-layout skills-layout">
<div class="skills-stage">
<div class="clipboard-source">
<span class="material-symbols-rounded" aria-hidden="true">content_copy</span>
<div> <div>
<small data-zh="刚刚复制" data-en="Just copied">刚刚复制</small> <h3 data-zh="说出需求" data-en="Say what you need">说出需求</h3>
<p data-zh="明天下午三点和产品团队确认新版上线计划。" data-en="Confirm the launch plan with the product team tomorrow at 3 PM."> <p data-zh="轻点听写,长按提问,语音与 AI 共用一个入口。" data-en="Tap to dictate, hold to ask — voice and AI share one entry.">轻点听写,长按提问,语音与 AI 共用一个入口。</p>
明天下午三点和产品团队确认新版上线计划。 </div>
</p> </li>
<li>
<span class="step-index">02</span>
<div>
<h3 data-zh="流式生成" data-en="Watch it stream">流式生成</h3>
<p data-zh="回答实时显示,可随时停下、修改或重来。" data-en="Answers appear live; stop, revise, or retry at any time.">回答实时显示,可随时停下、修改或重来。</p>
</div>
</li>
<li>
<span class="step-index">03</span>
<div>
<h3 data-zh="确认插入" data-en="Confirm the insert">确认插入</h3>
<p data-zh="仅当光标位置与输入上下文未变化时才自动上屏,还能口述修改最近一次插入的文字。" data-en="Auto-insert only when the cursor and context are unchanged — or revise the last inserted text by voice.">仅当光标位置与输入上下文未变化时才自动上屏,还能口述修改最近一次插入的文字。</p>
</div>
</li>
</ol>
</div>
<div class="media-frame reveal" style="--d:.12s">
<video
src="assets/whats-new/ai-voice-assistant-zh.mp4"
data-zh-src="assets/whats-new/ai-voice-assistant-zh.mp4"
data-en-src="assets/whats-new/ai-voice-assistant-en.mp4"
autoplay
muted
loop
playsinline
preload="metadata"
data-zh-label="OSGKeyboard 语音助手实机演示"
data-en-label="OSGKeyboard voice assistant recorded on device"
aria-label="OSGKeyboard 语音助手实机演示"
></video>
</div> </div>
</div> </div>
<span class="stage-arrow material-symbols-rounded" aria-hidden="true">south</span> </section>
<div class="skill-actions">
<span class="primary"><span class="material-symbols-rounded">reply</span><span data-zh="回复" data-en="Reply">回复</span></span> <!-- 中英打字 -->
<span><span class="material-symbols-rounded">summarize</span><span data-zh="总结" data-en="Summarize">总结</span></span> <section class="section feature-section section-alt" id="typing">
<span><span class="material-symbols-rounded">translate</span><span data-zh="翻译" data-en="Translate">翻译</span></span> <div class="section-inner split-layout reverse">
<span><span class="material-symbols-rounded">task_alt</span><span data-zh="待办" data-en="Tasks">待办</span></span> <div class="feature-copy reveal">
<span><span class="material-symbols-rounded">event</span><span data-zh="日程" data-en="Events">日程</span></span> <p class="eyebrow" data-zh="中英打字" data-en="Chinese and English typing">中英打字</p>
<span><span class="material-symbols-rounded">near_me</span><span data-zh="导航" data-en="Navigate">导航</span></span> <h2 data-zh="不方便说话时,打字同样顺手。" data-en="When speaking is not convenient, typing stays effortless.">不方便说话时,打字同样顺手。</h2>
<div class="mini-feature">
<h3><svg class="i" aria-hidden="true"><use href="#i-translate"/></svg><span data-zh="中文:输入方式由你选" data-en="Chinese: your scheme, your choice">中文:输入方式由你选</span></h3>
<p data-zh="全拼、自然码、小鹤、微软与搜狗双拼;模糊音、简拼排序、展开候选、数字与滑动选词、中英混输。" data-en="Full pinyin plus Ziranma, Xiaohe, Microsoft, and Sogou double pinyin — with fuzzy initials, candidate paging, number and swipe selection, and mixed input.">全拼、自然码、小鹤、微软与搜狗双拼;模糊音、简拼排序、展开候选、数字与滑动选词、中英混输。</p>
</div>
<div class="mini-feature">
<h3><svg class="i" aria-hidden="true"><use href="#i-spellcheck"/></svg><span data-zh="英文:补全、纠错、预测" data-en="English: complete, correct, predict">英文:补全、纠错、预测</span></h3>
<p data-zh="三格 QuickType、约 4 万词离线词表、前缀补全、自动纠错、下一词建议、撤销纠错与句首自动大写。" data-en="Three-slot QuickType, a 40k-word offline lexicon, prefix completion, autocorrect, next-word suggestions, undo, and sentence capitalization.">三格 QuickType、约 4 万词离线词表、前缀补全、自动纠错、下一词建议、撤销纠错与句首自动大写。</p>
</div>
<p
class="feature-note"
data-zh="一个个性词库,同时参与中文候选、英文建议、语音偏置与润色保护。"
data-en="One personal dictionary feeds Chinese candidates, English suggestions, speech biasing, and polish protection."
>一个个性词库,同时参与中文候选、英文建议、语音偏置与润色保护。</p>
</div>
<div class="media-frame reveal" style="--d:.12s">
<video
src="assets/whats-new/keyboard-typing.mp4"
autoplay
muted
loop
playsinline
preload="metadata"
data-zh-label="OSGKeyboard 中英打字实机演示"
data-en-label="OSGKeyboard bilingual typing recorded on device"
aria-label="OSGKeyboard 中英打字实机演示"
></video>
</div> </div>
</div> </div>
<div class="scene-copy"> </section>
<p class="eyebrow" data-zh="剪贴板与快捷技能" data-en="Clipboard and quick skills">剪贴板与快捷技能</p>
<!-- 剪贴板与技能 -->
<section class="section feature-section" id="skills">
<div class="section-inner split-layout">
<div class="feature-copy reveal">
<p class="eyebrow" data-zh="剪贴板与技能" data-en="Clipboard and skills">剪贴板与技能</p>
<h2 data-zh="复制之后,直接行动。" data-en="Copy, then take action.">复制之后,直接行动。</h2> <h2 data-zh="复制之后,直接行动。" data-en="Copy, then take action.">复制之后,直接行动。</h2>
<p <p
data-zh="从剪贴板历史中选择内容,生成回复、摘要或译文,也可提取待办、日程和地址并发起导航。剪贴板访问默认关闭,仅在你主动开启后读取。" data-zh="复制内容后,本地语义分析会从完整技能目录中推荐最多 5 个相关操作——全程不上传剪贴板原文。"
data-en="Use your clipboard history to draft a reply, summarize or translate text, create a task or event, or get directions. Clipboard access remains opt-in." data-en="After you copy, on-device semantic analysis picks up to five relevant actions from the full skill catalog — clipboard text never leaves the device for this."
>从剪贴板历史中选择内容,生成回复、摘要或译文,也可提取待办、日程和地址并发起导航。剪贴板访问默认关闭,仅在你主动开启后读取。</p> >复制内容后,本地语义分析会从完整技能目录中推荐最多 5 个相关操作——全程不上传剪贴板原文。</p>
<div class="skill-chips" aria-label="内置技能" data-zh-label="内置技能" data-en-label="Built-in skills">
<span><svg class="i" aria-hidden="true"><use href="#i-reply"/></svg><span data-zh="回复" data-en="Reply">回复</span></span>
<span><svg class="i" aria-hidden="true"><use href="#i-summarize"/></svg><span data-zh="总结" data-en="Summarize">总结</span></span>
<span><svg class="i" aria-hidden="true"><use href="#i-translate"/></svg><span data-zh="翻译" data-en="Translate">翻译</span></span>
<span><svg class="i" aria-hidden="true"><use href="#i-task"/></svg><span data-zh="待办" data-en="To-dos">待办</span></span>
<span><svg class="i" aria-hidden="true"><use href="#i-event"/></svg><span data-zh="日程" data-en="Events">日程</span></span>
<span><svg class="i" aria-hidden="true"><use href="#i-note"/></svg><span data-zh="备忘录" data-en="Notes">备忘录</span></span>
<span><svg class="i" aria-hidden="true"><use href="#i-nav"/></svg><span data-zh="导航" data-en="Navigate">导航</span></span>
</div> </div>
<ul class="quiet-list">
<li data-zh="待办与日程经原生 EventKit 写入,日历仅申请「仅写入」权限" data-en="To-dos and events write natively through EventKit; Calendar is write-only">待办与日程经原生 EventKit 写入,日历仅申请「仅写入」权限</li>
<li data-zh="剪贴板历史默认关闭,最多 15 条,仅存于本机" data-en="Clipboard history is off by default — 15 items max, stored only on device">剪贴板历史默认关闭,最多 15 条,仅存于本机</li>
<li data-zh="可创建自定义技能:名称、图标、提示词与可选快捷指令" data-en="Create custom skills with a name, icon, prompt, and an optional Shortcut">可创建自定义技能:名称、图标、提示词与可选快捷指令</li>
</ul>
</div> </div>
<div class="styles-strip"> <div class="media-frame reveal" style="--d:.12s">
<div> <video
<p class="eyebrow" data-zh="个性化润色" data-en="Personalized polish">个性化润色</p> src="assets/whats-new/clipboard-skills-zh.mp4"
<h3 data-zh="表达更清楚,语气还是你的。" data-en="Improve the writing without erasing your voice.">表达更清楚,语气还是你的。</h3> data-zh-src="assets/whats-new/clipboard-skills-zh.mp4"
</div> data-en-src="assets/whats-new/clipboard-skills-en.mp4"
<div class="style-options"> autoplay
<span data-zh="轻度润色" data-en="Light polish">轻度润色</span> muted
<span class="selected" data-zh="理清结构" data-en="Clarify structure">理清结构</span> loop
<span data-zh="正式表达" data-en="Formal">正式表达</span> playsinline
<span data-zh="日常聊天" data-en="Casual">日常聊天</span> preload="metadata"
</div> data-zh-label="OSGKeyboard 剪贴板技能实机演示"
data-en-label="OSGKeyboard clipboard skills recorded on device"
aria-label="OSGKeyboard 剪贴板技能实机演示"
></video>
</div> </div>
</div> </div>
</section> </section>
<!-- Privacy and service paths --> <!-- 个人化:2.1 核心卖点,真实界面卡片 + 实机视频 -->
<section class="section personal-section section-alt" id="personal">
<div class="section-inner">
<div class="center-heading reveal">
<p class="eyebrow" data-zh="越用越懂你" data-en="It learns how you sound">越用越懂你</p>
<h2 data-zh="表达更清楚,语气还是你的。" data-en="Clearer writing. Still your voice.">表达更清楚,语气还是你的。</h2>
<p
data-zh="从你确认过的真实输入中学习,而不是套用模板。所有偏好与词库只保存在本机。"
data-en="It learns from input you have actually confirmed — not from templates. Every preference and term stays on your device."
>从你确认过的真实输入中学习,而不是套用模板。所有偏好与词库只保存在本机。</p>
</div>
<div class="personal-grid">
<article class="personal-card reveal">
<div class="personal-media">
<img
src="assets/whats-new/personal-style-card-zh.png"
data-zh-src="assets/whats-new/personal-style-card-zh.png"
data-en-src="assets/whats-new/personal-style-card-en.png"
data-zh-alt="润色风格页面中的「生成专属风格」卡片"
data-en-alt="The Generate Your Style card on the Polish Styles page"
alt="润色风格页面中的「生成专属风格」卡片"
loading="lazy"
decoding="async"
>
</div>
<h3 data-zh="专属说话风格" data-en="A style that sounds like you">专属说话风格</h3>
<p data-zh="累积 2,500 个有效听写字符即可生成;优先学习你反复出现的口述与明确修改,保存前需你确认。" data-en="Unlocks after 2,500 effective dictated characters. It prioritizes your recurring phrasing and explicit edits, and nothing is saved without your review.">累积 2,500 个有效听写字符即可生成;优先学习你反复出现的口述与明确修改,保存前需你确认。</p>
</article>
<article class="personal-card reveal" style="--d:.08s">
<div class="personal-media">
<video
src="assets/whats-new/smart-reply-zh.mp4"
data-zh-src="assets/whats-new/smart-reply-zh.mp4"
data-en-src="assets/whats-new/smart-reply-en.mp4"
autoplay
muted
loop
playsinline
preload="metadata"
data-zh-label="个性回复实机演示"
data-en-label="Personalized replies recorded on device"
aria-label="个性回复实机演示"
></video>
</div>
<h3 data-zh="个性回复,三种语气" data-en="Replies in three tones">个性回复,三种语气</h3>
<p data-zh="普通、正式、轻松趣味,一键切换;回复偏好与听写偏好分别学习,选择反馈仅保留在本机。" data-en="Ordinary, formal, or playful — one tap apart. Reply and dictation preferences are learned separately, and choice feedback stays on device.">普通、正式、轻松趣味,一键切换;回复偏好与听写偏好分别学习,选择反馈仅保留在本机。</p>
</article>
<article class="personal-card reveal" style="--d:.16s">
<div class="personal-media">
<img
src="assets/whats-new/monthly-usage-zh.png"
data-zh-src="assets/whats-new/monthly-usage-zh.png"
data-en-src="assets/whats-new/monthly-usage-en.png"
data-zh-alt="首页当月听写字数日历卡片"
data-en-alt="Home monthly dictation-characters calendar card"
alt="首页当月听写字数日历卡片"
loading="lazy"
decoding="async"
>
</div>
<h3 data-zh="用量日历与个性词库" data-en="Usage calendar and personal terms">用量日历与个性词库</h3>
<p data-zh="当月听写字数一目了然;重复使用的名字与术语会进入个性词库,同时提升打字候选与语音识别。" data-en="See the month’s dictation at a glance. Names and terms you reuse join your personal dictionary, improving both typing candidates and recognition.">当月听写字数一目了然;重复使用的名字与术语会进入个性词库,同时提升打字候选与语音识别。</p>
</article>
</div>
</div>
</section>
<!-- 三种服务路径:端侧放大为主信任状(深色区块) -->
<section class="section privacy-section" id="privacy"> <section class="section privacy-section" id="privacy">
<div class="section-inner"> <div class="section-inner">
<div class="center-heading inverse"> <div class="center-heading inverse reveal">
<p class="eyebrow" data-zh="隐私与选择" data-en="Privacy and choice">隐私与选择</p> <p class="eyebrow" data-zh="隐私与选择" data-en="Privacy and choice">隐私与选择</p>
<h2 data-zh="数据如何处理,由你决定。" data-en="You choose how your data is processed.">数据如何处理,由你决定。</h2> <h2 data-zh="数据如何处理,由你决定。" data-en="You choose how your data is processed.">数据如何处理,由你决定。</h2>
<p <p
data-zh="默认优先使用端侧能力。需要云端识别或 AI 功能时,可配置自己的 API 密钥,或选择托管积分服务。每种处理方式都会清晰标示,决定权始终在你。" data-zh="默认优先使用端侧能力。需要云端识别或 AI 功能时,可配置自己的 API 密钥,或选择托管积分服务。"
data-en="On-device processing is the default. For cloud recognition or AI features, provide your own API key or use optional managed credits. The active option is always clearly shown." data-en="On-device processing is the default. For cloud recognition or AI features, bring your own API key or use optional managed credits."
>默认优先使用端侧能力。需要云端识别或 AI 功能时,可配置自己的 API 密钥,或选择托管积分服务。每种处理方式都会清晰标示,决定权始终在你。</p> >默认优先使用端侧能力。需要云端识别或 AI 功能时,可配置自己的 API 密钥,或选择托管积分服务。</p>
</div> </div>
<div class="service-paths"> <div class="service-paths">
<article> <article class="path-primary reveal">
<span class="path-number">01</span> <span class="path-badge" data-zh="默认" data-en="Default">默认</span>
<span class="material-symbols-rounded" aria-hidden="true">memory</span> <svg class="i path-icon" aria-hidden="true"><use href="#i-chip"/></svg>
<h3 data-zh="端侧优先" data-en="On-device first">端侧优先</h3> <h3 data-zh="端侧优先" data-en="On-device first">端侧优先</h3>
<p data-zh="iOS、iPadOS 默认使用 Apple 设备端语音识别;搭载 Apple 芯片的 Mac 默认使用 Qwen3 MLX,Apple 语音识别作为备用方案。" data-en="iOS and iPadOS use Apple on-device speech recognition by default. Apple Silicon Macs use Qwen3 MLX by default, with Apple Speech Recognition as a fallback."> <p data-zh="iPhone 与 iPad 默认使用 Apple 设备端语音识别;搭载 Apple 芯片的 Mac 默认运行本地 Qwen3 MLX 模型。原始录音不离开设备。" data-en="iPhone and iPad use Apple’s on-device speech recognition by default; Apple Silicon Macs run a local Qwen3 MLX model. Raw audio never leaves the device.">iPhone 与 iPad 默认使用 Apple 设备端语音识别;搭载 Apple 芯片的 Mac 默认运行本地 Qwen3 MLX 模型。原始录音不离开设备。</p>
iOS、iPadOS 默认使用 Apple 设备端语音识别;搭载 Apple 芯片的 Mac 默认使用 Qwen3 MLX,Apple 语音识别作为备用方案。 <span class="path-note" data-zh="无需 OSG 账号 · 音频留在设备" data-en="No OSG account · Audio stays on device">无需 OSG 账号 · 音频留在设备</span>
</p>
<span class="path-note" data-zh="无需 OSG 账号 · 音频留在设备" data-en="No OSG account required · Audio stays on device">无需 OSG 账号 · 音频留在设备</span>
</article> </article>
<article> <article class="reveal" style="--d:.08s">
<span class="path-number">02</span> <svg class="i path-icon" aria-hidden="true"><use href="#i-key"/></svg>
<span class="material-symbols-rounded" aria-hidden="true">key</span> <h3 data-zh="自备服务商" data-en="Your own API key">自备服务商</h3>
<h3 data-zh="使用自己的 API 密钥" data-en="Use your own API key">使用自己的 API 密钥</h3> <p data-zh="配置你选择的语音识别或 AI 服务商,请求直达服务商;凭证保存在系统钥匙串。" data-en="Configure the speech or AI provider you choose; requests go directly to them, and credentials stay in the system Keychain.">配置你选择的语音识别或 AI 服务商,请求直达服务商;凭证保存在系统钥匙串。</p>
<p data-zh="配置你选择的语音识别或 AI 服务商;凭证存储在系统钥匙串中。" data-en="Configure the speech-recognition or AI provider you choose. Credentials are securely stored in the system Keychain."> <span class="path-note" data-zh="无需 OSG 账号" data-en="No OSG account">无需 OSG 账号</span>
配置你选择的语音识别或 AI 服务商;凭证存储在系统钥匙串中。
</p>
<span class="path-note" data-zh="无需 OSG 账号 · 请求直达所选服务商" data-en="No OSG account required · Requests go directly to your provider">无需 OSG 账号 · 请求直达所选服务商</span>
</article> </article>
<article> <article class="reveal" style="--d:.16s">
<span class="path-number">03</span> <svg class="i path-icon" aria-hidden="true"><use href="#i-coin"/></svg>
<span class="material-symbols-rounded" aria-hidden="true">toll</span>
<h3 data-zh="托管积分" data-en="Managed credits">托管积分</h3> <h3 data-zh="托管积分" data-en="Managed credits">托管积分</h3>
<p data-zh="在 iOS、iPadOS 上,可通过 Apple 登录使用托管积分服务,无需自行管理 API 密钥。" data-en="On iOS and iPadOS, you can optionally use Sign in with Apple and managed credits without managing an API key."> <p data-zh="在 iPhone 与 iPad 上,可通过 Apple 登录使用托管语音与 AI,无需自行管理密钥。" data-en="On iPhone and iPad, optionally sign in with Apple to use managed speech and AI — no keys to manage.">在 iPhone 与 iPad 上,可通过 Apple 登录使用托管语音与 AI,无需自行管理密钥。</p>
在 iOS、iPadOS 上,可通过 Apple 登录使用托管积分服务,无需自行管理 API 密钥。 <span class="path-note" data-zh="主动选择后启用 · 可注销账号" data-en="Explicit opt-in · Deletion available">主动选择后启用 · 可注销账号</span>
</p>
<span class="path-note" data-zh="主动选择后启用 · 可注销账号" data-en="Explicit opt-in · Account deletion available">主动选择后启用 · 可注销账号</span>
</article> </article>
</div> </div>
<div class="privacy-proof"> <div class="privacy-proof reveal">
<div> <div>
<span class="material-symbols-rounded" aria-hidden="true">shield_lock</span> <svg class="i" aria-hidden="true"><use href="#i-shield"/></svg>
<h3 data-zh="不会采集日常键入内容;剪贴板访问需由你主动开启。" data-en="We do not collect what you type during normal keyboard use. Clipboard access is opt-in."> <h3 data-zh="击键不上传;剪贴板访问需由你主动开启;不集成第三方追踪 SDK。" data-en="Keystrokes are never uploaded. Clipboard access is opt-in. No third-party tracking SDKs.">
不会采集日常键入内容;剪贴板访问需由你主动开启。 击键不上传;剪贴板访问需由你主动开启;不集成第三方追踪 SDK。
</h3> </h3>
</div> </div>
<a class="inverse-link" href="privacy/"> <a class="inverse-link" href="privacy/">
<span data-zh="阅读完整隐私政策" data-en="Read the full privacy policy">阅读完整隐私政策</span> <span data-zh="阅读完整隐私政策" data-en="Read the full privacy policy">阅读完整隐私政策</span>
<span class="material-symbols-rounded" aria-hidden="true">arrow_outward</span> <svg class="i" aria-hidden="true"><use href="#i-arrow-out"/></svg>
</a> </a>
</div> </div>
</div> </div>
</section> </section>
<!-- Platform capabilities --> <!-- 可选账号与积分 -->
<section class="section platforms-section" id="platforms"> <section class="section account-section" id="account">
<div class="section-inner split-layout">
<div class="feature-copy reveal">
<p class="eyebrow" data-zh="可选账号与积分" data-en="Optional account and credits">可选账号与积分</p>
<h2 data-zh="账号是选项,不是门槛。" data-en="An account is an option, not a gate.">账号是选项,不是门槛。</h2>
<p
data-zh="本地听写和自备服务商始终可以独立使用。登录只在你需要托管积分时发生。"
data-en="On-device dictation and your own providers always work on their own. Sign-in only happens when you want managed credits."
>本地听写和自备服务商始终可以独立使用。登录只在你需要托管积分时发生。</p>
<ul class="quiet-list">
<li data-zh="首次引导以匿名凭证送 4 次免费体验,不创建账号、不消耗积分" data-en="First-run guidance includes 4 free guided trials via anonymous credentials — no account, no credits spent">首次引导以匿名凭证送 4 次免费体验,不创建账号、不消耗积分</li>
<li data-zh="Apple 登录即可领取符合条件的 1,000 积分注册奖励" data-en="Sign in with Apple to claim an eligible 1,000-credit welcome reward">Apple 登录即可领取符合条件的 1,000 积分注册奖励</li>
<li data-zh="邀请好友,双方均可获得积分奖励" data-en="Invite friends and you both earn credits">邀请好友,双方均可获得积分奖励</li>
<li data-zh="账号令牌不下发给键盘扩展;应用内可注销账号" data-en="Account tokens never reach the keyboard extension; deletion is available in-app">账号令牌不下发给键盘扩展;应用内可注销账号</li>
</ul>
</div>
<div class="media-frame reveal" style="--d:.12s">
<img
src="assets/whats-new/osg-credits-zh.png"
data-zh-src="assets/whats-new/osg-credits-zh.png"
data-en-src="assets/whats-new/osg-credits-en.png"
data-zh-alt="OSGKeyboard 账号中的积分余额"
data-en-alt="Credit balance in an OSGKeyboard account"
alt="OSGKeyboard 账号中的积分余额"
loading="lazy"
decoding="async"
>
</div>
</div>
</section>
<!-- 平台:实机截图随语言与主题自动切换 -->
<section class="section platforms-section section-alt" id="platforms">
<div class="section-inner"> <div class="section-inner">
<div class="center-heading compact"> <div class="center-heading reveal">
<p class="eyebrow" data-zh="跨平台" data-en="Across platforms">跨平台</p> <p class="eyebrow" data-zh="跨平台" data-en="Across platforms">跨平台</p>
<h2 data-zh="在 iPhone、iPad 上输入,在 Mac 上听写。" data-en="Type on iPhone and iPad. Dictate on Mac.">在 iPhone、iPad 上输入,在 Mac 上听写。</h2> <h2 data-zh="在 iPhone、iPad 上输入,在 Mac 上听写。" data-en="Type on iPhone and iPad. Dictate on Mac.">在 iPhone、iPad 上输入,在 Mac 上听写。</h2>
</div> </div>
<div class="platform-stories"> <div class="platform-grid">
<article> <article class="platform-card reveal">
<div class="platform-symbol ios-symbol" aria-hidden="true"> <div class="media-frame">
<svg viewBox="0 0 48 48" focusable="false"> <img
<rect x="8" y="5" width="32" height="38" rx="7"></rect> src="assets/screenshots/zh/light/iphone-home.png"
<path d="M15 17h3m5 0h3m5 0h3M15 23h3m5 0h3m5 0h3M15 29h18M20 36h8"></path> data-shot="iphone-home.png"
</svg> data-zh-alt="iPhone 上的 OSGKeyboard 首页"
data-en-alt="OSGKeyboard Home on iPhone"
alt="iPhone 上的 OSGKeyboard 首页"
loading="lazy"
decoding="async"
>
</div> </div>
<div>
<span class="platform-label">iOS / iPadOS 26+</span> <span class="platform-label">iOS / iPadOS 26+</span>
<h3 data-zh="一把键盘,覆盖完整输入流程" data-en="A complete input workspace in a custom keyboard">一把键盘,覆盖完整输入流程</h3> <h3 data-zh="一把键盘,覆盖完整输入流程" data-en="A complete input workspace in one keyboard">一把键盘,覆盖完整输入流程</h3>
<p data-zh="听写、AI 助手、中文与英文输入、剪贴板技能、个人词典和可选 iCloud 同步。" data-en="Dictation, AI assistant, Chinese and English typing, clipboard skills, personal dictionary, and optional iCloud sync."> <p data-zh="听写、AI 助手、中文与英文输入、剪贴板技能、个人词典和可选 iCloud 同步。" data-en="Dictation, AI assistant, Chinese and English typing, clipboard skills, personal dictionary, and optional iCloud sync.">听写、AI 助手、中文与英文输入、剪贴板技能、个人词典和可选 iCloud 同步。</p>
听写、AI 助手、中文与英文输入、剪贴板技能、个人词典和可选 iCloud 同步。
</p>
</div>
</article> </article>
<article> <article class="platform-card reveal" style="--d:.1s">
<div class="platform-symbol mac-symbol" aria-hidden="true"> <div class="media-frame">
<svg viewBox="0 0 48 48" focusable="false"> <img
<path d="M7 10h11l17 28h6M26 10h15"></path> src="assets/screenshots/zh/light/mac-recording.png"
</svg> data-shot="mac-recording.png"
data-zh-alt="Mac 上的 OSGKeyboard 听写浮窗"
data-en-alt="OSGKeyboard dictation overlay on Mac"
alt="Mac 上的 OSGKeyboard 听写浮窗"
loading="lazy"
decoding="async"
>
</div> </div>
<div>
<span class="platform-label">macOS 15+</span> <span class="platform-label">macOS 15+</span>
<h3 data-zh="按住 Option 键,在支持文本输入的位置,开始听写" data-en="Hold Option to start dictation where text input is supported">按住 Option 键,在支持文本输入的位置,开始听写</h3> <h3 data-zh="按住 Option,开口即文字" data-en="Hold Option and speak">按住 Option,开口即文字</h3>
<p data-zh="支持 Option 全局热键和屏幕底部的实时听写浮窗;搭载 Apple 芯片的 Mac 使用 Qwen3 MLX 本地模型,并以 Apple 语音识别作为备用方案。" data-en="Use the Option hotkey and a live dictation overlay at the bottom of the screen. Apple Silicon Macs use a local Qwen3 MLX model, with Apple Speech Recognition as a fallback."> <p data-zh="全局热键加屏幕底部的实时听写浮窗;Apple 芯片 Mac 默认使用 Qwen3 MLX 本地模型,Apple 语音识别作为备用。" data-en="A global hotkey with a live dictation overlay at the bottom of the screen. Apple Silicon Macs use a local Qwen3 MLX model, with Apple Speech Recognition as fallback.">全局热键加屏幕底部的实时听写浮窗;Apple 芯片 Mac 默认使用 Qwen3 MLX 本地模型,Apple 语音识别作为备用。</p>
支持 Option 全局热键和屏幕底部的实时听写浮窗;搭载 Apple 芯片的 Mac 使用 Qwen3 MLX 本地模型,并以 Apple 语音识别作为备用方案。
</p>
</div>
</article> </article>
</div> </div>
<p class="platform-footnote"> <p class="platform-footnote reveal">
<span data-zh="目前公开下载的 Mac 安装包(DMG)仍为 1.1 历史版本;如需 Mac 2.0.1 的最新功能,请从源码构建。" data-en="The publicly available Mac DMG is still version 1.1; build from source to use the latest Mac 2.0.1 capabilities."> <span data-zh="公开下载的 Mac DMG 是已签名并公证的历史版本 1.1;仓库中的 Mac 源码已演进至 2.2,最新功能请用 Xcode 26 从源码构建。" data-en="The signed, notarized Mac DMG available for download is the older version 1.1. The Mac source in the repository has moved on to 2.2 — build with Xcode 26 for the latest capabilities.">
目前公开下载的 Mac 安装包(DMG)仍为 1.1 历史版本;如需 Mac 2.0.1 的最新功能,请从源码构建。 公开下载的 Mac DMG 是已签名并公证的历史版本 1.1;仓库中的 Mac 源码已演进至 2.2,最新功能请用 Xcode 26 从源码构建。
</span> </span>
</p> </p>
</div> </div>
</section> </section>
<!-- Getting started and stable capabilities --> <!-- 稳定性信任条 -->
<section class="section start-section" id="start"> <section class="section reliability-section">
<div class="section-inner"> <div class="section-inner">
<div class="split-heading"> <div class="reliability-strip reveal">
<div> <span class="strip-title" data-zh="稳定性同样是功能" data-en="Reliability is a feature">稳定性同样是功能</span>
<p class="eyebrow" data-zh="开始使用" data-en="Get started">开始使用</p>
<h2 data-zh="完成设置,开始语音输入。" data-en="Complete setup and start dictating.">完成设置,开始语音输入。</h2>
</div>
<p data-zh="完成系统授权后即可使用端侧识别;只有选择云端识别或 AI 功能时,才需额外配置。" data-en="After granting the required system permissions, you can use on-device recognition. Additional setup is needed only for cloud recognition or AI features.">
完成系统授权后即可使用端侧识别;只有选择云端识别或 AI 功能时,才需额外配置。
</p>
</div>
<ol class="setup-steps">
<li>
<h3 data-zh="安装并授权" data-en="Install and authorize">安装并授权</h3>
<p data-zh="在 iOS、iPadOS 上添加键盘并开启“完全访问”;在 Mac 上授予麦克风和辅助功能权限。" data-en="Add the keyboard and enable Full Access on iOS and iPadOS; grant microphone and Accessibility permissions on Mac.">
在 iOS、iPadOS 上添加键盘并开启“完全访问”;在 Mac 上授予麦克风和辅助功能权限。
</p>
</li>
<li>
<h3 data-zh="选择处理方式" data-en="Choose how input is processed">选择处理方式</h3>
<p data-zh="使用端侧识别,或按需添加自己的 API 密钥、登录后使用托管积分。" data-en="Use on-device recognition, add your own API key, or sign in to use managed credits.">
使用端侧识别,或按需添加自己的 API 密钥、登录后使用托管积分。
</p>
</li>
<li>
<h3 data-zh="开口或打字" data-en="Speak or type">开口或打字</h3>
<p data-zh="轻点开始听写、长按进行提问,或切换到中文或英文键盘。" data-en="Tap to dictate, hold to ask, or switch between Chinese and English typing.">
轻点开始听写、长按进行提问,或切换到中文或英文键盘。
</p>
</li>
</ol>
<div class="stable-strip">
<span data-zh="稳定性同样是功能" data-en="Reliability is a feature">稳定性同样是功能</span>
<ul> <ul>
<li data-zh="键盘启动保护" data-en="Reliable keyboard startup">键盘启动保护</li> <li data-zh="内存阈值主动降级" data-en="Proactive memory relief">内存阈值主动降级</li>
<li data-zh="内存不足时自动降级" data-en="Automatic fallback when memory is low">内存不足时自动降级</li> <li data-zh="MetricKit 本机诊断" data-en="On-device MetricKit diagnostics">MetricKit 本机诊断</li>
<li data-zh="确认输入位置后再插入" data-en="Insert only after confirming the input position">确认输入位置后再插入</li> <li data-zh="启动保护与中断恢复" data-en="Startup protection and recovery">启动保护与中断恢复</li>
<li data-zh="中断后可恢复" data-en="Recover after interruptions">中断后可恢复</li> <li data-zh="CI 校验跨端配置" data-en="CI-checked configuration">CI 校验跨端配置</li>
</ul> </ul>
</div> </div>
</div> </div>
</section> </section>
<!-- FAQ: quotable product and privacy boundaries --> <!-- FAQ -->
<section class="section faq-section" id="faq"> <section class="section faq-section section-alt" id="faq">
<div class="section-inner faq-layout"> <div class="section-inner faq-layout">
<div> <div class="reveal">
<p class="eyebrow" data-zh="常见问题" data-en="FAQ">常见问题</p> <p class="eyebrow" data-zh="常见问题" data-en="FAQ">常见问题</p>
<h2 data-zh="隐私和产品限制,提前说清楚。" data-en="Clear answers about privacy and product limits.">隐私和产品限制,提前说清楚。</h2> <h2 data-zh="隐私和产品限制,提前说清楚。" data-en="Clear answers about privacy and product limits.">隐私和产品限制,提前说清楚。</h2>
</div> </div>
<div class="faq-list"> <div class="faq-list">
<details class="faq-item"> <details class="faq-item reveal">
<summary data-zh="必须注册 OSG 账号吗?" data-en="Do I need an OSG account?">必须注册 OSG 账号吗?</summary> <summary data-zh="必须注册 OSG 账号吗?" data-en="Do I need an OSG account?">必须注册 OSG 账号吗?</summary>
<p data-zh="不需要。使用端侧听写或自行配置的服务商时,无需 OSG 账号。通过 Apple 登录仅用于托管积分、积分购买、邀请奖励和账号资料管理。" data-en="No. You do not need an OSG account for on-device dictation or when using providers you configure yourself. Sign in with Apple is used only for managed credits, credit purchases, referral rewards, and profile management."> <p data-zh="不需要。使用端侧听写或自行配置的服务商时,无需 OSG 账号。通过 Apple 登录仅用于托管积分、积分购买、邀请奖励和账号资料管理。" data-en="No. You do not need an OSG account for on-device dictation or when using providers you configure yourself. Sign in with Apple is used only for managed credits, credit purchases, referral rewards, and profile management.">
不需要。使用端侧听写或自行配置的服务商时,无需 OSG 账号。通过 Apple 登录仅用于托管积分、积分购买、邀请奖励和账号资料管理。 不需要。使用端侧听写或自行配置的服务商时,无需 OSG 账号。通过 Apple 登录仅用于托管积分、积分购买、邀请奖励和账号资料管理。
</p> </p>
</details> </details>
<details class="faq-item"> <details class="faq-item reveal">
<summary data-zh="录音会上传吗?" data-en="Does my audio get uploaded?">录音会上传吗?</summary> <summary data-zh="录音会上传吗?" data-en="Does my audio get uploaded?">录音会上传吗?</summary>
<p data-zh="使用默认的端侧识别时,录音不会上传。只有你明确选择自行配置的云端识别或托管积分云端识别时,音频才会离开设备,并发送至相应云端服务进行处理。" data-en="On-device recognition does not upload your recordings. Audio leaves the device only when you explicitly choose cloud recognition configured with your own API key or cloud recognition paid for with managed credits."> <p data-zh="使用默认的端侧识别时,录音不会上传。只有你明确选择自行配置的云端识别或托管积分云端识别时,音频才会离开设备,并发送至相应云端服务进行处理。" data-en="On-device recognition does not upload your recordings. Audio leaves the device only when you explicitly choose cloud recognition configured with your own API key or cloud recognition paid for with managed credits.">
使用默认的端侧识别时,录音不会上传。只有你明确选择自行配置的云端识别或托管积分云端识别时,音频才会离开设备,并发送至相应云端服务进行处理。 使用默认的端侧识别时,录音不会上传。只有你明确选择自行配置的云端识别或托管积分云端识别时,音频才会离开设备,并发送至相应云端服务进行处理。
</p> </p>
</details> </details>
<details class="faq-item"> <details class="faq-item reveal">
<summary data-zh="为什么键盘需要“完全访问”?" data-en="Why does the keyboard need Full Access?">为什么键盘需要“完全访问”?</summary> <summary data-zh="为什么键盘需要“完全访问”?" data-en="Why does the keyboard need Full Access?">为什么键盘需要“完全访问”?</summary>
<p data-zh="开启“完全访问”后,键盘才能与主 App 共享设置、读取已保存的服务配置,并在你主动启用后访问剪贴板。此权限不代表 OSGKeyboard 会收集日常键入内容。" data-en="Full Access lets the keyboard share settings with the OSGKeyboard app, read saved provider configurations, and access the clipboard only after you opt in. We do not collect what you type during normal keyboard use."> <p data-zh="开启“完全访问”后,键盘才能与主 App 共享设置、读取已保存的服务配置,并在你主动启用后访问剪贴板。此权限不代表 OSGKeyboard 会收集日常键入内容。" data-en="Full Access lets the keyboard share settings with the OSGKeyboard app, read saved provider configurations, and access the clipboard only after you opt in. We do not collect what you type during normal keyboard use.">
开启“完全访问”后,键盘才能与主 App 共享设置、读取已保存的服务配置,并在你主动启用后访问剪贴板。此权限不代表 OSGKeyboard 会收集日常键入内容。 开启“完全访问”后,键盘才能与主 App 共享设置、读取已保存的服务配置,并在你主动启用后访问剪贴板。此权限不代表 OSGKeyboard 会收集日常键入内容。
</p> </p>
</details> </details>
<details class="faq-item"> <details class="faq-item reveal">
<summary data-zh="OSGKeyboard 是开源软件吗?" data-en="Is OSGKeyboard open source?">OSGKeyboard 是开源软件吗?</summary> <summary data-zh="OSGKeyboard 是开源软件吗?" data-en="Is OSGKeyboard open source?">OSGKeyboard 是开源软件吗?</summary>
<p data-zh="不是。项目采用源码可见许可,允许个人学习与非商用本地使用;再分发、公开衍生版本和商业使用需要获得许可。" data-en="No. It uses a source-available license for personal learning and non-commercial local use. Redistribution, public derivative versions, and commercial use require permission."> <p data-zh="不是。项目采用源码可见许可,允许个人学习与非商用本地使用;再分发、公开衍生版本和商业使用需要获得许可。" data-en="No. It uses a source-available license for personal learning and non-commercial local use. Redistribution, public derivative versions, and commercial use require permission.">
不是。项目采用源码可见许可,允许个人学习与非商用本地使用;再分发、公开衍生版本和商业使用需要获得许可。 不是。项目采用源码可见许可,允许个人学习与非商用本地使用;再分发、公开衍生版本和商业使用需要获得许可。
</p> </p>
</details> </details>
<details class="faq-item"> <details class="faq-item reveal">
<summary data-zh="Mac 下载为什么是 1.1?" data-en="Why is the Mac download version 1.1?">Mac 下载为什么是 1.1?</summary> <summary data-zh="Mac 下载为什么是 1.1?" data-en="Why is the Mac download version 1.1?">Mac 下载为什么是 1.1?</summary>
<p data-zh="当前可下载、已签名并公证的 Mac 安装包为 1.1 版。仓库中的 Mac 版源码已更新至 2.0.1;如需最新功能,请在 macOS 上使用 Xcode 26 或更高版本自行构建。" data-en="The signed and notarized Mac installer currently available for download is version 1.1. The Mac source in the repository has been updated to version 2.0.1; build it on macOS with Xcode 26 or later to use the latest capabilities."> <p data-zh="当前可下载、已签名并公证的 Mac 安装包为 1.1 版。仓库中的 Mac 版源码已演进至 2.2;如需最新功能,请在 macOS 上使用 Xcode 26 或更高版本自行构建。" data-en="The signed and notarized Mac installer currently available for download is version 1.1. The Mac source in the repository has moved on to 2.2; build it on macOS with Xcode 26 or later to use the latest capabilities.">
当前可下载、已签名并公证的 Mac 安装包为 1.1 版。仓库中的 Mac 版源码已更新至 2.0.1;如需最新功能,请在 macOS 上使用 Xcode 26 或更高版本自行构建。 当前可下载、已签名并公证的 Mac 安装包为 1.1 版。仓库中的 Mac 版源码已演进至 2.2;如需最新功能,请在 macOS 上使用 Xcode 26 或更高版本自行构建。
</p> </p>
</details> </details>
</div> </div>
</div> </div>
</section> </section>
<!-- Download CTA --> <!-- 下载 CTA:与 Hero 呼应的深色舞台 -->
<section class="section download-section" id="download"> <section class="section download-section" id="download">
<div class="section-inner"> <div class="section-inner">
<div class="download-panel"> <div class="download-panel reveal">
<div> <div>
<p class="eyebrow" data-zh="开口即文字" data-en="Say it. It’s typed.">开口即文字</p> <p class="eyebrow" data-zh="开口即文字" data-en="Say it. It’s typed.">开口即文字</p>
<h2 data-zh="下一段文字,开口就能输入。" data-en="Your next sentence starts with your voice.">下一段文字,开口就能输入。</h2> <h2 data-zh="下一段文字,开口就能输入。" data-en="Your next sentence starts with your voice.">下一段文字,开口就能输入。</h2>
@@ -666,7 +725,7 @@
data-zh-href="https://github.com/hkgood/OSGKeyboard#从源码构建" data-zh-href="https://github.com/hkgood/OSGKeyboard#从源码构建"
data-en-href="https://github.com/hkgood/OSGKeyboard/blob/main/README.en.md#build-from-source" data-en-href="https://github.com/hkgood/OSGKeyboard/blob/main/README.en.md#build-from-source"
> >
<span class="material-symbols-rounded" aria-hidden="true">terminal</span> <svg class="i" aria-hidden="true"><use href="#i-terminal"/></svg>
<span data-zh="从源码构建" data-en="Build from source">从源码构建</span> <span data-zh="从源码构建" data-en="Build from source">从源码构建</span>
</a> </a>
</div> </div>
@@ -677,8 +736,8 @@
<footer class="site-footer"> <footer class="site-footer">
<div class="footer-inner"> <div class="footer-inner">
<span data-zh="OSGKeyboard 2.0.1 · 源码可见,非开源软件" data-en="OSGKeyboard 2.0.1 · Source-available, not open source"> <span data-zh="OSGKeyboard 2.1.0 · 源码可见,非开源软件" data-en="OSGKeyboard 2.1.0 · Source-available, not open source">
OSGKeyboard 2.0.1 · 源码可见,非开源软件 OSGKeyboard 2.1.0 · 源码可见,非开源软件
</span> </span>
<div class="footer-links"> <div class="footer-links">
<a href="privacy/" data-zh="隐私政策" data-en="Privacy">隐私政策</a> <a href="privacy/" data-zh="隐私政策" data-en="Privacy">隐私政策</a>