Skip to content

feat: Add Windows support#459

Open
vikas53953 wants to merge 1 commit into
robinebers:mainfrom
vikas53953:windows-support
Open

feat: Add Windows support#459
vikas53953 wants to merge 1 commit into
robinebers:mainfrom
vikas53953:windows-support

Conversation

@vikas53953
Copy link
Copy Markdown

@vikas53953 vikas53953 commented May 10, 2026

Windows Support

Adds Windows build and runtime support for OpenUsage while preserving the existing macOS panel behavior.

What's Included

  • Gates the macOS-only tauri-nspanel dependency behind cfg(target_os = "macos").
  • Adds a Windows panel implementation using the standard Tauri webview window.
  • Updates tray behavior so left-click toggles the panel and right-click opens the menu on Windows.
  • Positions the Windows panel near the taskbar and clamps it to the current monitor.
  • Uses Tauri's app log directory outside macOS instead of hardcoded ~/Library/Logs.
  • Displays global shortcuts with Windows-friendly labels such as Ctrl and Alt while preserving Tauri's stored CommandOrControl shortcut format.
  • Adds Windows coverage for global shortcut display/recording and stabilizes the frontend test runner on Windows.
  • Adds Windows setup documentation.

Tested On

  • Windows Pro, build 26200.8328, 64-bit
  • Rust 1.95.0
  • Cargo 1.95.0
  • Bun 1.3.11
  • MSVC 19.44.35226
  • LLVM/clang 22.1.5
  • WebView2 Runtime 147.0.3912.98

Providers Tested

  • Claude: real usage data returned from the local HTTP API on Windows.
  • Codex: real usage data returned from the local HTTP API on Windows.

I did not individually authenticate and verify every bundled provider on Windows.

Verification

  • bun install
  • bun run bundle:plugins
  • cargo check --manifest-path src-tauri/Cargo.toml
  • cargo test --manifest-path src-tauri/Cargo.toml — passed, 89 tests
  • bun run build
  • bun run test src/hooks/app/use-panel.test.ts src/App.test.tsx — passed, 85 tests
  • bun run test src/main.test.tsx — passed, 2 tests
  • bun run tauri dev
  • bun run tauri build --no-sign
  • NSIS installer installed silently into a temporary directory, launched successfully, served provider data from http://127.0.0.1:6736/v1/usage, and uninstalled with exit code 0.

Test Note

A full parallel bun run test run showed Windows timeout flakiness in unrelated tests. The failed tests passed when run individually, but I am not claiming a clean full-suite bun run test result until that full-suite timeout behavior is resolved.

Screenshots

  • Attach Windows tray/panel screenshots when opening the PR.

Known Limitations

  • This PR treats Windows support as source-build / experimental support. Official release signing and updater behavior remain maintainer-owned.
  • Updater signing is configured upstream. Local Windows installer builds should use bun run tauri build --no-sign; maintainer release builds should provide TAURI_SIGNING_PRIVATE_KEY and omit --no-sign.
  • Provider plugins that read authentication tokens from macOS-specific paths such as ~/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.
  • Direct screenshot capture of the tray flyout was unreliable in this local desktop environment, so runtime proof also includes native process launch, local API responses, and installer launch verification.

@github-actions github-actions Bot added rust Pull requests that update rust code core docs labels May 10, 2026
Copy link
Copy Markdown

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

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: 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".

Comment thread src-tauri/src/panel_windows.rs Outdated
Comment on lines +168 to +169
let work_area = work_area_layout(monitor);
contains_point(&work_area, icon_center_x, icon_center_y)
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 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 👍 / 👎.

Comment on lines +82 to +90
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)
}
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 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 👍 / 👎.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

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.

Comment thread src-tauri/src/panel_macos.rs Outdated
Comment thread src-tauri/src/panel_windows.rs
Comment thread src-tauri/src/panel_windows.rs Outdated
Comment thread README.md Outdated
Copy link
Copy Markdown
Author

