Skip to content

fix(panels): keep a scaled panel's layout box at its natural height - #118

Merged
aqua5230 merged 1 commit into
mainfrom
fix/win-panel-zoom-clipping
Aug 30, 2026
Merged

fix(panels): keep a scaled panel's layout box at its natural height#118
aqua5230 merged 1 commit into
mainfrom
fix/win-panel-zoom-clipping

Conversation

@aqua5230

Copy link
Copy Markdown
Owner

問題

面板高於可用螢幕時會縮放以塞進畫面,但 Windows 上仍然缺底部內容:每張卡片被裁掉的量都不一樣,footer 下方還多出一條空白。兩個獨立的缺陷疊在一起。

缺陷一:zoom 呼叫在 UI 執行緒上自鎖

_apply_panel_zoom 原本在 _place_window_on_ui_thread 裡執行,等於在 WinForms UI 執行緒上呼叫 window.evaluate_js。pywebview 會同步等待 WebView2 的 ExecuteScriptAsync 完成,而該完成事件又被派回同一條執行緒——呼叫永遠不返回。

活體追蹤:

修正前:_apply_panel_zoom ENTER InvokeRequired=False → 沒有對應的 EXIT
修正後:_apply_panel_zoom ENTER InvokeRequired=True  → 3ms EXIT returned=True

現在改為在呼叫端(背景執行緒)解析並套用縮放,只把 resize/move 派給 UI 執行緒;文件端沒確認縮放成功就不縮小視窗。

缺陷二:Chromium 的 CSS zoom 不放大版面空間

zoom 只縮小繪製結果,版面框仍維持視窗高度,內容因此還是照未縮放的尺寸排版。.wrap 的子層是 flex: 0 1 auto.card / .footeroverflow: hidden,於是全部被等比壓縮、各自裁掉自己的內容;縮小後的繪製結果又比視窗矮 89px。

WebView2 實測(scale 0.912848,自然高度 1113 塞進 1016):

卡片 修前版面高 內容需要 被裁
claude 141 182 41
codex 168 182 14
agy 168 182 14
grok 92 116 24
projects 159 208 49
footer 131 158 27

WebKit 在 zoom 下會連版面框一起放大,所以 macOS 不受影響。

修法

usageApplyPanelZoom 改為同時接收自然高度,先把 body 撐開到該高度再套用 zoom,讓 flex 子層依照真正需要的空間排版。兩個殼層各自傳入它已知的高度——Windows 用 panel_height(),macOS 用 resolve_panel_size()——不使用 window.innerHeight / scale,那在 WebKit 上會雙重補償。macOS 端快取改成 (scale, height),確保高度變動但比例相同時仍會重新套用。naturalContentHeight() 量測時會連同 zoom 一起解除補償,避免量測值逐次放大。

驗證

WebView2 重新量測:body 高度等於視窗(底部留白 0),每張卡片的版面高度都覆蓋其內容——claude 184/182、grok 118/116、projects 209/208、footer 159/158,裁切卡片 0 張

  • 全套 1554 passed
  • ruff / mypy / check_file_size.py 全綠
  • 抽掉三個原始碼修正 → 3 個測試轉紅;裝回 → 全綠

macOS 專屬檢查(check_panel_parity.pytests/test_menubar.py)需 PyObjC,在 Windows 無法執行,交由 CI 驗證。

🤖 Generated with Claude Code

https://claude.ai/code/session_01Es6czYMZ99nN8ma4QxZ98V

A panel taller than the usable screen was scaled to fit by
usageApplyPanelZoom, but on Windows it still lost its bottom rows: every
card was clipped by a different amount and a blank strip sat under the
footer. Two separate faults stacked up.

_apply_panel_zoom ran inside _place_window_on_ui_thread, so
window.evaluate_js was called from the WinForms UI thread. pywebview waits
synchronously for WebView2's ExecuteScriptAsync completion, which is
dispatched back to that same thread -- the call never returned. A live
trace showed _apply_panel_zoom ENTER with InvokeRequired=False and no
matching EXIT. The scale is now resolved and applied from the calling
(background) thread, and only the resize/move are dispatched to the UI
thread; the window is not shrunk unless the document confirms it scaled.

The second fault outlived the first. Chromium's CSS zoom scales paint
output but leaves the layout box at the viewport height, so the content
was still laid out against the unscaled window: .wrap children are
flex: 0 1 auto over overflow: hidden cards, so they shrank proportionally
and each clipped its own content, while the scaled paint left the body
89px short of the window. Measured on WebView2 at scale 0.912848, natural
height 1113 into a 1016 viewport, every card was clipped -- claude by 41,
projects by 49, footer by 27. WebKit expands the layout box under zoom,
which is why macOS was unaffected.

usageApplyPanelZoom now takes the natural height alongside the scale and
gives the body that height before zooming, so flex children lay out
against the space they actually need. Both shells pass the height they
already know -- panel_height() on Windows, resolve_panel_size() on macOS,
never window.innerHeight / scale, which would double-compensate on WebKit.
macOS caches (scale, height) so a height change at an unchanged scale is
still applied. naturalContentHeight() releases the compensation alongside
zoom while measuring, so the value cannot ratchet upward.

Re-measured on WebView2: body height equals the window, and every card's
layout height covers its content -- claude 184/182, grok 118/116,
projects 209/208, footer 159/158.

Full suite 1554 passed; ruff, mypy, and check_file_size.py clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Es6czYMZ99nN8ma4QxZ98V

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ecfd8e521c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread wintray/app.py
Comment on lines +1009 to +1010
current_position = self._current_window_position()
work_area = self._work_area_for_point(current_position) or self._working_area()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Derive zoom from the target monitor

When the current native position and target anchor are on monitors with different work-area heights, this calculates the DOM zoom for the wrong monitor. For example, on the first show the hidden window may currently be on the primary monitor while _place_window_on_ui_thread() uses a saved position on a shorter secondary monitor; the page receives scale 1 here, but lines 1047-1053 resize it using the secondary monitor's smaller scale, recreating clipped content. The reverse mismatch also occurs with force_default=True. Resolve the same anchor/work area used by the queued placement before applying zoom, or pass the already-computed scale through to the UI mutation.

Useful? React with 👍 / 👎.

@aqua5230
aqua5230 merged commit b41ef98 into main Aug 30, 2026
7 checks passed
@aqua5230
aqua5230 deleted the fix/win-panel-zoom-clipping branch August 30, 2026 02:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant