@cloudworxx/tauri-plugin-mac-rounded-corners
v1.1.1
Published
Tauri v2 plugin for native macOS rounded corners with customizable Traffic Lights positioning. Auto-repositions after fullscreen. App Store compatible.
Maintainers
Readme
macOS Rounded Corners Plugin for Tauri v2
A reusable Tauri plugin that enables native macOS rounded corners with customizable Traffic Lights positioning for frameless windows.
Author
Sascha Klein
Website: cloudworxx.us
Email: [email protected]
Features
- ✅ Rounded corners (12px default, customizable)
- ✅ Traffic Lights positioning (Close, Minimize, Maximize buttons)
- ✅ Auto-reposition after fullscreen (NEW in v1.1.0)
- ✅ App Store compatible - Uses only public macOS APIs
- ✅ Custom titlebar support - Full-size content view
- ✅ Native shadows - Optional shadow effects
- ✅ TypeScript support - Full type definitions
- ✅ Works with
decorations: false - ✅ Zero runtime dependencies
- ✅ MIT Licensed - Free for personal and commercial use
What's New in v1.1.0
- Auto-reposition: Traffic Lights automatically reposition after fullscreen exit
- New command:
reposition_traffic_lights()for manual control - Event-based: Uses
onResizedlistener for instant repositioning - Bug fixes: Close button now works correctly, fullscreen positioning fixed
Installation
There are two ways to install this plugin: automatic (recommended) or manual.
Option A: Automatic Installation (Recommended)
The plugin includes an automatic installer that copies files and updates your project configuration.
1. Install the package
npm install @cloudworxx/tauri-plugin-mac-rounded-cornersThe postinstall script automatically:
- ✅ Copies
mod.rstosrc-tauri/src/plugins/mac_rounded_corners.rs - ✅ Creates/updates
src-tauri/src/plugins/mod.rs
2. Complete the setup
You still need to manually add:
a) Dependencies to src-tauri/Cargo.toml:
[target.'cfg(target_os = "macos")'.dependencies]
cocoa = "0.26"
objc = "0.2.7"b) Commands in src-tauri/src/lib.rs:
mod plugins;
use plugins::mac_rounded_corners;
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
mac_rounded_corners::enable_rounded_corners,
mac_rounded_corners::enable_modern_window_style,
mac_rounded_corners::reposition_traffic_lights
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}c) Permissions to tauri.conf.json:
{
"app": {
"security": {
"capabilities": [
{
"identifier": "main-capability",
"windows": ["*"],
"permissions": [
"core:window:allow-start-dragging",
"core:window:allow-is-fullscreen",
"core:window:allow-is-maximized",
"core:event:allow-listen"
]
}
]
}
}
}Option B: Manual Installation
1. Copy Rust Code
mkdir -p src-tauri/src/plugins
cp mod.rs src-tauri/src/plugins/mac_rounded_corners.rs2. Add Dependencies
Add to src-tauri/Cargo.toml:
[target.'cfg(target_os = "macos")'.dependencies]
cocoa = "0.26"
objc = "0.2.7"3. Register Commands
In src-tauri/src/lib.rs:
mod plugins;
use plugins::mac_rounded_corners;
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
mac_rounded_corners::enable_rounded_corners,
mac_rounded_corners::enable_modern_window_style,
mac_rounded_corners::reposition_traffic_lights // NEW in v1.1.0
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}Create src-tauri/src/plugins/mod.rs:
pub mod mac_rounded_corners;4. Add Required Permissions
IMPORTANT: Add these permissions to tauri.conf.json:
{
"app": {
"security": {
"capabilities": [
{
"identifier": "main-capability",
"windows": ["*"],
"permissions": [
"core:window:allow-start-dragging",
"core:window:allow-is-fullscreen",
"core:window:allow-is-maximized",
"core:event:allow-listen"
]
}
]
}
}
}5. Import TypeScript Functions
Option 1: Use from npm package (recommended)
import { enableModernWindowStyle } from '@cloudworxx/tauri-plugin-mac-rounded-corners';Option 2: Copy TypeScript file locally
mkdir -p src/lib
cp node_modules/@cloudworxx/tauri-plugin-mac-rounded-corners/index.ts src/lib/mac-rounded-corners.tsThen import from local file:
import { enableModernWindowStyle } from './lib/mac-rounded-corners';Usage
Basic Usage
After npm installation:
import { useEffect } from 'react';
import { enableModernWindowStyle } from '@cloudworxx/tauri-plugin-mac-rounded-corners';
function App() {
useEffect(() => {
// Enable rounded corners on mount
// Auto-repositioning after fullscreen is enabled automatically
enableModernWindowStyle();
}, []);
return <div>Your App</div>;
}With Custom Configuration
import { enableModernWindowStyle } from '@cloudworxx/tauri-plugin-mac-rounded-corners';
// Custom corner radius and Traffic Lights position
await enableModernWindowStyle({
cornerRadius: 16, // Default: 12
offsetX: 5, // Move Traffic Lights 5px right
offsetY: 10, // Move Traffic Lights 10px down
});Manual Repositioning
import { repositionTrafficLights } from '@cloudworxx/tauri-plugin-mac-rounded-corners';
// Manually reposition Traffic Lights (useful after window state changes)
await repositionTrafficLights();Simple Mode (No Shadow)
import { enableRoundedCorners } from '@cloudworxx/tauri-plugin-mac-rounded-corners';
// Simpler version without shadow and layer-based clipping
await enableRoundedCorners({
offsetX: 0,
offsetY: 0,
});CSS Setup
Add to your CSS to ensure clean rounded corners:
html, body, #root {
margin: 0;
padding: 0;
background-color: transparent;
overflow: hidden;
}
.App {
width: 100%;
height: 100vh;
background-color: #f6f6f6; /* Your app background */
}Traffic Lights Positioning
The Traffic Lights (Close, Minimize, Maximize) can be positioned using offset values:
offsetX: Horizontal offset in pixels
- Positive = move right
- Negative = move left
- Default:
0.0
offsetY: Vertical offset in pixels
- Positive = move down
- Negative = move up
- Default:
0.0
Examples
// Standard macOS position (top-left)
enableModernWindowStyle({ offsetX: 0, offsetY: 0 });
// Move right and down
enableModernWindowStyle({ offsetX: 10, offsetY: 15 });
// Move left (careful with negative values!)
enableModernWindowStyle({ offsetX: -5, offsetY: 5 });⚠️ Warning: Avoid extreme offset values (< -5px or very large positive values) as they may cause Traffic Lights to become unclickable.
API Reference
enableRoundedCorners(config?)
Enables basic rounded corners with transparent titlebar.
Parameters:
config.offsetX(optional): Horizontal Traffic Lights offset (default: 0)config.offsetY(optional): Vertical Traffic Lights offset (default: 0)
Returns: Promise<void>
enableModernWindowStyle(config?)
Enables modern window style with rounded corners, shadow, and layer-based clipping. Recommended.
Parameters:
config.cornerRadius(optional): Corner radius in pixels (default: 12)config.offsetX(optional): Horizontal Traffic Lights offset (default: 0)config.offsetY(optional): Vertical Traffic Lights offset (default: 0)
Returns: Promise<void>
repositionTrafficLights() ✨ NEW
Manually repositions Traffic Lights to their configured position. Useful if you need to manually trigger repositioning.
Returns: Promise<void>
cleanupRoundedCorners() ✨ NEW
Cleanup function to remove event listeners. Call when component unmounts.
Returns: void
Tauri Configuration
Ensure your tauri.conf.json has decorations disabled:
{
"tauri": {
"windows": [
{
"decorations": false
}
]
}
}Important: Do NOT set transparent: true - this plugin provides rounded corners without requiring the transparency API, making it more reliable and App Store compatible.
Platform Support
| Feature | macOS | Windows | Linux | |---------|-------|---------|-------| | Rounded Corners | ✅ | ❌ | ❌ | | Traffic Lights | ✅ (native) | N/A | N/A | | Shadow | ✅ (native) | ✅ | ✅ | | Auto-reposition | ✅ | N/A | N/A |
On non-macOS platforms, the plugin commands are no-ops and won't throw errors.
Troubleshooting
Traffic Lights not repositioning after fullscreen
Solution: Ensure you have the required permissions in tauri.conf.json:
"core:window:allow-is-fullscreen",
"core:event:allow-listen"Traffic Lights not clickable
- Check your offset values - they may be too extreme
- Ensure all window style masks are set (the plugin handles this)
- Safe ranges:
offsetX: -5 to 20,offsetY: 0 to 20
White flicker in corners
- Ensure
html,body,#roothavebackground-color: transparent - Remove
border-radiusfrom these elements - Let the Rust layer handle the clipping
Close button disabled
- This is fixed in v1.1.0 (adds
NSClosableWindowMask) - Make sure you're using the latest version
Permission errors in console
Add all required permissions to tauri.conf.json (see Installation step 4)
Technical Details
Used macOS APIs (all public)
NSWindow.styleMaskNSFullSizeContentViewWindowMasksetTitlebarAppearsTransparentsetTitleVisibilitysetHasShadowCALayer.setCornerRadiussetMasksToBoundsstandardWindowButton(for repositioning)
No private APIs are used - fully App Store compliant.
How Auto-Reposition Works
The plugin uses Tauri's onResized event listener to detect when the window exits fullscreen mode. When this event fires, it immediately calls repositionTrafficLights() to restore the custom position. This is event-based and instant (~0-50ms), not polling-based.
Why this works
NSFullSizeContentViewWindowMaskallows content in titlebar region- Transparent titlebar removes default chrome
- Layer-based corner radius clips the content
- Style masks enable Traffic Light functionality
- Frame manipulation positions the buttons
- Event listener repositions after state changes
Requirements
- Tauri v2.x
- macOS 10.15 or later
- Rust stable toolchain
- Node.js 18+
Changelog
See CHANGELOG.md for detailed version history.
Recent Changes
v1.1.0 (2025-01-02)
- Added auto-reposition feature for Traffic Lights after fullscreen
- New
reposition_traffic_lights()command - Event-based resize listener
- Fixed Close button not working
- Added required permissions documentation
v1.0.0 (2025-01-02)
- Initial release
FAQ
Q: Does this work with Tauri v1?
A: No, this plugin is designed for Tauri v2. For Tauri v1, you would need to adapt the code.
Q: Can I use this in production?
A: Yes! The plugin uses only public APIs and is App Store compatible.
Q: Will this work on Windows/Linux?
A: The commands are no-ops on non-macOS platforms, so your app won't crash, but rounded corners are macOS-only.
Q: Can I customize the corner radius?
A: Yes! Use the cornerRadius parameter in enableModernWindowStyle().
Q: Do I need transparent: true in tauri.conf.json?
A: No! This plugin works without the transparency API, which makes it more reliable and avoids potential App Store issues.
Q: Why do Traffic Lights reset position after fullscreen?
A: macOS resets custom button positions during fullscreen transitions. v1.1.0 automatically fixes this with event-based repositioning.
Q: Is this free?
A: Yes! MIT licensed - free for personal and commercial use.
Support
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 💬 Discussions: GitHub Discussions
License
MIT License - see LICENSE file for details.
Copyright (c) 2025 Sascha Klein (cloudworxx.us)
Credits
Created for PromptDeck
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Star History
If you find this plugin useful, please consider giving it a ⭐ on GitHub!
Made with ❤️ by Sascha Klein (cloudworxx.us)
