Skip to content

fix(windows): 静默启动 #468 最后一公里 - 前端兜底 - #474

Merged
appergb merged 5 commits into
betafrom
fix/silent-startup-frontend-show
May 18, 2026
Merged

fix(windows): 静默启动 #468 最后一公里 - 前端兜底#474
appergb merged 5 commits into
betafrom
fix/silent-startup-frontend-show

Conversation

@appergb

@appergbappergb commented May 18, 2026

Copy link
Copy Markdown
Collaborator

User description

closes#468 — 详见 PR description (Rust 端 #471 修了 setup + single-instance 后,前端 App.tsx 仍无条件 IPC show 把窗口拉出来;现在 show 前读 startMinimized 兜底)


PR Type

Bug fix


Description

  • Respect startMinimized before showing

  • Preserve fallback when settings read fails

  • Stop frontend relaunch from reopening


Diagram Walkthrough

flowchart LR
A["App startup"] -- "read settings" --> B["getSettings()"]
B -- "startMinimized=true" --> C["Skip show()"]
B -- "read fails" --> D["Fallback to show window"]
C -- "preserve silent launch" --> E["Main window stays hidden"]
D -- "keep existing behavior" --> E
Loading

File Walkthrough

Relevant files
Bug fix
App.tsx
Guard startup show by settings

openless-all/app/src/App.tsx

  • Imports getSettings for startup prefs
  • Checks startMinimized before window show
  • Skips relaunch window reveal when minimized
  • Keeps legacy show fallback on read errors
+21/-8

appergband others added 5 commits May 17, 2026 19:14
release-sync: beta → main (no tag, internal merge)
…诊断 (#471)
集中修复 4 个 Windows-only bug:
- #469 IME 切不回 — restore_profile 对称补 legacy TSF ChangeCurrentLanguage + ActivateLanguageProfile,回归点是 c3b5932 升级激活路径时漏改恢复路径。现代 ActivateProfile 失败降为 warn(legacy 已完成视觉切回)。
- #468 Win11 静默启动失效 — single-instance 回调读 start_minimized,true 时跳过 show_main_window;托盘 / CLI intent 路径不受影响。
- #466 QA 弹窗 X/ESC 关不掉 — Win 上从 SW_SHOWNOACTIVATE 切到 Tauri window.show + set_focus,让 webview 拿键盘焦点。配套 focus-dance:begin_qa_session 抓选区前临时把焦点还给用户原 app,抓完再 refocus QA;多轮场景下用 GetWindowThreadProcessId 区分本进程窗口,刷新 qa_focus_target,避免 stale HWND。
- #470 录音胶囊不显示 — 加 per-process 一次性诊断 log(CapsuleState 用 capsule_state_log_name 显式短名,不走 Debug 防字段扩展时泄露),下次能直接定位 prefs / state / Win32 哪一环失败。
pr_agent 五轮迭代到 No major issues + No security concerns;Cloud 评审建议的 begin_qa_session 双锁 TOCTOU 已合并为单 scope。三平台 build + pr_agent CI 全绿。
#470 没标 closes,是 refs—需要真机日志才能定位真因,先 ship 诊断手段。
权限页 (Settings → 快捷键 → 权限):
- Pill 组件加 whiteSpace:nowrap + flexShrink:0 → "已安装"徽章在 Win
窄宽下不再被挤成 3 行(每个汉字一行)。
- 行内 message span 加 whiteSpace:nowrap + overflow:hidden +
textOverflow:ellipsis + minWidth:0 → 长文本("Windows 低层键盘 hook
已安装")超宽时省略号收尾,徽章保持完整。
- zh-CN 描述大段精简:descAcc / descNoAcc / micDesc / accDesc /
hotkeyDesc / networkDesc / windowsImeDesc / windowsIme.* 都改短,
减少 Win 上拥挤感(用户反馈 zh 文案过长)。
高级页 (Settings → 高级 → 本地 ASR):
- Windows 上把"本地 ASR 模型(实验性)"标题区 + 警告小字 + Qwen3 行
整组 opacity:0.45 灰显 —— Qwen3 在 Win 是 stub 不支持,那条"实验性"
主线在 Win 没意义;用户视觉关注点应落到下方独立的 Foundry 行(Foundry
保持正常颜色)。Toggle 的 disabled 行为已经在原 onToggle 条件里,本次
纯加视觉灰显。
#471 在 Rust 端 setup() / single-instance callback 都加了 start_minimized
suppression,本机日志确认 Rust 路径完全正确("[main] start_minimized=true
→ 跳过初始 show")。但用户在 Win11 1.3.4-3 上仍然「重启进桌面那一刻主
窗口就出来了」。
定位到最后一条遗漏路径在前端:App.tsx 里 mount 时一条 useEffect 无条件
通过 IPC 调 `getCurrentWindow().show()`,把 Rust 端已经 suppress 的窗口
又拉出来 —— 这条路径 Rust log 完全看不见(走的是 plugin-window 的
showWindow IPC,不经过我们的 show_main_window helper)。
那条 useEffect 当初是给 issue #163 引入的:Windows 权限探测可能死锁让
首屏卡灰,所以前端兜底 force show 一次。
修法:show 前先读 prefs.startMinimized,true 就 return。读 prefs 失败
(极少数情况)保持原 show 行为,不让 #163 的兜底语义丢失。
closes#468 (the actual one this time, Rust + JS 双端都收敛了)
@github-actions

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis ✅

468 - PR Code Verified

Compliant requirements:

  • 启动时在前端读取 startMinimized,为 true 时跳过 show()

Requires further human verification:

  • 需要在 Win11 真机上确认登录启动和手动二次启动都不会再弹出主窗口。
⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ No major issues detected

@appergb

Copy link
Copy Markdown
CollaboratorAuthor

@claude 请审查这个 PR。

简短说明:

  • fix(windows): 修复 #466 / #468 / #469 / #470 四个 Windows bug #471 在 Rust 端两条 show 路径都加了 start_minimized 抑制,用户日志确认 Rust 正确没问题
  • 但用户在 1.3.4-3 Win11 真机仍然「开机进桌面那一刻主窗口就出来」
  • 定位:App.tsx mount 时一条 useEffect 通过 IPC getCurrentWindow().show() 把窗口拉出来,绕过 Rust suppression,Rust log 完全看不到
  • 修法:show 前读 prefs.startMinimized,true 就 return
  • pr_agent 已一轮通过(No major issues / No security concerns),三平台 CI 全绿

主要关注点:

  1. App.tsx 改成异步 IIFE 之后 cancelled 标志处理是否正确(getSettings 之后再判 cancelled,再 dynamic import)
  2. getSettings 失败时走原 show 路径是否合理(保留 [windows] hotkey hook 永远 starting 时启动 gate 死锁,主窗口卡 "正在启动" 灰屏 #163 的「权限探测死锁→前端兜底拉首屏」语义)
  3. 跨平台:这条 useEffect 在 mac 上也跑,prefs.startMinimized 在 mac 上意味着什么、有没有副作用

辛苦。

@claude

claudeBot commented May 18, 2026

Copy link
Copy Markdown

Claude finished @appergb's task in 2m 48s —— View job


PR #474 代码审查

  • 读取变更文件和 git diff
  • 分析 cancelled 标志处理正确性
  • 分析 getSettings 失败回退路径合理性
  • 分析跨平台(macOS)副作用
  • 输出完整审查意见

总体结论

修复思路正确,针对 issue #468 的最后一公里路径(前端 IPC show 绕过 Rust 抑制)定位准确。三个关注点的详细分析如下。


关注点 1:cancelled 标志处理

结论:基本正确,有一个无害的检测空隙。

当前检测点:

requestAnimationFrame → [cancelled check] → getSettings() ↓
prefs.startMinimized=true → return ✓
prefs.startMinimized=false → ⬇
import('@tauri-apps/api/window')
↓
[cancelled check] ← 第二次检测点
↓
isVisible() → show()

问题所在:在 getSettings() resolve(startMinimized=false 路径)之后到 dynamic import 完成之前,没有 cancelled 检测。若组件恰好在这一时间窗口卸载,IIFE 会继续执行 dynamic import,再到 if (cancelled) return 才停下——也就是说 dynamic import 会被触发但 show() 不会执行。

为什么不是真正的 Bug

  1. @tauri-apps/api/window 是静态依赖,通常已被 bundler 加载,dynamic import 几乎同步完成;
  2. 即便有竞争,cancelledshow() 之前仍然会被正确检测;
  3. 该 useEffect 依赖 [gate, os],在应用生命周期内 os 永不变,gate 只从 checkingready/onboarding 单向转换,重新触发(从而产生竞争)的概率极低。

如果追求最严格的写法,可在 getSettings() 之后加一次检测:

constprefs=awaitgetSettings();if(cancelled)return;// ← 可加if(prefs.startMinimized)return;

不加也不影响正确性,看项目风格偏好。


关注点 2:getSettings 失败时走原 show 路径

结论:合理,保留了 #163 的兜底语义。

}catch(err){console.warn('[startup] read startMinimized failed, falling back to show',err);}// 继续走 show 路径

失败时默认行为是"展示窗口"而非"静默",这是正确的 fail-safe 方向:

唯一值得商量的地方:失败时也许应读取 mockSettings 的默认值 startMinimized: false 而非直接 fallback,但效果等价,且当前写法更显式,更容易调试。


关注点 3:跨平台 —— macOS 副作用

结论:存在一个轻微的设计风险,实际影响极低,但建议后续修复。

当前问题:Settings UI 中 startMinimized toggle 没有平台限制Settings.tsx:600-604AutostartRow 也跨平台),Mac 用户可以将其打开。打开后,这条 useEffect 在 macOS 上同样会 return 而跳过 show()

macOS 的 Rust 端 setup()不做 startMinimized 抑制(这是 Windows 专属修复),所以 macOS 在正常启动时窗口由 Rust 端 show() 打开。但若由于某种原因(边缘场景)窗口在 webview 加载时处于不可见状态,这个前端兜底 show 本是最后的保险——新代码在 startMinimized=true 时把这个保险也禁用了。

场景旧行为新行为
Mac + startMinimized=false(99.9% 用户)isVisible() 检测后 NOP同左,多一次 IPC 读 prefs
Mac + startMinimized=true(极少数)若窗口可见则 NOP,不可见则 show直接跳过 show,窗口可能卡在不可见状态
Windows + startMinimized=trueshow() 被调用(Bug)正确跳过 ✓

建议的改法:将 startMinimized 检测限定到 Windows,与现有平台分支保持一致:

if(os==='win'){try{constprefs=awaitgetSettings();if(prefs.startMinimized)return;}catch(err){console.warn('[startup] read startMinimized failed, falling back to show',err);}}

这消除了对 macOS 的任何影响,且逻辑更清晰:静默启动是 Windows autostart 特性,不适用于 macOS 的 gate 流程。

Fix this →


其他小观察

  1. 性能:现在所有平台的启动 show 路径都多了一次 get_settings IPC 调用。在正常情况下这是 <2ms,可忽略。但如果 get_settings 在特定环境下较慢(如首次启动磁盘 IO),会轻微延迟窗口显示。Windows + startMinimized=false 的用户也受影响,虽然实际观感上几乎察觉不到。

  2. 注释质量:注释写得很清楚,明确解释了为什么要做这件事(Rust log 看不到、[windows] 启动时静默运行功能在 Win11 失效 #468 最后路径、[windows] hotkey hook 永远 starting 时启动 gate 死锁,主窗口卡 "正在启动" 灰屏 #163 语义),这是好的。


是否可以合并

Windows startMinimized=true 的修复逻辑正确,CI 全绿,可以合并。

macOS 的 os === 'win' 守卫是锦上添花的防御性改进,不影响当前 99.9% 用户,可以在这个 PR 里顺手加上,也可以留到后续 cleanup。由你们决定。

@appergb
appergb changed the base branch from main to betaMay 18, 2026 07:29
@appergb
appergb merged commit b4e806e into betaMay 18, 2026
4 checks passed
appergb pushed a commit that referenced this pull request May 18, 2026
合并 PR #474 后随版本号一起出 Beta 包,供 Windows 端验证
startMinimized 兜底是否彻底关掉 #468 漏窗最后一公里。
appergb added a commit that referenced this pull request May 18, 2026
* fix(startup): autostart catch 块缺 return 导致复活主窗口 (#468)
PR #474 引入的"前端兜底"useEffect 在 webview ready 后读
prefs.startMinimized,true 则跳过 currentWindow.show()。但 catch 块只
console.warn 没有 return,控制流继续走到 show()——等于"prefs 读失败 = 强 show"。
autostart 早期 IPC 抖动时 getSettings() 偶发失败,旧逻辑让用户明明开了
静默启动仍把主窗口弹出来。
修复: catch 块加 return,安全侧默认 = 当作 startMinimized=true 保持隐藏。
宁可让用户从 tray 手动唤起,也不要在 autostart 抖动时强 show 一个白色
/ 透明主窗口。
LSUIElement 不在本 PR 范围: 那是全局 macOS UX 决策,会影响 Finder
双击 / Spotlight / Launchpad 启动场景,应单独 PR + 单独评审。
正确做法是 Rust 端动态切 activationPolicy 而非 plist 静态写死。
Refs #468
* docs(startup): clarify catch rationale + improve warn diagnostics
Both local review and PR-Agent bot flagged the "fallback when settings
read fails" semantic reversal. Re-analysis confirms the change is
correct, but the rationale needs to be explicit in the code:
- Rust `get_settings` signature is `pub fn -> UserPreferences` (not
Result), so this catch is unreachable from a normal first-launch
scenario (returns defaults). Only Tauri IPC infrastructure failures
(e.g. __TAURI_INTERNALS__ not ready during autostart early-mount)
reach this branch.
- Tray is registered by Rust setup() before webview ready, so it remains
the universal fallback entry point — no "user locked out" risk.
- Improved console.warn to include err.message for diagnostics, since
raw err objects serialize as [object Object] in Tauri devtools.
No behavior change; comment + log improvement only.
Refs review of #488
---------
Co-authored-by: baiqing <lbx12309@icloud.com>
@appergb
appergb deleted the fix/silent-startup-frontend-show branch September 9, 2026 05:37
Sign up for freeto 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.

[windows] 启动时静默运行功能在 Win11 失效

1 participant

@appergb