feat: Add Windows support#459
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 437d9acfca
ℹ️ 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".
| let work_area = work_area_layout(monitor); | ||
| contains_point(&work_area, icon_center_x, icon_center_y) |
There was a problem hiding this comment.
Use full monitor bounds for tray hit testing
On Windows with the tray icon on a taskbar outside the primary monitor, this hit test can miss the correct monitor because work_area excludes the taskbar area where the tray icon lives. The code then falls back to primary_monitor(), so a click on a secondary-monitor tray can open the panel on the primary monitor instead of near the clicked tray icon. Use the monitor's full position/size for selecting the monitor, then use work_area only for clamping the panel.
Useful? React with 👍 / 👎.
| pub fn init(app_handle: &AppHandle) -> tauri::Result<()> { | ||
| if let Some(window) = app_handle.get_webview_window("main") { | ||
| if let Err(error) = window.set_shadow(false) { | ||
| log::debug!("Failed to disable Windows panel shadow: {}", error); | ||
| } | ||
| Ok(()) | ||
| } else { | ||
| Err(tauri::Error::WindowNotFound) | ||
| } |
There was a problem hiding this comment.
Hide the Windows panel when focus is lost
On Windows this only configures the normal webview window, so after opening the tray panel and clicking another app the panel stays visible until Escape or another tray click. macOS still hides on window_did_resign_key in panel_macos.rs; the Windows path needs an equivalent blur/focus-lost handler to preserve tray-panel behavior.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
4 issues found across 18 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="README.md">
<violation number="1" location="README.md:11">
P3: The new Windows source-build note makes the adjacent auto-update claim read as if it applies to Windows source builds, but the documented Windows build flow skips updater signing, so those builds should not be described as auto-updating without qualification.</violation>
</file>
<file name="src-tauri/src/panel_macos.rs">
<violation number="1" location="src-tauri/src/panel_macos.rs:220">
P2: Unwrapping `available_monitors()` can panic and crash panel positioning when monitor enumeration fails.</violation>
</file>
<file name="src-tauri/src/panel_windows.rs">
<violation number="1" location="src-tauri/src/panel_windows.rs:82">
P2: The Windows panel has no focus-loss handler, so after opening the panel and clicking another application, it remains visible until the user presses Escape or clicks the tray icon again. The macOS implementation auto-hides via `window_did_resign_key`. Add an equivalent handler on Windows — Tauri's `onFocusChanged` window event or the Rust `on_window_event` with `WindowEvent::Focused(false)` can be used to call `hide_panel` when the window loses focus.</violation>
<violation number="2" location="src-tauri/src/panel_windows.rs:168">
P2: The tray icon lives inside the taskbar, which is excluded from `work_area`. Using `work_area_layout` for the monitor hit test means the tray icon's center point will almost always fall outside the work area, so the `.find()` never matches the correct monitor and falls back to `primary_monitor()`. On multi-monitor setups with the taskbar on a secondary display, the panel opens on the wrong monitor.
Use the monitor's full bounds (`monitor.position()` + `monitor.size()`) for selecting which monitor contains the tray icon, then use `work_area` only for clamping the panel position afterward.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
437d9ac to
9d116e7
Compare
|
Update after addressing automated review feedback: The latest branch commit is now Addressed review items:
Verification:
Provider/plugin testing:
Known test note:
@codex review |
@vikas53953 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
1 issue found across 18 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="docs/windows-setup.md">
<violation number="1" location="docs/windows-setup.md:68">
P3: The tested OS/version is incorrect: build 26200.8328 is a Windows 11 25H2 build, not Windows 10, so the support/testing claim is misleading.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
|
|
||
| ## Tested Windows Configuration | ||
|
|
||
| - Windows 10 Pro 25H2, build 26200.8328, 64-bit |
There was a problem hiding this comment.
P3: The tested OS/version is incorrect: build 26200.8328 is a Windows 11 25H2 build, not Windows 10, so the support/testing claim is misleading.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/windows-setup.md, line 68:
<comment>The tested OS/version is incorrect: build 26200.8328 is a Windows 11 25H2 build, not Windows 10, so the support/testing claim is misleading.</comment>
<file context>
@@ -0,0 +1,74 @@
+
+## Tested Windows Configuration
+
+- Windows 10 Pro 25H2, build 26200.8328, 64-bit
+- Rust `1.95.0`
+- Cargo `1.95.0`
</file context>
|
Codex Review: Didn't find any major issues. Another round soon, please! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
9d116e7 to
c2843f2
Compare
|
This is a very small PR, nice! Quick questions:
If this checks out, and doesn't create debt for the macOs implementation, it'd be cool to have as an experimental feature. Thank you! |
|
@codex review |
There was a problem hiding this comment.
Pull request overview
Adds first-class Windows build/runtime support while preserving the existing macOS panel behavior by splitting the panel implementation per platform, adjusting tray/panel interactions, and updating frontend UI/test behavior for Windows.
Changes:
- Split panel implementation into macOS (
tauri-nspanel) vs non-macOS (standard Tauri window) and gated thetauri-nspaneldependency/plugin to macOS only. - Updated tray and panel UX for Windows (left-click toggle behavior, taskbar/monitor-aware positioning, draggable header) and adjusted log path handling outside macOS.
- Updated frontend entrypoint, global shortcut display/recording for Windows-friendly labels, and improved Windows stability in the test runner/docs.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| vite.config.ts | Increases Vitest timeout to reduce Windows flakiness. |
| src/main.tsx | Delegates bootstrapping to mountApp() in a separate entry module. |
| src/main.test.tsx | Updates test to target the new entry module and mocks Tauri log plugin. |
| src/main-entry.tsx | New bootstrap module for console log forwarding + React mount. |
| src/components/global-shortcut-section.tsx | Adds platform-aware shortcut display labels and recording display changes. |
| src/components/global-shortcut-section.test.tsx | Adds Windows-specific formatting/recording test coverage and platform mocking. |
| src/components/app/app-shell.tsx | Adds Windows-specific layout tweaks and a draggable header region. |
| src/App.test.tsx | Mocks platform to stabilize UI expectations across OS runners. |
| src-tauri/src/tray.rs | Adjusts tray click handling to use new panel APIs and left-click toggling. |
| src-tauri/src/panel.rs | Routes panel behavior to platform-specific modules and re-exports shared API. |
| src-tauri/src/panel_windows.rs | Implements Windows panel behavior using the standard Tauri window + positioning logic. |
| src-tauri/src/panel_macos.rs | Moves the existing NSPanel-based implementation into a macOS-only module. |
| src-tauri/src/lib.rs | Gates macOS-only plugin, updates hide_panel command, and uses platform log dir outside macOS. |
| src-tauri/Cargo.toml | Moves tauri-nspanel to a macOS-only dependency block. |
| src-tauri/capabilities/default.json | Grants the window start-dragging capability needed by the Windows draggable header. |
| README.md | Updates platform messaging and links to Windows setup instructions. |
| PR-DESCRIPTION.md | Adds a detailed PR description document including Windows verification steps. |
| docs/windows-setup.md | New documentation for building/running OpenUsage from source on Windows. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| fn panel_layout_for_window(window: &tauri::WebviewWindow) -> PanelLayout { | ||
| let panel_size = window.outer_size().ok(); | ||
| let conf: serde_json::Value = | ||
| serde_json::from_str(include_str!("../tauri.conf.json")).expect("tauri.conf.json is valid"); | ||
| let fallback_width = conf["app"]["windows"][0]["width"].as_f64().unwrap_or(400.0); | ||
| let fallback_height = conf["app"]["windows"][0]["height"] | ||
| .as_f64() | ||
| .unwrap_or(500.0); | ||
|
|
||
| PanelLayout { | ||
| x: 0.0, | ||
| y: 0.0, | ||
| width: panel_size | ||
| .as_ref() | ||
| .map(|size| size.width as f64) | ||
| .unwrap_or(fallback_width), | ||
| height: panel_size | ||
| .as_ref() | ||
| .map(|size| size.height as f64) | ||
| .unwrap_or(fallback_height), | ||
| } |
| pub fn init(app_handle: &AppHandle) -> tauri::Result<()> { | ||
| if let Some(window) = app_handle.get_webview_window("main") { | ||
| if let Err(error) = window.set_shadow(false) { | ||
| log::debug!("Failed to disable Windows panel shadow: {}", error); | ||
| } | ||
|
|
||
| let handle = app_handle.clone(); | ||
| window.on_window_event(move |event| { | ||
| if matches!(event, WindowEvent::Focused(false)) { | ||
| hide_panel(&handle); | ||
| } | ||
| }); | ||
|
|
| export function installConsoleLogForwarding() { | ||
| const originalError = console.error; | ||
| console.error = (...args: unknown[]) => { | ||
| originalError(...args); | ||
| logError(args.map(stringify).join(" ")).catch(() => {}); | ||
| }; | ||
|
|
||
| const originalWarn = console.warn; | ||
| console.warn = (...args: unknown[]) => { | ||
| originalWarn(...args); | ||
| logWarn(args.map(stringify).join(" ")).catch(() => {}); | ||
| }; | ||
| } |
|
Codex Review: Didn't find any major issues. Another round soon, please! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
c2843f2 to
4db558d
Compare
|
@robinebers addressed the cleanup items in
On plugins/auth: I verified Claude and Codex on Windows through the local HTTP API and both returned real usage data. I did not verify every bundled provider on Windows, so I kept that scoped honestly in the PR body: provider plugins with macOS-specific auth/path assumptions may still need provider-level follow-up work. This PR is focused on core app/window/tray/build support and documents Windows as source-build / experimental support. |
Windows Support
Adds Windows build and runtime support for OpenUsage while preserving the existing macOS panel behavior.
What's Included
tauri-nspaneldependency behindcfg(target_os = "macos").~/Library/Logs.CtrlandAltwhile preserving Tauri's storedCommandOrControlshortcut format.Tested On
1.95.01.95.01.3.1119.44.3522622.1.5147.0.3912.98Providers Tested
I did not individually authenticate and verify every bundled provider on Windows.
Verification
bun installbun run bundle:pluginscargo check --manifest-path src-tauri/Cargo.tomlcargo test --manifest-path src-tauri/Cargo.toml— passed, 89 testsbun run buildbun run test src/hooks/app/use-panel.test.ts src/App.test.tsx— passed, 85 testsbun run test src/main.test.tsx— passed, 2 testsbun run tauri devbun run tauri build --no-signhttp://127.0.0.1:6736/v1/usage, and uninstalled with exit code 0.Test Note
A full parallel
bun run testrun showed Windows timeout flakiness in unrelated tests. The failed tests passed when run individually, but I am not claiming a clean full-suitebun run testresult until that full-suite timeout behavior is resolved.Screenshots
Known Limitations
bun run tauri build --no-sign; maintainer release builds should provideTAURI_SIGNING_PRIVATE_KEYand omit--no-sign.~/Library/may need individual updates to support Windows equivalents such as%APPDATA%. This PR focuses on the core app and panel infrastructure; provider-level Windows auth support can be addressed in follow-up PRs.