Skip to content

Releases: MoonlitDropOfBlood/DSH-Desktop

v1.10.10

Choose a tag to compare

@github-actions github-actions released this 28 Oct 16:28

[1.10.10] - 2026-10-29

变更

  • 内置 Node 运行时升级 v24.19.0 → v24.22.1(当前官方最新 LTS):scripts/fetch-node.js
    默认版本更新,AGENTS.md / docs/agents/install-and-update.md §1b 文档同步。fetch-node 的
    .version 标记校验保证旧缓存(24.19.0)会被自动清掉重下,不会混进新安装包。

v1.10.9

Choose a tag to compare

@github-actions github-actions released this 27 Oct 00:54

[1.10.9] - 2026-10-27

修复

  • v1.10.8 修复后仍残余的两条错误状态源(切换会话后当前会话的右侧栏展开按钮消失 / 插件 client 重建后窗口控件模式冻结在首帧)。真实现场取证(保留现场、未刷新,Electron 临时 inspector + 全 DPI 原生捕获)确认 1.10.8 的 108px 常量与标记同源方案本身几何正常(右栏收起按钮右缘 1398、金刚键左缘 1412,间隙 14px),错乱来自另外两条独立缺陷:
    • ① 版本信号通知死路:placementStore.setVersion() 先写 versionModern 再调 DOM 兜底通知,而该通知一见版本已到位就早退——权威版本到达后从不通知订阅者。首帧若按 DOM 兜底判定(如干净首帧 = LEGACY),之后即使桥报来 0.1.5+ 版本,placement 也永久冻结,控件模式与避让规则全部错误。修法:拆出独立 publish();setVersion() 直接发布版本结果(MODERN→LEGACY 方向同样生效),DOM 兜底通知保留原早退(省掉版本落定后的无效全文档扫描)。
    • ② 右栏状态标记被隐藏会话钉死:<html data-dsh-desktop-rightbar-open> 原由文档级 querySelector("[data-sidebar-right-open]") 推导,而核心会保留其他会话的右栏子树(hidden 但仍带 open 属性)——现场实测文档中同时存在两个 open 面板、其一属于隐藏会话。切到右栏收起的会话后,隐藏面板让标记恒为真,当前会话的头部展开按钮被 display:none 持续隐藏(2 秒轮询只是反复算出同一个错误结果)。修法:只统计可见的 open 面板(closest('[hidden], [aria-hidden="true"]') === null,与核心 visibleSidebarPane 同口径);观察器同时监听 data-sidebar-right-open / hidden / aria-hidden,会话切换立即生效,2s 轮询仅兜底插入前赋值。
    • 两条修复均不触碰 1.10.8 的 108px CSS 常量、80px 头部避让与标记清理逻辑。

内部

  • 新增 scripts/test-placement-rightbar.js 行为守卫(已并入 npm test):从 client.js 逐字抽取生产函数(PLACEMENT / isModernVersion / createPlacementStore / hasVisibleRightbarOpen)配微型 DOM double 真执行——实现缺失或仍是旧语义都会明确 FAIL,不是拿副本测副本。54 条断言覆盖:版本到达必须切换且恰好通知一次、重复/无效版本不重复通知也不撤销已生效值、DOM 兜底不污染权威值(双向)、可见性语义(仅隐藏 / 仅 aria-hidden / 混合 / 隐藏祖先 / aria-hidden="false" / closest 抛错上抛)、观察器过滤器三属性齐全。变异验证:把三组旧实现改回去分别 15 / 2 / 6 条断言翻红,门禁非绿摆设。
  • 隔离真机动态几何验证:临时 DSH_HOME + 独立端口起真实核心(0.2.1-rc.3),加载本仓库插件产物,无头 Edge + CDP 驱动完整动态链——隐藏 open 面板不置标记(座位保持 28×28 / margin-right 80px)、取消隐藏立即置标记并 display:none(矩形归零)、再次隐藏后座位与 80px 几何恢复、金刚键组全程唯一。
  • eslint.config.js 补齐 Node .mjs 脚本的全局声明:scripts/**/*.mjs 此前落在 node globals 之外(WebSocket 也缺失),全量 npm run lint 恢复零报错。

v1.10.8

Choose a tag to compare

@github-actions github-actions released this 23 Oct 16:13

[1.10.8] - 2026-10-24

