知派课表 - Windows 桌面小组件（免安装版）
=============================================

一、这是什么
    把知派课表（kebiao.lezhipai.cn/widget）钉在你的桌面上：
    卡片位于【壁纸之上、桌面图标之下】，不占任务栏、不进 Alt-Tab，
    平时完全不打扰，看课表时瞄一眼桌面右下角即可。

二、快速开始
    ★ 先解压！右键 zip →「全部解压缩」→ 打开解压出来的文件夹 → 双击其中的 ZhiPaiWidget.exe
      （直接在压缩包预览窗口里双击是跑不起来的：依赖文件不会被解压出来。
        本版本会检测到这种情况并弹提示，但正确姿势仍然是先解压。）

    1. 双击 ZhiPaiWidget.exe 启动（无需安装，删掉文件夹即等于卸载）。
    2. 【未登录】时桌面不会出现空白卡片，只弹出【登录窗口】——不会有两个卡片。
       登录窗口也只有一个：扫码和账号密码在同一个窗口里切换，不会再各弹一个。
    3. 登录成功后卡片自动出现在桌面右下角，默认显示【本周】课表（一周 7 天）。
    4. 桌面卡片贴在壁纸上、位于图标层之下，鼠标点击会被桌面图标层挡住。
       程序用「点击接力」解决了这件事：你在卡片上点一下，程序立刻把这次点击
       送到卡片对应的位置上——所以卡片上的按钮、课程都是可以直接点的。
       若登录窗口被关掉了，双击右下角托盘图标即可重新打开。

    登录窗口里两种方式任选：
       · 微信扫码（推荐）—— 打开微信 → 扫一扫 → 扫描小程序码 → 手机上确认，卡片自动登录；
       · 账号密码 —— 切到「账号登录」，填用户名 / 密码，勾选「在这台电脑上记住登录」。

三、调整卡片外观（大小 / 透明度 / 样式 / 圆角 / 位置）
    右键托盘图标 →「调整卡片外观…」，拖动滑块实时生效并自动保存：
       · 默认显示      —— 今日 / 本周（默认本周：时间段为行、周一~周日为列的网格课表）
       · 卡片样式      —— 简约白（白底卡片）/ 玻璃（磨砂玻璃）
       · 卡片宽度      —— 260 ~ 900
       · 卡片高度      —— 拖动滑块，或勾选「跟随内容自动」（推荐，卡片高度刚好贴合课表，无空白边）
       · 透明度        —— 30% ~ 100%，调的是【整个卡片底】：
                          · 简约白：数值越低卡片底越透，越能看见卡片后面的壁纸；
                          · 玻 璃：数值越低磨砂越厚（越糊），越高越接近清玻璃。
       · 圆角          —— 0 ~ 40
       · 位置          —— 直接填 X / Y，或点「贴右下角」一键归位
    说明：高度默认「跟随内容」，程序会自动把卡片收成和课表一样高，
          上下没有多余空白；内容超高时会自动轻微缩小以保证一屏放得下。
          透明度低于 100% 时程序会把卡片位置的壁纸截图垫在卡片底下，
          所以「透出来」的永远是正确的那块壁纸（挡在卡片前面的窗口不会被透出来）。
          「玻璃」是在垫底壁纸之上再加毛玻璃模糊 + 斜向高光 + 高光描边，
          与「简约白」的区别一眼可见（简约白是白底透壁纸，玻璃是磨砂玻璃）。

    ★ 换电脑后玻璃依然有磨砂质感（2026-10-12 跨机修复）：
      玻璃的磨砂原本只靠浏览器的 backdrop-filter，而部分机器（无 GPU 合成的
      WebView2）会静默忽略它，玻璃就退化成「一层近乎全透的底」＝看起来没有任何玻璃效果。
      现在改成【双保险】：程序在壳层就把抓到的壁纸先做一次高斯近似模糊，
      作为玻璃卡片的背景图垫进去——不依赖显卡，任何机器上都有真实磨砂。
      若某次刷新把这张背景图弄丢了，程序会自己巡检并补刷（最多 3 轮），不用手动干预。

    ★ 换电脑后不会再出现「两个登录窗口」（2026-10-12 跨机修复）：
      之前只按「本机是否存过登录令牌」判断登录态。把整个文件夹拷到新电脑后，
      旧令牌还在但服务端早已失效，网页会显示账号密码登录表单，程序却以为已登录、
      把卡片也显示出来，于是「卡片上的登录表单」和「登录窗口的扫码页」同时出现。
      现在改为【双重判据】：既看令牌，也实际探测页面上有没有登录表单；
      只要页面在要求登录，一律按未登录处理——只弹一个登录窗口，桌面不出现空白卡片。

    ★ 切换「今日 / 本周」后卡片尺寸会同步贴合（2026-10-12 跨机修复）：
      卡片窗口是从壁纸层宿主窗口接管过来的，窗口框架记录的高度不一定等于真实高度，
      切换视图后可能出现「内容下移、底还是上一个视图的底」。现在每次显示与改高度都
      回读真实窗口矩形核对，偏差超过 2px 就地强制修正，并重推一次扁平化样式。

    ★ 退出后桌面不会残留白色底（2026-10-12 跨机修复）：
      卡片挂在桌面壁纸层（资源管理器窗口的子窗口）上，退出时若只靠常规关窗链路，
      子窗口可能没被真正销毁，桌面上会留一块卡片大小的白底。现在退出前会显式做
      「脱父 → 隐藏 → 销毁窗口」，并有 1.5 秒兜底强制结束进程，绝不留下白底。

    ★ 默认样式 = 玻璃（2026-10-13）：
      新装的默认样式就是「玻璃」，不用再进外观面板手动切。
      （2026-10-13 补：旧 config.json 里残留的 "standard" 也会一次性迁到玻璃，
        详见下文「卡片默认样式 = 玻璃，旧配置一次性迁移」。）

    ★ 「今日 / 本周」按钮与卡片是同一块玻璃（2026-10-13）：
      切换条原先只有半透明白底 + backdrop-filter；在没有 GPU 合成的机器上
      backdrop-filter 被忽略，它就退化成一块发白的实心胶囊，跟卡片的磨砂对不上。
      现在把**同一张**预模糊壁纸按切换条在卡片里的位置裁一刀贴上去，
      再叠一层更亮的白当高光——切换条看起来就是卡片上浮起的一小块玻璃。

    ★ 「今日 / 本周」按钮一定点得动（2026-10-13）：
      这两个按钮的点击由壳层的全局鼠标钩子「点击接力」送达。之前判定
      「该点上方有没有别的应用窗口」用的是**裸 Z 序扫描**，会被一些
      **命中测试透明的幽灵覆盖层**骗到（实测新机日志里连报
      `above:TopLevelWindowForOverflowXamlIsland`、`above:CEF-OSC-WIDGET`），
      用户看不见它们，却导致按钮点击被放行出去 → 点了没反应。
      现在两道保险：
        ① 判定改用系统的真实命中测试（WindowFromPhysicalPoint），
           它按逐像素 alpha 判定，天然跳过幽灵层；
        ② 切换条矩形内**无条件接管**点击（那是我们自己注入的控件，
           点它只可能是想切视图），从根上消除误挡。
      可用 `--test-guard` 一键自证（见第九节）。

    周视图还会自动高亮：
       · 正在上面的课 → 粉色高亮框
       · 今天下一节要上的课 → 黄色提示
       · 已经过去的课 → 变淡
       每分钟自动刷新一次，不用手动操作。

    ★ 卡片上直接切换「今日 / 本周」：
      卡片【底部居中】有一个【今日 | 本周】小切换条，鼠标点它即可换视图，
      它的样子会跟着卡片样式一起变（简约白=实白胶囊 / 玻璃=半透明磨砂）。
      整条都是热区：点在两个按钮之间或胶囊边缘也会切到离得最近的那个，
      不会出现「点了好几下没反应」。
      卡片底部专门留了一条空带放它，不会遮挡任何课程内容。
      （卡片在壁纸层收不到鼠标，点击由程序的全局鼠标钩子接管后
        在卡片内重放——这就是「点击接力」，全程无感，不需要任何设置。）

    ★ 换了深色壁纸也不用担心看不清（2026-10-12 新增）：
      程序每次垫壁纸时会顺带测量壁纸亮度；卡面偏暗时自动把课程名、时间、
      周网格、切换条整套翻成「深底浅字」，并给文字加轻微投影兜底；
      换回浅色壁纸（或调整透明度）后自动恢复深色文字。无需手动设置。

    ★ 卡片右上角没有刷新按钮：
      页面自带的 ⟳ 已从卡片上去掉（少一个图标更干净）。课表数据本身
      每分钟自动更新，不需要手动刷新；确实想立刻重拉一次的话，
      右键托盘图标 → 【刷新课表数据】即可，效果和原刷新按钮完全一样。

