fix: window controls, dragging, and text selection

- Add Tauri permissions for window minimize/maximize/close/drag
- Rewrite WindowControls with plain buttons + stopPropagation
  to prevent drag region from capturing button clicks
- Add data-tauri-drag-region to TopBar child containers
- Make OpenPylon text pointer-events-none and select-none
This commit is contained in:
Your Name
2026-02-15 21:19:24 +02:00
parent 85c54a3768
commit 63b7de0e6f
3 changed files with 45 additions and 29 deletions

View File

@@ -5,6 +5,14 @@
"windows": ["main"], "windows": ["main"],
"permissions": [ "permissions": [
"core:default", "core:default",
"core:window:allow-close",
"core:window:allow-minimize",
"core:window:allow-maximize",
"core:window:allow-unmaximize",
"core:window:allow-toggle-maximize",
"core:window:allow-is-maximized",
"core:window:allow-start-dragging",
"core:window:allow-set-focus",
"opener:default", "opener:default",
"dialog:default", "dialog:default",
"shell:default", "shell:default",

View File

@@ -81,7 +81,7 @@ export function TopBar() {
style={{ borderBottom: isBoardView && board ? `2px solid ${board.color}` : '1px solid var(--border)' }} style={{ borderBottom: isBoardView && board ? `2px solid ${board.color}` : '1px solid var(--border)' }}
> >
{/* Left section */} {/* Left section */}
<div className="flex items-center gap-2"> <div data-tauri-drag-region className="flex items-center gap-2">
{isBoardView && ( {isBoardView && (
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
@@ -104,7 +104,7 @@ export function TopBar() {
</div> </div>
{/* Center section */} {/* Center section */}
<div className="flex flex-1 items-center justify-center"> <div data-tauri-drag-region className="flex flex-1 items-center justify-center select-none">
{isBoardView && board ? ( {isBoardView && board ? (
editing ? ( editing ? (
<input <input
@@ -128,7 +128,7 @@ export function TopBar() {
</button> </button>
) )
) : ( ) : (
<span className="font-heading text-lg text-pylon-text"> <span className="pointer-events-none font-heading text-lg text-pylon-text">
OpenPylon OpenPylon
</span> </span>
)} )}

View File

@@ -1,8 +1,6 @@
import { useState, useEffect } from "react"; import { useState, useEffect, useCallback } from "react";
import { getCurrentWindow } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
import { motion } from "framer-motion";
import { Minus, Square, Copy, X } from "lucide-react"; import { Minus, Square, Copy, X } from "lucide-react";
import { springs } from "@/lib/motion";
export function WindowControls() { export function WindowControls() {
const [isMaximized, setIsMaximized] = useState(false); const [isMaximized, setIsMaximized] = useState(false);
@@ -22,32 +20,44 @@ export function WindowControls() {
}; };
}, []); }, []);
const appWindow = getCurrentWindow(); const handleMinimize = useCallback((e: React.MouseEvent) => {
e.stopPropagation();
e.preventDefault();
getCurrentWindow().minimize();
}, []);
const handleToggleMaximize = useCallback((e: React.MouseEvent) => {
e.stopPropagation();
e.preventDefault();
getCurrentWindow().toggleMaximize();
}, []);
const handleClose = useCallback((e: React.MouseEvent) => {
e.stopPropagation();
e.preventDefault();
getCurrentWindow().close();
}, []);
return ( return (
<div className="flex items-center"> <div className="flex items-center" onMouseDown={(e) => e.stopPropagation()}>
{/* Separator */} {/* Separator */}
<div className="mx-2 h-4 w-px bg-pylon-text-secondary/20" /> <div className="mx-2 h-4 w-px bg-pylon-text-secondary/20" />
{/* Minimize */} {/* Minimize */}
<motion.button <button
onClick={() => appWindow.minimize()} onClick={handleMinimize}
className="flex size-8 items-center justify-center rounded-md text-pylon-text-secondary transition-colors hover:bg-pylon-accent/10 hover:text-pylon-text" onMouseDown={(e) => e.stopPropagation()}
whileHover={{ scale: 1.1 }} className="flex size-8 items-center justify-center rounded-md text-pylon-text-secondary transition-all hover:scale-110 hover:bg-pylon-accent/10 hover:text-pylon-text active:scale-90"
whileTap={{ scale: 0.9 }}
transition={springs.snappy}
aria-label="Minimize" aria-label="Minimize"
> >
<Minus className="size-4" /> <Minus className="size-4" />
</motion.button> </button>
{/* Maximize / Restore */} {/* Maximize / Restore */}
<motion.button <button
onClick={() => appWindow.toggleMaximize()} onClick={handleToggleMaximize}
className="flex size-8 items-center justify-center rounded-md text-pylon-text-secondary transition-colors hover:bg-pylon-accent/10 hover:text-pylon-text" onMouseDown={(e) => e.stopPropagation()}
whileHover={{ scale: 1.1 }} className="flex size-8 items-center justify-center rounded-md text-pylon-text-secondary transition-all hover:scale-110 hover:bg-pylon-accent/10 hover:text-pylon-text active:scale-90"
whileTap={{ scale: 0.9 }}
transition={springs.snappy}
aria-label={isMaximized ? "Restore" : "Maximize"} aria-label={isMaximized ? "Restore" : "Maximize"}
> >
{isMaximized ? ( {isMaximized ? (
@@ -55,19 +65,17 @@ export function WindowControls() {
) : ( ) : (
<Square className="size-3.5" /> <Square className="size-3.5" />
)} )}
</motion.button> </button>
{/* Close */} {/* Close */}
<motion.button <button
onClick={() => appWindow.close()} onClick={handleClose}
className="flex size-8 items-center justify-center rounded-md text-pylon-text-secondary transition-colors hover:bg-pylon-danger/15 hover:text-pylon-danger" onMouseDown={(e) => e.stopPropagation()}
whileHover={{ scale: 1.1 }} className="flex size-8 items-center justify-center rounded-md text-pylon-text-secondary transition-all hover:scale-110 hover:bg-pylon-danger/15 hover:text-pylon-danger active:scale-90"
whileTap={{ scale: 0.9 }}
transition={springs.snappy}
aria-label="Close" aria-label="Close"
> >
<X className="size-4" /> <X className="size-4" />
</motion.button> </button>
</div> </div>
); );
} }