修复

  • 金刚键导致部分 UI 错乱(电脑息屏恢复后 / 插件 client 刷新后发作,按 Ctrl+R 刷新才恢复):金刚键小组件本身一直画得对(固定 top:10px; right:8px,组宽 92px),错乱来自它宣称「右上角被占」的方式——这件事被拆成了三份互不相干的状态:① 小组件 DOM(React 渲染);② <html data-dsh-desktop-corner> 在场标记(effect 设置,从不清理);③ --dsh-desktop-controls-clear 占位宽度(sync() JS 实测小组件宽度后写进 <html> 的内联 style,而插件的 apply() cleanup 会 removeProperty 掉它)。②③ 因此能互相打架,且坏状态只有整页刷新(Ctrl+R)才能复位——正是本次报障的现象。两种坏法各有一种典型症状:
    • 占位宽度一丢失 → 核心侧避让规则退化成 calc(var(--dsh-desktop-controls-clear, 0px) - var(--detail-gutter, 0px)) = calc(0px − 24px) = 负外边距,把核心自己的按钮往右推进金刚键带。实测量到:右栏打开时,「收起」按钮的 margin-right 从 108px 掉到 0px,位置 x=1504→1612,与金刚键组(x∈[1546,1638])重叠 26px——按钮被浮层盖住、点击被吃掉(与 v1.10.7 修的那处重叠同源,但触发条件不同:那次是核心自己的留白算错,这次是壳的占位变量丢了)。
    • 标记一泄漏 → 核心为一个已经不在屏幕上的小组件继续让路:会话头部 corner 座位永久留着 80px 空洞(那条 80px 常量规则原本没带标记限定),详情操作组被无谓地推走 84px。
    • 修法(把状态收敛到一个真源,让坏状态结构上不可能):占位宽度改为样式表常量 [data-dsh-desktop][data-dsh-desktop-corner]{--dsh-desktop-controls-clear:108px}——它与「金刚键在场」标记同源,二者不可能再不一致,也没有 JS 变量会丢;标记随小组件一起摘除(WindowControls 的 effect cleanup + apply() 的 cleanup 各一次,fiber 死在 React 卸载之前也覆盖到);避让公式再包一层 max(0px, …)——万一变量缺失或为 0,退化成「不避让」(按钮组回到核心自己的位置)而不是「往右撞进金刚键」;头部 corner 座位的 80px 规则补上 [data-dsh-desktop-corner] 限定,没有金刚键时不留空洞。
    • 实测对照(Edge 无头 + CDP 复现台,右栏打开态量几何):修复前 cleanup 后重叠 26px;修复后同一步骤重叠 0px、按钮位置不变(x=1504..1532,margin-right:108px)。标记撤掉后头部座位外边距从 80px 回到核心自己的 -16px(停止让路)。

内部

  • 新增 scripts/repro-corner-hmr.mjs 真机复现台(npm run repro:corner):Edge 无头 + CDP,注入一个仿真 preload 桥(window.dshDesktop,字段与 preload.js 对齐)让桌面壳的客户端插件在普通浏览器里真渲染出金刚键,然后量几何 / 截图。场景:stale(模拟插件 cleanup 后的半死状态,直接量出与金刚键组的重叠像素)、rebuild(touch 插件 client.js → 核心的 HMR watch stat 到 mtime 变化 → 广播 rebuilt 帧 → 浏览器端拆掉重建该 client 模块)、offline(CDP 断网再恢复 = 息屏后 SSE 重连)、openbar(右栏开关)。配套 scripts/repro-plugin-lifecycle.mjs(npm run repro:plugin-lifecycle)给 profile 里的插件副本插桩,把 apply() 的进入/退出、WindowControls 挂载、占位测量值打到 window.console——核心的 ctx.logger 在渲染进程里不可见,只能这样取证。

  • scripts/test-corner-avoidance.js 补四条新不变量(已并入 npm test):① 占位宽度不得由 JS setProperty 发布(可丢失的值不能进避让公式);② 标记必须带 cleanup(与小组件同生共死);③ 避让规则永不为负(max(0px, …),变量缺失时退化为 0 而非负外边距);④ 头部 80px 规则必须带 [data-dsh-desktop-corner] 限定。AGENTS.md §4 与 docs/agents/desktop-ui.md §4 同步补上这条红线与完整推导。

