Skip to content

Commit 8ac0f4d

Browse files
committed
feat: implement pagination for history view and add tooltips to sidebar and titlebar navigation.
1 parent 193743d commit 8ac0f4d

4 files changed

Lines changed: 191 additions & 132 deletions

File tree

src/components/Sidebar.tsx

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ArrowLeftRight, Puzzle, Clock, Settings, BarChart3 } from 'lucide-react'
2+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
23

34
export type Tab = 'convert' | 'plugins' | 'history' | 'analytics' | 'settings'
45

@@ -34,37 +35,57 @@ export default function AppSidebar({
3435
{/* Navigation links */}
3536
<div className="flex flex-col gap-1 px-2 mt-1">
3637
{mainNavItems.map(({ id, label, icon: Icon }) => (
37-
<button
38-
key={id}
39-
onClick={() => onTabChange(id)}
40-
className={`flex items-center h-9 rounded-lg text-sm font-medium transition-colors duration-150 cursor-pointer ${isSidebarExpanded ? 'gap-3 px-3' : 'justify-center px-0'
41-
} ${activeTab === id
42-
? 'bg-violet-600 text-white'
43-
: 'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800/60'
44-
}`}
45-
>
46-
<Icon size={18} className="flex-shrink-0" />
47-
<span className={isSidebarExpanded ? 'block' : 'hidden'}>{label}</span>
48-
</button>
38+
<TooltipProvider key={id} delayDuration={300}>
39+
<Tooltip>
40+
<TooltipTrigger asChild>
41+
<button
42+
onClick={() => onTabChange(id)}
43+
className={`flex items-center h-9 rounded-lg text-sm font-medium transition-colors duration-150 cursor-pointer ${isSidebarExpanded ? 'gap-3 px-3' : 'justify-center px-0'
44+
} ${activeTab === id
45+
? 'bg-violet-600 text-white'
46+
: 'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800/60'
47+
}`}
48+
>
49+
<Icon size={18} className="flex-shrink-0" />
50+
<span className={isSidebarExpanded ? 'block' : 'hidden'}>{label}</span>
51+
</button>
52+
</TooltipTrigger>
53+
{!isSidebarExpanded && (
54+
<TooltipContent side="right">
55+
{label}
56+
</TooltipContent>
57+
)}
58+
</Tooltip>
59+
</TooltipProvider>
4960
))}
5061
</div>
5162
</div>
5263

5364
{/* Bottom section: Settings */}
5465
<div className="flex flex-col gap-1 px-2 pb-3">
5566
{settingsNavItems.map(({ id, label, icon: Icon }) => (
56-
<button
57-
key={id}
58-
onClick={() => onTabChange(id)}
59-
className={`flex items-center h-9 rounded-lg text-sm font-medium transition-colors duration-150 cursor-pointer ${isSidebarExpanded ? 'gap-3 px-3' : 'justify-center px-0'
60-
} ${activeTab === id
61-
? 'bg-violet-600 text-white'
62-
: 'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800/60'
63-
}`}
64-
>
65-
<Icon size={18} className="flex-shrink-0" />
66-
<span className={isSidebarExpanded ? 'block' : 'hidden'}>{label}</span>
67-
</button>
67+
<TooltipProvider key={id} delayDuration={300}>
68+
<Tooltip>
69+
<TooltipTrigger asChild>
70+
<button
71+
onClick={() => onTabChange(id)}
72+
className={`flex items-center h-9 rounded-lg text-sm font-medium transition-colors duration-150 cursor-pointer ${isSidebarExpanded ? 'gap-3 px-3' : 'justify-center px-0'
73+
} ${activeTab === id
74+
? 'bg-violet-600 text-white'
75+
: 'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800/60'
76+
}`}
77+
>
78+
<Icon size={18} className="flex-shrink-0" />
79+
<span className={isSidebarExpanded ? 'block' : 'hidden'}>{label}</span>
80+
</button>
81+
</TooltipTrigger>
82+
{!isSidebarExpanded && (
83+
<TooltipContent side="right">
84+
{label}
85+
</TooltipContent>
86+
)}
87+
</Tooltip>
88+
</TooltipProvider>
6889
))}
6990
</div>
7091
</div>

src/components/Titlebar.tsx

Lines changed: 67 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useState, useEffect } from 'react'
22
import { Minus, Square, Copy, X, PanelLeft } from 'lucide-react'
3+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
34

45
interface TitlebarProps {
56
isSidebarExpanded: boolean
@@ -22,38 +23,76 @@ export default function Titlebar({ isSidebarExpanded, onToggleSidebar }: Titleba
2223
>
2324
{/* Left side: Sidebar toggle icon */}
2425
<div className="flex items-center pl-2" style={{ WebkitAppRegion: 'no-drag' } as React.CSSProperties}>
25-
<button
26-
onClick={onToggleSidebar}
27-
className="w-8 h-8 flex items-center justify-center rounded-lg text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 transition-colors duration-150"
28-
aria-label={isSidebarExpanded ? 'Collapse Sidebar' : 'Expand Sidebar'}
29-
>
30-
<PanelLeft size={16} />
31-
</button>
26+
<TooltipProvider delayDuration={300}>
27+
<Tooltip>
28+
<TooltipTrigger asChild>
29+
<button
30+
onClick={onToggleSidebar}
31+
className="w-8 h-8 flex items-center justify-center rounded-lg text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 transition-colors duration-150"
32+
aria-label={isSidebarExpanded ? 'Collapse Sidebar' : 'Expand Sidebar'}
33+
>
34+
<PanelLeft size={16} />
35+
</button>
36+
</TooltipTrigger>
37+
<TooltipContent side="bottom">
38+
{isSidebarExpanded ? 'Collapse Sidebar' : 'Expand Sidebar'}
39+
</TooltipContent>
40+
</Tooltip>
41+
</TooltipProvider>
3242
</div>
3343

3444
{/* Right side: Window controls */}
3545
<div className="flex h-full" style={{ WebkitAppRegion: 'no-drag' } as React.CSSProperties}>
36-
<button
37-
onClick={() => window.electronAPI.minimize()}
38-
className="w-12 h-full flex items-center justify-center text-zinc-500 hover:bg-zinc-800 hover:text-zinc-300 transition-colors duration-150"
39-
aria-label="Minimize"
40-
>
41-
<Minus size={14} />
42-
</button>
43-
<button
44-
onClick={() => window.electronAPI.maximize()}
45-
className="w-12 h-full flex items-center justify-center text-zinc-500 hover:bg-zinc-800 hover:text-zinc-300 transition-colors duration-150"
46-
aria-label={isMaximized ? 'Restore' : 'Maximize'}
47-
>
48-
{isMaximized ? <Copy size={11} /> : <Square size={11} />}
49-
</button>
50-
<button
51-
onClick={() => window.electronAPI.close()}
52-
className="w-12 h-full flex items-center justify-center text-zinc-500 hover:bg-red-600 hover:text-white transition-colors duration-150"
53-
aria-label="Close"
54-
>
55-
<X size={14} />
56-
</button>
46+
<TooltipProvider delayDuration={300}>
47+
<Tooltip>
48+
<TooltipTrigger asChild>
49+
<button
50+
onClick={() => window.electronAPI.minimize()}
51+
className="w-12 h-full flex items-center justify-center text-zinc-500 hover:bg-zinc-800 hover:text-zinc-300 transition-colors duration-150"
52+
aria-label="Minimize"
53+
>
54+
<Minus size={14} />
55+
</button>
56+
</TooltipTrigger>
57+
<TooltipContent side="bottom">
58+
Minimize
59+
</TooltipContent>
60+
</Tooltip>
61+
</TooltipProvider>
62+
63+
<TooltipProvider delayDuration={300}>
64+
<Tooltip>
65+
<TooltipTrigger asChild>
66+
<button
67+
onClick={() => window.electronAPI.maximize()}
68+
className="w-12 h-full flex items-center justify-center text-zinc-500 hover:bg-zinc-800 hover:text-zinc-300 transition-colors duration-150"
69+
aria-label={isMaximized ? 'Restore' : 'Maximize'}
70+
>
71+
{isMaximized ? <Copy size={11} /> : <Square size={11} />}
72+
</button>
73+
</TooltipTrigger>
74+
<TooltipContent side="bottom">
75+
{isMaximized ? 'Restore' : 'Maximize'}
76+
</TooltipContent>
77+
</Tooltip>
78+
</TooltipProvider>
79+
80+
<TooltipProvider delayDuration={300}>
81+
<Tooltip>
82+
<TooltipTrigger asChild>
83+
<button
84+
onClick={() => window.electronAPI.close()}
85+
className="w-12 h-full flex items-center justify-center text-zinc-500 hover:bg-red-600 hover:text-white transition-colors duration-150"
86+
aria-label="Close"
87+
>
88+
<X size={14} />
89+
</button>
90+
</TooltipTrigger>
91+
<TooltipContent side="bottom">
92+
Close
93+
</TooltipContent>
94+
</Tooltip>
95+
</TooltipProvider>
5796
</div>
5897
</div>
5998
)

src/components/ui/tooltip.tsx

Lines changed: 22 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,32 @@
11
"use client"
22

33
import * as React from "react"
4-
import { Tooltip as TooltipPrimitive } from "radix-ui"
4+
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
55

66
import { cn } from "@/lib/utils"
77

8-
function TooltipProvider({
9-
delayDuration = 0,
10-
...props
11-
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
12-
return (
13-
<TooltipPrimitive.Provider
14-
data-slot="tooltip-provider"
15-
delayDuration={delayDuration}
16-
{...props}
17-
/>
18-
)
19-
}
8+
const TooltipProvider = TooltipPrimitive.Provider
209

21-
function Tooltip({
22-
...props
23-
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
24-
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
25-
}
10+
const Tooltip = TooltipPrimitive.Root
2611

27-
function TooltipTrigger({
28-
...props
29-
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
30-
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
31-
}
12+
const TooltipTrigger = TooltipPrimitive.Trigger
3213

33-
function TooltipContent({
34-
className,
35-
sideOffset = 0,
36-
children,
37-
...props
38-
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
39-
return (
40-
<TooltipPrimitive.Portal>
41-
<TooltipPrimitive.Content
42-
data-slot="tooltip-content"
43-
sideOffset={sideOffset}
44-
className={cn(
45-
"z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 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 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
46-
className
47-
)}
48-
{...props}
49-
>
50-
{children}
51-
<TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
52-
</TooltipPrimitive.Content>
53-
</TooltipPrimitive.Portal>
54-
)
55-
}
14+
const TooltipContent = React.forwardRef<
15+
React.ElementRef<typeof TooltipPrimitive.Content>,
16+
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
17+
>(({ className, sideOffset = 4, ...props }, ref) => (
18+
<TooltipPrimitive.Portal>
19+
<TooltipPrimitive.Content
20+
ref={ref}
21+
sideOffset={sideOffset}
22+
className={cn(
23+
"z-50 overflow-hidden rounded-md bg-zinc-900 px-3 py-1.5 text-xs text-zinc-50 animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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",
24+
className
25+
)}
26+
{...props}
27+
/>
28+
</TooltipPrimitive.Portal>
29+
))
30+
TooltipContent.displayName = TooltipPrimitive.Content.displayName
5631

57-
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger }
32+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }

0 commit comments

Comments
 (0)