From 7b7e8cd6268fd817f63b9d22fd25491237f162c6 Mon Sep 17 00:00:00 2001 From: David Wagih Date: Tue, 8 Aug 2023 11:19:53 +0300 Subject: [PATCH 01/15] add Notebook component to explore folders better --- stopes/ui/seamlisten/react_app/src/App.tsx | 46 ++++++++ .../react_app/src/components/Notebook.css | 67 +++++++++++ .../react_app/src/components/Notebook.tsx | 109 ++++++++++++++++++ 3 files changed, 222 insertions(+) create mode 100644 stopes/ui/seamlisten/react_app/src/components/Notebook.css create mode 100644 stopes/ui/seamlisten/react_app/src/components/Notebook.tsx diff --git a/stopes/ui/seamlisten/react_app/src/App.tsx b/stopes/ui/seamlisten/react_app/src/App.tsx index 5054857..edfc55c 100644 --- a/stopes/ui/seamlisten/react_app/src/App.tsx +++ b/stopes/ui/seamlisten/react_app/src/App.tsx @@ -10,8 +10,53 @@ import Navbar from "react-bootstrap/Navbar"; import { Outlet, NavLink } from "react-router-dom"; import "bootstrap/dist/css/bootstrap.min.css"; +import Notebook, { Folder } from "./components/Notebook"; export function App(): JSX.Element { + const folders : Folder[] = [ + { + name: "folder1", + type: "folder", + content: [ + { + name: "file1", + type: "file", + content: "file1 content" + }, + { + name: "file2", + type: "file", + content: "file2 content" + }, + { + name: "file3", + type: "file", + content: "file3 content" + } + ] + }, + { + name: "folder2", + type: "folder", + content: [ + { + name: "file1", + type: "file", + content: "file1 content" + }, + { + name: "file2", + type: "file", + content: "file2 content" + }, + { + name: "file3", + type: "file", + content: "file3 content" + } + ] + } + ]; return (
@@ -34,6 +79,7 @@ export function App(): JSX.Element {
+
); } diff --git a/stopes/ui/seamlisten/react_app/src/components/Notebook.css b/stopes/ui/seamlisten/react_app/src/components/Notebook.css new file mode 100644 index 0000000..bca5b90 --- /dev/null +++ b/stopes/ui/seamlisten/react_app/src/components/Notebook.css @@ -0,0 +1,67 @@ +/* Notebook container */ +.notebook-container { + display: flex; + border: 1px solid #ccc; + border-radius: 5px; + overflow: hidden; + } + + /* Folder navigation panel */ + .folder-navigation { + flex: 1; + padding: 10px; + background-color: #f5f5f5; + } + + /* Folder item */ + .folder-item { + padding: 8px; + cursor: pointer; + border-radius: 3px; + margin-bottom: 5px; + transition: background-color 0.2s; + } + + .folder-item:hover { + background-color: #e0e0e0; + } + + .selected { + background-color: #2196f3; + color: white; + font-weight: bold; + } + + /* Notebook content panel */ + .notebook-content { + flex: 3; + padding: 10px; + border-left: 1px solid #ccc; + } + + /* File viewer */ + .file-viewer { + padding: 10px; + background-color: #fff; + border: 1px solid #ccc; + border-radius: 5px; + min-height: 200px; + overflow: auto; + } + + /* File content */ + pre { + white-space: pre-wrap; + font-family: monospace; + } + + /* Default text style */ + div { + font-family: Arial, sans-serif; + } + + /* Centered text */ + .text-center { + text-align: center; + } + \ No newline at end of file diff --git a/stopes/ui/seamlisten/react_app/src/components/Notebook.tsx b/stopes/ui/seamlisten/react_app/src/components/Notebook.tsx new file mode 100644 index 0000000..f35765b --- /dev/null +++ b/stopes/ui/seamlisten/react_app/src/components/Notebook.tsx @@ -0,0 +1,109 @@ +import { useState } from 'react'; +import './Notebook.css'; // Import your custom styling + +export interface Folder { + name: string; + type: 'folder'; + content: File[]; +} + +interface File { + name: string; + type: 'file'; + content: string; +} + +interface Props { + folders: Folder[]; +} + +const Notebook: React.FC = ({ folders }) => { + const [currentFolder, setCurrentFolder] = useState(null); + const [selectedFile, setSelectedFile] = useState(null); + + const handleFolderClick = (folder: Folder) => { + setCurrentFolder(folder); + setSelectedFile(null); + }; + + const handleFileClick = (file: File) => { + setSelectedFile(file); + }; + + return ( +
+ +
+ {currentFolder ? ( + + ) : ( +
Select a folder to view its contents
+ )} + +
+
+ ); +}; + +interface FolderNavigationProps { + folders: Folder[]; + currentFolder: Folder | null; + onSelectFolder: (folder: Folder) => void; +} + +const FolderNavigation: React.FC = ({ + folders, + currentFolder, + onSelectFolder, +}) => ( +
+ {folders.map((folder) => ( +
onSelectFolder(folder)} + className={`folder-item ${currentFolder === folder ? 'selected' : ''}`} + > + {folder.name} +
+ ))} +
+); + +interface FileViewerProps { + file: File | null; +} + +const FileViewer: React.FC = ({ file }) => ( +
+ {file ? ( +
{file.content}
+ ) : ( +
Select a file to view its content
+ )} +
+); + +interface FolderViewProps { + folder: Folder; + onSelectFile: (file: File) => void; +} + +const FolderView: React.FC = ({ folder, onSelectFile }) => ( +
+ {folder.content.map((file) => ( +
onSelectFile(file)} + className="file-item" + > + {file.name} +
+ ))} +
+); + +export default Notebook; From 3c6453f10b4676545d190d9c5e2648adad3ca939 Mon Sep 17 00:00:00 2001 From: David Wagih Date: Mon, 21 Aug 2023 22:19:35 +0300 Subject: [PATCH 02/15] implement the process folders logic --- .../ui/seamlisten/backend/app/fileviewer.py | 33 +++++++++++++++++-- .../src/common/fetchers/mining_result.ts | 28 ++++++++++++++-- .../react_app/src/components/FileExplorer.tsx | 16 ++++++++- 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/stopes/ui/seamlisten/backend/app/fileviewer.py b/stopes/ui/seamlisten/backend/app/fileviewer.py index 8a69eb8..925826c 100644 --- a/stopes/ui/seamlisten/backend/app/fileviewer.py +++ b/stopes/ui/seamlisten/backend/app/fileviewer.py @@ -9,6 +9,7 @@ import typing as tp import zipfile from pathlib import Path +import os import torchaudio from fastapi import APIRouter @@ -86,6 +87,7 @@ def open_segment_tsv(file: str, start_idx: int, end_idx: int) -> tp.List[LineRes with stutils.open(file) as f: for line in itertools.islice(f, start_idx, end_idx): results.append(auto_parse_line(line.strip())) + print("result", results) return results @@ -131,7 +133,14 @@ def get_annotations( @router.post("/general/") async def general_query(query: DefaultQuery) -> Response: query_path = query.gz_path.strip() - print(query_path.split(" ")) + # Convert the path to an absolute path + query_path = str(Path(os.path.expanduser(query_path)).resolve()) + # Check if the path is a directory + print(query_path) + if os.path.isdir(query_path): + result_data = gather_folder_contents(query_path) + return result_data + if query_path.endswith(".tsv.gz") or query_path.endswith(".zip"): return get_annotations( AnnotationQuery( @@ -150,7 +159,6 @@ async def general_query(query: DefaultQuery) -> Response: AudioQuery(sampling="ms", path=path, start=int(start), end=int(end)) ) return result - else: raise HTTPException( status_code=500, @@ -161,3 +169,24 @@ async def general_query(query: DefaultQuery) -> Response: a line with audio file, start and end timestamps """, ) + + +import os + + +def gather_folder_contents(folder_path, max_depth=3): + folder_contents = [] + for root, dirs, files in os.walk(folder_path, topdown=True): + depth = root[len(folder_path) + len(os.path.sep) :].count(os.path.sep) + if depth <= max_depth: + folder_data = { + "folder": root, + "subfolders": dirs, + "audio_files": [ + file for file in files if file.endswith((".wav", ".ms")) + ], + } + folder_contents.append(folder_data) + if depth == max_depth: + del dirs[:] # Stop os.walk from going deeper + return folder_contents diff --git a/stopes/ui/seamlisten/react_app/src/common/fetchers/mining_result.ts b/stopes/ui/seamlisten/react_app/src/common/fetchers/mining_result.ts index afd775e..d9ba68a 100644 --- a/stopes/ui/seamlisten/react_app/src/common/fetchers/mining_result.ts +++ b/stopes/ui/seamlisten/react_app/src/common/fetchers/mining_result.ts @@ -7,10 +7,11 @@ import { config } from "../constants/config"; import { AnnotationQuery, LineResult } from "../types/api"; -const url = config.host + ":" + config.port + config.annotations_route; +const processFilesUrl = config.host + ":" + config.port + config.annotations_route; +const processFoldersUrl = config.host + ":" + config.port + config.general_route; async function fetchFiles(data: AnnotationQuery): Promise { - let response = await fetch(url, { + let response = await fetch(processFilesUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), @@ -21,4 +22,25 @@ async function fetchFiles(data: AnnotationQuery): Promise { return response.json(); } -export default fetchFiles; +async function processFolder(folderPath: string): Promise { + const folderQuery = { + gz_path: folderPath, + start_idx: 0, // Provide appropriate values + end_idx: 0, // Provide appropriate values + }; + + const response = await fetch(processFoldersUrl, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(folderQuery), + }); + + if (!response.ok) { + throw response; + } + + return response.json(); +} + +export { fetchFiles, processFolder }; + diff --git a/stopes/ui/seamlisten/react_app/src/components/FileExplorer.tsx b/stopes/ui/seamlisten/react_app/src/components/FileExplorer.tsx index 54ba208..b963784 100644 --- a/stopes/ui/seamlisten/react_app/src/components/FileExplorer.tsx +++ b/stopes/ui/seamlisten/react_app/src/components/FileExplorer.tsx @@ -20,7 +20,7 @@ import { import WaveSurferComponent from "../common/components/audio/WaveSurfer"; import InnerScale from "../common/components/spinners/spinner"; import { config } from "../common/constants/config"; -import fetchFiles from "../common/fetchers/mining_result"; +import {fetchFiles , processFolder} from "../common/fetchers/mining_result"; import { LineResult } from "../common/types/api"; import Help from "./fileviewer/FileExplorerHelp"; import Table from "./fileviewer/Table"; @@ -68,6 +68,7 @@ export async function loader({ request }): Promise { files: [], audioBlob: undefined, error: null, + folderContents: [], // Add folderContents field }; try { @@ -84,6 +85,12 @@ export async function loader({ request }): Promise { toRet.files = files; return toRet; } + else if (isDirectoryPath(filename)){ + const folderContents = await processFolder(filename); + toRet.folderContents = folderContents; + console.log("folderContents", folderContents); + return toRet; + } const audioResult = await text_to_audio(filename, 1); if (audioResult) { @@ -98,6 +105,13 @@ export async function loader({ request }): Promise { return toRet; } +function isDirectoryPath(path) { + // Implement a logic to check if the provided path is a directory + // You might use regular expressions or other methods to check. + // For example, if the path ends with "/", it's likely a directory. + return path.endsWith("/"); +} + function Error({ error }) { const msg = error.data ? error.data.detail From 27eea00eb321491980bbe466e5f0fff37e68a7b1 Mon Sep 17 00:00:00 2001 From: David Wagih Date: Tue, 22 Aug 2023 16:41:39 +0300 Subject: [PATCH 03/15] resolve the comments and fix the response structure of processing a folder --- .../ui/seamlisten/backend/app/fileviewer.py | 45 ++++++++++--------- .../react_app/src/common/constants/config.js | 2 +- .../src/components/fileviewer/table/Row.tsx | 4 ++ 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/stopes/ui/seamlisten/backend/app/fileviewer.py b/stopes/ui/seamlisten/backend/app/fileviewer.py index 925826c..3ed2cf4 100644 --- a/stopes/ui/seamlisten/backend/app/fileviewer.py +++ b/stopes/ui/seamlisten/backend/app/fileviewer.py @@ -10,12 +10,10 @@ import zipfile from pathlib import Path import os - import torchaudio from fastapi import APIRouter from fastapi.exceptions import HTTPException from fastapi.responses import JSONResponse, Response - import stopes.core.utils as stutils import stopes.modules.speech.utils as stopes_speech from stopes.modules.speech.utils import LineResult, auto_parse_line @@ -134,10 +132,10 @@ def get_annotations( async def general_query(query: DefaultQuery) -> Response: query_path = query.gz_path.strip() # Convert the path to an absolute path - query_path = str(Path(os.path.expanduser(query_path)).resolve()) + query_path = Path(os.path.expanduser(query_path)).resolve() # Check if the path is a directory print(query_path) - if os.path.isdir(query_path): + if query_path.is_dir(): result_data = gather_folder_contents(query_path) return result_data @@ -171,22 +169,27 @@ async def general_query(query: DefaultQuery) -> Response: ) -import os +def gather_folder_contents(folder_path, max_depth=5): + def gather_contents_recursive(folder_path, current_depth): + if current_depth > max_depth: + return {"folder": folder_path, "subfolders": [], "audio_files": []} + + subfolders = [] + audio_files = [] + + for entry in os.listdir(folder_path): + entry_path = os.path.join(folder_path, entry) + if os.path.isdir(entry_path): + subfolders.append( + gather_contents_recursive(entry_path, current_depth + 1) + ) + elif entry.endswith((".wav", ".ms")): + audio_files.append(entry) + return { + "folder": folder_path, + "subfolders": subfolders, + "audio_files": audio_files, + } -def gather_folder_contents(folder_path, max_depth=3): - folder_contents = [] - for root, dirs, files in os.walk(folder_path, topdown=True): - depth = root[len(folder_path) + len(os.path.sep) :].count(os.path.sep) - if depth <= max_depth: - folder_data = { - "folder": root, - "subfolders": dirs, - "audio_files": [ - file for file in files if file.endswith((".wav", ".ms")) - ], - } - folder_contents.append(folder_data) - if depth == max_depth: - del dirs[:] # Stop os.walk from going deeper - return folder_contents + return gather_contents_recursive(folder_path, 1) diff --git a/stopes/ui/seamlisten/react_app/src/common/constants/config.js b/stopes/ui/seamlisten/react_app/src/common/constants/config.js index 7158d14..b246a7e 100644 --- a/stopes/ui/seamlisten/react_app/src/common/constants/config.js +++ b/stopes/ui/seamlisten/react_app/src/common/constants/config.js @@ -4,7 +4,7 @@ // This source code is licensed under the license found in the // LICENSE file in the root directory of this source tree. -const BACKEND_PORT = process.env.NODE_ENV === "development" ? "8000" : "8800"; +const BACKEND_PORT = process.env.NODE_ENV === "development" ? "8080" : "8800"; export const config = { host: "http://localhost", diff --git a/stopes/ui/seamlisten/react_app/src/components/fileviewer/table/Row.tsx b/stopes/ui/seamlisten/react_app/src/components/fileviewer/table/Row.tsx index c297fec..73e6b4d 100644 --- a/stopes/ui/seamlisten/react_app/src/components/fileviewer/table/Row.tsx +++ b/stopes/ui/seamlisten/react_app/src/components/fileviewer/table/Row.tsx @@ -20,6 +20,8 @@ function CellRender({ if (typeof object === "number") { return {object.toString()}; } + console.log("object kind", object.kind) + console.log("object", object) switch (object.kind) { case "audio": return ( @@ -47,6 +49,8 @@ function CellRender({ ); case "text": return {object.content}; + default: + return {object}; } } From 5ff11cc98fac88254ab7b00a57e018f3b832a1e0 Mon Sep 17 00:00:00 2001 From: David Wagih Date: Tue, 22 Aug 2023 16:45:44 +0300 Subject: [PATCH 04/15] using path Apis instead of os.path --- stopes/ui/seamlisten/backend/app/fileviewer.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/stopes/ui/seamlisten/backend/app/fileviewer.py b/stopes/ui/seamlisten/backend/app/fileviewer.py index 3ed2cf4..1269a37 100644 --- a/stopes/ui/seamlisten/backend/app/fileviewer.py +++ b/stopes/ui/seamlisten/backend/app/fileviewer.py @@ -177,19 +177,16 @@ def gather_contents_recursive(folder_path, current_depth): subfolders = [] audio_files = [] - for entry in os.listdir(folder_path): - entry_path = os.path.join(folder_path, entry) - if os.path.isdir(entry_path): - subfolders.append( - gather_contents_recursive(entry_path, current_depth + 1) - ) - elif entry.endswith((".wav", ".ms")): - audio_files.append(entry) + for entry in folder_path.iterdir(): + if entry.is_dir(): + subfolders.append(gather_contents_recursive(entry, current_depth + 1)) + elif entry.suffix in {".wav", ".ms"}: + audio_files.append(entry.name) return { - "folder": folder_path, + "folder": str(folder_path), "subfolders": subfolders, "audio_files": audio_files, } - return gather_contents_recursive(folder_path, 1) + return gather_contents_recursive(Path(folder_path), 1) From e999e599f31115a0c165bf5275a7e252b3013bac Mon Sep 17 00:00:00 2001 From: David Wagih <78867146+david-wagih@users.noreply.github.com> Date: Tue, 29 Aug 2023 19:19:51 +0300 Subject: [PATCH 05/15] Update stopes/ui/seamlisten/backend/app/fileviewer.py Co-authored-by: Pierre Andrews <628467+Mortimerp9@users.noreply.github.com> --- stopes/ui/seamlisten/backend/app/fileviewer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stopes/ui/seamlisten/backend/app/fileviewer.py b/stopes/ui/seamlisten/backend/app/fileviewer.py index 1269a37..d4a79d0 100644 --- a/stopes/ui/seamlisten/backend/app/fileviewer.py +++ b/stopes/ui/seamlisten/backend/app/fileviewer.py @@ -132,7 +132,7 @@ def get_annotations( async def general_query(query: DefaultQuery) -> Response: query_path = query.gz_path.strip() # Convert the path to an absolute path - query_path = Path(os.path.expanduser(query_path)).resolve() + query_path = Path(query_path). expanduser().resolve() # Check if the path is a directory print(query_path) if query_path.is_dir(): From 7b8385e0b74e6a22c0791c99caba0e5bc3490881 Mon Sep 17 00:00:00 2001 From: David Wagih Date: Tue, 29 Aug 2023 19:31:28 +0300 Subject: [PATCH 06/15] fix the comments needed --- .../ui/seamlisten/backend/app/fileviewer.py | 25 +++++----- stopes/ui/seamlisten/react_app/src/App.tsx | 47 +------------------ 2 files changed, 15 insertions(+), 57 deletions(-) diff --git a/stopes/ui/seamlisten/backend/app/fileviewer.py b/stopes/ui/seamlisten/backend/app/fileviewer.py index d4a79d0..b977616 100644 --- a/stopes/ui/seamlisten/backend/app/fileviewer.py +++ b/stopes/ui/seamlisten/backend/app/fileviewer.py @@ -85,7 +85,6 @@ def open_segment_tsv(file: str, start_idx: int, end_idx: int) -> tp.List[LineRes with stutils.open(file) as f: for line in itertools.islice(f, start_idx, end_idx): results.append(auto_parse_line(line.strip())) - print("result", results) return results @@ -109,7 +108,6 @@ def get_annotations( query: AnnotationQuery, ) -> tp.List[LineResult]: path = query.gz_path.strip() - print(path, query.start_idx, query.end_idx) if path.endswith(".tsv.gz") or path.endswith(".tsv"): try: return open_segment_tsv(path, query.start_idx, query.end_idx) @@ -130,28 +128,27 @@ def get_annotations( @router.post("/general/") async def general_query(query: DefaultQuery) -> Response: - query_path = query.gz_path.strip() + query_path_str = query.gz_path.strip() # Convert the path to an absolute path - query_path = Path(query_path). expanduser().resolve() + query_path = Path.expanduser(query_path_str).resolve() # Check if the path is a directory - print(query_path) if query_path.is_dir(): result_data = gather_folder_contents(query_path) return result_data - if query_path.endswith(".tsv.gz") or query_path.endswith(".zip"): + if query_path.suffixes(".tsv.gz") or query_path.suffixes(".zip"): return get_annotations( AnnotationQuery( gz_path=query.gz_path, start_idx=query.start_idx, end_idx=query.end_idx ) ) - elif len(query_path.split(" ")) == 3: + elif len(query_path_str.split(" ")) == 3: path, start, end = query.gz_path.split(" ") result = await serve_file( AudioQuery(sampling="wav", path=path, start=int(start), end=int(end)) ) return result - elif len(query_path.split("|")) == 3: + elif len(query_path_str.split("|")) == 3: path, start, end = query.gz_path.split("|") result = await serve_file( AudioQuery(sampling="ms", path=path, start=int(start), end=int(end)) @@ -172,7 +169,12 @@ async def general_query(query: DefaultQuery) -> Response: def gather_folder_contents(folder_path, max_depth=5): def gather_contents_recursive(folder_path, current_depth): if current_depth > max_depth: - return {"folder": folder_path, "subfolders": [], "audio_files": []} + return { + "folder": str(folder_path), + "subfolders": None, + "audio_files": None, + "unexplored": True, + } subfolders = [] audio_files = [] @@ -185,8 +187,9 @@ def gather_contents_recursive(folder_path, current_depth): return { "folder": str(folder_path), - "subfolders": subfolders, - "audio_files": audio_files, + "subfolders": subfolders if subfolders else None, + "audio_files": audio_files if audio_files else None, + "unexplored": False, } return gather_contents_recursive(Path(folder_path), 1) diff --git a/stopes/ui/seamlisten/react_app/src/App.tsx b/stopes/ui/seamlisten/react_app/src/App.tsx index edfc55c..5e44a55 100644 --- a/stopes/ui/seamlisten/react_app/src/App.tsx +++ b/stopes/ui/seamlisten/react_app/src/App.tsx @@ -10,53 +10,9 @@ import Navbar from "react-bootstrap/Navbar"; import { Outlet, NavLink } from "react-router-dom"; import "bootstrap/dist/css/bootstrap.min.css"; -import Notebook, { Folder } from "./components/Notebook"; export function App(): JSX.Element { - const folders : Folder[] = [ - { - name: "folder1", - type: "folder", - content: [ - { - name: "file1", - type: "file", - content: "file1 content" - }, - { - name: "file2", - type: "file", - content: "file2 content" - }, - { - name: "file3", - type: "file", - content: "file3 content" - } - ] - }, - { - name: "folder2", - type: "folder", - content: [ - { - name: "file1", - type: "file", - content: "file1 content" - }, - { - name: "file2", - type: "file", - content: "file2 content" - }, - { - name: "file3", - type: "file", - content: "file3 content" - } - ] - } - ]; + return (
@@ -79,7 +35,6 @@ export function App(): JSX.Element {
-
); } From 00c5618175987904bf9f5d319e6ccbb80e18a4d2 Mon Sep 17 00:00:00 2001 From: David Wagih Date: Tue, 29 Aug 2023 19:52:04 +0300 Subject: [PATCH 07/15] fix the explore folder feat problem and make sure it used relative paths --- .../ui/seamlisten/backend/app/fileviewer.py | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/stopes/ui/seamlisten/backend/app/fileviewer.py b/stopes/ui/seamlisten/backend/app/fileviewer.py index b977616..16f20f0 100644 --- a/stopes/ui/seamlisten/backend/app/fileviewer.py +++ b/stopes/ui/seamlisten/backend/app/fileviewer.py @@ -104,24 +104,22 @@ def open_zip_file(file: str, start_idx: int, end_idx: int) -> tp.List[LineResult @router.post("/annotations/") -def get_annotations( - query: AnnotationQuery, -) -> tp.List[LineResult]: +def get_annotations(query: AnnotationQuery) -> tp.List[LineResult]: path = query.gz_path.strip() - if path.endswith(".tsv.gz") or path.endswith(".tsv"): + resolved_path = Path(path).expanduser().resolve() + + if resolved_path.suffixes in ([".tsv.gz"], [".tsv"]): try: - return open_segment_tsv(path, query.start_idx, query.end_idx) + return open_segment_tsv(str(resolved_path), query.start_idx, query.end_idx) except FileNotFoundError: raise HTTPException( status_code=404, - detail=""" - File not found - """, + detail="File not found", ) - elif path.endswith(".zip"): - if not Path(path).exists(): + elif resolved_path.suffixes == [".zip"]: + if not resolved_path.exists(): raise HTTPException(status_code=404, detail="File not found") - return open_zip_file(path, query.start_idx, query.end_idx) + return open_zip_file(str(resolved_path), query.start_idx, query.end_idx) else: raise ValueError("Unknown format") @@ -129,9 +127,12 @@ def get_annotations( @router.post("/general/") async def general_query(query: DefaultQuery) -> Response: query_path_str = query.gz_path.strip() - # Convert the path to an absolute path - query_path = Path.expanduser(query_path_str).resolve() - # Check if the path is a directory + query_path = Path(query_path_str).expanduser().resolve() + print(query_path) + + if not query_path.exists(): + raise HTTPException(status_code=400, detail="Path does not exist") + if query_path.is_dir(): result_data = gather_folder_contents(query_path) return result_data From b8a804dfcd9fdb53e96f5eebc1a549fb969c6f0f Mon Sep 17 00:00:00 2001 From: David Wagih Date: Wed, 6 Sep 2023 02:21:28 +0300 Subject: [PATCH 08/15] fix all issues related to tree view and adding download button --- .../ui/seamlisten/react_app/package-lock.json | 912 +++++++++++++++++- stopes/ui/seamlisten/react_app/package.json | 5 + .../react_app/src/components/FileExplorer.tsx | 11 +- .../src/components/FolderTreeView.tsx | 158 +++ .../src/components/fileviewer/table/Row.tsx | 2 - 5 files changed, 1042 insertions(+), 46 deletions(-) create mode 100644 stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx diff --git a/stopes/ui/seamlisten/react_app/package-lock.json b/stopes/ui/seamlisten/react_app/package-lock.json index 2549572..31ac6fb 100644 --- a/stopes/ui/seamlisten/react_app/package-lock.json +++ b/stopes/ui/seamlisten/react_app/package-lock.json @@ -8,7 +8,12 @@ "name": "react_app", "version": "0.1.0", "dependencies": { + "@emotion/react": "^11.11.1", + "@emotion/styled": "^11.11.0", "@fortawesome/fontawesome-svg-core": "^6.2.1", + "@mui/icons-material": "^5.14.7", + "@mui/lab": "^5.0.0-alpha.142", + "@mui/material": "^5.14.7", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -1807,11 +1812,11 @@ } }, "node_modules/@babel/runtime": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", - "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz", + "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==", "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" @@ -1829,6 +1834,11 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/runtime/node_modules/regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + }, "node_modules/@babel/template": { "version": "7.18.10", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", @@ -2151,18 +2161,139 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "dependencies": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, "node_modules/@emotion/is-prop-valid": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz", - "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", "dependencies": { - "@emotion/memoize": "^0.8.0" + "@emotion/memoize": "^0.8.1" } }, "node_modules/@emotion/memoize": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", - "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/react": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz", + "integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.2", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", + "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", + "dependencies": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/serialize/node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "node_modules/@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + }, + "node_modules/@emotion/styled": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz", + "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/is-prop-valid": "^1.2.1", + "@emotion/serialize": "^1.1.2", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, "node_modules/@emotion/stylis": { "version": "0.8.5", @@ -2174,6 +2305,24 @@ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + }, "node_modules/@eslint/eslintrc": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", @@ -2237,6 +2386,40 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@floating-ui/core": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.4.1.tgz", + "integrity": "sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==", + "dependencies": { + "@floating-ui/utils": "^0.1.1" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.1.tgz", + "integrity": "sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw==", + "dependencies": { + "@floating-ui/core": "^1.4.1", + "@floating-ui/utils": "^0.1.1" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.2.tgz", + "integrity": "sha512-5qhlDvjaLmAst/rKb3VdlCinwTF4EYMiVxuuc/HVUjs46W0zgtbMmAZ1UTsDrRTxRmUEzl92mOtWbeeXL26lSQ==", + "dependencies": { + "@floating-ui/dom": "^1.5.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.1.tgz", + "integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==" + }, "node_modules/@fortawesome/fontawesome-common-types": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.2.1.tgz", @@ -3039,6 +3222,309 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "node_modules/@mui/base": { + "version": "5.0.0-beta.13", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.13.tgz", + "integrity": "sha512-uC0l97pBspfDAp+iz2cJq8YZ8Sd9i73V77+WzUiOAckIVEyCm5dyVDZCCO2/phmzckVEeZCGcytybkjMQuhPQw==", + "dependencies": { + "@babel/runtime": "^7.22.10", + "@emotion/is-prop-valid": "^1.2.1", + "@floating-ui/react-dom": "^2.0.1", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.14.7", + "@popperjs/core": "^2.11.8", + "clsx": "^2.0.0", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/base/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "5.14.7", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.7.tgz", + "integrity": "sha512-sCWTUNElBPgB30iLvWe3PU7SIlTKZNf6/E/sko85iHVeHCM6WPkDw+y89CrZYjhFNmPqt2fIQM/pZu+rP2lFLA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + } + }, + "node_modules/@mui/icons-material": { + "version": "5.14.7", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.14.7.tgz", + "integrity": "sha512-mWp4DwMa8c1Gx9yOEtPgxM4b+e6hAbtZyzfSubdBwrnEE6G5D2rbAJ5MB+If6kfI48JaYaJ5j8+zAdmZLuZc0A==", + "dependencies": { + "@babel/runtime": "^7.22.10" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@mui/material": "^5.0.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/lab": { + "version": "5.0.0-alpha.142", + "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.142.tgz", + "integrity": "sha512-JDrT5G3QBZ0nzkKMFWzJY5KN8WcyDx4p7qOjg6hs7yKLq90VSdsqIOmyhvWDxJR7zPNQjo0WRYBAaRaQ5FlGxg==", + "dependencies": { + "@babel/runtime": "^7.22.10", + "@mui/base": "5.0.0-beta.13", + "@mui/system": "^5.14.7", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.14.7", + "clsx": "^2.0.0", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material": "^5.0.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/lab/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/@mui/material": { + "version": "5.14.7", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.7.tgz", + "integrity": "sha512-jIZj9F7zMv6IlyaYDVv5M2Kp20jIX8c0kzuwteySHS/A0IvPVyomQEPtWc51MCbpDNCqzwoZUp3rQtA2lI8k7A==", + "dependencies": { + "@babel/runtime": "^7.22.10", + "@mui/base": "5.0.0-beta.13", + "@mui/core-downloads-tracker": "^5.14.7", + "@mui/system": "^5.14.7", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.14.7", + "@types/react-transition-group": "^4.4.6", + "clsx": "^2.0.0", + "csstype": "^3.1.2", + "prop-types": "^15.8.1", + "react-is": "^18.2.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/@mui/private-theming": { + "version": "5.14.7", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.7.tgz", + "integrity": "sha512-Y86+hmDnJab2Ka42PgxKpK3oL7EiacbeeX3X/lG9LGO0wSc45wZjHeTfIlVSkkUCkexiMKEJp5NlSjZhr27NRQ==", + "dependencies": { + "@babel/runtime": "^7.22.10", + "@mui/utils": "^5.14.7", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "5.14.7", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.7.tgz", + "integrity": "sha512-hKBETEDsIAkL8/mBwPiQj/vw28OeIhMXC3Tvj4J2bb9snxAKpiZioR1PwqP+6P41twsC/GKBd0Vr9oaWYaHuMg==", + "dependencies": { + "@babel/runtime": "^7.22.10", + "@emotion/cache": "^11.11.0", + "csstype": "^3.1.2", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "5.14.7", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.7.tgz", + "integrity": "sha512-jeZtHglc+Pi6qjGoopT6O4RqYXVBMqHVOsjMGP0hxGSSPm1T4gsAu7jU8eqGx9YwwjvvJ0eotTjFqw7iJ6qE2Q==", + "dependencies": { + "@babel/runtime": "^7.22.10", + "@mui/private-theming": "^5.14.7", + "@mui/styled-engine": "^5.14.7", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.14.7", + "clsx": "^2.0.0", + "csstype": "^3.1.2", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.4.tgz", + "integrity": "sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA==", + "peerDependencies": { + "@types/react": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "5.14.7", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.7.tgz", + "integrity": "sha512-RtheP/aBoPogVdi8vj8Vo2IFnRa4mZVmnD0RGlVZ49yF60rZs+xP4/KbpIrTr83xVs34QmHQ2aQ+IX7I0a0dDw==", + "dependencies": { + "@babel/runtime": "^7.22.10", + "@types/prop-types": "^15.7.5", + "@types/react-is": "^18.2.1", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0" + } + }, + "node_modules/@mui/utils/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -3149,9 +3635,9 @@ } }, "node_modules/@popperjs/core": { - "version": "2.11.6", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", - "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==", + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -4214,10 +4700,18 @@ "@types/react": "*" } }, + "node_modules/@types/react-is": { + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.2.1.tgz", + "integrity": "sha512-wyUkmaaSZEzFZivD8F2ftSyAfk6L+DfFliVj/mYdOXbVjRcS87fQJLTnhk6dRZPuJjI+9g6RZJO4PNCngUrmyw==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==", + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", + "integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==", "dependencies": { "@types/react": "*" } @@ -5899,6 +6393,14 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -6574,9 +7076,9 @@ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" }, "node_modules/csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -8305,6 +8807,11 @@ "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -16033,6 +16540,11 @@ "postcss": "^8.2.15" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -18953,11 +19465,18 @@ } }, "@babel/runtime": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", - "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz", + "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==", "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.14.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + } } }, "@babel/runtime-corejs3": { @@ -19137,18 +19656,117 @@ "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", "requires": {} }, + "@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "requires": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" + } + } + }, + "@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "requires": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, "@emotion/is-prop-valid": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz", - "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", "requires": { - "@emotion/memoize": "^0.8.0" + "@emotion/memoize": "^0.8.1" } }, "@emotion/memoize": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", - "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "@emotion/react": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz", + "integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==", + "requires": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.2", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + } + }, + "@emotion/serialize": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", + "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", + "requires": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + }, + "dependencies": { + "@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + } + } + }, + "@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + }, + "@emotion/styled": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz", + "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==", + "requires": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/is-prop-valid": "^1.2.1", + "@emotion/serialize": "^1.1.2", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1" + } }, "@emotion/stylis": { "version": "0.8.5", @@ -19160,6 +19778,22 @@ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" }, + "@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "requires": {} + }, + "@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + }, + "@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + }, "@eslint/eslintrc": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", @@ -19204,6 +19838,36 @@ } } }, + "@floating-ui/core": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.4.1.tgz", + "integrity": "sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==", + "requires": { + "@floating-ui/utils": "^0.1.1" + } + }, + "@floating-ui/dom": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.1.tgz", + "integrity": "sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw==", + "requires": { + "@floating-ui/core": "^1.4.1", + "@floating-ui/utils": "^0.1.1" + } + }, + "@floating-ui/react-dom": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.2.tgz", + "integrity": "sha512-5qhlDvjaLmAst/rKb3VdlCinwTF4EYMiVxuuc/HVUjs46W0zgtbMmAZ1UTsDrRTxRmUEzl92mOtWbeeXL26lSQ==", + "requires": { + "@floating-ui/dom": "^1.5.1" + } + }, + "@floating-ui/utils": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.1.tgz", + "integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==" + }, "@fortawesome/fontawesome-common-types": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.2.1.tgz", @@ -19798,6 +20462,151 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "@mui/base": { + "version": "5.0.0-beta.13", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.13.tgz", + "integrity": "sha512-uC0l97pBspfDAp+iz2cJq8YZ8Sd9i73V77+WzUiOAckIVEyCm5dyVDZCCO2/phmzckVEeZCGcytybkjMQuhPQw==", + "requires": { + "@babel/runtime": "^7.22.10", + "@emotion/is-prop-valid": "^1.2.1", + "@floating-ui/react-dom": "^2.0.1", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.14.7", + "@popperjs/core": "^2.11.8", + "clsx": "^2.0.0", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "dependencies": { + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + } + } + }, + "@mui/core-downloads-tracker": { + "version": "5.14.7", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.7.tgz", + "integrity": "sha512-sCWTUNElBPgB30iLvWe3PU7SIlTKZNf6/E/sko85iHVeHCM6WPkDw+y89CrZYjhFNmPqt2fIQM/pZu+rP2lFLA==" + }, + "@mui/icons-material": { + "version": "5.14.7", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.14.7.tgz", + "integrity": "sha512-mWp4DwMa8c1Gx9yOEtPgxM4b+e6hAbtZyzfSubdBwrnEE6G5D2rbAJ5MB+If6kfI48JaYaJ5j8+zAdmZLuZc0A==", + "requires": { + "@babel/runtime": "^7.22.10" + } + }, + "@mui/lab": { + "version": "5.0.0-alpha.142", + "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.142.tgz", + "integrity": "sha512-JDrT5G3QBZ0nzkKMFWzJY5KN8WcyDx4p7qOjg6hs7yKLq90VSdsqIOmyhvWDxJR7zPNQjo0WRYBAaRaQ5FlGxg==", + "requires": { + "@babel/runtime": "^7.22.10", + "@mui/base": "5.0.0-beta.13", + "@mui/system": "^5.14.7", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.14.7", + "clsx": "^2.0.0", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "dependencies": { + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + } + } + }, + "@mui/material": { + "version": "5.14.7", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.7.tgz", + "integrity": "sha512-jIZj9F7zMv6IlyaYDVv5M2Kp20jIX8c0kzuwteySHS/A0IvPVyomQEPtWc51MCbpDNCqzwoZUp3rQtA2lI8k7A==", + "requires": { + "@babel/runtime": "^7.22.10", + "@mui/base": "5.0.0-beta.13", + "@mui/core-downloads-tracker": "^5.14.7", + "@mui/system": "^5.14.7", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.14.7", + "@types/react-transition-group": "^4.4.6", + "clsx": "^2.0.0", + "csstype": "^3.1.2", + "prop-types": "^15.8.1", + "react-is": "^18.2.0", + "react-transition-group": "^4.4.5" + }, + "dependencies": { + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + } + } + }, + "@mui/private-theming": { + "version": "5.14.7", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.7.tgz", + "integrity": "sha512-Y86+hmDnJab2Ka42PgxKpK3oL7EiacbeeX3X/lG9LGO0wSc45wZjHeTfIlVSkkUCkexiMKEJp5NlSjZhr27NRQ==", + "requires": { + "@babel/runtime": "^7.22.10", + "@mui/utils": "^5.14.7", + "prop-types": "^15.8.1" + } + }, + "@mui/styled-engine": { + "version": "5.14.7", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.7.tgz", + "integrity": "sha512-hKBETEDsIAkL8/mBwPiQj/vw28OeIhMXC3Tvj4J2bb9snxAKpiZioR1PwqP+6P41twsC/GKBd0Vr9oaWYaHuMg==", + "requires": { + "@babel/runtime": "^7.22.10", + "@emotion/cache": "^11.11.0", + "csstype": "^3.1.2", + "prop-types": "^15.8.1" + } + }, + "@mui/system": { + "version": "5.14.7", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.7.tgz", + "integrity": "sha512-jeZtHglc+Pi6qjGoopT6O4RqYXVBMqHVOsjMGP0hxGSSPm1T4gsAu7jU8eqGx9YwwjvvJ0eotTjFqw7iJ6qE2Q==", + "requires": { + "@babel/runtime": "^7.22.10", + "@mui/private-theming": "^5.14.7", + "@mui/styled-engine": "^5.14.7", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.14.7", + "clsx": "^2.0.0", + "csstype": "^3.1.2", + "prop-types": "^15.8.1" + } + }, + "@mui/types": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.4.tgz", + "integrity": "sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA==", + "requires": {} + }, + "@mui/utils": { + "version": "5.14.7", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.7.tgz", + "integrity": "sha512-RtheP/aBoPogVdi8vj8Vo2IFnRa4mZVmnD0RGlVZ49yF60rZs+xP4/KbpIrTr83xVs34QmHQ2aQ+IX7I0a0dDw==", + "requires": { + "@babel/runtime": "^7.22.10", + "@types/prop-types": "^15.7.5", + "@types/react-is": "^18.2.1", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "dependencies": { + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + } + } + }, "@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -19862,9 +20671,9 @@ } }, "@popperjs/core": { - "version": "2.11.6", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", - "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==" + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==" }, "@react-aria/ssr": { "version": "3.3.0", @@ -20670,10 +21479,18 @@ "@types/react": "*" } }, + "@types/react-is": { + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.2.1.tgz", + "integrity": "sha512-wyUkmaaSZEzFZivD8F2ftSyAfk6L+DfFliVj/mYdOXbVjRcS87fQJLTnhk6dRZPuJjI+9g6RZJO4PNCngUrmyw==", + "requires": { + "@types/react": "*" + } + }, "@types/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==", + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", + "integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==", "requires": { "@types/react": "*" } @@ -21906,6 +22723,11 @@ "wrap-ansi": "^7.0.0" } }, + "clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==" + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -22385,9 +23207,9 @@ } }, "csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "damerau-levenshtein": { "version": "1.0.8", @@ -23689,6 +24511,11 @@ "pkg-dir": "^4.1.0" } }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -29070,6 +29897,11 @@ "postcss-selector-parser": "^6.0.4" } }, + "stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", diff --git a/stopes/ui/seamlisten/react_app/package.json b/stopes/ui/seamlisten/react_app/package.json index b3c7304..7d0ba9c 100644 --- a/stopes/ui/seamlisten/react_app/package.json +++ b/stopes/ui/seamlisten/react_app/package.json @@ -3,7 +3,12 @@ "version": "0.1.0", "private": true, "dependencies": { + "@emotion/react": "^11.11.1", + "@emotion/styled": "^11.11.0", "@fortawesome/fontawesome-svg-core": "^6.2.1", + "@mui/icons-material": "^5.14.7", + "@mui/lab": "^5.0.0-alpha.142", + "@mui/material": "^5.14.7", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", diff --git a/stopes/ui/seamlisten/react_app/src/components/FileExplorer.tsx b/stopes/ui/seamlisten/react_app/src/components/FileExplorer.tsx index b963784..0a02527 100644 --- a/stopes/ui/seamlisten/react_app/src/components/FileExplorer.tsx +++ b/stopes/ui/seamlisten/react_app/src/components/FileExplorer.tsx @@ -26,6 +26,7 @@ import Help from "./fileviewer/FileExplorerHelp"; import Table from "./fileviewer/Table"; import { text_to_audio } from "../common/components/audio/audioquery_constructor"; +import FolderTreeView from "./FolderTreeView"; const FILENAME_PARAM = "file"; const PAGENUMBER_PARAM = "page"; @@ -38,6 +39,7 @@ type LoaderReturn = { files: LineResult[]; audioBlob: Blob; error: any; + folderContents: any; }; function parseParams(searchParams) { @@ -59,7 +61,6 @@ function parseLocation(location: Location) { export async function loader({ request }): Promise { const url = new URL(request.url); - const { filename, numberLines, pageNumber } = parseParams(url.searchParams); const toRet = { filename, @@ -88,10 +89,8 @@ export async function loader({ request }): Promise { else if (isDirectoryPath(filename)){ const folderContents = await processFolder(filename); toRet.folderContents = folderContents; - console.log("folderContents", folderContents); return toRet; } - const audioResult = await text_to_audio(filename, 1); if (audioResult) { toRet.audioBlob = audioResult; @@ -105,6 +104,7 @@ export async function loader({ request }): Promise { return toRet; } +// todo: needs to be enhanced function isDirectoryPath(path) { // Implement a logic to check if the provided path is a directory // You might use regular expressions or other methods to check. @@ -137,7 +137,7 @@ function useFileNavigate() { const Files = (): JSX.Element => { const [displayHelper, setDisplayHelper] = useState(false); const navigate = useFileNavigate(); - let { filename, pageNumber, numberLines, files, audioBlob, error } = + let { filename, pageNumber, numberLines, files, audioBlob, error, folderContents } = useLoaderData() as LoaderReturn; const [newFilename, setNewFilename] = useState( filename || config.default_path @@ -233,6 +233,9 @@ const Files = (): JSX.Element => { + {loading ? ( ) : error ? ( diff --git a/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx b/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx new file mode 100644 index 0000000..0fee7fc --- /dev/null +++ b/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx @@ -0,0 +1,158 @@ +import { useState } from "react"; +import TreeView from "@mui/lab/TreeView"; +import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; +import ChevronRightIcon from "@mui/icons-material/ChevronRight"; +import TreeItem from "@mui/lab/TreeItem"; +import { processFolder } from "../common/fetchers/mining_result"; +import PlayArea from "../common/components/audio/PlayArea"; +import { text_to_audio } from "../common/components/audio/audioquery_constructor"; +import WaveSurferComponent from "../common/components/audio/WaveSurfer"; + +const FolderTreeView = ({ folderContents }) => { + const [expandedFolders, setExpandedFolders] = useState(new Set()); + const [selectedAudioFile, setSelectedAudioFile] = useState(null); + const [audioBlob, setAudioBlob] = useState(null); + + const handleNodeToggle = (event, nodeIds) => { + const clickedNodeId = nodeIds[0]; + + if (expandedFolders.has(clickedNodeId)) { + // Node is expanded, collapse it + expandedFolders.delete(clickedNodeId); + } else { + // Node is collapsed, expand it and fetch its contents + expandedFolders.add(clickedNodeId); + fetchFolderContents(clickedNodeId); + } + + setExpandedFolders(new Set(expandedFolders)); + }; + + const handleAudioFileClick = async(audioFile) => { + setSelectedAudioFile(audioFile); + const audioResult = await text_to_audio(audioFile, 1); + if (audioResult) { + setAudioBlob(audioResult); + } + }; + + const fetchFolderContents = async (folderPath) => { + try { + // Make a request to fetch the contents of the clicked folderPath + const folderContents = await processFolder(folderPath); + + // Handle the fetched data and update your state accordingly + // For example, you can update your state with the new folder contents + // and re-render the component. + } catch (error) { + console.error("Error fetching folder contents:", error); + } + }; + + const DownloadButton = ({ blob, filename }) => { + const download = () => { + const url = window.URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = filename; + document.body.appendChild(a); + a.click(); + a.remove(); + }; + + return ( + + ); + } + + + + + const renderFolderTree = (folderData) => { + if (!folderData) { + return null; + } + + return ( + } + defaultExpandIcon={} + sx={{ + height: 240, + flexGrow: 1, + maxWidth: 400, + overflowY: "auto", + display: "flex", + }} + > +
+ handleNodeToggle(event, [folderData.folder])} + > + {folderData.subfolders && folderData.subfolders.length > 0 && ( + folderData.subfolders.map((subfolder) => renderFolderTree(subfolder)) + )} + {folderData.audio_files && folderData.audio_files.length > 0 && ( + folderData.audio_files.map((audioFile) => ( + handleAudioFileClick( + `${folderData.folder}/${audioFile}` + )} + /> + )) + )} + +
+
+ ); + }; + + return ( +
+ {renderFolderTree(folderContents)} + {!!audioBlob && ( + <> + {}} + focusedRowID={""} + currentRowID={""} + currentPlayingID={""} + currentPlayingIDHandler={() => {}} + /> + + + )} +
+ ); +}; + +export default FolderTreeView; + + diff --git a/stopes/ui/seamlisten/react_app/src/components/fileviewer/table/Row.tsx b/stopes/ui/seamlisten/react_app/src/components/fileviewer/table/Row.tsx index 73e6b4d..5f7ee57 100644 --- a/stopes/ui/seamlisten/react_app/src/components/fileviewer/table/Row.tsx +++ b/stopes/ui/seamlisten/react_app/src/components/fileviewer/table/Row.tsx @@ -20,8 +20,6 @@ function CellRender({ if (typeof object === "number") { return {object.toString()}; } - console.log("object kind", object.kind) - console.log("object", object) switch (object.kind) { case "audio": return ( From 9be834dc8be0e40f1133a34ab5b9d0dc13e1bf55 Mon Sep 17 00:00:00 2001 From: David Wagih Date: Wed, 6 Sep 2023 02:22:09 +0300 Subject: [PATCH 09/15] fix errors in fileviewer file --- stopes/ui/seamlisten/backend/app/fileviewer.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/stopes/ui/seamlisten/backend/app/fileviewer.py b/stopes/ui/seamlisten/backend/app/fileviewer.py index 16f20f0..035fecf 100644 --- a/stopes/ui/seamlisten/backend/app/fileviewer.py +++ b/stopes/ui/seamlisten/backend/app/fileviewer.py @@ -46,7 +46,6 @@ def _read_audio(path: str, frame_offset: int, num_frames: int) -> tp.Any: return torchaudio.load( f, frame_offset=frame_offset, num_frames=num_frames ) - return torchaudio.load(path, frame_offset=frame_offset, num_frames=num_frames) @@ -108,7 +107,7 @@ def get_annotations(query: AnnotationQuery) -> tp.List[LineResult]: path = query.gz_path.strip() resolved_path = Path(path).expanduser().resolve() - if resolved_path.suffixes in ([".tsv.gz"], [".tsv"]): + if resolved_path.suffixes in ([".gz"], [".tsv"]): try: return open_segment_tsv(str(resolved_path), query.start_idx, query.end_idx) except FileNotFoundError: @@ -128,7 +127,6 @@ def get_annotations(query: AnnotationQuery) -> tp.List[LineResult]: async def general_query(query: DefaultQuery) -> Response: query_path_str = query.gz_path.strip() query_path = Path(query_path_str).expanduser().resolve() - print(query_path) if not query_path.exists(): raise HTTPException(status_code=400, detail="Path does not exist") @@ -137,7 +135,7 @@ async def general_query(query: DefaultQuery) -> Response: result_data = gather_folder_contents(query_path) return result_data - if query_path.suffixes(".tsv.gz") or query_path.suffixes(".zip"): + if query_path.suffixes in ([".gz"], [".tsv"], [".zip"]): return get_annotations( AnnotationQuery( gz_path=query.gz_path, start_idx=query.start_idx, end_idx=query.end_idx From b827766af611d88f3a69bf65a419cbf06fb27209 Mon Sep 17 00:00:00 2001 From: David Wagih Date: Wed, 6 Sep 2023 07:38:36 +0300 Subject: [PATCH 10/15] remove unused imports --- .../react_app/src/components/FolderTreeView.tsx | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx b/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx index 0fee7fc..239f65d 100644 --- a/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx +++ b/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx @@ -3,8 +3,6 @@ import TreeView from "@mui/lab/TreeView"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import ChevronRightIcon from "@mui/icons-material/ChevronRight"; import TreeItem from "@mui/lab/TreeItem"; -import { processFolder } from "../common/fetchers/mining_result"; -import PlayArea from "../common/components/audio/PlayArea"; import { text_to_audio } from "../common/components/audio/audioquery_constructor"; import WaveSurferComponent from "../common/components/audio/WaveSurfer"; @@ -22,7 +20,6 @@ const FolderTreeView = ({ folderContents }) => { } else { // Node is collapsed, expand it and fetch its contents expandedFolders.add(clickedNodeId); - fetchFolderContents(clickedNodeId); } setExpandedFolders(new Set(expandedFolders)); @@ -36,18 +33,7 @@ const FolderTreeView = ({ folderContents }) => { } }; - const fetchFolderContents = async (folderPath) => { - try { - // Make a request to fetch the contents of the clicked folderPath - const folderContents = await processFolder(folderPath); - // Handle the fetched data and update your state accordingly - // For example, you can update your state with the new folder contents - // and re-render the component. - } catch (error) { - console.error("Error fetching folder contents:", error); - } - }; const DownloadButton = ({ blob, filename }) => { const download = () => { From 3089b2143055b103360abc592fd2a3829ea86807 Mon Sep 17 00:00:00 2001 From: David Wagih Date: Wed, 6 Sep 2023 07:46:26 +0300 Subject: [PATCH 11/15] fix the styling of the tree view --- .../ui/seamlisten/react_app/package-lock.json | 147 ++++++++++++++++++ stopes/ui/seamlisten/react_app/package.json | 2 + .../src/components/FolderTreeView.tsx | 107 ++++++++++--- stopes/ui/seamlisten/react_app/tsconfig.json | 3 +- 4 files changed, 240 insertions(+), 19 deletions(-) diff --git a/stopes/ui/seamlisten/react_app/package-lock.json b/stopes/ui/seamlisten/react_app/package-lock.json index 31ac6fb..d0652a4 100644 --- a/stopes/ui/seamlisten/react_app/package-lock.json +++ b/stopes/ui/seamlisten/react_app/package-lock.json @@ -14,6 +14,8 @@ "@mui/icons-material": "^5.14.7", "@mui/lab": "^5.0.0-alpha.142", "@mui/material": "^5.14.7", + "@mui/x-tree-view": "^6.0.0-alpha.1", + "@react-spring/web": "^9.7.3", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -3525,6 +3527,35 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, + "node_modules/@mui/x-tree-view": { + "version": "6.0.0-alpha.1", + "resolved": "https://registry.npmjs.org/@mui/x-tree-view/-/x-tree-view-6.0.0-alpha.1.tgz", + "integrity": "sha512-JUG3HmBrmGEALbCFg1b+i7h726e1dWYZs4db3syO1j+Q++E3nbvE4Lehp5yGTFm+8esH0Tny50tuJaa4WX6VSA==", + "dependencies": { + "@babel/runtime": "^7.22.6", + "@mui/utils": "^5.14.3", + "@types/react-transition-group": "^4.4.6", + "clsx": "^2.0.0", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/base": "^5.0.0-alpha.87", + "@mui/material": "^5.8.6", + "@mui/system": "^5.8.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + } + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -3654,6 +3685,66 @@ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" } }, + "node_modules/@react-spring/animated": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.3.tgz", + "integrity": "sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==", + "dependencies": { + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/core": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.3.tgz", + "integrity": "sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ==", + "dependencies": { + "@react-spring/animated": "~9.7.3", + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-spring/donate" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/shared": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.3.tgz", + "integrity": "sha512-NEopD+9S5xYyQ0pGtioacLhL2luflh6HACSSDUZOwLHoxA5eku1UPuqcJqjwSD6luKjjLfiLOspxo43FUHKKSA==", + "dependencies": { + "@react-spring/types": "~9.7.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/types": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.3.tgz", + "integrity": "sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw==" + }, + "node_modules/@react-spring/web": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.3.tgz", + "integrity": "sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg==", + "dependencies": { + "@react-spring/animated": "~9.7.3", + "@react-spring/core": "~9.7.3", + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/@remix-run/router": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.3.tgz", @@ -20607,6 +20698,19 @@ } } }, + "@mui/x-tree-view": { + "version": "6.0.0-alpha.1", + "resolved": "https://registry.npmjs.org/@mui/x-tree-view/-/x-tree-view-6.0.0-alpha.1.tgz", + "integrity": "sha512-JUG3HmBrmGEALbCFg1b+i7h726e1dWYZs4db3syO1j+Q++E3nbvE4Lehp5yGTFm+8esH0Tny50tuJaa4WX6VSA==", + "requires": { + "@babel/runtime": "^7.22.6", + "@mui/utils": "^5.14.3", + "@types/react-transition-group": "^4.4.6", + "clsx": "^2.0.0", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + } + }, "@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -20683,6 +20787,49 @@ "@babel/runtime": "^7.6.2" } }, + "@react-spring/animated": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.3.tgz", + "integrity": "sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==", + "requires": { + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + } + }, + "@react-spring/core": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.3.tgz", + "integrity": "sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ==", + "requires": { + "@react-spring/animated": "~9.7.3", + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + } + }, + "@react-spring/shared": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.3.tgz", + "integrity": "sha512-NEopD+9S5xYyQ0pGtioacLhL2luflh6HACSSDUZOwLHoxA5eku1UPuqcJqjwSD6luKjjLfiLOspxo43FUHKKSA==", + "requires": { + "@react-spring/types": "~9.7.3" + } + }, + "@react-spring/types": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.3.tgz", + "integrity": "sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw==" + }, + "@react-spring/web": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.3.tgz", + "integrity": "sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg==", + "requires": { + "@react-spring/animated": "~9.7.3", + "@react-spring/core": "~9.7.3", + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + } + }, "@remix-run/router": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.3.tgz", diff --git a/stopes/ui/seamlisten/react_app/package.json b/stopes/ui/seamlisten/react_app/package.json index 7d0ba9c..1e4cdd7 100644 --- a/stopes/ui/seamlisten/react_app/package.json +++ b/stopes/ui/seamlisten/react_app/package.json @@ -9,6 +9,8 @@ "@mui/icons-material": "^5.14.7", "@mui/lab": "^5.0.0-alpha.142", "@mui/material": "^5.14.7", + "@mui/x-tree-view": "^6.0.0-alpha.1", + "@react-spring/web": "^9.7.3", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", diff --git a/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx b/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx index 239f65d..a6ca644 100644 --- a/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx +++ b/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx @@ -1,10 +1,85 @@ -import { useState } from "react"; -import TreeView from "@mui/lab/TreeView"; -import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; -import ChevronRightIcon from "@mui/icons-material/ChevronRight"; -import TreeItem from "@mui/lab/TreeItem"; +import { useState } from "react"; import { text_to_audio } from "../common/components/audio/audioquery_constructor"; import WaveSurferComponent from "../common/components/audio/WaveSurfer"; +import { useSpring, animated } from '@react-spring/web'; +import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon'; +import { TransitionProps } from '@mui/material/transitions'; +import Collapse from '@mui/material/Collapse'; +import { alpha, styled } from '@mui/material/styles'; +import { TreeView } from '@mui/x-tree-view/TreeView'; +import { TreeItem, TreeItemProps, treeItemClasses } from '@mui/x-tree-view/TreeItem'; +import React from 'react'; + + +function MinusSquare(props: SvgIconProps) { + return ( + + {/* tslint:disable-next-line: max-line-length */} + + + ); +} + +function PlusSquare(props: SvgIconProps) { + return ( + + {/* tslint:disable-next-line: max-line-length */} + + + ); +} + +function CloseSquare(props: SvgIconProps) { + return ( + + {/* tslint:disable-next-line: max-line-length */} + + + ); +} + +function TransitionComponent(props: TransitionProps) { + const style = useSpring({ + from: { + opacity: 0, + transform: 'translate3d(20px,0,0)', + }, + to: { + opacity: props.in ? 1 : 0, + transform: `translate3d(${props.in ? 0 : 20}px,0,0)`, + }, + }); + + return ( + + + + ); +} + +const CustomTreeItem = React.forwardRef( + (props: TreeItemProps, ref: React.Ref) => ( + + ), +); + +const StyledTreeItem = styled(CustomTreeItem)(({ theme }) => ({ + [`& .${treeItemClasses.iconContainer}`]: { + '& .close': { + opacity: 0.3, + }, + }, + [`& .${treeItemClasses.group}`]: { + marginLeft: 15, + paddingLeft: 18, + borderLeft: `1px dashed ${alpha(theme.palette.text.primary, 0.4)}`, + }, +})); const FolderTreeView = ({ folderContents }) => { const [expandedFolders, setExpandedFolders] = useState(new Set()); @@ -77,19 +152,15 @@ const FolderTreeView = ({ folderContents }) => { return ( } - defaultExpandIcon={} - sx={{ - height: 240, - flexGrow: 1, - maxWidth: 400, - overflowY: "auto", - display: "flex", - }} + aria-label="customized" + defaultExpanded={['1']} + defaultCollapseIcon={} + defaultExpandIcon={} + defaultEndIcon={} + sx={{ height: 264, flexGrow: 1, maxWidth: 400, overflowY: 'auto' }} >
- { )} {folderData.audio_files && folderData.audio_files.length > 0 && ( folderData.audio_files.map((audioFile) => ( - { /> )) )} - +
); diff --git a/stopes/ui/seamlisten/react_app/tsconfig.json b/stopes/ui/seamlisten/react_app/tsconfig.json index ffa3aa7..c2fa1a1 100644 --- a/stopes/ui/seamlisten/react_app/tsconfig.json +++ b/stopes/ui/seamlisten/react_app/tsconfig.json @@ -4,7 +4,8 @@ "allowJs": true, "target": "es5", "jsx": "react-jsx", - "moduleResolution": "node" + "moduleResolution": "node", + "esModuleInterop": true, }, "include": ["./src/**/*", "./src/**/**/*"] } From 847f87cdf38af871cfd82d6fe87adb3865987774 Mon Sep 17 00:00:00 2001 From: David Wagih Date: Wed, 6 Sep 2023 08:19:40 +0300 Subject: [PATCH 12/15] enhance the styling of the download button --- .../ui/seamlisten/react_app/package-lock.json | 14 +++++++++++ stopes/ui/seamlisten/react_app/package.json | 1 + .../common/components/audio/WaveSurfer.css | 1 + .../src/components/FolderTreeView.css | 19 +++++++++++++++ .../src/components/FolderTreeView.tsx | 24 ++++++------------- 5 files changed, 42 insertions(+), 17 deletions(-) create mode 100644 stopes/ui/seamlisten/react_app/src/components/FolderTreeView.css diff --git a/stopes/ui/seamlisten/react_app/package-lock.json b/stopes/ui/seamlisten/react_app/package-lock.json index d0652a4..df4ec55 100644 --- a/stopes/ui/seamlisten/react_app/package-lock.json +++ b/stopes/ui/seamlisten/react_app/package-lock.json @@ -25,6 +25,7 @@ "@types/react-dom": "^18.0.9", "bootstrap": "^5.2.2", "crypto-hash": "^2.0.1", + "font-awesome": "^4.7.0", "puppeteer": "^18.1.0", "react": "^18.2.0", "react-bootstrap": "^2.5.0", @@ -8954,6 +8955,14 @@ } } }, + "node_modules/font-awesome": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", + "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==", + "engines": { + "node": ">=0.10.3" + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -24691,6 +24700,11 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" }, + "font-awesome": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", + "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==" + }, "for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", diff --git a/stopes/ui/seamlisten/react_app/package.json b/stopes/ui/seamlisten/react_app/package.json index 1e4cdd7..7f3725b 100644 --- a/stopes/ui/seamlisten/react_app/package.json +++ b/stopes/ui/seamlisten/react_app/package.json @@ -20,6 +20,7 @@ "@types/react-dom": "^18.0.9", "bootstrap": "^5.2.2", "crypto-hash": "^2.0.1", + "font-awesome": "^4.7.0", "puppeteer": "^18.1.0", "react": "^18.2.0", "react-bootstrap": "^2.5.0", diff --git a/stopes/ui/seamlisten/react_app/src/common/components/audio/WaveSurfer.css b/stopes/ui/seamlisten/react_app/src/common/components/audio/WaveSurfer.css index e14f5f8..294687b 100644 --- a/stopes/ui/seamlisten/react_app/src/common/components/audio/WaveSurfer.css +++ b/stopes/ui/seamlisten/react_app/src/common/components/audio/WaveSurfer.css @@ -12,4 +12,5 @@ .playPauseIcon :hover { color: #2bad60; + cursor: pointer; } diff --git a/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.css b/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.css new file mode 100644 index 0000000..b3b684f --- /dev/null +++ b/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.css @@ -0,0 +1,19 @@ +/* Style the button container */ +.download-button { + background-color: #7a26c1;; /* Set the background color */ + color: #ffffff; /* Set the text color */ + border: none; /* Remove the default button border */ + border-radius: 4px; /* Add some border radius for rounded corners */ + padding: 10px 20px; /* Add padding to make the button larger */ + cursor: pointer; /* Show a pointer cursor on hover */ + transition: background-color 0.3s ease; /* Add a smooth background color transition on hover */ + margin-top: 1rem; + /* Center the icon and text vertically and horizontally */ + + } + + /* Style the button on hover */ + .download-button:hover { + background-color: #2bad60; /* Change the background color on hover */ + } + diff --git a/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx b/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx index a6ca644..9192323 100644 --- a/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx +++ b/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx @@ -9,6 +9,9 @@ import { alpha, styled } from '@mui/material/styles'; import { TreeView } from '@mui/x-tree-view/TreeView'; import { TreeItem, TreeItemProps, treeItemClasses } from '@mui/x-tree-view/TreeItem'; import React from 'react'; +import 'font-awesome/css/font-awesome.min.css'; +import "./FolderTreeView.css" + function MinusSquare(props: SvgIconProps) { @@ -122,25 +125,12 @@ const FolderTreeView = ({ folderContents }) => { }; return ( - ); - } + }; From 36edd8a1df35ee42df6111b907c67a080bc34dd7 Mon Sep 17 00:00:00 2001 From: David Wagih Date: Wed, 6 Sep 2023 18:44:00 +0300 Subject: [PATCH 13/15] fix some bugs related to expanding folders --- .../react_app/src/components/FileExplorer.tsx | 7 +- .../src/components/FolderTreeView.tsx | 68 +++++++++++-------- 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/stopes/ui/seamlisten/react_app/src/components/FileExplorer.tsx b/stopes/ui/seamlisten/react_app/src/components/FileExplorer.tsx index 0a02527..66b7ed1 100644 --- a/stopes/ui/seamlisten/react_app/src/components/FileExplorer.tsx +++ b/stopes/ui/seamlisten/react_app/src/components/FileExplorer.tsx @@ -233,9 +233,7 @@ const Files = (): JSX.Element => { - + {loading ? ( ) : error ? ( @@ -264,6 +262,9 @@ const Files = (): JSX.Element => { )} )} + ); }; diff --git a/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx b/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx index 9192323..a625fd5 100644 --- a/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx +++ b/stopes/ui/seamlisten/react_app/src/components/FolderTreeView.tsx @@ -142,39 +142,49 @@ const FolderTreeView = ({ folderContents }) => { return ( } - defaultExpandIcon={} - defaultEndIcon={} - sx={{ height: 264, flexGrow: 1, maxWidth: 400, overflowY: 'auto' }} + aria-label="customized" + defaultExpanded={['1']} + defaultCollapseIcon={} + defaultExpandIcon={} + defaultEndIcon={} + sx={{ height: 264, flexGrow: 1, maxWidth: 400, overflowY: 'auto' }} > -
- handleNodeToggle(event, [folderData.folder])} - > - {folderData.subfolders && folderData.subfolders.length > 0 && ( - folderData.subfolders.map((subfolder) => renderFolderTree(subfolder)) - )} - {folderData.audio_files && folderData.audio_files.length > 0 && ( - folderData.audio_files.map((audioFile) => ( + handleNodeToggle(event, [folderData.folder])} + > + {folderData.subfolders && folderData.subfolders.length > 0 && ( + folderData.subfolders.map((subfolder) => ( + handleAudioFileClick( - `${folderData.folder}/${audioFile}` - )} - /> - )) - )} - -
+ key={subfolder.folder} + nodeId={subfolder.folder} + label={subfolder.folder} + onClick={(event) => handleNodeToggle(event, [subfolder.folder])} + > + {renderFolderTree(subfolder)} + +
+ )) + )} + {folderData.audio_files && folderData.audio_files.length > 0 && ( + folderData.audio_files.map((audioFile) => ( + handleAudioFileClick( + `${folderData.folder}/${audioFile}` + )} + /> + )) + )} + ); + }; return ( From 42edb3fde652bf8953adfeff2fc91458e25831f8 Mon Sep 17 00:00:00 2001 From: David Wagih <78867146+david-wagih@users.noreply.github.com> Date: Thu, 7 Sep 2023 12:11:45 +0300 Subject: [PATCH 14/15] Update stopes/ui/seamlisten/backend/app/fileviewer.py Co-authored-by: Pierre Andrews <628467+Mortimerp9@users.noreply.github.com> --- stopes/ui/seamlisten/backend/app/fileviewer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stopes/ui/seamlisten/backend/app/fileviewer.py b/stopes/ui/seamlisten/backend/app/fileviewer.py index 035fecf..d2d0ca6 100644 --- a/stopes/ui/seamlisten/backend/app/fileviewer.py +++ b/stopes/ui/seamlisten/backend/app/fileviewer.py @@ -115,7 +115,7 @@ def get_annotations(query: AnnotationQuery) -> tp.List[LineResult]: status_code=404, detail="File not found", ) - elif resolved_path.suffixes == [".zip"]: + elif resolved_path.suffixes[-1] == ".zip": if not resolved_path.exists(): raise HTTPException(status_code=404, detail="File not found") return open_zip_file(str(resolved_path), query.start_idx, query.end_idx) From 9a21bce7e9008c4894d5ecda14fd861b721331f1 Mon Sep 17 00:00:00 2001 From: David Wagih <78867146+david-wagih@users.noreply.github.com> Date: Thu, 7 Sep 2023 12:11:53 +0300 Subject: [PATCH 15/15] Update stopes/ui/seamlisten/backend/app/fileviewer.py Co-authored-by: Pierre Andrews <628467+Mortimerp9@users.noreply.github.com> --- stopes/ui/seamlisten/backend/app/fileviewer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stopes/ui/seamlisten/backend/app/fileviewer.py b/stopes/ui/seamlisten/backend/app/fileviewer.py index d2d0ca6..4c2bbdf 100644 --- a/stopes/ui/seamlisten/backend/app/fileviewer.py +++ b/stopes/ui/seamlisten/backend/app/fileviewer.py @@ -135,7 +135,7 @@ async def general_query(query: DefaultQuery) -> Response: result_data = gather_folder_contents(query_path) return result_data - if query_path.suffixes in ([".gz"], [".tsv"], [".zip"]): + if query_path.suffixes[-1] in (".gz", ".tsv", ".zip"): return get_annotations( AnnotationQuery( gz_path=query.gz_path, start_idx=query.start_idx, end_idx=query.end_idx