v1.10.7

Choose a tag to compare

@github-actions github-actions released this 22 Oct 13:46

[1.10.7] - 2026-10-20

修复

  • 金刚键与「自动化任务」详情页右上角按钮重叠(按钮被盖住、点不到):DSH 的「自动化任务」管理插件(@deepseek-ai/dsh-client-ui-schedule)把任务管理整页注册在核心 main 槽,右半边的详情面板就是那个编辑页(面板里的「运行时间 / 重复 / 日期 / 时间 / 时区」几行即是改时间的地方),它顶栏右上角那排操作按钮(关联会话 / 更多 / 关闭)落在 x∈[W−100, W−16]、y∈[8,36] —— 与桌面壳的金刚键固定浮层(x∈[W−100, W−8]、y∈[10,38],z-index 顶格)完全重叠:按钮被浮层盖住,点击也被浮层吃掉(看上去像两个「更多/关闭」叠在一起)。修法沿用右栏 chrome 那把尺子:把操作组整体左移 margin-right: calc(var(--dsh-desktop-controls-clear) - var(--detail-gutter)),--dsh-desktop-controls-clear 是壳实测发布的值(金刚键组 92 + 窗口内缩 8 + 间隙 8 = 108px),减掉核心自己的详情留白后两端相消,按钮组右缘恒为 W−108(金刚键左缘再往左 8px),核心改留白(24→20→16)只会让间隙变宽、永不相撞。选择器只认插件的稳定 data 属性 data-detail-tab(:has(> [data-detail-tab]) + :last-child,核心的哈希类名 t-XoWW_* 变了也不受影响),并三重限定作用域:[data-slot="main"](右栏抽屉里是同一个组件,但它排在 38px 高的 dock 条之下,与金刚键本就不重叠,不该被推)、[data-rightbar-collapsed](右栏文件浏览器开着时中心列右缘不到窗口右缘,本来就不撞)、新增的 MODERN 模式标记 [data-dsh-desktop-corner](LEGACY 模式不发布 clearance、不触发)。另经实机测量排除两处原有避让机制均已生效:右栏抽屉自己的全屏/收起按钮(被 --dsh-desktop-controls-clear 推开)、会话头部 corner 座位(常量 80px 让位 / 右栏打开时整条隐藏)——注入桌面壳真实 CSS 后扫描冲突带(W−100…W−8 × 10…38),每种可达状态下该带内只剩金刚键自己。

内部

  • 新增 scripts/test-corner-avoidance.js 并并入 npm test:把这次的几何常量钉死——金刚键度量(28px 盒 / 4px 间隙 / 8px 内缩 / 10px 顶距 → 组宽 92px)、clear 的 108px 推导、避让规则的形状(clear − gutter、:has(> [data-detail-tab]) + :last-child、三重作用域)与「留白相消后按钮组右缘 ≤ 金刚键左缘 − 8px」的不等式,避免以后改动其中任一常量而静默恢复重叠。AGENTS.md §4 与 docs/agents/desktop-ui.md §4 同步补上这条红线与完整推导。

  • 同批修掉 v1.10.7 自己的 Linux 发布失败(CI):v1.10.7 首次发版时 Linux job 挂在 rpm 目标上(rpmbuild failed (exit code 1)),AppImage 与 deb 都已打好,整个 job 却红 → release job 被跳过 → Release 根本没发出去(Windows/macOS 也白跑)。根因不在我们的代码:2026-10-19 GitHub 把 ubuntu-latest 换成 26.04 镜像,其 rpm 6.0.1 忽略 electron-builder 内置 fpm 1.9.3 的 --define buildroot <dir>(rpm 4.20+ 改从 %{_builddir}/<name>-<version>-build/BUILDROOT 取文件)→ rpm 包里每个文件都是 "File not found" → rpmbuild exit 1。修法两条:① Linux job 固定 runs-on: ubuntu-24.04(rpm 4.18.2;v1.10.6 的 .rpm 正是在它上面产出的),勿跟随 ubuntu-latest;② rpm 目标拆成独立 step 且 continue-on-error: true——一种格式绝不能阻断整个发布,artifact 上传配 if-no-files-found: ignore。AGENTS.md §11 与 docs/agents/install-and-update.md §11 同步补上这条红线。

v1.10.6

