diff --git a/docs/assets/device/iphone-17-pro-silver.png b/docs/assets/device/iphone-17-pro-silver.png
new file mode 100644
index 0000000..5bebee0
Binary files /dev/null and b/docs/assets/device/iphone-17-pro-silver.png differ
diff --git a/docs/assets/site.css b/docs/assets/site.css
index 1e95bec..9871d49 100644
--- a/docs/assets/site.css
+++ b/docs/assets/site.css
@@ -56,23 +56,11 @@
--dur-slow: 0.95s;
/* 嵌入式手机演示的配色(随主题切换) */
- --ph-bg: #ffffff;
- --ph-ink: #0f1214;
- --ph-muted: #8a939c;
- --ph-line: rgba(15, 18, 20, 0.1);
- --ph-in: #e9ecef;
- --ph-out: #2c8a52;
/* 键盘复刻色:取自 App 源码 ColorLibrary(brandAccent / aiTeal / recordRed)与原生键面色 */
--kb-accent: #3aa05a;
--kb-teal: #2bafa4;
--kb-red: #ff3b30;
- --ph-kb: #efeeec;
- --ph-key: #ffffff;
- --ph-key-ink: #1c1c1e;
- --ph-key-off: #d6d6d6;
- --ph-track: rgba(0, 0, 0, 0.1);
- --ph-glass: rgba(255, 255, 255, 0.66);
}
html[data-theme="dark"] {
@@ -91,18 +79,6 @@ html[data-theme="dark"] {
--shadow-card: 0 28px 56px -28px rgba(0, 0, 0, 0.6);
--shadow-media: 0 32px 64px -32px rgba(0, 0, 0, 0.65);
- --ph-bg: #0b0c0e;
- --ph-ink: #f2f4f5;
- --ph-muted: #7c858d;
- --ph-line: rgba(255, 255, 255, 0.1);
- --ph-in: #23262b;
- --ph-out: #2f9a5b;
- --ph-kb: #232324;
- --ph-key: #525252;
- --ph-key-ink: #ffffff;
- --ph-key-off: #3b3b3b;
- --ph-track: #1a1a1a;
- --ph-glass: rgba(255, 255, 255, 0.09);
}
/* --------------------------------------------------------------------------
@@ -1032,7 +1008,7 @@ html.js .reveal.is-visible [data-split] .ch {
* -------------------------------------------------------------------------- */
.marquee {
overflow: hidden;
- padding: 20px 0;
+ padding: 12px 0;
border-bottom: 1px solid var(--line);
background: var(--canvas);
-webkit-mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
@@ -1059,10 +1035,10 @@ html.js .reveal.is-visible [data-split] .ch {
.marquee li {
display: flex;
align-items: center;
- gap: 30px;
- padding-right: 30px;
+ gap: 16px;
+ padding-right: 16px;
color: var(--ink-3);
- font-size: clamp(1.05rem, 1.8vw, 1.4rem);
+ font-size: clamp(0.6rem, 0.9vw, 0.72rem);
font-weight: 650;
letter-spacing: -0.01em;
white-space: nowrap;
@@ -1073,8 +1049,8 @@ html.js .reveal.is-visible [data-split] .ch {
}
.marquee li::after {
- width: 7px;
- height: 7px;
+ width: 4px;
+ height: 4px;
border-radius: 50%;
background: var(--accent);
content: "";
@@ -1142,11 +1118,17 @@ h3 {
grid-template-columns: 1.02fr 1fr;
}
+/* 账号段落只保留文字,限制行宽保证可读性 */
+.account-layout .feature-copy {
+ max-width: 760px;
+}
+
.split-layout.reverse .feature-copy {
order: 2;
}
-.split-layout.reverse .media-frame {
+.split-layout.reverse .media-frame,
+.split-layout.reverse .device-stage {
order: 1;
}
@@ -1279,9 +1261,8 @@ h3 {
/* --------------------------------------------------------------------------
* 9. 滚动叙事(三幕剧)
- * 消息类 m1/o1/m2/c2/o2/m3/c3/ev 与键盘 data-* 状态由 site.js 依滚动进度写到 .story 上;
- * 只有 .story.is-live(脚本已接管)时才进入「未发生 → 隐藏」的初始态,
- * 无 JavaScript 时手机直接展示最终画面。
+ * 滚动进度决定当前幕;手机里播放这一幕的真机录屏,幕内进度条跟随播放进度(--ap)。
+ * 无 JavaScript 时三幕文案平铺,手机停在第一幕的首帧。
* -------------------------------------------------------------------------- */
.story-section {
padding-bottom: 0;
@@ -1390,9 +1371,10 @@ h3 {
background: var(--accent);
transform: scaleX(var(--ap, 0));
transform-origin: left;
+ transition: transform 0.3s linear;
}
-/* 舞台:手机 + 背后呼吸光晕 */
+/* 舞台:真机 + 背后呼吸光晕 */
.story-device {
position: relative;
display: grid;
@@ -1407,956 +1389,73 @@ h3 {
background: radial-gradient(closest-side, color-mix(in srgb, var(--accent) 26%, transparent), transparent);
content: "";
filter: blur(30px);
- opacity: 0.55;
- transition: opacity 0.6s ease, transform 0.8s var(--ease-out);
+ animation: device-glow 6s ease-in-out infinite;
}
-.story:is([data-mic="rec"], [data-mic="busy"]) .story-device::before {
- opacity: 1;
- transform: scale(1.08);
-}
-
-/* 手机外壳:容器查询单位(cqw)让内部 UI 随机身等比缩放 */
-.phone {
- --w: min(340px, calc((100svh - 150px) * 0.468));
- position: relative;
- width: var(--w);
- aspect-ratio: 9 / 19.2;
- padding: calc(var(--w) * 0.034);
- border-radius: calc(var(--w) * 0.16);
- background: linear-gradient(145deg, #39404a, #0d0f12 60%);
- box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1),
- 0 60px 100px -40px rgba(0, 0, 0, 0.5), 0 30px 60px -30px rgba(16, 20, 24, 0.35);
- container-type: inline-size;
-}
-
-.phone-screen {
- position: relative;
- display: flex;
- overflow: hidden;
- height: 100%;
- flex-direction: column;
- border-radius: 12.6cqw;
- background: var(--ph-bg);
- color: var(--ph-ink);
- font-size: 4cqw;
- line-height: 1.38;
- text-align: left;
-}
-
-.phone-status {
- display: flex;
- height: 12cqw;
- flex: none;
- align-items: center;
- justify-content: space-between;
- padding: 1.6cqw 8cqw 0;
- font-size: 3.6cqw;
- font-weight: 650;
-}
-
-.phone-island {
- position: absolute;
- top: 2.8cqw;
- left: 50%;
- width: 27cqw;
- height: 7.6cqw;
- margin-left: -13.5cqw;
- border-radius: 99px;
- background: #000;
-}
-
-.phone-signal {
- display: flex;
- align-items: flex-end;
- gap: 0.7cqw;
- height: 2.8cqw;
-}
-
-.phone-signal i {
- width: 0.9cqw;
- border-radius: 0.4cqw;
- background: currentColor;
-}
-
-.phone-signal i:nth-child(1) { height: 40%; }
-.phone-signal i:nth-child(2) { height: 70%; }
-.phone-signal i:nth-child(3) { height: 100%; }
-
-.chat-head {
- display: flex;
- flex: none;
- flex-direction: column;
- align-items: center;
- gap: 1.2cqw;
- padding: 1.2cqw 0 3cqw;
- border-bottom: 1px solid var(--ph-line);
-}
-
-.chat-avatar {
- display: grid;
- width: 11cqw;
- height: 11cqw;
- place-items: center;
- border-radius: 50%;
- background: linear-gradient(145deg, #7fd0a0, #2c8a52);
- color: #fff;
- font-size: 4.4cqw;
- font-weight: 700;
-}
-
-.chat-name {
- display: grid;
- justify-items: center;
- line-height: 1.2;
-}
-
-.chat-name b {
- font-size: 3.4cqw;
- font-weight: 650;
-}
-
-.chat-name small {
- color: var(--ph-muted);
- font-size: 2.7cqw;
-}
-
-/* 底部对齐:新消息出现时把旧消息向上推,和真实聊天一样 */
-.chat-body {
- display: flex;
- overflow: hidden;
- min-height: 0;
- flex: 1;
- flex-direction: column;
- justify-content: flex-end;
- padding: 1cqw 4.4cqw 3cqw;
-}
-
-.bubble {
- position: relative;
- flex: none;
- max-width: 76%;
- margin-top: 2.4cqw;
- padding: 2.1cqw 3.4cqw;
- border-radius: 5cqw;
- font-size: 3.6cqw;
- transition: opacity 0.35s ease, transform 0.7s var(--ease-spring), box-shadow 0.4s ease,
- max-height 0.5s var(--ease-out), margin 0.5s var(--ease-out), padding 0.5s var(--ease-out);
-}
-
-.bubble.in {
- align-self: flex-start;
- border-bottom-left-radius: 1.4cqw;
- background: var(--ph-in);
- transform-origin: 0 100%;
-}
-
-.bubble.out {
- align-self: flex-end;
- border-bottom-right-radius: 1.4cqw;
- background: var(--ph-out);
- color: #fff;
- transform-origin: 100% 100%;
-}
-
-/* 未出现的气泡不占位(高度收为 0),出现时弹簧放大 */
-.story.is-live .bubble {
- max-height: 0;
- margin-top: 0;
- padding-block: 0;
- opacity: 0;
- transform: translateY(3cqw) scale(0.6);
-}
-
-.story.is-live.m1 .b-m1,
-.story.is-live.o1 .b-o1,
-.story.is-live.m2 .b-m2,
-.story.is-live.o2 .b-o2,
-.story.is-live.m3 .b-m3 {
- max-height: 40cqw;
- margin-top: 2.4cqw;
- padding-block: 2.1cqw;
- opacity: 1;
- transform: none;
-}
-
-/* 长按复制:气泡描边高亮 + 「已复制」提示 */
-.story.c2 .b-m2,
-.story.c3 .b-m3,
-.skills-demo.is-copied .sd-msg {
- box-shadow: 0 0 0 0.8cqw color-mix(in srgb, var(--kb-accent) 40%, transparent);
-}
-
-.copy-toast {
- position: absolute;
- top: -6.8cqw;
- left: 4cqw;
- display: flex;
- align-items: center;
- gap: 1cqw;
- padding: 1.1cqw 2.6cqw;
- border-radius: 99px;
- background: var(--ph-ink);
- color: var(--ph-bg);
- font-size: 2.9cqw;
- font-weight: 600;
- opacity: 0;
- transform: translateY(2cqw) scale(0.8);
- transition: opacity 0.3s ease, transform 0.6s var(--ease-spring);
-}
-
-.copy-toast .i {
- width: 3.4cqw;
- height: 3.4cqw;
- stroke-width: 2.4;
-}
-
-.story.c2 .b-m2 .copy-toast,
-.story.c3 .b-m3 .copy-toast,
-.skills-demo.is-copied .copy-toast {
- opacity: 1;
- transform: none;
-}
-
-/* 日历通知横幅:从刘海下方落下 */
-.event-banner {
- position: absolute;
- z-index: 3;
- top: 2.6cqw;
- right: 3.4cqw;
- left: 3.4cqw;
- display: flex;
- align-items: center;
- gap: 3cqw;
- padding: 3cqw 3.4cqw;
- border-radius: 6cqw;
- background: color-mix(in srgb, var(--ph-bg) 82%, transparent);
- -webkit-backdrop-filter: blur(20px) saturate(1.5);
- backdrop-filter: blur(20px) saturate(1.5);
- box-shadow: 0 6cqw 12cqw -4cqw rgba(0, 0, 0, 0.35), inset 0 0 0 1px var(--ph-line);
- transition: transform 0.9s var(--ease-spring), opacity 0.4s ease;
-}
-
-.story.is-live .event-banner {
- opacity: 0;
- transform: translateY(-130%) scale(0.92);
-}
-
-.story.is-live.ev .event-banner {
- opacity: 1;
- transform: none;
-}
-
-.event-icon {
- display: grid;
- width: 10cqw;
- height: 10cqw;
- flex: none;
- place-items: center;
- border-radius: 2.8cqw;
- background: var(--ph-out);
- color: #fff;
-}
-
-.event-icon .i {
- width: 5.4cqw;
- height: 5.4cqw;
-}
-
-.event-copy {
- display: grid;
- min-width: 0;
- flex: 1;
- line-height: 1.3;
-}
-
-.event-copy small {
- color: var(--ph-muted);
- font-size: 2.7cqw;
-}
-
-.event-copy b {
- font-size: 3.5cqw;
-}
-
-.event-copy span {
- overflow: hidden;
- color: var(--ph-muted);
- font-size: 2.9cqw;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.event-check {
- display: grid;
- width: 6.4cqw;
- height: 6.4cqw;
- flex: none;
- place-items: center;
- border-radius: 50%;
- background: color-mix(in srgb, var(--ph-out) 18%, transparent);
- color: var(--ph-out);
- transition: transform 0.7s var(--ease-spring) 0.35s;
-}
-
-.event-check .i {
- width: 4cqw;
- height: 4cqw;
- stroke-width: 2.6;
-}
-
-.story.is-live .event-check {
- transform: scale(0);
-}
-
-.story.is-live.ev .event-check {
- transform: none;
-}
-
-/* 输入框:文字由 site.js 按滚动进度逐字写入 */
-.compose {
- display: flex;
- flex: none;
- align-items: flex-end;
- gap: 2.4cqw;
- padding: 2.4cqw 3.6cqw 2.8cqw;
-}
-
-.compose-field {
- position: relative;
- display: block;
- min-height: 9.4cqw;
- flex: 1;
- padding: 2cqw 3.6cqw;
- border-radius: 4.8cqw;
- box-shadow: inset 0 0 0 1px var(--ph-line);
- font-size: 3.7cqw;
-}
-
-.compose-placeholder {
- position: absolute;
- top: 2cqw;
- left: 3.6cqw;
- color: var(--ph-muted);
- transition: opacity 0.2s ease;
-}
-
-.story.has-text .compose-placeholder {
- opacity: 0;
-}
-
-.compose-text {
- position: relative;
-}
-
-.compose-text.is-swapping {
- animation: text-swap 0.45s var(--ease-out);
-}
-
-@keyframes text-swap {
- from { opacity: 0; filter: blur(4px); }
- to { opacity: 1; filter: blur(0); }
-}
-
-.compose .caret {
- background: var(--ph-out);
- visibility: hidden;
-}
-
-.story.has-text .compose .caret {
- visibility: visible;
-}
-
-.compose-send {
- display: grid;
- width: 9.4cqw;
- height: 9.4cqw;
- flex: none;
- place-items: center;
- border-radius: 50%;
- background: var(--ph-line);
- color: #fff;
- transition: background 0.3s ease, transform 0.6s var(--ease-spring);
-}
-
-.compose-send .i {
- width: 5cqw;
- height: 5cqw;
- stroke-width: 2.4;
-}
-
-.story.has-text .compose-send {
- background: var(--ph-out);
- transform: scale(1.05);
-}
-
-/*
- * 键盘:按 App 真实的「AI 助手」界面复刻(AIKeyboardView)。
- * 尺寸以 cqw 表示,等比映射原生 pt 尺寸(顶栏 44pt、麦克风 156×60pt、圆键 48pt)。
- * 所有状态都由祖先元素上的 data-* 属性驱动,故事区与技能区共用同一套样式:
- * data-bar 顶栏:tabs 常规 / cancel 录音中 / paste AI 结果 / skills 复制后
- * data-mic 麦克风:idle / rec 听写 / ai 问 AI / busy 处理中 / done 已插入
- * data-ctx 上下文区:hint / live / think / extract / tip / none
- * data-surface 主体:main 常规 / variants 回复候选
- * data-pick 被点按的元素:0-2 回复卡片 / event 日程胶囊
- * data-undo 插入后出现撤销与改写按钮;data-action 回车键可用
- */
-.kb {
- display: flex;
- flex: none;
- flex-direction: column;
- padding: 1cqw 3cqw 7cqw;
- background: var(--ph-kb);
- color: var(--ph-key-ink);
- transition: background 0.4s ease;
-}
-
-.kb .i {
- stroke-width: 2;
-}
-
-.kb-top {
- position: relative;
- height: 10.5cqw;
- flex: none;
-}
-
-.kb-bar {
- position: absolute;
- inset: 0;
- display: flex;
- align-items: center;
- gap: 1.6cqw;
- opacity: 0;
- pointer-events: none;
- transform: translateY(1.5cqw);
- transition: opacity 0.3s ease, transform 0.55s var(--ease-spring);
-}
-
-[data-bar="tabs"] .kb-bar-tabs,
-[data-bar="cancel"] .kb-bar-cancel,
-[data-bar="paste"] .kb-bar-paste,
-[data-bar="skills"] .kb-bar-skills {
- opacity: 1;
- transform: none;
-}
-
-.kb-bar-tabs,
-.kb-bar-paste {
- justify-content: space-between;
-}
-
-.kb-bar-cancel {
- justify-content: flex-end;
-}
-
-/* OSG 标志:两实一空的三个圆点 */
-.kb-logo {
- display: flex;
- gap: 0.8cqw;
-}
-
-.kb-logo i {
- width: 3.4cqw;
- height: 3.4cqw;
- border-radius: 50%;
- background: currentColor;
-}
-
-.kb-logo i:nth-child(2) {
- background: transparent;
- box-shadow: inset 0 0 0 0.45cqw currentColor;
-}
-
-/* AI / 中文 / EN 分段切换:固定在键盘水平中心,与真实界面一致 */
-.kb-seg {
- position: absolute;
- left: 50%;
- display: flex;
- gap: 0.5cqw;
- padding: 0.5cqw;
- border-radius: 99px;
- background: var(--ph-track);
- translate: -50% 0;
-}
-
-.kb-seg > span {
- display: grid;
- width: 11.4cqw;
- height: 7.2cqw;
- place-items: center;
- border-radius: 99px;
- color: color-mix(in srgb, var(--ph-key-ink) 55%, transparent);
- font-size: 2.85cqw;
- font-weight: 500;
-}
-
-.kb-seg > .is-on {
- background: var(--ph-key);
- box-shadow: 0 0 0 0.25cqw var(--ph-line);
- color: var(--ph-key-ink);
-}
-
-.kb-seg .i {
- width: 3.7cqw;
- height: 3.7cqw;
-}
-
-.kb-chips {
- display: flex;
- gap: 1.4cqw;
-}
-
-.kb-chip {
- display: grid;
- width: 8.2cqw;
- height: 8.2cqw;
- flex: none;
- place-items: center;
- border-radius: 50%;
- background: var(--ph-key);
- box-shadow: 0 0 0 0.25cqw var(--ph-line);
- color: color-mix(in srgb, var(--ph-key-ink) 60%, transparent);
-}
-
-.kb-chip .i {
- width: 3.8cqw;
- height: 3.8cqw;
-}
-
-/* 玻璃胶囊:粘贴与技能 */
-.kb-cap {
- display: inline-flex;
- height: 7.2cqw;
- flex: none;
- align-items: center;
- padding: 0 3cqw;
- border-radius: 99px;
- background: var(--ph-glass);
- box-shadow: 0 0.6cqw 1.6cqw -0.6cqw rgba(0, 0, 0, 0.2), inset 0 0 0 0.25cqw var(--ph-line);
- font-size: 3cqw;
- font-weight: 650;
- white-space: nowrap;
- transition: background 0.25s ease, color 0.25s ease, transform 0.45s var(--ease-spring);
-}
-
-.kb-caps {
- display: flex;
- overflow: hidden;
- min-width: 0;
- flex: 1;
- gap: 1.4cqw;
- padding: 1cqw 0;
- -webkit-mask-image: linear-gradient(90deg, #000 86%, transparent);
- mask-image: linear-gradient(90deg, #000 86%, transparent);
-}
-
-/* 复制后技能胶囊依次弹出:模拟「按相关度挑出能用的技能」 */
-[data-bar="skills"] .kb-caps .kb-cap {
- animation: cap-in 0.55s var(--ease-spring) both;
-}
-
-.kb-caps .kb-cap:nth-child(2) { animation-delay: 0.06s; }
-.kb-caps .kb-cap:nth-child(3) { animation-delay: 0.12s; }
-.kb-caps .kb-cap:nth-child(4) { animation-delay: 0.18s; }
-.kb-caps .kb-cap:nth-child(5) { animation-delay: 0.24s; }
-
-@keyframes cap-in {
- from { opacity: 0; transform: translateY(2cqw) scale(0.7); }
- to { opacity: 1; transform: none; }
-}
-
-[data-pick="event"] .cap-event {
- background: var(--kb-accent);
- color: #fff;
- transform: scale(0.94);
-}
-
-/* 主体:上下文区 + 两排操作键;回复候选出现时整体让位 */
-.kb-stage {
- position: relative;
- height: 51cqw;
- flex: none;
-}
-
-.kb-main {
- position: absolute;
- inset: 0;
- display: flex;
- flex-direction: column;
- transition: opacity 0.3s ease, transform 0.5s var(--ease-out);
-}
-
-[data-surface="variants"] .kb-main {
- opacity: 0;
- transform: scale(0.97);
-}
-
-.kb-ctx {
- position: relative;
- flex: 1;
-}
-
-.ctx {
- position: absolute;
- top: 50%;
- left: 50%;
- max-width: 88%;
- font-size: 3.3cqw;
- opacity: 0;
- text-align: center;
- transform: translate(-50%, -30%);
- transition: opacity 0.3s ease, transform 0.5s var(--ease-out);
- white-space: nowrap;
-}
-
-[data-ctx="hint"] .ctx-hint,
-[data-ctx="live"] .ctx-live,
-[data-ctx="think"] .ctx-think,
-[data-ctx="extract"] .ctx-extract,
-[data-ctx="tip"] .ctx-tip {
- opacity: 1;
- transform: translate(-50%, -50%);
-}
-
-.ctx-hint {
- padding: 1.2cqw 3.2cqw;
- border-radius: 99px;
- box-shadow: inset 0 0 0 0.3cqw color-mix(in srgb, var(--ph-key-ink) 18%, transparent);
- color: color-mix(in srgb, var(--ph-key-ink) 55%, transparent);
- font-weight: 500;
-}
-
-/* 实时转写:听写时边说边出字 */
-.ctx-live {
- width: 88%;
- font-size: 3.7cqw;
- line-height: 1.35;
- white-space: normal;
-}
-
-.ctx-think,
-.ctx-extract {
- color: color-mix(in srgb, var(--ph-key-ink) 55%, transparent);
- font-size: 3.6cqw;
-}
-
-.ctx-tip {
- padding: 1.6cqw 3.6cqw;
- border-radius: 99px;
- background: var(--ph-glass);
- box-shadow: 0 1cqw 3cqw -1cqw rgba(0, 0, 0, 0.22), inset 0 0 0 0.25cqw var(--ph-line);
- font-size: 3.6cqw;
-}
-
-.kb-row {
- display: flex;
- height: 14.3cqw;
- flex: none;
- align-items: center;
- justify-content: space-between;
- padding: 0 2cqw;
-}
-
-.kb-row-low {
- height: 11.4cqw;
- margin-top: 5.2cqw;
-}
-
-.kb-circle {
- display: grid;
- width: 11.4cqw;
- height: 11.4cqw;
- flex: none;
- place-items: center;
- border-radius: 50%;
- background: var(--ph-key);
- box-shadow: 0 1cqw 2.4cqw -1cqw rgba(0, 0, 0, 0.25), inset 0 0 0 0.25cqw var(--ph-line);
- transition: opacity 0.35s ease, scale 0.5s var(--ease-spring);
-}
-
-.kb-circle .i {
- width: 5cqw;
- height: 5cqw;
-}
-
-/* 撤销与口述改写只在刚插入过文字后出现 */
-.kb-undo,
-.kb-edit {
- opacity: 0;
- scale: 0.7;
-}
-
-[data-undo="1"] .kb-undo,
-[data-undo="1"] .kb-edit {
- opacity: 1;
- scale: 1;
-}
-
-.kb-mic {
- position: relative;
- display: grid;
- width: 37cqw;
- height: 14.3cqw;
- flex: none;
- place-items: center;
- border-radius: 99px;
- background: var(--kb-accent);
- box-shadow: inset 0 0.3cqw 0 rgba(255, 255, 255, 0.3),
- 0 1.6cqw 4cqw -1.6cqw color-mix(in srgb, var(--kb-accent) 85%, transparent);
- color: #fff;
- transition: background-color 0.35s ease, box-shadow 0.35s ease, transform 0.5s var(--ease-spring);
-}
-
-.kb-mic > * {
- grid-area: 1 / 1;
- transition: opacity 0.25s ease, transform 0.45s var(--ease-spring);
-}
-
-.mic-icon {
- width: 6.6cqw;
- height: 6.6cqw;
-}
-
-.kb .mic-check {
- width: 7cqw;
- height: 7cqw;
- stroke-width: 3;
-}
-
-.mic-wave {
- height: 6.2cqw;
- gap: 1cqw;
-}
-
-.mic-wave i {
- width: 1cqw;
- height: 100%;
- border-radius: 1cqw;
- background: #ffc7c7;
- transform: scaleY(0.14);
-}
-
-.kb .mic-spin {
- width: 5.2cqw;
- height: 5.2cqw;
- border-width: 0.7cqw;
-}
-
-.mic-wave,
-.mic-spin,
-.mic-check {
- opacity: 0;
- transform: scale(0.6);
-}
-
-[data-mic="rec"] .kb-mic {
- background: var(--kb-red);
- box-shadow: inset 0 0.3cqw 0 rgba(255, 255, 255, 0.3),
- 0 1.6cqw 4.4cqw -1.6cqw color-mix(in srgb, var(--kb-red) 85%, transparent);
-}
-
-[data-mic="ai"] .kb-mic {
- background: var(--kb-teal);
- transform: scale(1.03);
-}
-
-[data-mic="ai"] .mic-wave i {
- background: #fff;
-}
-
-[data-mic="busy"] .kb-mic {
- background: color-mix(in srgb, var(--kb-red) 72%, var(--ph-kb));
- box-shadow: none;
-}
-
-:is([data-mic="rec"], [data-mic="ai"], [data-mic="busy"], [data-mic="done"]) .mic-icon {
- opacity: 0;
- transform: scale(0.6);
-}
-
-:is([data-mic="rec"], [data-mic="ai"]) .mic-wave,
-[data-mic="busy"] .mic-spin,
-[data-mic="done"] .mic-check {
- opacity: 1;
- transform: none;
-}
-
-/* 回车 / 发送:无可发送内容时为浅灰禁用态 */
-.kb-action {
- display: grid;
- width: 31.4cqw;
- height: 11.4cqw;
- flex: none;
- place-items: center;
- border-radius: 99px;
- background: var(--ph-key-off);
- color: color-mix(in srgb, var(--ph-key-ink) 45%, transparent);
- transition: background 0.3s ease, color 0.3s ease, transform 0.45s var(--ease-spring);
-}
-
-.kb-action .i {
- width: 5.4cqw;
- height: 5.4cqw;
-}
-
-[data-action="on"] .kb-action {
- background: var(--ph-key);
- box-shadow: 0 1cqw 2.4cqw -1cqw rgba(0, 0, 0, 0.25), inset 0 0 0 0.25cqw var(--ph-line);
- color: var(--ph-key-ink);
-}
-
-.story.press-send .kb-action {
- animation: key-press 0.5s var(--ease-out);
-}
-
-@keyframes key-press {
- 30% { transform: scale(0.9); }
-}
-
-/* 回复候选:三张玻璃卡片(普通 / 正式 / 轻松趣味),自下而上依次落定 */
-.kb-variants {
- position: absolute;
- inset: 0;
- display: flex;
- flex-direction: column;
- gap: 1.8cqw;
- padding: 1.2cqw 0.6cqw 0;
- pointer-events: none;
-}
-
-.kb-card {
- display: flex;
- align-items: flex-start;
- gap: 2.6cqw;
- padding: 2cqw 3cqw;
- border-radius: 3.6cqw;
- background: var(--ph-glass);
- box-shadow: 0 0.8cqw 2.4cqw -1.2cqw rgba(0, 0, 0, 0.2), inset 0 0 0 0.25cqw var(--ph-line);
- opacity: 0;
- transform: translateY(5cqw) scale(0.96);
- transition: opacity 0.35s ease, transform 0.6s var(--ease-spring), box-shadow 0.3s ease;
-}
-
-.kb-card:nth-child(2) { transition-delay: 0.07s; }
-.kb-card:nth-child(3) { transition-delay: 0.14s; }
-
-[data-surface="variants"] .kb-card {
- opacity: 1;
- transform: none;
-}
-
-.kb-card .i-solid {
- width: 4.6cqw;
- height: 4.6cqw;
- flex: none;
- margin-top: 0.4cqw;
- fill: currentColor;
- stroke: none;
-}
-
-.kb-card > span {
- display: grid;
- gap: 0.3cqw;
- line-height: 1.32;
-}
-
-.kb-card small {
- color: color-mix(in srgb, var(--ph-key-ink) 55%, transparent);
- font-size: 2.7cqw;
-}
-
-.kb-card b {
- font-size: 3.2cqw;
- font-weight: 500;
-}
-
-[data-pick="0"] .kb-card[data-card="0"],
-[data-pick="1"] .kb-card[data-card="1"],
-[data-pick="2"] .kb-card[data-card="2"] {
- box-shadow: 0 0 0 0.7cqw color-mix(in srgb, var(--kb-accent) 50%, transparent);
- transform: scale(0.97);
- transition-delay: 0s;
+@keyframes device-glow {
+ 0%, 100% { opacity: 0.45; transform: scale(1); }
+ 50% { opacity: 0.85; transform: scale(1.08); }
}
/* --------------------------------------------------------------------------
- * 技能区演示:复用键盘复刻组件,独立的容器查询让它按自身宽度缩放
+ * 真机外框:Apple 官方 iPhone 17 Pro 机框图(原图 1350 × 2760)。
+ * 屏幕开口为 1206 × 2622、起点 (72, 69),换算成百分比后放置录屏;
+ * 模拟器录屏正是这个分辨率,像素一一对应。机框自带灵动岛与圆角,叠在录屏之上。
* -------------------------------------------------------------------------- */
-.skills-frame {
- display: grid;
- place-items: center;
- padding: clamp(18px, 3vw, 34px);
- background: radial-gradient(120% 90% at 50% 0%, color-mix(in srgb, var(--accent) 14%, var(--surface)), var(--surface-soft));
+.iphone {
+ --w: min(330px, calc((100svh - 150px) * 0.489));
+ position: relative;
+ width: var(--w);
+ aspect-ratio: 1350 / 2760;
+ filter: drop-shadow(0 40px 48px rgba(8, 12, 16, 0.26)) drop-shadow(0 12px 16px rgba(8, 12, 16, 0.14));
}
-.skills-demo {
- display: flex;
+.iphone-screen {
+ position: absolute;
+ top: 2.5%;
+ left: 5.333%;
+ width: 89.333%;
+ height: 95%;
overflow: hidden;
- width: min(100%, 430px);
- flex-direction: column;
- border-radius: 22px;
- background: var(--ph-bg);
- box-shadow: 0 30px 60px -30px rgba(0, 0, 0, 0.45), inset 0 0 0 1px var(--ph-line);
- color: var(--ph-ink);
- container-type: inline-size;
- text-align: left;
+ /* 开口圆角约 230px:230 / 1206 与 230 / 2622 */
+ border-radius: 19% / 8.8%;
+ background: #000;
}
-/* 消息来源区:被复制的那条消息,最后被「已添加到日历」横幅替换 */
-.sd-source {
- position: relative;
+/* 同一屏幕里叠放多段录屏,当前一段淡入 */
+.iphone-clip {
+ position: absolute;
+ inset: 0;
+ display: block;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ opacity: 0;
+ transition: opacity 0.5s var(--ease-out);
+}
+
+.iphone-clip.is-on {
+ opacity: 1;
+}
+
+.iphone-bezel {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ user-select: none;
+}
+
+/* 分栏区块里的真机:与文案并排居中 */
+.device-stage {
display: grid;
- min-height: 34cqw;
- align-items: center;
- padding: 7cqw 5cqw;
+ margin: 0;
+ place-items: center;
}
-.sd-msg {
- position: relative;
- justify-self: start;
- max-width: 82%;
- padding: 2.4cqw 3.8cqw;
- border-radius: 5cqw 5cqw 5cqw 1.4cqw;
- background: var(--ph-in);
- font-size: 3.7cqw;
- line-height: 1.4;
- transition: box-shadow 0.4s ease, opacity 0.4s ease, transform 0.6s var(--ease-out);
-}
-
-.skills-demo .event-banner {
- top: 50%;
- right: 5cqw;
- left: 5cqw;
- translate: 0 -50%;
-}
-
-.skills-demo .event-banner,
-.skills-demo .event-check {
- opacity: 0;
- transform: translateY(-40%) scale(0.92);
-}
-
-.skills-demo .event-check {
- transform: scale(0);
-}
-
-.skills-demo.is-done .event-banner {
- opacity: 1;
- transform: none;
-}
-
-.skills-demo.is-done .event-check {
- opacity: 1;
- transform: none;
-}
-
-.skills-demo.is-done .sd-msg {
- opacity: 0;
- transform: translateY(6cqw) scale(0.94);
-}
-
-.skills-demo .kb {
- padding-bottom: 5cqw;
+.device-stage .iphone {
+ --w: min(320px, 76vw);
}
/* --------------------------------------------------------------------------
@@ -3389,7 +2488,8 @@ html.vt-theme::view-transition-new(root) {
order: 1;
}
- .split-layout.reverse .media-frame {
+ .split-layout.reverse .media-frame,
+ .split-layout.reverse .device-stage {
order: 2;
}
@@ -3440,8 +2540,8 @@ html.vt-theme::view-transition-new(root) {
order: -1;
}
- .phone {
- --w: min(290px, calc((100svh - 360px) * 0.468));
+ .iphone {
+ --w: min(260px, calc((100svh - 360px) * 0.489));
}
.story-acts {
diff --git a/docs/assets/site.js b/docs/assets/site.js
index 8294ba9..1352508 100644
--- a/docs/assets/site.js
+++ b/docs/assets/site.js
@@ -5,7 +5,7 @@
* 2. 深浅色主题(默认跟随系统,手动选择后持久化;支持时以圆形扩散过渡)
* 3. 移动端导航开合、导航滑块与当前区块高亮
* 4. 逐字拆分标题(data-split)
- * 5. 滚动引擎:进度条、Hero 设备「立起」、媒体视差、三幕滚动叙事
+ * 5. 滚动引擎:进度条、Hero 设备「立起」、媒体视差、三幕滚动叙事(真机录屏随幕切换)
* 6. 声波渲染、Hero 语音胶囊打字机、数字计数
* 7. 指针光斑 / 3D 倾斜 / 磁吸按钮、FAQ 高度过渡、离屏视频暂停
* 开启「减弱动态效果」时,持续动画与指针动效全部停用,内容直接呈现最终态。
@@ -36,8 +36,10 @@
const clamp = (value, min = 0, max = 1) => Math.min(max, Math.max(min, value));
- // 语言切换后需要重算的模块(胶囊文案、叙事文案、导航滑块)在此登记
+ // 语言切换后需要重算的模块(胶囊文案、真机录屏、导航滑块)在此登记
const languageHooks = [];
+ // 主题切换后需要换源的模块(真机录屏按深浅色各录一份)在此登记
+ const themeHooks = [];
/* ---------------- 逐字拆分 ---------------- */
@@ -217,6 +219,8 @@
image.src = `assets/screenshots/${lang}/${resolved}/${image.dataset.shot}`;
});
+ themeHooks.forEach((hook) => hook(resolved));
+
if (persist) localStorage.setItem("osg-site-theme", resolved);
}
@@ -484,184 +488,134 @@
languageHooks.push(() => playCapsule());
- /* ---------------- 键盘复刻:共用状态写入 ---------------- */
+ /* ---------------- 真机录屏:Apple 机框 + 真实键盘扩展的片段 ---------------- */
- const KB_DEFAULTS = { bar: "tabs", mic: "idle", ctx: "hint", surface: "main", pick: "", undo: "0", action: "off" };
+ // 片段按「幕-语言-主题」命名,每组都录自模拟器里真实的 OSGKeyboard 扩展
+ function clipPath(video, suffix) {
+ const lang = root.dataset.lang === "en" ? "en" : "zh";
+ const theme = root.dataset.theme === "dark" ? "dark" : "light";
+ return `assets/story/${video.dataset.clip}-${lang}-${theme}${suffix}`;
+ }
- // 把键盘状态写到容器的 data-* 上(只写变化的值,避免无谓的样式重算)
- function applyKeyboardState(element, state) {
- Object.keys(KB_DEFAULTS).forEach((key) => {
- const value = state[key] ?? KB_DEFAULTS[key];
- if (element.dataset[key] !== value) element.dataset[key] = value;
+ const clipVideos = Array.from(document.querySelectorAll(".iphone-clip"));
+
+ function playClip(video, restart) {
+ if (reduceMotion) return;
+ if (restart && video.readyState >= 1) video.currentTime = 0;
+ video.dataset.playing = "1";
+ video.play().catch(() => {});
+ }
+
+ function pauseClip(video) {
+ video.dataset.playing = "0";
+ video.pause();
+ }
+
+ function refreshClipSources() {
+ clipVideos.forEach((video) => {
+ // 减弱动态效果:不加载视频,只显示最能说明这一幕的关键帧海报
+ if (reduceMotion) {
+ video.poster = clipPath(video, "-key.jpg");
+ return;
+ }
+ const src = clipPath(video, ".mp4");
+ if (video.getAttribute("src") === src) return;
+ const resume = video.dataset.playing === "1";
+ video.poster = clipPath(video, ".jpg");
+ video.src = src;
+ if (resume) playClip(video, false);
});
}
+ // 初始化时语言与主题会先后写入,合并成一次换源,避免同一片段加载两遍
+ let clipRefreshQueued = false;
+ function queueClipRefresh() {
+ if (clipRefreshQueued) return;
+ clipRefreshQueued = true;
+ queueMicrotask(() => {
+ clipRefreshQueued = false;
+ refreshClipSources();
+ });
+ }
+
+ languageHooks.push(queueClipRefresh);
+ themeHooks.push(queueClipRefresh);
+
/* ---------------- 三幕滚动叙事 ---------------- */
- // 与气泡文案保持一致:听写结果、选中的「普通」回复
- const STORY_TEXT = {
- zh: {
- dictation: "收到了,我下午看完给你反馈。",
- reply: "好的,我整理一下预算版,明天上午发你。"
- },
- en: {
- dictation: "Got it — I’ll review it this afternoon and get back to you.",
- reply: "Sure, I’ll put the budget version together and send it over tomorrow morning."
- }
- };
-
- // 键盘时间轴(滚动进度 0 → 1),每段是完整状态,取最后一个 at ≤ p 的段
- // 第一幕 听写:录音 → 打勾插入 → 发送
- // 第二幕 嘴替:复制消息 → AI 思考 → 三条回复 → 点「普通」插入 → 发送
- // 第三幕 Agentic:复制会议通知 → 顶栏技能 → 点「日程」→ 提取 → 写入日历
- const STORY_PHASES = [
- { at: 0 },
- { at: 0.065, bar: "cancel", mic: "rec", ctx: "live" },
- { at: 0.235, mic: "done", ctx: "none", undo: "1", action: "on", compose: "dictation" },
- { at: 0.27, undo: "1", action: "on", compose: "dictation", send: true },
- { at: 0.29 },
- { at: 0.375, bar: "paste", mic: "busy", ctx: "think" },
- { at: 0.44, bar: "paste", surface: "variants", ctx: "none" },
- { at: 0.545, bar: "paste", surface: "variants", ctx: "none", pick: "0" },
- { at: 0.575, mic: "done", ctx: "none", undo: "1", action: "on", compose: "reply" },
- { at: 0.6, undo: "1", action: "on", compose: "reply", send: true },
- { at: 0.62 },
- { at: 0.715, bar: "skills", ctx: "none" },
- { at: 0.775, bar: "skills", ctx: "extract", mic: "busy", pick: "event" },
- { at: 0.84, ctx: "tip" },
- { at: 0.93 }
- ];
-
- // 对话气泡:到达阈值即出现;复制提示只在区间内显示
- const STORY_MESSAGES = { m1: 0.02, o1: 0.29, m2: 0.33, o2: 0.62, m3: 0.67, ev: 0.885 };
- const STORY_COPIES = { c2: [0.36, 0.44], c3: [0.705, 0.775] };
- const LIVE_RANGE = [0.075, 0.225];
- const ACT_RANGES = [[0, 0.31], [0.31, 0.655], [0.655, 1]];
+ // 滚动只决定「当前是哪一幕」;画面是这一幕的真机片段,从头播放,播完停留片刻再重播
+ const ACT_RANGES = [[0, 0.34], [0.34, 0.67], [0.67, 1]];
+ const STORY_REPLAY_DELAY = 2200;
const story = document.getElementById("storyStage");
- const storyText = document.getElementById("storyText");
- const storyLive = document.getElementById("storyLive");
const storyActs = story ? Array.from(story.querySelectorAll(".story-act")) : [];
- const storyWaves = story
- ? Array.from(story.querySelectorAll(".mic-wave")).map((host) => createWave(host))
- : [];
- let storyLastText = null;
- let storyLastLive = null;
- let storyLastAct = -1;
+ const storyClips = story ? Array.from(story.querySelectorAll(".iphone-clip")) : [];
+ let storyAct = -1;
+ let storyInView = false;
+ let storyReplayTimer = 0;
- function updateStory(force) {
+ function setStoryAct(act) {
+ storyAct = act;
+ clearTimeout(storyReplayTimer);
+ storyActs.forEach((element, i) => {
+ element.classList.toggle("is-active", i === act);
+ // 演完的幕进度条保持满格,还没到的幕清空;当前幕由片段播放进度驱动
+ if (i !== act || reduceMotion) element.style.setProperty("--ap", i <= act ? "1" : "0");
+ });
+ storyClips.forEach((video, i) => {
+ video.classList.toggle("is-on", i === act);
+ if (i === act && storyInView) playClip(video, true);
+ else pauseClip(video);
+ });
+ }
+
+ function updateStory() {
if (!story) return;
const rect = story.getBoundingClientRect();
const viewport = innerHeight;
const inView = rect.bottom > 0 && rect.top < viewport;
- storyWaves.forEach((wave) => { wave.active = inView; });
- if (!inView && !force) return;
+ const p = clamp(-rect.top / Math.max(1, story.offsetHeight - viewport));
- const total = Math.max(1, story.offsetHeight - viewport);
- const p = clamp(-rect.top / total);
- const copy = STORY_TEXT[root.dataset.lang === "en" ? "en" : "zh"];
-
- let phase = STORY_PHASES[0];
- STORY_PHASES.forEach((item) => { if (p >= item.at) phase = item; });
- applyKeyboardState(story, phase);
- story.classList.toggle("press-send", Boolean(phase.send));
-
- Object.entries(STORY_MESSAGES).forEach(([name, at]) => story.classList.toggle(name, p >= at));
- Object.entries(STORY_COPIES).forEach(([name, [from, to]]) => story.classList.toggle(name, p >= from && p < to));
-
- // 实时转写:录音阶段按滚动进度逐字出现
- let live = "";
- if (phase.ctx === "live") {
- const characters = Array.from(copy.dictation);
- const count = Math.round(clamp((p - LIVE_RANGE[0]) / (LIVE_RANGE[1] - LIVE_RANGE[0])) * characters.length);
- live = characters.slice(0, count).join("");
- }
- if (storyLive && live !== storyLastLive) {
- storyLive.textContent = live;
- storyLastLive = live;
- }
-
- // 输入框:插入时整句落入(与真实插入行为一致),发送后清空
- const text = phase.compose ? copy[phase.compose] : "";
- if (storyText && text !== storyLastText) {
- storyText.textContent = text;
- if (text && !storyLastText && !reduceMotion) {
- storyText.classList.remove("is-swapping");
- void storyText.offsetWidth;
- storyText.classList.add("is-swapping");
- }
- storyLastText = text;
- }
- story.classList.toggle("has-text", text.length > 0);
-
- // 左侧三幕:高亮当前幕,幕内进度条随滚动填充
let act = 0;
ACT_RANGES.forEach(([start], i) => { if (p >= start) act = i; });
- if (act !== storyLastAct) {
- storyActs.forEach((element, i) => element.classList.toggle("is-active", i === act));
- storyLastAct = act;
+
+ // 离开视口暂停,回来接着播
+ if (inView !== storyInView) {
+ storyInView = inView;
+ const current = storyClips[storyAct];
+ if (current) {
+ if (inView) playClip(current, false);
+ else pauseClip(current);
+ }
}
- storyActs.forEach((element, i) => {
- const [start, end] = ACT_RANGES[i];
- element.style.setProperty("--ap", clamp((p - start) / (end - start)).toFixed(3));
+ if (act !== storyAct) setStoryAct(act);
+ }
+
+ storyClips.forEach((video, i) => {
+ video.addEventListener("timeupdate", () => {
+ if (i !== storyAct || !video.duration || reduceMotion) return;
+ storyActs[i]?.style.setProperty("--ap", (video.currentTime / video.duration).toFixed(3));
});
-
- storyWaves.forEach((wave) => { wave.level = phase.mic === "rec" ? 1 : 0; });
- if (inView) wakeWaves();
- }
-
- if (story) {
- story.classList.add("is-live");
- languageHooks.push(() => {
- storyLastText = null;
- storyLastLive = null;
- updateStory(true);
+ video.addEventListener("ended", () => {
+ storyActs[i]?.style.setProperty("--ap", "1");
+ clearTimeout(storyReplayTimer);
+ storyReplayTimer = setTimeout(() => {
+ if (i === storyAct && storyInView) playClip(video, true);
+ }, STORY_REPLAY_DELAY);
});
- }
+ });
- /* ---------------- 技能区循环演示 ---------------- */
+ /* ---------------- 技能区、打字区:进入视口时循环播放真机片段 ---------------- */
- // 复制 → 顶栏换成技能胶囊 → 点「日程」→ 提取 → 加入日历 → 横幅确认,然后重来
- const SKILL_DEMO_STEPS = [
- { ms: 1300 },
- { ms: 1900, bar: "skills", ctx: "none", copied: true },
- { ms: 550, bar: "skills", ctx: "none", pick: "event", copied: true },
- { ms: 1500, bar: "skills", ctx: "extract", mic: "busy", pick: "event" },
- { ms: 1200, ctx: "tip" },
- { ms: 2800, done: true }
- ];
-
- const skillsDemo = document.getElementById("skillsDemo");
- let skillsDemoVisible = false;
- let skillsDemoTimer = 0;
- let skillsDemoIndex = 0;
-
- function showSkillsStep(step) {
- applyKeyboardState(skillsDemo, step);
- skillsDemo.classList.toggle("is-copied", Boolean(step.copied));
- skillsDemo.classList.toggle("is-done", Boolean(step.done));
- }
-
- function tickSkillsDemo() {
- clearTimeout(skillsDemoTimer);
- if (!skillsDemoVisible || document.hidden) return;
- const step = SKILL_DEMO_STEPS[skillsDemoIndex];
- showSkillsStep(step);
- skillsDemoIndex = (skillsDemoIndex + 1) % SKILL_DEMO_STEPS.length;
- skillsDemoTimer = setTimeout(tickSkillsDemo, step.ms);
- }
-
- if (skillsDemo) {
- if (reduceMotion) {
- // 减弱动态效果:停在「技能胶囊已出现」这一帧
- showSkillsStep(SKILL_DEMO_STEPS[1]);
- } else {
- new IntersectionObserver((entries) => {
- skillsDemoVisible = entries[0].isIntersecting;
- tickSkillsDemo();
- }, { threshold: 0.35 }).observe(skillsDemo);
- document.addEventListener("visibilitychange", tickSkillsDemo);
- }
+ const loopClips = clipVideos.filter((video) => !story || !story.contains(video));
+ if (!reduceMotion && loopClips.length && "IntersectionObserver" in window) {
+ const clipObserver = new IntersectionObserver((entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) playClip(entry.target, false);
+ else pauseClip(entry.target);
+ });
+ }, { threshold: 0.35 });
+ loopClips.forEach((video) => clipObserver.observe(video));
}
/* ---------------- 滚动引擎 ---------------- */
diff --git a/docs/assets/story/agent-en-dark-key.jpg b/docs/assets/story/agent-en-dark-key.jpg
new file mode 100644
index 0000000..1d925eb
Binary files /dev/null and b/docs/assets/story/agent-en-dark-key.jpg differ
diff --git a/docs/assets/story/agent-en-dark.jpg b/docs/assets/story/agent-en-dark.jpg
new file mode 100644
index 0000000..91c8fd1
Binary files /dev/null and b/docs/assets/story/agent-en-dark.jpg differ
diff --git a/docs/assets/story/agent-en-dark.mp4 b/docs/assets/story/agent-en-dark.mp4
new file mode 100644
index 0000000..2340750
Binary files /dev/null and b/docs/assets/story/agent-en-dark.mp4 differ
diff --git a/docs/assets/story/agent-en-light-key.jpg b/docs/assets/story/agent-en-light-key.jpg
new file mode 100644
index 0000000..c167e34
Binary files /dev/null and b/docs/assets/story/agent-en-light-key.jpg differ
diff --git a/docs/assets/story/agent-en-light.jpg b/docs/assets/story/agent-en-light.jpg
new file mode 100644
index 0000000..6c83b40
Binary files /dev/null and b/docs/assets/story/agent-en-light.jpg differ
diff --git a/docs/assets/story/agent-en-light.mp4 b/docs/assets/story/agent-en-light.mp4
new file mode 100644
index 0000000..fe2f30f
Binary files /dev/null and b/docs/assets/story/agent-en-light.mp4 differ
diff --git a/docs/assets/story/agent-zh-dark-key.jpg b/docs/assets/story/agent-zh-dark-key.jpg
new file mode 100644
index 0000000..16f392b
Binary files /dev/null and b/docs/assets/story/agent-zh-dark-key.jpg differ
diff --git a/docs/assets/story/agent-zh-dark.jpg b/docs/assets/story/agent-zh-dark.jpg
new file mode 100644
index 0000000..38e0ea1
Binary files /dev/null and b/docs/assets/story/agent-zh-dark.jpg differ
diff --git a/docs/assets/story/agent-zh-dark.mp4 b/docs/assets/story/agent-zh-dark.mp4
new file mode 100644
index 0000000..2fa4d13
Binary files /dev/null and b/docs/assets/story/agent-zh-dark.mp4 differ
diff --git a/docs/assets/story/agent-zh-light-key.jpg b/docs/assets/story/agent-zh-light-key.jpg
new file mode 100644
index 0000000..cfeb262
Binary files /dev/null and b/docs/assets/story/agent-zh-light-key.jpg differ
diff --git a/docs/assets/story/agent-zh-light.jpg b/docs/assets/story/agent-zh-light.jpg
new file mode 100644
index 0000000..c1e9ce3
Binary files /dev/null and b/docs/assets/story/agent-zh-light.jpg differ
diff --git a/docs/assets/story/agent-zh-light.mp4 b/docs/assets/story/agent-zh-light.mp4
new file mode 100644
index 0000000..f05c8a1
Binary files /dev/null and b/docs/assets/story/agent-zh-light.mp4 differ
diff --git a/docs/assets/story/reply-en-dark-key.jpg b/docs/assets/story/reply-en-dark-key.jpg
new file mode 100644
index 0000000..3a542c9
Binary files /dev/null and b/docs/assets/story/reply-en-dark-key.jpg differ
diff --git a/docs/assets/story/reply-en-dark.jpg b/docs/assets/story/reply-en-dark.jpg
new file mode 100644
index 0000000..22a870b
Binary files /dev/null and b/docs/assets/story/reply-en-dark.jpg differ
diff --git a/docs/assets/story/reply-en-dark.mp4 b/docs/assets/story/reply-en-dark.mp4
new file mode 100644
index 0000000..5b5ff78
Binary files /dev/null and b/docs/assets/story/reply-en-dark.mp4 differ
diff --git a/docs/assets/story/reply-en-light-key.jpg b/docs/assets/story/reply-en-light-key.jpg
new file mode 100644
index 0000000..1acdeed
Binary files /dev/null and b/docs/assets/story/reply-en-light-key.jpg differ
diff --git a/docs/assets/story/reply-en-light.jpg b/docs/assets/story/reply-en-light.jpg
new file mode 100644
index 0000000..e97b2b6
Binary files /dev/null and b/docs/assets/story/reply-en-light.jpg differ
diff --git a/docs/assets/story/reply-en-light.mp4 b/docs/assets/story/reply-en-light.mp4
new file mode 100644
index 0000000..eb7e5b0
Binary files /dev/null and b/docs/assets/story/reply-en-light.mp4 differ
diff --git a/docs/assets/story/reply-zh-dark-key.jpg b/docs/assets/story/reply-zh-dark-key.jpg
new file mode 100644
index 0000000..82ae4f6
Binary files /dev/null and b/docs/assets/story/reply-zh-dark-key.jpg differ
diff --git a/docs/assets/story/reply-zh-dark.jpg b/docs/assets/story/reply-zh-dark.jpg
new file mode 100644
index 0000000..6a4fe91
Binary files /dev/null and b/docs/assets/story/reply-zh-dark.jpg differ
diff --git a/docs/assets/story/reply-zh-dark.mp4 b/docs/assets/story/reply-zh-dark.mp4
new file mode 100644
index 0000000..f0493c2
Binary files /dev/null and b/docs/assets/story/reply-zh-dark.mp4 differ
diff --git a/docs/assets/story/reply-zh-light-key.jpg b/docs/assets/story/reply-zh-light-key.jpg
new file mode 100644
index 0000000..7022bfe
Binary files /dev/null and b/docs/assets/story/reply-zh-light-key.jpg differ
diff --git a/docs/assets/story/reply-zh-light.jpg b/docs/assets/story/reply-zh-light.jpg
new file mode 100644
index 0000000..17f6c26
Binary files /dev/null and b/docs/assets/story/reply-zh-light.jpg differ
diff --git a/docs/assets/story/reply-zh-light.mp4 b/docs/assets/story/reply-zh-light.mp4
new file mode 100644
index 0000000..cffadd0
Binary files /dev/null and b/docs/assets/story/reply-zh-light.mp4 differ
diff --git a/docs/assets/story/skills-en-dark-key.jpg b/docs/assets/story/skills-en-dark-key.jpg
new file mode 100644
index 0000000..38c4da7
Binary files /dev/null and b/docs/assets/story/skills-en-dark-key.jpg differ
diff --git a/docs/assets/story/skills-en-dark.jpg b/docs/assets/story/skills-en-dark.jpg
new file mode 100644
index 0000000..dd7a97a
Binary files /dev/null and b/docs/assets/story/skills-en-dark.jpg differ
diff --git a/docs/assets/story/skills-en-dark.mp4 b/docs/assets/story/skills-en-dark.mp4
new file mode 100644
index 0000000..94f9a98
Binary files /dev/null and b/docs/assets/story/skills-en-dark.mp4 differ
diff --git a/docs/assets/story/skills-en-light-key.jpg b/docs/assets/story/skills-en-light-key.jpg
new file mode 100644
index 0000000..c6134dc
Binary files /dev/null and b/docs/assets/story/skills-en-light-key.jpg differ
diff --git a/docs/assets/story/skills-en-light.jpg b/docs/assets/story/skills-en-light.jpg
new file mode 100644
index 0000000..c99bd80
Binary files /dev/null and b/docs/assets/story/skills-en-light.jpg differ
diff --git a/docs/assets/story/skills-en-light.mp4 b/docs/assets/story/skills-en-light.mp4
new file mode 100644
index 0000000..d4e0c94
Binary files /dev/null and b/docs/assets/story/skills-en-light.mp4 differ
diff --git a/docs/assets/story/skills-zh-dark-key.jpg b/docs/assets/story/skills-zh-dark-key.jpg
new file mode 100644
index 0000000..8a3d26a
Binary files /dev/null and b/docs/assets/story/skills-zh-dark-key.jpg differ
diff --git a/docs/assets/story/skills-zh-dark.jpg b/docs/assets/story/skills-zh-dark.jpg
new file mode 100644
index 0000000..ec7286f
Binary files /dev/null and b/docs/assets/story/skills-zh-dark.jpg differ
diff --git a/docs/assets/story/skills-zh-dark.mp4 b/docs/assets/story/skills-zh-dark.mp4
new file mode 100644
index 0000000..c23fb15
Binary files /dev/null and b/docs/assets/story/skills-zh-dark.mp4 differ
diff --git a/docs/assets/story/skills-zh-light-key.jpg b/docs/assets/story/skills-zh-light-key.jpg
new file mode 100644
index 0000000..0ac3cad
Binary files /dev/null and b/docs/assets/story/skills-zh-light-key.jpg differ
diff --git a/docs/assets/story/skills-zh-light.jpg b/docs/assets/story/skills-zh-light.jpg
new file mode 100644
index 0000000..4fc40c4
Binary files /dev/null and b/docs/assets/story/skills-zh-light.jpg differ
diff --git a/docs/assets/story/skills-zh-light.mp4 b/docs/assets/story/skills-zh-light.mp4
new file mode 100644
index 0000000..7613461
Binary files /dev/null and b/docs/assets/story/skills-zh-light.mp4 differ
diff --git a/docs/assets/story/typing-en-dark-key.jpg b/docs/assets/story/typing-en-dark-key.jpg
new file mode 100644
index 0000000..b999f70
Binary files /dev/null and b/docs/assets/story/typing-en-dark-key.jpg differ
diff --git a/docs/assets/story/typing-en-dark.jpg b/docs/assets/story/typing-en-dark.jpg
new file mode 100644
index 0000000..a901cc5
Binary files /dev/null and b/docs/assets/story/typing-en-dark.jpg differ
diff --git a/docs/assets/story/typing-en-dark.mp4 b/docs/assets/story/typing-en-dark.mp4
new file mode 100644
index 0000000..927535f
Binary files /dev/null and b/docs/assets/story/typing-en-dark.mp4 differ
diff --git a/docs/assets/story/typing-en-light-key.jpg b/docs/assets/story/typing-en-light-key.jpg
new file mode 100644
index 0000000..6bcfd23
Binary files /dev/null and b/docs/assets/story/typing-en-light-key.jpg differ
diff --git a/docs/assets/story/typing-en-light.jpg b/docs/assets/story/typing-en-light.jpg
new file mode 100644
index 0000000..ccaa520
Binary files /dev/null and b/docs/assets/story/typing-en-light.jpg differ
diff --git a/docs/assets/story/typing-en-light.mp4 b/docs/assets/story/typing-en-light.mp4
new file mode 100644
index 0000000..0aa78ad
Binary files /dev/null and b/docs/assets/story/typing-en-light.mp4 differ
diff --git a/docs/assets/story/typing-zh-dark-key.jpg b/docs/assets/story/typing-zh-dark-key.jpg
new file mode 100644
index 0000000..ebd8bf9
Binary files /dev/null and b/docs/assets/story/typing-zh-dark-key.jpg differ
diff --git a/docs/assets/story/typing-zh-dark.jpg b/docs/assets/story/typing-zh-dark.jpg
new file mode 100644
index 0000000..63037c4
Binary files /dev/null and b/docs/assets/story/typing-zh-dark.jpg differ
diff --git a/docs/assets/story/typing-zh-dark.mp4 b/docs/assets/story/typing-zh-dark.mp4
new file mode 100644
index 0000000..3cb33bc
Binary files /dev/null and b/docs/assets/story/typing-zh-dark.mp4 differ
diff --git a/docs/assets/story/typing-zh-light-key.jpg b/docs/assets/story/typing-zh-light-key.jpg
new file mode 100644
index 0000000..26d079b
Binary files /dev/null and b/docs/assets/story/typing-zh-light-key.jpg differ
diff --git a/docs/assets/story/typing-zh-light.jpg b/docs/assets/story/typing-zh-light.jpg
new file mode 100644
index 0000000..29ab086
Binary files /dev/null and b/docs/assets/story/typing-zh-light.jpg differ
diff --git a/docs/assets/story/typing-zh-light.mp4 b/docs/assets/story/typing-zh-light.mp4
new file mode 100644
index 0000000..cf06343
Binary files /dev/null and b/docs/assets/story/typing-zh-light.mp4 differ
diff --git a/docs/assets/story/voice-en-dark-key.jpg b/docs/assets/story/voice-en-dark-key.jpg
new file mode 100644
index 0000000..1dd65b6
Binary files /dev/null and b/docs/assets/story/voice-en-dark-key.jpg differ
diff --git a/docs/assets/story/voice-en-dark.jpg b/docs/assets/story/voice-en-dark.jpg
new file mode 100644
index 0000000..a12c621
Binary files /dev/null and b/docs/assets/story/voice-en-dark.jpg differ
diff --git a/docs/assets/story/voice-en-dark.mp4 b/docs/assets/story/voice-en-dark.mp4
new file mode 100644
index 0000000..e1da888
Binary files /dev/null and b/docs/assets/story/voice-en-dark.mp4 differ
diff --git a/docs/assets/story/voice-en-light-key.jpg b/docs/assets/story/voice-en-light-key.jpg
new file mode 100644
index 0000000..2944c50
Binary files /dev/null and b/docs/assets/story/voice-en-light-key.jpg differ
diff --git a/docs/assets/story/voice-en-light.jpg b/docs/assets/story/voice-en-light.jpg
new file mode 100644
index 0000000..5c92ddd
Binary files /dev/null and b/docs/assets/story/voice-en-light.jpg differ
diff --git a/docs/assets/story/voice-en-light.mp4 b/docs/assets/story/voice-en-light.mp4
new file mode 100644
index 0000000..9017743
Binary files /dev/null and b/docs/assets/story/voice-en-light.mp4 differ
diff --git a/docs/assets/story/voice-zh-dark-key.jpg b/docs/assets/story/voice-zh-dark-key.jpg
new file mode 100644
index 0000000..f73a076
Binary files /dev/null and b/docs/assets/story/voice-zh-dark-key.jpg differ
diff --git a/docs/assets/story/voice-zh-dark.jpg b/docs/assets/story/voice-zh-dark.jpg
new file mode 100644
index 0000000..0ed726d
Binary files /dev/null and b/docs/assets/story/voice-zh-dark.jpg differ
diff --git a/docs/assets/story/voice-zh-dark.mp4 b/docs/assets/story/voice-zh-dark.mp4
new file mode 100644
index 0000000..2d5133c
Binary files /dev/null and b/docs/assets/story/voice-zh-dark.mp4 differ
diff --git a/docs/assets/story/voice-zh-light-key.jpg b/docs/assets/story/voice-zh-light-key.jpg
new file mode 100644
index 0000000..8c00566
Binary files /dev/null and b/docs/assets/story/voice-zh-light-key.jpg differ
diff --git a/docs/assets/story/voice-zh-light.jpg b/docs/assets/story/voice-zh-light.jpg
new file mode 100644
index 0000000..52d559f
Binary files /dev/null and b/docs/assets/story/voice-zh-light.jpg differ
diff --git a/docs/assets/story/voice-zh-light.mp4 b/docs/assets/story/voice-zh-light.mp4
new file mode 100644
index 0000000..df2c01f
Binary files /dev/null and b/docs/assets/story/voice-zh-light.mp4 differ
diff --git a/docs/index.html b/docs/index.html
index 07c4b64..38ae0e9 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -50,7 +50,7 @@
-
+
+