四、关于登录窗口
    · 底部有两个常驻大按钮：【扫码登录】和【关闭】，不依赖标题栏小按钮。
    · 按 Esc 键也能立即关闭窗口（无论焦点在窗口还是页面里）。
    · 扫码登录用的是知派课表官方小程序码，全程在本窗口内完成，不跳转微信网页。
    · 扫码成功后登录状态会自动同步给桌面卡片（本机持久保存，重启电脑依然有效）。
    · 二维码约 1 分钟自动刷新；若提示已失效，点页面上的刷新链接即可。
    · 标题栏按钮：扫码登录 / 外观（卡片设置）/ 关闭。
      已登录时托盘打开设置窗会直接进「外观」，不再显示和桌面卡片一样的页面。
    · 程序已屏蔽网页端「添加到桌面」的浏览器安装指引（那是给浏览器用的，
      桌面小组件本身就是独立应用，点了也找不到，已不再弹出）。

五、托盘菜单（右键粉色「知」图标）
    · 显示 / 隐藏卡片 —— 状态会被记住，下次启动保持一致
    · 打开登录 / 设置窗口 —— 可交互，用于扫码或账号密码登录
    · 调整卡片外观…  —— 打开「外观」设置（大小 / 透明度 / 圆角 / 位置 / 默认视图）
    · 刷新课表数据   —— 立即重新拉取课表（原卡片右上角 ⟳ 的功能挪到了这里）
    · 开机自启       —— 勾选后写入注册表 Run 键（HKEY_CURRENT_USER）
    · 退出（保留自启）/ 退出并清除自启
                       —— 清除自启会同时删掉 Run 键，删文件夹无残留