Choose a tag to compare

@github-actions github-actions released this 30 Sep 17:38

[1.10.6] - 2026-10-01

修复

  • 「重启核心」快捷键 macOS:⌘⇧R → ⌃⌥⇧R(Windows/Linux 仍为 Ctrl+Alt+Shift+R):1.10.5 改的 ⌘⇧R 与壳自带的「强制重新加载」撞车——⌘⇧R 正是 Electron 给 { role: "forceReload" } 的默认加速键,而该 role 就在 buildMenu() 的视图菜单里(main.js),与新注册的「重新启动 DSH」同处一个应用菜单模板。上次漏掉的原因:结论「全树扫描不存在 primary+shift+KeyR 绑定」只扫描了 DSH 核心的 node_modules 树,而 Electron role 加速键是原生命令层注册的,DSH 的 dsh-client-shortcuts/lib/protocol.js 完全感知不到——该结论对核心成立、对壳不成立。新键 ⌃⌥⇧R 同样是三修饰键(protocol.js L200 对 windows/macos 的 modifiers.length >= 3 直接放行,DSH 与浏览器/系统都够不着),但解决了 ⌘⌥⇧R 真正难受的根因:不是"三个修饰键"本身,而是 ⌘ 与 ⇧ 同在右手小指、必须叠键或换手;⌃⌥⇧R 是 ⌃左小指 / ⌥左拇指 / ⇧右小指 / R左食指,两只手零叠键,且修饰键名字与 Windows 端逐字一致。同时避开 L221 在 mac 上保留的两个族:⌥ 不带 ⌘(重音死键命名空间)与 ⌃+⌘。core.shortcut.mac 的 zh/en 两条文案(locales.js + client.js 内嵌副本,共四处)改为 ⌃⌥⇧R,core.shortcut.win 两条不动。restartDSH() 链路、菜单结构、托盘行为均无变化。

内部

  • 新增 scripts/test-shortcut-collisions.js 并并入 npm test:把这次事故的根因(方法论漏洞)固化成 CI 守卫——解析 main.js 取出 restartCoreAccelerator() 的两个平台取值,与「所有显式 accelerator: 字面量」+「所有在用 Electron role 的默认加速键」逐平台求交集,并校验 ≥3 修饰键、protocol.js L221 保留族、DSH web 双修饰键生成空间。role 可以不写 accelerator 仍自带一个默认键,这正是 role 不可见、扫描扫不到的成因。脚本内含一条自证断言,证明它确实能抓到 v1.10.5 那个 ⌘⇧R,避免守卫本身空转。scripts/test-locales.js 另加四条字面量断言,把 win/mac 快捷键提示钉死(该提示已错过两次,不该再靠人眼同步三处副本)。

v1.10.5

Choose a tag to compare

@github-actions github-actions released this 29 Sep 17:40

[1.10.5] - 2026-09-30

变更

  • 「重启核心」快捷键 macOS 特例:⌘⌥⇧R → ⌘⇧R(Windows/Linux 仍为 Ctrl+Alt+Shift+R):MacBook 上三修饰键是要小指+无名指+食指同时按住别扭姿势,纯属手势负担,且两端本就不该用同一个键。改前把 DSH 侧的占用查了个遍(这次不靠推测):dsh-client-shortcuts 在本壳里跑的是 web runtime——我们的 preload 不暴露 keyboard 桥,client.js:1859 的 runtime === "desktop" 分支会抛 "Desktop keyboard bridge unavailable",所以它必然落到 web 变体;全树扫描 KeyR 只找到一条真正的快捷键声明(dsh-client-ui-sidebar-right/lib/client.js:275,右栏 refresh = primary+KeyR / primary+alt+KeyR,其余 6 处命中是终端区裸 Ctrl+R 的放行规则、LuckySheet 表格键、xterm 枚举表),即 mac 上 ⌘R(我们自己的刷新)与 ⌘⌥R(DSH web 刷新)占着前两格,不存在任何 primary+shift+KeyR 绑定,且 protocol.js:109 明确放行 primary+shift 两修饰键。反过来 Windows/Linux 的三修饰键是真不能动:Ctrl+Alt+R 正是 DSH 的 web refresh、Ctrl+Shift+R 是 Chromium 硬刷新,只有 modifiers.length >= 3 那条一律放行的规则能给出第三个空位。实现上两处注册点(app 菜单注册 + 托盘展示)收敛到同一个 restartCoreAccelerator(),不再各写一份字面量;设置页说明文案 core.shortcut.mac 的 zh/en 两条改为 ⌘⇧R,Windows 两条不动。restartDSH() 链路、菜单结构、托盘行为均无变化。

