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:
@@ -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",
|
||||||
|
|||||||
@@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user