六、常见问题
    Q: 双击 exe 完全没反应？
    A: 两种常见情况：
       ① 在压缩包预览窗口里直接双击 → 先「全部解压缩」再运行（见第二节）；
       ② 已经有一个实例在后台运行（可能在托盘里）→ 右键右下角托盘图标即可。
          若托盘里也没有，说明上一个实例是崩溃残留，本版本会自动接管，直接重开即可。

    Q: 启动后看不到卡片？
    A: 未登录时本来就只有登录窗口；登录后若仍没有，右键托盘 →「显示卡片」。
       程序内置渲染自检，若壁纸层无法显示会自动降级为置顶小窗并在日志中记录。

    Q: 提示缺少 WebView2 运行时？
    A: 下载官方 Evergreen 运行时（免费，约 100MB）安装后重新打开：
       https://go.microsoft.com/fwlink/p/?LinkId=2124703
       Win10 1809+ / Win11 通常已自带，无需安装。

    Q: 想移动卡片位置？
    A: 卡片定位在桌面壁纸层，不支持鼠标拖动（壁纸层收不到鼠标是系统特性）。
       右键托盘 →「调整卡片外观…」→ 位置里填 X / Y，或直接点「贴右下角」。

    Q: 透明度拉低后和想象中的「窗口透明」不一样？
    A: 现在的透明度直接作用于【整个卡片底】：把卡片位置的壁纸截下来垫在底下，
       白底变成半透明 —— 视觉上就是整张卡片透出壁纸。
       「玻璃」样式还会把透出的壁纸做毛玻璃模糊并加高光，效果更接近磨砂玻璃。
       （本机 Windows 不支持真正的窗口级半透明——实测系统会静默拒绝分层样式，
         垫壁纸是等效替代方案，且不会把压在卡片前面的窗口透出来。）

    Q: 换了一台电脑，玻璃看不出效果 / 出现两个登录界面 / 退出后桌面留白底？
    A: 这三个问题都已在 2026-10-12 修复，请在本版本（同一天之后打包的）里确认。
       若仍复现，请把 logs\shell-*.log 发给开发者，日志里关键行如下，能直接定位：
         · 玻璃：`卡片皮肤：skin-applied|...|blurWall=1|wallEl=1`（blurWall=1 才算坐实）
         · 挂载：`挂载核对 mounted=True ... 策略=[...] LAYERED生效=True`
         · 尺寸：`高度核对：真实窗口 350x453 期望 350x453`（两数应一致）
         · 登录：`卡片已登录（logged|196|pane=0|form=0）`（pane/form 必须为 0）
         · 退出：`退出收尾：脱父 → 隐藏 → 销毁卡片窗口` 与 `=== 退出，ExitCode=0 ===`

    Q: 卡片底部的「今日 | 本周」点了没反应？
    A: 点击由程序的全局鼠标钩子接管后在卡片内重放，正常立即生效。
       切换条整条都是热区（点缝隙/边缘也会生效）；视图刚切换后约半秒内
       程序在重抓垫底壁纸，这期间点下的操作会自动等抓屏完成后再执行，不会丢。
       若确实无响应，请把 logs\shell-*.log 发给开发者——日志里会记录
       「点击接力·钩子存活 / 点击接力：屏幕(x,y)→页面(x,y) / 点击接力诊断」，
       能直接定位是钩子没收到点击、命中判定出了问题、还是被别的窗口挡了。

    Q: 卡片贴在屏幕边缘时，其他应用的弹窗 / 托盘菜单点不动？
    A: 已修复（2026-10-12）。程序只接管「真正落在卡片上」的点击；卡片矩形内
       若有别的窗口压着（消息弹窗 / 托盘右键菜单 / 通知），点击会原样放行
       给那个窗口，不会被吞掉。自检含回归验证：在卡片上临时压一个窗口，
       确认点击放行、撤除后接力恢复。

    Q: 卸载会留残留吗？
    A: 不会。先托盘「退出并清除自启」，再删除整个文件夹即可。
       所有数据（配置、缓存、日志）都在本文件夹内。

七、目录说明
    ZhiPaiWidget.exe        主程序
    Microsoft.Web.WebView2  WebView2 官方组件
    runtimes\               WebView2 原生加载器（自动匹配 64/32 位）
    config.json             首次运行后生成
    UserData\               WebView2 用户数据（登录态），首次运行后生成
    logs\                   运行日志与自检报告

