Skip to content

Commit 46c8b09

Browse files
committed
add close window on Esc
1 parent 86ad301 commit 46c8b09

File tree

17 files changed

+276
-247
lines changed

17 files changed

+276
-247
lines changed

apps/backend/tauri/capabilities/main.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,9 @@
33
"identifier": "main",
44
"description": "Capability for the main window",
55
"windows": ["Tab_Clip_Manager_main", "Tab_Clip_Manager_settings"],
6-
"permissions": ["core:default", "core:event:allow-listen"]
6+
"permissions": [
7+
"core:default",
8+
"core:event:allow-listen",
9+
"core:window:allow-close"
10+
]
711
}

apps/frontend/src/hooks/useElementsStore.tsx

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,29 @@ import { useEffect } from 'react'
22
import { invoke } from '@tauri-apps/api/core'
33

44
type ActionsMap = {
5-
[key: string]: () => Promise<string[]>;
6-
};
5+
[key: string]: () => Promise<string[]>
6+
}
77

88
export default function useElementsStore() {
99
useEffect(() => {
10-
async function init() { }
10+
async function init() {}
1111
init()
1212
}, [])
1313

14-
1514
async function getData(): Promise<string[]> {
1615
const data = await invoke<string[]>('get_recent_clipboard_entries')
1716
return Array.isArray(data) ? data : []
1817
}
1918

20-
const getClipElements = async (
21-
tabName: string
22-
): Promise<string[]> => {
23-
19+
const getClipElements = async (tabName: string): Promise<string[]> => {
2420
const actions: ActionsMap = {
25-
recent: async () => await getData()
26-
};
27-
const defaultAction: () => Promise<string[]> = async () => await [];
28-
const result = (actions.hasOwnProperty(tabName) ? actions[tabName] : defaultAction)()
29-
return result
21+
recent: async () => await getData(),
3022
}
31-
return { getClipElements }
23+
const defaultAction: () => Promise<string[]> = async () => await []
24+
const result = (
25+
actions.hasOwnProperty(tabName) ? actions[tabName] : defaultAction
26+
)()
27+
return result
3228
}
29+
return { getClipElements }
30+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { useEffect } from 'react'
2+
import { getCurrentWindow } from '@tauri-apps/api/window'
3+
4+
export default function useEscapeKeyListener() {
5+
useEffect(() => {
6+
const handleKeyDown = async (event: KeyboardEvent) => {
7+
console.log(event.key)
8+
if (event.key === 'Escape') {
9+
await getCurrentWindow().close()
10+
}
11+
}
12+
13+
window.addEventListener('keydown', handleKeyDown)
14+
return () => window.removeEventListener('keydown', handleKeyDown)
15+
}, [])
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { useEffect } from 'react'
2+
import { listen } from '@tauri-apps/api/event'
3+
4+
export default function useWindowOpenListener(
5+
handleTabChange: (tab: string) => void
6+
) {
7+
useEffect(() => {
8+
const event = listen<string>('window_open', async () => {
9+
handleTabChange('recent')
10+
})
11+
12+
return () => {
13+
event.then((unlisten) => unlisten())
14+
}
15+
}, [handleTabChange])
16+
}

apps/frontend/src/style/App.css

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
@import "tailwindcss";
2-
@import "../../../../packages/ui/theme.css";
1+
@import 'tailwindcss';
2+
@import '../../../../packages/ui/theme.css';
33
@layer components {
44
.container {
55
box-shadow: var(--shadow);
@@ -15,11 +15,11 @@
1515
}
1616

1717
.clip-element {
18-
@apply display-flex-row transition-all rounded-sm bg-clip-background pt-2.5 px-6 pb-9 mt-0.5 mx-0.5 mb-3.5 hover:shadow-sm hover:shadow-slate-500;
18+
@apply display-flex-row transition-all rounded-sm bg-clip-background pt-2.5 px-6 pb-9 mt-0.5 mx-0.5 mb-3.5 hover:shadow-sm hover:shadow-slate-500 cursor-default select-none;
1919
/* INFO: min-width 300px, min-height 380px */
2020
/* INFO: max-width 310px, max-height 385px */
2121

22-
&> * {
22+
& > * {
2323
@apply bg-clip-background;
2424
}
2525
}

apps/frontend/src/windows/main/App.tsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import '@style/App.css'
22

3-
import { useEffect, useState } from 'react'
4-
import { listen } from '@tauri-apps/api/event'
3+
import { useState } from 'react'
4+
55
import { ClipTab } from '@repo/types'
66

77
import ClipElements from '@components/ClipElements'
88
import ClipTabs from '@components/ClipTabs'
99
import useElementsStore from '@hooks/useElementsStore'
10+
import useWindowOpenListener from '@hooks/useWindowOpenListener'
11+
import useEscapeKeyListener from '@hooks/useEscapeKeyListener'
1012

1113
import { Info } from 'lucide-react'
1214

@@ -27,24 +29,17 @@ const tabs: ClipTab[] = [
2729
export default function App() {
2830
const { getClipElements } = useElementsStore()
2931

30-
const [activeTab, setActiveTab] = useState<string>('')
32+
const [activeTab, setActiveTab] = useState<string>('recent')
3133
const [elements, setElements] = useState<string[]>([])
3234

3335
const handleTabChange = async (tabName: string) => {
3436
setActiveTab(tabName)
3537
const newElements = await getClipElements(tabName)
3638
setElements(newElements || [])
37-
3839
}
3940

40-
useEffect(() => {
41-
const event = listen<string>('window_open', async () => {
42-
handleTabChange(tabs[0].name)
43-
})
44-
return () => {
45-
event.then((unlisten) => unlisten())
46-
}
47-
})
41+
useWindowOpenListener(handleTabChange)
42+
useEscapeKeyListener()
4843

4944
return (
5045
<main className="container content-center text-center rounded-sm display-flex-col background-main">

packages/configs/vite.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/** @type {import('vite').UserConfig} */
22

33
import react from '@vitejs/plugin-react-swc'
4-
import tailwindcss from "@tailwindcss/vite"
4+
import tailwindcss from '@tailwindcss/vite'
55
import tsconfigPaths from 'vite-tsconfig-paths'
66

77
export default {

packages/ui/components/button.tsx

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,36 @@
1-
import * as React from "react"
2-
import { Slot } from "@radix-ui/react-slot"
3-
import { cva, type VariantProps } from "class-variance-authority"
1+
import * as React from 'react'
2+
import { Slot } from '@radix-ui/react-slot'
3+
import { cva, type VariantProps } from 'class-variance-authority'
44

5-
import { cn } from "@repo/ui/lib/utils"
5+
import { cn } from '@repo/ui/lib/utils'
66

77
const buttonVariants = cva(
88
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
99
{
1010
variants: {
1111
variant: {
1212
default:
13-
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
13+
'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
1414
destructive:
15-
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
15+
'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
1616
outline:
17-
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
17+
'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
1818
secondary:
19-
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
19+
'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
2020
ghost:
21-
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
22-
link: "text-primary underline-offset-4 hover:underline",
21+
'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
22+
link: 'text-primary underline-offset-4 hover:underline',
2323
},
2424
size: {
25-
default: "h-9 px-4 py-2 has-[>svg]:px-3",
26-
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
27-
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
28-
icon: "size-9",
25+
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
26+
sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
27+
lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
28+
icon: 'size-9',
2929
},
3030
},
3131
defaultVariants: {
32-
variant: "default",
33-
size: "default",
32+
variant: 'default',
33+
size: 'default',
3434
},
3535
}
3636
)
@@ -41,11 +41,11 @@ function Button({
4141
size,
4242
asChild = false,
4343
...props
44-
}: React.ComponentProps<"button"> &
44+
}: React.ComponentProps<'button'> &
4545
VariantProps<typeof buttonVariants> & {
4646
asChild?: boolean
4747
}) {
48-
const Comp = asChild ? Slot : "button"
48+
const Comp = asChild ? Slot : 'button'
4949

5050
return (
5151
<Comp

packages/ui/components/dropdown-menu.tsx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import * as React from "react"
2-
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
3-
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
1+
import * as React from 'react'
2+
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
3+
import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react'
44

5-
import { cn } from "@repo/ui/lib/utils"
5+
import { cn } from '@repo/ui/lib/utils'
66

77
function DropdownMenu({
88
...props
@@ -40,7 +40,7 @@ function DropdownMenuContent({
4040
data-slot="dropdown-menu-content"
4141
sideOffset={sideOffset}
4242
className={cn(
43-
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
43+
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md',
4444
className
4545
)}
4646
{...props}
@@ -60,11 +60,11 @@ function DropdownMenuGroup({
6060
function DropdownMenuItem({
6161
className,
6262
inset,
63-
variant = "default",
63+
variant = 'default',
6464
...props
6565
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
6666
inset?: boolean
67-
variant?: "default" | "destructive"
67+
variant?: 'default' | 'destructive'
6868
}) {
6969
return (
7070
<DropdownMenuPrimitive.Item
@@ -153,7 +153,7 @@ function DropdownMenuLabel({
153153
data-slot="dropdown-menu-label"
154154
data-inset={inset}
155155
className={cn(
156-
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
156+
'px-2 py-1.5 text-sm font-medium data-[inset]:pl-8',
157157
className
158158
)}
159159
{...props}
@@ -168,7 +168,7 @@ function DropdownMenuSeparator({
168168
return (
169169
<DropdownMenuPrimitive.Separator
170170
data-slot="dropdown-menu-separator"
171-
className={cn("bg-border -mx-1 my-1 h-px", className)}
171+
className={cn('bg-border -mx-1 my-1 h-px', className)}
172172
{...props}
173173
/>
174174
)
@@ -177,12 +177,12 @@ function DropdownMenuSeparator({
177177
function DropdownMenuShortcut({
178178
className,
179179
...props
180-
}: React.ComponentProps<"span">) {
180+
}: React.ComponentProps<'span'>) {
181181
return (
182182
<span
183183
data-slot="dropdown-menu-shortcut"
184184
className={cn(
185-
"text-muted-foreground ml-auto text-xs tracking-widest",
185+
'text-muted-foreground ml-auto text-xs tracking-widest',
186186
className
187187
)}
188188
{...props}
@@ -209,7 +209,7 @@ function DropdownMenuSubTrigger({
209209
data-slot="dropdown-menu-sub-trigger"
210210
data-inset={inset}
211211
className={cn(
212-
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8",
212+
'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8',
213213
className
214214
)}
215215
{...props}
@@ -228,7 +228,7 @@ function DropdownMenuSubContent({
228228
<DropdownMenuPrimitive.SubContent
229229
data-slot="dropdown-menu-sub-content"
230230
className={cn(
231-
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
231+
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg',
232232
className
233233
)}
234234
{...props}

packages/ui/components/input.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import * as React from "react"
1+
import * as React from 'react'
22

3-
import { cn } from "@repo/ui/lib/utils"
3+
import { cn } from '@repo/ui/lib/utils'
44

5-
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
5+
function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
66
return (
77
<input
88
type={type}
99
data-slot="input"
1010
className={cn(
11-
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
12-
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
13-
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
11+
'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
12+
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
13+
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
1414
className
1515
)}
1616
{...props}

0 commit comments

Comments
 (0)