Skip to content

fix: custom titlebar + glass-panel shell + minimize-to-tray regression#229

Closed
YCC3741 wants to merge 18 commits intocodefrom
bug-fix
Closed

fix: custom titlebar + glass-panel shell + minimize-to-tray regression#229
YCC3741 wants to merge 18 commits intocodefrom
bug-fix

Conversation

@YCC3741
Copy link
Copy Markdown
Collaborator

@YCC3741 YCC3741 commented Apr 20, 2026

Summary

This branch bundles the glass-panel UI refresh cherry-picked from #228 together with a follow-up fix for a minimize-to-tray regression the new window shell introduced.

Glass-panel UI refresh (cherry-picked from #228)

  • Custom TitleBar.vue with drag / minimize / close buttons plus dynamic page titles.
  • decorations: false + transparent: true window backed by the new bf-glass-window utility class that reuses the existing mica gradient — visual identity preserved.
  • Router meta carries titleKey / titleIcon / windowWidth / windowHeight; the afterEach hook resizes the window per route.
  • Material Symbols font loaded in index.html; global scrollbar / MessageBox overrides in App.vue.
  • Pages restructured with TitleBar + scroll wrapper: LoginPage, AccountList (page header retained per request), Settings, About, ManageAccount.
  • QR form: enlarge-on-click dialog + "copy QR" button + hint text (original size="large" button styling retained).
  • i18n: added titleBar.* / loginQr.{enlarge,copyQr,copyQrSuccess}; removed unused loginShell.*; warnHtmlMessage: false in i18n init.
  • mockups/ added to .gitignore.

Follow-up fix — minimize honours minimize_to_tray

The borderless + transparent + non-resizable window no longer reliably emits Windows' WM_SIZE(SIZE_MINIMIZED, 0, 0) that tray::handle_minimize_to_tray was listening for, so the TitleBar minimize button always dropped to the taskbar even when the user had ticked "Minimize to notification center".

Fix: switch the TitleBar minimize path to a new minimize_main_window Tauri command that reads the config and either hides-and-shows-tray or falls through to a plain window.minimize(). The Resized(0, 0) listener is kept as a defensive fallback for any OS-initiated minimize.

  • tray.rs — extract hide_to_tray (generic over Runtime); expose is_minimize_to_tray_enabled; add TrayState newtype for command-layer access to the tray ID via managed state.
  • lib.rs.manage(TrayState(arc)) so the setup / window-event closures and the new command share one Arc<Mutex<Option<TrayIconId>>>.
  • commands/system.rs — new minimize_main_window<R: Runtime> with system.window_not_found / system.minimize_failed error codes.
  • commands/mod.rs — register in collect_commands! (with ::<tauri::Wry> turbofish per the login_gamepass_start precedent) and in REQUIRED_COMMANDS.
  • bindings.ts — regenerated via cargo run --example export_bindings.
  • TitleBar.vue — call commands.minimizeMainWindow(); fallback to appWindow.minimize() on command error so the button never becomes a silent no-op.

Test plan

Automated

  • cargo test --lib — 730 passed
  • npx vitest run — 585 passed across 40 files

Manual (requires npm run tauri dev)

Glass-panel shell

  • Open login page — custom titlebar renders with app icon, drag works, minimize + close buttons work, region switcher + settings/about buttons in titlebar slot work.
  • Navigate Login → AccountList → Settings → About → ManageAccount — window resizes per route, titlebar title updates.
  • Esc / click-outside on any MessageBox (e.g. confirm / alert) — glass styling applied.
  • Login region selection unaffected.
  • Account reordering (SortableJS) on AccountList unaffected.
  • Drag title bar to move window; double-clicking titlebar does nothing (expected — not implemented).

QR form

  • QR button enlarge opens dialog; hint text "右鍵可以複製" visible.
  • Right-click QR image copies it (unchanged behaviour).
  • "Copy QR" button copies and shows success message.
  • Existing buttons (login / region / refresh) remain size="large".

Minimize-to-tray

  • Settings → uncheck "Minimize to notification center" → TitleBar minimize → window goes to Windows taskbar.
  • Settings → check "Minimize to notification center" → TitleBar minimize → window hides, tray icon appears.
  • Left-click tray icon → window restored, tray icon hidden.

Credits

Glass-panel shell commits carry Co-Authored-By: William Leung <61426712+lshw54@users.noreply.github.com> (original PR #228 author). The follow-up minimize-to-tray fix is authored locally.

YCC3741 and others added 15 commits April 20, 2026 19:53
…tale cookie and misc improvements

- Replace vuedraggable with direct SortableJS for account list drag reorder
- Add NSIS installer target with embedded WebView2 bootstrapper
- Fix stale bfWebToken cookie in auth_aspx by reading live value from cookie jar
- Add updateSessionService to keep frontend session in sync on game switch
- Restore VideoReport button with updated beanfun-event URL
- Add account limit notice display and disable add-account button at limit
- Add QR code image copy to clipboard on right-click
- Disable global context menu in production
- Config store: silently handle read-only file write failures
- Update README with installer vs portable version table
- Update tests to match SortableJS migration and config store changes
Port the two missing WPF buttons (bfb_Gash_Click + btn_Deposite_Click) that were omitted during the Tauri rewrite. The i18n keys already existed; this wires up the backend command and frontend UI.
The region picker always showed on every launch even when loginRegion
and loginMethod were already persisted in Config.xml. This implements
the WPF loginMethodInit parity: watch config.loaded and router.replace
to the correct form (id-pass or QR) based on saved preferences.

Back buttons on login forms now navigate with ?pick=1 so the user can
still reach the region picker without triggering the auto-redirect.
Disable native decorations and enable transparency so the glass
panel itself becomes the visible window. Lock the window size
and grant the permissions the custom TitleBar needs to drag,
minimize, close, and resize the frame.

Co-Authored-By: William Leung <61426712+lshw54@users.noreply.github.com>
The custom TitleBar replaces the native window chrome that we
disabled in tauri.conf. It exposes drag-to-move on the title
area, minimize/close buttons, and a slot for per-page actions
(region switch, settings, about). The displayed title and icon
come from route.meta so each page can self-describe.

Co-Authored-By: William Leung <61426712+lshw54@users.noreply.github.com>
Wire the global pieces that the glass-panel-as-window architecture
needs across the whole app:

- Make :root, html, body and #app transparent so only the page's
  glass card is visible.
- Disable text selection globally (with an opt-in carve-out for
  inputs / textareas / contenteditable) so the app feels native
  rather than like a web page.
- Thin rounded scrollbars matching the design system.
- Element Plus MessageBox glass override so confirms blend with
  the new chrome instead of looking like default web dialogs.
- Load the Material Symbols Outlined font for the icons used by
  the TitleBar and per-page action buttons.
- Ignore the local mockups/ folder.

Co-Authored-By: William Leung <61426712+lshw54@users.noreply.github.com>
Composite the existing bf-mica-bg gradient with a white overlay
and the bf-glass-panel border / shadow / radius into a single
class. Top-level pages adopting the glass-panel-as-window
architecture use this instead of stacking two utilities or
duplicating the full gradient inline, keeping our existing
visual identity (warm orange/cream gradient, fonts) untouched
while still going opaque so the desktop doesn't bleed through.

Co-Authored-By: William Leung <61426712+lshw54@users.noreply.github.com>
Extend RouteMeta with titleKey, titleIcon, windowWidth and
windowHeight so each route self-describes the chrome it wants.
TitleBar reads titleKey/titleIcon; an afterEach hook resizes
the Tauri window via setSize() and observes the page root so
content-driven height changes track reactively. Also export a
resizeWindow() helper for pages that need to override the
default at runtime (e.g. unauthenticated Settings).

Co-Authored-By: William Leung <61426712+lshw54@users.noreply.github.com>
Add the titleBar.* tree (per-page chrome titles plus minimize /
close tooltips) consumed by route.meta.titleKey, the new
loginQr.enlarge / loginQr.copyQr / loginQr.copyQrSuccess keys
for the QR enlarge + copy buttons, and remove the dead
loginShell.* keys that the old brand header used. accountList.title
and accountList.subtitle stay (the list page still renders its
own header alongside the TitleBar).

Disable warnHtmlMessage in createAppI18n so the warm-color HTML
fragments embedded in some translations stop spamming the
console.

Co-Authored-By: William Leung <61426712+lshw54@users.noreply.github.com>
Drop the old brand header (icon + heading + subline) since the
TitleBar now communicates which page the user is on. Wrap the
RouterView in a flex column so the glass card scrolls without
overflowing the chrome. The TitleBar slot carries the region
switcher (hidden on the picker itself) plus settings and about
shortcuts so chrome real estate is consistent across login
flow steps.

Co-Authored-By: William Leung <61426712+lshw54@users.noreply.github.com>
Add a zoom_in button that opens an overlay with a 360px QR for
the user to scan from a phone held farther away, and an explicit
content_copy button so the copy affordance is discoverable
without right-clicking. The existing right-click-to-copy still
works and the existing large-size action buttons (refresh, back,
deeplink, game start) are kept as-is.

Co-Authored-By: William Leung <61426712+lshw54@users.noreply.github.com>
Promote the page root to bf-glass-window and pull the settings
+ about icons up into the TitleBar slot so the page chrome lines
up with the rest of the app. The list header (title + subtitle)
stays - the post-login landing still wants the explicit "pick
an account" framing under the chrome, even though the TitleBar
already announces the page.

A scroll wrapper takes over from the old min-height/padded root
so the account rows scroll inside the card rather than blowing
past the rounded corners.

Co-Authored-By: William Leung <61426712+lshw54@users.noreply.github.com>
Same rewrap as AccountList: each page root is now a flex column
with bf-glass-window for the visible card, the TitleBar mounts
at the top, and a scroll wrapper holds the original content so
long forms (Settings) and long histories (ManageAccount) stay
inside the rounded card.

Co-Authored-By: William Leung <61426712+lshw54@users.noreply.github.com>
Add a global tests/setup.ts that stubs the Tauri window and dpi
modules and globally stubs TitleBar so per-page specs don't have
to fight the chrome they don't care about. Refresh existing
specs to match the chrome rewrap (LoginPage no longer renders
the brand header, AccountList icons moved to the TitleBar slot,
Iphone / Wallet icon stubs needed for the new game info bar).
The drag-end harness now hands handleDragEnd a real DOM stub
since the new SortableJS revert pass touches event.from /
event.item, and the persist-failure case clones the populated
fixture so the prior drag test cannot mutate the shared array.
A FRONTEND_ONLY exception keeps the new loginQr.copyQrSuccess
key out of the dead-key guard.

Co-Authored-By: William Leung <61426712+lshw54@users.noreply.github.com>
…mize

The borderless + transparent + non-resizable window introduced with
the glass-panel shell no longer reliably emits Windows'
WM_SIZE(SIZE_MINIMIZED, 0, 0), which `tray::handle_minimize_to_tray`
listens for. As a result, clicking the TitleBar minimize button
always minimized to the taskbar even with `minimize_to_tray = true`.

Switch the TitleBar's minimize path from direct `appWindow.minimize()`
to a new `minimize_main_window` Tauri command that reads the config
and either hides+shows-tray or falls through to a plain minimize.
The window-event fallback is kept for any OS-initiated minimize that
still fires `Resized(0, 0)`.

- tray: extract `hide_to_tray` helper (generic over Runtime); expose
  `is_minimize_to_tray_enabled`; add `TrayState` newtype so commands
  can reach the tray ID via managed state.
- lib: register `TrayState` via `.manage()` so the setup/event
  closures and the new command share one Arc<Mutex<Option<TrayIconId>>>.
- commands/system: add `minimize_main_window<R: Runtime>` with
  `system.window_not_found` / `system.minimize_failed` error codes.
- commands/mod: collect the new command and add it to the bindings
  contract test.
- bindings.ts: regenerated via `cargo run --example export_bindings`.
- TitleBar.vue: call `commands.minimizeMainWindow()`; fallback to
  `appWindow.minimize()` on command error so the button never
  becomes a silent no-op.
YCC3741 added 3 commits April 20, 2026 21:06
The two D7 drag-end specs only assert against the Pinia store and
the `commands.setConfig` spy — the mounted wrapper handle is never
referenced. CI ESLint (`no-unused-vars`) blocked the PR on this.

Replace `const wrapper = await ctx.mountIt()` with a plain
`await ctx.mountIt()` so the component is still mounted (side
effects, router, Pinia subscribers) but no dangling binding trips
the lint.
CI Prettier check caught 5 files that were prettier-clean on `code`
but got reformatted off-style by the PR #228 cherry-picks and the
earlier glass-panel restructure. Running `prettier --write` on just
those files restores the committed style:

- README.md
- src/pages/AccountList.vue (template indentation after the glass-panel wrap)
- src/stores/config.ts
- src/windows/MapleTools.vue
- tests/unit/pages/AccountList.spec.ts (from the previous unused-var fix)

Pure whitespace / quote-style changes — no logic.
CI `cargo test` failed intermittently with:

  Registry(Os { code: 1018, "Illegal operation attempted on a
  registry key that has been marked for deletion." })

in `import_records_with_valid_json_writes_file_and_returns_records`
and `export_then_import_preserves_records_through_roundtrip`.

Root cause: `RegistryScope::drop` issued
`delete_subkey_non_recursive(TEST_REGISTRY_PARENT)` after deleting
its own child subkey. Because cargo runs test functions in parallel,
test A's Drop could start deleting the (now-empty) PARENT while
test B was mid-flight creating its own child under PARENT, producing
ERROR_KEY_DELETED (1018) on test B's registry ops.

Drop the parent-key cleanup. The sub-keys themselves are still
recursively deleted on per-test Drop, and `RegistryScope::new` also
pre-deletes its own sub-key before use, so no state leaks between
runs. The parent `HKCU\SOFTWARE\BEANFUN_NEXT_TEST` key remains
orphaned (empty) after the test suite exits — harmless since it's a
sandboxed test-only prefix that future runs reuse anyway.

Also remove the now-unused `delete_subkey_non_recursive` helper so
clippy `-D warnings` stays green on `dead_code`.
@YCC3741
Copy link
Copy Markdown
Collaborator Author

YCC3741 commented Apr 20, 2026

done in #230

@YCC3741 YCC3741 closed this Apr 20, 2026
YCC3741 added a commit that referenced this pull request Apr 20, 2026
fix: HK login, UI improvements, and native WebView2 cookie seeding (supersedes #229)
@YCC3741 YCC3741 deleted the bug-fix branch April 21, 2026 00:25
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