八、自检模式（排查问题用）
    ZhiPaiWidget.exe --selftest --shot
        挂载 + WebView2 + 渲染像素探针，落盘 logs\selftest-*.txt 与截图后自动退出。
    ZhiPaiWidget.exe --selftest-input --shot
        用真实合成鼠标/键盘点击登录窗口，实证「用户名可输入、记住登录可勾选」。
    ZhiPaiWidget.exe --selftest-settings --shot
        打开设置小窗验证后自动退出。
    ZhiPaiWidget.exe --selftest-qr --shot
        验证微信扫码登录页、小程序码渲染、登录态跨窗口同步后自动退出。
    ZhiPaiWidget.exe --selftest-look --shot
       验证：未登录不出现空卡片 / 浏览器引导已抑制 / 周视图网格（时间段×星期）/
       时间段与课程居中 / 按时间段高亮（now·next·done）/ 卡片高度贴合内容（上下无空白）/
       外观可设置（宽度 / 透明度作用于卡片底 / 玻璃四要素：磨砂·壁纸图层·高光·近全透）/
       深色壁纸适配（卡面偏暗时文字自动翻浅色）/ 切换条不遮挡内容 /
       点击接力启用 / 卡片上切视图链路。

    ★ 玻璃兜底说明：自检报告里的「玻璃·预模糊壁纸」一项对应跨机修复的
      壳层预模糊兜底（不依赖 GPU）。这一项为 ✓ 时，即便「玻璃·磨砂」显示
      「backdrop-filter 未生效」，换到无显卡合成的机器上玻璃仍然有真实磨砂。

    通用型（不挑机器）验证 —— 换任何电脑都能一键自证：

    ZhiPaiWidget.exe --probe-screen
        屏幕环境探针：列出本机所有显示器、虚拟桌面范围、卡片归属屏，
        并跑一遍「显示器解析算法」的合成断言。单屏机上「跨屏实测」显示
        SKIP 属正常（本机没有副屏可实测），算法本身已被合成断言覆盖。

    ZhiPaiWidget.exe --test-brand
        品牌色统一回归测试：扫描卡片/切换条/周网格的全部注入 CSS，
        断言「统一知派橙 #F97316（与官网生产 CSS 一致）、旧品牌粉零残留、
        『已上完』红色删除线保留」。不依赖登录/卡片/网络，任何机器可跑；
        进程退出码 = 失败项数（0 = 全过）。报告落盘 logs\test-brand-*.txt。

    ZhiPaiWidget.exe --test-week
        周视图排版规则的回归测试（2026-10-13 方案三）：用离线夹具（3 天 / 2 个学校
        时段 / 3 门校外课）+ 固定颜色假数据，断言——
          ① 7 天各一个独立列，列内学校课与校外课混排、按时间从早到晚
             （逐项比对每项的起始分钟）；
          ② 最左侧时间轴只放学校课程的时段，校外时段不进时间轴；
          ③ 校外课的上课时间用小字排在课名下方（逐项比对 nameY/timeY）；
          ④ 课名前有上课人颜色圆点（颜色逐个核对）；
          ⑤ 取不到课程数据时自动退化：无法区分校内外 → 时间轴列出全部时段、
             无圆点无小字，布局不变、不丢课。
        报告落盘 logs\test-week-*.txt，进程退出码 = 失败项数。

    ZhiPaiWidget.exe --probe-week
        周视图端到端取证（真实登录数据）：dump 原始周视图 DOM、/courses 与
        /attendees 原始 JSON、网格化后的逐列结构（每项的起始分钟、圆点颜色、
        校外项的课名/时间坐标）、每列的项数与列高（证明周末列没有整列空格）、
        以及今日视图逐条课程的圆点颜色。报告落盘 logs\probe-week-*.txt。

    ZhiPaiWidget.exe --test-guard
        点击接力守卫回归测试：证明新实现（WindowFromPhysicalPoint，按逐像素
        alpha 命中测试）不会把「命中测试透明的幽灵覆盖层」当成别的应用窗口
        而吞掉点击 —— 正是「今日/本周按钮点不动」的根因。四项全过 = 退出码 0。

    ★ 登录/设置窗两种尺寸（2026-10-13）：
      · 扫码登录模式 → 420x600 紧凑窗口，页面留白/二维码同步收紧，正好填满不滚动；
      · 外观面板模式 → 470x840 完整窗口（滑块多，需要高一些）。
      切换页签时窗口自动换尺寸，位置锚定左上角不跳动。

    ★ 登录/设置窗两种尺寸（2026-10-13）：
      登录（扫码）模式 420x600 / 外观面板 470x840。
      2026-10-13 三次反馈后：页眉改**白色**（标题/按钮文字相应改深色，
      当前页签用「浅橙底 + 知派橙字」区分）；底部整条操作栏（提示文案 +
      扫码登录 + 关闭按钮）按用户要求**整行移除** —— 关闭 / 扫码入口统一在标题栏。

    ★ 周视图二次改版：8 列独立流式 —— 不再严格对齐时间轴（2026-10-13 第三条反馈）：
      用户反馈上一版「严格按照一个时间轴线」仍不紧凑：周六周日没有学校课时，
      上面 6 个时段行整列都是空的。本次改为（用户在三个方案中选定「方案三·按天独立」）：
      · 左侧时间轴**只保留学校课程**的时段标签（用户原话：最左侧的时间轴仅用于学校课程）；
      · 右侧 7 天各是一个**独立列**：列内把学校课与校外课混在一起、按时间从早到晚
        排下去，列与列之间不再强行对齐 —— 周末只有 3 节课就从顶部排 3 格，
        不再出现「整列空格」；
      · 校外课保持课名在上、上课时间小字在下（第二条要求，保留）；每节课名前的
        上课人颜色圆点也保留；
      · 工作日五天课表相同、且学校课格子 min-height 一致，所以它们自然对齐，
        左侧时间轴标签与工作日学校课在视觉上仍严格对齐；
      · 高亮逻辑同步适配：今天的课程按各自时间高亮（进行中 / 已结束 / 即将开始），
        过去的日子整列淡化；取不到数据时自动退化（不丢课、不劣化）。

    ★ 登录页「网页端/网页版」文案统一为「桌面卡」（2026-10-13 六条反馈）：
      上一版只把 .brand h1 用 CSS ::after 换成了「知派课表 · 桌面卡」，但页面上
      还有三处写着旧说法 —— 其中就包括用户指出的**小程序码下方那行
      「打开「知派课表」小程序即可登录网页端」**。现在统一改为文本节点替换：
      TreeWalker 遍历 + MutationObserver 常驻（Vue 扫码状态切换 / 会员 toast 插入
      重建节点后自动补改），覆盖全部四处：
        ① .brand h1   「知派课表 · 网页版」→「知派课表 · 桌面卡」
        ② .qr-tip-sub 「…即可登录网页端」→「…即可登录桌面卡」
        ③ 注册页提示「网页端与小程序课程互通」「网页端使用同一账号体系」→ 桌面卡
        ④ 会员 toast「登录成功，网页端为会员专属…」→ 桌面卡
      只注入在壳层 WebView2 里，浏览器直接访问 kebiao.lezhipai.cn 的网页版不受影响。
      --selftest-qr 的「扫码状态串」输出 h1 / qrs / qrp / webLeft / title 五项；
      webLeft=0 表示整页已无「网页」字样（实测 2026-10-13：webLeft=0 ✓）。

    ★ 账号登录文案 + 扫码后窗口残留（2026-10-13 第七次反馈，两条）：
      ① 用户反馈「使用账号登录里的文案还没改」：实测是页面内 MutationObserver 会漏掉
         「账号登录」表单里那行「网页端使用同一账号体系：用「用户名 + 密码」登录即可看到
         相同课程。」—— 这是 SPA 局部重渲染新增的文本节点，observer 不触发
         （手动再跑一次 fix 即可修，selftest 实证：手动调用返回 fix|1，observer 漏触发）。
         修复：在设置窗 1.5s 桥接节拍里每轮顺带把文案统一脚本再跑一遍（已初始化时走
         live 分支，幂等、开销可忽略），保证「使用账号登录」表单里的「网页端」也被统一成
         「桌面卡」。--selftest-qr 已新增「切到账号表单 → 全文案无『网页』」断言
         （实测 PASS，dump 显示「桌面卡使用同一账号体系…」）。
      ② 用户反馈「设置里打开扫码登录，换一个账号扫码，先变成课程卡片、又变成网页端界面」：
         根因 = 已登录态下切另一个账号扫码时，主卡片并非「由未登录转已登录」，不会走
         MainWindow 的 LoggedIn → DismissSettingsForLogin，登录窗就残留（短暂显示课程卡、
         随后若新账号未授权还会跳回 /login = 用户看到的「网页端界面」）。
         修复：设置窗桥接检测到「本次扫描刚成功」（BridgeScript 返回 bridged 且处于登录模式）
         即主动请求壳层收起登录窗（新增 LoginSucceeded 事件 → DismissSettingsForLogin），
         外观面板调样式时绝不误关（限定 !_lookMode）。同步把登录页文案统一脚本注入到
         主卡片 WebView2（之前只在设置窗注入），任何 WebView2 露出 /login 都 branded。

    ★ 卡片默认样式 = 玻璃，旧配置一次性迁移（2026-10-13 六条反馈）：
      用户反馈「首次登录默认是玻璃样式」。代码默认其实早已是 glass，但本机
      config.json 里留着旧版本程序自己写入的 "Skin":"standard"，新版本照读
      就还是白卡 —— 序列化器不跑构造函数，光看字段分不清「用户显式选了简约白」
      和「旧默认值就是 standard」。引入配置版本号 CfgVer（当前 2）：
        · 读到的版本 < 2 → 把 standard 抬成 glass（只发生一次），随后写回；
        · 用户之后在设置窗主动选简约白 → CfgVer 已是 2 → 永远尊重，不再迁移。
      实测（bin 目录 config.json，迁移前 Skin=standard）：启动一次后自动变为
      "Skin":"glass","CfgVer":2。--test-brand 新增 4 条迁移断言（新装即玻璃 /
      旧配置迁移 / 显式选择被尊重 / 本就玻璃保持不变）。

    ★ 卡片四周留白收口（2026-10-13）：
      用户反馈「卡片四周的边都太宽，整体收缩，比内容大一点就可以，距离参考
      今日/本周按钮底边和卡片底边之间的距离；左边还比右边宽，感觉没居中」。
      --probe-gap 实测（真实登录态）定位到三处：
        ① .widget-card 的 padding 是 上20/右22/下46/左22 —— 收到 8/8/46/8
           （底部 46px 是悬浮切换条的预留带，不动；8px 正是参照距离 barGap）；
        ② 网页端 .topbar 装饰细线（6px 高）自带 margin-bottom:14px —— 收到 6px；
        ③「左边比右边宽」的根因不是 padding 不对称（实测左右各 23px 对称），而是
           周网格左侧时间轴列宽 64px、只放约 27px 宽的「09:05」两行小字，左右各空
           17px，视觉上左侧多出一圈 —— 列宽收到 40px，内间隙降到约 6px，与右侧
           课程格内容到边缘的距离基本一致。
      收口后（同一份课表）：内容距顶 41→21px，距左/右各 9px（对称），卡片高度
      501→468px；切换条到底边 8px 不变。--probe-gap 可随时复查这组数字。

    ZhiPaiWidget.exe --probe-gap
      卡片四周留白取证（真实登录数据）：周视图/今日视图各量一遍「卡片→内容」的
      四条间隙（gapL/gapR/gapT/gapB）、切换条参照距离（barGap）、纵向滚动条占位
      （sbw，非 0 说明右侧被滚动条占了位）、卡片直接子元素画像（kids，解释顶部
      空白的构成）。报告落盘 logs\probe-gap-*.txt，并各存一张截图。

    ★ 课程名字体统一：校外课与学校课同一套字体（2026-10-13 第四条反馈）：
      之前校外课名自己带了一条 font-weight:600（显式加粗），学校课名是常规字重，
      两边看起来像两种字体。现在把「课程名」抽成一条共用规则：字族 / 字号（10px）/
      字重 / 行高（1.25）/ 字距 / 字形六项由同一条 CSS 控制，不再靠继承碰运气——
      校外课名去掉加粗，向学校课名看齐。两个刻意保留的差异（不属于字体不一致）：
      · 「正在上的课」格子仍整体加粗（700）—— 那是时间状态高亮，2026-10-12 定下的；
      · 校外课名下方的上课时间仍是 9px 小字 —— 那是说明文字，不是课程名。
      --test-week 的 ①-10~①-13 用计算样式逐项比对两类课名的字体签名
      （并验证高亮加粗没有被统一规则吃掉）；--probe-week 会在真实数据上输出
      「课程名字体」一行（itemWh/outWh/hotN）。
      同一份课表实测：7 列 / 6 个学校时段 / 42 项（12 门校外）；卡片 499px（上一版 503px）；
      周末列高仅 131~143px（工作日列 247~377px），列内零空档。
      回归测试见 --test-week，端到端取证见 --probe-week。

    ★ 「正在上的课」高亮增强：两种卡面样式都一眼可见（2026-10-13 第五条反馈）：
      旧高亮只是把格子底从浅橙换成略深的浅橙 + 深橙字，在白卡 / 玻璃卡上
      和普通格子几乎糊在一起。现在：
      · 周视图：「正在上课」的格子改成知派橙实底渐变 + 白字 + 加粗 + 外圈光晕，
        整张卡片上最抢眼的一格（皮肤层与网格自带兜底双层同色，样式开关都一致）；
      · 今日视图：正在上的课程条加浅橙渐变底 + 左侧 4px 橙条 + 橙色细描边，
        课名加粗变深橙；「上课中」徽标同步换成橙底白字；
      · 深色壁纸（深色卡面）另配一套：更亮的橙底 + 深字，对比度同样拉满。
      --test-week 第③阶段用计算样式在两种卡面下逐项断言（21 项断言）；
      --probe-order 会在真实数据上取高亮样式与截图（当前没有课正在上时用
      模拟标记取证，跑完立即撤掉）。

    ★ 卡片启动默认停在桌面右下角（2026-10-13 第五条反馈）：
      之前「默认右下角」只在启动时按默认高度 720 算一次，而真实内容高度只有
      430 左右 —— 贴合把高度收掉后，卡片就悬在屏幕中段了。现在：
      · config.json 新增 SnapEdge（默认 "bottom-right"）：每次高度贴合后都按
        **当前内容高度**把卡片右下角锚定到工作区右下角（留白 = ScreenMargin）；
      · 改外观（宽 / 高 / 样式）后同样保持右下角；
      · 想自由摆放：把 config.json 里的 SnapEdge 改成 "free"。
      --probe-order 输出「窗口矩形 / 工作区 / 右间距 / 下间距」，本次实测
      今日与本周两种视图下右间距、下间距都精确等于 32.0。

    ★ 卡片层级修正：壁纸之上、桌面图标之下（2026-10-13 第五条反馈）：
      现象是桌面快捷方式被卡片盖住。取证（--probe-order）抓到两个元凶：
      · ShowWindow 系列会把窗口带到**同层 Z 序最顶** —— 卡片挂在 Progman 下，
        「同层最顶」就等于压在桌面图标层 SHELLDLL_DefView 上面。渲染探针每次
        SW_HIDE → SW_SHOWNA 都会把卡片顶上去一次。已改为 SetWindowPos +
        SWP_SHOWWINDOW + SWP_NOZORDER（只开可见性，Z 序不动）；
      · explorer 桌面刷新 / 显示设置变更也会重排 Progman 子窗口 Z 序。
      现在挂载后有「Z 序守护」每 4 秒核对一次，发现越层立即压回图标层之下，
      并写 shell 日志；--probe-order 会 dump 子窗口完整 Z 序链并给出判定。
      本次实测：图标层 z0 → 卡片 z1 → 壁纸层 z2 ✓（修正前卡片在 z0）。

    ZhiPaiWidget.exe --probe-order
      桌面层级 + 卡片定位 + 「正在上课」高亮的联合取证：① Progman 子窗口
      Z 序链（顶 → 底）与「卡片是否在图标层之下」判定、守护纠正前后对照；
      ② 今日 / 本周两视图的窗口矩形 / 工作区 / 右下角间距；③ 两种视图的
      高亮计算样式（与普通课对照）+ 截图。报告落盘 logs\probe-order-*.txt。

    ★ 周视图改版（已由上面「8 列独立流式」取代，保留历史记录）：校外课程收进底部一行
      + 课名前有上课人颜色圆点（2026-10-13）：
      · 「学校以外的课程」（兴趣班 / 机构课等，数据上不属于学校班级）不再和
        学校的上课时间段逐行对齐 —— 那样每门课都要占一整行（一行只有一个格子
        有内容），课一多卡片就被撑到两千像素高。现在它们统一收进网格最下面的
        「校外」一行：每个格子是当天的校外课，按时间从早到晚自上而下排列，
        课名在上、上课时间用小字排在课名下方。同一份课表实测：
        改动前 16 个时间段行 / 卡片 714px；改动后 6 个学校行 + 1 行校外 / 卡片 503px。
      · 每节课的课程名前都有一个小圆点，颜色 = 这位上课人在「上课人（成员）」
        里设置的颜色（网页端同一套配色，未设置时回退课程自带颜色）——
        一眼就能看出这节课是谁的。今日视图的课程名前同样有这个圆点。
      · 取不到课程数据（离线 / 登录失效）时自动退回旧排版，不会白屏或丢课。
      · 页面重渲染（切视图 / 刷新）冲掉网格后，程序每 1.2 秒巡检会自动重建，
        并按新内容高度重新贴合窗口；回归测试见 --test-week。

    ★ 「登录后是一张白色空白卡片」的根因与修复（2026-10-13 三次反馈）：
      卡片未登录被收起期间，WebView2 的页面视口会停在旧尺寸（窗口藏着时 WPF
      不再排布它）；登录成功重新显示后，页面就按错误的视口排版 ——
      现场日志：「卡片 1076x714 而窗口只有 420 DIP」，渲染探针连续 5 次差异率
      0.00% → 卡片一片空白，直到用户点一下设置（窗口重排）才自愈。
      修复：① 显示卡片后**主动**做一次「卡片重同步」（重新布局 + 视口刷新 +
            页面重排 + 高度重贴），不再依赖外部事件把它"撞"正常；
            ② 渲染探针判定「未渲染」时先取 DOM 证据分流：页面有内容但屏幕空白
            → 唤醒重同步；内容为空 → 重新加载；都救不回来才降级置顶（比原逻辑保守）。
      回归测试见 --test-reveal（CASE-2 完全复刻扫码登录路径）。

    ★ 「今日 / 本周」按钮悬到卡片外面的根因与修复（2026-10-13 四次反馈）：
      现象：周/今日视图里，底部居中的切换条整条掉到卡片外面（悬在桌面/任务栏上）。
      根因有两层，probe-gap 真实登录态取证钉死：
        ① 切换条是 position:fixed;bottom:8px —— 锚的是**窗口**底边，不是卡片底边；
        ② 窗口贴合偶发不落地（SetWindowPos 被壁纸层吞掉），旧巡检只认
           「内容高度变化」→ 贴合失败后内容高度不再变，窗口 400 vs 卡片 384
           永久卡死，多出来的那段透明区正好把切换条托到卡外（barGap=-8）。
      修复：
        ① 页面侧 place()：切换条 bottom 动态算成「卡片底边上方 8px」，跟随卡片走
           （resize + 1.5s 轻轮询双触发）——即使贴合滞后，条也永远贴着卡片；
        ② 壳层自愈：1.5s 巡检同时上报窗口视口高 vh，发现「窗口比卡片高 2px 以上」
           就持续重试贴合直到落地（只修「窗口偏高」方向，内容内滚兜底不受影响）。
      实证：修复前 barGap=-8；修复后 barGap=8、overlapContent=0，且日志可见
        贴合未落地 → 重新贴合 → 窗口 420x384 收敛（2 秒内自愈）。
      验证用 --probe-gap，看「切换条」一行的 barGap（应 ≥0）与 vh（应≈卡片高）。

    ★ 已上完课程的样式（2026-10-13 三次反馈）：
      今日视图里已结束的课程 = **深灰文字**（浅色卡面 #4b5563 / 深色卡面 #9ca3af）
      + **红色删除线**（浅色 #d6336c / 深色亮红 #ff7fa5）。
      文字色与划线色是两个独立属性；验证用 --probe-today，报告里 tx=文字色、col=划线色。

    ★ 登录成功后的窗口顺序（2026-10-13）：
      先把登录窗从屏幕上抹掉（Hide 瞬时）→ 再显示桌面卡片。
      顺序反了的话，登录窗里的 /login 会跳到 /widget（长得和卡片一模一样），
      两个窗口同屏几百毫秒 = 「刚登录进去闪现两个窗口」，已修复。

    ★ 「未登录却冒出两个窗口」的根因与修复（2026-10-13 二次反馈）：
      卡片未登录时会渲染「账号密码」登录表单，本应被收起；但窗口摆放函数
      MoveAndSize() 恒定带 SWP_SHOWWINDOW —— 登录前的高度自适应、外观面板改宽高
      只要调一次尺寸，就会把**已经收起的卡片强行显示回来**，而"已收起"标记仍是 true，
      后续收起逻辑命中幂等分支直接返回 → 卡片就长期留在桌面上，
      和登录窗的扫码页同屏 = 「两个登录窗口」。
      修复：① MoveAndSize 只在「卡片该可见」时才带 SWP_SHOWWINDOW；
            ② 收起/显示改为 WPF 层（Visibility）+ Win32 层同时做，任一层不再留不一致状态；
            ③ 收起逻辑改为复核「窗口是否真的不可见」，不再只信标记位。
      回归测试见 --test-hide（用旧代码跑 ②③ 会 FAIL，新代码 5/5 PASS）。

    ★ 品牌色统一（2026-10-13）：
      卡片配色 / 切换条选中态 / 徽标 / 上课中格子 / 设置窗页眉与主按钮
      全部统一为知派橙 #F97316（色值取自官网 kebiao.lezhipai.cn 生产 CSS）。
      「已上完的课」的删除线保持红色 —— 那是状态语义色，不随品牌统一。

    ZhiPaiWidget.exe --test-screen
        合成多屏回归测试：不需要真实副屏、不需要登录、不需要显卡。
        用 5 种合成布局（双屏横排 / 副屏在左含负坐标 / 主屏+上方竖屏 /
        三屏混合 / 分辨率不一致）断言「点归属屏、卡片归属屏、跨屏取重叠大者、
        并集覆盖」共 25 条。全部通过 = 通用型成立；进程退出码 0 即全通过。
        报告落盘 logs\test-screen-*.txt。

    ZhiPaiWidget.exe --test-guard
        点击接力守卫回归测试（不需要登录）。四项：
          ① 无遮挡            → 守卫应判 desktop（接力接管）
          ② 可见小窗压上      → 守卫结论须与系统命中测试一致
          ③ 全透明分层幽灵层  → 守卫必须忽略它（**这条复现「按钮点不动」的老 bug**：
             老实现会判 `above:Static`＝拦点击，新实现判 desktop＝放行给接力）
          ④ 撤除后            → 守卫恢复 desktop
        进程退出码 = 失败项数（0 即全通过）。报告落盘 logs\test-guard-*.txt。

    ZhiPaiWidget.exe --test-hide
        未登录「收起」态稳定性回归测试（不需要登录），五项：
          ① 未登录收起后      → 应不可见
          ② 调尺寸/挪位置后   → 应**仍然**不可见（老代码在这里把卡片复活 → 两个窗口）
          ③ 改 WPF 高度后     → 应仍然不可见（高度自适应同款路径）
          ④ 再次收起（幂等）  → 应仍然不可见
          ⑤ 恢复显示后        → 应可见（反向验证没把显示能力弄坏）
        进程退出码 = 失败项数（0 即全通过）。报告落盘 logs\test-hide-*.txt。
        若哪天又冒出「两个登录窗口」，先跑这条，②③ 会直接告诉你是哪条路径把卡片复活了。

    ZhiPaiWidget.exe --test-reveal
        卡片「显示回归」测试（需要已登录），两条用例各跑一遍「隐藏 → 显示」：
          CASE-1 只隐藏/显示（基础路径）
          CASE-2 隐藏**期间**重载页面再显示 —— 完全复刻「扫码登录」那条真机路径
                 （2026-10-13 反馈「登录后是一张白色空白卡片」的复现用例）
        每条用例给出 BEFORE（旧行为：只显窗）与 AFTER（新行为：显窗 + 卡片重同步）的
        「页面视口宽 vs 窗口 DIP 宽」「像素差异率（是否真的被合成到屏幕）」。
        断言 AFTER：① 视口 == 窗口宽  ② 差异率 ≥ 5%（真渲染）  ③ 页面有内容。
        「登录后卡片空白」类问题先跑这条，BEFORE 的视口数字会直接暴露病灶。
        报告落盘 logs\test-reveal-*.txt。

    ZhiPaiWidget.exe --test-glass
        玻璃样式切换回归（2026-10-13「玻璃切视图时底下模糊杂乱图案」的修复验证）：
          ① 卡面/壁纸层用 cover 保持比例（不会再把壁纸切片拉变形）；
          ② 尺寸契约：抓到的壁纸与窗口矩形不符（±2% 外）必须判为不可用并重抓；
          ③ 抓取前会等窗口矩形稳定（连续两次采样一致），不在尺寸过渡中途抓屏。
        报告落盘 logs\test-glass-*.txt。

    ZhiPaiWidget.exe --probe-look [--shot]
        外观面板尺寸取证：打开设置窗并切到「外观」面板，实测输出窗口 DIP 尺寸 /
        物理像素尺寸（乘 DPI）/ 面板内容实测高度 / 下方空白高度。
        断言：下方空白 ≤ 24 DIP（面板高度按内容贴合，不再有固定 840 留下的大片空白）。

    ZhiPaiWidget.exe --quit
        优雅退出（部署/更新用）：通知正在运行的实例走正常退出链路
        （收卡片 → 摘托盘图标 → 退出）。★ 请用它替代 taskkill /F ——
        强杀进程时托盘图标来不及摘除，会留下一个点了没反应的「幽灵图标」，
        用户看到的就是「右下角程序图标点不动」。

    ★ 已上完课程的可见性（2026-10-13 两次反馈的最终修复）：
      页面自带 `.lesson-row.ended{opacity:.5}`（周视图 .chip.ended 是 .45）——
      整行对半淡化才是「看不清」的元凶。皮肤里已强制 opacity:1，
      配色 = 深灰文字 + 红删除线（浅色卡面文字还带白色微光晕，跨明暗分界也 readable）。
      同时皮肤 <style> 带 MutationObserver 保持器：页面重渲染清掉样式会自动补回，
      登录态轮询每周期也检查 skin=0 兜底重注。

    ★ 卡面明暗判定带迟滞（死区 ±0.06）：高对比壁纸下亮度不会在阈值附近来回横跳，
      配色不再频繁翻转。
        「设置界面太大 / 底部空白」类问题跑这条。报告落盘 logs\probe-look-*.txt。

    ZhiPaiWidget.exe --probe-windows
        顶层窗口取证：列出屏幕上所有可见顶层窗口（类名 / 标题 / 进程 PID / 尺寸 / 位置，
        本进程窗口标 ★），并单独列出本进程的全部顶层窗口（含隐藏）。
        症状「桌面上莫名多了一个窗口」时跑一次，报告直接回答：
        那个多出来的窗口是「本组件的设置窗 / 卡片」，还是「外部程序（浏览器等）」。
        报告落盘 logs\probe-windows-*.txt。

    ZhiPaiWidget.exe --probe-click
        点击命中医断：在「今日按钮中心 / 本周按钮中心 / 卡片中心」逐点打印
           · wfpPhys = 系统真实命中（谁会收到这次点击）
           · zscan   = 老实现（裸 Z 序扫描，会被幽灵层骗）
           · verdict = 最终判定（hit 才会被接力接管）
        「按钮点不动」类问题，把这个报告发回来即可定位。报告落盘 logs\probe-click-*.txt。

    ★ 为什么要有这几条：小组件是通用软件，不是为某一台机器定制的。
      代码里不允许出现「写死主屏 / 写死分辨率 / 写死路径」这类单机假设 ——
      所有屏幕判定都走 ScreenUtil（按窗口/点归属屏解析），因此
      双屏、三屏、副屏在左（负坐标）、竖屏、125%/150%/200% 缩放、
      跨屏全屏 都能正确工作。

版本 1.0.0    知派课表 · 上海知派数字科技有限公司
