Skip to content

迷你播放器歌词卷帘修两处抖:收尾跳 12px、拖窗缘/换目标弹簧打架 - #741

Merged
ShellMonster merged 2 commits into
mainfrom
work/mini-jitter-fix
Nov 3, 2026
Merged

ShellMonster merged 2 commits into
mainfrom
work/mini-jitter-fix

Conversation

@ShellMonster

@ShellMonster ShellMonster commented Nov 3, 2026 •

Copy link
Copy Markdown
Owner

User description

修什么

迷你播放器歌词卷帘(mini curtain)的两个抖动,展开回弹(ζ = 0.55 欠阻尼,票 #307 验收项)一个字没动。

Bug 1:收起最后一帧一下砍 12px

miniCurtainSyncHeight 原来只要 h > 0 就整段加 miniCurtainGap(10px)的缝。收起收尾时板只剩 ~1.8px,那一帧「1.8px 的板 + 10px 的缝」一起砍掉,窗高从 ~106 直接落回 84 —— 肉眼可见的一跳。

修法:新 miniCurtainWinHeight,缝按板高渐出(gap = min(板高取整, 10px))。收尾那一段每帧只掉 ~5px,全程单调递减。

Bug 2(a):动画期间用户拖窗缘,弹簧把窗顶回去

收起/展开的 60Hz 动画里 miniCurtainSetHeight 每帧 w.SetSize;迷你窗故意是可调的(记位置用),用户拖窗缘时弹簧和自己抢窗口 ⇒ 抖。

框架(mygo v0.7.3)没有暴露 live-resize 查询(inLiveResize 在 internal/darwin/surface.go 内部)。不改框架的修法:w.OnResize + 「报上来的高 ≠ 我自己 setHeight 写下去的高」认出外来 resize → 记 resizeForeign,150ms 静默窗内 miniCurtainSyncHeight 不发 SetSize,并把弹簧钉在用户拖到的高度(spring.holdAt)——松手后从那一格接着往 goal 走,不是「跳回半路重新展开」。

Bug 2(b):跨屏拖动/改分辨率时 retarget 让画高跳一截

miniCurtainRetarget(OnMove 触发)重写 target:全开高 400 → 250 时,h = ratio × newFull 在 0.9 处一帧跳掉 ~135px。

修法:新 spring.shift(oldFull, newFull) —— 换全开高时 pos / v 按 oldFull/newFull 缩放,p 钉到缩放后的 pos(不能缩 p:展开过冲的 p>1 经镜像会翻成更矮)。画出来的高度连着:放大完全连续;缩小时封顶 1(画高永远 ≤ 目标是 #307 镜像守的底线,drawn > newFull 那一段落差避不开,但比旧版少掉 ratio 那一截)。

验证

  • TDD:先红后绿,新用例 4 条——
    • TestTheMiniCurtainCollapseTailNeverDropsTwelvePixelsInOneFrame:尾段(窗高 < 104)每帧 ≤ 5px,全程单调不回升;
    • TestTheMiniCurtainRetargetKeepsTheDrawnHeightContinuous:放大连续、缩小落在 newFull;
    • TestTheMiniCurtainRetargetShiftAlsoCoversTheMirroredSpring:展开过冲那一支也覆盖;
    • TestTheMiniCurtainSetSizeIsSuppressedWhileTheUserResizes:拖窗期间不发 SetSize、静默窗过后接着走。
  • 验收线全绿:TestTheMiniCurtainUnrollsWithASpring(队列抽屉「正在播放」改淡主色底 #307 欠阻尼回弹)、RollsUpWithoutBouncing、ReverseNeverGrows 等全部 minicurtain/mini 用例 PASS(go test ./internal/ui/ -run 'MiniCurtain|Mini')。
  • 默认验收:go build -trimpath / go vet -trimpath 干净,非 ui 包测试全过。
  • E2E:internal/e2e/minicurtain_test.go 的收起段换 pumpCollapseIsMonotone —— 真窗口上边逼帧边断「单调递减 + 尾段单帧 ≤ 6px」(BIU_RUN_MYGO_E2E=1 才能跑,本机没跑真窗,无头一族已覆盖同一条判据)。
  • gofmt 改动文件干净。

已知环境噪音(与本次改动无关,基线上同样失败)

  • TestTheCoverStaysSquareAtTheMinimumWindowHeight、TestTheDateRangeOpensItsCalendarAtTheMinimumWindowWidth:基线 stash 后同样 FAIL(前者是封面夹具 flake,后者是日历跨月——11 月 3 日跑时「上个月」的断言落不到 10 月)。
  • 全量 go test ./internal/ui/ 在本机超时于 TestEveryPageFollowsTheDisplayStyleSetting(imgcache 拉真网图 404 的环境问题,单独跑该用例在改前改后都过)。

CodeAnt-AI Description

Stop mini-player lyric curtain jumps during closing and resizing

What Changed

  • The gap between the player and lyrics shrinks with the lyric panel, preventing a sudden height drop as the curtain closes.
  • Resizing the mini-player during a curtain animation no longer makes the window fight the user's drag; the animation resumes from the chosen height after resizing stops.
  • When the available curtain height changes, such as after moving the window to another display, its current height stays continuous where the new limit allows.

Impact

✅ Smoother lyric curtain closing
✅ No window-edge resize tug-of-war
✅ Fewer curtain jumps when changing displays

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

- Bug1:miniCurtainSyncHeight 原来 h>0 就整段加 10px 的缝,收起最后
  一帧是「1.8px 的板 + 10px 的缝」一起砍掉。改成 miniCurtainWinHeight
  ——缝按板高渐出(≤ 10px),尾段每帧 ≤ 5px。
- Bug2a:动画期间每帧 SetSize,用户拖窗缘时弹簧把窗顶回去 ⇒ 抖。框架
  没有 live-resize 查询,用 OnResize + 「报上来的高 ≠ 我自己 setHeight
  的高」认出外来 resize,150ms 静默窗内不 SetSize,并把弹簧钉在用户
  拖到的高度(holdAt),松手后从那一格接着走。
- Bug2b:miniCurtainRetarget 在 OnMove/换屏时重写 target,比例不动 ⇒
  h = ratio × newFull 一帧跳掉上百像素。加 spring.shift:换全开高时
  p/pos/v 按 oldFull/newFull 缩放,画出来的高度连着(封顶 1 是 #307
  镜像守的底线,缩小时的落差避不开但比旧版少 ratio 那一截)。

🔴 展开回弹(ζ = 0.55 欠阻尼,票 #307 验收项)一个字没动:参数、
镜像、起步规则原样,TestTheMiniCurtainUnrollsWithASpring 照绿。
@codeant-ai

codeant-ai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

CodeAnt  CodeAnt Review Status

✅  Review completed

Status Commit Started (UTC) Finished (UTC)
✅  #506cb Reviewed your PR 77de150 Nov 03, 2026 · 02:38 02:49

@codeant-ai

codeant-ai Bot commented Nov 3, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai Bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Nov 3, 2026
@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

PudMusic UI 截图

截图测试已通过。

截图来自项目现有的 MyGo UI 测试渲染器,使用本地 fixture,不启动真实登录态。

if board < 0 {
board = 0
}
s.miniCurtain.spring.holdAt(board / float64(full))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: miniCurtainSyncHeight still advances the spring during resizeForeign, so the content drifts while the window stays user-sized and jumps when the 150 ms guard expires.

Assessment: 🟠 Major · 🔁 Occurrence: Rarely · 🏷️ Logic error

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** internal/ui/minidisplays.go
**Line:** 547:547
**Comment:**
	*Logic Error: `miniCurtainSyncHeight` still advances the spring during `resizeForeign`, so the content drifts while the window stays user-sized and jumps when the 150 ms guard expires.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

board = 0
}
s.miniCurtain.spring.holdAt(board / float64(full))
s.miniCurtain.height = float32(board)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: If the user drags beyond the available full height, the spring clamps its ratio to 1 but this keeps the larger board; the next frame draws a shorter curtain while the guarded window stays tall, leaving clickable transparent space.

Assessment: 🟠 Major · 🔁 Occurrence: Rarely · 🏷️ Logic error

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** internal/ui/minidisplays.go
**Line:** 548:548
**Comment:**
	*Logic Error: If the user drags beyond the available full height, the spring clamps its ratio to 1 but this keeps the larger board; the next frame draws a shorter curtain while the guarded window stays tall, leaving clickable transparent space.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +1063 to +1068
if s.miniCurtain.resizeForeign.IsZero() {
want := miniCurtainWinHeight(h)
if s.miniCurtain.setHeight != want {
s.miniCurtain.setHeight = want
s.miniCurtainSetHeight(want)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: resizeForeign suppresses only SetSize; spring.step still advances the height during the quiet window, so clearing the flag writes a newer height and jumps away from the user's size.

Assessment: 🟠 Major · 🔁 Occurrence: Rarely · 🏷️ Logic error

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** internal/ui/minicurtain.go
**Line:** 1063:1068
**Comment:**
	*Logic Error: `resizeForeign` suppresses only `SetSize`; `spring.step` still advances the height during the quiet window, so clearing the flag writes a newer height and jumps away from the user's size.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

@codeant-ai

codeant-ai Bot commented Nov 3, 2026

Copy link
Copy Markdown

CodeAnt  CodeAnt Nitpicks

3 code suggestions

1. This check treats each poll as one frame, but its two RPCs can span multiple rendered frames; a valid smooth tail can exceed six pixels between samples and fail.

Logic error · internal/e2e/minicurtain_test.go:171-172


2. miniOnWindowResized still subtracts a fixed 10px gap; below 10px this conflicts with the shrinking-gap formula, so releasing a resize changes the chosen height.

Api mismatch · internal/ui/minicurtain.go:887


3. During mirrored overshoot, pos reverses the visible position but v keeps its sign; the next frame can move the curtain opposite its prior motion.

Logic error · internal/ui/minicurtain_spring.go:243

@codeant-ai

codeant-ai Bot commented Nov 3, 2026

Copy link
Copy Markdown

CodeAnt  CodeAnt PR Risk

🟡  Medium Risk

  • The PR needs attention before merging because the curtain spring keeps advancing while window resizing suppresses size updates.
  • Dragging beyond the available curtain height can leave the window taller than its rendered content, creating transparent clickable space.

Assessed commit: 77de15062a18

- P1(反解常数缝):miniOnWindowResized 原来按「缝 = 10」反解板高,
  缝渐出之后用户把窗拖到 84~94(板 <10px)那一档时反解出 0/负,
  松手 holdAt(0) 把窗拽回 84。新增 miniCurtainBoardHeight 做
  miniCurtainWinHeight 的反函数:r = h − 84 ≥ 2×gap 时 b = r − gap
  (缝满),更矮时 b = round(r/2)(缝跟着板缩,r = b + round(b))。
- P1(线程归属):去 mygo v0.7.3 源码确认过——windowDidResize: 是
  AppKit delegate 回调,在主线程上发;w.h.Resized() →
  fire(&h.w.onResize) 一路同步到我们的闭包(darwin/window.go:2088 →
  window.go:1859),和 viewMini 同一条线程,不需要 Window.do 序列化。
  在 miniOnWindowResized 注释里记下行号。
- P2(布局缝 vs 窗缝):viewMiniCurtainArrow 的盒子原来按常数
  miniCurtainGap 排,窗高按「min(gap, 板)」渐出——收尾那几帧内容比
  窗高,板尾被裁。改成盒子高吃同一个 min(gap, round(板)),缝容不下
  6px 三角时三角不画。

新用例 TestTheMiniCurtainDragToAShortBoardDoesNotSnapBack:往返断
miniCurtainWinHeight ↔ miniCurtainBoardHeight 同一数,再断拖到 90
那一格弹簧钉在「板 ≈ 3 / 全开」而不是 0。
@ShellMonster
ShellMonster merged commit 651b865 into main Nov 3, 2026
2 of 3 checks passed
@ShellMonster
ShellMonster deleted the work/mini-jitter-fix branch November 3, 2026 03:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL This PR changes 500-999 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant