diff --git a/.github/workflows/add-to-catalystneuro-dashboard.yml b/.github/workflows/add_to_catalystneuro_dashboard.yml similarity index 100% rename from .github/workflows/add-to-catalystneuro-dashboard.yml rename to .github/workflows/add_to_catalystneuro_dashboard.yml diff --git a/.github/workflows/project_action.yml b/.github/workflows/add_to_nwb_dashboard.yml similarity index 100% rename from .github/workflows/project_action.yml rename to .github/workflows/add_to_nwb_dashboard.yml diff --git a/.github/workflows/Build-and-deploy-mac.yml b/.github/workflows/build_and_deploy_mac.yml similarity index 100% rename from .github/workflows/Build-and-deploy-mac.yml rename to .github/workflows/build_and_deploy_mac.yml diff --git a/.github/workflows/Build-and-deploy-win.yml b/.github/workflows/build_and_deploy_win.yml similarity index 100% rename from .github/workflows/Build-and-deploy-win.yml rename to .github/workflows/build_and_deploy_win.yml diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml deleted file mode 100644 index 243ba8ce5f..0000000000 --- a/.github/workflows/codespell.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: Codespell - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - codespell: - name: Check for spelling errors - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Codespell - uses: codespell-project/actions-codespell@v1 diff --git a/.github/workflows/daily_tests.yml b/.github/workflows/daily_tests.yml new file mode 100644 index 0000000000..5fe653197a --- /dev/null +++ b/.github/workflows/daily_tests.yml @@ -0,0 +1,37 @@ +name: Daily Tests + +on: + workflow_dispatch: + schedule: + - cron: "0 14 * * *" # Daily at 10am EST + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + + DevTests: + uses: ./.github/workflows/testing_dev.yml + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + LiveServices: + uses: ./.github/workflows/testing_dev_with_live_services.yml + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + DANDI_STAGING_API_KEY: ${{ secrets.DANDI_STAGING_API_KEY }} + + BuildTests: + uses: ./.github/workflows/testing_flask_build_and_dist.yml + + ExampleDataCache: + uses: ./.github/workflows/example_data_cache.yml + secrets: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + S3_GIN_BUCKET: ${{ secrets.S3_GIN_BUCKET }} + + ExampleDataTests: + needs: ExampleDataCache + uses: ./.github/workflows/testing_pipelines.yml diff --git a/.github/workflows/deploy_tests_on_pull_request.yml b/.github/workflows/deploy_tests_on_pull_request.yml new file mode 100644 index 0000000000..f006b1d2b3 --- /dev/null +++ b/.github/workflows/deploy_tests_on_pull_request.yml @@ -0,0 +1,35 @@ +name: Deploy + +on: + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + + DevTests: + uses: ./.github/workflows/testing_dev.yml + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + LiveServices: + uses: ./.github/workflows/testing_dev_with_live_services.yml + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + DANDI_STAGING_API_KEY: ${{ secrets.DANDI_STAGING_API_KEY }} + + BuildTests: + uses: ./.github/workflows/testing_flask_build_and_dist.yml + + ExampleDataCache: + uses: ./.github/workflows/example_data_cache.yml + secrets: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + S3_GIN_BUCKET: ${{ secrets.S3_GIN_BUCKET }} + + ExampleDataTests: + needs: ExampleDataCache + uses: ./.github/workflows/testing_pipelines.yml diff --git a/.github/workflows/example_data_cache.yml b/.github/workflows/example_data_cache.yml new file mode 100644 index 0000000000..e27070078c --- /dev/null +++ b/.github/workflows/example_data_cache.yml @@ -0,0 +1,74 @@ +name: Example data cache +on: + workflow_call: + secrets: + AWS_ACCESS_KEY_ID: + required: true + AWS_SECRET_ACCESS_KEY: + required: true + S3_GIN_BUCKET: + required: true + +jobs: + + run: + # Will read on PR dashboard as 'Deploy / ExampleDataCache / {os}' + # Action dashboard identified by 'Deploy' + # Requirement settings identified as 'ExampleDataCache / {os}' + name: ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + python-version: ["3.12"] + os: [ubuntu-latest, macos-latest, macos-13] #, windows-latest] + + steps: + + - name: Get ephy_testing_data current head hash + id: ephys + run: echo "::set-output name=HASH_EPHY_DATASET::$(git ls-remote https://gin.g-node.org/NeuralEnsemble/ephy_testing_data.git HEAD | cut -f1)" + - name: Get cached ephys example data - ${{ steps.ephys.outputs.HASH_EPHY_DATASET }} + uses: actions/cache@v4 + id: cache-ephys-datasets + with: + path: ./ephy_testing_data + key: ephys-datasets-${{ matrix.os }}-${{ steps.ephys.outputs.HASH_EPHY_DATASET }} + - name: Get ophys_testing_data current head hash + id: ophys + run: echo "::set-output name=HASH_OPHYS_DATASET::$(git ls-remote https://gin.g-node.org/CatalystNeuro/ophys_testing_data.git HEAD | cut -f1)" + - name: Get cached ophys example data - ${{ steps.ophys.outputs.HASH_OPHYS_DATASET }} + uses: actions/cache@v4 + id: cache-ophys-datasets + with: + path: ./ophys_testing_data + key: ophys-datasets-${{ matrix.os }}-${{ steps.ophys.outputs.HASH_OPHYS_DATASET }} + - name: Get behavior_testing_data current head hash + id: behavior + run: echo "::set-output name=HASH_BEHAVIOR_DATASET::$(git ls-remote https://gin.g-node.org/CatalystNeuro/behavior_testing_data.git HEAD | cut -f1)" + - name: Get cached behavior example data - ${{ steps.behavior.outputs.HASH_BEHAVIOR_DATASET }} + uses: actions/cache@v4 + id: cache-behavior-datasets + with: + path: ./behavior_testing_data + key: behavior-datasets-${{ matrix.os }}-${{ steps.behavior.outputs.HASH_behavior_DATASET }} + + - if: steps.cache-ephys-datasets.outputs.cache-hit != 'true' || steps.cache-ophys-datasets.outputs.cache-hit != 'true' || steps.cache-behavior-datasets.outputs.cache-hit != 'true' + name: Install and configure AWS CLI + run: | + pip install awscli + aws configure set aws_access_key_id ${{ secrets.AWS_ACCESS_KEY_ID }} + aws configure set aws_secret_access_key ${{ secrets.AWS_SECRET_ACCESS_KEY }} + + - if: steps.cache-ephys-datasets.outputs.cache-hit != 'true' + name: Download ephys dataset from S3 + run: | + aws s3 cp --region=us-east-2 ${{ secrets.S3_GIN_BUCKET }}/ephy_testing_data ./ephy_testing_data --recursive + - if: steps.cache-ophys-datasets.outputs.cache-hit != 'true' + name: Download ophys dataset from S3 + run: | + aws s3 cp --region=us-east-2 ${{ secrets.S3_GIN_BUCKET }}/ophys_testing_data ./ophys_testing_data --recursive + - if: steps.cache-behavior-datasets.outputs.cache-hit != 'true' + name: Download behavior dataset from S3 + run: | + aws s3 cp --region=us-east-2 ${{ secrets.S3_GIN_BUCKET }}/behavior_testing_data ./behavior_testing_data --recursive diff --git a/.github/workflows/testing-pipelines.yml b/.github/workflows/testing-pipelines.yml deleted file mode 100644 index 18b3655481..0000000000 --- a/.github/workflows/testing-pipelines.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Example Pipeline Tests -on: - schedule: - - cron: "0 16 * * *" # Daily at noon EST - pull_request: - -concurrency: # Cancel previous workflows on the same pull request - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -env: - CACHE_NUMBER: 2 # increase to reset cache manually - -jobs: - testing: - name: Pipelines on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - defaults: - run: - shell: bash -l {0} - - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - include: - - os: ubuntu-latest - label: environments/environment-Linux.yml - - - os: macos-latest # Mac arm64 runner - label: environments/environment-MAC-apple-silicon.yml - - - os: macos-13 # Mac x64 runner - label: environments/environment-MAC-intel.yml - - - os: windows-latest - label: environments/environment-Windows.yml - - - steps: - - uses: actions/checkout@v4 - - run: git fetch --prune --unshallow --tags - - # see https://github.com/conda-incubator/setup-miniconda#caching-environments - - name: Setup Mambaforge - uses: conda-incubator/setup-miniconda@v3 - with: - miniforge-variant: Mambaforge - miniforge-version: latest - activate-environment: nwb-guide - use-mamba: true - - - name: Set cache date - id: get-date - run: echo "today=$(/bin/date -u '+%Y%m%d')" >> $GITHUB_OUTPUT - shell: bash - - - name: Cache Conda env - uses: actions/cache@v4 - with: - path: ${{ env.CONDA }}/envs - key: conda-${{ runner.os }}-${{ runner.arch }}-${{steps.get-date.outputs.today }}-${{ hashFiles(matrix.label) }}-${{ env.CACHE_NUMBER }} - id: cache - - - if: steps.cache.outputs.cache-hit != 'true' - name: Create and activate environment - run: mamba env update --name nwb-guide --file ${{ matrix.label }} - - - name: Use Node.js 20 - uses: actions/setup-node@v4 - with: - node-version: 20 - - - name: Install GUIDE - run: npm ci --verbose - - - if: matrix.os != 'ubuntu-latest' - name: Run tests - run: npm run test:pipelines - - - if: matrix.os == 'ubuntu-latest' - name: Run tests with xvfb - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test:pipelines diff --git a/.github/workflows/testing.yml b/.github/workflows/testing_dev.yml similarity index 81% rename from .github/workflows/testing.yml rename to .github/workflows/testing_dev.yml index 4dbace72d7..72bd3d2444 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing_dev.yml @@ -1,28 +1,24 @@ -name: Dev Tests +name: Dev tests on: - schedule: - - cron: "0 16 * * *" # Daily at noon EST - pull_request: - -concurrency: # Cancel previous workflows on the same pull request - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -env: - CACHE_NUMBER: 2 # increase to reset cache manually + workflow_call: + secrets: + CODECOV_TOKEN: + required: true jobs: - testing: - name: ${{ matrix.os }} # Will read on the dashboard as 'Dev Tests / {os}' + + run: + # Will read on PR dashboard as 'Deploy / DevTests / {os}' + # Action dashboard identified by 'Deploy' + # Requirement settings identified as 'DevTests / {os}' + name: ${{ matrix.os }} runs-on: ${{ matrix.os }} defaults: run: shell: bash -l {0} - strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] include: - os: ubuntu-latest label: environments/environment-Linux.yml @@ -33,9 +29,8 @@ jobs: - os: macos-13 # Mac x64 runner label: environments/environment-MAC-intel.yml - - os: windows-latest - label: environments/environment-Windows.yml - +# - os: windows-latest +# label: environments/environment-Windows.yml steps: - uses: actions/checkout@v4 @@ -89,7 +84,7 @@ jobs: name: test-screenshots-${{ matrix.os }} path: docs/assets/tutorials retention-days: 1 - + overwrite: true - name: Upload coverage reports to Codecov diff --git a/.github/workflows/testing-live-services.yml b/.github/workflows/testing_dev_with_live_services.yml similarity index 79% rename from .github/workflows/testing-live-services.yml rename to .github/workflows/testing_dev_with_live_services.yml index dcf8fff649..1d4be248d5 100644 --- a/.github/workflows/testing-live-services.yml +++ b/.github/workflows/testing_dev_with_live_services.yml @@ -1,28 +1,26 @@ -name: Dev Tests (Live) +name: Dev tests with live services on: - schedule: - - cron: "0 16 * * *" # Daily at noon EST - pull_request: - -concurrency: # Cancel previous workflows on the same pull request - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -env: - CACHE_NUMBER: 2 # increase to reset cache manually + workflow_call: + secrets: + CODECOV_TOKEN: + required: true + DANDI_STAGING_API_KEY: + required: true jobs: - testing: - name: Services on ${{ matrix.os }} # Will read on the dashboard as 'Dev Tests (Live) / Services on {os}' + + run: + # Will read on PR dashboard as 'Deploy / LiveServices / {os}' + # Action dashboard identified by 'Deploy' + # Requirement settings identified as 'LiveServices / {os}' + name: ${{ matrix.os }} runs-on: ${{ matrix.os }} defaults: run: shell: bash -l {0} - strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] include: - os: ubuntu-latest label: environments/environment-Linux.yml @@ -33,9 +31,8 @@ jobs: - os: macos-13 # Mac x64 runner label: environments/environment-MAC-intel.yml - - os: windows-latest - label: environments/environment-Windows.yml - +# - os: windows-latest +# label: environments/environment-Windows.yml steps: - uses: actions/checkout@v4 @@ -87,7 +84,6 @@ jobs: name: Run tests with xvfb run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run coverage:app - - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4 env: diff --git a/.github/workflows/pyflask-build-and-dist-tests.yml b/.github/workflows/testing_flask_build_and_dist.yml similarity index 82% rename from .github/workflows/pyflask-build-and-dist-tests.yml rename to .github/workflows/testing_flask_build_and_dist.yml index 3c4d609719..bc7390ad22 100644 --- a/.github/workflows/pyflask-build-and-dist-tests.yml +++ b/.github/workflows/testing_flask_build_and_dist.yml @@ -1,31 +1,23 @@ -name: Build Tests +name: Test Flask build and dev tests on Flask distributable on: - schedule: - - cron: "0 16 * * *" # Daily at noon EST - pull_request: - workflow_dispatch: - -# Cancel previous workflows on the same pull request -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -env: - CACHE_NUMBER: 1 # increase to reset cache manually + workflow_call: jobs: - testing: - name: PyInstaller on ${{ matrix.os }} # Will read on the dashboard as 'Build Tests / PyInstaller on {os}' + + run: + # Will read on PR dashboard as 'Deploy / BuildTests / {os}' + # Action dashboard identified by 'Deploy' + # Requirement settings identified as 'BuildTests / {os}' + name: ${{ matrix.os }} runs-on: ${{ matrix.os }} defaults: run: shell: bash -l {0} - strategy: fail-fast: false matrix: include: - # current linux installation instructions use dev mode instead of distributable + # linux installation instructions use dev mode instead of distributable # - python-version: "3.9" # os: ubuntu-latest # label: environments/environment-Linux.yml @@ -88,7 +80,7 @@ jobs: - run: npm ci --verbose - # fix for macos build + # Fix for macos build - remove bad sonpy file - if: matrix.os == 'macos-latest' || matrix.os == 'macos-13' run: rm -f /Users/runner/miniconda3/envs/nwb-guide/lib/python3.9/site-packages/sonpy/linux/sonpy.so diff --git a/.github/workflows/testing_pipelines.yml b/.github/workflows/testing_pipelines.yml new file mode 100644 index 0000000000..1a4ca41d6f --- /dev/null +++ b/.github/workflows/testing_pipelines.yml @@ -0,0 +1,133 @@ +name: Example data pipeline Tests +on: + workflow_call: + +jobs: + + run: + # Will read on PR dashboard as 'Deploy / ExampleDataTests / {os}' + # Action dashboard identified by 'Deploy' + # Requirement settings identified as 'ExampleDataTests / {os}' + name: ${{ matrix.os }} + runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash -l {0} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + label: environments/environment-Linux.yml + + - os: macos-latest # Mac arm64 runner + label: environments/environment-MAC-apple-silicon.yml + + - os: macos-13 # Mac x64 runner + label: environments/environment-MAC-intel.yml + +# - os: windows-latest +# label: environments/environment-Windows.yml + + + steps: + - uses: actions/checkout@v4 + - run: git fetch --prune --unshallow --tags + + # see https://github.com/conda-incubator/setup-miniconda#caching-environments + - name: Setup Mambaforge + uses: conda-incubator/setup-miniconda@v3 + with: + miniforge-variant: Mambaforge + miniforge-version: latest + activate-environment: nwb-guide + use-mamba: true + + # Setup conda environment from cache + - name: Set environment cache date + id: get-date + run: echo "today=$(/bin/date -u '+%Y%m%d')" >> $GITHUB_OUTPUT + shell: bash + - name: Cache Conda env + uses: actions/cache@v4 + with: + path: ${{ env.CONDA }}/envs + key: conda-${{ runner.os }}-${{ runner.arch }}-${{steps.get-date.outputs.today }}-${{ hashFiles(matrix.label) }}-${{ env.CACHE_NUMBER }} + id: cache + - if: steps.cache.outputs.cache-hit != 'true' + name: Create and activate environment + run: mamba env update --name nwb-guide --file ${{ matrix.label }} + + - name: Use Node.js 20 + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install GUIDE + run: npm ci --verbose + + # Load example data caches + - name: Get ephy_testing_data current head hash + id: ephys + run: echo "::set-output name=HASH_EPHY_DATASET::$(git ls-remote https://gin.g-node.org/NeuralEnsemble/ephy_testing_data.git HEAD | cut -f1)" + - name: Cache ephys dataset - ${{ steps.ephys.outputs.HASH_EPHY_DATASET }} + uses: actions/cache@v4 + id: cache-ephys-datasets + with: + path: ./ephy_testing_data + key: ephys-datasets-${{ matrix.os }}-${{ steps.ephys.outputs.HASH_EPHY_DATASET }} + - name: Get ophys_testing_data current head hash + id: ophys + run: echo "::set-output name=HASH_OPHYS_DATASET::$(git ls-remote https://gin.g-node.org/CatalystNeuro/ophys_testing_data.git HEAD | cut -f1)" + - name: Cache ophys dataset - ${{ steps.ophys.outputs.HASH_OPHYS_DATASET }} + uses: actions/cache@v4 + id: cache-ophys-datasets + with: + path: ./ophys_testing_data + key: ophys-datasets-${{ matrix.os }}-${{ steps.ophys.outputs.HASH_OPHYS_DATASET }} + - name: Get behavior_testing_data current head hash + id: behavior + run: echo "::set-output name=HASH_BEHAVIOR_DATASET::$(git ls-remote https://gin.g-node.org/CatalystNeuro/behavior_testing_data.git HEAD | cut -f1)" + - name: Cache behavior dataset - ${{ steps.behavior.outputs.HASH_BEHAVIOR_DATASET }} + uses: actions/cache@v4 + id: cache-behavior-datasets + with: + path: ./behavior_testing_data + key: behavior-datasets-${{ matrix.os }}-${{ steps.behavior.outputs.HASH_behavior_DATASET }} + + - name: Save working directory to environment file + run: echo "GIN_DATA_DIR=$(pwd)" >> .env + if: runner.os != 'Windows' + + - name: Save working directory to environment file (Windows) + run: echo GIN_DATA_DIR=%cd% >> .env + shell: bash + if: runner.os == 'Windows' + + # Display environment file for debugging + - name: Print environment file + run: cat .env + if: runner.os != 'Windows' + + - name: Print environment file + run: type .env + shell: bash + if: runner.os == 'Windows' + + # Run pipeline tests + - if: matrix.os != 'ubuntu-latest' + name: Run tests + run: npm run test:pipelines + + - if: matrix.os == 'ubuntu-latest' + name: Run tests with xvfb + run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test:pipelines + + - name: Archive Pipeline Test Screenshots + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-pipelines-screenshots-${{ matrix.os }} + path: docs/assets/tutorials/test-pipelines + retention-days: 1 + overwrite: true diff --git a/.github/workflows/update-package.yml b/.github/workflows/update-package.yml deleted file mode 100644 index bbc041ca95..0000000000 --- a/.github/workflows/update-package.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Update package.lock - -on: - workflow_dispatch: - -jobs: - deploy: - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - - name: Checkout New Branch - run: git checkout -b update_package_lock - - name: Update package.lock file - run: npm install --ignore-scripts --verbose - - name: Commit Changes and Create Pull Request - run: | - git config --global user.email 41898282+github-actions[bot]@users.noreply.github.com - git config --global user.name github-actions[bot] - git commit . -m "Update package.lock file" - git push origin update_package_lock - gh pr create --title "[Github.CI] Update package.lock file" --body "Updated package.lock file to match package.json" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 865e043ba7..a6393f3dad 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ _build dist out tests/screenshots +docs/assets/tutorials/test-pipelines coverage diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 65e3078722..169e416899 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,3 +23,10 @@ repos: hooks: - id: prettier types_or: [css, javascript] + +- repo: https://github.com/codespell-project/codespell + rev: v2.2.6 + hooks: + - id: codespell + additional_dependencies: + - tomli diff --git a/README.md b/README.md index bf95742c47..d86a00a2d7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- NeuroConv logo + NeuroConv logo

NWB Graphical User Interface for Data Entry

Full Tests diff --git a/docs/assets/tutorials/pipelines/created.png b/docs/assets/tutorials/pipelines/created.png new file mode 100644 index 0000000000..02f57c293d Binary files /dev/null and b/docs/assets/tutorials/pipelines/created.png differ diff --git a/docs/assets/tutorials/pipelines/list.png b/docs/assets/tutorials/pipelines/list.png new file mode 100644 index 0000000000..0393f05ced Binary files /dev/null and b/docs/assets/tutorials/pipelines/list.png differ diff --git a/docs/developer_guide.rst b/docs/developer_guide.rst index 208b730ce7..2888018bf0 100644 --- a/docs/developer_guide.rst +++ b/docs/developer_guide.rst @@ -88,13 +88,17 @@ Repo Structure - `main` - `src` - Contains all the source code for the backend - `assets` - Contains all the backend-facing assets (e.g. images, css, etc.) - - `renderer` - - `src` - Contains all the source code for the frontend + - `preload` + - `preload.js` - Exposes electron-specific variables to the frontend + - `frontend` + - `core` - Contains all the source code for the frontend - `index.js` - The entry point for the application - `pages.js` - The main code that controls which pages are rendered and how they are linked together - - `stories` - Contains all the Web Components and related Storybook stories - - `electron` - Contains all the Electron-related code to enable conditional inclusion for development mode + - `components` - Contains all the UI Components used throughout the app - `assets` - Contains all the frontend-facing assets (e.g. images, css, etc.) + - `utils` + - `electron.js` - Contains electron-exposed variables + - `url.js` - Saving the history state for hot reloading and refresh page functionality - `pyflask` - Contains all the source code for the backend - `schemas` - Contains all the JSON schemas used for validation diff --git a/docs/index.rst b/docs/index.rst index 2fb04c2525..2805f6417b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -2,7 +2,7 @@ NWB GUIDE documentation ======================= | -.. image:: ../src/electron/renderer/assets/img/logo-guide-draft-transparent-tight.png +.. image:: ../src/electron/frontend/assets/img/logo-guide-draft-transparent-tight.png :scale: 50 % :align: center :alt: NWB GUIDE logo diff --git a/electron.vite.config.js b/electron.vite.config.js index 21a8b4b8f1..bf952ab7c5 100644 --- a/electron.vite.config.js +++ b/electron.vite.config.js @@ -4,7 +4,7 @@ import ViteYaml from "@modyfi/vite-plugin-yaml"; import { resolve } from "path"; -const htmlRoot = "src/electron/renderer"; +const htmlRoot = "src/electron/frontend"; export default defineConfig({ main: { diff --git a/package.json b/package.json index 0d4635cb81..35533e529e 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,7 @@ "scripts": { "start": "electron-vite dev --outDir build", "postinstall": "electron-builder install-app-deps", - "dev": "concurrently -n BE,FE --kill-others \"npm run dev:server\" \"npm run dev:app\"", - "dev:app": "vite src/electron/renderer", - "dev:server": "cd src/pyflask && python -m flask run --port 4242", + "server": "cd src/pyflask && python -m flask run --port 4242", "build": "npm run build:app", "echo": "python -c \"print('hello')\"", "build:app": "electron-vite build --outDir build", @@ -77,7 +75,7 @@ "win": { "asar": false, "target": "nsis", - "icon": "src/electron/renderer/assets/app-icon/logo-guide-draft.ico", + "icon": "src/electron/frontend/assets/app-icon/logo-guide-draft.ico", "requestedExecutionLevel": "requireAdministrator" }, "mac": { @@ -98,7 +96,7 @@ ] } ], - "icon": "src/electron/renderer/assets/img/logo-guide-draft.png", + "icon": "src/electron/frontend/assets/img/logo-guide-draft.png", "darkModeSupport": false, "hardenedRuntime": true, "gatekeeperAssess": false, @@ -114,7 +112,7 @@ "linux": { "asar": true, "target": "AppImage", - "icon": "src/electron/renderer/assets/img/logo-guide-draft.png", + "icon": "src/electron/frontend/assets/img/logo-guide-draft.png", "extraResources": [ { "from": "./build/flask/nwb-guide", @@ -127,7 +125,7 @@ "oneClick": false, "perMachine": false, "allowToChangeInstallationDirectory": true, - "installerIcon": "src/electron/renderer/assets/app-icon/logo-guide-draft.ico" + "installerIcon": "src/electron/frontend/assets/app-icon/logo-guide-draft.ico" }, "publish": { "provider": "github" diff --git a/src/electron/renderer/assets/app-icon/logo-guide-draft.ico b/src/electron/frontend/assets/app-icon/logo-guide-draft.ico similarity index 100% rename from src/electron/renderer/assets/app-icon/logo-guide-draft.ico rename to src/electron/frontend/assets/app-icon/logo-guide-draft.ico diff --git a/src/electron/frontend/assets/css/custom.css b/src/electron/frontend/assets/css/custom.css new file mode 100644 index 0000000000..8b422f30c6 --- /dev/null +++ b/src/electron/frontend/assets/css/custom.css @@ -0,0 +1,51 @@ +/* Update Notification Box */ +#update-available:not(:empty) { + margin-top: 15px; + background-color: #ffe5d2; + color: black; + padding: 15px 10px; + border: 1px solid #f6c39c; + border-radius: 5px; +} + +#update-available .update-container { + display: flex; + align-items: center; + gap: 15px; +} + +#update-available .update-container::before { + content: ""; + width: 6px; + height: 6px; + margin-left: 5px; + background-color: #d17128; + border-radius: 50%; +} + +#update-available .header { + display: flex; + align-items: center; + gap: 5px; +} + +#update-available h4 { + margin: 0; +} + +#update-available span { + color: gray; + font-size: 80%; +} + +#update-available .header svg { + height: 15px; + width: auto; + cursor: pointer; +} + +#update-available .controls { + margin-left: auto; + display: flex; + gap: 10px; +} diff --git a/src/electron/renderer/assets/css/demo.css b/src/electron/frontend/assets/css/demo.css similarity index 100% rename from src/electron/renderer/assets/css/demo.css rename to src/electron/frontend/assets/css/demo.css diff --git a/src/electron/renderer/assets/css/fontawesome.css b/src/electron/frontend/assets/css/fontawesome.css similarity index 100% rename from src/electron/renderer/assets/css/fontawesome.css rename to src/electron/frontend/assets/css/fontawesome.css diff --git a/src/electron/renderer/assets/css/global.css b/src/electron/frontend/assets/css/global.css similarity index 100% rename from src/electron/renderer/assets/css/global.css rename to src/electron/frontend/assets/css/global.css diff --git a/src/electron/renderer/assets/css/guided.css b/src/electron/frontend/assets/css/guided.css similarity index 100% rename from src/electron/renderer/assets/css/guided.css rename to src/electron/frontend/assets/css/guided.css diff --git a/src/electron/renderer/assets/css/individualtab.css b/src/electron/frontend/assets/css/individualtab.css similarity index 100% rename from src/electron/renderer/assets/css/individualtab.css rename to src/electron/frontend/assets/css/individualtab.css diff --git a/src/electron/renderer/assets/css/main.css b/src/electron/frontend/assets/css/main.css similarity index 100% rename from src/electron/renderer/assets/css/main.css rename to src/electron/frontend/assets/css/main.css diff --git a/src/electron/renderer/assets/css/main_tabs.css b/src/electron/frontend/assets/css/main_tabs.css similarity index 100% rename from src/electron/renderer/assets/css/main_tabs.css rename to src/electron/frontend/assets/css/main_tabs.css diff --git a/src/electron/renderer/assets/css/nativize.css b/src/electron/frontend/assets/css/nativize.css similarity index 100% rename from src/electron/renderer/assets/css/nativize.css rename to src/electron/frontend/assets/css/nativize.css diff --git a/src/electron/renderer/assets/css/nav.css b/src/electron/frontend/assets/css/nav.css similarity index 90% rename from src/electron/renderer/assets/css/nav.css rename to src/electron/frontend/assets/css/nav.css index f0f0576c98..a5a15982c9 100755 --- a/src/electron/renderer/assets/css/nav.css +++ b/src/electron/frontend/assets/css/nav.css @@ -158,8 +158,10 @@ a[data-toggle="collapse"] { #main-nav .sidebar-body a { font-size: 14px; - display: block; - line-height: 45px; + display: flex; + align-items: center; + position: relative; + padding: 15px 0px; padding-left: 20px; margin-bottom: 5px; text-align: left; @@ -170,6 +172,19 @@ a[data-toggle="collapse"] { border-left: 4px solid transparent; } +[data-update-available] [data-id="settings"] > div { + display: flex; + flex-direction: column; +} + +[data-update-available] [data-id="settings"] > div::after { + content: "Update Available"; + color: gray; + font-size: 70%; + font-weight: normal; + line-height: 1.5; +} + #main-nav .sidebar-body svg { fill: #000; } diff --git a/src/electron/renderer/assets/css/print.css b/src/electron/frontend/assets/css/print.css similarity index 100% rename from src/electron/renderer/assets/css/print.css rename to src/electron/frontend/assets/css/print.css diff --git a/src/electron/renderer/assets/css/section.css b/src/electron/frontend/assets/css/section.css similarity index 100% rename from src/electron/renderer/assets/css/section.css rename to src/electron/frontend/assets/css/section.css diff --git a/src/electron/renderer/assets/css/spur.css b/src/electron/frontend/assets/css/spur.css similarity index 100% rename from src/electron/renderer/assets/css/spur.css rename to src/electron/frontend/assets/css/spur.css diff --git a/src/electron/renderer/assets/css/tablepath.css b/src/electron/frontend/assets/css/tablepath.css similarity index 100% rename from src/electron/renderer/assets/css/tablepath.css rename to src/electron/frontend/assets/css/tablepath.css diff --git a/src/electron/renderer/assets/css/variables.css b/src/electron/frontend/assets/css/variables.css similarity index 100% rename from src/electron/renderer/assets/css/variables.css rename to src/electron/frontend/assets/css/variables.css diff --git a/src/electron/renderer/assets/icons/dandi.svg b/src/electron/frontend/assets/icons/dandi.svg similarity index 99% rename from src/electron/renderer/assets/icons/dandi.svg rename to src/electron/frontend/assets/icons/dandi.svg index 23dcdd8d12..0b62d3ddb0 100644 --- a/src/electron/renderer/assets/icons/dandi.svg +++ b/src/electron/frontend/assets/icons/dandi.svg @@ -3,7 +3,7 @@ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> Created by potrace 1.16, written by Peter Selinger 2001-2019 diff --git a/src/electron/renderer/assets/icons/delete.svg b/src/electron/frontend/assets/icons/delete.svg similarity index 100% rename from src/electron/renderer/assets/icons/delete.svg rename to src/electron/frontend/assets/icons/delete.svg diff --git a/src/electron/frontend/assets/icons/download.svg b/src/electron/frontend/assets/icons/download.svg new file mode 100644 index 0000000000..7b794bd408 --- /dev/null +++ b/src/electron/frontend/assets/icons/download.svg @@ -0,0 +1 @@ + diff --git a/src/electron/renderer/assets/icons/exploration.svg b/src/electron/frontend/assets/icons/exploration.svg similarity index 93% rename from src/electron/renderer/assets/icons/exploration.svg rename to src/electron/frontend/assets/icons/exploration.svg index 2b5f3e8fc9..082dc98def 100644 --- a/src/electron/renderer/assets/icons/exploration.svg +++ b/src/electron/frontend/assets/icons/exploration.svg @@ -3,5 +3,5 @@ viewBox="0 -960 960 960" width="22" height="22" - style="margin-right: 28px; margin-bottom: -5px" + style="margin-right: 28px;" > diff --git a/src/electron/renderer/assets/icons/folder_open.svg b/src/electron/frontend/assets/icons/folder_open.svg similarity index 100% rename from src/electron/renderer/assets/icons/folder_open.svg rename to src/electron/frontend/assets/icons/folder_open.svg diff --git a/src/electron/renderer/assets/icons/fullscreen.svg b/src/electron/frontend/assets/icons/fullscreen.svg similarity index 100% rename from src/electron/renderer/assets/icons/fullscreen.svg rename to src/electron/frontend/assets/icons/fullscreen.svg diff --git a/src/electron/renderer/assets/icons/fullscreen_exit.svg b/src/electron/frontend/assets/icons/fullscreen_exit.svg similarity index 100% rename from src/electron/renderer/assets/icons/fullscreen_exit.svg rename to src/electron/frontend/assets/icons/fullscreen_exit.svg diff --git a/src/electron/renderer/assets/icons/global.svg b/src/electron/frontend/assets/icons/global.svg similarity index 100% rename from src/electron/renderer/assets/icons/global.svg rename to src/electron/frontend/assets/icons/global.svg diff --git a/src/electron/frontend/assets/icons/info.svg b/src/electron/frontend/assets/icons/info.svg new file mode 100644 index 0000000000..9dfdef6d67 --- /dev/null +++ b/src/electron/frontend/assets/icons/info.svg @@ -0,0 +1 @@ + diff --git a/src/electron/renderer/assets/icons/inspect.svg b/src/electron/frontend/assets/icons/inspect.svg similarity index 90% rename from src/electron/renderer/assets/icons/inspect.svg rename to src/electron/frontend/assets/icons/inspect.svg index d788e86ff4..dcb66ff7af 100644 --- a/src/electron/renderer/assets/icons/inspect.svg +++ b/src/electron/frontend/assets/icons/inspect.svg @@ -3,5 +3,5 @@ xmlns="http://www.w3.org/2000/svg" height="25" viewBox="0 -960 960 960" width="25" -style="margin-right: 25px; margin-bottom: -5px" +style="margin-right: 25px;" > diff --git a/src/electron/renderer/assets/icons/key.svg b/src/electron/frontend/assets/icons/key.svg similarity index 100% rename from src/electron/renderer/assets/icons/key.svg rename to src/electron/frontend/assets/icons/key.svg diff --git a/src/electron/renderer/assets/icons/neurosift-logo.svg b/src/electron/frontend/assets/icons/neurosift-logo.svg similarity index 96% rename from src/electron/renderer/assets/icons/neurosift-logo.svg rename to src/electron/frontend/assets/icons/neurosift-logo.svg index 6886c10c28..90368db67b 100644 --- a/src/electron/renderer/assets/icons/neurosift-logo.svg +++ b/src/electron/frontend/assets/icons/neurosift-logo.svg @@ -6,7 +6,7 @@ xmlns="http://www.w3.org/2000/svg" height="22" width="22" - style="margin-right: 28px; margin-bottom: -5px" + style="margin-right: 28px;" viewBox="0 0 192.000000 192.000000" preserveAspectRatio="xMidYMid meet" > diff --git a/src/electron/renderer/assets/icons/preview.svg b/src/electron/frontend/assets/icons/preview.svg similarity index 90% rename from src/electron/renderer/assets/icons/preview.svg rename to src/electron/frontend/assets/icons/preview.svg index 467b1d1868..192e9bada2 100644 --- a/src/electron/renderer/assets/icons/preview.svg +++ b/src/electron/frontend/assets/icons/preview.svg @@ -3,5 +3,5 @@ xmlns="http://www.w3.org/2000/svg" height="25" viewBox="0 -960 960 960" width="25" -style="margin-right: 30px; margin-bottom: -5px" +style="margin-right: 30px;" > diff --git a/src/electron/renderer/assets/icons/python.svg b/src/electron/frontend/assets/icons/python.svg similarity index 100% rename from src/electron/renderer/assets/icons/python.svg rename to src/electron/frontend/assets/icons/python.svg diff --git a/src/electron/renderer/assets/icons/restart.svg b/src/electron/frontend/assets/icons/restart.svg similarity index 100% rename from src/electron/renderer/assets/icons/restart.svg rename to src/electron/frontend/assets/icons/restart.svg diff --git a/src/electron/renderer/assets/icons/save.svg b/src/electron/frontend/assets/icons/save.svg similarity index 100% rename from src/electron/renderer/assets/icons/save.svg rename to src/electron/frontend/assets/icons/save.svg diff --git a/src/electron/renderer/assets/icons/search.svg b/src/electron/frontend/assets/icons/search.svg similarity index 100% rename from src/electron/renderer/assets/icons/search.svg rename to src/electron/frontend/assets/icons/search.svg diff --git a/src/electron/renderer/assets/icons/server.svg b/src/electron/frontend/assets/icons/server.svg similarity index 100% rename from src/electron/renderer/assets/icons/server.svg rename to src/electron/frontend/assets/icons/server.svg diff --git a/src/electron/renderer/assets/icons/settings.svg b/src/electron/frontend/assets/icons/settings.svg similarity index 94% rename from src/electron/renderer/assets/icons/settings.svg rename to src/electron/frontend/assets/icons/settings.svg index 273e062186..41928d8075 100644 --- a/src/electron/renderer/assets/icons/settings.svg +++ b/src/electron/frontend/assets/icons/settings.svg @@ -3,5 +3,5 @@ height="25" viewBox="0 -960 960 960" width="25" - style="margin-right: 25px; margin-bottom: -5px" + style="margin-right: 25px;" > diff --git a/src/electron/renderer/assets/icons/web_asset.svg b/src/electron/frontend/assets/icons/web_asset.svg similarity index 100% rename from src/electron/renderer/assets/icons/web_asset.svg rename to src/electron/frontend/assets/icons/web_asset.svg diff --git a/src/electron/renderer/assets/icons/wifi.svg b/src/electron/frontend/assets/icons/wifi.svg similarity index 100% rename from src/electron/renderer/assets/icons/wifi.svg rename to src/electron/frontend/assets/icons/wifi.svg diff --git a/src/electron/renderer/assets/img/logo-catalystneuro.png b/src/electron/frontend/assets/img/logo-catalystneuro.png similarity index 100% rename from src/electron/renderer/assets/img/logo-catalystneuro.png rename to src/electron/frontend/assets/img/logo-catalystneuro.png diff --git a/src/electron/renderer/assets/img/logo-guide-draft-transparent-tight.png b/src/electron/frontend/assets/img/logo-guide-draft-transparent-tight.png similarity index 100% rename from src/electron/renderer/assets/img/logo-guide-draft-transparent-tight.png rename to src/electron/frontend/assets/img/logo-guide-draft-transparent-tight.png diff --git a/src/electron/renderer/assets/img/logo-guide-draft-transparent.png b/src/electron/frontend/assets/img/logo-guide-draft-transparent.png similarity index 100% rename from src/electron/renderer/assets/img/logo-guide-draft-transparent.png rename to src/electron/frontend/assets/img/logo-guide-draft-transparent.png diff --git a/src/electron/renderer/assets/img/logo-guide-draft.png b/src/electron/frontend/assets/img/logo-guide-draft.png similarity index 100% rename from src/electron/renderer/assets/img/logo-guide-draft.png rename to src/electron/frontend/assets/img/logo-guide-draft.png diff --git a/src/electron/renderer/assets/img/logo-neuroconv.png b/src/electron/frontend/assets/img/logo-neuroconv.png similarity index 100% rename from src/electron/renderer/assets/img/logo-neuroconv.png rename to src/electron/frontend/assets/img/logo-neuroconv.png diff --git a/src/electron/renderer/assets/lotties/contact-us-lotties.js b/src/electron/frontend/assets/lotties/contact-us-lotties.js similarity index 100% rename from src/electron/renderer/assets/lotties/contact-us-lotties.js rename to src/electron/frontend/assets/lotties/contact-us-lotties.js diff --git a/src/electron/renderer/assets/lotties/documentation-lotties.js b/src/electron/frontend/assets/lotties/documentation-lotties.js similarity index 100% rename from src/electron/renderer/assets/lotties/documentation-lotties.js rename to src/electron/frontend/assets/lotties/documentation-lotties.js diff --git a/src/electron/renderer/assets/lotties/index.js b/src/electron/frontend/assets/lotties/index.js similarity index 100% rename from src/electron/renderer/assets/lotties/index.js rename to src/electron/frontend/assets/lotties/index.js diff --git a/src/electron/renderer/assets/lotties/overview-lotties.js b/src/electron/frontend/assets/lotties/overview-lotties.js similarity index 100% rename from src/electron/renderer/assets/lotties/overview-lotties.js rename to src/electron/frontend/assets/lotties/overview-lotties.js diff --git a/src/electron/renderer/src/stories/Accordion.js b/src/electron/frontend/core/components/Accordion.js similarity index 100% rename from src/electron/renderer/src/stories/Accordion.js rename to src/electron/frontend/core/components/Accordion.js diff --git a/src/electron/renderer/src/stories/BasicTable.js b/src/electron/frontend/core/components/BasicTable.js similarity index 100% rename from src/electron/renderer/src/stories/BasicTable.js rename to src/electron/frontend/core/components/BasicTable.js diff --git a/src/electron/renderer/src/stories/Button.js b/src/electron/frontend/core/components/Button.js similarity index 100% rename from src/electron/renderer/src/stories/Button.js rename to src/electron/frontend/core/components/Button.js diff --git a/src/electron/renderer/src/stories/Chevron.js b/src/electron/frontend/core/components/Chevron.js similarity index 100% rename from src/electron/renderer/src/stories/Chevron.js rename to src/electron/frontend/core/components/Chevron.js diff --git a/src/electron/frontend/core/components/CodeBlock.js b/src/electron/frontend/core/components/CodeBlock.js new file mode 100644 index 0000000000..2858360232 --- /dev/null +++ b/src/electron/frontend/core/components/CodeBlock.js @@ -0,0 +1,68 @@ +import { LitElement, css, html } from "lit"; +import { Button } from "./Button"; + +export class CodeBlock extends LitElement { + static get styles() { + return css` + :host { + display: block; + position: relative; + font-size: 85%; + background: #111; + color: whitesmoke; + border-radius: 10px; + border: 1px solid gray; + overflow: hidden; + } + + pre { + overflow: auto; + padding: 15px; + box-sizing: border-box; + user-select: text; + margin: 0; + } + `; + } + + constructor({ text = "" }) { + super(); + this.text = text; + } + + render() { + const controls = document.createElement("div"); + + setTimeout(() => { + console.log(controls); + }, 1000); + + const copyButton = new Button({ + label: "Copy", + onClick: () => navigator.clipboard.writeText(this.text), + primary: true, + color: "rgba(0, 0, 0, 0.3)", + buttonStyles: { + color: "white", + fontSize: "85%", + borderRadius: "5px", + border: "1px solid rgba(255, 255, 255, 0.5)", + }, + }); + + Object.assign(controls.style, { + position: "absolute", + bottom: "10px", + right: "10px", + }); + + controls.append(copyButton); + + return html` + ${controls} +

${this.text}
+ `; + } +} + +customElements.get("code-block") || customElements.define("code-block", CodeBlock); diff --git a/src/electron/renderer/src/stories/DandiResults.js b/src/electron/frontend/core/components/DandiResults.js similarity index 100% rename from src/electron/renderer/src/stories/DandiResults.js rename to src/electron/frontend/core/components/DandiResults.js diff --git a/src/electron/renderer/src/stories/Dashboard.js b/src/electron/frontend/core/components/Dashboard.js similarity index 84% rename from src/electron/renderer/src/stories/Dashboard.js rename to src/electron/frontend/core/components/Dashboard.js index 74a228430c..9dd1287244 100644 --- a/src/electron/renderer/src/stories/Dashboard.js +++ b/src/electron/frontend/core/components/Dashboard.js @@ -5,6 +5,9 @@ import { Main, checkIfPageIsSkipped } from "./Main.js"; import { Sidebar } from "./sidebar.js"; import { NavigationSidebar } from "./NavigationSidebar.js"; +// Defined by Garrett late in GUIDE development to clearly separate global styles unrelated to SODA (May 20th, 2024) +import "../../assets/css/custom.css"; + // Global styles to apply with the dashboard import "../../assets/css/variables.css"; import "../../assets/css/nativize.css"; @@ -29,8 +32,8 @@ import "../../../../../node_modules/fomantic-ui/dist/components/accordion.min.cs import "../../../../../node_modules/@sweetalert2/theme-bulma/bulma.css"; // import "../../node_modules/intro.js/minified/introjs.min.css" import "../../assets/css/guided.css"; -import isElectron from "../electron/check.js"; -import { isStorybook, reloadPageToHome } from "../dependencies/globals"; +import { isElectron } from "../../utils/electron.js"; +import { isStorybook, reloadPageToHome } from "../globals.js"; import { getCurrentProjectName, updateAppProgress } from "../progress/index.js"; // import "https://jsuites.net/v4/jsuites.js" @@ -242,32 +245,44 @@ export class Dashboard extends LitElement { this.page.set(toPass, false); - this.page.checkSyncState().then(async () => { - const projectName = info.globalState?.project?.name; - - this.subSidebar.header = projectName - ? `

${projectName}

Conversion Pipeline` - : projectName; - - this.updateSections({ sidebar: false, main: true }); - - if (this.#transitionPromise.value) this.#transitionPromise.trigger(page); // This ensures calls to page.to() can be properly awaited until the next page is ready - - const { skipped } = this.subSidebar.sections[info.section]?.pages?.[info.id] ?? {}; - - if (skipped) { - if (isStorybook) return; // Do not skip on storybook - - // Run skip functions - Object.entries(page.workflow).forEach(([key, state]) => { - if (typeof state.skip === "function") state.skip(); - }); - - // Skip right over the page if configured as such - if (previous && previous.info.previous === this.page) await this.page.onTransition(-1); - else await this.page.onTransition(1); - } - }); + this.page + .checkSyncState() + .then(async () => { + const projectName = info.globalState?.project?.name; + + this.subSidebar.header = projectName + ? `

${projectName}

Conversion Pipeline` + : projectName; + + this.updateSections({ sidebar: false, main: true }); + + if (this.#transitionPromise.value) this.#transitionPromise.trigger(page); // This ensures calls to page.to() can be properly awaited until the next page is ready + + const { skipped } = this.subSidebar.sections[info.section]?.pages?.[info.id] ?? {}; + + if (skipped) { + if (isStorybook) return; // Do not skip on storybook + + // Run skip functions + Object.entries(page.workflow).forEach(([key, state]) => { + if (typeof state.skip === "function") state.skip(); + }); + + // Skip right over the page if configured as such + if (previous && previous.info.previous === this.page) await this.page.onTransition(-1); + else await this.page.onTransition(1); + } + }) + .catch((e) => { + const previousId = previous?.info?.id; + if (previousId) { + page.onTransition(previousId); // Revert back to previous page + page.notify( + `

Fallback to previous page after error occurred

${e}`, + "error" + ); + } else reloadPageToHome(); + }); } // Populate the sections tracked for this page by using the global state as a model @@ -282,7 +297,12 @@ export class Dashboard extends LitElement { const section = info.section; let state = globalState.sections[section]; - if (!state) state = globalState.sections[section] = { open: false, active: false, pages: {} }; + if (!state) + state = globalState.sections[section] = { + open: false, + active: false, + pages: {}, + }; let pageState = state.pages[id]; if (!pageState) diff --git a/src/electron/renderer/src/stories/DateTimeSelector.js b/src/electron/frontend/core/components/DateTimeSelector.js similarity index 100% rename from src/electron/renderer/src/stories/DateTimeSelector.js rename to src/electron/frontend/core/components/DateTimeSelector.js diff --git a/src/electron/renderer/src/stories/FileSystemSelector.js b/src/electron/frontend/core/components/FileSystemSelector.js similarity index 96% rename from src/electron/renderer/src/stories/FileSystemSelector.js rename to src/electron/frontend/core/components/FileSystemSelector.js index 3bef333787..da0c10140b 100644 --- a/src/electron/renderer/src/stories/FileSystemSelector.js +++ b/src/electron/frontend/core/components/FileSystemSelector.js @@ -1,6 +1,6 @@ import { LitElement, css, html } from "lit"; -import { fs, remote } from "../electron/index"; +import { fs, remote } from "../../utils/electron"; import { List } from "./List"; const { dialog } = remote; diff --git a/src/electron/renderer/src/stories/Footer.js b/src/electron/frontend/core/components/Footer.js similarity index 100% rename from src/electron/renderer/src/stories/Footer.js rename to src/electron/frontend/core/components/Footer.js diff --git a/src/electron/renderer/src/stories/FullScreenToggle.ts b/src/electron/frontend/core/components/FullScreenToggle.ts similarity index 100% rename from src/electron/renderer/src/stories/FullScreenToggle.ts rename to src/electron/frontend/core/components/FullScreenToggle.ts diff --git a/src/electron/renderer/src/stories/InfoBox.js b/src/electron/frontend/core/components/InfoBox.js similarity index 100% rename from src/electron/renderer/src/stories/InfoBox.js rename to src/electron/frontend/core/components/InfoBox.js diff --git a/src/electron/renderer/src/stories/InstanceManager.js b/src/electron/frontend/core/components/InstanceManager.js similarity index 96% rename from src/electron/renderer/src/stories/InstanceManager.js rename to src/electron/frontend/core/components/InstanceManager.js index 41ecd6e37a..cc50717135 100644 --- a/src/electron/renderer/src/stories/InstanceManager.js +++ b/src/electron/frontend/core/components/InstanceManager.js @@ -1,6 +1,6 @@ import { LitElement, css, html } from "lit"; import "./Button"; -import { notify } from "../dependencies/globals"; +import { notify } from "../dependencies"; import { Accordion } from "./Accordion"; import { InstanceListItem } from "./instances/item"; import { checkStatus } from "../validation"; diff --git a/src/electron/renderer/src/stories/JSONSchemaForm.js b/src/electron/frontend/core/components/JSONSchemaForm.js similarity index 100% rename from src/electron/renderer/src/stories/JSONSchemaForm.js rename to src/electron/frontend/core/components/JSONSchemaForm.js diff --git a/src/electron/renderer/src/stories/JSONSchemaInput.js b/src/electron/frontend/core/components/JSONSchemaInput.js similarity index 100% rename from src/electron/renderer/src/stories/JSONSchemaInput.js rename to src/electron/frontend/core/components/JSONSchemaInput.js diff --git a/src/electron/renderer/src/stories/List.ts b/src/electron/frontend/core/components/List.ts similarity index 100% rename from src/electron/renderer/src/stories/List.ts rename to src/electron/frontend/core/components/List.ts diff --git a/src/electron/renderer/src/stories/Loader.ts b/src/electron/frontend/core/components/Loader.ts similarity index 100% rename from src/electron/renderer/src/stories/Loader.ts rename to src/electron/frontend/core/components/Loader.ts diff --git a/src/electron/renderer/src/stories/Main.js b/src/electron/frontend/core/components/Main.js similarity index 100% rename from src/electron/renderer/src/stories/Main.js rename to src/electron/frontend/core/components/Main.js diff --git a/src/electron/renderer/src/stories/Modal.ts b/src/electron/frontend/core/components/Modal.ts similarity index 100% rename from src/electron/renderer/src/stories/Modal.ts rename to src/electron/frontend/core/components/Modal.ts diff --git a/src/electron/renderer/src/stories/NavigationSidebar.js b/src/electron/frontend/core/components/NavigationSidebar.js similarity index 100% rename from src/electron/renderer/src/stories/NavigationSidebar.js rename to src/electron/frontend/core/components/NavigationSidebar.js diff --git a/src/electron/renderer/src/stories/OptionalSection.js b/src/electron/frontend/core/components/OptionalSection.js similarity index 100% rename from src/electron/renderer/src/stories/OptionalSection.js rename to src/electron/frontend/core/components/OptionalSection.js diff --git a/src/electron/renderer/src/stories/Overlay.ts b/src/electron/frontend/core/components/Overlay.ts similarity index 100% rename from src/electron/renderer/src/stories/Overlay.ts rename to src/electron/frontend/core/components/Overlay.ts diff --git a/src/electron/renderer/src/stories/ProgressBar.ts b/src/electron/frontend/core/components/ProgressBar.ts similarity index 66% rename from src/electron/renderer/src/stories/ProgressBar.ts rename to src/electron/frontend/core/components/ProgressBar.ts index 06da911777..bb30bbdbd7 100644 --- a/src/electron/renderer/src/stories/ProgressBar.ts +++ b/src/electron/frontend/core/components/ProgressBar.ts @@ -5,13 +5,37 @@ import { LitElement, html, css, unsafeCSS } from 'lit'; export type ProgressProps = { size?: string, + isBytes?: boolean, + format?: { n: number, total: number, [key: string]: any, - }, + } } +export function humanReadableBytes(size: number | string) { + + // Define the units + const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + + // Initialize the index to 0 + let index = 0; + + // Convert the size to a floating point number + size = parseFloat(size); + + // Loop until the size is less than 1024 and increment the unit + while (size >= 1000 && index < units.length - 1) { + size /= 1000; + index += 1; + } + + // Return the size formatted with 2 decimal places and the appropriate unit + return `${size.toFixed(2)} ${units[index]}`; +} + + const animationDuration = 500 // ms export class ProgressBar extends LitElement { @@ -88,12 +112,14 @@ export class ProgressBar extends LitElement { declare format: any declare size: string + declare isBytes: boolean constructor(props: ProgressProps = {}) { super(); this.size = props.size ?? 'medium' this.format = props.format ?? {} + this.isBytes = props.isBytes ?? false if (!('n' in this.format)) this.format.n = 0 if (!('total' in this.format)) this.format.total = 0 } @@ -103,6 +129,17 @@ export class ProgressBar extends LitElement { const percent = this.format.total ? 100 * (this.format.n / this.format.total) : 0; const remaining = this.format.rate && this.format.total ? (this.format.total - this.format.n) / this.format.rate : 0; // Seconds + const numerator = this.isBytes ? humanReadableBytes(this.format.n) : this.format.n + const denominator = this.isBytes ? humanReadableBytes(this.format.total) : this.format.total + + + const elapsed = this.format.elapsed + + let subMessage = '' + if ('elapsed' in this.format && 'rate' in this.format) subMessage = `${elapsed?.toFixed(1)}s elapsed, ${remaining.toFixed(1)}s remaining` + else if ('elapsed' in this.format) subMessage = `${elapsed?.toFixed(1)}s elapsed` + else if ('rate' in this.format) subMessage = `${remaining.toFixed(1)}s remaining` + return html`
${this.format.prefix ? html`
@@ -112,10 +149,10 @@ export class ProgressBar extends LitElement {
- ${this.format.n} / ${this.format.total} (${percent.toFixed(1)}%) + ${numerator} / ${denominator} (${percent.toFixed(1)}%)
- ${'elapsed' in this.format && 'rate' in this.format ? html`${this.format.elapsed?.toFixed(1)}s elapsed, ${remaining.toFixed(1)}s remaining` : ''} + ${subMessage ? html`${subMessage}` : ''}
`; diff --git a/src/electron/renderer/src/stories/Search.js b/src/electron/frontend/core/components/Search.js similarity index 100% rename from src/electron/renderer/src/stories/Search.js rename to src/electron/frontend/core/components/Search.js diff --git a/src/electron/renderer/src/stories/SimpleTable.js b/src/electron/frontend/core/components/SimpleTable.js similarity index 100% rename from src/electron/renderer/src/stories/SimpleTable.js rename to src/electron/frontend/core/components/SimpleTable.js diff --git a/src/electron/renderer/src/stories/Table.js b/src/electron/frontend/core/components/Table.js similarity index 100% rename from src/electron/renderer/src/stories/Table.js rename to src/electron/frontend/core/components/Table.js diff --git a/src/electron/renderer/src/stories/forms/GlobalFormModal.ts b/src/electron/frontend/core/components/forms/GlobalFormModal.ts similarity index 100% rename from src/electron/renderer/src/stories/forms/GlobalFormModal.ts rename to src/electron/frontend/core/components/forms/GlobalFormModal.ts diff --git a/src/electron/renderer/src/stories/forms/utils.ts b/src/electron/frontend/core/components/forms/utils.ts similarity index 100% rename from src/electron/renderer/src/stories/forms/utils.ts rename to src/electron/frontend/core/components/forms/utils.ts diff --git a/src/electron/renderer/src/stories/globals.js b/src/electron/frontend/core/components/globals.js similarity index 100% rename from src/electron/renderer/src/stories/globals.js rename to src/electron/frontend/core/components/globals.js diff --git a/src/electron/renderer/src/stories/hot.js b/src/electron/frontend/core/components/hot.js similarity index 100% rename from src/electron/renderer/src/stories/hot.js rename to src/electron/frontend/core/components/hot.js diff --git a/src/electron/renderer/src/stories/instances/item.ts b/src/electron/frontend/core/components/instances/item.ts similarity index 100% rename from src/electron/renderer/src/stories/instances/item.ts rename to src/electron/frontend/core/components/instances/item.ts diff --git a/src/electron/renderer/src/stories/multiselect/MultiSelectForm.js b/src/electron/frontend/core/components/multiselect/MultiSelectForm.js similarity index 100% rename from src/electron/renderer/src/stories/multiselect/MultiSelectForm.js rename to src/electron/frontend/core/components/multiselect/MultiSelectForm.js diff --git a/src/electron/renderer/src/stories/pages/FormPage.js b/src/electron/frontend/core/components/pages/FormPage.js similarity index 100% rename from src/electron/renderer/src/stories/pages/FormPage.js rename to src/electron/frontend/core/components/pages/FormPage.js diff --git a/src/electron/renderer/src/stories/pages/Page.js b/src/electron/frontend/core/components/pages/Page.js similarity index 96% rename from src/electron/renderer/src/stories/pages/Page.js rename to src/electron/frontend/core/components/pages/Page.js index 18ffd028f3..e40b2602fc 100644 --- a/src/electron/renderer/src/stories/pages/Page.js +++ b/src/electron/frontend/core/components/pages/Page.js @@ -1,7 +1,10 @@ import { LitElement, html } from "lit"; import { runConversion } from "./guided-mode/options/utils.js"; import { get, save } from "../../progress/index.js"; -import { dismissNotification, isStorybook, notify } from "../../dependencies/globals"; + +import { dismissNotification, notify } from "../../dependencies.js"; +import { isStorybook } from "../../globals.js"; + import { randomizeElements, mapSessions, merge } from "./utils"; import { resolveMetadata } from "./guided-mode/data/utils.js"; diff --git a/src/electron/renderer/src/stories/pages/Unsafe.js b/src/electron/frontend/core/components/pages/Unsafe.js similarity index 100% rename from src/electron/renderer/src/stories/pages/Unsafe.js rename to src/electron/frontend/core/components/pages/Unsafe.js diff --git a/src/electron/renderer/src/stories/pages/contact-us/Contact.js b/src/electron/frontend/core/components/pages/contact-us/Contact.js similarity index 94% rename from src/electron/renderer/src/stories/pages/contact-us/Contact.js rename to src/electron/frontend/core/components/pages/contact-us/Contact.js index 90e7ade163..2b8995dae9 100644 --- a/src/electron/renderer/src/stories/pages/contact-us/Contact.js +++ b/src/electron/frontend/core/components/pages/contact-us/Contact.js @@ -2,7 +2,7 @@ import { html } from "lit"; import { contact_lottie } from "../../../../assets/lotties/contact-us-lotties.js"; import { Page } from "../Page.js"; -import { startLottie } from "../../../dependencies/globals"; +import { startLottie } from "../../../dependencies.js"; export class ContactPage extends Page { header = { diff --git a/src/electron/renderer/src/stories/pages/documentation/Documentation.js b/src/electron/frontend/core/components/pages/documentation/Documentation.js similarity index 96% rename from src/electron/renderer/src/stories/pages/documentation/Documentation.js rename to src/electron/frontend/core/components/pages/documentation/Documentation.js index 023e05c51c..77ca419d45 100644 --- a/src/electron/renderer/src/stories/pages/documentation/Documentation.js +++ b/src/electron/frontend/core/components/pages/documentation/Documentation.js @@ -2,7 +2,7 @@ import { html } from "lit"; import { docu_lottie } from "../../../../assets/lotties/documentation-lotties.js"; import { Page } from "../Page.js"; -import { startLottie } from "../../../dependencies/globals"; +import { startLottie } from "../../../dependencies.js"; import { Button } from "../../Button.js"; diff --git a/src/electron/renderer/src/stories/pages/globals.js b/src/electron/frontend/core/components/pages/globals.js similarity index 100% rename from src/electron/renderer/src/stories/pages/globals.js rename to src/electron/frontend/core/components/pages/globals.js diff --git a/src/electron/renderer/src/stories/pages/guided-mode/GuidedFooter.js b/src/electron/frontend/core/components/pages/guided-mode/GuidedFooter.js similarity index 100% rename from src/electron/renderer/src/stories/pages/guided-mode/GuidedFooter.js rename to src/electron/frontend/core/components/pages/guided-mode/GuidedFooter.js diff --git a/src/electron/renderer/src/stories/pages/guided-mode/GuidedHeader.js b/src/electron/frontend/core/components/pages/guided-mode/GuidedHeader.js similarity index 100% rename from src/electron/renderer/src/stories/pages/guided-mode/GuidedHeader.js rename to src/electron/frontend/core/components/pages/guided-mode/GuidedHeader.js diff --git a/src/electron/renderer/src/stories/pages/guided-mode/GuidedHome.js b/src/electron/frontend/core/components/pages/guided-mode/GuidedHome.js similarity index 97% rename from src/electron/renderer/src/stories/pages/guided-mode/GuidedHome.js rename to src/electron/frontend/core/components/pages/guided-mode/GuidedHome.js index 07d4fa1c85..a71dc03d6b 100644 --- a/src/electron/renderer/src/stories/pages/guided-mode/GuidedHome.js +++ b/src/electron/frontend/core/components/pages/guided-mode/GuidedHome.js @@ -2,7 +2,7 @@ import { html } from "lit"; import { Page } from "../Page.js"; import { ProgressCard } from "./ProgressCard.js"; -import { startLottie } from "../../../dependencies/globals"; +import { startLottie } from "../../../dependencies.js"; import * as progress from "../../../progress/index.js"; import { newDataset } from "../../../../assets/lotties/index.js"; diff --git a/src/electron/renderer/src/stories/pages/guided-mode/GuidedStart.js b/src/electron/frontend/core/components/pages/guided-mode/GuidedStart.js similarity index 100% rename from src/electron/renderer/src/stories/pages/guided-mode/GuidedStart.js rename to src/electron/frontend/core/components/pages/guided-mode/GuidedStart.js diff --git a/src/electron/renderer/src/stories/pages/guided-mode/ProgressCard.js b/src/electron/frontend/core/components/pages/guided-mode/ProgressCard.js similarity index 100% rename from src/electron/renderer/src/stories/pages/guided-mode/ProgressCard.js rename to src/electron/frontend/core/components/pages/guided-mode/ProgressCard.js diff --git a/src/electron/renderer/src/stories/pages/guided-mode/data/GuidedMetadata.js b/src/electron/frontend/core/components/pages/guided-mode/data/GuidedMetadata.js similarity index 100% rename from src/electron/renderer/src/stories/pages/guided-mode/data/GuidedMetadata.js rename to src/electron/frontend/core/components/pages/guided-mode/data/GuidedMetadata.js diff --git a/src/electron/renderer/src/stories/pages/guided-mode/data/GuidedPathExpansion.js b/src/electron/frontend/core/components/pages/guided-mode/data/GuidedPathExpansion.js similarity index 89% rename from src/electron/renderer/src/stories/pages/guided-mode/data/GuidedPathExpansion.js rename to src/electron/frontend/core/components/pages/guided-mode/data/GuidedPathExpansion.js index 4fa48857a5..20e4e07cc6 100644 --- a/src/electron/renderer/src/stories/pages/guided-mode/data/GuidedPathExpansion.js +++ b/src/electron/frontend/core/components/pages/guided-mode/data/GuidedPathExpansion.js @@ -9,7 +9,7 @@ import { onThrow } from "../../../../errors"; import pathExpansionSchema from "../../../../../../../schemas/json/path-expansion.schema.json" assert { type: "json" }; import { merge } from "../../utils"; import { List } from "../../../List"; -import { fs } from "../../../../electron/index.js"; +import { fs } from "../../../../../utils/electron.js"; import { Button } from "../../../Button.js"; import { Modal } from "../../../Modal"; import { header } from "../../../forms/utils"; @@ -21,6 +21,8 @@ const propOrder = ["path", "subject_id", "session_id"]; export async function autocompleteFormatString(path) { let notification; + const interfaceName = path[0]; + const { base_directory } = path.reduce((acc, key) => acc[key] ?? {}, this.form.resolved); const schema = getSchema(path, this.info.globalState.schema.source_data); @@ -42,7 +44,7 @@ export async function autocompleteFormatString(path) { } const modal = new Modal({ - header: "Autocomplete Format String", + header: `${interfaceName} — Autocomplete Format String`, }); const content = document.createElement("div"); @@ -140,7 +142,7 @@ export async function autocompleteFormatString(path) { throw e; }); - const results = await run("locate/autocomplete", { + const results = await run("neuroconv/locate/autocomplete", { base_directory, additional_metadata: {}, ...form.results, @@ -259,7 +261,9 @@ export class GuidedPathExpansionPage extends Page { throw message; } - const results = await run(`locate`, finalStructure, { title: "Locating Data" }).catch((error) => { + const results = await run(`neuroconv/locate`, finalStructure, { + title: "Locating Data", + }).catch((error) => { this.notify(error.message, "error"); throw error; }); @@ -319,14 +323,21 @@ export class GuidedPathExpansionPage extends Page { const structureState = this.#initialize(); // Require properties for all sources - const generatedSchema = { type: "object", properties: {}, additionalProperties: false }; + const generatedSchema = { + type: "object", + properties: {}, + additionalProperties: false, + }; const controls = {}; const baseDirectory = this.workflow.base_directory.value; const globals = (structureState.globals = {}); for (let key in this.info.globalState.interfaces) { - generatedSchema.properties[key] = { type: "object", ...pathExpansionSchema }; + generatedSchema.properties[key] = { + type: "object", + ...pathExpansionSchema, + }; if (baseDirectory) globals[key] = { base_directory: baseDirectory }; @@ -368,6 +379,8 @@ export class GuidedPathExpansionPage extends Page { validateOnChange: async (name, parent, parentPath) => { const value = parent[name]; + const interfaceName = parentPath.slice(-1)[0]; + if (fs) { const baseDir = form.getFormElement([...parentPath, "base_directory"]); if (name === "format_string_path") { @@ -392,14 +405,14 @@ export class GuidedPathExpansionPage extends Page { if (value.split(".").length > 1) entry.file_path = value; else entry.folder_path = value; - const interfaceName = parentPath.slice(-1)[0]; - - const results = await run(`locate`, { [interfaceName]: entry }, { swal: false }).catch( - (error) => { - this.notify(error.message, "error"); - throw error; - } - ); + const results = await run( + `neuroconv/locate`, + { [interfaceName]: entry }, + { swal: false } + ).catch((error) => { + this.notify(error.message, "error"); + throw error; + }); const resolved = []; @@ -421,7 +434,10 @@ export class GuidedPathExpansionPage extends Page { return [ { - message: html`

Source Files Found

+ message: html`

+ ✅Source Files Found for + ${interfaceName} +

${base_directory} ${new List({ diff --git a/src/electron/renderer/src/stories/pages/guided-mode/data/GuidedSourceData.js b/src/electron/frontend/core/components/pages/guided-mode/data/GuidedSourceData.js similarity index 84% rename from src/electron/renderer/src/stories/pages/guided-mode/data/GuidedSourceData.js rename to src/electron/frontend/core/components/pages/guided-mode/data/GuidedSourceData.js index f3876100a1..088a418a7b 100644 --- a/src/electron/renderer/src/stories/pages/guided-mode/data/GuidedSourceData.js +++ b/src/electron/frontend/core/components/pages/guided-mode/data/GuidedSourceData.js @@ -1,10 +1,10 @@ -import Swal from "sweetalert2"; -import { isStorybook } from "../../../../dependencies/globals"; +import { isStorybook } from "../../../../globals.js"; + import { JSONSchemaForm } from "../../../JSONSchemaForm.js"; import { InstanceManager } from "../../../InstanceManager.js"; import { ManagedPage } from "./ManagedPage.js"; import { onThrow } from "../../../../errors"; -import { merge, sanitize } from "../../utils"; +import { merge } from "../../utils"; import preprocessSourceDataSchema from "../../../../../../../schemas/source-data.schema"; import { createGlobalFormModal } from "../../../forms/GlobalFormModal"; @@ -13,11 +13,10 @@ import { Button } from "../../../Button.js"; import globalIcon from "../../../../../assets/icons/global.svg?raw"; -import { baseUrl } from "../../../../server/globals"; - import { run } from "../options/utils.js"; import { getInfoFromId } from "./utils"; import { Modal } from "../../../Modal"; +import Swal from "sweetalert2"; const propsToIgnore = { "*": { @@ -81,9 +80,7 @@ export class GuidedSourceDataPage extends ManagedPage { heightAuto: false, backdrop: "rgba(0,0,0, 0.4)", timerProgressBar: false, - didOpen: () => { - Swal.showLoading(); - }, + didOpen: () => Swal.showLoading(), }); }; @@ -96,39 +93,22 @@ export class GuidedSourceDataPage extends ManagedPage { const info = this.info.globalState.results[subject][session]; // NOTE: This clears all user-defined results - const result = await fetch(`${baseUrl}/neuroconv/metadata`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - source_data: sanitize(structuredClone(form.resolved)), // Use resolved values, including global source data + const result = await run( + `neuroconv/metadata`, + { + source_data: form.resolved, // Use resolved values, including global source data interfaces: this.info.globalState.interfaces, - }), - }) - .then((res) => res.json()) - .catch((error) => { - Swal.close(); - stillFireSwal = false; - this.notify(`Critical Error: ${error.message}`, "error", 4000); - throw error; - }); - - Swal.close(); + }, + { swal: false } + ).catch((e) => { + Swal.close(); + stillFireSwal = false; + this.notify(e.message, "error"); + throw e; + }); if (isStorybook) return; - if (result.message) { - const [type, ...splitText] = result.message.split(":"); - const text = splitText.length - ? splitText.join(":").replaceAll("<", "<").replaceAll(">", ">") - : result.traceback - ? `
${result.traceback.trim().split("\n").slice(-2)[0].trim()}
` - : ""; - - const message = `

Request Failed

${type}

${text}

`; - this.notify(message, "error"); - throw result; - } - const { results: metadata, schema } = result; // Merge arrays from generated pipeline data @@ -148,6 +128,8 @@ export class GuidedSourceDataPage extends ManagedPage { }) ); + Swal.close(); + await this.save(undefined, false); // Just save new raw values return this.to(1); @@ -223,7 +205,9 @@ export class GuidedSourceDataPage extends ManagedPage { } render() { - this.localState = { results: structuredClone(this.info.globalState.results ?? {}) }; + this.localState = { + results: structuredClone(this.info.globalState.results ?? {}), + }; this.forms = this.mapSessions(this.createForm, this.localState.results); @@ -253,7 +237,7 @@ export class GuidedSourceDataPage extends ManagedPage { source_data: merge(globalState.project.SourceData, souceCopy), }; - const results = await run("alignment", sessionInfo, { + const results = await run("neuroconv/alignment", sessionInfo, { title: "Checking Alignment", message: "Please wait...", }); diff --git a/src/electron/renderer/src/stories/pages/guided-mode/data/GuidedStructure.js b/src/electron/frontend/core/components/pages/guided-mode/data/GuidedStructure.js similarity index 90% rename from src/electron/renderer/src/stories/pages/guided-mode/data/GuidedStructure.js rename to src/electron/frontend/core/components/pages/guided-mode/data/GuidedStructure.js index cda4756ab0..03ad6d742a 100644 --- a/src/electron/renderer/src/stories/pages/guided-mode/data/GuidedStructure.js +++ b/src/electron/frontend/core/components/pages/guided-mode/data/GuidedStructure.js @@ -9,6 +9,7 @@ import { Modal } from "../../../Modal"; import { List } from "../../../List"; import { baseUrl } from "../../../../server/globals"; import { ready } from "../../../../../../../schemas/interfaces.info"; +import { run } from "../options/utils.js"; const defaultEmptyMessage = "No formats selected"; @@ -66,14 +67,7 @@ export class GuidedStructurePage extends Page { getSchema = async () => { const interfaces = { ...this.list.object }; - const schema = - Object.keys(interfaces).length === 0 - ? {} - : await fetch(`${baseUrl}/neuroconv/schema`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(interfaces), - }).then((res) => res.json()); + const schema = Object.keys(interfaces).length === 0 ? {} : await run(`neuroconv/schema`, interfaces); let schemas = this.info.globalState.schema; if (!schemas) schemas = this.info.globalState.schema = {}; @@ -83,7 +77,9 @@ export class GuidedStructurePage extends Page { }; beforeSave = async () => { - const interfaces = (this.info.globalState.interfaces = { ...this.list.object }); + const interfaces = (this.info.globalState.interfaces = { + ...this.list.object, + }); // Remove or reassign extra interfaces in results if (this.info.globalState.results) { diff --git a/src/electron/renderer/src/stories/pages/guided-mode/data/ManagedPage.js b/src/electron/frontend/core/components/pages/guided-mode/data/ManagedPage.js similarity index 100% rename from src/electron/renderer/src/stories/pages/guided-mode/data/ManagedPage.js rename to src/electron/frontend/core/components/pages/guided-mode/data/ManagedPage.js diff --git a/src/electron/renderer/src/stories/pages/guided-mode/data/utils.js b/src/electron/frontend/core/components/pages/guided-mode/data/utils.js similarity index 96% rename from src/electron/renderer/src/stories/pages/guided-mode/data/utils.js rename to src/electron/frontend/core/components/pages/guided-mode/data/utils.js index f2c4fcf703..b5259e3bb5 100644 --- a/src/electron/renderer/src/stories/pages/guided-mode/data/utils.js +++ b/src/electron/frontend/core/components/pages/guided-mode/data/utils.js @@ -1,5 +1,5 @@ import { getEditableItems } from "../../../JSONSchemaInput.js"; -import { merge } from "../../utils"; +import { merge } from "../../utils.js"; // Merge project-wide data into metadata export function populateWithProjectMetadata(info, globalState) { diff --git a/src/electron/renderer/src/stories/pages/guided-mode/options/GuidedInspectorPage.js b/src/electron/frontend/core/components/pages/guided-mode/options/GuidedInspectorPage.js similarity index 95% rename from src/electron/renderer/src/stories/pages/guided-mode/options/GuidedInspectorPage.js rename to src/electron/frontend/core/components/pages/guided-mode/options/GuidedInspectorPage.js index ae262f389e..bb769353a1 100644 --- a/src/electron/renderer/src/stories/pages/guided-mode/options/GuidedInspectorPage.js +++ b/src/electron/frontend/core/components/pages/guided-mode/options/GuidedInspectorPage.js @@ -4,7 +4,7 @@ import { Page } from "../../Page.js"; import { unsafeSVG } from "lit/directives/unsafe-svg.js"; import folderOpenSVG from "../../../../../assets/icons/folder_open.svg?raw"; -import { electron } from "../../../../electron/index.js"; +import { electron } from "../../../../../utils/electron.js"; import { getSharedPath, removeFilePaths, truncateFilePaths } from "../../../preview/NWBFilePreview.js"; const { ipcRenderer } = electron; import { until } from "lit/directives/until.js"; @@ -143,7 +143,7 @@ export class GuidedInspectorPage extends Page { if (!this.report) { const result = await run( - "inspect_file", + "neuroconv/inspect_file", { nwbfile_path: fileArr[0].info.file, ...options }, { title } ).catch((error) => { @@ -181,7 +181,7 @@ export class GuidedInspectorPage extends Page { const { close: closeProgressPopup } = swalOpts; const result = await run( - "inspect_folder", + "neuroconv/inspect_folder", { path, ...options, request_id: swalOpts.id }, swalOpts ).catch((error) => { diff --git a/src/electron/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js b/src/electron/frontend/core/components/pages/guided-mode/options/GuidedStubPreview.js similarity index 94% rename from src/electron/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js rename to src/electron/frontend/core/components/pages/guided-mode/options/GuidedStubPreview.js index d1b0221556..55979eaa5a 100644 --- a/src/electron/renderer/src/stories/pages/guided-mode/options/GuidedStubPreview.js +++ b/src/electron/frontend/core/components/pages/guided-mode/options/GuidedStubPreview.js @@ -4,7 +4,7 @@ import { Page } from "../../Page.js"; import { unsafeSVG } from "lit/directives/unsafe-svg.js"; import folderOpenSVG from "../../../../../assets/icons/folder_open.svg?raw"; -import { electron } from "../../../../electron/index.js"; +import { electron } from "../../../../../utils/electron.js"; import { NWBFilePreview, getSharedPath } from "../../../preview/NWBFilePreview.js"; const { ipcRenderer } = electron; diff --git a/src/electron/renderer/src/stories/pages/guided-mode/options/GuidedUpload.js b/src/electron/frontend/core/components/pages/guided-mode/options/GuidedUpload.js similarity index 100% rename from src/electron/renderer/src/stories/pages/guided-mode/options/GuidedUpload.js rename to src/electron/frontend/core/components/pages/guided-mode/options/GuidedUpload.js diff --git a/src/electron/renderer/src/stories/pages/guided-mode/options/utils.js b/src/electron/frontend/core/components/pages/guided-mode/options/utils.js similarity index 78% rename from src/electron/renderer/src/stories/pages/guided-mode/options/utils.js rename to src/electron/frontend/core/components/pages/guided-mode/options/utils.js index 3cc8bca534..259c49c74b 100644 --- a/src/electron/renderer/src/stories/pages/guided-mode/options/utils.js +++ b/src/electron/frontend/core/components/pages/guided-mode/options/utils.js @@ -21,7 +21,7 @@ export const openProgressSwal = (options, callback) => { }); }; -export const run = async (url, payload, options = {}) => { +export const run = async (pathname, payload, options = {}) => { let internalSwal; if (options.swal === false) { @@ -72,28 +72,35 @@ export const run = async (url, payload, options = {}) => { element.insertAdjacentHTML("beforeend", `
`); } - if (!("base" in options)) options.base = "/neuroconv"; - if (options.base[0] !== "/") options.base = `/${options.base}`; - // Clear private keys from being passed payload = sanitize(structuredClone(payload)); - const results = await fetch(`${baseUrl}${options.base || ""}/${url}`, { + const results = await fetch(new URL(pathname, baseUrl), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), ...(options.fetch ?? {}), - }).then((res) => res.json()); - - if (internalSwal) Swal.close(); - - if (results?.message) throw new Error(`Request to ${url} failed: ${results.message}`); + }) + .then(async (res) => { + const json = await res.json(); + + if (!res.ok) { + const message = json.message; + const header = `

Request to ${pathname} failed

${json.type}`; + const text = message.replaceAll("<", "<").replaceAll(">", ">").trim(); + throw new Error(`${header}

${text}

`); + } + return json; + }) + .finally(() => { + if (internalSwal) Swal.close(); + }); return results || true; }; export const runConversion = async (info, options = {}) => - run(`convert`, info, { + run(`neuroconv/convert`, info, { title: "Running the conversion", onError: (results) => { if (results.message.includes("already exists")) { diff --git a/src/electron/renderer/src/stories/pages/guided-mode/results/GuidedDandiResults.js b/src/electron/frontend/core/components/pages/guided-mode/results/GuidedDandiResults.js similarity index 100% rename from src/electron/renderer/src/stories/pages/guided-mode/results/GuidedDandiResults.js rename to src/electron/frontend/core/components/pages/guided-mode/results/GuidedDandiResults.js diff --git a/src/electron/frontend/core/components/pages/guided-mode/results/GuidedResults.js b/src/electron/frontend/core/components/pages/guided-mode/results/GuidedResults.js new file mode 100644 index 0000000000..af1be09d66 --- /dev/null +++ b/src/electron/frontend/core/components/pages/guided-mode/results/GuidedResults.js @@ -0,0 +1,106 @@ +import { html } from "lit"; +import { unsafeSVG } from "lit/directives/unsafe-svg.js"; +import folderOpenSVG from "../../../../../assets/icons/folder_open.svg?raw"; + +import { Page } from "../../Page.js"; +import { getStubArray } from "../options/GuidedStubPreview.js"; +import { getSharedPath } from "../../../preview/NWBFilePreview.js"; + +import { electron, path } from "../../../../../utils/electron.js"; + +import manualActionsJSON from "../../../../../../../schemas/json/manual_actions.json"; + +import { CodeBlock } from "../../../CodeBlock.js"; + +const { ipcRenderer } = electron; + +export class GuidedResultsPage extends Page { + constructor(...args) { + super(...args); + } + + header = { + controls: () => + html` { + if (ipcRenderer) ipcRenderer.send("showItemInFolder", this.#sharedPath()); + }} + >${unsafeSVG(folderOpenSVG)}`, + }; + + footer = {}; + + #sharedPath = () => { + const { conversion } = this.info.globalState; + if (!conversion) return ""; + return getSharedPath(getStubArray(conversion).map((item) => item.file)); + }; + + updated() { + this.save(); // Save the current state + } + + render() { + const { conversion } = this.info.globalState; + + if (!conversion) + return html`

Your conversion failed. Please try again.

`; + + // Show a snippet for how to open the NWB file + return html` +

Your data was successfully converted to NWB!

+
    + ${getStubArray(conversion) + .map(({ file }) => file.split(path.sep).slice(-1)[0]) + .sort() + .map((id) => html`
  1. ${id}
  2. `)} +
+

But what about my other data?

+

+ The GUIDE still can't do everything. You may need to manually adjust the NWB file to ensure it contains + all the necessary data.

+ + For example, to append to the file using PyNWB you would start with: +

+ ${new CodeBlock({ + text: `from pynwb import NWBHDF5IO, NWBFile + +nwbfile_path= "${this.#sharedPath()}" + +# Open the file +with NWBHDF5IO(path=nwbfile_path, mode="r+") as io: + nwbfile = io.read() + + # Then adjust the file as needed + + # ... +`, + })} +
Related Documentation
+
+ ${manualActionsJSON.map( + ({ name, description, url }) => html` +
+

+ ${name} +

+ ${description} +
+ ` + )} +
+

+ For more information, please refer to the + PyNWB and + MatNWB documentation. +

+ `; + } +} + +customElements.get("nwbguide-guided-results-page") || + customElements.define("nwbguide-guided-results-page", GuidedResultsPage); diff --git a/src/electron/renderer/src/stories/pages/guided-mode/setup/GuidedNewDatasetInfo.js b/src/electron/frontend/core/components/pages/guided-mode/setup/GuidedNewDatasetInfo.js similarity index 100% rename from src/electron/renderer/src/stories/pages/guided-mode/setup/GuidedNewDatasetInfo.js rename to src/electron/frontend/core/components/pages/guided-mode/setup/GuidedNewDatasetInfo.js diff --git a/src/electron/renderer/src/stories/pages/guided-mode/setup/GuidedSubjects.js b/src/electron/frontend/core/components/pages/guided-mode/setup/GuidedSubjects.js similarity index 100% rename from src/electron/renderer/src/stories/pages/guided-mode/setup/GuidedSubjects.js rename to src/electron/frontend/core/components/pages/guided-mode/setup/GuidedSubjects.js diff --git a/src/electron/renderer/src/stories/pages/guided-mode/setup/Preform.js b/src/electron/frontend/core/components/pages/guided-mode/setup/Preform.js similarity index 100% rename from src/electron/renderer/src/stories/pages/guided-mode/setup/Preform.js rename to src/electron/frontend/core/components/pages/guided-mode/setup/Preform.js diff --git a/src/electron/renderer/src/stories/pages/guided-mode/setup/utils.ts b/src/electron/frontend/core/components/pages/guided-mode/setup/utils.ts similarity index 100% rename from src/electron/renderer/src/stories/pages/guided-mode/setup/utils.ts rename to src/electron/frontend/core/components/pages/guided-mode/setup/utils.ts diff --git a/src/electron/renderer/src/stories/pages/inspect/InspectPage.js b/src/electron/frontend/core/components/pages/inspect/InspectPage.js similarity index 89% rename from src/electron/renderer/src/stories/pages/inspect/InspectPage.js rename to src/electron/frontend/core/components/pages/inspect/InspectPage.js index 906894f837..7aff516e4f 100644 --- a/src/electron/renderer/src/stories/pages/inspect/InspectPage.js +++ b/src/electron/frontend/core/components/pages/inspect/InspectPage.js @@ -25,15 +25,20 @@ export class InspectPage extends Page { }; inspect = async (paths, kwargs = {}, options = {}) => { - const swalOpts = await createProgressPopup({ title: "Inspecting selected filesystem entries.", ...options }); + const swalOpts = await createProgressPopup({ + title: "Inspecting selected filesystem entries.", + ...options, + }); const { close: closeProgressPopup } = swalOpts; - const result = await run("inspect", { request_id: swalOpts.id, paths, ...kwargs }, swalOpts).catch((error) => { - this.notify(error.message, "error"); - closeProgressPopup(); - throw error; - }); + const result = await run("neuroconv/inspect", { request_id: swalOpts.id, paths, ...kwargs }, swalOpts).catch( + (error) => { + this.notify(error.message, "error"); + closeProgressPopup(); + throw error; + } + ); closeProgressPopup(); diff --git a/src/electron/renderer/src/stories/pages/inspect/utils.js b/src/electron/frontend/core/components/pages/inspect/utils.js similarity index 100% rename from src/electron/renderer/src/stories/pages/inspect/utils.js rename to src/electron/frontend/core/components/pages/inspect/utils.js diff --git a/src/electron/renderer/src/stories/pages/preview/PreviewPage.js b/src/electron/frontend/core/components/pages/preview/PreviewPage.js similarity index 100% rename from src/electron/renderer/src/stories/pages/preview/PreviewPage.js rename to src/electron/frontend/core/components/pages/preview/PreviewPage.js diff --git a/src/electron/renderer/src/stories/pages/settings/SettingsPage.js b/src/electron/frontend/core/components/pages/settings/SettingsPage.js similarity index 73% rename from src/electron/renderer/src/stories/pages/settings/SettingsPage.js rename to src/electron/frontend/core/components/pages/settings/SettingsPage.js index e314a9a416..732ba905de 100644 --- a/src/electron/renderer/src/stories/pages/settings/SettingsPage.js +++ b/src/electron/frontend/core/components/pages/settings/SettingsPage.js @@ -12,19 +12,33 @@ import { Button } from "../../Button.js"; import { global, remove, save } from "../../../progress/index.js"; import { merge, setUndefinedIfNotDeclared } from "../utils"; -import { homeDirectory, notyf, testDataFolderPath } from "../../../dependencies/globals"; -import { SERVER_FILE_PATH, electron, path, port, fs } from "../../../electron/index.js"; +import { notyf } from "../../../dependencies.js"; +import { homeDirectory, testDataFolderPath } from "../../../globals.js"; + +import { + SERVER_FILE_PATH, + electron, + path, + port, + fs, + onUpdateAvailable, + onUpdateProgress, +} from "../../../../utils/electron.js"; import saveSVG from "../../../../assets/icons/save.svg?raw"; import folderSVG from "../../../../assets/icons/folder_open.svg?raw"; import deleteSVG from "../../../../assets/icons/delete.svg?raw"; import generateSVG from "../../../../assets/icons/restart.svg?raw"; +import downloadSVG from "../../../../assets/icons/download.svg?raw"; +import infoSVG from "../../../../assets/icons/info.svg?raw"; import { header } from "../../forms/utils"; import examplePipelines from "../../../../../../example_pipelines.yml"; import { run } from "../guided-mode/options/utils.js"; import { joinPath } from "../../../globals"; +import { Modal } from "../../Modal"; +import { ProgressBar, humanReadableBytes } from "../../ProgressBar"; const DATA_OUTPUT_PATH = joinPath(testDataFolderPath, "single_session_data"); const DATASET_OUTPUT_PATH = joinPath(testDataFolderPath, "multi_session_dataset"); @@ -171,7 +185,7 @@ export class SettingsPage extends Page { generateTestData = async () => { if (!fs.existsSync(DATA_OUTPUT_PATH)) { await run( - "generate", + "data/generate", { output_path: DATA_OUTPUT_PATH, }, @@ -187,7 +201,7 @@ export class SettingsPage extends Page { } await run( - "generate/dataset", + "data/generate/dataset", { input_path: DATA_OUTPUT_PATH, output_path: DATASET_OUTPUT_PATH, @@ -218,6 +232,90 @@ export class SettingsPage extends Page { this.#openNotyf(`Global settings changes saved.`, "success"); }; + #releaseNotesModal; + + // Populate the Update Available display + updated() { + const updateDiv = this.querySelector("#update-available"); + + if (updateDiv.innerHTML) return; // Only populate once + + onUpdateAvailable((updateInfo) => { + console.warn("Update Available", updateInfo); + + const relativePath = updateInfo.path; + const file = updateInfo.files.find((f) => f.url === relativePath); + const filesize = file.size; + + const container = document.createElement("div"); + container.classList.add("update-container"); + + const mainUpdateInfo = document.createElement("div"); + + const infoIcon = document.createElement("slot"); + infoIcon.innerHTML = infoSVG; + + infoIcon.onclick = () => { + if (this.#releaseNotesModal) return (this.#releaseNotesModal.open = true); + + const modal = (this.#releaseNotesModal = new Modal({ + header: `Release Notes`, + })); + + const releaseNotes = document.createElement("div"); + releaseNotes.style.padding = "25px"; + releaseNotes.innerHTML = updateInfo.releaseNotes; + modal.append(releaseNotes); + + document.body.append(modal); + + modal.open = true; + }; + + const controls = document.createElement("div"); + controls.classList.add("controls"); + const downloadButton = new Button({ + icon: downloadSVG, + label: `Update (${humanReadableBytes(filesize)})`, + size: "extra-small", + onClick: () => electron.ipcRenderer.send("download-update"), + }); + + controls.append(downloadButton); + + const header = document.createElement("div"); + header.classList.add("header"); + + const title = document.createElement("h4"); + title.innerText = `NWB GUIDE ${updateInfo.version}`; + header.append(title, infoIcon); + + const description = document.createElement("span"); + description.innerText = `A new version of the application is available.`; + + mainUpdateInfo.append(header, description); + + container.append(mainUpdateInfo, controls); + + let progressBarEl; + onUpdateProgress((progress) => { + if (!progressBarEl) { + progressBarEl = new ProgressBar({ + isBytes: true, + format: { total: filesize }, + }); + const hr = document.createElement("hr"); + updateDiv.append(hr, progressBarEl); + } + progressBarEl.format = { + prefix: `Download Progress for NWB GUIDE ${updateInfo.version}`, + ...progress, + }; + }); + updateDiv.append(container); + }); + } + render() { this.localState = structuredClone(global.data); @@ -235,7 +333,7 @@ export class SettingsPage extends Page { }); const generatePipelineButton = new Button({ - label: "Generate Example Pipelines", + label: "Generate Test Pipelines", onClick: async () => { const { testing_data_folder } = this.form.results.developer ?? {}; @@ -250,20 +348,20 @@ export class SettingsPage extends Page { const resolved = pipelineNames.reverse().map((name) => { try { saveNewPipelineFromYaml(name, examplePipelines[name], testing_data_folder); - return true; + return { name, success: true }; } catch (e) { console.error(e); - return name; + return { name, error: e.message }; } }); - const nSuccessful = resolved.reduce((acc, v) => (acc += v === true ? 1 : 0), 0); + const nSuccessful = resolved.reduce((acc, v) => (acc += v.success === true ? 1 : 0), 0); const nFailed = resolved.length - nSuccessful; if (nFailed) { const failDisplay = nFailed === 1 - ? `the ${resolved.find((v) => typeof v === "string")} pipeline` + ? `the ${resolved.find((v) => !v.success).name} pipeline` : `${nFailed} pipelines`; this.#openNotyf( `

Generated ${nSuccessful} test pipelines.

Could not find source data for ${failDisplay}.`, @@ -275,6 +373,8 @@ export class SettingsPage extends Page { `

Pipeline Generation Failed

Could not find source data for any pipelines.`, "error" ); + + return resolved; }, }); @@ -335,6 +435,7 @@ export class SettingsPage extends Page { +


${this.form} diff --git a/src/electron/renderer/src/stories/pages/uploads/UploadsPage.js b/src/electron/frontend/core/components/pages/uploads/UploadsPage.js similarity index 94% rename from src/electron/renderer/src/stories/pages/uploads/UploadsPage.js rename to src/electron/frontend/core/components/pages/uploads/UploadsPage.js index 434f830c37..3d97bb6d30 100644 --- a/src/electron/renderer/src/stories/pages/uploads/UploadsPage.js +++ b/src/electron/frontend/core/components/pages/uploads/UploadsPage.js @@ -117,7 +117,10 @@ export async function createDandiset(results = {}) { const api_key = await getAPIKey.call(this, staging); - const api = new dandi.API({ token: api_key, type: staging ? "staging" : undefined }); + const api = new dandi.API({ + token: api_key, + type: staging ? "staging" : undefined, + }); await api.init(); const metadata = { @@ -259,7 +262,7 @@ export async function uploadToDandi(info, type = "project" in info ? "project" : if (info.project) payload.project = info.project; else payload.filesystem_paths = info.filesystem_paths; - const result = await run(type ? `upload/${type}` : "upload", payload, { + const result = await run(type ? `neuroconv/upload/${type}` : "neuroconv/upload", payload, { title: "Uploading your files to DANDI", }).catch((error) => { this.notify(error.message, "error"); @@ -348,7 +351,9 @@ export class UploadsPage extends Page { onClick: async () => { await this.form.validate(); // Will throw an error in the callback - const results = await uploadToDandi.call(this, { ...global.data.uploads }); + const results = await uploadToDandi.call(this, { + ...global.data.uploads, + }); global.data.uploads = {}; global.save(); @@ -387,7 +392,9 @@ export class UploadsPage extends Page { width: "max-content", }, onClick: async () => { - await createDandiset.call(this, { title: this.form.resolved.dandiset }); + await createDandiset.call(this, { + title: this.form.resolved.dandiset, + }); this.requestUpdate(); }, }), diff --git a/src/electron/renderer/src/stories/pages/uploads/utils.ts b/src/electron/frontend/core/components/pages/uploads/utils.ts similarity index 100% rename from src/electron/renderer/src/stories/pages/uploads/utils.ts rename to src/electron/frontend/core/components/pages/uploads/utils.ts diff --git a/src/electron/renderer/src/stories/pages/utils.js b/src/electron/frontend/core/components/pages/utils.js similarity index 100% rename from src/electron/renderer/src/stories/pages/utils.js rename to src/electron/frontend/core/components/pages/utils.js diff --git a/src/electron/renderer/src/stories/preview/NWBFilePreview.js b/src/electron/frontend/core/components/preview/NWBFilePreview.js similarity index 90% rename from src/electron/renderer/src/stories/preview/NWBFilePreview.js rename to src/electron/frontend/core/components/preview/NWBFilePreview.js index cd268a90e7..5c6cafdad3 100644 --- a/src/electron/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/electron/frontend/core/components/preview/NWBFilePreview.js @@ -5,7 +5,7 @@ import { unsafeHTML } from "lit/directives/unsafe-html.js"; import { run } from "../pages/guided-mode/options/utils"; import { until } from "lit/directives/until.js"; import { InstanceManager } from "../InstanceManager"; -import { path } from "../../electron"; +import { path } from "../../../utils/electron.js"; import { FullScreenToggle } from "../FullScreenToggle"; export function getSharedPath(array) { @@ -62,7 +62,7 @@ class NWBPreviewInstance extends LitElement { }) : until( (async () => { - const htmlRep = await run("html", { nwbfile_path: this.file }, { swal: false }); + const htmlRep = await run("neuroconv/html", { nwbfile_path: this.file }, { swal: false }); return unsafeHTML(htmlRep); })(), html`Loading HTML representation...` @@ -154,11 +154,15 @@ export class NWBFilePreview extends LitElement { const report = onlyFirstFile ? await run( - "inspect_file", + "neuroconv/inspect_file", { nwbfile_path: fileArr[0].info.file, ...options }, { title } ) // Inspect the first file - : await run("inspect_folder", { path, ...options }, { title: title + "s" }); // Inspect the folder + : await run( + "neuroconv/inspect_folder", + { path, ...options }, + { title: title + "s" } + ); // Inspect the folder const result = onlyFirstFile ? { diff --git a/src/electron/renderer/src/stories/preview/Neurosift.js b/src/electron/frontend/core/components/preview/Neurosift.js similarity index 100% rename from src/electron/renderer/src/stories/preview/Neurosift.js rename to src/electron/frontend/core/components/preview/Neurosift.js diff --git a/src/electron/renderer/src/stories/preview/inspector/InspectorList.js b/src/electron/frontend/core/components/preview/inspector/InspectorList.js similarity index 100% rename from src/electron/renderer/src/stories/preview/inspector/InspectorList.js rename to src/electron/frontend/core/components/preview/inspector/InspectorList.js diff --git a/src/electron/renderer/src/stories/sidebar.js b/src/electron/frontend/core/components/sidebar.js similarity index 92% rename from src/electron/renderer/src/stories/sidebar.js rename to src/electron/frontend/core/components/sidebar.js index 88a1ff0127..6d79ed6e0a 100644 --- a/src/electron/renderer/src/stories/sidebar.js +++ b/src/electron/frontend/core/components/sidebar.js @@ -181,7 +181,16 @@ export class Sidebar extends LitElement { const a = document.createElement("a"); a.setAttribute("data-id", id); a.href = "#"; - a.innerHTML = `${icon} ${label}`; + + a.insertAdjacentHTML("afterbegin", icon); + + const labelContainer = document.createElement("div"); + const labelEl = document.createElement("span"); + labelEl.innerHTML = label; + labelContainer.append(labelEl); + + a.append(labelContainer); + a.onclick = () => this.#onClick(id); const li = document.createElement("li"); diff --git a/src/electron/renderer/src/stories/status/StatusBar.ts b/src/electron/frontend/core/components/status/StatusBar.ts similarity index 100% rename from src/electron/renderer/src/stories/status/StatusBar.ts rename to src/electron/frontend/core/components/status/StatusBar.ts diff --git a/src/electron/renderer/src/stories/status/StatusIndicator.ts b/src/electron/frontend/core/components/status/StatusIndicator.ts similarity index 100% rename from src/electron/renderer/src/stories/status/StatusIndicator.ts rename to src/electron/frontend/core/components/status/StatusIndicator.ts diff --git a/src/electron/renderer/src/stories/table/Cell.ts b/src/electron/frontend/core/components/table/Cell.ts similarity index 100% rename from src/electron/renderer/src/stories/table/Cell.ts rename to src/electron/frontend/core/components/table/Cell.ts diff --git a/src/electron/renderer/src/stories/table/ContextMenu.ts b/src/electron/frontend/core/components/table/ContextMenu.ts similarity index 100% rename from src/electron/renderer/src/stories/table/ContextMenu.ts rename to src/electron/frontend/core/components/table/ContextMenu.ts diff --git a/src/electron/renderer/src/stories/table/cells/array.ts b/src/electron/frontend/core/components/table/cells/array.ts similarity index 100% rename from src/electron/renderer/src/stories/table/cells/array.ts rename to src/electron/frontend/core/components/table/cells/array.ts diff --git a/src/electron/renderer/src/stories/table/cells/base.ts b/src/electron/frontend/core/components/table/cells/base.ts similarity index 100% rename from src/electron/renderer/src/stories/table/cells/base.ts rename to src/electron/frontend/core/components/table/cells/base.ts diff --git a/src/electron/renderer/src/stories/table/cells/date-time.ts b/src/electron/frontend/core/components/table/cells/date-time.ts similarity index 100% rename from src/electron/renderer/src/stories/table/cells/date-time.ts rename to src/electron/frontend/core/components/table/cells/date-time.ts diff --git a/src/electron/renderer/src/stories/table/cells/dropdown.ts b/src/electron/frontend/core/components/table/cells/dropdown.ts similarity index 100% rename from src/electron/renderer/src/stories/table/cells/dropdown.ts rename to src/electron/frontend/core/components/table/cells/dropdown.ts diff --git a/src/electron/renderer/src/stories/table/cells/editors/base.ts b/src/electron/frontend/core/components/table/cells/editors/base.ts similarity index 100% rename from src/electron/renderer/src/stories/table/cells/editors/base.ts rename to src/electron/frontend/core/components/table/cells/editors/base.ts diff --git a/src/electron/renderer/src/stories/table/cells/input.ts b/src/electron/frontend/core/components/table/cells/input.ts similarity index 97% rename from src/electron/renderer/src/stories/table/cells/input.ts rename to src/electron/frontend/core/components/table/cells/input.ts index 5c090813ab..17572ead1d 100644 --- a/src/electron/renderer/src/stories/table/cells/input.ts +++ b/src/electron/frontend/core/components/table/cells/input.ts @@ -1,12 +1,12 @@ import { LitElement, css, html } from "lit"; import { TableCellBase } from "./base.js"; import { BaseRenderer } from "./renderers/base.js"; -import { Modal } from "../../Modal"; +import { Modal } from "../../Modal.js"; import { SimpleTable } from "../../SimpleTable.js"; import { JSONSchemaInput } from "../../JSONSchemaInput.js"; -import { header } from "../../forms/utils"; +import { header } from "../../forms/utils.js"; export class NestedEditor extends LitElement { diff --git a/src/electron/renderer/src/stories/table/cells/renderers/base.ts b/src/electron/frontend/core/components/table/cells/renderers/base.ts similarity index 100% rename from src/electron/renderer/src/stories/table/cells/renderers/base.ts rename to src/electron/frontend/core/components/table/cells/renderers/base.ts diff --git a/src/electron/renderer/src/stories/table/convert.ts b/src/electron/frontend/core/components/table/convert.ts similarity index 100% rename from src/electron/renderer/src/stories/table/convert.ts rename to src/electron/frontend/core/components/table/convert.ts diff --git a/src/electron/renderer/src/stories/table/utils.ts b/src/electron/frontend/core/components/table/utils.ts similarity index 100% rename from src/electron/renderer/src/stories/table/utils.ts rename to src/electron/frontend/core/components/table/utils.ts diff --git a/src/electron/renderer/src/stories/utils/progress.js b/src/electron/frontend/core/components/utils/progress.js similarity index 100% rename from src/electron/renderer/src/stories/utils/progress.js rename to src/electron/frontend/core/components/utils/progress.js diff --git a/src/electron/renderer/src/stories/utils/useGlobalStyles.js b/src/electron/frontend/core/components/utils/useGlobalStyles.js similarity index 100% rename from src/electron/renderer/src/stories/utils/useGlobalStyles.js rename to src/electron/frontend/core/components/utils/useGlobalStyles.js diff --git a/src/electron/renderer/src/dependencies/globals.js b/src/electron/frontend/core/dependencies.js similarity index 95% rename from src/electron/renderer/src/dependencies/globals.js rename to src/electron/frontend/core/dependencies.js index f7ec74427b..810456d112 100644 --- a/src/electron/renderer/src/dependencies/globals.js +++ b/src/electron/frontend/core/dependencies.js @@ -2,8 +2,6 @@ import { Notyf } from "notyf"; import checkChromatic from "chromatic/isChromatic"; import lottie from "lottie-web"; -export * from "./simple.js"; - // ---------- Lottie Helper ---------- const isChromatic = checkChromatic(); diff --git a/src/electron/renderer/src/errors.ts b/src/electron/frontend/core/errors.ts similarity index 74% rename from src/electron/renderer/src/errors.ts rename to src/electron/frontend/core/errors.ts index 89f7ae17f3..ba1794a915 100644 --- a/src/electron/renderer/src/errors.ts +++ b/src/electron/frontend/core/errors.ts @@ -1,4 +1,4 @@ -import { notify } from './dependencies/globals' +import { notify } from './dependencies' export const onThrow = (message: string, id?: string) => { return notify(id ? `[${id}]: ${message}` : message, "error", 7000); diff --git a/src/electron/renderer/src/dependencies/simple.js b/src/electron/frontend/core/globals.js similarity index 69% rename from src/electron/renderer/src/dependencies/simple.js rename to src/electron/frontend/core/globals.js index b9c86f59c3..fa3fc0e26e 100644 --- a/src/electron/renderer/src/dependencies/simple.js +++ b/src/electron/frontend/core/globals.js @@ -1,6 +1,15 @@ -import { app, crypto, isElectron } from "../electron/index.js"; -import paths from "../../../../paths.config.json" assert { type: "json" }; -import { joinPath } from "../globals"; +import { app, path, crypto, isElectron } from "../utils/electron.js"; + +import paths from "../../../paths.config.json" assert { type: "json" }; + +import supportedInterfaces from "../../../supported_interfaces.json" assert { type: "json" }; + +export const joinPath = (...args) => (path ? path.join(...args) : args.filter((str) => str).join("/")); + +export let runOnLoad = (fn) => { + if (document.readyState === "complete") fn(); + else window.addEventListener("load", fn); +}; export const reloadPageToHome = () => { if (isStorybook) return; @@ -29,3 +38,5 @@ export const ENCRYPTION_IV = crypto ? crypto.randomBytes(IV_LENGTH) : null; // Storybook export const isStorybook = window.location.href.includes("iframe.html"); + +export { supportedInterfaces }; diff --git a/src/electron/renderer/src/index.ts b/src/electron/frontend/core/index.ts similarity index 95% rename from src/electron/renderer/src/index.ts rename to src/electron/frontend/core/index.ts index b3493aa94d..08415c3e10 100644 --- a/src/electron/renderer/src/index.ts +++ b/src/electron/frontend/core/index.ts @@ -1,18 +1,18 @@ import "./pages.js" -import { isElectron, electron } from './electron/index.js' +import { isElectron, electron } from '../utils/electron.js' const { ipcRenderer } = electron; -import { Dashboard } from './stories/Dashboard.js' +import { Dashboard } from './components/Dashboard.js' import { notyf, notify -} from './dependencies/globals' +} from './dependencies.js' import Swal from 'sweetalert2' import { loadServerEvents, pythonServerOpened } from "./server/index.js"; -import { statusBar } from "./server/globals"; +import { statusBar } from "./server/globals.js"; // Set the sidebar subtitle to the current app version const dashboard = document.querySelector('nwb-dashboard') as Dashboard diff --git a/src/electron/renderer/src/pages.js b/src/electron/frontend/core/pages.js similarity index 71% rename from src/electron/renderer/src/pages.js rename to src/electron/frontend/core/pages.js index 7dbd400e77..11f042adcf 100644 --- a/src/electron/renderer/src/pages.js +++ b/src/electron/frontend/core/pages.js @@ -1,33 +1,32 @@ -import { GettingStartedPage } from "./stories/pages/getting-started/GettingStarted"; -import { DocumentationPage } from "./stories/pages/documentation/Documentation"; -import { ContactPage } from "./stories/pages/contact-us/Contact"; -import { GuidedHomePage } from "./stories/pages/guided-mode/GuidedHome"; -import { GuidedNewDatasetPage } from "./stories/pages/guided-mode/setup/GuidedNewDatasetInfo"; -import { GuidedStructurePage } from "./stories/pages/guided-mode/data/GuidedStructure"; -import { sections } from "./stories/pages/globals"; -import { GuidedSubjectsPage } from "./stories/pages/guided-mode/setup/GuidedSubjects"; -import { GuidedSourceDataPage } from "./stories/pages/guided-mode/data/GuidedSourceData"; -import { GuidedMetadataPage } from "./stories/pages/guided-mode/data/GuidedMetadata"; -import { GuidedUploadPage } from "./stories/pages/guided-mode/options/GuidedUpload"; -import { GuidedResultsPage } from "./stories/pages/guided-mode/results/GuidedResults"; -import { Dashboard } from "./stories/Dashboard"; -import { GuidedStubPreviewPage } from "./stories/pages/guided-mode/options/GuidedStubPreview"; -import { GuidedInspectorPage } from "./stories/pages/guided-mode/options/GuidedInspectorPage"; +import { DocumentationPage } from "./components/pages/documentation/Documentation"; +import { ContactPage } from "./components/pages/contact-us/Contact"; +import { GuidedHomePage } from "./components/pages/guided-mode/GuidedHome"; +import { GuidedNewDatasetPage } from "./components/pages/guided-mode/setup/GuidedNewDatasetInfo"; +import { GuidedStructurePage } from "./components/pages/guided-mode/data/GuidedStructure"; +import { sections } from "./components/pages/globals"; +import { GuidedSubjectsPage } from "./components/pages/guided-mode/setup/GuidedSubjects"; +import { GuidedSourceDataPage } from "./components/pages/guided-mode/data/GuidedSourceData"; +import { GuidedMetadataPage } from "./components/pages/guided-mode/data/GuidedMetadata"; +import { GuidedUploadPage } from "./components/pages/guided-mode/options/GuidedUpload"; +import { GuidedResultsPage } from "./components/pages/guided-mode/results/GuidedResults"; +import { Dashboard } from "./components/Dashboard"; +import { GuidedStubPreviewPage } from "./components/pages/guided-mode/options/GuidedStubPreview"; +import { GuidedInspectorPage } from "./components/pages/guided-mode/options/GuidedInspectorPage"; import logo from "../assets/img/logo-guide-draft-transparent-tight.png"; -import { GuidedPathExpansionPage } from "./stories/pages/guided-mode/data/GuidedPathExpansion"; +import { GuidedPathExpansionPage } from "./components/pages/guided-mode/data/GuidedPathExpansion"; import uploadIcon from "../assets/icons/dandi.svg?raw"; import inspectIcon from "../assets/icons/inspect.svg?raw"; import neurosiftIcon from "../assets/icons/neurosift-logo.svg?raw"; import settingsIcon from "../assets/icons/settings.svg?raw"; -import { UploadsPage } from "./stories/pages/uploads/UploadsPage"; -import { SettingsPage } from "./stories/pages/settings/SettingsPage"; -import { InspectPage } from "./stories/pages/inspect/InspectPage"; -import { PreviewPage } from "./stories/pages/preview/PreviewPage"; -import { GuidedPreform } from "./stories/pages/guided-mode/setup/Preform"; -import { GuidedDandiResultsPage } from "./stories/pages/guided-mode/results/GuidedDandiResults"; +import { UploadsPage } from "./components/pages/uploads/UploadsPage"; +import { SettingsPage } from "./components/pages/settings/SettingsPage"; +import { InspectPage } from "./components/pages/inspect/InspectPage"; +import { PreviewPage } from "./components/pages/preview/PreviewPage"; +import { GuidedPreform } from "./components/pages/guided-mode/setup/Preform"; +import { GuidedDandiResultsPage } from "./components/pages/guided-mode/results/GuidedDandiResults"; let dashboard = document.querySelector("nwb-dashboard"); if (!dashboard) dashboard = new Dashboard(); @@ -45,7 +44,7 @@ const guidedIcon = ` fill="white" class="bi bi-compass-fill" viewBox="0 0 16 16" - style="margin-right: 30px; margin-bottom: -5px" + style="margin-right: 30px;" > diff --git a/src/electron/renderer/src/progress/index.js b/src/electron/frontend/core/progress/index.js similarity index 95% rename from src/electron/renderer/src/progress/index.js rename to src/electron/frontend/core/progress/index.js index 3527f9da29..761ab85348 100644 --- a/src/electron/renderer/src/progress/index.js +++ b/src/electron/frontend/core/progress/index.js @@ -7,11 +7,11 @@ import { appDirectory, ENCRYPTION_KEY, ENCRYPTION_IV, -} from "../dependencies/simple.js"; -import { fs, crypto } from "../electron/index.js"; +} from "../globals.js"; +import { fs, crypto } from "../../utils/electron.js"; import { joinPath, runOnLoad } from "../globals"; -import { merge } from "../stories/pages/utils.js"; +import { merge } from "../components/pages/utils.js"; import { updateAppProgress, updateFile } from "./update.js"; import { updateURLParams } from "../../utils/url.js"; diff --git a/src/electron/renderer/src/progress/operations.js b/src/electron/frontend/core/progress/operations.js similarity index 88% rename from src/electron/renderer/src/progress/operations.js rename to src/electron/frontend/core/progress/operations.js index b4c4658b15..8d60e238ef 100644 --- a/src/electron/renderer/src/progress/operations.js +++ b/src/electron/frontend/core/progress/operations.js @@ -1,6 +1,6 @@ import { joinPath } from "../globals"; -import { conversionSaveFolderPath, guidedProgressFilePath, previewSaveFolderPath } from "../dependencies/simple"; -import { fs } from "../electron"; +import { conversionSaveFolderPath, guidedProgressFilePath, previewSaveFolderPath } from "../globals"; +import { fs } from "../../utils/electron"; export const remove = (name) => { //Get the path of the progress file to delete diff --git a/src/electron/renderer/src/progress/update.js b/src/electron/frontend/core/progress/update.js similarity index 93% rename from src/electron/renderer/src/progress/update.js rename to src/electron/frontend/core/progress/update.js index 5a2258f9b7..c621427b67 100644 --- a/src/electron/renderer/src/progress/update.js +++ b/src/electron/frontend/core/progress/update.js @@ -1,6 +1,6 @@ import { updateURLParams } from "../../utils/url.js"; -import { guidedProgressFilePath } from "../dependencies/simple.js"; -import { fs } from "../electron/index.js"; +import { guidedProgressFilePath } from "../globals.js"; +import { fs } from "../../utils/electron.js"; import { joinPath } from "../globals"; import { get, hasEntry } from "./index.js"; diff --git a/src/electron/renderer/src/promises.ts b/src/electron/frontend/core/promises.ts similarity index 100% rename from src/electron/renderer/src/promises.ts rename to src/electron/frontend/core/promises.ts diff --git a/src/electron/renderer/src/server/globals.ts b/src/electron/frontend/core/server/globals.ts similarity index 94% rename from src/electron/renderer/src/server/globals.ts rename to src/electron/frontend/core/server/globals.ts index 5ad4564721..6f23ea2c7b 100644 --- a/src/electron/renderer/src/server/globals.ts +++ b/src/electron/frontend/core/server/globals.ts @@ -1,4 +1,4 @@ -import { isElectron, app, port } from '../electron/index.js' +import { isElectron, app, port } from '../../utils/electron.js' import serverSVG from "../../assets/icons/server.svg?raw"; import webAssetSVG from "../../assets/icons/web_asset.svg?raw"; @@ -19,7 +19,7 @@ export const resolve = (object, callback) => { // ------------------------------------------------- -import { StatusBar } from "../stories/status/StatusBar.js"; +import { StatusBar } from "../components/status/StatusBar.js"; import { unsafeSVG } from "lit/directives/unsafe-svg.js"; const appVersion = app?.getVersion(); diff --git a/src/electron/renderer/src/server/index.ts b/src/electron/frontend/core/server/index.ts similarity index 94% rename from src/electron/renderer/src/server/index.ts rename to src/electron/frontend/core/server/index.ts index 62dba82f20..e8b69b2292 100644 --- a/src/electron/renderer/src/server/index.ts +++ b/src/electron/frontend/core/server/index.ts @@ -1,13 +1,13 @@ -import { isElectron, electron, app, port } from '../electron/index.js' +import { isElectron, electron, app, port } from '../../utils/electron.js' const { ipcRenderer } = electron; import { notyf, -} from '../dependencies/globals' +} from '../dependencies.js' import Swal from 'sweetalert2' -import { activateServer, baseUrl, statusBar } from './globals'; +import { activateServer, baseUrl, statusBar } from './globals.js'; // Check if the Flask server is live const serverIsLiveStartup = async () => { diff --git a/src/electron/renderer/src/validation/dandi.ts b/src/electron/frontend/core/validation/dandi.ts similarity index 100% rename from src/electron/renderer/src/validation/dandi.ts rename to src/electron/frontend/core/validation/dandi.ts diff --git a/src/electron/renderer/src/validation/index.js b/src/electron/frontend/core/validation/index.js similarity index 100% rename from src/electron/renderer/src/validation/index.js rename to src/electron/frontend/core/validation/index.js diff --git a/src/electron/renderer/src/validation/validation.json b/src/electron/frontend/core/validation/validation.json similarity index 100% rename from src/electron/renderer/src/validation/validation.json rename to src/electron/frontend/core/validation/validation.json diff --git a/src/electron/renderer/src/validation/validation.ts b/src/electron/frontend/core/validation/validation.ts similarity index 99% rename from src/electron/renderer/src/validation/validation.ts rename to src/electron/frontend/core/validation/validation.ts index 9a6b8eb1ae..961f9b5fc3 100644 --- a/src/electron/renderer/src/validation/validation.ts +++ b/src/electron/frontend/core/validation/validation.ts @@ -1,5 +1,5 @@ import schema from './validation.json' -import { JSONSchemaForm, getSchema } from '../stories/JSONSchemaForm' +import { JSONSchemaForm, getSchema } from '../components/JSONSchemaForm' import Swal from 'sweetalert2' diff --git a/src/electron/renderer/index.html b/src/electron/frontend/index.html similarity index 97% rename from src/electron/renderer/index.html rename to src/electron/frontend/index.html index 1429dac62b..a1d25a4fff 100755 --- a/src/electron/renderer/index.html +++ b/src/electron/frontend/index.html @@ -31,7 +31,7 @@ - +