vikas53953 commented May 10, 2026

Update after addressing automated review feedback:

The latest branch commit is now 4db558d. I amended the original PR commit to keep this as one clean Windows-support commit.

Addressed review items:

  • Removed PR-DESCRIPTION.md; the PR body now carries the description and verification notes.
  • README scopes auto-update to released macOS builds only; Windows is documented as source-build support.
  • macOS monitor enumeration handles available_monitors() failure gracefully with log::warn! + return instead of panicking.
  • Windows panel hides on focus loss using WindowEvent::Focused(false) -> hide_panel.
  • Windows panel focus-loss handler registration is now idempotent, so repeated init_panel calls do not stack handlers.
  • Windows tray monitor selection uses full monitor bounds for hit testing; work area is only used afterward for clamping.
  • Windows fallback panel sizing converts configured logical width/height to physical pixels before physical positioning.
  • Console log forwarding is now idempotent so repeated mountApp() / HMR / test entry does not nest wrappers.
  • Windows tested configuration wording avoids the misleading Windows 10/11 product label and records the exact tested build.

Verification:

  • cargo check --manifest-path src-tauri/Cargo.toml: passed
  • cargo test --manifest-path src-tauri/Cargo.toml: passed, 89 tests
  • bun run build: passed
  • bun run test src/hooks/app/use-panel.test.ts src/App.test.tsx: passed, 85 tests
  • bun run test src/main.test.tsx: passed, 2 tests
  • bun run tauri build --no-sign: passed, MSI and EXE rebuilt

Provider/plugin testing:

  • Claude and Codex returned real usage data from the local HTTP API on Windows.
  • I did not individually authenticate and verify every bundled provider on Windows.
  • Provider-level Windows auth/path work remains documented as a limitation/follow-up area.

Known test note:

  • Full bun run test did not cleanly pass as a single parallel full-suite run today due to Windows timeout flakiness in unrelated tests.
  • On retry, 1077/1078 passed.
  • The affected tests passed when run individually.
  • I do not want to claim the full bun run test passed until that full-suite timeout behavior is clean.

@codex review
@cubic-dev-ai please re-check the latest commit 4db558d and confirm whether any review issues remain.

@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented May 10, 2026

Update after addressing automated review feedback:

The latest branch commit remains 9d116e7 because I amended the original PR commit to keep this as one clean Windows-support commit.

Addressed review items:
...

@vikas53953 I have started the AI code review. It will take a few minutes to complete.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

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.

Comment thread docs/windows-setup.md Outdated

## Tested Windows Configuration

- Windows 10 Pro 25H2, build 26200.8328, 64-bit
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot May 10, 2026

Choose a reason for hiding this comment

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

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>
Fix with Cubic

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

ℹ️ 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".

@robinebers
Copy link
Copy Markdown
Owner

This is a very small PR, nice!

Quick questions:

  • PR-DESCRIPTION.md can probably be removed?
  • Have you tried plugins? I wonder about their paths and auth

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!

@validatedev validatedev requested a review from Copilot May 10, 2026 11:28
@validatedev
Copy link
Copy Markdown
Collaborator

@codex review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 the tauri-nspanel dependency/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.

Comment on lines +10 to +30
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),
}
Comment on lines +93 to +105
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);
}
});

Comment thread src/main-entry.tsx
Comment on lines +18 to +30
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(() => {});
};
}
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

ℹ️ 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".

Copy link
Copy Markdown
Author

@robinebers addressed the cleanup items in 4db558d.

  • Removed PR-DESCRIPTION.md; the PR body now has the description/verification details.
  • Kept the macOS implementation isolated in panel_macos.rs and did not add shared macOS debt.
  • Addressed the follow-up review comments around Windows HiDPI fallback sizing, duplicate focus handlers, and repeated console log forwarding.
  • Rebuilt the Windows installer with bun run tauri build --no-sign.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core docs rust Pull requests that update rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants