Skip to content

Refactor window handling, improve theme support, and update dependencies#1807

Open
ItsEeleeya wants to merge 191 commits into
CapSoftware:mainfrom
ItsEeleeya:next-base-improvements
Open

Refactor window handling, improve theme support, and update dependencies#1807
ItsEeleeya wants to merge 191 commits into
CapSoftware:mainfrom
ItsEeleeya:next-base-improvements

Conversation

@ItsEeleeya

@ItsEeleeya ItsEeleeya commented May 12, 2026

Copy link
Copy Markdown
Contributor

Refactor window handling, improve theme support, and modernize native integration

Overview

This PR refactors a few things with small improvements as a base for upcoming PRs. The changes modernize how windows are revealed, streamline macOS integration with native AppKit APIs, and better theme handling.

Key Changes

Window Management Refactoring

  • Re-implemented auto-show window logic: Windows are now only shown when fully ready, completely eliminating white flashing (except during dev when Vinxi may interfere)
    • AutoRevealWindowOnReady: Automatically shows windows once content is loaded
    • RevealWindowWithSuspense: Defers visibility for windows like Settings until routing and child mounting completes
  • Renamed ShowCapWindowCapWindow throughout the codebase
  • Main window changes:
    • Removed fake traffic lights (Tauri now handles native positioning)
    • Removed custom AppDelegate as Tauri now supports custom traffic lights inset
  • Simplified drag region handling with new data-tauri-drag-region="deep" attribute (eliminates scattered manual markers)
  • Restored proper native menu behavior: onMouseDown={showCropOptionsMenu} in Editor crop section with onClick as keyboard/touch fallback

Theme/Appearance Improvements

  • Renamed AppThemeAppearance (with serde fallback for backward compatibility)
  • Fixed system theme toggle: Changing theme back to "system" now properly takes effect without requiring a reload
  • Removed window_transparency from general settings (not visible in UI; will return with full support in future)

Code Organization

  • Extracted display utilities to new src-tauri/src/display_utils.rs out of windows.rs
    • Monitor and display helper logic consolidated
    • MonitorExt and CursorMonitorInfo types organized
  • Cleaned up panel_manager.rs: Now macOS-only with streamlined logic
  • Moved AppKit menu configuration to new menu.rs module
  • Added help menu items with native AppKit integration

Native Platform Integration

  • macOS: Increased usage of objc2 ecosystem crates (app-kit, foundation, permissions)
    • Bumped objc2-app-kit version
  • Linux: Now using Windows 11-style caption controls
  • Added new WebviewWindowExt trait with with_nswindow_on_main() utility for direct AppKit window access
  • Settings window: Now persists position across restarts (removed from tauri_plugin_window_state denylist)

UI/UX Enhancements

  • Editor window header height now matches windows with toolbars on macOS 26
  • Fixed invisible target-select-overlay appearing over Settings/Upgrade windows when they close (no longer auto-opens)
  • Fixed Cropper bounds display (removed incorrect scale-50 class)
  • Small visual refinements in debug window
  • (macOS) With the introduction of proper menu items, Settings can now be opened with the +, shortcut

Dependencies & Plugins

  • Updated Tauri and associated plugins to latest versions
  • Added tauri-plugin-prevent-default: Enables default context menu only during development

Technical Notes

  • All changes maintain backward compatibility where applicable
  • Window reveal logic fully tested with zero white-flash regressions
  • AppKit integration now uses modern objc2 crates instead of legacy objc/cocoa
  • Future improvements planned for macOS focus handling via custom NSTrackingArea in dedicated Tauri plugin
  • This refactor lays groundwork for upcoming window management and platform-specific enhancements

Suggested changelog entires:

  • (macOS) App's menu has been overhauled with the correct menu items and help menu.
    • Some menu items may show an icon depending on system version (macOS 15, 26, 27 beta)
  • (macOS) Settings can now be opened with the +, shortcut.
  • (macOS) Once again using the native traffic lights
  • Fixed an visual issue within the full screen Cropper experience where the bounds tooltip appeared at the incorrect scale.
  • (Linux) Now using the same style of caption buttons as Windows.
  • Fixed an issue where changing the appearance back to system didn't always effect the window's content view.
  • The default context menu has been disabled. Default WebView shortcuts should now also be disabled.

Greptile Summary

Refactors desktop window reveal and native window handling while updating appearance synchronization, platform integration, and dependencies.

  • Extracts reusable reveal and display utilities and modernizes macOS window/menu integration.
  • Renames the persisted theme setting to appearance and updates native theme conversion.
  • Updates Tauri plugins, native dependencies, title bars, and window-state behavior.

Confidence Score: 4/5

The PR is not safe to merge until explicit appearance selections also update the webview content theme.

Selecting an appearance opposite to the OS preference updates native window chrome but leaves page content styled according to the OS media query.

apps/desktop/src/app.tsx

Important Files Changed

Filename Overview
apps/desktop/src/app.tsx Replaces theme listeners with an OS-media-query effect that no longer honors explicit light or dark appearance choices in webview content.
apps/desktop/src/utils/RevealWindow.tsx Centralizes once-per-webview reveal state and provides automatic and Suspense-aware reveal helpers.
apps/desktop/src-tauri/src/windows.rs Refactors window construction and native panel handling, including typed nonactivating-panel style masks.
apps/desktop/src-tauri/src/general_settings.rs Renames the persisted theme field to appearance with backward-compatible deserialization and an idiomatic native-theme conversion.
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
apps/desktop/src/app.tsx:113-117
**Explicit appearance is ignored**

When the user selects Light while the OS is dark, or Dark while the OS is light, this effect continues deriving the webview's `dark` class exclusively from the OS media query. The native window adopts the explicit appearance through `setAppearance`, but the page content retains the opposite OS-selected appearance, producing mismatched window chrome and content.

Reviews (4): Last reviewed commit: "Update pnpm-lock.yaml" | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Context used:

ItsEeleeya added 30 commits May 7, 2026 15:37
The header height now matches that of windows with Toolbars on macOS 26
The header height now matches that of windows with Toolbars on macOS 26
Comment thread apps/desktop/src/routes/(window-chrome)/new-main/index.tsx
Comment thread apps/desktop/src/routes/teleprompter.tsx
Comment thread apps/desktop/src/components/titlebar/controls/CaptionControlsWindows11.tsx Outdated
…indows11.tsx

Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
export function maybeShowWindow() {
if (windowShown) return;
windowShown = true;
void getCurrentWindow().show();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Worth adding a .catch() here so a failed show() (e.g. window already closed / plugin not ready) doesn’t turn into an unhandled rejection.

Suggested change
void getCurrentWindow().show();
void getCurrentWindow().show().catch((err) =>
console.error("Failed to show window", err),
);

let _ = MACOS_NATIVE_TERMINATE_APP.set(app.clone());

app.run_on_main_thread(|| {
let mtm = MainThreadMarker::new().expect("Running on main");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Minor: would avoid expect() here so a (surprising) main-thread scheduling failure doesn’t crash the whole app.

Suggested change
let mtm = MainThreadMarker::new().expect("Running on main");
let Some(mtm) = MainThreadMarker::new() else {
tracing::error!("menu init must run on the main thread");
return;
};

tracing::warn!("macOS native termination requested before handler was installed");
}
};
0 // NSTerminateCancel — we handle the actual exit ourselves

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If the handler isn't installed yet (or OnceLock wasn't set), returning NSTerminateCancel here can leave the user unable to quit. Seems safer to return NSTerminateNow in the None case.

Suggested change
0 // NSTerminateCancel — we handle the actual exit ourselves
if let Some(app) = MACOS_NATIVE_TERMINATE_APP.get() {
tauri::async_runtime::spawn({
let app = app.clone();
async move {
crate::request_app_exit(app).await;
}
});
0 // NSTerminateCancel
} else {
tracing::warn!("macOS native termination requested before handler was installed");
1 // NSTerminateNow
}

return;
};
// SAFETY: Tauri runs this on the main thread
let mtm = unsafe { MainThreadMarker::new_unchecked() };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Since we already gate this through run_on_main_thread, I'd still avoid MainThreadMarker::new_unchecked() here and keep it fail-safe if something changes upstream.

Suggested change
let mtm = unsafe { MainThreadMarker::new_unchecked() };
let Some(mtm) = MainThreadMarker::new() else {
tracing::error!("with_nswindow_on_main must run on the main thread");
return;
};

Comment thread apps/desktop/src-tauri/src/platform/macos/mod.rs Outdated
.plugin(tauri_plugin_clipboard_manager::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_prevent_default::debug())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Just to sanity check intent: since this is calling tauri_plugin_prevent_default::debug() unconditionally, is this meant to run in release builds too? If it’s dev-only, it might be worth gating behind cfg(debug_assertions) so prod behavior stays explicit.


pub fn on_event(app: &AppHandle, event: tauri::menu::MenuEvent) {
let url = match event.id().as_ref() {
APP_MENU_QUIT_ID => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

APP_MENU_QUIT_ID doesn’t look wired up anywhere (menu uses PredefinedMenuItem::quit, and patch_native_app_menu swaps it to terminate:), so this arm likely never fires.

Might be clearer to either remove APP_MENU_QUIT_ID/this arm, or define an explicit Quit menu item with that id if you still want to funnel quit through on_event.

"settings" => {
let app = app.clone();
spawn_on_runtime(async move {
let _ = CapWindow::Settings { page: None }.show(&app).await;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Swallowing the show() error here can make failures (closed window, not ready, etc.) silent. Logging it would help debugging.

Suggested change
let _ = CapWindow::Settings { page: None }.show(&app).await;
if let Err(err) = CapWindow::Settings { page: None }.show(&app).await {
tracing::warn!(?err, "Failed to show Settings window");
}

Comment thread apps/desktop/src/app.tsx Outdated
@richiemcilroy

Copy link
Copy Markdown
Member

hey @greptileai please re-review the pr

Comment thread apps/desktop/src/app.tsx
@richiemcilroy

Copy link
Copy Markdown
Member

hey @greptileai, please re-review the PR

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This repository has used its 100 free open-source review credits for this billing period. An organization admin can enable additional billed reviews, or reviews resume next period.

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.

2 participants