v1.10.4

Choose a tag to compare

@github-actions github-actions released this 29 Sep 12:53

[1.10.4] - 2026-09-29

修复

  • 侧栏左下角更新徽章"点了没反应 + 能反复点"(更新全局单任务):两条独立根因——① 徽章没有任何运行中状态:鲸港自更新的进度只经 dsh:shellDownloadProgress 推给设置页桌面版分区,那是侧栏徽章唯一看不到的地方,点击的返回值被 .catch(() => {}) 吞掉,屏幕上一个字都不变;② dsh:downloadShellUpdate 根本没有并发保护(核心更新有自己的 installInProgress,壳自更新一把都没有),第二次点击会对同一个 temp 路径再起一条完整下载,两条完成回调各拉一次安装器。修复:主进程新增全局单更新任务状态机 updateTask(核心安装与鲸港自更新共用一把锁——两条链都要替换运行中的应用文件,本就不可能并行),setUpdateTask() 是唯一写入口(开任务/推进阶段/收尾都在这里广播,纯进度按 400ms 节流、阶段切换立即推),pushUpdateState() 把它连同 kind/phase/percent 推给渲染端;sendShellProgress() 成为壳下载进度的唯一漏斗并顺带驱动该状态;dsh:installUpdate/dsh:downloadShellUpdate 忙时回 {busy:true}(不再静默 resolve 成功形状的失败);updateDSH 守卫扩为 installInProgress || updateTaskBusy()。渲染端徽章改三态:idle(保留两次点击防误触)/ busy(disabled 状态徽章 + 细进度条,文案按「检查中…/下载中 N%/正在校验安装包…/已下载,正在启动安装程序…」切换,判定放在 autoUpdate 早退之前——运行中的任务没有静默这一说)/ failed(「更新失败,点击重试」,单击即可重试,tooltip 带失败原因);设置页两个下载按钮同步置灰。拉起安装器后状态保留 30s 再清零,避免秒内二次点击再拉一个安装器。新增 5 组 i18n key(zh-CN/en-US 同步进 locales.js + client.js 内嵌副本,scripts/test-locales.js 加 20 条断言)。机制详解见 docs/agents/install-and-update.md §9e。

v1.10.3

Choose a tag to compare

@github-actions github-actions released this 25 Sep 22:47

[1.10.3] - 2026-09-26

变更

  • 「重启核心」快捷键 Ctrl+Alt+R → Ctrl+Alt+Shift+R(macOS ⌘⌥⇧R):DSH 0.1.7 引入客户端快捷键系统(dsh-client-shortcuts),其默认键位规则把 Ctrl+Alt+X 系划为默认规划区——desktop 下 Ctrl+Alt+B(右栏开关)/Ctrl+Alt+O(打开本地)/Ctrl+Alt+Enter(全屏,fixed)已占用,web 下 Ctrl+Alt+T/P/O/B 更多,按规则"不含 Alt 的默认键位使用 Mod+Alt",后续 R 系命令(刷新/重载类)落进 Ctrl+Alt+R 只是时间问题。壳的重启快捷键与该规划区冲突后让路:app 菜单(真正注册处)、托盘菜单展示项、设置页快捷键说明文案(zh/en × win/mac 四条,locales.js + client.js 内嵌副本同步)统一改为三修饰键 Ctrl+Alt+Shift+R——保留 R 的 Restart 语义,DSH 默认键位规则最高只到双修饰键、不会生成三修饰组合,浏览器/系统也无占用,彻底跳出冲突区。纯展示层变更:restartDSH() 链路、菜单结构、托盘行为均不动。

v1.10.2

Choose a tag to compare

@github-actions github-actions released this 20 Sep 17:45

[1.10.2] - 2026-09-21

修复

  • 右栏展开后会话头部残留 88px 死区(金刚键双重避让):0.1.5+ 现行方案假设「右栏打开 → 头部 corner 座位 :empty 自动隐藏」,但核心槽位 outlet 恒留一个 display:contents 锚点 div(<div data-slot="conversation.session.header.corner">)作为子节点——即使 ExpandButton 在展开态渲染 null——:empty 永不命中,核心的 :empty{display:none} 与「margin-right:80px 规则随之失效」的设计前提一并落空:展开后空 corner 仍以 margin-left:8 + margin-right:80 占位,头部右缘留出 88px 死区(此时金刚键小组件落在面板上方、面板自带的全屏/收起按钮已由 --dsh-desktop-controls-clear 正确避让,头部这份避让纯属多余)。修复:apply() 新增一个 attributeFilter MutationObserver(+2s 轮询兜底面板重挂载),把面板的 data-sidebar-right-open 状态镜像为 <html data-dsh-desktop-rightbar-open> 标记,CSS [data-dsh-desktop][data-dsh-desktop-rightbar-open] [data-conversation-header-corner]{display:none} 按标记整条隐藏——刻意不用 :has() 探测 corner 内容:Chromium 跨 display:contents 锚点的 :has() 失效重算不可靠,实测会把收起态的展开按钮一并误藏(本次经会话内动态插件两态实测验证:展开态 corner 完全脱离布局、收起态按钮正常显示)。LEGACY(≤0.1.4)无该 DOM 属性、结构性不受影响;普通浏览器无 data-dsh-desktop 标记同样不受影响。

v1.10.1

Choose a tag to compare

@github-actions github-actions released this 18 Sep 16:11

[1.10.1] - 2026-09-18

变更

  • 核心启动链提速(spawn 前 shell 侧开销 ~54ms,此前实测 184–796ms):① dshRuntime() 不再为选运行时同步 spawn node.exe --version——改为读 fetch-node.js 早就写在二进制旁的 .version 标记(缺失/解析失败才回退 execFileSync 探测),并把解析结果进程级记忆化(内置 node 只随壳更新变化、override env 启动即固定);② 重启链杀核后的首次端口复查立即执行(killDSH 已等死透 + settle 窗,原来的盲等 400ms 前摇是纯延迟);③ killDSH 的 400ms settle 从 syncSleep 改为异步定时器——原实现每次重启/退出都把主线程硬冻结 400ms(窗口拖动/托盘/启动页全卡)。新增启动耗时日志 core ready in N ms (spawn → URL),以后启动变慢可直接从日志归因(壳准备 vs 核心自身启动)。真机验证(隔离环境 + E2E_RESTARTS 钩子):冷启动 spawn 后 4377ms 就绪,完整重启链 4.76s。

修复

  • log() 的 stdout EPIPE 崩溃风暴:stdout 是管道且读取方先退出时(dev 管道、CI 采集器、后台任务托管),EPIPE 从流上异步抛出 → uncaughtException → 处理器自己又 console.log → 再抛 EPIPE → 无限递归,实测日志以 ~30MB/s 膨胀。修复:进程启动时给 process.stdout/stderr 挂空 error 监听吞掉流级错误 + console.log 本体包 try/catch(同步抛的边角);文件日志是唯一权威落点,console 损失永远不许拖垮主进程。
  • build-installers 发布 job 的 401 Bad credentials(v1.10.0 发布事故):release job 的三个 gh CLI 步骤用 secrets.GH_TOKEN(个人 PAT)调 GitHub API——该 secret 未配置/失效时 gh 直接报 401 Unauthorized: Bad credentials,Release 发不出去(4 个构建 job 全部成功、产物已上传,唯独最后一步发布失败)。工作流顶部本就有 permissions: contents: write,内置 GITHUB_TOKEN 的权限完全覆盖「创建/编辑 Release + 上传资产」,改为 secrets.GITHUB_TOKEN 后不再依赖任何仓库 secret,这类失败从根上消失。release-whaleharbor-promo.yml 此前已用 GH_TOKEN || GITHUB_TOKEN 兜底,本次对齐。
  • build-installers 资产校验的空间→点号误判(v1.10.0 发布事故续):GitHub 上传资产时把文件名空格规范成点号(DeepSeek Harness Desktop Setup 1.10.0.exe → DeepSeek.Harness.Desktop.Setup.1.10.0.exe),verify 步骤按原始名精确比对永远匹配不上,8 个资产明明全在 Release 上却 5 次重试后判失败。改为双侧归一化(小写 + 空格→点)后比较。