Skip to content

fix(capsule): 切换样式立即生效 + 设置动画/视觉统一(Windows 样式切换修复) - #912

Merged
appergb merged 4 commits into
betafrom
fix/capsule-style-immediate
Aug 4, 2026
Merged

fix(capsule): 切换样式立即生效 + 设置动画/视觉统一(Windows 样式切换修复)#912
appergb merged 4 commits into
betafrom
fix/capsule-style-immediate

Conversation

@appergb

@appergb appergb commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

User description

目标

胶囊样式功能发布后的反馈修复 + 设置页动画/视觉统一,随 1.3.16 测试版发布。

变更

1. Windows/Linux 切换胶囊样式不生效(用户反馈)

样式原子此前只靠 emit_capsule 主线程闭包 ~30Hz 同步,入场帧 payload 又是同步前克隆的旧值;Windows 主线程拥塞时闭包延迟会让整场录音下发旧样式。

  • set_settings 保存后直接同步 capsule_style 原子 → 下次录音入场帧即携带新样式
  • 胶囊 webview 监听 prefs:changed 广播 → 设置里切换立即生效(录音中当帧换肤),并重置胶囊瞬态(退出动画/插入字数/操作态/预备态计时)
  • Linux:胶囊窗口从不显示是既定设计,未改动;共享链路修复同样覆盖

2. 录音方式首次进入不显示当前选中项

滑动指示块测量依赖 prefs/platformCaps 异步加载时序(先 prefs 后 caps 时按钮未挂载即 return,此后不再触发)→ 依赖补 showDesktopHotkey,按钮挂载即补量。

