diff --git a/.vscode/easycode.ignore b/.vscode/easycode.ignore new file mode 100644 index 0000000..84722bf --- /dev/null +++ b/.vscode/easycode.ignore @@ -0,0 +1,13 @@ +node_modules/ +dist/ +vendor/ +cache/ +.*/ +*.min.* +*.test.* +*.spec.* +*.bundle.* +*.bundle-min.* +*.*.js +*.*.ts +*.log \ No newline at end of file diff --git a/src/App.css b/src/App.css index d33bd8a..a247426 100644 --- a/src/App.css +++ b/src/App.css @@ -31,12 +31,13 @@ background-color: #20222C; height: 100%; width: 440px; - overflow-y: auto; + overflow: auto; user-select: none; } .sidebar-header{ position:sticky; top:0; + z-index: 100; } .sidebar-content { height: calc(100vh - 40px); @@ -48,9 +49,9 @@ flex-direction: column; padding: 20px; } - .buttonContainer { padding-top: 20px; + z-index: 100; } /* Additional styles for the tree nodes if needed */ .treeNode { @@ -75,11 +76,16 @@ h1 { font-size: 2rem; /* Add more Neumorphic styles here */ } +.treeContainer{ + position:relative; +} + .resize-handle { width: 10px; /* Make it a bit wider to ensure it's easily grabbable */ - height: 100vh; + height: 100%; position:fixed; + margin-left: 10px; background-color: #666; /* Use a darker color for visibility */ position: absolute; @@ -153,25 +159,51 @@ h1 { font-weight: 500; line-height: normal; transition: all 300ms ease-in-out; - } - .button-toggled{ - width: 40px; - height: 40px; - flex-shrink: 0; - border-radius: 5px; - border: 2px solid #FFD739; - background: linear-gradient(180deg, #B97208 0%, #FFD800 100%); - box-shadow: 8px 8px 15px 0px #14151D, -6px -6px 15px 0px #303240; - color: #20222C; - text-align: center; - font-family: Inter; - font-size: 15px; - font-style: normal; - font-weight: 500; - line-height: normal; - transition: all 300ms ease-in-out; + width: 40px; + height: 40px; + flex-shrink: 0; + border-radius: 5px; + border: 2px solid #FFD739; + background: linear-gradient(180deg, #B97208 0%, #FFD800 100%); + box-shadow: 8px 8px 15px 0px #14151D, -6px -6px 15px 0px #303240; + color: #20222C; + text-align: center; + font-family: Inter; + font-size: 15px; + font-style: normal; + font-weight: 500; + line-height: normal; + transition: all 300ms ease-in-out; +} +/* Styles for the scrollbar track (the part the scrollbar sits in) */ +::-webkit-scrollbar-track { + border-radius: 2px; + background: linear-gradient(90deg, #B97208 0%, #FFD800 100%); + box-shadow: 3px 3px 6px 0px #010102, -2px -2px 4px 0px #feffff; + + overflow: visible; + /* Track background color */ +} - } +/* Styles for the scrollbar thumb (the draggable part) */ +::-webkit-scrollbar-thumb { + background: linear-gradient(90deg, #555555 0%, #888888 100%); + cursor: pointer;/* Thumb background color */ +} + +/* Handle hover and active states for the scrollbar thumb */ +::-webkit-scrollbar-thumb:hover { + background: linear-gradient(90deg, #333333 0%, #555555 100%); /* Thumb background color on hover */ + cursor: grab !important; + /* Thumb background color */ +} +/* Styles for the scrollbar itself (including width and height) */ +::-webkit-scrollbar { + width: 10px; + /* Width of the vertical scrollbar */ + height: 10px; + /* Height of the horizontal scrollbar */ +} \ No newline at end of file diff --git a/src/App.js b/src/App.js index 58dee14..500524f 100644 --- a/src/App.js +++ b/src/App.js @@ -15,16 +15,35 @@ function log(...messages) { console.log(...messages); } } -// Helper function to sort items +// updated sort function that smart sorts numbers without needing leading zeros const sortItems = (items) => { return items.sort((a, b) => { if (a.is_dir && !b.is_dir) return -1; if (!a.is_dir && b.is_dir) return 1; + + // Extracting numbers from file names for comparison + const regex = /^\d+/; // Regex to match leading numbers in a file name + const numA = a.name.match(regex); + const numB = b.name.match(regex); + + // If both are numeric, compare as numbers + if (numA && numB) { + const numberA = parseInt(numA[0], 10); + const numberB = parseInt(numB[0], 10); + if (numberA !== numberB) { + return numberA - numberB; + } + } + + // If only one is numeric, or if numeric parts are equal, compare as strings return a.name.localeCompare(b.name); }); }; - -// TreeNode component +/** + * TreeNode component represents each node in a file tree structure. + * It is responsible for rendering individual tree nodes, handling node expansion/collapse, and node selection. + * + */ const TreeNode = React.forwardRef(({ item, level = 0, @@ -33,100 +52,99 @@ const TreeNode = React.forwardRef(({ onExpand, onCollapse, expandedPaths, - onFileSelected // This is a new prop to be passed from the App component + onFileSelected, + onHeightChange // This is a new prop to be passed from the App component }, ref) => { const [children, setChildren] = useState([]); const isExpanded = expandedPaths.includes(item.path); const itemRef = ref || React.createRef(); // Use the forwarded ref or create a new one + const indentSize = 20; + const nodeHeight = 18; // Assuming each node has a fixed height of 30px - - // Update your expand function - const expand = useCallback(async (item) => { - if (item.is_dir && !isExpanded) { - try { - const result = await invoke('read_dir', { path: item.path }); - const sortedChildren = sortItems(Array.isArray(result) ? result : JSON.parse(result)); - setChildren(sortedChildren); - onExpand(item.path); - } catch (error) { - console.error('Error reading directory:', error); - } - } - }, [item.is_dir, item.path, isExpanded, onExpand]); - - // Updated collapse function - const collapse = useCallback(() => { - if (isExpanded) { - setChildren([]); - onCollapse(item.path); - } - }, [item.path, isExpanded, onCollapse]); - - useEffect(() => { - const performExpand = async () => { - if (item.is_dir && isExpanded) { + // Callback function to be passed to TreeNode, which is called when a child node is expanded + const expand = useCallback(async (item) => { + if (item.is_dir && !isExpanded) { // Only expand if the node is a directory and not already expanded try { - const result = await invoke('read_dir', { path: item.path }); - const sortedChildren = sortItems(Array.isArray(result) ? result : JSON.parse(result)); - setChildren(sortedChildren); + const result = await invoke('read_dir', { path: item.path }); // Use Tauri's File System API to read the directory + const sortedChildren = sortItems(Array.isArray(result) ? result : JSON.parse(result)); // + setChildren(sortedChildren); // Update the children state + onExpand(item.path);// Call the onExpand callback function } catch (error) { console.error('Error reading directory:', error); } } - }; - - performExpand(); - }, [item, isExpanded, invoke]); - - useEffect(() => { - // Only scroll into view if the current item is selected and not already in view - if (isSelected(item.path) && !isItemInView(itemRef.current)) { - itemRef.current.scrollIntoView({ - behavior: 'smooth', - block: 'nearest' - }); - } - }, [isSelected, item.path, itemRef]); - - // Helper function to check if the element is in view - function isItemInView(element) { - if (!element) { - return false; - } - const rect = element.getBoundingClientRect(); - return ( - rect.top >= 0 && - rect.left >= 0 && - rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && - rect.right <= (window.innerWidth || document.documentElement.clientWidth) - ); - } + }, [item.is_dir, item.path, isExpanded, onExpand]); // Depend on the item path, isExpanded state, and onExpand callback function + // Callback function to be passed to TreeNode, which is called when a child node is collapsed + const collapse = useCallback(() => { + if (isExpanded) { // Only collapse if the node is already expanded + setChildren([]); // Clear the children state + onCollapse(item.path); // Call the onCollapse callback function + } + }, [item.path, isExpanded, onCollapse]); // Depend on the item path, isExpanded state, and onCollapse callback function + + // This effect is called when the children state changes and reports the height change to the parent + useEffect(() => { + const performExpand = async () => { // Define an async function to perform the expansion + if (item.is_dir && isExpanded) { // Only expand if the node is a directory and is expanded + try { + const result = await invoke('read_dir', { path: item.path });// Use Tauri's File System API to read the directory + const sortedChildren = sortItems(Array.isArray(result) ? result : JSON.parse(result));// Sort the children + setChildren(sortedChildren); // Update the children state + } catch (error) { + console.error('Error reading directory:', error); + } + } + }; + performExpand();// Call the async function to perform the expansion + }, [item, isExpanded, invoke]);// Depend on the item, isExpanded state, and invoke function + + // This effect is called when the children state changes and reports the height change to the parent + useEffect(() => { + // Only scroll into view if the current item is selected and not already in view + if (isSelected(item.path) && !isItemInView(itemRef.current)) { + itemRef.current.scrollIntoView({ // Scroll the item into view + behavior: 'smooth',// Use smooth scrolling + block: 'nearest',// Scroll to the bottom of the item + }); + } + }, [isSelected, item.path, itemRef]); - const handleToggle = (e) => { - e.stopPropagation(); - if (item.is_dir) { - if (isExpanded) { - collapse(); // Ensure this function is called when collapsing - } else { - expand(item).catch(console.error); + // Helper function to check if the element is in view + function isItemInView(element) { + if (!element) { + return false; } + const rect = element.getBoundingClientRect(); + return ( + rect.top >= 0 && + rect.left >= 0 && + rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && + rect.right <= (window.innerWidth || document.documentElement.clientWidth) + ); } - }; - - const handleClick = (e) => { - e.stopPropagation(); - onSelected(item.path); // Existing selection handling - // If the item is a file, trigger the event to load and play the animation - if (!item.is_dir) { - onFileSelected(item.path); // This is a new prop function to be passed from the App component - } - }; + const handleToggle = (e) => { + e.stopPropagation(); + if (item.is_dir) { + if (isExpanded) { + collapse(); // Ensure this function is called when collapsing + } else { + expand(item).catch(console.error); + } + } + }; + const handleClick = (e) => { + e.stopPropagation(); + onSelected(item.path); // Existing selection handling + // If the item is a file, trigger the event to load and play the animation + if (!item.is_dir) { + onFileSelected(item.path); // This is a new prop function to be passed from the App component + } + }; - const indentSize = 20; return ( <div> @@ -175,6 +193,7 @@ const TreeNode = React.forwardRef(({ // App component const App = () => { const [folderStructure, setFolderStructure] = useState([]); + const rootRef = useRef(null); // Create a ref for the root TreeNode if not already done const [sidebarWidth, setSidebarWidth] = useState(200); const [collapsed, setCollapsed] = useState(true); const [isResizing, setIsResizing] = useState(false); @@ -184,23 +203,11 @@ const App = () => { const [isTreeToggled, setIsTreeToggled] = useState(false); const [animationData, setAnimationData] = useState(null); const [isPlaying, setIsPlaying] = useState(false); - const [progress, setProgress] = useState(0); - const lottieRef = useRef(null); const [fileSize, setFileSize] = useState(null); - const resizeHandleRef = useRef(null); - const treeNodesContainerRef = useRef(null); + const [isFilePickerOpen, setIsFilePickerOpen] = useState(false); - useEffect(() => { - if (treeNodesContainerRef.current) { - const treeNodes = Array.from(treeNodesContainerRef.current.getElementsByClassName('treeChildren')); - const tallestNodeHeight = Math.max(...treeNodes.map(node => node.offsetHeight)); - if (resizeHandleRef.current) { - resizeHandleRef.current.style.height = `${tallestNodeHeight}px`; - } - } - - }, []); // Add dependencies if needed + useEffect(() => { const fetchDir = async () => { @@ -323,7 +330,7 @@ const App = () => { return ( <div className="App"> - <div className="sidebar" ref={treeNodesContainerRef} style={{ width: `${sidebarWidth}px` }}> + <div className="sidebar" style={{ width: `${sidebarWidth}px` }}> <div className="sidebar-header"> <button onClick={() => { @@ -335,9 +342,11 @@ const App = () => { <FontAwesomeIcon icon={faFolderTree} className="icon-large" /> </button> <button onClick={handleChooseFile} className="button"> - <FontAwesomeIcon icon={faFileImport} className = "icon-large"/> + <FontAwesomeIcon icon={faFileImport} className="icon-large" /> </button> </div> + <div className = "treeContainer"> + <div className="resize-handle" onMouseDown={startResizing} /> {!collapsed && folderStructure.map((item, index) => ( <TreeNode @@ -349,10 +358,10 @@ const App = () => { onExpand={handleExpand} onCollapse={handleCollapse} expandedPaths={expandedPaths} - onFileSelected={handleFileSelected} // Pass the new function as a prop to TreeNode + onFileSelected={handleFileSelected} /> ))} - <div className="resize-handle" ref={resizeHandleRef} onMouseDown={startResizing} /> + </div> </div> <div className="mainArea"> <div className="appName"> diff --git a/src/PlayerUI.css b/src/PlayerUI.css index 551191b..90468a5 100644 --- a/src/PlayerUI.css +++ b/src/PlayerUI.css @@ -1,5 +1,4 @@ /* PlayerUI.css */ - /** general modifiers */ .noSelect { -webkit-user-select: none; @@ -20,7 +19,6 @@ /* or use transform: scale(1.5); */ } /*******************************************/ - .playbackContainer { position:relative; margin-top: 20px; @@ -47,7 +45,6 @@ transform: translate(-50%, -50%); /* Adjust for centering */ z-index: 100; - background: #FFF; padding: 10px; border-radius: 10px; @@ -131,7 +128,6 @@ width:100%; justify-content: left; align-items: center; - } .progress-bar-container { position: relative; @@ -277,7 +273,6 @@ line-height: normal; transition: all 300ms ease-in-out; cursor: pointer; - } .pauseBtn:hover, .pauseBtn:focus { @@ -297,7 +292,6 @@ font-weight: 500; line-height: normal; transition: all 300ms ease-in-out; - } .modalBackdrop { position: absolute; @@ -311,7 +305,6 @@ justify-content: center; align-items: center; } - .markersModal { display: flex; flex-direction: column; @@ -324,28 +317,23 @@ color:#000000; cursor: pointer; } - .markersTable { width: 100%; border-collapse: collapse; border-radius: 10px; } - .markersTable th, .markersTable td { border: 1px solid #ddd; padding: 8px; text-align: left; } - .markersTable { background-color: #f2f2f2; } - .markersTable tr:hover { background-color: #ddd; } - .markersTable th { padding-top: 12px; padding-bottom: 12px;