diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx
index a05aab9..c5ab944 100644
--- a/packages/frontend/src/App.tsx
+++ b/packages/frontend/src/App.tsx
@@ -1,5 +1,5 @@
-import { Box, Button, Flex, Heading, Input, Spinner, Text, useStyleConfig, useBreakpointValue, IconButton, Drawer, DrawerBody, DrawerHeader, DrawerOverlay, DrawerContent, DrawerCloseButton, useDisclosure, Container, Menu, MenuButton, MenuList, MenuItem } from "@chakra-ui/react";
-import { ChevronLeftIcon, ChevronRightIcon, HamburgerIcon, ViewIcon, SettingsIcon } from "@chakra-ui/icons";
+import { Box, Button, Flex, Heading, Input, Spinner, Text, useStyleConfig, useBreakpointValue, IconButton, Drawer, DrawerBody, DrawerHeader, DrawerOverlay, DrawerContent, DrawerCloseButton, useDisclosure, Container, Menu, MenuButton, MenuList, MenuItem, useToken } from "@chakra-ui/react";
+import { ChevronLeftIcon, ChevronRightIcon, HamburgerIcon, ViewIcon, SettingsIcon, DragHandleIcon } from "@chakra-ui/icons";
import { useState, useRef, useEffect } from "react";
import { useNavigate, useLocation } from "react-router-dom";
import { SongList } from "./components/SongList";
@@ -63,6 +63,35 @@ const StyledFileInput = ({ isMobile = false }) => {
);
};
+const ResizeHandle = ({ onMouseDown }: { onMouseDown: (e: React.MouseEvent) => void }) => (
+
+
+
+);
+
export default function RekordboxReader() {
const { songs, playlists, setPlaylists, loading } = useXmlParser();
const [selectedSong, setSelectedSong] = useState(null);
@@ -73,6 +102,9 @@ export default function RekordboxReader() {
const { isOpen, onOpen, onClose } = useDisclosure();
const isMobile = useBreakpointValue({ base: true, md: false });
+ const [sidebarWidth, setSidebarWidth] = useState(400);
+ const [isResizing, setIsResizing] = useState(false);
+ const resizeRef = useRef<{ startX: number; startWidth: number } | null>(null);
// Get the current playlist from URL or default to "All Songs"
const currentPlaylist = location.pathname === "/"
@@ -161,6 +193,39 @@ export default function RekordboxReader() {
playlists.find((p) => p.name === currentPlaylist)?.tracks.includes(song.id)
);
+ const handleResizeStart = (e: React.MouseEvent) => {
+ e.preventDefault();
+ setIsResizing(true);
+ resizeRef.current = {
+ startX: e.pageX,
+ startWidth: sidebarWidth,
+ };
+ };
+
+ const handleResizeMove = (e: MouseEvent) => {
+ if (!isResizing || !resizeRef.current) return;
+
+ const delta = e.pageX - resizeRef.current.startX;
+ const newWidth = Math.max(300, Math.min(800, resizeRef.current.startWidth + delta));
+ setSidebarWidth(newWidth);
+ };
+
+ const handleResizeEnd = () => {
+ setIsResizing(false);
+ resizeRef.current = null;
+ };
+
+ useEffect(() => {
+ if (isResizing) {
+ window.addEventListener('mousemove', handleResizeMove);
+ window.addEventListener('mouseup', handleResizeEnd);
+ }
+ return () => {
+ window.removeEventListener('mousemove', handleResizeMove);
+ window.removeEventListener('mouseup', handleResizeEnd);
+ };
+ }, [isResizing]);
+
if (loading) {
return (
@@ -198,6 +263,7 @@ export default function RekordboxReader() {
overflow="hidden"
margin={0}
padding={0}
+ userSelect={isResizing ? 'none' : 'auto'}
>
{/* Header */}
@@ -275,8 +341,9 @@ export default function RekordboxReader() {
{/* Sidebar - Desktop */}
{!isMobile && (
{playlistManager}
+
)}