3. 静音后自动停止(#903 新增)展开/收起动画

  • 开关行只留 Toggle(不再因内联秒数下拉抽搐)
  • 秒数行随开关展开/收起(grid 0fr→1fr + 淡入淡出)
  • 整组随录音方式(按住/自动 ↔ 切换式)从下方拉出/收回,不再突然跳出;与 Collapsible 折叠组同款动画模式

4. 视觉统一

  • 静音时长下拉改 SelectLite 默认底色 + 固定 200 宽,与麦克风下拉一致、右边缘对齐
  • SelectLite 触发器选中值切换动画(ol-select-value-in)

5. 更新框变灰

UpdateDialog 全屏遮罩 0.18 → 0.05:不再把设置页白色选项行盖灰(深色侧边栏/渐变看不出,白卡片最明显)。

测试

  • cargo test --manifest-path src-tauri/Cargo.toml:902 passed / 0 failed(含新增 2 个:原子同步、持久化 roundtrip)
  • npm test:14 个契约测试全部通过
  • npm run build:tsc + vite 通过
  • 手动 macOS:本地 release DMG 安装验证中(设置动画/胶囊换肤/持久化)

兼容性

  • 无新权限、无版本号变更、无数据格式变更(capsuleStyle 已有 serde default 兜底)

PR Type

Bug fix, Enhancement, Tests


Description

  • Sync capsule style immediately on settings save

  • Add sliding thumb to recording mode selector

  • Animate silence auto-stop expand/collapse and unify dropdowns

  • Reduce update overlay and add preference tests


Diagram Walkthrough

flowchart LR
  A["set_settings saves prefs"] --> B["sync capsule style atomic"]
  A --> C["emit prefs:changed"]
  B --> D["next recording frame uses new style"]
  C --> E["capsule webview live restyle"]
  F["recording mode change"] --> G["sliding thumb animates"]
  H["silence auto-stop toggle"] --> I["section expands/collapses"]
Loading

File Walkthrough

Relevant files
Bug fix
2 files
settings.rs
Sync capsule style immediately on settings save                   
+6/-0     
AutoUpdate.tsx
Reduce update dialog overlay opacity                                         
+4/-1     
Enhancement
5 files
coordinator.rs
Add capsule style sync helper with test                                   
+40/-0   
Capsule.tsx
Live-update capsule style from prefs broadcast                     
+35/-1   
SelectLite.tsx
Add selected value switch animation                                           
+4/-0     
RecordingInputSection.tsx
Add sliding thumb and animated settings sections                 
+118/-31
global.css
Add select value transition keyframes                                       
+7/-0     
Tests
1 files
types.rs
Add capsule style preference roundtrip test                           
+18/-0   

sim added 3 commits August 4, 2026 17:21
修复 Windows/Linux 上「切到默认风格仍显示流光 Siri」:样式原子此前只靠
emit_capsule 主线程闭包 ~30Hz 同步,入场帧 payload 又是同步前克隆的旧值,
Windows 主线程拥塞时闭包延迟会让整场录音下发旧样式。现在 set_settings 保存
时直接同步原子,任何平台下次录音从入场帧起就携带最新样式;胶囊 webview 另
监听 prefs:changed 广播,录音中切换也当帧换肤,并重置胶囊瞬态(退出动画/
插入字数/操作态)
- 录音方式分段选框改为滑动指示块(left/width 过渡),并修复首次进入设置时
  因 prefs/platformCaps 异步加载时序导致的高亮块永不定位
- 「静音后自动停止」整组随录音方式切换从下方拉出/收回(grid 0fr→1fr),
  不再突然跳出;秒数行随开关展开/收起,开关行不再因内联下拉抽搐
- 静音时长下拉改用 SelectLite 默认底色、固定 200 宽,与麦克风下拉同视觉、
  右边缘对齐
- SelectLite 触发器选中值切换动画(ol-select-value-in)
- UpdateDialog 全屏遮罩 0.18→0.05:不再把设置页白色选项行盖成灰色
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

(Review updated until commit 3604cfa)

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

903 - Partially compliant

Compliant requirements:

Non-compliant requirements:

Requires further human verification:

  • Visual result of the expand/collapse animations and the sliding segment thumb can only be confirmed by manual UI testing.
  • Windows/Linux "capsule style applies immediately during a live recording" fix should be validated on the target platforms.
⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Initial-mount reset

The transient-state reset effect runs once on initial mount in addition to when the style actually changes, clearing warmStartRef, operatingRef, and insertedCharsRef right after the component first renders. If any init path synchronously sets these refs before the effect runs (e.g. a preview/payload that starts in recording state), the state would be silently cleared without a style change. In the normal flow this is harmless, but it's a behavior change from the previous code — verify no init sequence writes these refs before mount effects execute.

useEffect(() => {
  setLeaving(false);
  insertedCharsRef.current = 0;
  operatingRef.current = false;
  warmStartRef.current = null;
}, [capsuleStyle]);
Label-keyed animation

The value-switch animation is keyed by displayLabel instead of the selected value. If two options ever resolve to the same label (e.g. a translated string collision or a default/fallback rendering where multiple values display the same text), switching between them won't replay the ol-select-value-in animation, and the key mismatch will cause React to replace the node anyway. Current options in the codebase use distinct labels, so this is a low-risk edge case, but keying by the raw value would be more robust.

key={displayLabel}
style={{
  flex: 1,
  minWidth: 0,
  overflow: 'hidden',
  textOverflow: 'ellipsis',
  whiteSpace: 'nowrap',
  color: selected ? 'var(--ol-ink)' : 'var(--ol-ink-4)',
  // 值切换动画:key 变化让 span 重挂载,重放 ol-select-value-in
  //(global.css)。仅选中值变化时触发一次,不是常驻动画。
  animation: 'ol-select-value-in .16s var(--ol-motion-quick)',
}}
Event ordering

The new prefs:changed listener updates capsuleStyle immediately, while the existing capsule:state listener still carries a payload style from the start of the session. If the user changes the style mid-recording and an older capsule:state event emitted before the change arrives afterward, the style could briefly revert. Both paths are described as idempotent, so the impact is limited to a transient flicker, but it's worth confirming the last-write-wins behavior is acceptable for live-record style switching.

useEffect(() => {
  if (!isTauri) return;
  let unlisten: (() => void) | undefined;
  let cancelled = false;
  (async () => {
    const { listen } = await import('@tauri-apps/api/event');
    const handle = await listen<{ capsuleStyle?: CapsuleStyle }>('prefs:changed', event => {
      const next = event.payload?.capsuleStyle;
      if (next === 'siri' || next === 'classic') setCapsuleStyle(next);
    });
    if (cancelled) handle();
    else unlisten = handle;
  })();
  return () => {
    cancelled = true;
    if (unlisten) unlisten();
  };
}, []);

pr_agent #912 评审两项:
- 录音方式非切换式/开关关闭时,折叠容器内 Toggle 与秒数下拉仍在 tab 顺序
  与 a11y 树里,键盘用户可 Tab 到不可见控件 —— 收起时加 inert + aria-hidden
  (与 Collapsible 同款方案)
- 语言切换/窗口缩放导致按钮重排时滑动指示块停在旧位置 —— ResizeObserver
  监听 track 尺寸变化兜底重测
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit 3604cfa

@appergb
appergb merged commit 1fd9b43 into beta Aug 4, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant