diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..bdaa4581
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,22 @@
+; http://editorconfig.org/
+
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.{yml,yaml}]
+indent_size = 2
+
+[{vendor,inc/phpseclib}/**]
+; Use editor default (possible autodetection).
+indent_style =
+indent_size =
+end_of_line =
+trim_trailing_whitespace =
+insert_final_newline =
\ No newline at end of file
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..b702f289
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,3 @@
+# These are supported funding model platforms
+
+github: [LaswitchTech]
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 00000000..cc599e09
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,69 @@
+name: Bug report
+description: Create a report to help us improve
+title: ""
+labels: ["bug"]
+assignees:
+ - LouisOuellet
+
+body:
+ - type: textarea
+ id: description
+ attributes:
+ label: Description
+ description: Describe the bug or feature.
+ placeholder: "Description of the bug or feature"
+ validations:
+ required: true
+
+ - type: textarea
+ id: steps
+ attributes:
+ label: Steps to reproduce
+ description: Provide clear steps to reproduce.
+ placeholder: |
+ 1. First Step
+ 2. Second Step
+ 3. And so on...
+ validations:
+ required: true
+
+ - type: textarea
+ id: expected
+ attributes:
+ label: Expected behavior
+ placeholder: "What you expected to happen"
+ validations:
+ required: true
+
+ - type: textarea
+ id: actual
+ attributes:
+ label: Actual behavior
+ placeholder: "What actually happened"
+ validations:
+ required: true
+
+ - type: input
+ id: python_version
+ attributes:
+ label: Python version
+ placeholder: "e.g. 3.11.2"
+ validations:
+ required: false
+
+ - type: input
+ id: os_version
+ attributes:
+ label: Operating system version
+ placeholder: "e.g. Windows 10 / macOS 13.4 / Ubuntu 22.04"
+ validations:
+ required: false
+
+ - type: textarea
+ id: logs
+ attributes:
+ label: Screenshots or Logs
+ description: Paste logs or attach screenshots.
+ placeholder: "Paste your logs or attach the screenshot"
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 00000000..8005e322
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,2 @@
+blank_issues_enabled: false
+contact_links: []
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..db233359
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: feature request
+assignees: 'LouisOuellet'
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 00000000..d82c1128
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,48 @@
+name: Feature request
+description: Suggest an idea for this project
+title: ""
+labels: ["feature request"]
+assignees:
+ - LouisOuellet
+
+body:
+ - type: textarea
+ id: problem
+ attributes:
+ label: Is your feature request related to a problem?
+ description: Describe the problem this feature would solve.
+ placeholder: >
+ A clear and concise description of what the problem is.
+ Example: I'm always frustrated when [...]
+ validations:
+ required: true
+
+ - type: textarea
+ id: solution
+ attributes:
+ label: Describe the solution you'd like
+ description: Describe what you want to happen.
+ placeholder: >
+ A clear and concise description of the desired solution.
+ validations:
+ required: true
+
+ - type: textarea
+ id: alternatives
+ attributes:
+ label: Describe alternatives you've considered
+ description: Describe any alternative solutions or features you've considered.
+ placeholder: >
+ A clear and concise description of any alternative approaches.
+ validations:
+ required: false
+
+ - type: textarea
+ id: context
+ attributes:
+ label: Additional context
+ description: Add any other context or screenshots about the feature request.
+ placeholder: >
+ Add any other context, links, or screenshots here.
+ validations:
+ required: false
diff --git a/.github/no-response.yml b/.github/no-response.yml
new file mode 100644
index 00000000..c7c7e698
--- /dev/null
+++ b/.github/no-response.yml
@@ -0,0 +1,13 @@
+# Configuration for probot-no-response - https://github.com/probot/no-response
+
+# Number of days of inactivity before an Issue is closed for lack of response
+daysUntilClose: 14
+# Label requiring a response
+responseRequiredLabel: need more info
+# Comment to post when closing an Issue for lack of response. Set to `false` to disable
+closeComment: >
+ This issue has been automatically closed because there has been no response
+ to our request for more information from the original author. With only the
+ information that is currently in the issue, we don't have enough information
+ to take action. Please reach out if you have or find the answers we need so
+ that we can investigate further.
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 00000000..4cde3ed5
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,60 @@
+name: Release
+
+on:
+ push:
+ tags:
+ - 'v*'
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+
+ - name: Set Tag as Filename
+ id: tag_name
+ run: echo "TAG_NAME=${GITHUB_REF##*/}" >> $GITHUB_ENV
+
+ - name: Create ZIP file
+ run: zip -r "${{ env.TAG_NAME }}.zip" .
+
+ - name: Generate Changelog
+ id: generate_changelog
+ run: |
+ # Find the most recent tag before the current one
+ PREV_TAG=$(git describe --tags --abbrev=0 HEAD^)
+
+ # Create a new CHANGELOG.md file with headers
+ echo -e "# Changelog\n" > CHANGELOG.md
+
+ # List commit messages between the previous tag and current HEAD
+ git log ${PREV_TAG}..HEAD --pretty=format:"* %s" >> CHANGELOG.md
+
+ # List unique contributors for these commits
+ echo -e "\n\n# Contributors\n" >> CHANGELOG.md
+ git log ${PREV_TAG}..HEAD --format='%aN' | sort -u | awk '{print "* " $0}' >> CHANGELOG.md
+
+ - name: Create Release
+ id: create_release
+ uses: actions/create-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GH_PAT }}
+ with:
+ tag_name: ${{ github.ref }}
+ release_name: Release ${{ github.ref }}
+ draft: false
+ prerelease: false
+ body_path: ./CHANGELOG.md
+
+ - name: Upload Asset
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GH_PAT }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: ./${{ env.TAG_NAME }}.zip
+ asset_name: source.zip
+ asset_content_type: application/zip
diff --git a/.gitignore b/.gitignore
index b7faf403..4ef19446 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,16 +1,52 @@
+# Python
+/build/
+
+# Environments
+.env
+.venv
+env/
+venv/
+.venv/
+ENV/
+env.bak/
+venv.bak/
+TOKEN
+runtime
+
+# Mac OS X
+.DS_Store
+*.DS_Store
+
+# Git
+.git
+
+# Filetypes
+*.cfg
+*.log
+*.db
+
+# Unique Directories
+/tmp/
+/data/
+/dist/Replicator/
+!/dist/macos/
+!/dist/linux/
+!/dist/windows/
+
+# Exclusions for Python projects
+!build.cfg
+
# Byte-compiled / optimized / DLL files
__pycache__/
-*.py[codz]
+*.py[cod]
*$py.class
-# C extensions
-*.so
-
# Distribution / packaging
.Python
build/
develop-eggs/
-dist/
+config/
+logs/
downloads/
eggs/
.eggs/
@@ -46,7 +82,7 @@ htmlcov/
nosetests.xml
coverage.xml
*.cover
-*.py.cover
+*.py,cover
.hypothesis/
.pytest_cache/
cover/
@@ -82,47 +118,13 @@ target/
profile_default/
ipython_config.py
-# pyenv
-# For a library or package, you might want to ignore these files since the code is
-# intended to run in multiple environments; otherwise, check them in:
-# .python-version
-
-# pipenv
-# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
-# However, in case of collaboration, if having platform-specific dependencies or dependencies
-# having no cross-platform support, pipenv may install dependencies that don't work, or not
-# install all needed dependencies.
-#Pipfile.lock
-
-# UV
-# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
-# This is especially recommended for binary packages to ensure reproducibility, and is more
-# commonly ignored for libraries.
-#uv.lock
-
-# poetry
-# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
-# This is especially recommended for binary packages to ensure reproducibility, and is more
-# commonly ignored for libraries.
-# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
-#poetry.lock
-#poetry.toml
-
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
-# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
-# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
#pdm.lock
-#pdm.toml
-.pdm-python
-.pdm-build/
-
-# pixi
-# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
-#pixi.lock
-# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
-# in the .venv directory. It is recommended not to include this directory in version control.
-.pixi
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
@@ -134,74 +136,11 @@ celerybeat.pid
# SageMath parsed files
*.sage.py
-# Environments
-.env
-.envrc
-.venv
-env/
-venv/
-ENV/
-env.bak/
-venv.bak/
+# building source files
+src/bin/freerdp/source
+src/bin/freerdp/install
+src/bin/freerdp/build
-# Spyder project settings
-.spyderproject
-.spyproject
-
-# Rope project settings
-.ropeproject
-
-# mkdocs documentation
-/site
-
-# mypy
-.mypy_cache/
-.dmypy.json
-dmypy.json
-
-# Pyre type checker
-.pyre/
-
-# pytype static type analyzer
-.pytype/
-
-# Cython debug symbols
-cython_debug/
-
-# PyCharm
-# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
-# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
-# and can be added to the global gitignore or merged into this file. For a more nuclear
-# option (not recommended) you can uncomment the following to ignore the entire idea folder.
-#.idea/
-
-# Abstra
-# Abstra is an AI-powered process automation framework.
-# Ignore directories containing user credentials, local state, and settings.
-# Learn more at https://abstra.io/docs
-.abstra/
-
-# Visual Studio Code
-# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
-# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
-# and can be added to the global gitignore or merged into this file. However, if you prefer,
-# you could uncomment the following to ignore the entire vscode folder
-# .vscode/
-
-# Ruff stuff:
-.ruff_cache/
-
-# PyPI configuration file
-.pypirc
-
-# Cursor
-# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
-# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
-# refer to https://docs.cursor.com/context/ignore-files
-.cursorignore
-.cursorindexingignore
-
-# Marimo
-marimo/_static/
-marimo/_lsp/
-__marimo__/
+# Exclusions
+!src/bin/freerdp/*/*/lib/
+!src/bin/freerdp/*/*/xfreerdp
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000..137e0dc7
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,4 @@
+[submodule "src/core"]
+ path = src/core
+ url = https://github.com/LaswitchTech/corePY.git
+ branch = dev
diff --git a/README.md b/README.md
index 714570af..09556cf1 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,49 @@
+
+
# Replicator
-Data Replication Software, Replacement for Windows DFSR with support for Local, SMB and FTP filesystems.
+
+
+
+
+
+
+## Description
+Replicator is a cross-platform Python application designed to provide replication services similar to Windows DFSR. It supports local filesystems and SMB making it a versatile solution for data replication needs across different operating systems.
+
+## Features
+ - **Cross-Platform Compatibility**: Replicator is compatible with Windows, macOS and Linux, with specific adjustments made to ensure seamless operation on both operating systems.
+ - **SMB Support**: The application can replicate data to and from SMB shares, making it suitable for networked environments.
+ - **Configurable Replication Modes**: Users can choose between different replication modes (e.g., Mirror, Incremental) to suit their specific needs.
+ - **Scheduling**: Replication tasks can be scheduled to run at specific times or intervals, allowing for automated data synchronization.
+ - **Conflict Resolution**: The application includes strategies for resolving conflicts that may arise during replication, ensuring data integrity and consistency.
+ - **Service Integration**: Replicator can be integrated with system services to run in the background, providing continuous replication without user intervention.
+ - **Logging and Debugging**: The application includes logging features for easier debugging and tracking of issues during the replication process.
+
+## License
+This software is distributed under the [GPLv3](LICENSE) license.
+
+## Security
+Please disclose any vulnerabilities found responsibly – report security issues to the maintainers privately. See [SECURITY.md](SECURITY.md) for more information.
+
+## Contributing
+Contributions to Replicator are welcome! If you have ideas for new features or have found bugs, please open an issue or submit a pull request.
+
+### How to Contribute
+ - **Fork the Repository**: Create a fork of the repository on GitHub.
+ - **Create a New Branch**: For new features or bug fixes, create a new branch in your fork.
+ - **Submit a Pull Request**: Once your changes are ready, submit a pull request to the main repository.
+
+## To Do
+ - ~~**Support for Local**: Add support for Local filesystems.~~
+ - ~~**Support for SMB**: Add support for SMB Shares.~~
+ - ~~**Support for FTP**: Add support for FTP Shares.~~ --- REMOVED ---
+ - ~~**Support for SSHFS**: Add support for SSH filesystems.~~ --- REMOVED ---
+ - ~~**Mode Mirror**: Add replication mode Mirror.~~
+ - **Mode Incremental**: Add replication mode Incremental.
+ - ~~**Direction**: Add replication direction (One way, Two way).~~
+ - ~~**Scheduling**: Add scheduling capabilities for replication tasks.~~
+ - ~~**Conflict Resolution**: Implement conflict resolution strategies for file changes.~~
+ - ~~**Service Integration**: Integrate with system services for background operation.~~
+
+## Wait, where is the documentation?
+Review the [Documentation](https://laswitchtech.com/en/blog/projects/replicator/index).
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..86201e0b
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,10 @@
+# Security Policy
+
+Security vulnerabilities can be reported for the current stable release and the `stable` branch.
+
+## Reporting a Vulnerability
+
+You have multiple options on reporting vulnerabilities
+
+* Send an e-mail to [Support Team](mailto:support@laswitchtech.com)
+* Open a [Github Issue](https://github.com/LaswitchTech/Replicator/issues)
diff --git a/VERSION b/VERSION
new file mode 100644
index 00000000..45c7a584
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+v0.0.1
diff --git a/build.cfg b/build.cfg
new file mode 100644
index 00000000..098a7b7b
--- /dev/null
+++ b/build.cfg
@@ -0,0 +1,11 @@
+# Replicator build configuration
+
+NAME=Replicator
+ENTRY=src/main.py
+MODE=windowed
+PKG=auto
+CLEAN=0
+DMG=1
+SYSTEM_PYQT=0
+
+ADD_DATA=src/core:core;src/icons:icons;src/bin:bin;src/replicator:replicator
diff --git a/build.sh b/build.sh
new file mode 120000
index 00000000..64329510
--- /dev/null
+++ b/build.sh
@@ -0,0 +1 @@
+src/core/build.sh
\ No newline at end of file
diff --git a/cli.bat b/cli.bat
new file mode 100644
index 00000000..eb9b7f0a
--- /dev/null
+++ b/cli.bat
@@ -0,0 +1,71 @@
+@echo off
+setlocal EnableExtensions EnableDelayedExpansion
+
+REM ---------------------------------------------------------------------------
+REM Replicator CLI launcher (Windows)
+REM - Ensures a local venv exists (.venv)
+REM - Installs minimal runtime deps (PyQt5)
+REM - Runs src\main.py with a visible console
+REM
+REM Note: If you double-click this file, the console may flash and close.
+REM Run it from an existing Command Prompt for persistent output.
+REM ---------------------------------------------------------------------------
+
+set "SCRIPT_DIR=%~dp0"
+set "VENV_DIR=%SCRIPT_DIR%.venv"
+set "VENV_PY=%VENV_DIR%\Scripts\python.exe"
+
+REM Prefer Windows Python Launcher
+set "PY_CMD="
+set "PY_ARGS="
+where py >nul 2>&1
+if %ERRORLEVEL%==0 (
+ set "PY_CMD=py"
+ set "PY_ARGS=-3.11"
+) else (
+ where python >nul 2>&1
+ if %ERRORLEVEL%==0 (
+ set "PY_CMD=python"
+ set "PY_ARGS="
+ )
+)
+
+if "%PY_CMD%"=="" (
+ echo ERROR: Python not found. Install Python 3.11+ and ensure either `py` or `python` is available in PATH.
+ exit /b 1
+)
+
+REM Create venv if missing
+if not exist "%VENV_PY%" (
+ echo Creating virtualenv: %VENV_DIR%
+ %PY_CMD% %PY_ARGS% -m venv "%VENV_DIR%"
+ if %ERRORLEVEL% NEQ 0 (
+ echo ERROR: Failed to create virtualenv.
+ exit /b %ERRORLEVEL%
+ )
+)
+
+REM Upgrade pip/wheel
+"%VENV_PY%" -m pip install --upgrade pip wheel >nul
+
+REM Install minimal deps (idempotent)
+echo Installing minimal runtime deps (PyQt5)...
+"%VENV_PY%" -m pip install "PyQt5>=5.15,<6" >nul
+
+REM Prefer the built console companion if available
+if exist "%SCRIPT_DIR%dist\windows\Replicator-cli.exe" (
+ "%SCRIPT_DIR%dist\windows\Replicator-cli.exe" %*
+ endlocal
+ exit /b %ERRORLEVEL%
+)
+
+REM Fallback: Run the source entry in console mode (dev checkout)
+if not exist "%SCRIPT_DIR%src\main.py" (
+ echo ERROR: Entry not found: %SCRIPT_DIR%src\main.py
+ echo NOTE: Replicator-cli.exe not found at %SCRIPT_DIR%dist\windows\Replicator-cli.exe
+ exit /b 1
+)
+
+"%VENV_PY%" "%SCRIPT_DIR%src\main.py" %*
+endlocal
+exit /b %ERRORLEVEL%
diff --git a/cli.sh b/cli.sh
new file mode 100755
index 00000000..ea005e83
--- /dev/null
+++ b/cli.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+cd "$ROOT_DIR"
+
+exec "./launch.sh" "$@"
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/generic/libqtuiotouchplugin.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/generic/libqtuiotouchplugin.dylib
new file mode 100755
index 00000000..8a3f54df
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/generic/libqtuiotouchplugin.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/iconengines/libqsvgicon.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/iconengines/libqsvgicon.dylib
new file mode 100755
index 00000000..3b074a83
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/iconengines/libqsvgicon.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqgif.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqgif.dylib
new file mode 100755
index 00000000..ab755e86
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqgif.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqicns.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqicns.dylib
new file mode 100755
index 00000000..c7ece31f
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqicns.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqico.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqico.dylib
new file mode 100755
index 00000000..20ef8c61
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqico.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqjpeg.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqjpeg.dylib
new file mode 100755
index 00000000..273191e6
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqjpeg.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqmacheif.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqmacheif.dylib
new file mode 100755
index 00000000..078c5029
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqmacheif.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqmacjp2.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqmacjp2.dylib
new file mode 100755
index 00000000..632cfbce
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqmacjp2.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqsvg.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqsvg.dylib
new file mode 100755
index 00000000..d04f4a04
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqsvg.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqtga.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqtga.dylib
new file mode 100755
index 00000000..816cf64d
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqtga.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqtiff.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqtiff.dylib
new file mode 100755
index 00000000..59014c67
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqtiff.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqwbmp.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqwbmp.dylib
new file mode 100755
index 00000000..09dae7c6
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqwbmp.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqwebp.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqwebp.dylib
new file mode 100755
index 00000000..e080a932
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/imageformats/libqwebp.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/platforms/libqcocoa.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/platforms/libqcocoa.dylib
new file mode 100755
index 00000000..7d7f2702
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/platforms/libqcocoa.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/platforms/libqminimal.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/platforms/libqminimal.dylib
new file mode 100755
index 00000000..832360b6
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/platforms/libqminimal.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/platforms/libqoffscreen.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/platforms/libqoffscreen.dylib
new file mode 100755
index 00000000..c0188c9f
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/platforms/libqoffscreen.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/platforms/libqwebgl.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/platforms/libqwebgl.dylib
new file mode 100755
index 00000000..9981cead
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/platforms/libqwebgl.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/platformthemes/libqxdgdesktopportal.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/platformthemes/libqxdgdesktopportal.dylib
new file mode 100755
index 00000000..c8b3a7b4
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/platformthemes/libqxdgdesktopportal.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/styles/libqmacstyle.dylib b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/styles/libqmacstyle.dylib
new file mode 100755
index 00000000..79b51b23
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/plugins/styles/libqmacstyle.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/translations b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/translations
new file mode 120000
index 00000000..447e157c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/Qt5/translations
@@ -0,0 +1 @@
+../../../Resources/PyQt5/Qt5/translations
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/QtCore.abi3.so b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/QtCore.abi3.so
new file mode 100755
index 00000000..0d8e8edd
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/QtCore.abi3.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/QtGui.abi3.so b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/QtGui.abi3.so
new file mode 100755
index 00000000..890191f9
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/QtGui.abi3.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/QtSvg.abi3.so b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/QtSvg.abi3.so
new file mode 100755
index 00000000..0e6614fc
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/QtSvg.abi3.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/QtWidgets.abi3.so b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/QtWidgets.abi3.so
new file mode 100755
index 00000000..705b6dc6
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/QtWidgets.abi3.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/sip.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/sip.cpython-311-darwin.so
new file mode 100755
index 00000000..2a9b08c1
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/PyQt5/sip.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/Python b/dist/macos/Replicator.app/Contents/Frameworks/Python
new file mode 120000
index 00000000..e485e133
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/Python
@@ -0,0 +1 @@
+Python.framework/Versions/3.11/Python
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Python b/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Python
new file mode 120000
index 00000000..be758541
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Python
@@ -0,0 +1 @@
+Versions/Current/Python
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Resources b/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Resources
new file mode 120000
index 00000000..953ee36f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Resources
@@ -0,0 +1 @@
+Versions/Current/Resources
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Versions/3.11/Python b/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Versions/3.11/Python
new file mode 100755
index 00000000..3cbfd4d1
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Versions/3.11/Python differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Versions/3.11/Resources/Info.plist b/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Versions/3.11/Resources/Info.plist
new file mode 100644
index 00000000..c7e76894
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Versions/3.11/Resources/Info.plist
@@ -0,0 +1,28 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ Python
+ CFBundleGetInfoString
+ Python Runtime and Library
+ CFBundleIdentifier
+ org.python.python
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ Python
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 3.11.14, (c) 2001-2023 Python Software Foundation.
+ CFBundleLongVersionString
+ 3.11.14, (c) 2001-2023 Python Software Foundation.
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 3.11.14
+
+
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Versions/3.11/_CodeSignature/CodeResources b/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Versions/3.11/_CodeSignature/CodeResources
new file mode 100644
index 00000000..3b059e78
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Versions/3.11/_CodeSignature/CodeResources
@@ -0,0 +1,128 @@
+
+
+
+
+ files
+
+ Resources/Info.plist
+
+ 5cCbGbqwL3PCsmrGaOO/Pg3ax4c=
+
+
+ files2
+
+ Resources/Info.plist
+
+ hash2
+
+ f6GvCgok6FC7HDI6N5dv6y5nBc2YA8Y0hRxKn+fj6M4=
+
+
+
+ rules
+
+ ^Resources/
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^version.plist$
+
+
+ rules2
+
+ .*\.dSYM($|/)
+
+ weight
+ 11
+
+ ^(.*/)?\.DS_Store$
+
+ omit
+
+ weight
+ 2000
+
+ ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
+
+ nested
+
+ weight
+ 10
+
+ ^.*
+
+ ^Info\.plist$
+
+ omit
+
+ weight
+ 20
+
+ ^PkgInfo$
+
+ omit
+
+ weight
+ 20
+
+ ^Resources/
+
+ weight
+ 20
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^[^/]+$
+
+ nested
+
+ weight
+ 10
+
+ ^embedded\.provisionprofile$
+
+ weight
+ 20
+
+ ^version\.plist$
+
+ weight
+ 20
+
+
+
+
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Versions/Current b/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Versions/Current
new file mode 120000
index 00000000..902b2c90
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/Python.framework/Versions/Current
@@ -0,0 +1 @@
+3.11
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/QtCore b/dist/macos/Replicator.app/Contents/Frameworks/QtCore
new file mode 120000
index 00000000..eb4d1ef3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/QtCore
@@ -0,0 +1 @@
+PyQt5/Qt5/lib/QtCore.framework/Versions/5/QtCore
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/QtDBus b/dist/macos/Replicator.app/Contents/Frameworks/QtDBus
new file mode 120000
index 00000000..509df15f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/QtDBus
@@ -0,0 +1 @@
+PyQt5/Qt5/lib/QtDBus.framework/Versions/5/QtDBus
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/QtGui b/dist/macos/Replicator.app/Contents/Frameworks/QtGui
new file mode 120000
index 00000000..70a2b83f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/QtGui
@@ -0,0 +1 @@
+PyQt5/Qt5/lib/QtGui.framework/Versions/5/QtGui
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/QtNetwork b/dist/macos/Replicator.app/Contents/Frameworks/QtNetwork
new file mode 120000
index 00000000..642f5624
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/QtNetwork
@@ -0,0 +1 @@
+PyQt5/Qt5/lib/QtNetwork.framework/Versions/5/QtNetwork
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/QtPrintSupport b/dist/macos/Replicator.app/Contents/Frameworks/QtPrintSupport
new file mode 120000
index 00000000..531f139d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/QtPrintSupport
@@ -0,0 +1 @@
+PyQt5/Qt5/lib/QtPrintSupport.framework/Versions/5/QtPrintSupport
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/QtQml b/dist/macos/Replicator.app/Contents/Frameworks/QtQml
new file mode 120000
index 00000000..4671a083
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/QtQml
@@ -0,0 +1 @@
+PyQt5/Qt5/lib/QtQml.framework/Versions/5/QtQml
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/QtQmlModels b/dist/macos/Replicator.app/Contents/Frameworks/QtQmlModels
new file mode 120000
index 00000000..042a8ead
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/QtQmlModels
@@ -0,0 +1 @@
+PyQt5/Qt5/lib/QtQmlModels.framework/Versions/5/QtQmlModels
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/QtQuick b/dist/macos/Replicator.app/Contents/Frameworks/QtQuick
new file mode 120000
index 00000000..60c0937b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/QtQuick
@@ -0,0 +1 @@
+PyQt5/Qt5/lib/QtQuick.framework/Versions/5/QtQuick
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/QtSvg b/dist/macos/Replicator.app/Contents/Frameworks/QtSvg
new file mode 120000
index 00000000..a9847407
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/QtSvg
@@ -0,0 +1 @@
+PyQt5/Qt5/lib/QtSvg.framework/Versions/5/QtSvg
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/QtWebSockets b/dist/macos/Replicator.app/Contents/Frameworks/QtWebSockets
new file mode 120000
index 00000000..8a6e3c10
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/QtWebSockets
@@ -0,0 +1 @@
+PyQt5/Qt5/lib/QtWebSockets.framework/Versions/5/QtWebSockets
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/QtWidgets b/dist/macos/Replicator.app/Contents/Frameworks/QtWidgets
new file mode 120000
index 00000000..deba0910
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/QtWidgets
@@ -0,0 +1 @@
+PyQt5/Qt5/lib/QtWidgets.framework/Versions/5/QtWidgets
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/base_library.zip b/dist/macos/Replicator.app/Contents/Frameworks/base_library.zip
new file mode 120000
index 00000000..89ddc936
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/base_library.zip
@@ -0,0 +1 @@
+../Resources/base_library.zip
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/bin b/dist/macos/Replicator.app/Contents/Frameworks/bin
new file mode 120000
index 00000000..5423f908
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/bin
@@ -0,0 +1 @@
+../Resources/bin
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/core b/dist/macos/Replicator.app/Contents/Frameworks/core
new file mode 120000
index 00000000..8f7c1f19
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/core
@@ -0,0 +1 @@
+../Resources/core
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/icons b/dist/macos/Replicator.app/Contents/Frameworks/icons
new file mode 120000
index 00000000..92077459
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/icons
@@ -0,0 +1 @@
+../Resources/icons
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/libcrypto.3.dylib b/dist/macos/Replicator.app/Contents/Frameworks/libcrypto.3.dylib
new file mode 100755
index 00000000..c68b11e2
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/libcrypto.3.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/liblzma.5.dylib b/dist/macos/Replicator.app/Contents/Frameworks/liblzma.5.dylib
new file mode 100755
index 00000000..7321eb16
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/liblzma.5.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/libmpdec.4.dylib b/dist/macos/Replicator.app/Contents/Frameworks/libmpdec.4.dylib
new file mode 100755
index 00000000..88c33980
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/libmpdec.4.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/libsqlite3.dylib b/dist/macos/Replicator.app/Contents/Frameworks/libsqlite3.dylib
new file mode 100755
index 00000000..b19e2d9d
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/libsqlite3.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/libssl.3.dylib b/dist/macos/Replicator.app/Contents/Frameworks/libssl.3.dylib
new file mode 100755
index 00000000..b8632371
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/libssl.3.dylib differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3.11 b/dist/macos/Replicator.app/Contents/Frameworks/python3.11
new file mode 120000
index 00000000..79569286
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/python3.11
@@ -0,0 +1 @@
+python3__dot__11
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_bisect.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_bisect.cpython-311-darwin.so
new file mode 100755
index 00000000..eb8e9e20
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_bisect.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_blake2.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_blake2.cpython-311-darwin.so
new file mode 100755
index 00000000..ac715c0a
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_blake2.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_bz2.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_bz2.cpython-311-darwin.so
new file mode 100755
index 00000000..a963a4dc
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_bz2.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_cn.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_cn.cpython-311-darwin.so
new file mode 100755
index 00000000..f539e948
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_cn.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_hk.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_hk.cpython-311-darwin.so
new file mode 100755
index 00000000..a0becafc
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_hk.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_iso2022.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_iso2022.cpython-311-darwin.so
new file mode 100755
index 00000000..e305d2dc
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_iso2022.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_jp.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_jp.cpython-311-darwin.so
new file mode 100755
index 00000000..63875754
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_jp.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_kr.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_kr.cpython-311-darwin.so
new file mode 100755
index 00000000..5daca109
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_kr.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_tw.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_tw.cpython-311-darwin.so
new file mode 100755
index 00000000..f4222c57
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_codecs_tw.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_contextvars.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_contextvars.cpython-311-darwin.so
new file mode 100755
index 00000000..17767664
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_contextvars.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_csv.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_csv.cpython-311-darwin.so
new file mode 100755
index 00000000..0763582a
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_csv.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_ctypes.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_ctypes.cpython-311-darwin.so
new file mode 100755
index 00000000..a775bc0a
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_ctypes.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_datetime.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_datetime.cpython-311-darwin.so
new file mode 100755
index 00000000..2c73069e
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_datetime.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_decimal.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_decimal.cpython-311-darwin.so
new file mode 100755
index 00000000..74ec6bdb
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_decimal.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_hashlib.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_hashlib.cpython-311-darwin.so
new file mode 100755
index 00000000..d8ec992d
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_hashlib.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_heapq.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_heapq.cpython-311-darwin.so
new file mode 100755
index 00000000..809b0f5e
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_heapq.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_json.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_json.cpython-311-darwin.so
new file mode 100755
index 00000000..2c2ea0c3
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_json.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_lzma.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_lzma.cpython-311-darwin.so
new file mode 100755
index 00000000..53fc01b7
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_lzma.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_md5.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_md5.cpython-311-darwin.so
new file mode 100755
index 00000000..4631558d
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_md5.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_multibytecodec.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_multibytecodec.cpython-311-darwin.so
new file mode 100755
index 00000000..10003664
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_multibytecodec.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_opcode.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_opcode.cpython-311-darwin.so
new file mode 100755
index 00000000..9ee91e82
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_opcode.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_pickle.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_pickle.cpython-311-darwin.so
new file mode 100755
index 00000000..458d4078
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_pickle.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_posixsubprocess.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_posixsubprocess.cpython-311-darwin.so
new file mode 100755
index 00000000..9771de57
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_posixsubprocess.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_random.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_random.cpython-311-darwin.so
new file mode 100755
index 00000000..d1b136be
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_random.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_scproxy.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_scproxy.cpython-311-darwin.so
new file mode 100755
index 00000000..75a7df4b
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_scproxy.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_sha1.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_sha1.cpython-311-darwin.so
new file mode 100755
index 00000000..8c47cbeb
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_sha1.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_sha256.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_sha256.cpython-311-darwin.so
new file mode 100755
index 00000000..902123f5
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_sha256.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_sha3.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_sha3.cpython-311-darwin.so
new file mode 100755
index 00000000..e73af632
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_sha3.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_sha512.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_sha512.cpython-311-darwin.so
new file mode 100755
index 00000000..ed3e2a48
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_sha512.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_socket.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_socket.cpython-311-darwin.so
new file mode 100755
index 00000000..57a31974
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_socket.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_sqlite3.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_sqlite3.cpython-311-darwin.so
new file mode 100755
index 00000000..99f741ca
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_sqlite3.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_ssl.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_ssl.cpython-311-darwin.so
new file mode 100755
index 00000000..802a2af3
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_ssl.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_statistics.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_statistics.cpython-311-darwin.so
new file mode 100755
index 00000000..cec70bda
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_statistics.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_struct.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_struct.cpython-311-darwin.so
new file mode 100755
index 00000000..c71be608
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_struct.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_typing.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_typing.cpython-311-darwin.so
new file mode 100755
index 00000000..4a7f1342
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/_typing.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/array.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/array.cpython-311-darwin.so
new file mode 100755
index 00000000..41dd25a5
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/array.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/binascii.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/binascii.cpython-311-darwin.so
new file mode 100755
index 00000000..a2fff8b9
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/binascii.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/fcntl.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/fcntl.cpython-311-darwin.so
new file mode 100755
index 00000000..2f283375
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/fcntl.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/grp.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/grp.cpython-311-darwin.so
new file mode 100755
index 00000000..7b95d54b
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/grp.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/math.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/math.cpython-311-darwin.so
new file mode 100755
index 00000000..2b48ee51
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/math.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/pyexpat.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/pyexpat.cpython-311-darwin.so
new file mode 100755
index 00000000..1f478850
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/pyexpat.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/resource.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/resource.cpython-311-darwin.so
new file mode 100755
index 00000000..cff7d1fb
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/resource.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/select.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/select.cpython-311-darwin.so
new file mode 100755
index 00000000..f22a57bd
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/select.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/termios.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/termios.cpython-311-darwin.so
new file mode 100755
index 00000000..40437079
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/termios.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/unicodedata.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/unicodedata.cpython-311-darwin.so
new file mode 100755
index 00000000..286d7065
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/unicodedata.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/zlib.cpython-311-darwin.so b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/zlib.cpython-311-darwin.so
new file mode 100755
index 00000000..f9ca3c9c
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Frameworks/python3__dot__11/lib-dynload/zlib.cpython-311-darwin.so differ
diff --git a/dist/macos/Replicator.app/Contents/Frameworks/replicator b/dist/macos/Replicator.app/Contents/Frameworks/replicator
new file mode 120000
index 00000000..f50463de
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Frameworks/replicator
@@ -0,0 +1 @@
+../Resources/replicator
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Info.plist b/dist/macos/Replicator.app/Contents/Info.plist
new file mode 100644
index 00000000..fc99d631
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Info.plist
@@ -0,0 +1,24 @@
+
+
+
+
+ CFBundleDisplayName
+ Replicator
+ CFBundleExecutable
+ Replicator
+ CFBundleIconFile
+ icon.icns
+ CFBundleIdentifier
+ Replicator
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ Replicator
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 0.0.0
+ NSHighResolutionCapable
+
+
+
diff --git a/dist/macos/Replicator.app/Contents/MacOS/Replicator b/dist/macos/Replicator.app/Contents/MacOS/Replicator
new file mode 100755
index 00000000..50763987
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/MacOS/Replicator differ
diff --git a/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/lib b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/lib
new file mode 120000
index 00000000..a64516e1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/lib
@@ -0,0 +1 @@
+../../../Frameworks/PyQt5/Qt5/lib
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/plugins b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/plugins
new file mode 120000
index 00000000..1d5e3b1a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/plugins
@@ -0,0 +1 @@
+../../../Frameworks/PyQt5/Qt5/plugins
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_ar.qm b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_ar.qm
new file mode 100644
index 00000000..33eda481
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_ar.qm differ
diff --git a/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_bg.qm b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_bg.qm
new file mode 100644
index 00000000..ad48af72
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_bg.qm differ
diff --git a/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_ca.qm b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_ca.qm
new file mode 100644
index 00000000..ae864657
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_ca.qm differ
diff --git a/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_cs.qm b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_cs.qm
new file mode 100644
index 00000000..40aec718
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_cs.qm differ
diff --git a/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_da.qm b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_da.qm
new file mode 100644
index 00000000..eefbe648
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_da.qm differ
diff --git a/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_de.qm b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_de.qm
new file mode 100644
index 00000000..2e94a253
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_de.qm differ
diff --git a/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_en.qm b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_en.qm
new file mode 100644
index 00000000..be651eed
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/PyQt5/Qt5/translations/qt_en.qm
@@ -0,0 +1 @@
+
+ This issue has been automatically closed because there has been no response
+ to our request for more information from the original author. With only the
+ information that is currently in the issue, we don't have enough information
+ to take action. Please reach out if you have or find the answers we need so
+ that we can investigate further.
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/.github/workflows/release.yml b/dist/macos/Replicator.app/Contents/Resources/core/.github/workflows/release.yml
new file mode 100644
index 00000000..4cde3ed5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/.github/workflows/release.yml
@@ -0,0 +1,60 @@
+name: Release
+
+on:
+ push:
+ tags:
+ - 'v*'
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+
+ - name: Set Tag as Filename
+ id: tag_name
+ run: echo "TAG_NAME=${GITHUB_REF##*/}" >> $GITHUB_ENV
+
+ - name: Create ZIP file
+ run: zip -r "${{ env.TAG_NAME }}.zip" .
+
+ - name: Generate Changelog
+ id: generate_changelog
+ run: |
+ # Find the most recent tag before the current one
+ PREV_TAG=$(git describe --tags --abbrev=0 HEAD^)
+
+ # Create a new CHANGELOG.md file with headers
+ echo -e "# Changelog\n" > CHANGELOG.md
+
+ # List commit messages between the previous tag and current HEAD
+ git log ${PREV_TAG}..HEAD --pretty=format:"* %s" >> CHANGELOG.md
+
+ # List unique contributors for these commits
+ echo -e "\n\n# Contributors\n" >> CHANGELOG.md
+ git log ${PREV_TAG}..HEAD --format='%aN' | sort -u | awk '{print "* " $0}' >> CHANGELOG.md
+
+ - name: Create Release
+ id: create_release
+ uses: actions/create-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GH_PAT }}
+ with:
+ tag_name: ${{ github.ref }}
+ release_name: Release ${{ github.ref }}
+ draft: false
+ prerelease: false
+ body_path: ./CHANGELOG.md
+
+ - name: Upload Asset
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GH_PAT }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: ./${{ env.TAG_NAME }}.zip
+ asset_name: source.zip
+ asset_content_type: application/zip
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/.gitignore b/dist/macos/Replicator.app/Contents/Resources/core/.gitignore
new file mode 100644
index 00000000..3554cd79
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/.gitignore
@@ -0,0 +1,137 @@
+# Python
+/build/
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+TOKEN
+runtime
+
+# Mac OS X
+.DS_Store
+*.DS_Store
+
+# Git
+.git
+
+# Filetypes
+*.cfg
+*.log
+*.db
+
+# Unique Directories
+/tmp/
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+config/
+logs/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# building source files
+src/bin/freerdp/source
+src/bin/freerdp/install
+src/bin/freerdp/build
+
+# Exclusions
+!src/bin/freerdp/*/*/lib/
+!src/bin/freerdp/*/*/xfreerdp
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/LICENSE b/dist/macos/Replicator.app/Contents/Resources/core/LICENSE
new file mode 100644
index 00000000..f288702d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/README.md b/dist/macos/Replicator.app/Contents/Resources/core/README.md
new file mode 100644
index 00000000..c11ae7f0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/README.md
@@ -0,0 +1,38 @@
+
+
+# corePY
+
+
+
+
+
+
+## Description
+corePY is a framework of libraries designed to facilitate the creation of python-based applications.
+
+## Features
+ - **Uniform Styling**: corePY provides a consistent look and feel across all applications built using its libraries.
+ - **Modular Architecture**: The framework is designed with modularity in mind, allowing developers to easily integrate and extend functionality.
+ - **Cross-Platform Support**: corePY is compatible with multiple operating systems, ensuring broad accessibility for users.
+ - **Command-Line**: corePY includes a robust command-line interface for managing applications and performing various tasks.
+
+## License
+This software is distributed under the [GPLv3](LICENSE) license.
+
+## Security
+Please disclose any vulnerabilities found responsibly – report security issues to the maintainers privately. See [SECURITY.md](SECURITY.md) for more information.
+
+## Contributing
+Contributions to corePY are welcome! If you have ideas for new features or have found bugs, please open an issue or submit a pull request.
+
+### How to Contribute
+ - **Fork the Repository**: Create a fork of the repository on GitHub.
+ - **Create a New Branch**: For new features or bug fixes, create a new branch in your fork.
+ - **Submit a Pull Request**: Once your changes are ready, submit a pull request to the main repository.
+
+## To Do
+ - **Provisioning System**: Develop a provisioning system for easier deployment and management.
+ - **Log on file**: Add the ability to save the log on file.
+
+## Wait, where is the documentation?
+Review the [Documentation](https://laswitchtech.com/en/blog/projects/corepy/index).
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/SECURITY.md b/dist/macos/Replicator.app/Contents/Resources/core/SECURITY.md
new file mode 100644
index 00000000..4a0c7af6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/SECURITY.md
@@ -0,0 +1,10 @@
+# Security Policy
+
+Security vulnerabilities can be reported for the current stable release and the `stable` branch.
+
+## Reporting a Vulnerability
+
+You have multiple options on reporting vulnerabilities
+
+* Send an e-mail to [Support Team](mailto:support@laswitchtech.com)
+* Open a [Github Issue](https://github.com/LaswitchTech/corePY/issues)
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/VERSION b/dist/macos/Replicator.app/Contents/Resources/core/VERSION
new file mode 100644
index 00000000..3e7bcf08
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/VERSION
@@ -0,0 +1 @@
+v1.0.4
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/__init__.py b/dist/macos/Replicator.app/Contents/Resources/core/__init__.py
new file mode 100644
index 00000000..4223ff4c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/__init__.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+# src/core/__init__.py
+
+from .application import Application
+from .cli import CommandLine
+from .helper import Helper
+from .configuration import Configuration
+from .log import Log
+from .ui import MsgBox, StepIndicator, Form
+
+from .network.diagnostic import Diagnostic
+from .network.tools import Tools
+
+from .database import SQLite
+from .filesystem import FileSystem
+
+__version__ = "1.0.0"
+
+__all__ = ["Application", "CommandLine", "Helper", "Configuration", "Log", "MsgBox", "StepIndicator", "Form", "Diagnostic", "Tools", "SQLite", "FileSystem"]
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/application.py b/dist/macos/Replicator.app/Contents/Resources/core/application.py
new file mode 100644
index 00000000..1bacf1ef
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/application.py
@@ -0,0 +1,478 @@
+#!/usr/bin/env python3
+# src/core/application.py
+from __future__ import annotations
+
+from typing import Optional, Iterable
+from PyQt5.QtCore import pyqtSignal, Qt, QThread
+from PyQt5.QtWidgets import QProxyStyle, QStyle, QApplication, QProgressDialog, QPushButton
+
+from .helper import Helper
+from .configuration import Configuration
+from .log import Log
+from .service import Service
+from .ui import MsgBox
+import os
+import subprocess
+
+# ---------------------------------------------------------------------------
+# Custom Style to suppress focus rectangles
+# ---------------------------------------------------------------------------
+
+class NoFocusRectStyle(QProxyStyle):
+ def drawPrimitive(self, element, option, painter, widget=None):
+ if element == QStyle.PE_FrameFocusRect:
+ return # skip drawing the focus rect completely
+ super().drawPrimitive(element, option, painter, widget)
+
+# ---------------------------------------------------------------------------
+# Application update dialog
+# ---------------------------------------------------------------------------
+
+class ApplicationDialog(QProgressDialog):
+ canceled_by_user = pyqtSignal()
+
+ def __init__(self, parent=None):
+ super().__init__("Updating...", "Cancel", 0, 0, parent)
+ self.setWindowModality(Qt.WindowModal)
+ self.setWindowFlags(
+ Qt.Dialog | Qt.WindowTitleHint
+ | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint
+ )
+ self.setObjectName("ApplicationDialog")
+ self.setMinimumDuration(0)
+ self.setAutoReset(False)
+ self.setFixedWidth(300)
+
+ # Replace default Cancel button with our own so we can style it if needed
+ btn = QPushButton("Cancel", self)
+ btn.clicked.connect(self._on_cancel)
+ self.setCancelButton(btn)
+
+ def _on_cancel(self):
+ self.canceled_by_user.emit()
+ self.reject()
+
+# ---------------------------------------------------------------------------
+# Background worker for application update
+# ---------------------------------------------------------------------------
+
+class ApplicationThread(QThread):
+
+ finished_with_result = pyqtSignal(int, bool) # rc, canceled
+ progress_text = pyqtSignal(str) # label to show in dialog
+
+ def __init__(
+ self,
+ repo_root: str,
+ tasks: list[tuple[list[str], str | None]] | None = None,
+ logger: Log | None = None,
+ parent=None,
+ ):
+ super().__init__(parent)
+ self._repo_root = repo_root
+ self._tasks = tasks or []
+ self._logger = logger
+
+ def run(self) -> None:
+ import time
+
+ rc = -1
+ canceled = False
+
+ def run_command(args: list[str]) -> int:
+ nonlocal canceled
+ try:
+ proc = subprocess.Popen(args)
+ except Exception as e:
+ if self._logger:
+ self._logger.append(
+ f"[ApplicationThread] Failed to start command {args!r}: {e}",
+ channel="system",
+ level="error",
+ )
+ return -1
+
+ while True:
+ if self.isInterruptionRequested():
+ canceled = True
+ try:
+ proc.terminate()
+ except Exception:
+ pass
+ try:
+ return proc.wait()
+ except Exception:
+ return -1
+
+ r = proc.poll()
+ if r is not None:
+ return r
+
+ time.sleep(0.05)
+
+ # ---- 1) update repository ---------------------------------------------------
+ if self._logger:
+ self._logger.append(
+ f"[ApplicationThread] Starting git pull in {self._repo_root}",
+ channel="system",
+ level="info",
+ )
+
+ self.progress_text.emit("Updating application files...")
+ rc = run_command(["sudo", "git", "-C", self._repo_root, "pull", "--recurse-submodules"])
+
+ if self._logger:
+ self._logger.append(
+ f"[ApplicationThread] git pull finished ({rc})",
+ channel="system",
+ level="info" if rc == 0 else "error",
+ )
+
+ if rc != 0 or canceled:
+ self.finished_with_result.emit(rc if rc is not None else -1, canceled)
+ return
+
+ # ---- 2) Post-update tasks -----------------------------------------
+ for args, label in self._tasks:
+ if label:
+ self.progress_text.emit(label)
+
+ rc = run_command(args)
+
+ if self._logger:
+ self._logger.append(
+ f"[ApplicationThread] Command finished ({rc}): {' '.join(args)}",
+ channel="system",
+ level="info" if rc == 0 else "error",
+ )
+
+ if rc != 0 or canceled:
+ break
+
+ self.finished_with_result.emit(rc if rc is not None else -1, canceled)
+
+# ---------------------------------------------------------------------------
+# Application class
+# ---------------------------------------------------------------------------
+
+class Application(QApplication):
+
+ updating = pyqtSignal(object)
+
+ def __init__(self, name: Optional[str] = None, argv=None):
+
+ # Initialize QApplication
+ super().__init__(argv or [])
+
+ # Application name
+ if name:
+ self.setApplicationName(name)
+
+ # Set application mode
+ self._mode = "gui"
+
+ # Set application style
+ self.setStyle('Fusion')
+
+ # Use custom style to suppress focus rectangles
+ self.setStyle(NoFocusRectStyle(self.style()))
+
+ # Main window placeholder (e.g. Client)
+ self._mainWindow = None
+
+ # Helper
+ self._helper = Helper()
+
+ # Configuration manager
+ self._configuration = Configuration()
+ self._configuration.configChanged.connect(self.reset)
+
+ # Default configuration entries
+ self._configuration.add("administration.update", None, "button", label="Check for Updates", action=self.update)
+
+ # Save any new defaults
+ self._configuration.save()
+
+ # Logger
+ self._logger = Log()
+
+ # Service manager
+ self._service = Service(self._logger, self._configuration)
+
+ # Initial stylesheet load
+ self._loadStylesheet()
+
+ # ------------------------------------------------------------------
+ # Properties / accessors
+ # ------------------------------------------------------------------
+
+ @property
+ def helper(self) -> Helper:
+ return self._helper
+
+ @property
+ def logger(self) -> Log:
+ return self._logger
+
+ @property
+ def configuration(self) -> Configuration:
+ return self._configuration
+
+ @property
+ def service(self) -> Service:
+ return self._service
+
+ @property
+ def mainWindow(self):
+ return self._mainWindow
+
+ @property
+ def name(self) -> str:
+ return self.applicationName()
+
+ @property
+ def mode(self) -> str:
+ return self._mode
+
+ # ------------------------------------------------------------------
+ # Main window management
+ # ------------------------------------------------------------------
+
+ def set_mainWindow(self, window):
+ self._mainWindow = window
+ self._loadStylesheet()
+ self._mainWindow.show()
+
+ # ------------------------------------------------------------------
+ # Stylesheet handling
+ # ------------------------------------------------------------------
+
+ def _loadStylesheet(self):
+
+ # Base stylesheet (e.g. core/styles/style.css)
+ base_css = self._helper.load_stylesheet("core/styles/style.css") # you can implement this in Helper
+
+ # Retrieve icon paths
+ check_svg = self._helper.get_path("core/icons/check.svg")
+ chevron_up_svg = self._helper.get_path("core/icons/chevron-up.svg")
+ chevron_down_svg = self._helper.get_path("core/icons/chevron-down.svg")
+ chevron_expand_svg = self._helper.get_path("core/icons/chevron-expand.svg")
+
+ # Override styles
+ override = (
+ "\n"
+ "QCheckBox::indicator:checked { "
+ f"image: {self._helper.qss_url(check_svg)};"
+ " }\n"
+ "QComboBox::down-arrow { "
+ f"image: {self._helper.qss_url(chevron_expand_svg)};"
+ " }\n"
+ "QSpinBox::up-arrow { "
+ f"image: {self._helper.qss_url(chevron_up_svg)};"
+ " }\n"
+ "QSpinBox::down-arrow { "
+ f"image: {self._helper.qss_url(chevron_down_svg)};"
+ " }\n"
+ )
+
+ # Start with base + global overrides
+ css = (base_css or "") + override
+
+ # If main window has its own override, append it
+ if self._mainWindow and hasattr(self._mainWindow, "override"):
+ css += self._mainWindow.override()
+
+ # Apply combined stylesheet
+ self.setStyleSheet(css)
+
+ # ------------------------------------------------------------------
+ # UI reset on configuration change
+ # ------------------------------------------------------------------
+
+ def reset(self):
+
+ # Do nothing if no main window
+ if not self._mainWindow:
+ return
+
+ # Call main window reset if available
+ if hasattr(self._mainWindow, 'reset'):
+ self._loadStylesheet()
+ self._mainWindow.reset()
+
+ # ------------------------------------------------------------------
+ # System Helpers
+ # ------------------------------------------------------------------
+
+ def _run_system_command(self, args: list[str], wait: bool = False) -> int | None:
+
+ # Only attempt on Linux; ignore silently on other platforms
+ try:
+ os_name = self._helper.get_os()
+ except Exception:
+ os_name = None
+
+ if os_name != "linux":
+ if self._logger:
+ self._logger.append(
+ f"[Application] Ignoring system command {args!r} on non-Linux OS: {os_name}",
+ channel="system",
+ level="warning",
+ )
+ return None
+
+ try:
+ if wait:
+ proc = subprocess.Popen(args)
+ rc = proc.wait()
+ if self._logger:
+ self._logger.append(
+ f"[Application] (wait) Command finished ({rc}): {' '.join(args)}",
+ channel="system",
+ level="info",
+ )
+ return rc
+ else:
+ subprocess.Popen(args)
+ if self._logger:
+ self._logger.append(
+ f"[Application] Executed system command: {' '.join(args)}",
+ channel="system",
+ level="info",
+ )
+ return None
+ except Exception as e:
+ if self._logger:
+ self._logger.append(
+ f"[Application] Failed to execute system command {args!r}: {e}",
+ channel="system",
+ level="error",
+ )
+ return None
+
+ def shutdown(self) -> None:
+ self._run_system_command(["systemctl", "poweroff"])
+
+ def restart(self) -> None:
+ self._run_system_command(["systemctl", "reboot"])
+
+ # ------------------------------------------------------------------
+ # Application Helpers
+ # ------------------------------------------------------------------
+
+ def update(self):
+ # Determine repo root
+ try:
+ here = os.path.abspath(os.path.dirname(__file__))
+ repo_root = os.path.abspath(os.path.join(here, "..", ".."))
+ except Exception as e:
+ if self._logger:
+ self._logger.append(
+ f"[Application] Failed to determine repository root for update: {e}",
+ channel="system",
+ level="error",
+ )
+ MsgBox.show(
+ parent=self._mainWindow,
+ title="Update Failed",
+ message="Failed to determine the repository root for the update.",
+ icon="error",
+ buttons=("OK"),
+ default="OK",
+ icon_lookup_fn=self._helper.get_path,
+ )
+ return
+
+ # Only attempt on Linux
+ try:
+ os_name = self._helper.get_os()
+ except Exception:
+ os_name = None
+
+ if os_name != "linux":
+ if self._logger:
+ self._logger.append(
+ "[Application] Update ignored on non-Linux OS.",
+ channel="system",
+ level="warning",
+ )
+ MsgBox.show(
+ parent=self._mainWindow,
+ title="Update Not Available",
+ message="Updating is only supported on Linux systems.",
+ icon="info",
+ buttons=("OK"),
+ default="OK",
+ icon_lookup_fn=self._helper.get_path,
+ )
+ return
+
+ # --- Build post-update task list via listener -------------------------
+ tasks: list[tuple[list[str], str | None]] = []
+
+ def add_task(args: list[str], label: str | None = None) -> None:
+ tasks.append((args, label))
+
+ # Let external code (main.py) register tasks.
+ # Those tasks *will* run after git pull in ApplicationThread.
+ self.updating.emit(add_task)
+
+ # --- Create dialog + worker ------------------------------------------
+ dlg_parent = self._mainWindow if self._mainWindow is not None else None
+ progress = ApplicationDialog(parent=dlg_parent)
+ progress.setLabelText(f"Updating {self.name}...")
+
+ worker = ApplicationThread(repo_root, tasks=tasks, logger=self._logger, parent=self)
+
+ # Update label whenever the worker reports a progress text
+ worker.progress_text.connect(progress.setLabelText)
+
+ def on_worker_finished(rc: int, canceled: bool) -> None:
+ progress.close()
+
+ if canceled:
+ MsgBox.show(
+ parent=self._mainWindow,
+ title="Update Canceled",
+ message="The update was canceled. The application may not be fully up to date.",
+ icon="warning",
+ buttons=("OK"),
+ default="OK",
+ icon_lookup_fn=self._helper.get_path,
+ )
+ return
+
+ if rc not in (0, None):
+ MsgBox.show(
+ parent=self._mainWindow,
+ title="Update Failed",
+ message=f"Update failed with exit code {rc}. Check the logs for details.",
+ icon="error",
+ buttons=("OK"),
+ default="OK",
+ icon_lookup_fn=self._helper.get_path,
+ )
+ return
+
+ # Everything (git + tasks) succeeded
+ buttons: Iterable[str] = ("Exit", "OK")
+ choice = MsgBox.show(
+ parent=self._mainWindow,
+ title="Update Successful",
+ message="The application has been updated. Please restart the application to apply the latest changes.",
+ icon="info",
+ buttons=buttons,
+ default="OK",
+ icon_lookup_fn=self._helper.get_path,
+ )
+ if choice == "Exit":
+ self.quit()
+
+ def on_user_cancel() -> None:
+ worker.requestInterruption()
+
+ progress.canceled_by_user.connect(on_user_cancel)
+ worker.finished_with_result.connect(on_worker_finished)
+
+ progress.show()
+ worker.start()
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/build.sh b/dist/macos/Replicator.app/Contents/Resources/core/build.sh
new file mode 100755
index 00000000..932220a7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/build.sh
@@ -0,0 +1,1402 @@
+#!/bin/bash
+set -euo pipefail
+
+# -----------------------------------------------------------------------------
+# build.sh (generic PyInstaller build script)
+# - Designed to be reused across LaswitchTech projects
+# - Defaults work out-of-the-box for this repo (Replicator)
+# -----------------------------------------------------------------------------
+
+log() {
+ echo "$(date +'%Y-%m-%d %H:%M:%S') - $*"
+}
+
+die() {
+ echo "ERROR: $*" >&2
+ exit 1
+}
+
+detect_os() {
+ case "$(uname -s)" in
+ Darwin) echo "macos" ;;
+ Linux) echo "linux" ;;
+ WindowsNT|MINGW64*|MINGW32*|MSYS*|CYGWIN*) echo "windows" ;;
+ *) echo "unsupported" ;;
+ esac
+}
+
+
+# Portable in-place sed (macOS vs GNU)
+sed_inplace() {
+ # usage: sed_inplace 's/a/b/' file
+ if sed --version >/dev/null 2>&1; then
+ sed -i "$1" "$2"
+ else
+ sed -i '' "$1" "$2"
+ fi
+}
+
+# -----------------------------------------------------------------------------
+# corePY vendored bin sync
+# Some projects vendor corePY under src/core/src and keep binaries there.
+# To make PyInstaller data inclusion consistent, mirror vendored binaries into
+# src/bin (overwriting existing content).
+# -----------------------------------------------------------------------------
+
+sync_vendored_bins() {
+ local src_dir="src/core/src/bin"
+ local dst_dir="src/bin"
+
+ [ -d "$src_dir" ] || return 0
+
+ log "Syncing vendored binaries: $src_dir -> $dst_dir"
+ mkdir -p "$dst_dir"
+
+ # Use cp -R to stay portable across macOS/Linux/Windows Git Bash.
+ # Trailing '/.' copies contents (including hidden files) into destination.
+ cp -R "$src_dir/." "$dst_dir/" 2>/dev/null || cp -R "$src_dir"/* "$dst_dir/" 2>/dev/null || true
+}
+
+#
+# Icon generation
+# - macOS: icon.svg -> icon.icns (every build)
+# - Windows: icon.svg -> icon.ico (best-effort, every build)
+# Requires: iconutil (macOS) + either rsvg-convert (librsvg) OR inkscape.
+# Windows ICO generation prefers: ImageMagick (magick/convert) OR Python + Pillow.
+# -----------------------------------------------------------------------------
+
+generate_icns_from_svg_macos() {
+ [ "$OS" = "macos" ] || return 0
+
+ local svg="src/icons/icon.svg"
+ local out="src/icons/icon.icns"
+
+ [ -f "$svg" ] || return 0
+
+ if ! command -v iconutil >/dev/null 2>&1; then
+ log "WARN: iconutil not found; cannot generate .icns from $svg"
+ return 0
+ fi
+
+ local renderer=""
+ if command -v rsvg-convert >/dev/null 2>&1; then
+ renderer="rsvg"
+ elif command -v inkscape >/dev/null 2>&1; then
+ renderer="inkscape"
+ else
+ log "WARN: Neither rsvg-convert nor inkscape found; cannot generate .icns from $svg"
+ log " Install one of them (recommended: brew install librsvg)"
+ return 0
+ fi
+
+ mkdir -p "build"
+
+ local iconset="build/icon.iconset"
+ rm -rf "$iconset"
+ mkdir -p "$iconset"
+
+ # Required sizes for iconutil
+ local sizes=(16 32 128 256 512)
+
+ log "Regenerating macOS icon: $out (from $svg using $renderer)"
+
+ for s in "${sizes[@]}"; do
+ local s2=$((s * 2))
+
+ if [ "$renderer" = "rsvg" ]; then
+ rsvg-convert -w "$s" -h "$s" "$svg" -o "$iconset/icon_${s}x${s}.png"
+ rsvg-convert -w "$s2" -h "$s2" "$svg" -o "$iconset/icon_${s}x${s}@2x.png"
+ else
+ # inkscape CLI (v1+)
+ inkscape "$svg" --export-type=png --export-width="$s" --export-height="$s" --export-filename="$iconset/icon_${s}x${s}.png" >/dev/null 2>&1
+ inkscape "$svg" --export-type=png --export-width="$s2" --export-height="$s2" --export-filename="$iconset/icon_${s}x${s}@2x.png" >/dev/null 2>&1
+ fi
+ done
+
+ # Build .icns
+ iconutil -c icns "$iconset" -o "$out"
+
+ # Cleanup iconset directory (keep build folder)
+ rm -rf "$iconset"
+}
+
+# -----------------------------------------------------------------------------
+# Windows icon generation: icon.svg -> icon.ico
+# Best-effort: uses inkscape/rsvg-convert to render PNGs then combines to ICO.
+# Prefers ImageMagick; falls back to Python+Pillow if available.
+# -----------------------------------------------------------------------------
+
+generate_ico_from_svg_windows() {
+ [ "$OS" = "windows" ] || return 0
+
+ local svg="src/icons/icon.svg"
+ local out="src/icons/icon.ico"
+
+ [ -f "$svg" ] || return 0
+
+ local renderer=""
+ if command -v rsvg-convert >/dev/null 2>&1; then
+ renderer="rsvg"
+ elif command -v inkscape >/dev/null 2>&1; then
+ renderer="inkscape"
+ else
+ log "WARN: Neither rsvg-convert nor inkscape found; cannot generate .ico from $svg"
+ log " Install one of them (recommended: Inkscape)"
+ return 0
+ fi
+
+ mkdir -p "build"
+
+ local tmpdir="build/icon.ico.tmp"
+ rm -rf "$tmpdir"
+ mkdir -p "$tmpdir"
+
+ # Common ICO sizes
+ local sizes=(16 24 32 48 64 128 256)
+
+ log "Regenerating Windows icon: $out (from $svg using $renderer)"
+
+ for s in "${sizes[@]}"; do
+ if [ "$renderer" = "rsvg" ]; then
+ rsvg-convert -w "$s" -h "$s" "$svg" -o "$tmpdir/${s}.png"
+ else
+ # inkscape CLI (v1+)
+ inkscape "$svg" --export-type=png --export-width="$s" --export-height="$s" --export-filename="$tmpdir/${s}.png" >/dev/null 2>&1
+ fi
+ done
+
+ # Combine PNGs into ICO
+ if command -v magick >/dev/null 2>&1; then
+ # ImageMagick 7+
+ magick "$tmpdir/16.png" "$tmpdir/24.png" "$tmpdir/32.png" "$tmpdir/48.png" "$tmpdir/64.png" "$tmpdir/128.png" "$tmpdir/256.png" "$out" 2>/dev/null || true
+ elif command -v convert >/dev/null 2>&1; then
+ # ImageMagick 6 (convert)
+ convert "$tmpdir/16.png" "$tmpdir/24.png" "$tmpdir/32.png" "$tmpdir/48.png" "$tmpdir/64.png" "$tmpdir/128.png" "$tmpdir/256.png" "$out" 2>/dev/null || true
+ else
+ # Fallback: Python + Pillow (if available)
+ python - <<'PY' "$tmpdir" "$out" 2>/dev/null || true
+import sys
+from pathlib import Path
+
+tmpdir = Path(sys.argv[1])
+out = Path(sys.argv[2])
+
+try:
+ from PIL import Image
+except Exception:
+ raise SystemExit(1)
+
+sizes = [16, 24, 32, 48, 64, 128, 256]
+imgs = []
+for s in sizes:
+ p = tmpdir / f"{s}.png"
+ if p.exists():
+ imgs.append(Image.open(p))
+
+if not imgs:
+ raise SystemExit(1)
+
+# Pillow writes multi-size ICO when you pass sizes
+base = imgs[-1]
+base.save(out, format="ICO", sizes=[(s, s) for s in sizes])
+PY
+ fi
+
+ if [ -f "$out" ]; then
+ log "Windows icon generated: $out"
+ else
+ log "WARN: Failed to generate $out (install ImageMagick or Python Pillow for best results)"
+ fi
+
+ rm -rf "$tmpdir"
+}
+
+# Guess app name from current folder if not provided
+infer_name() {
+ local base
+ base="$(basename "$(pwd)")"
+ # keep it simple: only allow alnum, dash, underscore
+ echo "$base" | tr -cd '[:alnum:]_-'
+}
+
+show_help() {
+ cat <<'EOF'
+Usage:
+ ./build.sh [options]
+
+Options:
+ --name NAME App name (default: folder name)
+ --entry PATH Entry script/module for PyInstaller (default: src/main.py)
+ --config PATH Path to build.cfg (default: ./build.cfg if present)
+ --windowed Build GUI app (no console) (macOS: --windowed; Linux: still onefile)
+ --console Build console app (default)
+ --icon PATH Icon path (macOS: .icns recommended)
+ --add-data SRC:DST Add data folder/file (repeatable). Uses PyInstaller --add-data=SRC:DST.
+ --hidden-import MOD Add hidden import (repeatable)
+ --onefile Force onefile (default on Linux)
+ --onedir Force onedir (default on macOS)
+ --python PY Python 3.11 binary (default: python3.11 from PATH)
+ --system-pyqt On Linux ARM, prefer APT PyQt5 with --system-site-packages
+ --clean Remove build/dist artifacts before building
+ --debug-pyi Enable PyInstaller debug output (--log-level=DEBUG --debug=all)
+ --dmg On macOS, create a DMG (only meaningful for onedir .app)
+ --gen-install Generate install/run wrapper scripts into repo root (default)
+ --no-gen-install Do not generate install/run wrapper scripts
+ --venv-dir DIR Virtualenv directory for wrapper scripts (default: .venv)
+ --requirements PATH Requirements file used by wrapper scripts if present (default: requirements.txt)
+ -h, --help Show this help
+
+Examples:
+ ./build.sh --name Replicator --entry src/main.py --windowed --add-data src/app:app
+ ./build.sh --console --hidden-import PyQt5.QtSvg
+EOF
+}
+
+# -----------------------------------------------------------------------------
+# Defaults (good for Replicator)
+# -----------------------------------------------------------------------------
+OS="$(detect_os)"
+[ "$OS" = "unsupported" ] && die "Unsupported operating system."
+
+APP_NAME="$(infer_name)"
+ENTRY="src/main.py"
+MODE="console" # console|windowed
+
+# Packaging defaults: macOS prefers onedir (for .app), Linux prefers onefile
+PKG="" # empty = auto, or onefile/onedir
+
+#
+# Python discovery
+# - macOS/Linux: prefer python3.11
+# - Windows (Git Bash): prefer the Python Launcher when available, but only if the requested runtime exists.
+PYTHON_BIN=""
+PYTHON_LAUNCH_ARGS=""
+
+python_cmd_ok() {
+ # usage: python_cmd_ok [args...]
+ # returns 0 if command runs and Python version is >= 3.11
+ "$@" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' >/tmp/.replicator_pyver 2>/dev/null || return 1
+ local ver
+ ver="$(cat /tmp/.replicator_pyver 2>/dev/null || true)"
+ rm -f /tmp/.replicator_pyver 2>/dev/null || true
+ case "$ver" in
+ 3.11|3.12|3.13|3.14|3.15|3.16|3.17|3.18|3.19|4.*) return 0 ;;
+ *) return 1 ;;
+ esac
+}
+
+# Candidate selection (ordered)
+if command -v python3.11 >/dev/null 2>&1 && python_cmd_ok python3.11; then
+ PYTHON_BIN="python3.11"
+ PYTHON_LAUNCH_ARGS=""
+elif [ "$OS" = "windows" ] && command -v py >/dev/null 2>&1; then
+ # Try explicit 3.11 first, then any 3.x that satisfies >=3.11
+ if python_cmd_ok py -3.11; then
+ PYTHON_BIN="py"
+ PYTHON_LAUNCH_ARGS="-3.11"
+ elif python_cmd_ok py -3.12; then
+ PYTHON_BIN="py"
+ PYTHON_LAUNCH_ARGS="-3.12"
+ elif python_cmd_ok py -3.13; then
+ PYTHON_BIN="py"
+ PYTHON_LAUNCH_ARGS="-3.13"
+ elif python_cmd_ok py -3; then
+ PYTHON_BIN="py"
+ PYTHON_LAUNCH_ARGS="-3"
+ fi
+fi
+
+# Fallbacks (Git Bash often exposes only `python`)
+if [ -z "$PYTHON_BIN" ]; then
+ if command -v python3 >/dev/null 2>&1 && python_cmd_ok python3; then
+ PYTHON_BIN="python3"
+ PYTHON_LAUNCH_ARGS=""
+ elif command -v python >/dev/null 2>&1 && python_cmd_ok python; then
+ PYTHON_BIN="python"
+ PYTHON_LAUNCH_ARGS=""
+ fi
+fi
+
+# If user supplied --python, we still validate it later in the script.
+
+python_exec() {
+ # Wrapper so we can call: python_exec -m venv ...
+ [ -n "${PYTHON_BIN:-}" ] || die "Python not found. Install Python 3.11+ and ensure it is in PATH (python) or via the Windows Python Launcher (py)."
+
+ if [ -n "${PYTHON_LAUNCH_ARGS:-}" ]; then
+ # shellcheck disable=SC2086
+ "$PYTHON_BIN" ${PYTHON_LAUNCH_ARGS} "$@"
+ else
+ "$PYTHON_BIN" "$@"
+ fi
+}
+
+venv_python_path() {
+ if [ "$OS" = "windows" ]; then
+ echo "$VENV_DIR/Scripts/python.exe"
+ else
+ echo "$VENV_DIR/bin/python"
+ fi
+}
+
+venv_activate_path() {
+ if [ "$OS" = "windows" ]; then
+ echo "$VENV_DIR/Scripts/activate"
+ else
+ echo "$VENV_DIR/bin/activate"
+ fi
+}
+
+USE_SYSTEM_PYQT=0
+
+# macOS DMG toggle
+MAKE_DMG=0
+
+# PyInstaller debug toggle
+PYI_DEBUG=0
+
+# Icon (optional)
+ICON_FILE=""
+
+# Repeatable arrays
+declare -a ADD_DATA
+ADD_DATA=()
+
+declare -a HIDDEN_IMPORTS
+HIDDEN_IMPORTS=()
+
+# Common default data folders (only add if they exist)
+# Replicator usually has icons/app/styles/img in some projects; keep generic.
+DEFAULT_DATA_CANDIDATES=(
+ "src/app:app"
+ "src/styles:styles"
+ "src/icons:icons"
+ "src/img:img"
+ "assets:assets"
+)
+
+# Optional config file
+CFG_FILE=""
+
+# Wrapper script defaults
+GENERATE_INSTALL=1
+VENV_DIR=".venv"
+REQ_FILE="requirements.txt"
+
+# Load simple KEY=VALUE config (no code execution)
+# Supported keys:
+# NAME, ENTRY, MODE, PKG, ICON, PYTHON, SYSTEM_PYQT, DMG, CLEAN
+# ADD_DATA, HIDDEN_IMPORTS
+# Notes:
+# - Lines beginning with # or ; are ignored
+# - Whitespace around keys/values is trimmed
+# - ADD_DATA / HIDDEN_IMPORTS can be comma or semicolon separated
+trim_ws() {
+ # trim leading/trailing whitespace
+ local s="$1"
+ s="${s#${s%%[![:space:]]*}}"
+ s="${s%${s##*[![:space:]]}}"
+ echo "$s"
+}
+
+split_list() {
+ # split comma/semicolon separated list into lines
+ echo "$1" | tr ';' '\n' | tr ',' '\n'
+}
+
+apply_cfg_kv() {
+ local key="$1" val="$2"
+ key="$(trim_ws "$key")"
+ val="$(trim_ws "$val")"
+ [ -z "$key" ] && return 0
+
+ case "$key" in
+ NAME)
+ APP_NAME="$val"
+ ;;
+ ENTRY)
+ ENTRY="$val"
+ ;;
+ MODE)
+ # console|windowed
+ if [ "$val" = "console" ] || [ "$val" = "windowed" ]; then
+ MODE="$val"
+ fi
+ ;;
+ PKG)
+ # onefile|onedir|auto
+ if [ "$val" = "onefile" ] || [ "$val" = "onedir" ]; then
+ PKG="$val"
+ elif [ "$val" = "auto" ]; then
+ PKG=""
+ fi
+ ;;
+ ICON)
+ ICON_FILE="$val"
+ ;;
+ PYTHON)
+ PYTHON_BIN="$val"
+ ;;
+ SYSTEM_PYQT)
+ if [ "$val" = "1" ] || [ "$val" = "true" ] || [ "$val" = "yes" ]; then
+ USE_SYSTEM_PYQT=1
+ elif [ "$val" = "0" ] || [ "$val" = "false" ] || [ "$val" = "no" ]; then
+ USE_SYSTEM_PYQT=0
+ fi
+ ;;
+ DMG)
+ if [ "$val" = "1" ] || [ "$val" = "true" ] || [ "$val" = "yes" ]; then
+ MAKE_DMG=1
+ elif [ "$val" = "0" ] || [ "$val" = "false" ] || [ "$val" = "no" ]; then
+ MAKE_DMG=0
+ fi
+ ;;
+ CLEAN)
+ if [ "$val" = "1" ] || [ "$val" = "true" ] || [ "$val" = "yes" ]; then
+ CLEAN=1
+ elif [ "$val" = "0" ] || [ "$val" = "false" ] || [ "$val" = "no" ]; then
+ CLEAN=0
+ fi
+ ;;
+ ADD_DATA)
+ # replaces current list
+ ADD_DATA=()
+ while IFS= read -r item; do
+ item="$(trim_ws "$item")"
+ [ -n "$item" ] && ADD_DATA+=("$item")
+ done < <(split_list "$val")
+ ;;
+ HIDDEN_IMPORTS)
+ # replaces current list
+ HIDDEN_IMPORTS=()
+ while IFS= read -r item; do
+ item="$(trim_ws "$item")"
+ [ -n "$item" ] && HIDDEN_IMPORTS+=("$item")
+ done < <(split_list "$val")
+ ;;
+ GEN_INSTALL)
+ if [ "$val" = "1" ] || [ "$val" = "true" ] || [ "$val" = "yes" ]; then
+ GENERATE_INSTALL=1
+ elif [ "$val" = "0" ] || [ "$val" = "false" ] || [ "$val" = "no" ]; then
+ GENERATE_INSTALL=0
+ fi
+ ;;
+ VENV_DIR)
+ VENV_DIR="$val"
+ ;;
+ REQUIREMENTS)
+ REQ_FILE="$val"
+ ;;
+ *)
+ # unknown keys ignored for forward-compat
+ ;;
+ esac
+}
+
+load_cfg_file() {
+ local f="$1"
+ [ -f "$f" ] || return 0
+ log "Loading config: $f"
+
+ while IFS= read -r line || [ -n "$line" ]; do
+ # strip CR (Windows line endings)
+ line="${line%$'\r'}"
+
+ # ignore comments/blank lines
+ case "$(trim_ws "$line")" in
+ ""|\#*|\;*) continue ;;
+ esac
+
+ # allow inline comments after a value using #
+ # (only if there is at least one space before #)
+ if echo "$line" | grep -q "[[:space:]]#"; then
+ line="$(echo "$line" | sed 's/[[:space:]]#.*$//')"
+ fi
+
+ if echo "$line" | grep -q "="; then
+ local k v
+ k="${line%%=*}"
+ v="${line#*=}"
+ apply_cfg_kv "$k" "$v"
+ fi
+ done < "$f"
+}
+
+# -----------------------------------------------------------------------------
+# Parse args
+# -----------------------------------------------------------------------------
+while [ $# -gt 0 ]; do
+ case "$1" in
+ --name)
+ shift
+ [ $# -gt 0 ] || die "--name requires a value"
+ APP_NAME="$1"
+ ;;
+ --entry)
+ shift
+ [ $# -gt 0 ] || die "--entry requires a value"
+ ENTRY="$1"
+ ;;
+ --config)
+ shift
+ [ $# -gt 0 ] || die "--config requires a value"
+ CFG_FILE="$1"
+ ;;
+ --windowed)
+ MODE="windowed"
+ ;;
+ --console)
+ MODE="console"
+ ;;
+ --icon)
+ shift
+ [ $# -gt 0 ] || die "--icon requires a value"
+ ICON_FILE="$1"
+ ;;
+ --add-data)
+ shift
+ [ $# -gt 0 ] || die "--add-data requires a value like SRC:DST"
+ ADD_DATA+=("$1")
+ ;;
+ --hidden-import)
+ shift
+ [ $# -gt 0 ] || die "--hidden-import requires a module name"
+ HIDDEN_IMPORTS+=("$1")
+ ;;
+ --onefile)
+ PKG="onefile"
+ ;;
+ --onedir)
+ PKG="onedir"
+ ;;
+ --python)
+ shift
+ [ $# -gt 0 ] || die "--python requires a path/binary"
+ PYTHON_BIN="$1"
+ ;;
+ --system-pyqt)
+ USE_SYSTEM_PYQT=1
+ ;;
+ --clean)
+ CLEAN=1
+ ;;
+ --debug-pyi)
+ PYI_DEBUG=1
+ ;;
+ --dmg)
+ MAKE_DMG=1
+ ;;
+ --gen-install)
+ GENERATE_INSTALL=1
+ ;;
+ --no-gen-install)
+ GENERATE_INSTALL=0
+ ;;
+ --venv-dir)
+ shift
+ [ $# -gt 0 ] || die "--venv-dir requires a value"
+ VENV_DIR="$1"
+ ;;
+ --requirements)
+ shift
+ [ $# -gt 0 ] || die "--requirements requires a value"
+ REQ_FILE="$1"
+ ;;
+ -h|--help)
+ show_help
+ exit 0
+ ;;
+ *)
+ die "Unknown option: $1 (use --help)"
+ ;;
+ esac
+ shift
+done
+
+# -----------------------------------------------------------------------------
+# Load config (if provided, or if ./build.cfg exists)
+# CLI flags should override config values.
+# -----------------------------------------------------------------------------
+
+# Capture what the user explicitly set via CLI so we can re-apply after config
+CLI_APP_NAME="$APP_NAME"
+CLI_ENTRY="$ENTRY"
+CLI_MODE="$MODE"
+CLI_PKG="$PKG"
+CLI_ICON="$ICON_FILE"
+CLI_PYTHON_BIN="$PYTHON_BIN"
+CLI_USE_SYSTEM_PYQT="$USE_SYSTEM_PYQT"
+CLI_MAKE_DMG="$MAKE_DMG"
+CLI_CLEAN="${CLEAN:-0}"
+CLI_ADD_DATA=("${ADD_DATA[@]+${ADD_DATA[@]}}")
+CLI_HIDDEN_IMPORTS=("${HIDDEN_IMPORTS[@]+${HIDDEN_IMPORTS[@]}}")
+CLI_GENERATE_INSTALL="$GENERATE_INSTALL"
+CLI_VENV_DIR="$VENV_DIR"
+CLI_REQ_FILE="$REQ_FILE"
+
+# Determine config path
+if [ -z "$CFG_FILE" ] && [ -f "build.cfg" ]; then
+ CFG_FILE="build.cfg"
+fi
+
+# Apply config
+if [ -n "$CFG_FILE" ]; then
+ load_cfg_file "$CFG_FILE"
+fi
+
+# Re-apply CLI values only when the user actually supplied flags.
+# Heuristic: if arrays were empty pre-config but are non-empty post-config,
+# we only override them when CLI provided values (captured arrays non-empty).
+# Scalars: if CLI value differs from the default-initialized value AND the flag was used,
+# we treat it as explicitly set. For simplicity, we treat any CLI value as authoritative
+# when it differs from what config loaded.
+
+# Scalars
+[ "$CLI_APP_NAME" != "$(infer_name)" ] && APP_NAME="$CLI_APP_NAME" || true
+[ "$CLI_ENTRY" != "src/main.py" ] && ENTRY="$CLI_ENTRY" || true
+[ "$CLI_MODE" != "console" ] && MODE="$CLI_MODE" || true
+[ -n "$CLI_PKG" ] && PKG="$CLI_PKG" || true
+[ -n "$CLI_ICON" ] && ICON_FILE="$CLI_ICON" || true
+[ "$CLI_PYTHON_BIN" != "$(command -v python3.11 || true)" ] && PYTHON_BIN="$CLI_PYTHON_BIN" || true
+[ "$CLI_USE_SYSTEM_PYQT" != "0" ] && USE_SYSTEM_PYQT="$CLI_USE_SYSTEM_PYQT" || true
+[ "$CLI_MAKE_DMG" != "0" ] && MAKE_DMG="$CLI_MAKE_DMG" || true
+if [ "$CLI_CLEAN" = "1" ]; then CLEAN=1; fi
+# New CLI re-apply for wrapper script settings
+if [ "$CLI_GENERATE_INSTALL" != "1" ]; then
+ GENERATE_INSTALL="$CLI_GENERATE_INSTALL"
+fi
+if [ "$CLI_VENV_DIR" != ".venv" ]; then
+ VENV_DIR="$CLI_VENV_DIR"
+fi
+if [ "$CLI_REQ_FILE" != "requirements.txt" ]; then
+ REQ_FILE="$CLI_REQ_FILE"
+fi
+
+# Arrays
+if [ "${#CLI_ADD_DATA[@]}" -gt 0 ]; then
+ nonempty=0
+ for v in "${CLI_ADD_DATA[@]}"; do
+ [ -n "$v" ] && nonempty=1
+ done
+ if [ "$nonempty" -eq 1 ]; then
+ ADD_DATA=("${CLI_ADD_DATA[@]}")
+ fi
+fi
+
+if [ "${#CLI_HIDDEN_IMPORTS[@]}" -gt 0 ]; then
+ nonempty=0
+ for v in "${CLI_HIDDEN_IMPORTS[@]}"; do
+ [ -n "$v" ] && nonempty=1
+ done
+ if [ "$nonempty" -eq 1 ]; then
+ HIDDEN_IMPORTS=("${CLI_HIDDEN_IMPORTS[@]}")
+ fi
+fi
+
+# -----------------------------------------------------------------------------
+# Regenerate platform icons from icon.svg on every build (if present)
+# -----------------------------------------------------------------------------
+sync_vendored_bins
+generate_icns_from_svg_macos
+generate_ico_from_svg_windows
+
+# -----------------------------------------------------------------------------
+# Generate developer-friendly install/run wrapper scripts (if enabled)
+# -----------------------------------------------------------------------------
+generate_wrapper_scripts() {
+ [ "${GENERATE_INSTALL:-1}" -eq 1 ] || return 0
+
+ local out_dir="$1" # e.g. . (repo root)
+ mkdir -p "$out_dir"
+
+ # ---------------------------------------------------------------------------
+ # POSIX shell wrapper (macOS/Linux + Windows Git Bash)
+ # ---------------------------------------------------------------------------
+ cat >"$out_dir/launch.sh" <<'SH'
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Replicator dev launcher (macOS/Linux + Windows Git Bash)
+# - Creates a virtualenv if missing
+# - Installs runtime deps (prefers requirements.txt if present)
+# - Runs src/main.py
+
+ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+cd "$ROOT_DIR"
+
+VENV_DIR="__VENV_DIR__"
+REQ_FILE="__REQ_FILE__"
+
+# Detect Windows Git Bash (MSYS/MINGW/CYGWIN)
+UNAME_S="$(uname -s 2>/dev/null || echo '')"
+IS_WINDOWS=0
+case "$UNAME_S" in
+ MINGW*|MSYS*|CYGWIN*) IS_WINDOWS=1 ;;
+ *) IS_WINDOWS=0 ;;
+esac
+
+# Choose python command
+PY_CMD=""
+PY_ARGS=()
+
+# Prefer explicit python3.11 on macOS/Linux
+if command -v python3.11 >/dev/null 2>&1; then
+ PY_CMD="python3.11"
+elif [ "$IS_WINDOWS" -eq 1 ] && command -v py >/dev/null 2>&1; then
+ # Prefer Windows Python Launcher (does NOT require python.exe in PATH)
+ PY_CMD="py"
+ PY_ARGS=(-3.11)
+elif command -v python3 >/dev/null 2>&1; then
+ PY_CMD="python3"
+elif command -v python >/dev/null 2>&1; then
+ PY_CMD="python"
+else
+ echo "ERROR: Python not found in PATH. On Windows, install Python 3.11+ and/or ensure the Python Launcher (py) is available." >&2
+ exit 1
+fi
+
+# Resolve venv python/activate paths
+if [ "$IS_WINDOWS" -eq 1 ]; then
+ VENV_PY="$VENV_DIR/Scripts/python.exe"
+ ACTIVATE_SH="$VENV_DIR/Scripts/activate"
+else
+ VENV_PY="$VENV_DIR/bin/python"
+ ACTIVATE_SH="$VENV_DIR/bin/activate"
+fi
+
+# Create venv if needed
+if [ ! -x "$VENV_PY" ]; then
+ echo "Creating virtualenv: $VENV_DIR"
+ # shellcheck disable=SC2086
+ "$PY_CMD" "${PY_ARGS[@]}" -m venv "$VENV_DIR"
+fi
+
+# Activate venv
+# shellcheck disable=SC1090
+source "$ACTIVATE_SH"
+
+python -m pip install --upgrade pip wheel
+
+if [ -f "$REQ_FILE" ]; then
+ echo "Installing requirements from $REQ_FILE"
+ python -m pip install -r "$REQ_FILE"
+else
+ echo "Installing minimal runtime deps (PyQt5)"
+ python -m pip install "PyQt5>=5.15,<6"
+fi
+
+exec python "src/main.py" "$@"
+SH
+
+ # Patch placeholders
+ sed_inplace "s|__VENV_DIR__|$VENV_DIR|g" "$out_dir/launch.sh"
+ sed_inplace "s|__REQ_FILE__|$REQ_FILE|g" "$out_dir/launch.sh"
+ chmod +x "$out_dir/launch.sh" || true
+
+ # CLI convenience (same as launch.sh but kept as a stable name for docs/scripts)
+ cat >"$out_dir/cli.sh" <<'SH'
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+cd "$ROOT_DIR"
+
+exec "./launch.sh" "$@"
+SH
+ chmod +x "$out_dir/cli.sh" || true
+
+ # ---------------------------------------------------------------------------
+ # Windows: VBS launcher (launch.vbs) and CLI .bat launcher (cli.bat)
+ # ---------------------------------------------------------------------------
+ cat >"$out_dir/launch.vbs" <<'VBS'
+' Replicator Windows launcher (no console)
+' - Prefers launching the built binary if present
+' - Falls back to launching Git Bash + launch.sh if available
+
+Option Explicit
+
+Dim shell, fso, scriptDir, exePath, bashPath, cmd
+Set shell = CreateObject("WScript.Shell")
+Set fso = CreateObject("Scripting.FileSystemObject")
+
+scriptDir = fso.GetParentFolderName(WScript.ScriptFullName)
+
+' Try built EXE first
+exePath = scriptDir & "\\dist\\windows\\Replicator.exe"
+If fso.FileExists(exePath) Then
+ shell.CurrentDirectory = scriptDir
+ shell.Run Chr(34) & exePath & Chr(34), 1, False
+ WScript.Quit 0
+End If
+
+' Fallback: Git Bash launch.sh (if user is in a dev checkout)
+bashPath = shell.ExpandEnvironmentStrings("%ProgramFiles%") & "\\Git\\bin\\bash.exe"
+If fso.FileExists(bashPath) Then
+ cmd = Chr(34) & bashPath & Chr(34) & " -lc " & Chr(34) & "cd \"" & scriptDir & "\" && ./launch.sh" & Chr(34)
+ shell.Run cmd, 0, False
+End If
+VBS
+
+ cat >"$out_dir/cli.bat" <<'BAT'
+@echo off
+setlocal EnableExtensions EnableDelayedExpansion
+
+REM ---------------------------------------------------------------------------
+REM Replicator CLI launcher (Windows)
+REM - Ensures a local venv exists (.venv)
+REM - Installs minimal runtime deps (PyQt5)
+REM - Runs src\main.py with a visible console
+REM
+REM Note: If you double-click this file, the console may flash and close.
+REM Run it from an existing Command Prompt for persistent output.
+REM ---------------------------------------------------------------------------
+
+set "SCRIPT_DIR=%~dp0"
+set "VENV_DIR=%SCRIPT_DIR%.venv"
+set "VENV_PY=%VENV_DIR%\Scripts\python.exe"
+
+REM Prefer Windows Python Launcher
+set "PY_CMD="
+set "PY_ARGS="
+where py >nul 2>&1
+if %ERRORLEVEL%==0 (
+ set "PY_CMD=py"
+ set "PY_ARGS=-3.11"
+) else (
+ where python >nul 2>&1
+ if %ERRORLEVEL%==0 (
+ set "PY_CMD=python"
+ set "PY_ARGS="
+ )
+)
+
+if "%PY_CMD%"=="" (
+ echo ERROR: Python not found. Install Python 3.11+ and ensure either `py` or `python` is available in PATH.
+ exit /b 1
+)
+
+REM Create venv if missing
+if not exist "%VENV_PY%" (
+ echo Creating virtualenv: %VENV_DIR%
+ %PY_CMD% %PY_ARGS% -m venv "%VENV_DIR%"
+ if %ERRORLEVEL% NEQ 0 (
+ echo ERROR: Failed to create virtualenv.
+ exit /b %ERRORLEVEL%
+ )
+)
+
+REM Upgrade pip/wheel
+"%VENV_PY%" -m pip install --upgrade pip wheel >nul
+
+REM Install minimal deps (idempotent)
+echo Installing minimal runtime deps (PyQt5)...
+"%VENV_PY%" -m pip install "PyQt5>=5.15,<6" >nul
+
+REM Prefer the built console companion if available
+if exist "%SCRIPT_DIR%dist\windows\Replicator-cli.exe" (
+ "%SCRIPT_DIR%dist\windows\Replicator-cli.exe" %*
+ endlocal
+ exit /b %ERRORLEVEL%
+)
+
+REM Fallback: Run the source entry in console mode (dev checkout)
+if not exist "%SCRIPT_DIR%src\main.py" (
+ echo ERROR: Entry not found: %SCRIPT_DIR%src\main.py
+ echo NOTE: Replicator-cli.exe not found at %SCRIPT_DIR%dist\windows\Replicator-cli.exe
+ exit /b 1
+)
+
+"%VENV_PY%" "%SCRIPT_DIR%src\main.py" %*
+endlocal
+exit /b %ERRORLEVEL%
+BAT
+}
+
+# -----------------------------------------------------------------------------
+# Validate
+# -----------------------------------------------------------------------------
+[ -n "$PYTHON_BIN" ] || die "Python not found. Install Python 3.11+ and ensure it is available as python (Windows/Git Bash) or via the Windows Python Launcher (py), or as python3.11 (macOS/Linux)."
+[ -f "$ENTRY" ] || die "Entry not found: $ENTRY"
+
+# Validate python runtime is actually runnable and >= 3.11
+if ! python_exec -c "import sys; assert (sys.version_info.major, sys.version_info.minor) >= (3, 11), sys.version" >/dev/null 2>&1; then
+ if [ "$OS" = "windows" ] && [ "$PYTHON_BIN" = "py" ]; then
+ die "Python launcher found, but no suitable Python 3.11+ runtime is installed. Install Python 3.11+ (check 'Add python.exe to PATH') or run: py --list to verify installed versions."
+ fi
+ die "Python is not runnable or is < 3.11. Install Python 3.11+ and try again."
+fi
+
+ARCH="$(uname -m)"
+if [ "$OS" = "linux" ] && { [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "armv7l" ] || [ "$ARCH" = "armhf" ]; }; then
+ # Auto-enable system PyQt on ARM unless explicitly overridden by user (flag)
+ # Keeping your prior behavior but behind the switch.
+ :
+fi
+
+# Auto package choice
+if [ -z "$PKG" ]; then
+ if [ "$OS" = "macos" ]; then
+ PKG="onedir"
+ else
+ PKG="onefile"
+ fi
+fi
+
+# Auto icon discovery if not provided
+if [ -z "$ICON_FILE" ]; then
+ if [ "$OS" = "macos" ] && [ -f "src/icons/icon.icns" ]; then
+ ICON_FILE="src/icons/icon.icns"
+ elif [ "$OS" = "windows" ] && [ -f "src/icons/icon.ico" ]; then
+ ICON_FILE="src/icons/icon.ico"
+ elif [ -f "src/icons/icon.png" ]; then
+ ICON_FILE="src/icons/icon.png"
+ fi
+fi
+
+# Add default data folders if present (and not already provided)
+for cand in "${DEFAULT_DATA_CANDIDATES[@]}"; do
+ src="${cand%%:*}"
+ if [ -e "$src" ]; then
+ # Avoid duplicates
+ dup=0
+ for existing in "${ADD_DATA[@]+${ADD_DATA[@]}}"; do
+ [ "$existing" = "$cand" ] && dup=1
+ done
+ [ "$dup" -eq 0 ] && ADD_DATA+=("$cand")
+ fi
+done
+
+# -----------------------------------------------------------------------------
+# Prepare output
+# -----------------------------------------------------------------------------
+FINAL_DIR="dist/$OS"
+
+if [ "${CLEAN:-0}" -eq 1 ]; then
+ log "Cleaning build artifacts..."
+ rm -rf build dist *.spec || true
+fi
+
+mkdir -p "$FINAL_DIR"
+generate_wrapper_scripts "."
+
+#
+# -----------------------------------------------------------------------------
+# Create/verify venv (Python 3.11)
+# -----------------------------------------------------------------------------
+NEED_RECREATE=0
+if [ ! -x "$(venv_python_path)" ]; then
+ NEED_RECREATE=1
+else
+ VENV_VER="$("$(venv_python_path)" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' || echo unknown)"
+ if [ "$VENV_VER" != "3.11" ]; then
+ NEED_RECREATE=1
+ fi
+fi
+
+if [ "$NEED_RECREATE" -eq 1 ]; then
+ log "Creating fresh Python 3.11 virtual environment in $VENV_DIR..."
+ rm -rf "$VENV_DIR"
+ if [ "$USE_SYSTEM_PYQT" -eq 1 ]; then
+ python_exec -m venv --system-site-packages "$VENV_DIR"
+ else
+ python_exec -m venv "$VENV_DIR"
+ fi
+fi
+
+# shellcheck disable=SC1091
+source "$(venv_activate_path)"
+
+ACTIVE_VER="$(python -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')"
+[ "$ACTIVE_VER" = "3.11" ] || die "Active Python is $ACTIVE_VER, expected 3.11. On Windows, install Python 3.11 and/or use: py -3.11"
+log "Using Python $(python -V)"
+
+log "Updating pip..."
+python -m pip install --upgrade pip wheel
+
+log "Installing build dependencies..."
+python -m pip install "pyinstaller>=6.9,<7"
+
+# PyInstaller on Windows requires .ico (or Pillow for automatic conversion from .png).
+# Install Pillow when building on Windows so we can safely use .png icons or generate .ico.
+if [ "$OS" = "windows" ]; then
+ python -m pip install "pillow>=10,<12" || true
+fi
+
+
+# Optional: system PyQt5 on Linux ARM (APT)
+if [ "$USE_SYSTEM_PYQT" -eq 1 ]; then
+ if [ "$OS" != "linux" ]; then
+ die "--system-pyqt is only supported on Linux"
+ fi
+ if command -v apt-get >/dev/null 2>&1; then
+ log "Installing system PyQt5 via APT (requires sudo)..."
+ sudo apt-get update
+ sudo apt-get install -y python3-pyqt5 python3-pyqt5.qtsvg
+ else
+ die "APT not found; cannot install system PyQt5. Install PyQt5 manually or omit --system-pyqt."
+ fi
+else
+ # Default: wheels
+ # (Only install PyQt5 if the project uses it; safe to install regardless.)
+ python -m pip install "PyQt5>=5.15,<6" || true
+fi
+
+# Ensure SIP bindings are bundled for PyQt5.
+# Note: the importable module is typically `PyQt5.sip` (not `sip`).
+# PyInstaller may still emit a non-fatal warning about hidden import "sip" depending on hooks.
+has_pyqt5=0
+if python -c "import importlib.util; import sys; sys.exit(0 if importlib.util.find_spec('PyQt5') else 1)" >/dev/null 2>&1; then
+ has_pyqt5=1
+fi
+
+if [ "$has_pyqt5" -eq 1 ]; then
+ # Add PyQt5.sip unless the user already provided it.
+ found=0
+ for h in "${HIDDEN_IMPORTS[@]+${HIDDEN_IMPORTS[@]}}"; do
+ [ "$h" = "PyQt5.sip" ] && found=1
+ done
+ if [ "$found" -eq 0 ]; then
+ HIDDEN_IMPORTS+=("PyQt5.sip")
+ fi
+
+ # If the user ever provided `sip`, normalize it early.
+ for i in "${!HIDDEN_IMPORTS[@]}"; do
+ if [ "${HIDDEN_IMPORTS[$i]}" = "sip" ]; then
+ HIDDEN_IMPORTS[$i]="PyQt5.sip"
+ fi
+ done
+fi
+
+# -----------------------------------------------------------------------------
+# Build
+# -----------------------------------------------------------------------------
+log "Building $APP_NAME for $OS ($PKG, $MODE) from entry: $ENTRY"
+
+PYI_ARGS=(
+ --name "$APP_NAME"
+ --noconfirm
+)
+
+# Optional PyInstaller debug
+if [ "${PYI_DEBUG:-0}" -eq 1 ]; then
+ PYI_ARGS+=(--log-level=DEBUG --debug=all)
+fi
+
+# Packaging
+if [ "$PKG" = "onefile" ]; then
+ PYI_ARGS+=(--onefile)
+else
+ PYI_ARGS+=(--onedir)
+fi
+
+# Window mode
+if [ "$MODE" = "windowed" ]; then
+ PYI_ARGS+=(--windowed)
+fi
+
+# Icon
+if [ -n "$ICON_FILE" ] && [ -f "$ICON_FILE" ]; then
+ if [ "$OS" = "windows" ]; then
+ ext="${ICON_FILE##*.}"
+ ext="$(echo "$ext" | tr '[:upper:]' '[:lower:]')"
+
+ # On Windows, PyInstaller only accepts .ico/.exe as icon inputs unless Pillow is installed.
+ # If the provided icon is .png, convert it to a temporary .ico using Pillow.
+ if [ "$ext" = "png" ]; then
+ mkdir -p build
+ ICO_FROM_PNG="build/icon_from_png.ico"
+ python - <<'PY' "$ICON_FILE" "$ICO_FROM_PNG" || true
+import sys
+from pathlib import Path
+
+src = Path(sys.argv[1])
+dst = Path(sys.argv[2])
+
+try:
+ from PIL import Image
+except Exception as e:
+ raise SystemExit(1)
+
+img = Image.open(src).convert("RGBA")
+# Common Windows icon sizes
+sizes = [(16,16), (24,24), (32,32), (48,48), (64,64), (128,128), (256,256)]
+img.save(dst, format="ICO", sizes=sizes)
+print(dst)
+PY
+ if [ -f "$ICO_FROM_PNG" ]; then
+ PYI_ARGS+=(--icon "$ICO_FROM_PNG")
+ else
+ log "WARN: Failed to convert PNG icon to ICO; continuing without --icon"
+ fi
+ else
+ # .ico/.exe already
+ PYI_ARGS+=(--icon "$ICON_FILE")
+ fi
+ else
+ PYI_ARGS+=(--icon "$ICON_FILE")
+ fi
+fi
+
+# Hidden imports
+# NOTE:
+# - PyQt5 typically exposes SIP bindings as `PyQt5.sip` (and ships `PyQt5_sip` as a wheel).
+# - Users sometimes add `sip` as a hidden import; that module often does not exist and causes warnings.
+# We normalize `sip` -> `PyQt5.sip` when available, and skip hidden imports that cannot be resolved.
+can_import() {
+ # usage: can_import module.name
+ python - </dev/null 2>&1
+import importlib.util
+spec = importlib.util.find_spec("$1")
+raise SystemExit(0 if spec is not None else 1)
+PY
+}
+
+for hi in "${HIDDEN_IMPORTS[@]+${HIDDEN_IMPORTS[@]}}"; do
+ [ -n "$hi" ] || continue
+
+ # Normalize common SIP hidden import for PyQt5.
+ # The standalone `sip` module is often NOT importable even if the sip build tooling is installed.
+ if [ "$hi" = "sip" ]; then
+ if can_import "PyQt5.sip"; then
+ log "Normalizing hidden import: sip -> PyQt5.sip"
+ hi="PyQt5.sip"
+ else
+ log "WARN: Skipping hidden import not found: sip (and PyQt5.sip not available)"
+ continue
+ fi
+ fi
+
+ if can_import "$hi"; then
+ PYI_ARGS+=(--hidden-import "$hi")
+ else
+ log "WARN: Skipping hidden import not found: $hi"
+ fi
+done
+
+# Data
+# PyInstaller (on some versions) requires the equals form: --add-data=SRC:DST
+for ad in "${ADD_DATA[@]+${ADD_DATA[@]}}"; do
+ # Allow config/CLI to provide either:
+ # - SRC:DST
+ # - --add-data=SRC:DST
+ # - --add-data SRC:DST (accidentally split by callers)
+ [ -n "$ad" ] || continue
+ if [ "$ad" = "--add-data" ]; then
+ # Skip stray flag (the next token would have been the value)
+ continue
+ fi
+
+ if [[ "$ad" == --add-data=* ]]; then
+ PYI_ARGS+=("$ad")
+ else
+ PYI_ARGS+=("--add-data=$ad")
+ fi
+
+done
+
+# Extra sanity checks (don’t fail builds for optional modules)
+log "Running a quick import sanity check (best-effort)..."
+python - <<'PY' || true
+import sys
+print("Python:", sys.version)
+try:
+ import PyInstaller # noqa
+ print("PyInstaller OK")
+except Exception as e:
+ print("PyInstaller import failed:", e)
+PY
+
+# Print PyInstaller command for debug
+log "PyInstaller command:"
+printf ' %q ' pyinstaller "${PYI_ARGS[@]}" "$ENTRY"
+printf '\n'
+
+
+# Execute PyInstaller
+pyinstaller "${PYI_ARGS[@]}" "$ENTRY"
+
+# ---------------------------------------------------------------------------
+# Windows: also build a console-subsystem companion EXE for CLI usage.
+# - Keeps the main EXE as --windowed (nice for double-click)
+# - Produces -cli.exe without --windowed so stdout/stderr work in terminals
+# ---------------------------------------------------------------------------
+if [ "$OS" = "windows" ] && [ "$MODE" = "windowed" ]; then
+ CLI_APP_NAME="${APP_NAME}-cli"
+
+ log "Building $CLI_APP_NAME for $OS ($PKG, console) from entry: $ENTRY"
+
+ # Copy args and adjust for console build
+ PYI_CLI_ARGS=("${PYI_ARGS[@]}")
+
+ # Replace --name value
+ for i in "${!PYI_CLI_ARGS[@]}"; do
+ if [ "${PYI_CLI_ARGS[$i]}" = "--name" ]; then
+ PYI_CLI_ARGS[$((i+1))]="$CLI_APP_NAME"
+ fi
+ done
+
+ # Remove --windowed if present
+ PYI_CLI_ARGS_STRIPPED=()
+ for a in "${PYI_CLI_ARGS[@]}"; do
+ if [ "$a" = "--windowed" ]; then
+ continue
+ fi
+ PYI_CLI_ARGS_STRIPPED+=("$a")
+ done
+
+ log "PyInstaller command (CLI companion):"
+ printf ' %q ' pyinstaller "${PYI_CLI_ARGS_STRIPPED[@]}" "$ENTRY"
+ printf '\n'
+
+ pyinstaller "${PYI_CLI_ARGS_STRIPPED[@]}" "$ENTRY"
+fi
+
+# -----------------------------------------------------------------------------
+# Collect output
+# -----------------------------------------------------------------------------
+if [ "$OS" = "macos" ]; then
+ # If entry builds a .app bundle, it'll appear under dist/.app for onedir+windowed.
+ if [ -d "dist/$APP_NAME.app" ]; then
+ log "Moving .app to $FINAL_DIR/"
+ rm -rf "$FINAL_DIR/$APP_NAME.app" || true
+ mv "dist/$APP_NAME.app" "$FINAL_DIR/"
+
+ if [ "$MAKE_DMG" -eq 1 ]; then
+ log "Creating DMG..."
+ DMG_NAME="$FINAL_DIR/$APP_NAME.dmg"
+ hdiutil create "$DMG_NAME" -volname "$APP_NAME" -srcfolder "$FINAL_DIR/$APP_NAME.app" -ov -format UDZO
+ log "DMG created at $DMG_NAME"
+ fi
+ else
+ # Non-windowed or non-app outputs (e.g., console onedir)
+ log "Moving build output to $FINAL_DIR/"
+ rm -rf "$FINAL_DIR/$APP_NAME" || true
+ mv "dist/$APP_NAME" "$FINAL_DIR/"
+ fi
+else
+ log "Moving build output to $FINAL_DIR/"
+
+ # Windows onefile creates dist/.exe; onedir creates dist//
+ # Move all matching outputs for this app (and its -cli companion).
+ if [ "$OS" = "windows" ]; then
+ # Clean old outputs
+ rm -f "$FINAL_DIR/${APP_NAME}.exe" "$FINAL_DIR/${APP_NAME}-cli.exe" || true
+ rm -rf "$FINAL_DIR/$APP_NAME" "$FINAL_DIR/${APP_NAME}-cli" || true
+
+ # Prefer moving EXEs if they exist
+ if [ -f "dist/${APP_NAME}.exe" ]; then
+ mv "dist/${APP_NAME}.exe" "$FINAL_DIR/"
+ elif [ -f "dist/$APP_NAME" ]; then
+ mv "dist/$APP_NAME" "$FINAL_DIR/" || true
+ fi
+
+ if [ -f "dist/${APP_NAME}-cli.exe" ]; then
+ mv "dist/${APP_NAME}-cli.exe" "$FINAL_DIR/"
+ elif [ -f "dist/${APP_NAME}-cli" ]; then
+ mv "dist/${APP_NAME}-cli" "$FINAL_DIR/" || true
+ fi
+
+ # If onedir outputs were produced, move them too
+ if [ -d "dist/$APP_NAME" ]; then
+ rm -rf "$FINAL_DIR/$APP_NAME" || true
+ mv "dist/$APP_NAME" "$FINAL_DIR/"
+ fi
+ if [ -d "dist/${APP_NAME}-cli" ]; then
+ rm -rf "$FINAL_DIR/${APP_NAME}-cli" || true
+ mv "dist/${APP_NAME}-cli" "$FINAL_DIR/"
+ fi
+
+ else
+ rm -rf "$FINAL_DIR/$APP_NAME" || true
+ mv "dist/$APP_NAME" "$FINAL_DIR/"
+ fi
+fi
+
+log "Build completed successfully. Output: $FINAL_DIR"
+
+# ---------------------------------------------------------------------------
+# Windows: create a .lnk shortcut (best-effort)
+# ---------------------------------------------------------------------------
+if [ "$OS" = "windows" ]; then
+ EXE_PATH="$(pwd)/$FINAL_DIR/$APP_NAME.exe"
+ LNK_PATH="$(pwd)/$FINAL_DIR/$APP_NAME.lnk"
+
+ # Create a shortcut inside dist/windows (so we can ship it alongside the exe)
+ if [ -f "$EXE_PATH" ]; then
+ mkdir -p "$FINAL_DIR" || true
+ cat >"build/make_shortcut.vbs" <<'VBS'
+Option Explicit
+Dim shell, args, exePath, lnkPath
+Set shell = CreateObject("WScript.Shell")
+Set args = WScript.Arguments
+exePath = args.Item(0)
+lnkPath = args.Item(1)
+Dim sc
+Set sc = shell.CreateShortcut(lnkPath)
+sc.TargetPath = exePath
+sc.WorkingDirectory = CreateObject("Scripting.FileSystemObject").GetParentFolderName(exePath)
+sc.WindowStyle = 1
+sc.Description = "Replicator"
+sc.Save
+VBS
+
+ if command -v cscript >/dev/null 2>&1; then
+ cscript //nologo "build/make_shortcut.vbs" "$(cygpath -w "$EXE_PATH" 2>/dev/null || echo "$EXE_PATH")" "$(cygpath -w "$LNK_PATH" 2>/dev/null || echo "$LNK_PATH")" >/dev/null 2>&1 || true
+ fi
+
+ # Fallback: create a .url shortcut (always works)
+ if [ ! -f "$LNK_PATH" ]; then
+ cat >"$FINAL_DIR/$APP_NAME.url" </dev/null 2>&1; then
+ cscript //nologo "build/make_shortcut.vbs" "$(cygpath -w "$CLI_EXE_PATH" 2>/dev/null || echo "$CLI_EXE_PATH")" "$(cygpath -w "$CLI_LNK_PATH" 2>/dev/null || echo "$CLI_LNK_PATH")" >/dev/null 2>&1 || true
+ fi
+ if [ ! -f "$CLI_LNK_PATH" ]; then
+ cat >"$FINAL_DIR/${APP_NAME}-cli.url" < [folder-name]" >&2
+ exit 1
+fi
+
+TARGET_BIN="$1"
+TARGET_DIR="${2:-$TARGET_BIN}"
+
+# Root of your project (adjust if needed)
+PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+
+UNAME_OS="$(uname -s)"
+
+case "$UNAME_OS" in
+ Linux)
+ OS="linux"
+ ;;
+ Darwin)
+ OS="macos"
+ ;;
+ *)
+ echo "Unsupported OS: $UNAME_OS" >&2
+ exit 1
+ ;;
+esac
+ARCH="$(uname -m)"
+
+case "$ARCH" in
+ armv6l|armv7l|armhf)
+ ARCH="armhf"
+ ;;
+ aarch64|arm64)
+ ARCH="arm64"
+ ;;
+ *)
+ echo "Unsupported architecture: $ARCH" >&2
+ exit 1
+ ;;
+esac
+
+DEST_DIR="$PROJECT_ROOT/src/bin/${TARGET_DIR}/${OS}/${ARCH}"
+mkdir -p "$DEST_DIR"
+
+SOURCE_BIN="$(command -v "${TARGET_BIN}" || true)"
+
+if [[ -z "$SOURCE_BIN" ]]; then
+ echo "${TARGET_BIN} not found in PATH. Attempting installation..."
+
+ if [[ "$OS" == "macos" ]]; then
+ if ! command -v brew >/dev/null 2>&1; then
+ echo "Homebrew is required but not installed." >&2
+ exit 1
+ fi
+ echo "Installing ${TARGET_BIN} using Homebrew..."
+ brew install "${TARGET_BIN}" || {
+ echo "Failed to install ${TARGET_BIN} via Homebrew." >&2
+ exit 1
+ }
+ elif [[ "$OS" == "linux" ]]; then
+ echo "Installing ${TARGET_BIN} using apt-get..."
+ sudo apt-get update -y
+ sudo apt-get install -y "${TARGET_BIN}" || {
+ echo "Failed to install ${TARGET_BIN} via apt-get." >&2
+ exit 1
+ }
+ fi
+
+ SOURCE_BIN="$(command -v "${TARGET_BIN}" || true)"
+ if [[ -z "$SOURCE_BIN" ]]; then
+ echo "Installation succeeded but ${TARGET_BIN} is still not in PATH." >&2
+ exit 1
+ fi
+fi
+
+echo "Found ${TARGET_BIN} at: $SOURCE_BIN"
+echo "Copying to: $DEST_DIR/${TARGET_BIN}"
+
+cp "$SOURCE_BIN" "$DEST_DIR/${TARGET_BIN}"
+chmod 755 "$DEST_DIR/${TARGET_BIN}"
+
+echo "Done."
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/cli.py b/dist/macos/Replicator.app/Contents/Resources/core/cli.py
new file mode 100644
index 00000000..0baf85c6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/cli.py
@@ -0,0 +1,249 @@
+#!/usr/bin/env python3
+# src/core/cli.py
+import sys
+from typing import Any, Optional
+
+from PyQt5.QtWidgets import QApplication
+
+from .helper import Helper
+from .configuration import Configuration
+from .log import Log
+from .service import Service
+
+# ---------------------------------------------------------------------------
+# CommandLine class
+# ---------------------------------------------------------------------------
+
+class CommandLine(QApplication):
+
+ def __init__(self, name: Optional[str] = None, argv=None):
+
+ # Initialize QApplication
+ super().__init__(argv or [])
+
+ # Application name
+ if name:
+ self.setApplicationName(name)
+
+ # Set application mode
+ self._mode = "cli"
+
+ # Initialize command line
+ self._commands: dict[str, Any] = {}
+
+ # Add help command
+ self.add("help", "Show this help message", self.help)
+
+ # Helper
+ self._helper = Helper()
+
+ # Configuration manager
+ self._configuration = Configuration()
+
+ # Logger
+ self._logger = Log()
+
+ # Service manager
+ self._service = Service(self._logger, self._configuration)
+
+ # Initialize core commands
+ if hasattr(self._configuration, "cli") and callable(getattr(self._configuration, "cli")):
+ self._configuration.cli(self)
+ if hasattr(self._logger, "cli") and callable(getattr(self._logger, "cli")):
+ self._logger.cli(self)
+ if hasattr(self._service, "cli") and callable(getattr(self._service, "cli")):
+ self._service.cli(self)
+
+ # ------------------------------------------------------------------
+ # Properties / accessors
+ # ------------------------------------------------------------------
+
+ @property
+ def helper(self) -> Helper:
+ return self._helper
+
+ @property
+ def logger(self) -> Log:
+ return self._logger
+
+ @property
+ def configuration(self) -> Configuration:
+ return self._configuration
+
+ @property
+ def service(self) -> Service:
+ return self._service
+
+ @property
+ def name(self) -> str:
+ return self.applicationName()
+
+ @property
+ def mode(self) -> str:
+ return self._mode
+
+ # ------------------------------------------------------------------
+ # Core API
+ # ------------------------------------------------------------------
+
+ def help(self) -> None:
+ print()
+ print(f"Usage: {self.name} [command] [options]")
+ print()
+ print("Available commands:")
+ print()
+
+ # Group entries by category while preserving insertion order.
+ # Category order is the order we first see a category during add().
+ categories_order: list[str] = []
+ grouped: dict[str, list[tuple[str, str]]] = {}
+
+ # Build usage strings including required arguments
+ # Preserve insertion order (the order commands were added)
+ for cmd, info in self._commands.items():
+ category = info.get("category") or "General"
+ # Capitalize category
+ category = category.capitalize()
+ if category not in grouped:
+ grouped[category] = []
+ categories_order.append(category)
+
+ desc = info.get("description", "") or ""
+ args_required = info.get("args_required", 0) or 0
+ arg_names = info.get("arg_names") or []
+
+ # Ensure we have at least `args_required` argument labels
+ if len(arg_names) < args_required:
+ # Pad with generic placeholders if needed
+ start_index = len(arg_names) + 1
+ for i in range(start_index, args_required + 1):
+ arg_names.append(f"")
+
+ usage = cmd
+ if args_required > 0:
+ usage += " " + " ".join(arg_names[:args_required])
+
+ grouped[category].append((usage, desc))
+
+ # Compute padding for alignment across all commands
+ all_entries: list[tuple[str, str]] = []
+ for cat in categories_order:
+ all_entries.extend(grouped.get(cat, []))
+
+ if all_entries:
+ max_cmd_len = max(len(usage) for usage, _ in all_entries)
+ else:
+ max_cmd_len = 0
+
+ # Print grouped commands with a simple separator/header per category
+ for idx, cat in enumerate(categories_order):
+ if idx != 0:
+ print()
+ print(f"[{cat}]")
+ for usage, desc in grouped.get(cat, []):
+ print(f" {usage:<{max_cmd_len}} {desc}")
+
+ print()
+
+ def add(self, command: str, description: str = "", callable: Optional[callable] = None, args: int = 0, arg_names: Optional[list[str]] = None) -> None:
+ # Support category prefixes using dot notation (e.g. "service.status").
+ # The CLI token remains the final segment ("--status"), while the prefix
+ # is used only for grouping in help output.
+ category: Optional[str] = None
+
+ raw = command.lstrip("-")
+ if "." in raw:
+ parts = [p for p in raw.split(".") if p]
+ if len(parts) >= 2:
+ category = ".".join(parts[:-1])
+ command = parts[-1]
+
+ if not command.startswith("--"):
+ command = f"--{command}"
+ # Normalize argument names
+ if arg_names is None:
+ arg_names = []
+ # Store command metadata
+ self._commands[command] = {
+ "description": description,
+ "callable": callable,
+ "args_required": args,
+ "arg_names": arg_names,
+ "category": category,
+ }
+
+ def run(self, command: str, *args: Any, **kwargs: Any) -> Any:
+ cmd = self._commands.get(command)
+ if not cmd:
+ raise ValueError(f"Command not found: {command}")
+
+ required = cmd.get("args_required", 0)
+ arg_names = cmd.get("arg_names") or []
+
+ if len(arg_names) < required:
+ # Pad missing argument names with generic placeholders
+ start_index = len(arg_names) + 1
+ for i in range(start_index, required + 1):
+ arg_names.append(f"")
+
+ if len(args) < required:
+ missing = required - len(args)
+ missing_list = " ".join(arg_names[len(args):required])
+ raise ValueError(f"Missing {missing} argument(s) for {command}: {missing_list}")
+
+ func = cmd.get("callable")
+ if not func:
+ raise ValueError(f"Command has no callable: {command}")
+
+ return func(*args, **kwargs)
+
+ def exec(self) -> int:
+ argv = list(self.arguments()) # includes program name as first element
+
+ # No command provided: show help and exit 0
+ if len(argv) < 2:
+ self.help()
+ return 0
+
+ commands: list[tuple[str, list[str]]] = []
+
+ current_cmd: Optional[str] = None
+ current_args: list[str] = []
+
+ # Parse argv into (command, args) groups
+ for arg in argv[1:]:
+ if arg.startswith("--"):
+ # Starting a new command
+ if current_cmd is not None:
+ commands.append((current_cmd, current_args))
+ current_cmd = arg
+ current_args = []
+ else:
+ # Positional argument for the current command
+ if current_cmd is None:
+ # We got an argument before any command; treat as error
+ print(f"Error: unexpected argument '{arg}' before any command.", file=sys.stderr)
+ return 1
+ current_args.append(arg)
+
+ # Append the last pending command
+ if current_cmd is not None:
+ commands.append((current_cmd, current_args))
+
+ # Still no valid command? Show help.
+ if not commands:
+ self.help()
+ return 0
+
+ exit_code = 0
+
+ # Execute each command in sequence; stop on first error
+ for command, cmd_args in commands:
+ try:
+ self.run(command, *cmd_args)
+ except Exception as e:
+ print(f"Error in {command}: {e}", file=sys.stderr)
+ exit_code = 1
+ break
+
+ return exit_code
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/configuration.py b/dist/macos/Replicator.app/Contents/Resources/core/configuration.py
new file mode 100644
index 00000000..7b24a7ad
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/configuration.py
@@ -0,0 +1,765 @@
+#!/usr/bin/env python3
+# src/core/configuration.py
+from __future__ import annotations
+
+import os
+import json
+import shutil
+import sys
+from collections import defaultdict
+from typing import Any, Tuple, Optional, TYPE_CHECKING
+from urllib import request as _urlrequest, error as _urlerror
+
+from PyQt5.QtCore import pyqtSignal, QObject
+from PyQt5.QtWidgets import (
+ QDialog, QVBoxLayout, QTabWidget, QWidget, QFormLayout,
+ QLabel, QPushButton, QHBoxLayout, QApplication, QFileDialog,
+ QLineEdit, QComboBox, QCheckBox, QSpinBox
+)
+
+# Allow this module to be used both as part of the 'app' package and as a standalone script
+try:
+ from .helper import Helper
+ from .ui import Form
+ from .network.tools import Tools
+except ImportError: # likely running as a top-level script
+ from helper import Helper
+ from ui import Form
+ from network.tools import Tools
+
+if TYPE_CHECKING:
+ # For type hints only, avoids circular import at runtime
+ try:
+ from .application import Application
+ except ImportError:
+ from application import Application
+
+class Configuration(QObject):
+
+ # Emitted after a successful save (or when we choose to later)
+ configChanged = pyqtSignal(dict)
+
+ def __init__(
+ self,
+ helper: Optional[Helper] = None,
+ filename: str = "configuration.cfg"
+ ):
+
+ # Initialize QObject
+ super().__init__()
+
+ # Retrieve the application instance (may be None in CLI usage)
+ self._app: Application | None = QApplication.instance() # type: ignore[valid-type]
+
+ # Ensure we have either an Application or an explicit Helper
+ if self._app is None and helper is None:
+ raise RuntimeError(
+ "Configuration must be created after QApplication/Application or with an explicit Helper."
+ )
+
+ # --- auto-wire from QApplication if not provided ---
+ if helper is None and self._app is not None:
+ # narrow the type for linters / IDEs
+ # no runtime import to avoid circular imports
+ helper = self._app.helper # type: ignore[attr-defined]
+
+ # Helper
+ self._helper: Helper = helper
+
+ # Parent
+ self._parent = None
+
+ # Root directory for config files
+ self.root_dir = helper.root_dir
+ self._filename = filename
+
+ # Actual config values (nested dict)
+ self._data: dict[str, Any] = {}
+
+ # Schema: key -> {"default": ..., "widget": "text"/"select"/...}
+ self._schema: dict[str, dict[str, Any]] = {}
+
+ # UI widgets mapped by config key
+ self._widgets: dict[str, Any] = {}
+
+ # Actual QLabel widgets mapped by config key (for show/hide)
+ self._widget_labels: dict[str, Any] = {}
+
+ # Labels: key → display label (e.g. "freerdp" -> "FreeRDP",
+ # "freerdp.display" -> "Display")
+ self._labels: dict[str, str] = {}
+
+ # Load existing file if any
+ self.load()
+
+ # Add built-in administration actions
+ self.add("administration.import", None, "button", label="Import Configuration", action=self.import_cfg)
+ self.add("administration.export", None, "button", label="Export Configuration", action=self.export_cfg)
+
+ # Built-in provisioning settings
+ self.add("provisioning.host", "", "text", label="Host/URL")
+ self.add("provisioning.token", "", "password", label="Token")
+ self.add("provisioning.appid", self._app.applicationName() if self._app else "corePY App", "text", label="App ID")
+
+ # ------------------------------------------------------------------
+ # Core API
+ # ------------------------------------------------------------------
+
+ def load(self, file: str | None = None) -> None:
+ if file is not None:
+ self._filename = file
+
+ config_dir = self._get_config_dir()
+ os.makedirs(config_dir, exist_ok=True)
+
+ path = os.path.join(config_dir, self._filename)
+ if not os.path.exists(path):
+ # Nothing yet, keep empty data
+ self._data = {}
+ return
+
+ try:
+ with open(path, "r", encoding="utf-8") as f:
+ data = json.load(f)
+ if isinstance(data, dict):
+ self._data = data
+ else:
+ self._data = {}
+ except Exception as e:
+ print(f"[Configuration] Failed loading {self._filename}: {e}")
+ self._data = {}
+
+ def save(self) -> None:
+ config_dir = self._get_config_dir()
+ os.makedirs(config_dir, exist_ok=True)
+
+ path = os.path.join(config_dir, self._filename)
+ try:
+ with open(path, "w", encoding="utf-8") as f:
+ json.dump(self._data, f, indent=2)
+ # Notify listeners
+ self.configChanged.emit(self._data)
+ except Exception as e:
+ print(f"[Configuration] Failed saving {self._filename}: {e}")
+
+ def add(self, key: str, default_value: Any, widget: str | None = None, **options: Any) -> None:
+ self._schema[key] = {
+ "default": default_value,
+ "widget": widget,
+ "options": options or {},
+ }
+
+ # If there's no value yet in self._data, apply the default now
+ try:
+ current = self.get(key, _no_fallback=True)
+ except KeyError:
+ # Not present at all → set default
+ self.set(key, default_value)
+
+ def get(self, key: str, default: Any | None = None, _no_fallback: bool = False) -> Any:
+ parts = key.split(".")
+ node: Any = self._data
+
+ for part in parts:
+ if not isinstance(node, dict) or part not in node:
+ # Missing key
+ if _no_fallback:
+ raise KeyError(key)
+ # explicit default wins
+ if default is not None:
+ return default
+ # fall back to schema default if any
+ meta = self._schema.get(key)
+ if meta is not None:
+ return meta.get("default")
+ return None
+ node = node[part]
+
+ return node
+
+ def set(self, key: str, value: Any) -> None:
+ parts = key.split(".")
+ if not parts:
+ return
+
+ node = self._data
+ for part in parts[:-1]:
+ if part not in node or not isinstance(node[part], dict):
+ node[part] = {}
+ node = node[part]
+
+ node[parts[-1]] = value
+
+ def input(self, key: str, show_label: bool = False) -> QWidget:
+ if key not in self._schema:
+ raise KeyError(f"Configuration key not defined in schema: {key}")
+
+ widget, label_text = self._build_widget_for_key(key)
+
+ if not show_label:
+ return widget
+
+ container = QWidget()
+ layout = QHBoxLayout(container)
+ layout.setContentsMargins(0, 0, 0, 0)
+ label = QLabel(label_text)
+ layout.addWidget(label)
+ layout.addWidget(widget)
+ return container
+
+ def label(self, key: str, label: str | None = None) -> str:
+ if label is not None:
+ self._labels[key] = label
+
+ if key in self._labels:
+ return self._labels[key]
+
+ # Fallback: nice label from the last segment
+ return self._nice_label(key.split(".")[-1])
+
+ def reload(self, key: str, value: Any) -> None:
+ self.set(key, value)
+ w = self._widgets.get(key)
+ if w is None:
+ return
+
+ if isinstance(w, QLineEdit):
+ w.setText(str(value) if value is not None else "")
+ elif isinstance(w, QComboBox):
+ idx = w.findText(str(value))
+ if idx >= 0:
+ w.setCurrentIndex(idx)
+ elif isinstance(w, QCheckBox):
+ w.setChecked(bool(value))
+ elif isinstance(w, QSpinBox):
+ try:
+ w.setValue(int(value))
+ except Exception:
+ pass
+ else:
+ # Custom widgets (ColorButton, FileInput, PictureButton, etc.)
+ # Prefer a color-specific API if available, then fall back to a generic setter.
+ if hasattr(w, "setHex") and callable(getattr(w, "setHex")):
+ # Color button / color-like widget
+ w.setHex(value)
+ elif hasattr(w, "setValue") and callable(getattr(w, "setValue")):
+ w.setValue(value)
+
+ def visibility(self, key: str, visible: bool) -> None:
+ w = self._widgets.get(key)
+ if w is not None:
+ w.setVisible(visible)
+
+ lbl = self._widget_labels.get(key)
+ if lbl is not None:
+ lbl.setVisible(visible)
+
+ def reset(self, save: bool = False) -> None:
+ # Start with a clean configuration dict
+ self._data = {}
+
+ # 1) Rebuild data from schema defaults
+ for key, meta in self._schema.items():
+ default = meta.get("default")
+ self.set(key, default)
+
+ # 2) Push defaults into widgets so the UI reflects the new values
+ for key in self._schema.keys():
+ try:
+ value = self.get(key, _no_fallback=True)
+ except KeyError:
+ continue
+ self.reload(key, value)
+
+ # 3) Optionally persist and notify listeners
+ if save:
+ self.save()
+
+ # ------------------------------------------------------------------
+ # Convenience properties
+ # ------------------------------------------------------------------
+
+ @property
+ def data(self) -> dict[str, Any]:
+ return self._data
+
+ @property
+ def schema(self) -> dict[str, dict[str, Any]]:
+ return self._schema
+
+ # ------------------------------------------------------------------
+ # core API Callables
+ # ------------------------------------------------------------------
+
+ def cli(self, cli: QApplication = None) -> None:
+ # cli.add("config.import", "Import configuration from the given file.", self.import_from_path, args=1, arg_names=[""])
+ # cli.add("config.provision", "Contact the provisioning server and import the returned configuration.", self.provision)
+ pass
+
+ # ------------------------------------------------------------------
+ # UI dialog builder
+ # ------------------------------------------------------------------
+
+ def show(self, parent=None):
+
+ # Only use a QWidget as dialog parent, otherwise use None
+
+ if parent is not None and isinstance(parent, QWidget):
+ self._parent = parent
+
+ dlg = QDialog(self._parent)
+ dlg.setWindowTitle("Configuration")
+ dlg.setObjectName("configurationWindow")
+
+ root = QVBoxLayout(dlg)
+ tabs = QTabWidget()
+ root.addWidget(tabs)
+
+ # structure[category][subcategory] = [(full_key, meta), ...]
+ structure: dict[str, dict[str | None, list[Tuple[str, dict[str, Any]]]]] = defaultdict(
+ lambda: defaultdict(list)
+ )
+
+ for full_key, meta in self._schema.items():
+ parts = full_key.split(".")
+ if len(parts) == 1:
+ category = parts[0]
+ subcat: str | None = None
+ name = parts[0]
+ elif len(parts) == 2:
+ category = parts[0]
+ subcat = None
+ name = parts[1]
+ else:
+ category = parts[0]
+ subcat = parts[1]
+ name = ".".join(parts[2:]) # rare case of deeper nesting
+
+ structure[category][subcat].append((full_key, meta))
+
+ # Build tabs
+ self._widgets.clear()
+ self._widget_labels.clear()
+
+ for category, subcats in structure.items():
+ # Top-level tab widget
+ cat_widget = QWidget()
+ cat_layout = QVBoxLayout(cat_widget)
+
+ # If there's more than one subcategory (or exactly one non-None),
+ # we add a nested QTabWidget, otherwise a simple form.
+ non_none_subs = [s for s in subcats.keys() if s is not None]
+
+ if non_none_subs:
+ # There are sub-tabs
+ subtabs = QTabWidget()
+ cat_layout.addWidget(subtabs)
+
+ # subcat == None → put those fields in a "General" sub-tab
+ if None in subcats:
+ general_tab = QWidget()
+ general_form = QFormLayout(general_tab)
+ self._populate_form(general_form, subcats[None])
+ subtabs.addTab(general_tab, "General")
+
+ for subcat_name, fields in subcats.items():
+ if subcat_name is None:
+ continue
+ sub_widget = QWidget()
+ sub_form = QFormLayout(sub_widget)
+ self._populate_form(sub_form, fields)
+ subtabs.addTab(sub_widget, self.label(f"{category}.{subcat_name}"))
+ else:
+ # Only a single bucket (no sub-tabs)
+ only_fields = subcats.get(None, [])
+ form = QFormLayout()
+ cat_layout.addLayout(form)
+ self._populate_form(form, only_fields)
+
+ tabs.addTab(cat_widget, self.label(category))
+
+ # Buttons row (Reset / Cancel / Save)
+ btn_row = QHBoxLayout()
+ btn_row.addStretch(1)
+ reset_btn = QPushButton("Reset")
+ save_btn = QPushButton("Save")
+ cancel_btn = QPushButton("Cancel")
+ btn_row.addWidget(reset_btn)
+ btn_row.addWidget(cancel_btn)
+ btn_row.addWidget(save_btn)
+ root.addLayout(btn_row)
+
+ # Wire buttons
+ def on_save():
+ self._update_from_widgets()
+ self.save()
+ dlg.accept()
+
+ def on_cancel():
+ dlg.reject()
+
+ def on_reset():
+ # Reset all values back to their defaults and keep the dialog open.
+ # Do not save immediately; let the user confirm with Save.
+ self.reset(save=False)
+
+ save_btn.clicked.connect(on_save)
+ cancel_btn.clicked.connect(on_cancel)
+ reset_btn.clicked.connect(on_reset)
+
+ dlg.exec_()
+
+ # ------------------------------------------------------------------
+ # Import / Export
+ # ------------------------------------------------------------------
+
+ def _import_dict(self, imported: dict[str, Any]) -> bool:
+ if not isinstance(imported, dict):
+ print(f"[Configuration] Imported configuration is not a dict: {type(imported)}")
+ return False
+
+ collected_keys: list[str] = []
+
+ def _apply(prefix: str, node: dict[str, Any]) -> None:
+ for key, value in node.items():
+ if isinstance(value, dict):
+ new_prefix = f"{prefix}.{key}" if prefix else key
+ _apply(new_prefix, value)
+ else:
+ full_key = f"{prefix}.{key}" if prefix else key
+ # Use the existing setter so we don't stomp entire blocks
+ self.set(full_key, value)
+ collected_keys.append(full_key)
+
+ _apply("", imported)
+
+ # After applying, reload each key recursively with imported values
+ for full_key in collected_keys:
+ node = imported
+ parts = full_key.split(".")
+ for part in parts[:-1]:
+ node = node.get(part, {})
+ value = node.get(parts[-1], None)
+ self.reload(full_key, value)
+
+ # Persist and notify listeners via save()
+ self.save()
+ return True
+
+ def import_from_path(self, path: str) -> bool:
+ try:
+ with open(path, "r", encoding="utf-8") as f:
+ imported = json.load(f)
+ except Exception as e:
+ print(f"[Configuration] Failed importing configuration from {path}: {e}")
+ return False
+
+ return self._import_dict(imported)
+
+ def import_cfg(self, parent: Optional[QWidget] = None) -> bool:
+ # Allow this method to be called as a Qt slot callback (clicked(bool)), where parent might be a bool
+ if not isinstance(parent, QWidget):
+ parent = self._parent if isinstance(self._parent, QWidget) else None
+ path, _ = QFileDialog.getOpenFileName(
+ parent,
+ "Import configuration",
+ "",
+ "Config files (*.cfg);;All files (*)",
+ )
+ if not path:
+ return False # User cancelled
+
+ try:
+ with open(path, "r", encoding="utf-8") as f:
+ imported = json.load(f)
+ except Exception as e:
+ print(f"[Configuration] Failed importing configuration from {path}: {e}")
+ return False
+
+ return self._import_dict(imported)
+
+ def export_cfg(self, parent: Optional[QWidget] = None) -> bool:
+ # Allow this method to be called as a Qt slot callback (clicked(bool)), where parent might be a bool
+ if not isinstance(parent, QWidget):
+ parent = self._parent if isinstance(self._parent, QWidget) else None
+ path, _ = QFileDialog.getSaveFileName(
+ parent,
+ "Export configuration",
+ "configuration.cfg",
+ "Config files (*.cfg);;All files (*)",
+ )
+ if not path:
+ return False # User cancelled
+
+ # Ensure latest config is written to disk
+ self.save()
+
+ config_dir = self._get_config_dir()
+ src = os.path.join(config_dir, self._filename)
+
+ try:
+ shutil.copy2(src, path)
+ return True
+ except Exception as e:
+ print(f"[Configuration] Failed exporting configuration to {path}: {e}")
+ return False
+
+ # ------------------------------------------------------------------
+ # Provisioning
+ # ------------------------------------------------------------------
+
+ def provision(self) -> int:
+ app: Application | None = QApplication.instance()
+ appname = app.applicationName() if app is not None else "unknown"
+ host = self.get("provisioning.host", "") or ""
+ token = self.get("provisioning.token", "") or ""
+ appid = self.get("provisioning.appid", appname) or "unknown"
+
+ if not host:
+ print("[Configuration] provisioning.host is not set; cannot provision.", file=sys.stderr)
+ return 1
+
+ # Allow host to be either full URL or just hostname. If no scheme, default to https.
+ url = host.strip()
+ if not url.lower().startswith(("http://", "https://")):
+ url = "https://" + url
+
+ # Helper and Tools are always available because Configuration enforces helper in __init__
+ helper = self._helper # type: ignore[attr-defined]
+ tools = Tools(helper=helper)
+
+ # Identify device
+ try:
+ serial = helper.get_serial() or ""
+ except Exception:
+ serial = ""
+
+ try:
+ os_name = helper.get_os() or ""
+ except Exception:
+ os_name = ""
+
+ try:
+ hostname = tools.host() or ""
+ except Exception:
+ hostname = ""
+
+ private_ip = ""
+ try:
+ ips = tools.ip() or []
+ if ips:
+ private_ip = ips[0]
+ except Exception:
+ private_ip = ""
+
+ mac = ""
+ try:
+ macs = tools.mac() or []
+ if macs:
+ mac = macs[0]
+ except Exception:
+ mac = ""
+
+ public_ip = ""
+ try:
+ public_ip = tools.wan() or ""
+ except Exception:
+ public_ip = ""
+
+ payload = {
+ "appid": appid or app.applicationName() or "unknown",
+ "serial": serial,
+ "mac": mac,
+ "hostname": hostname,
+ "private_ip": private_ip,
+ "public_ip": public_ip,
+ "os": os_name,
+ }
+
+ data = json.dumps(payload).encode("utf-8")
+
+ headers = {
+ "Content-Type": "application/json",
+ }
+ if token:
+ headers["Authorization"] = f"Bearer {token}"
+
+ req = _urlrequest.Request(url, data=data, headers=headers, method="POST")
+
+ try:
+ with _urlrequest.urlopen(req, timeout=10) as resp:
+ body = resp.read()
+ except _urlerror.URLError as e:
+ print(f"[Configuration] Provisioning request failed: {e}", file=sys.stderr)
+ return 1
+
+ try:
+ imported = json.loads(body.decode("utf-8"))
+ except Exception as e:
+ print(f"[Configuration] Failed to parse provisioning response as JSON: {e}", file=sys.stderr)
+ return 1
+
+ if not isinstance(imported, dict):
+ print(f"[Configuration] Provisioning response is not a JSON object: {type(imported)}", file=sys.stderr)
+ return 1
+
+ # Use the existing internal import helper so we don't stomp entire blocks
+ ok = self._import_dict(imported)
+
+ # Return code
+ return 0 if ok else 1
+
+ # ------------------------------------------------------------------
+ # Internal helpers
+ # ------------------------------------------------------------------
+
+ def _build_widget_for_key(self, full_key: str) -> Tuple[QWidget, str]:
+ meta = self._schema.get(full_key, {})
+ widget_type = meta.get("widget")
+ options = meta.get("options", {}) or {}
+ current_value = self.get(full_key)
+
+ # Infer widget type if not explicitly set
+ if widget_type is None:
+ if isinstance(current_value, bool):
+ widget_type = "checkbox"
+ elif isinstance(current_value, int):
+ widget_type = "spin"
+ elif isinstance(current_value, str) and "choices" in options:
+ widget_type = "select"
+ else:
+ widget_type = "text"
+
+ # Build widget via Form helpers
+ if widget_type == "checkbox":
+ w = Form.checkbox(checked=bool(current_value))
+ elif widget_type in ("spin", "integer", "number"):
+ w = Form.spin(
+ value=int(current_value) if current_value is not None else 0,
+ minimum=options.get("min", 0),
+ maximum=options.get("max", 65535),
+ )
+ elif widget_type == "select":
+ w = Form.select(
+ items=options.get("choices", []),
+ current=current_value,
+ )
+ elif widget_type == "password":
+ w = Form.password(
+ text=current_value or "",
+ placeholder=options.get("placeholder", "")
+ )
+ elif widget_type == "color":
+ w = Form.color(initial=current_value or "#000000")
+ elif widget_type == "picture":
+ w = Form.picture(initial=current_value or "")
+ elif widget_type == "file":
+ w = Form.file(
+ initial=current_value or "",
+ caption=options.get("caption", "Select File"),
+ directory=options.get("directory", ""),
+ filter=options.get("filter", "All Files (*)"),
+ as_base64=options.get("as_base64", False),
+ on_changed=options.get("on_changed", None),
+ )
+ elif widget_type == "wifi":
+ w = Form.wifi(
+ current=current_value or ""
+ )
+ elif widget_type == "button":
+ w = Form.button(
+ label=options.get("label", "Button"),
+ icon=options.get("icon", None),
+ action=options.get("action", None)
+ )
+ else:
+ # default: text line
+ w = Form.text(
+ text=current_value or "",
+ placeholder=options.get("placeholder", "")
+ )
+
+ # --- generic on_changed wiring for basic widgets ---
+ callback = options.get("on_changed")
+ if callback is not None:
+ from PyQt5.QtWidgets import QLineEdit, QComboBox, QCheckBox, QSpinBox
+
+ try:
+ if isinstance(w, QCheckBox):
+ w.toggled.connect(callback)
+ elif isinstance(w, QLineEdit):
+ w.textChanged.connect(callback)
+ elif isinstance(w, QSpinBox):
+ w.valueChanged.connect(callback)
+ elif isinstance(w, QComboBox):
+ w.currentTextChanged.connect(callback)
+
+ # Initial call so state (e.g. visibility) is correct
+ callback(current_value)
+ except Exception as e:
+ print(f"[Configuration] on_changed for {full_key} failed: {e}")
+
+ # Register this widget for saving (used by _update_from_widgets)
+ self._widgets[full_key] = w
+
+ label_text = options.get(
+ "label",
+ self.label(full_key)
+ )
+ return w, label_text
+
+ def _populate_form(
+ self,
+ form_layout,
+ fields: list[Tuple[str, dict[str, Any]]]
+ ) -> None:
+ for full_key, _meta in fields:
+ w, label_text = self._build_widget_for_key(full_key)
+ label_widget = QLabel(label_text)
+ form_layout.addRow(label_widget, w)
+
+ # Keep track of the label widget so we can hide/show it
+ self._widget_labels[full_key] = label_widget
+
+ def _nice_label(self, raw: str) -> str:
+ s = raw.replace("_", " ").replace("-", " ")
+ if not s:
+ return ""
+ return s[0].upper() + s[1:]
+
+ def _update_from_widgets(self) -> None:
+ for key, widget in self._widgets.items():
+ value: Any
+
+ if isinstance(widget, QLineEdit):
+ value = widget.text()
+ elif isinstance(widget, QComboBox):
+ value = widget.currentText()
+ elif isinstance(widget, QCheckBox):
+ value = widget.isChecked()
+ elif isinstance(widget, QSpinBox):
+ value = widget.value()
+ else:
+ if hasattr(widget, "hex") and callable(getattr(widget, "hex")):
+ value = widget.hex()
+ elif hasattr(widget, "value") and callable(getattr(widget, "value")):
+ value = widget.value()
+ else:
+ continue
+
+ self.set(key, value)
+
+ def _get_config_dir(self) -> str:
+ """Return the directory used to store configuration files.
+
+ Prefer Helper.get_config_path() (new, OS-appropriate location).
+ Fall back to the legacy project-root `config/` directory if the
+ helper doesn't provide get_config_path().
+ """
+ # Preferred: OS-appropriate config directory via Helper
+ try:
+ # Let Helper resolve the app name (or use the QApplication name)
+ return self._helper.get_config_path(ensure=True, scope="system")
+ except Exception:
+ # Legacy fallback (kept for backward compatibility)
+ return os.path.join(self.root_dir, "config")
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/database/__init__.py b/dist/macos/Replicator.app/Contents/Resources/core/database/__init__.py
new file mode 100644
index 00000000..287f550f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/database/__init__.py
@@ -0,0 +1,8 @@
+#!/usr/bin/env python3
+# src/core/filesystem/__init__.py
+
+from .sqlite import SQLite
+
+__version__ = "1.0.0"
+
+__all__ = ["SQLite"]
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/database/sqlite.py b/dist/macos/Replicator.app/Contents/Resources/core/database/sqlite.py
new file mode 100644
index 00000000..7489d1c3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/database/sqlite.py
@@ -0,0 +1,324 @@
+#!/usr/bin/env python3
+# src/core/database/sqlite.py
+
+from __future__ import annotations
+
+import os
+import sqlite3
+from contextlib import contextmanager
+from typing import Any, Optional, Iterable, Dict, List, Tuple, Union
+
+from PyQt5.QtWidgets import QApplication
+
+try:
+ from core.helper import Helper
+ from core.log import Log
+except ImportError:
+ from helper import Helper
+ from log import Log
+
+
+Row = Dict[str, Any]
+
+
+class SQLite:
+ """
+ Lightweight SQLite wrapper for corePY.
+
+ Features:
+ - Auto-wires Helper/Logger from QApplication when available
+ - Centralized DB path handling
+ - Connection lifecycle (connect/close)
+ - CRUD for tables (exists, create, drop, list, columns)
+ - CRUD for records (insert/select/update/delete/upsert)
+ - Parameterized queries (safe)
+ - Transaction context manager
+ """
+
+ def __init__(
+ self,
+ *,
+ helper: Optional[Helper] = None,
+ logger: Optional[Log] = None,
+ db_path: Optional[str] = None,
+ app_name: Optional[str] = None,
+ ensure_dir: bool = True,
+ timeout: float = 30.0,
+ ):
+ super().__init__()
+
+ # --- auto-wire from QApplication if not provided ---
+ if helper is None or logger is None:
+ app = QApplication.instance()
+ if app is not None:
+ helper = helper or getattr(app, "helper", None)
+ logger = logger or getattr(app, "logger", None)
+
+ self._helper: Helper = helper or Helper()
+ self._logger: Optional[Log] = logger
+
+ self._timeout = float(timeout)
+
+ # Determine DB path
+ self._db_path = self._resolve_db_path(
+ db_path=db_path,
+ app_name=app_name or getattr(QApplication.instance(), "name", None) or "corePY",
+ )
+
+ if ensure_dir:
+ self._ensure_parent_dir(self._db_path)
+
+ self._conn: Optional[sqlite3.Connection] = None
+
+ # ------------------------------------------------------------------
+ # Path / connection
+ # ------------------------------------------------------------------
+
+ def _resolve_db_path(self, *, db_path: Optional[str], app_name: str) -> str:
+ if db_path:
+ return os.path.abspath(db_path)
+
+ # Prefer corePY's config/data folder if Helper exposes one; else fallback to ~/./
+ # Adjust this to your Helper conventions if you already have something like get_path("config") etc.
+ base = None
+ try:
+ # If your helper has a dedicated config/data directory, use it
+ # Example patterns used in your projects: helper.get_path("config"), helper.get_path("Data"), etc.
+ base = self._helper.get_path("config") # may be None depending on your helper
+ except Exception:
+ base = None
+
+ if not base or not os.path.isdir(base):
+ home = os.path.expanduser("~")
+ base = os.path.join(home, f".{app_name.lower()}")
+
+ return os.path.join(base, "database.sqlite")
+
+ def _ensure_parent_dir(self, path: str) -> None:
+ parent = os.path.dirname(path)
+ if parent and not os.path.isdir(parent):
+ os.makedirs(parent, exist_ok=True)
+
+ @property
+ def path(self) -> str:
+ return self._db_path
+
+ def connect(self) -> sqlite3.Connection:
+ if self._conn is not None:
+ return self._conn
+
+ self._log(f"[SQLite] opening: {self._db_path}", level="debug", channel="sqlite")
+
+ conn = sqlite3.connect(
+ self._db_path,
+ timeout=self._timeout,
+ isolation_level=None, # we manage transactions manually when needed
+ check_same_thread=False,
+ )
+ conn.row_factory = sqlite3.Row
+
+ # IMPORTANT: set _conn before running any PRAGMAs to avoid recursion
+ self._conn = conn
+
+ # Sensible defaults (execute directly on the connection to avoid calling self.execute/connect)
+ conn.execute("PRAGMA foreign_keys = ON;")
+ conn.execute("PRAGMA journal_mode = WAL;") # good concurrency for desktop apps
+ conn.execute("PRAGMA synchronous = NORMAL;") # good balance for WAL
+
+ return conn
+
+ def close(self) -> None:
+ if self._conn is not None:
+ try:
+ self._conn.close()
+ except Exception:
+ pass
+ self._conn = None
+
+ # ------------------------------------------------------------------
+ # Low-level query helpers
+ # ------------------------------------------------------------------
+
+ def execute(self, sql: str, params: Union[Tuple[Any, ...], Dict[str, Any], None] = None) -> sqlite3.Cursor:
+ conn = self.connect()
+ cur = conn.cursor()
+ if params is None:
+ cur.execute(sql)
+ else:
+ cur.execute(sql, params)
+ return cur
+
+ def executemany(self, sql: str, seq: Iterable[Union[Tuple[Any, ...], Dict[str, Any]]]) -> int:
+ conn = self.connect()
+ cur = conn.cursor()
+ cur.executemany(sql, seq)
+ return cur.rowcount
+
+ def query(self, sql: str, params: Union[Tuple[Any, ...], Dict[str, Any], None] = None) -> List[Row]:
+ cur = self.execute(sql, params)
+ rows = cur.fetchall()
+ return [dict(r) for r in rows]
+
+ def one(self, sql: str, params: Union[Tuple[Any, ...], Dict[str, Any], None] = None) -> Optional[Row]:
+ cur = self.execute(sql, params)
+ r = cur.fetchone()
+ return dict(r) if r else None
+
+ def scalar(self, sql: str, params: Union[Tuple[Any, ...], Dict[str, Any], None] = None) -> Any:
+ cur = self.execute(sql, params)
+ r = cur.fetchone()
+ if not r:
+ return None
+ # sqlite3.Row is indexable
+ return r[0]
+
+ @contextmanager
+ def transaction(self):
+ """
+ Usage:
+ with db.transaction():
+ db.insert(...)
+ db.update(...)
+ """
+ conn = self.connect()
+ try:
+ conn.execute("BEGIN;")
+ yield
+ conn.execute("COMMIT;")
+ except Exception:
+ conn.execute("ROLLBACK;")
+ raise
+
+ # ------------------------------------------------------------------
+ # Table CRUD
+ # ------------------------------------------------------------------
+
+ def list_tables(self) -> List[str]:
+ rows = self.query(
+ "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name;"
+ )
+ return [r["name"] for r in rows]
+
+ def table_exists(self, table: str) -> bool:
+ r = self.scalar(
+ "SELECT 1 FROM sqlite_master WHERE type='table' AND name=? LIMIT 1;",
+ (table,),
+ )
+ return bool(r)
+
+ def drop_table(self, table: str) -> None:
+ self.execute(f'DROP TABLE IF EXISTS "{table}";')
+
+ def create_table(self, table: str, columns_sql: str) -> None:
+ """
+ columns_sql example:
+ "id INTEGER PRIMARY KEY, name TEXT NOT NULL, created_utc TEXT"
+ """
+ self.execute(f'CREATE TABLE IF NOT EXISTS "{table}" ({columns_sql});')
+
+ def columns(self, table: str) -> List[Row]:
+ # PRAGMA doesn't accept binding for identifiers; table must be trusted input
+ return self.query(f'PRAGMA table_info("{table}");')
+
+ # ------------------------------------------------------------------
+ # Record CRUD
+ # ------------------------------------------------------------------
+
+ def insert(self, table: str, data: Row) -> int:
+ keys = list(data.keys())
+ cols = ", ".join([f'"{k}"' for k in keys])
+ placeholders = ", ".join([f":{k}" for k in keys])
+ sql = f'INSERT INTO "{table}" ({cols}) VALUES ({placeholders});'
+ cur = self.execute(sql, data)
+ return int(cur.lastrowid or 0)
+
+ def select(
+ self,
+ table: str,
+ where: Optional[str] = None,
+ params: Union[Tuple[Any, ...], Dict[str, Any], None] = None,
+ *,
+ columns: Optional[List[str]] = None,
+ order_by: Optional[str] = None,
+ limit: Optional[int] = None,
+ offset: Optional[int] = None,
+ ) -> List[Row]:
+ cols = "*"
+ if columns:
+ cols = ", ".join([f'"{c}"' for c in columns])
+
+ sql = f'SELECT {cols} FROM "{table}"'
+ if where:
+ sql += f" WHERE {where}"
+ if order_by:
+ sql += f" ORDER BY {order_by}"
+ if limit is not None:
+ sql += f" LIMIT {int(limit)}"
+ if offset is not None:
+ sql += f" OFFSET {int(offset)}"
+ sql += ";"
+ return self.query(sql, params)
+
+ def update(self, table: str, data: Row, where: str, params: Union[Tuple[Any, ...], Dict[str, Any]]) -> int:
+ keys = list(data.keys())
+ set_clause = ", ".join([f'"{k}"=:{k}' for k in keys])
+ sql = f'UPDATE "{table}" SET {set_clause} WHERE {where};'
+
+ merged: Dict[str, Any]
+ if isinstance(params, dict):
+ merged = dict(params)
+ else:
+ # If params is tuple, we can't merge by name; require dict for update WHERE params
+ raise ValueError("SQLite.update requires WHERE params as dict for safe named binding.")
+
+ merged.update(data)
+ cur = self.execute(sql, merged)
+ return int(cur.rowcount or 0)
+
+ def delete(self, table: str, where: str, params: Union[Tuple[Any, ...], Dict[str, Any]]) -> int:
+ sql = f'DELETE FROM "{table}" WHERE {where};'
+ cur = self.execute(sql, params)
+ return int(cur.rowcount or 0)
+
+ def upsert(
+ self,
+ table: str,
+ data: Row,
+ conflict_columns: List[str],
+ update_columns: Optional[List[str]] = None,
+ ) -> None:
+ """
+ INSERT ... ON CONFLICT(...) DO UPDATE SET ...
+ """
+ keys = list(data.keys())
+ cols = ", ".join([f'"{k}"' for k in keys])
+ placeholders = ", ".join([f":{k}" for k in keys])
+
+ conflict = ", ".join([f'"{c}"' for c in conflict_columns])
+
+ if update_columns is None:
+ update_columns = [k for k in keys if k not in conflict_columns]
+
+ if update_columns:
+ set_clause = ", ".join([f'"{k}"=excluded."{k}"' for k in update_columns])
+ sql = (
+ f'INSERT INTO "{table}" ({cols}) VALUES ({placeholders}) '
+ f"ON CONFLICT({conflict}) DO UPDATE SET {set_clause};"
+ )
+ else:
+ sql = (
+ f'INSERT INTO "{table}" ({cols}) VALUES ({placeholders}) '
+ f"ON CONFLICT({conflict}) DO NOTHING;"
+ )
+
+ self.execute(sql, data)
+
+ # ------------------------------------------------------------------
+ # Logging
+ # ------------------------------------------------------------------
+
+ def _log(self, msg: str, *, level: str = "info", channel: str = "sqlite") -> None:
+ if self._logger is not None and hasattr(self._logger, "append"):
+ self._logger.append(msg, level=level, channel=channel) # type: ignore[call-arg]
+ else:
+ print(msg)
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/filesystem/__init__.py b/dist/macos/Replicator.app/Contents/Resources/core/filesystem/__init__.py
new file mode 100644
index 00000000..1d9dfde4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/filesystem/__init__.py
@@ -0,0 +1,9 @@
+#!/usr/bin/env python3
+# src/core/filesystem/__init__.py
+
+from .filesystem import FileSystem
+from .share import Share
+
+__version__ = "1.0.0"
+
+__all__ = ["FileSystem", "Share"]
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/filesystem/filesystem.py b/dist/macos/Replicator.app/Contents/Resources/core/filesystem/filesystem.py
new file mode 100644
index 00000000..a9902378
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/filesystem/filesystem.py
@@ -0,0 +1,382 @@
+#!/usr/bin/env python3
+# corePY/filesystem/filesystem.py
+
+from __future__ import annotations
+
+import os
+import shutil
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Optional, Union, Iterable, Tuple
+
+from PyQt5.QtWidgets import QApplication
+
+try:
+ from core.helper import Helper
+except ImportError:
+ from helper import Helper
+
+PathLike = Union[str, os.PathLike]
+
+@dataclass(frozen=True)
+class CommandSpec:
+ """A simple command container useful for logging/execution."""
+
+ argv: list[str]
+ label: str
+ sensitive: bool = False
+
+
+class FileSystem:
+ """Cross-platform filesystem helper.
+
+ Goals:
+ - Provide *basic* operations (read/copy/delete) for files/folders.
+ - Prefer native tools when asked to preserve metadata:
+ - Windows: robocopy (best for NTFS ACLs)
+ - Linux/macOS: rsync (best for POSIX perms/ACL/xattrs when supported)
+ - Fall back to Python stdlib when tools are missing.
+
+ Notes:
+ - UNC paths (\\\\server\\share\\path) are treated as normal paths on Windows.
+ - "preserve_metadata" is best-effort; some filesystems/mounts can't store all metadata.
+ """
+
+ def __init__(
+ self,
+ helper: Optional[Helper] = None,
+ logger: Optional[object] = None,
+ ):
+ super().__init__()
+
+ # --- auto-wire from QApplication if not provided ---
+ if helper is None or logger is None:
+ app = QApplication.instance()
+ if app is None:
+ raise RuntimeError("FileSystem must be created after QApplication/Application.")
+ helper = helper or app.helper # type: ignore[attr-defined]
+ logger = logger or getattr(app, "logger", None)
+
+ self._helper: Helper = helper
+ self._logger = logger
+ self._os: str = self._helper.get_os()
+
+ # Cache for tool capability detection
+ self._rsync_caps: Optional[dict[str, bool]] = None
+
+ # ------------------------------------------------------------------
+ # Small helpers
+ # ------------------------------------------------------------------
+
+ def _p(self, path: PathLike) -> str:
+ return str(path)
+
+ def _log(self, msg: str, level: str = "debug", channel: str = "filesystem") -> None:
+ if self._logger is not None and hasattr(self._logger, "append"):
+ try:
+ self._logger.append(msg, level=level, channel=channel) # type: ignore[call-arg]
+ return
+ except Exception:
+ pass
+ # Fallback: silent unless explicitly needed
+
+ def _rsync_capabilities(self) -> dict[str, bool]:
+ """Detect which rsync flags are supported on this host.
+
+ macOS often ships an older rsync that does not support -A/-X.
+ We detect capabilities once and cache them.
+ """
+ if self._rsync_caps is not None:
+ return self._rsync_caps
+
+ caps = {
+ "has_rsync": False,
+ "acls": False, # -A / --acls
+ "xattrs": False, # -X / --xattrs
+ "hardlinks": False, # -H / --hard-links
+ "numeric_ids": False, # --numeric-ids
+ }
+
+ rsync = shutil.which("rsync")
+ if not rsync:
+ self._rsync_caps = caps
+ return caps
+
+ caps["has_rsync"] = True
+
+ # Try `--help` (most reliable to see flag availability)
+ rc, out = self._helper.run([rsync, "--help"])
+ if rc != 0:
+ # If help fails, assume minimal support
+ self._rsync_caps = caps
+ return caps
+
+ help_text = out or ""
+
+ # Look for common help strings
+ if "--acls" in help_text or " -A" in help_text:
+ caps["acls"] = True
+ if "--xattrs" in help_text or " -X" in help_text:
+ caps["xattrs"] = True
+ if "--hard-links" in help_text or " -H" in help_text:
+ caps["hardlinks"] = True
+ if "--numeric-ids" in help_text:
+ caps["numeric_ids"] = True
+
+ self._rsync_caps = caps
+ return caps
+
+ def _build_rsync_args(self, *, allow_deletion: bool, preserve_metadata: bool) -> Optional[list[str]]:
+ """Build a safe rsync argv for the current host.
+
+ We always include `-a` (archive) when preserve_metadata=True.
+ Additional flags (-H/-A/-X/--numeric-ids) are appended only if supported.
+ """
+ caps = self._rsync_capabilities()
+ if not caps.get("has_rsync"):
+ return None
+
+ rsync = shutil.which("rsync") or "rsync"
+
+ args: list[str] = [rsync]
+
+ if preserve_metadata:
+ # archive mode preserves perms, times, symlinks, etc.
+ args.append("-a")
+ else:
+ # minimal recursion + times (best-effort)
+ args.extend(["-r", "-t"])
+
+ if caps.get("hardlinks"):
+ args.append("-H")
+ if preserve_metadata and caps.get("acls"):
+ args.append("-A")
+ if preserve_metadata and caps.get("xattrs"):
+ args.append("-X")
+ if preserve_metadata and caps.get("numeric_ids"):
+ args.append("--numeric-ids")
+
+ if allow_deletion:
+ args.append("--delete")
+
+ return args
+
+ def exists(self, path: PathLike) -> bool:
+ return os.path.exists(self._p(path))
+
+ def is_file(self, path: PathLike) -> bool:
+ return os.path.isfile(self._p(path))
+
+ def is_dir(self, path: PathLike) -> bool:
+ return os.path.isdir(self._p(path))
+
+ def mkdirs(self, path: PathLike) -> None:
+ os.makedirs(self._p(path), exist_ok=True)
+
+ # ------------------------------------------------------------------
+ # Read operations
+ # ------------------------------------------------------------------
+
+ def read_bytes(self, path: PathLike) -> bytes:
+ p = self._p(path)
+ with open(p, "rb") as f:
+ return f.read()
+
+ def read_text(self, path: PathLike, encoding: str = "utf-8", errors: str = "strict") -> str:
+ p = self._p(path)
+ with open(p, "r", encoding=encoding, errors=errors) as f:
+ return f.read()
+
+ # ------------------------------------------------------------------
+ # Delete operations
+ # ------------------------------------------------------------------
+
+ def delete(self, path: PathLike) -> bool:
+ """Delete a file or folder (recursive). Returns True if deleted, False if not found."""
+ p = self._p(path)
+ if not os.path.exists(p):
+ return False
+
+ if os.path.isdir(p) and not os.path.islink(p):
+ shutil.rmtree(p)
+ else:
+ os.remove(p)
+ return True
+
+ # ------------------------------------------------------------------
+ # Copy operations
+ # ------------------------------------------------------------------
+
+ def copy(
+ self,
+ src: PathLike,
+ dst: PathLike,
+ *,
+ preserve_metadata: bool = True,
+ allow_deletion: bool = False,
+ ) -> bool:
+ """Copy file or directory from src to dst.
+
+ If src is a directory:
+ - preserve_metadata=True prefers robocopy (Windows) or rsync (Linux/macOS)
+ - allow_deletion=True makes it a mirror (best-effort with stdlib fallback)
+
+ Returns:
+ - True on success
+ - False on failure
+ """
+ src_s = self._p(src)
+ dst_s = self._p(dst)
+
+ if not os.path.exists(src_s):
+ self._log(f"[FileSystem] copy: source not found: {src_s}", level="warning")
+ return False
+
+ try:
+ if os.path.isdir(src_s):
+ return self._copy_dir(src_s, dst_s, preserve_metadata=preserve_metadata, allow_deletion=allow_deletion)
+ else:
+ return self._copy_file(src_s, dst_s, preserve_metadata=preserve_metadata)
+ except Exception as e:
+ self._log(f"[FileSystem] copy failed: {src_s} -> {dst_s} ({e})", level="error")
+ return False
+
+ def _copy_file(self, src: str, dst: str, *, preserve_metadata: bool = True) -> bool:
+ self.mkdirs(os.path.dirname(dst) or ".")
+
+ # shutil.copy2 preserves basic metadata (mtime/atime + mode where possible)
+ if preserve_metadata:
+ shutil.copy2(src, dst)
+ else:
+ shutil.copy(src, dst)
+
+ return True
+
+ def _copy_dir(
+ self,
+ src: str,
+ dst: str,
+ *,
+ preserve_metadata: bool = True,
+ allow_deletion: bool = False,
+ ) -> bool:
+ # Prefer native tools when metadata matters
+ if preserve_metadata:
+ cmd = self.build_mirror_command(src, dst, allow_deletion=allow_deletion)
+ if cmd is not None:
+ self._log(f"[FileSystem] executing: {cmd.label}: {' '.join(cmd.argv)}")
+ rc, out = self._helper.run(cmd.argv)
+
+ # robocopy has special return codes: 0-7 are generally success-ish.
+ if cmd.argv and os.path.basename(cmd.argv[0]).lower().startswith("robocopy"):
+ ok = rc <= 7
+ else:
+ ok = rc == 0
+
+ if not ok:
+ self._log(f"[FileSystem] tool copy failed rc={rc}: {out}", level="warning")
+ return ok
+
+ # Fallback: Python stdlib directory copy
+ # Ensure destination exists
+ os.makedirs(dst, exist_ok=True)
+
+ # Copy/update files
+ for root, dirs, files in os.walk(src):
+ rel = os.path.relpath(root, src)
+ target_root = dst if rel == "." else os.path.join(dst, rel)
+ os.makedirs(target_root, exist_ok=True)
+
+ for d in dirs:
+ os.makedirs(os.path.join(target_root, d), exist_ok=True)
+
+ for f in files:
+ s = os.path.join(root, f)
+ t = os.path.join(target_root, f)
+ # copy2 for metadata best-effort
+ shutil.copy2(s, t)
+
+ # Mirror delete extras if requested (best-effort)
+ if allow_deletion:
+ self._mirror_delete_extras(src, dst)
+
+ return True
+
+ def _mirror_delete_extras(self, src: str, dst: str) -> None:
+ """Delete files/dirs that exist in dst but not in src (best-effort)."""
+ # Walk destination and compare
+ for root, dirs, files in os.walk(dst, topdown=False):
+ rel = os.path.relpath(root, dst)
+ src_root = src if rel == "." else os.path.join(src, rel)
+
+ # Remove files not present in src
+ for f in files:
+ d_path = os.path.join(root, f)
+ s_path = os.path.join(src_root, f)
+ if not os.path.exists(s_path):
+ try:
+ os.remove(d_path)
+ except Exception as e:
+ self._log(f"[FileSystem] mirror delete file failed: {d_path} ({e})", level="warning")
+
+ # Remove dirs not present in src
+ for d in dirs:
+ d_path = os.path.join(root, d)
+ s_path = os.path.join(src_root, d)
+ if not os.path.exists(s_path):
+ try:
+ shutil.rmtree(d_path)
+ except Exception as e:
+ self._log(f"[FileSystem] mirror delete dir failed: {d_path} ({e})", level="warning")
+
+ # ------------------------------------------------------------------
+ # Command generation (reusable by Replicator)
+ # ------------------------------------------------------------------
+
+ def build_mirror_command(self, src_dir: PathLike, dst_dir: PathLike, *, allow_deletion: bool) -> Optional[CommandSpec]:
+ """Build the best command to mirror src_dir -> dst_dir on this host.
+
+ Returns a CommandSpec or None if no suitable tool is available.
+ """
+ src = self._p(src_dir)
+ dst = self._p(dst_dir)
+
+ # -------------------------
+ # Windows: robocopy
+ # -------------------------
+ if self._os == "windows":
+ # robocopy is typically available in PATH on Windows
+ robocopy = shutil.which("robocopy") or "robocopy"
+
+ # /MIR implies /E + delete extras; /E copies subdirs including empty
+ # /COPY:DATSOU preserves Data, Attributes, Timestamps, Security (ACL), Owner, Auditing
+ # /DCOPY:DAT preserves directory timestamps
+ # /R /W keep retries low for UI responsiveness
+ args = [
+ robocopy,
+ src,
+ dst,
+ "/MIR" if allow_deletion else "/E",
+ "/COPY:DATSOU",
+ "/DCOPY:DAT",
+ "/R:1",
+ "/W:1",
+ ]
+ return CommandSpec(argv=args, label="robocopy")
+
+ # -------------------------
+ # Linux/macOS: rsync
+ # -------------------------
+ if self._os in ("linux", "macos"):
+ # Use trailing slashes to copy contents of src into dst
+ src_arg = src.rstrip("/") + "/"
+ dst_arg = dst.rstrip("/") + "/"
+
+ base = self._build_rsync_args(allow_deletion=allow_deletion, preserve_metadata=True)
+ if not base:
+ return None
+
+ args = base + [src_arg, dst_arg]
+ return CommandSpec(argv=args, label="rsync")
+
+ return None
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/filesystem/share.py b/dist/macos/Replicator.app/Contents/Resources/core/filesystem/share.py
new file mode 100644
index 00000000..8ff56eaa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/filesystem/share.py
@@ -0,0 +1,617 @@
+#!/usr/bin/env python3
+# src/core/filesystem/share.py
+
+"""Share mounting utilities for corePY (SMB-only).
+
+Goal
+----
+Provide a single, cross-platform interface for mounting/unmounting SMB shares.
+
+Supported protocol:
+ - SMB (CIFS / Windows shares)
+
+Backends
+--------
+Native OS tools (preferred):
+ * Linux: mount.cifs / umount
+ * macOS: mount_smbfs / umount
+ * Windows: net use
+
+Notes
+-----
+* Many mount operations require elevated privileges depending on target path.
+ This class does not force privilege escalation; you can pass `elevate=True`
+ to prefix commands with sudo on Unix-like systems.
+* On Windows, SMB mounting via `net use` maps network resources to a drive letter.
+ - If you pass a drive letter (e.g. `mount_point='Z:'`), it will map directly.
+ - If you pass a directory path, corePY will map the share to a free drive letter
+ and create a directory junction at `mount_point` pointing to that drive.
+"""
+
+from __future__ import annotations
+
+import os
+import platform
+import shlex
+import shutil
+import subprocess
+import time
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Dict, List, Optional, Tuple
+from urllib.parse import quote, unquote
+
+
+class ShareError(RuntimeError):
+ pass
+
+
+@dataclass
+class ShareAuth:
+ username: Optional[str] = None
+ password: Optional[str] = None
+ domain: Optional[str] = None
+
+
+@dataclass
+class ShareTarget:
+ protocol: str # smb
+ host: str
+ path: str = "" # share name and optional subpath (e.g. "Share" or "Share/dir")
+ port: Optional[int] = None # unused for smb, kept for compatibility
+
+ def normalized_protocol(self) -> str:
+ p = (self.protocol or "").strip().lower()
+ if p in {"cifs"}:
+ return "smb"
+ return p
+
+ def decoded_path(self) -> str:
+ # Accept either raw share names or percent-encoded ones (e.g. "Test%20with%20spaces").
+ try:
+ return unquote(self.path or "")
+ except Exception:
+ return self.path or ""
+
+
+class Share:
+ """Mount and unmount SMB shares."""
+
+ def __init__(self, logger=None, bin_root: Optional[str] = None):
+ self._logger = logger
+ self._bin_root = Path(bin_root) if bin_root else None
+
+ self._backend: Optional[str] = None # native_smb_linux|native_smb_macos|native_smb_windows
+ self._last_cmd: Optional[List[str]] = None
+ self._last_mount_point: Optional[str] = None
+ self._last_probe_ok: Optional[bool] = None
+ self._last_probe_message: Optional[str] = None
+ self._last_windows_drive: Optional[str] = None
+ self._last_windows_junction: Optional[str] = None
+ self._last_windows_unc: Optional[str] = None
+
+ # ---------------------------------------------------------------------
+ # Public API
+ # ---------------------------------------------------------------------
+
+ def mount(
+ self,
+ protocol: str,
+ host: str,
+ remote: str,
+ mount_point: str,
+ auth: Optional[ShareAuth] = None,
+ *,
+ port: Optional[int] = None,
+ options: Optional[Dict[str, str]] = None,
+ read_only: bool = False,
+ elevate: bool = False,
+ timeout: int = 60,
+ ) -> None:
+ """Mount a share.
+
+ Parameters
+ ----------
+ protocol:
+ smb
+ host:
+ Hostname or IP.
+ remote:
+ SMB share name and optional path (e.g. "Share" or "Share/dir").
+ mount_point:
+ Local mount directory (Linux/macOS) or drive letter like "Z:" (Windows).
+ auth:
+ Username/password/domain.
+ options:
+ Backend-specific mount options.
+ read_only:
+ Attempt to mount read-only.
+ elevate:
+ On Linux/macOS, prefix with sudo.
+ timeout:
+ Seconds.
+ """
+ auth = auth or ShareAuth()
+ options = options or {}
+
+ target = ShareTarget(protocol=protocol, host=host, path=remote or "", port=port)
+ p = target.normalized_protocol()
+
+ # Normalize remote path early (accept percent-encoded share names)
+ target.path = target.decoded_path()
+
+ self._log_debug(f"Mount request: protocol={p} host={host} remote={remote} mount_point={mount_point}")
+
+ if p != "smb":
+ raise ShareError(f"Unsupported protocol: {protocol}. Supported protocol(s): smb")
+
+ # Ensure mountpoint exists for directory-based mounts
+ # On Windows, directory mounts are implemented as symlinks and any pre-created empty directory will be replaced.
+ if not self._is_windows_drive_letter(mount_point):
+ Path(mount_point).mkdir(parents=True, exist_ok=True)
+
+ self._mount_smb(target, mount_point, auth, options, read_only, elevate, timeout)
+
+ # Probe the mount to confirm it is usable.
+ ok, msg = self._probe_mount(mount_point, timeout=timeout)
+ self._last_probe_ok = ok
+ self._last_probe_message = msg
+ self._log_debug(f"Share mount probe: mount_point={mount_point} ok={ok} msg={msg}")
+
+ if not ok:
+ raise ShareError(f"Mount completed but mount probe failed: {msg}")
+
+ self._last_mount_point = mount_point
+
+ def umount(self, mount_point: Optional[str] = None, *, elevate: bool = False, timeout: int = 60) -> None:
+ """Unmount a share."""
+ mp = mount_point or self._last_mount_point
+ if not mp:
+ raise ShareError("No mount point provided, and no previous mount point is known.")
+
+ self._log_debug(f"Unmount request: mount_point={mp} backend={self._backend}")
+
+ # Best-effort probe before unmount.
+ ok_before, msg_before = self._probe_mount(mp, timeout=min(5, timeout))
+ self._log_debug(f"Share pre-unmount probe: mount_point={mp} ok={ok_before} msg={msg_before}")
+
+ system = platform.system().lower()
+ if system == "windows":
+ # If we created a directory symlink, remove it first, then disconnect.
+ drive: Optional[str] = None
+ unc: Optional[str] = None
+
+ if mp and not self._is_windows_drive_letter(mp):
+ # Directory-style mount (symlink)
+ junction = mp
+ unc = self._last_windows_unc
+ try:
+ if junction and os.path.exists(junction):
+ # Remove symlink directory (best-effort)
+ self._run(["cmd", "/c", "rmdir", "/S", "/Q", junction], timeout=timeout)
+ except Exception:
+ pass
+ else:
+ # Drive-letter mount
+ drive = mp
+
+ if drive:
+ # Example: net use Z: /delete /y
+ self._run(["net", "use", drive, "/delete", "/y"], timeout=timeout)
+
+ if unc:
+ # Example: net use \\server\Share /delete /y
+ self._run(["net", "use", unc, "/delete", "/y"], timeout=timeout)
+
+ # Clear remembered mapping
+ self._last_windows_drive = None
+ self._last_windows_junction = None
+ self._last_windows_unc = None
+
+ ok_after, msg_after = self._probe_mount(mp, timeout=min(5, timeout))
+ self._log_debug(f"Share post-unmount probe: mount_point={mp} ok={ok_after} msg={msg_after}")
+ return
+
+ # Linux/macOS
+ umount_bin = shutil.which("umount") or "umount"
+ cmd = (["sudo"] if elevate else []) + [umount_bin, mp]
+ self._run(cmd, timeout=timeout)
+
+ ok_after, msg_after = self._probe_mount(mp, timeout=min(5, timeout))
+ self._log_debug(f"Share post-unmount probe: mount_point={mp} ok={ok_after} msg={msg_after}")
+
+ # ---------------------------------------------------------------------
+ # SMB
+ # ---------------------------------------------------------------------
+
+ def _mount_smb(
+ self,
+ target: ShareTarget,
+ mount_point: str,
+ auth: ShareAuth,
+ options: Dict[str, str],
+ read_only: bool,
+ elevate: bool,
+ timeout: int,
+ ) -> None:
+ system = platform.system().lower()
+
+ if system == "windows":
+ self._backend = "native_smb_windows"
+
+ # `net use` supports mapping to a drive letter.
+ # If the caller provided a directory path, we:
+ # 1) create an authenticated connection to the UNC path (no drive letter)
+ # 2) create a directory symlink at mount_point pointing to that UNC path
+ #
+ # IMPORTANT: `mklink /J` (junction) cannot target mapped drives / UNC paths
+ # and will fail with "Local volumes are required...". For UNC we must use
+ # a directory symbolic link (`mklink /D`).
+
+ unc = self._smb_unc(target.host, target.path)
+
+ junction_path: Optional[str] = None
+ drive_letter: Optional[str] = None
+
+ if self._is_windows_drive_letter(mount_point):
+ # Drive-letter style mount
+ drive_letter = mount_point.upper()
+
+ cmd = ["net", "use", drive_letter, unc]
+ if auth.username:
+ if auth.domain:
+ cmd += [f"/user:{auth.domain}\\{auth.username}"]
+ else:
+ cmd += [f"/user:{auth.username}"]
+ if auth.password is not None:
+ cmd += [auth.password]
+ cmd += ["/persistent:no"]
+
+ # Retry once on 1219 by clearing existing connections to that host
+ try:
+ self._run(cmd, timeout=timeout)
+ except ShareError as e:
+ if "1219" in str(e):
+ self._log_debug(f"Windows SMB: detected error 1219; clearing connections to host {target.host} and retrying")
+ try:
+ self._run(["net", "use", f"\\\\{target.host}\\*", "/delete", "/y"], timeout=timeout)
+ except Exception:
+ pass
+ self._run(cmd, timeout=timeout)
+ else:
+ raise
+
+ self._last_windows_drive = drive_letter
+ self._last_windows_junction = None
+ self._last_windows_unc = None
+ return
+
+ # Directory-style mount (symlink to UNC)
+ junction_path = mount_point
+
+ # 1) Create (or refresh) an authenticated connection to the UNC path
+ # without assigning a drive letter.
+ cmd = ["net", "use", unc]
+ if auth.username:
+ if auth.domain:
+ cmd += [f"/user:{auth.domain}\\{auth.username}"]
+ else:
+ cmd += [f"/user:{auth.username}"]
+ # Avoid interactive password prompt: pass empty string when password is None.
+ cmd += [(auth.password if auth.password is not None else "")]
+ cmd += ["/persistent:no"]
+
+ try:
+ self._run(cmd, timeout=timeout)
+ except ShareError as e:
+ if "1219" in str(e):
+ self._log_debug(f"Windows SMB: detected error 1219; clearing connections to host {target.host} and retrying")
+ try:
+ self._run(["net", "use", f"\\\\{target.host}\\*", "/delete", "/y"], timeout=timeout)
+ except Exception:
+ pass
+ self._run(cmd, timeout=timeout)
+ else:
+ raise
+
+ # 2) Create a directory symlink pointing to the UNC path.
+ try:
+ if os.path.exists(junction_path):
+ self._run(["cmd", "/c", "rmdir", "/S", "/Q", junction_path], timeout=timeout)
+
+ # NOTE: Creating symlinks may require admin privileges unless Developer Mode is enabled.
+ self._run(["cmd", "/c", "mklink", "/D", junction_path, unc], timeout=timeout)
+
+ self._last_windows_drive = None
+ self._last_windows_junction = junction_path
+ self._last_windows_unc = unc
+ except Exception:
+ # Best-effort rollback of the UNC connection
+ try:
+ self._run(["net", "use", unc, "/delete", "/y"], timeout=timeout)
+ except Exception:
+ pass
+ raise
+
+ return
+
+ if system == "darwin":
+ self._backend = "native_smb_macos"
+ mount_smbfs = self._bin("mount_smbfs")
+
+ # Format: //user:pass@server/share /mnt/point
+ userinfo = ""
+ if auth.username:
+ userinfo = auth.username
+ if auth.password:
+ userinfo += ":" + self._url_escape(auth.password)
+ userinfo += "@"
+
+ # For SMB URLs, the path portion must be URL-encoded (but keep '/').
+ path = (target.path or "").lstrip("/")
+ path = quote(path, safe="/")
+ url = f"//{userinfo}{target.host}/{path}"
+ if read_only:
+ cmd = (["sudo"] if elevate else []) + [mount_smbfs, "-o", "ro", url, mount_point]
+ else:
+ cmd = (["sudo"] if elevate else []) + [mount_smbfs, url, mount_point]
+
+ self._run(cmd, timeout=timeout)
+ return
+
+ # Linux
+ self._backend = "native_smb_linux"
+ mount_cifs = self._bin("mount.cifs")
+
+ share, subpath = self._split_share_and_subpath(target.path)
+ if not share:
+ raise ShareError("SMB mount requires a share name (remote='Share' or 'Share/subpath').")
+
+ unc = f"//{target.host}/{share}"
+
+ mount_opts: Dict[str, str] = {}
+ if auth.username is not None:
+ mount_opts["username"] = auth.username
+ if auth.password is not None:
+ mount_opts["password"] = auth.password
+ if auth.domain:
+ mount_opts["domain"] = auth.domain
+ if read_only:
+ mount_opts["ro"] = ""
+
+ # Allow caller overrides
+ mount_opts.update(options)
+
+ opts_str = self._kv_to_mount_opts(mount_opts)
+
+ cmd = (["sudo"] if elevate else []) + [mount_cifs, unc, mount_point]
+ if opts_str:
+ cmd += ["-o", opts_str]
+
+ self._run(cmd, timeout=timeout)
+
+ # If a subpath was requested, verify it exists (informational only)
+ if subpath:
+ check_path = os.path.join(mount_point, subpath)
+ if not os.path.exists(check_path):
+ self._log_debug(f"Warning: SMB subpath does not exist after mount: {check_path}")
+
+ # ---------------------------------------------------------------------
+ # Helpers
+ # ---------------------------------------------------------------------
+
+ def _run(self, cmd: List[str], *, timeout: int) -> None:
+ self._last_cmd = cmd
+ printable = [shlex.quote(str(c)) for c in cmd]
+ self._log_debug("Executing: " + " ".join(printable))
+
+ try:
+ # On Windows, launching console utilities from a GUI process can flash a console window.
+ # Use CREATE_NO_WINDOW / SW_HIDE best-effort to avoid visible windows.
+ run_kwargs: Dict[str, object] = {
+ "stdout": subprocess.PIPE,
+ "stderr": subprocess.PIPE,
+ "text": True,
+ "timeout": timeout,
+ "check": False,
+ "stdin": subprocess.DEVNULL,
+ }
+
+ if platform.system().lower() == "windows":
+ # Hide console window (best-effort)
+ create_no_window = getattr(subprocess, "CREATE_NO_WINDOW", 0)
+ if create_no_window:
+ run_kwargs["creationflags"] = int(create_no_window)
+
+ # Some Windows builds still flash; STARTUPINFO can help in many cases.
+ try:
+ si = subprocess.STARTUPINFO() # type: ignore[attr-defined]
+ si.dwFlags |= getattr(subprocess, "STARTF_USESHOWWINDOW", 1)
+ si.wShowWindow = getattr(subprocess, "SW_HIDE", 0)
+ run_kwargs["startupinfo"] = si
+ except Exception:
+ pass
+
+ completed = subprocess.run(cmd, **run_kwargs) # type: ignore[arg-type]
+ except FileNotFoundError as e:
+ raise ShareError(f"Required executable not found: {cmd[0]}") from e
+ except subprocess.TimeoutExpired as e:
+ raise ShareError(f"Command timed out after {timeout}s: {' '.join(cmd)}") from e
+
+ if completed.returncode != 0:
+ msg = (completed.stderr or completed.stdout or "").strip()
+ raise ShareError(f"Mount command failed (code {completed.returncode}): {msg}")
+
+ if completed.stdout:
+ self._log_debug(completed.stdout.strip())
+
+ def _bin(self, name: str) -> str:
+ """Resolve an executable.
+
+ Resolution order:
+ 1) PATH
+ 2) Bundled binary under src/bin/[name]/[OS]/[arch]/(bin/)[exe] (best-effort)
+ """
+ candidates = [name]
+ if platform.system().lower() == "windows" and not name.lower().endswith(".exe"):
+ candidates.insert(0, name + ".exe")
+
+ # PATH first
+ for c in candidates:
+ p = shutil.which(c)
+ if p:
+ return p
+
+ # Bundled fallback
+ root = self._resolve_bin_root()
+ os_name = self._os_folder()
+ arch = self._arch_folder()
+
+ for c in candidates:
+ bundled_bin = root / name / os_name / arch / "bin" / c
+ bundled_flat = root / name / os_name / arch / c
+ for bundled in (bundled_bin, bundled_flat):
+ if bundled.exists() and bundled.is_file():
+ return str(bundled)
+
+ return candidates[0]
+
+ def _resolve_bin_root(self) -> Path:
+ if self._bin_root:
+ return self._bin_root
+
+ here = Path(__file__).resolve()
+
+ search_roots: List[Path] = []
+ try:
+ search_roots.append(Path.cwd())
+ except Exception:
+ pass
+
+ search_roots.append(here.parent)
+ search_roots.extend(list(here.parents))
+ search_roots.append(here.parent.parent)
+ search_roots.append(here.parent.parent.parent)
+
+ for root in search_roots:
+ candidate = root / "src" / "bin"
+ if candidate.exists():
+ self._bin_root = candidate
+ return candidate
+
+ for root in search_roots:
+ candidate = root / "bin"
+ if candidate.exists():
+ self._bin_root = candidate
+ return candidate
+
+ self._bin_root = Path.cwd() / "src" / "bin"
+ return self._bin_root
+
+ def _os_folder(self) -> str:
+ s = platform.system().lower()
+ if s == "darwin":
+ return "macos"
+ if s == "windows":
+ return "windows"
+ return "linux"
+
+ def _arch_folder(self) -> str:
+ m = (platform.machine() or "").lower()
+ if m in {"x86_64", "amd64"}:
+ return "x86_64"
+ if m in {"aarch64", "arm64"}:
+ return "arm64"
+ if m.startswith("arm"):
+ return "arm"
+ if m in {"i386", "i686", "x86"}:
+ return "x86"
+ return m or "unknown"
+
+ def _is_windows_drive_letter(self, s: str) -> bool:
+ return len(s) == 2 and s[1] == ":" and s[0].isalpha()
+
+ def _smb_unc(self, host: str, remote: str) -> str:
+ remote = unquote(remote or "")
+ remote = remote.replace("/", "\\").lstrip("\\")
+ return f"\\\\{host}\\{remote}"
+
+ def _split_share_and_subpath(self, remote: str) -> Tuple[str, str]:
+ r = unquote(remote or "").lstrip("/")
+ if not r:
+ return "", ""
+ parts = r.split("/")
+ share = parts[0]
+ sub = "/".join(parts[1:]) if len(parts) > 1 else ""
+ return share, sub
+
+ def _kv_to_mount_opts(self, d: Dict[str, str]) -> str:
+ parts = []
+ for k, v in d.items():
+ if v is None:
+ continue
+ if v == "":
+ parts.append(str(k))
+ else:
+ parts.append(f"{k}={v}")
+ return ",".join(parts)
+
+ def _url_escape(self, s: str) -> str:
+ # Minimal escaping suitable for passwords in smb URLs
+ safe = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~"
+ out = []
+ for ch in s:
+ if ch in safe:
+ out.append(ch)
+ else:
+ out.append("%" + format(ord(ch), "02X"))
+ return "".join(out)
+
+ def _log_debug(self, msg: str) -> None:
+ if self._logger is not None:
+ for method in ("debug", "info"):
+ if hasattr(self._logger, method):
+ getattr(self._logger, method)(msg)
+ return
+
+ def _probe_mount(self, mount_point: str, *, timeout: int = 10) -> Tuple[bool, str]:
+ """Best-effort probe to determine if a mount point is usable."""
+ try:
+ if self._is_windows_drive_letter(mount_point):
+ return self._probe_windows_drive(mount_point)
+
+ mp = str(mount_point)
+ if not os.path.exists(mp):
+ return False, "mount point does not exist"
+ if not os.path.isdir(mp):
+ return False, "mount point is not a directory"
+
+ deadline = time.time() + float(timeout)
+ last_err: Optional[str] = None
+ while time.time() < deadline:
+ try:
+ _ = os.listdir(mp)
+ return True, "listdir ok"
+ except Exception as e:
+ last_err = str(e)
+ time.sleep(0.25)
+
+ return False, f"listdir failed: {last_err or 'unknown error'}"
+ except Exception as e:
+ return False, f"probe exception: {e}"
+
+ def _probe_windows_drive(self, drive: str) -> Tuple[bool, str]:
+ try:
+ d = drive.upper()
+ if not d.endswith(":"):
+ d += ":"
+ root = d + "\\"
+ if not os.path.exists(root):
+ return False, "drive root does not exist"
+ try:
+ _ = os.listdir(root)
+ return True, "drive listdir ok"
+ except Exception as e:
+ return False, f"drive listdir failed: {e}"
+ except Exception as e:
+ return False, f"drive probe exception: {e}"
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/helper.py b/dist/macos/Replicator.app/Contents/Resources/core/helper.py
new file mode 100644
index 00000000..c7a638c6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/helper.py
@@ -0,0 +1,391 @@
+#!/usr/bin/env python3
+# src/core/helper.py
+import os
+import sys
+import platform
+import subprocess
+
+from pathlib import Path
+from PyQt5.QtWidgets import QApplication
+
+class Helper:
+ """
+ Small collection of cross-app helpers.
+ """
+
+ def __init__(self, root_dir: str | None = None, script_dir: str | None = None):
+ if script_dir is None:
+ if getattr(sys, "frozen", False):
+ script_dir = os.path.dirname(sys.executable)
+ else:
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ self.script_dir = script_dir
+
+ if root_dir is None:
+ # If we are in .../src/app, root_dir should be project root (two levels up).
+ base = os.path.dirname(self.script_dir) # e.g. .../src
+ parent = os.path.dirname(base) # e.g. project_root
+ self.root_dir = parent
+ else:
+ self.root_dir = root_dir
+
+ self.home_dir = os.path.expanduser("~")
+
+ def get_path(self, rel_path: str, type: str = "SYS") -> str | None:
+ """
+ Locate a resource file according to the specified type.
+ Wrapper around get_sys_path, get_config_path, get_data_path.
+
+ Types:
+ - SYS: Search in standard system locations (PyInstaller, .app Resources, src/)
+ - CONFIG: User or system configuration directory (see get_config_path)
+ - DATA: User or system data directory (see get_data_path)
+ """
+ rel = rel_path.replace("\\", "/")
+
+ if type.upper() == "SYS":
+ return self.get_sys_path(rel)
+ elif type.upper() == "CONFIG":
+ return self.get_config_path(rel)
+ elif type.upper() == "DATA":
+ return self.get_data_path(rel)
+ else:
+ print(f"[Helper] Unknown path type: {type}")
+ return None
+
+ def get_sys_path(self, rel_path: str) -> str | None:
+ rel = rel_path.replace("\\", "/")
+
+ # 1) PyInstaller onefile temp dir
+ meipass = getattr(sys, "_MEIPASS", None)
+ if meipass:
+ p = os.path.join(meipass, rel)
+ if os.path.exists(p):
+ return p
+
+ # 2) Next to the frozen exe
+ if getattr(sys, "frozen", False):
+ p = os.path.join(self.script_dir, rel)
+ if os.path.exists(p):
+ return p
+
+ # 3) macOS .app Resources in onefile
+ if getattr(sys, "frozen", False) and self.get_os() == "macos":
+ # .../Replicator.app/Contents/MacOS/Replicator -> parents[1] = Contents
+ contents = Path(sys.executable).resolve().parents[1]
+ p = contents / "Resources" / rel
+ if p.exists():
+ return str(p)
+
+ # 4) macOS .app Resources
+ res = os.path.join(self.root_dir, "Resources", rel)
+ if os.path.exists(res):
+ return res
+
+ # 5) repo src/
+ src = os.path.join(self.root_dir, "src", rel)
+ if os.path.exists(src):
+ return src
+
+ print(f"[Helper] Could not find resource: {rel}")
+ return None
+
+ def _get_app_name(self, app_name: str | None = None) -> str:
+ """Resolve the application name used for user directories."""
+ if app_name:
+ name = str(app_name).strip()
+ if name:
+ return name
+
+ # Prefer QApplication name when running with a Qt app
+ try:
+ qapp = QApplication.instance()
+ if qapp:
+ qname = (qapp.applicationName() or "").strip()
+ if qname:
+ return qname
+ except Exception:
+ pass
+
+ # Fallback: executable/script name
+ try:
+ base = os.path.basename(sys.executable if getattr(sys, "frozen", False) else sys.argv[0])
+ name = os.path.splitext(base)[0].strip()
+ return name or "app"
+ except Exception:
+ return "app"
+
+ def get_data_path(
+ self,
+ rel_path: str | None = None,
+ app_name: str | None = None,
+ scope: str | None = None,
+ ensure: bool = True,
+ ) -> str:
+ """Return the OS-appropriate directory for app *data*.
+
+ Scope:
+ - scope="user" (default): per-user data directory
+ - scope="system": system-wide/shared data directory (requires appropriate permissions)
+
+ Defaults by OS:
+ - Windows:
+ user -> %LOCALAPPDATA%\\
+ system -> %PROGRAMDATA%\\
+ - macOS:
+ user -> ~/Library/Application Support/
+ system -> ~/Library/Application Support/
+ - Linux:
+ user -> $XDG_DATA_HOME/ (fallback ~/.local/share/)
+ system -> /var/lib/
+
+ If `rel_path` is provided, it is appended under the app directory.
+ If `ensure` is True, the directory is created (or its parent if `rel_path` looks like a file).
+
+ Notes:
+ - If scope="system" is requested but the target is not writable, this function falls back
+ to the user scope directory.
+ - You can set COREPY_DATA_SCOPE to "user" or "system" to change the default scope.
+ """
+ name = self._get_app_name(app_name)
+ os_name = self.get_os()
+
+ # Resolve scope: explicit arg wins, then env override, then default to "user"
+ resolved_scope = (scope or os.environ.get("COREPY_DATA_SCOPE") or "user").strip().lower()
+ if resolved_scope not in ("user", "system"):
+ resolved_scope = "user"
+
+ if os_name == "windows":
+ if resolved_scope == "system":
+ base = os.environ.get("PROGRAMDATA") or os.environ.get("ALLUSERSPROFILE") or self.home_dir
+ root = Path(base) / name
+ else:
+ base = os.environ.get("LOCALAPPDATA") or os.environ.get("APPDATA") or self.home_dir
+ root = Path(base) / name
+ elif os_name == "macos":
+ # if resolved_scope == "system":
+ # root = Path("/Library") / "Application Support" / name
+ # else:
+ # root = Path(self.home_dir) / "Library" / "Application Support" / name
+ root = Path(self.home_dir) / "Library" / "Application Support" / name
+ else:
+ if resolved_scope == "system":
+ root = Path("/var") / "lib" / name
+ else:
+ xdg = os.environ.get("XDG_DATA_HOME")
+ if xdg:
+ root = Path(xdg) / name
+ else:
+ root = Path(self.home_dir) / ".local" / "share" / name
+
+ if rel_path:
+ rel = rel_path.replace("\\", "/").lstrip("/")
+ root = root / rel
+
+ if ensure:
+ # If rel_path includes a filename, create parent; otherwise create dir
+ target_dir = root.parent if root.suffix else root
+ target_dir.mkdir(parents=True, exist_ok=True)
+
+ if resolved_scope == "system" and ensure:
+ try:
+ # If we cannot write to the chosen system directory, fall back to user scope.
+ probe_dir = root.parent if root.suffix else root
+ if not os.access(str(probe_dir), os.W_OK):
+ fallback = self.get_data_path(rel_path=rel_path, app_name=app_name, scope="user", ensure=ensure)
+ return fallback
+ except Exception:
+ fallback = self.get_data_path(rel_path=rel_path, app_name=app_name, scope="user", ensure=ensure)
+ return fallback
+
+ return str(root)
+
+ def get_config_path(
+ self,
+ rel_path: str | None = None,
+ app_name: str | None = None,
+ scope: str | None = None,
+ ensure: bool = True,
+ ) -> str:
+ """Return the OS-appropriate directory for app *configuration*.
+
+ Scope:
+ - scope="user" (default): per-user configuration directory
+ - scope="system": system-wide/shared configuration directory (requires appropriate permissions)
+
+ Defaults by OS:
+ - Windows:
+ user -> %APPDATA%\\
+ system -> %PROGRAMDATA%\\\\config
+ - macOS:
+ user -> ~/Library/Preferences/
+ system -> /Library/Preferences/
+ - Linux:
+ user -> $XDG_CONFIG_HOME/ (fallback ~/.config/)
+ system -> /etc/
+
+ If `rel_path` is provided, it is appended under the app directory.
+ If `ensure` is True, the directory is created (or its parent if `rel_path` looks like a file).
+
+ Notes:
+ - If scope="system" is requested but the target is not writable, this function falls back
+ to the user scope directory.
+ - You can set COREPY_CONFIG_SCOPE to "user" or "system" to change the default scope.
+ """
+ name = self._get_app_name(app_name)
+ os_name = self.get_os()
+
+ # Resolve scope: explicit arg wins, then env override, then default to "user"
+ resolved_scope = (scope or os.environ.get("COREPY_CONFIG_SCOPE") or "user").strip().lower()
+ if resolved_scope not in ("user", "system"):
+ resolved_scope = "user"
+
+ if os_name == "windows":
+ if resolved_scope == "system":
+ base = os.environ.get("PROGRAMDATA") or os.environ.get("ALLUSERSPROFILE") or self.home_dir
+ root = Path(base) / name / "config"
+ else:
+ base = os.environ.get("APPDATA") or os.environ.get("LOCALAPPDATA") or self.home_dir
+ root = Path(base) / name
+ elif os_name == "macos":
+ if resolved_scope == "system":
+ root = Path("/Library") / "Preferences" / name
+ else:
+ root = Path(self.home_dir) / "Library" / "Preferences" / name
+ else:
+ if resolved_scope == "system":
+ root = Path("/etc") / name
+ else:
+ xdg = os.environ.get("XDG_CONFIG_HOME")
+ if xdg:
+ root = Path(xdg) / name
+ else:
+ root = Path(self.home_dir) / ".config" / name
+
+ if rel_path:
+ rel = rel_path.replace("\\", "/").lstrip("/")
+ root = root / rel
+
+ if ensure:
+ target_dir = root.parent if root.suffix else root
+ target_dir.mkdir(parents=True, exist_ok=True)
+
+ if resolved_scope == "system" and ensure:
+ try:
+ probe_dir = root.parent if root.suffix else root
+ if not os.access(str(probe_dir), os.W_OK):
+ fallback = self.get_config_path(rel_path=rel_path, app_name=app_name, scope="user", ensure=ensure)
+ return fallback
+ except Exception:
+ fallback = self.get_config_path(rel_path=rel_path, app_name=app_name, scope="user", ensure=ensure)
+ return fallback
+
+ return str(root)
+
+ @staticmethod
+ def get_os() -> str:
+ name = platform.system()
+ if name == "Darwin":
+ return "macos"
+ if name == "Linux":
+ return "linux"
+ if name == "Windows":
+ return "windows"
+ return "unknown"
+
+ @staticmethod
+ def get_arch() -> str:
+ arch = platform.machine().lower()
+ if arch in ("x86_64", "amd64"):
+ return "x86_64"
+ if arch in ("aarch64", "arm64"):
+ return "arm64"
+ if arch in ("i386", "i686", "x86", "i86pc"):
+ return "x86"
+ if arch in ("armv7l", "armv8l", "arm"):
+ return "armhf"
+ return "unknown"
+
+ @staticmethod
+ def get_serial() -> str:
+ # Linux / Pi: /proc/cpuinfo usually contains a 'Serial' line
+ try:
+ if Helper.get_os() == "linux":
+ cpuinfo_path = "/proc/cpuinfo"
+ if os.path.exists(cpuinfo_path):
+ with open(cpuinfo_path, "r", encoding="utf-8", errors="ignore") as f:
+ for line in f:
+ if line.lower().startswith("serial"):
+ parts = line.split(":", 1)
+ if len(parts) == 2:
+ return parts[1].strip()
+ except Exception:
+ # Swallow errors and fall through to empty string
+ pass
+
+ # Fallback: nothing suitable found
+ return ""
+
+ @staticmethod
+ def get_screen_resolution() -> tuple[int, int]:
+ app = QApplication.instance()
+ if not app:
+ return (0, 0)
+ screen = app.primaryScreen()
+ size = screen.size()
+ return (size.width(), size.height())
+
+ @staticmethod
+ def get_now() -> str:
+ from datetime import datetime
+ return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+
+ @staticmethod
+ def file_exists(path: str | None) -> bool:
+ return bool(path) and os.path.isfile(path)
+
+ @staticmethod
+ def dir_exists(path: str | None) -> bool:
+ return bool(path) and os.path.isdir(path)
+
+ @staticmethod
+ def join(*paths: str) -> str:
+ return os.path.join(*paths)
+
+ @staticmethod
+ def qss_url(p: str | None) -> str:
+ """
+ Return a url("...") QSS literal, or url("") for None.
+ """
+ if not p:
+ return 'url("")'
+ return f'url("{p.replace(os.sep, "/")}")'
+
+ @staticmethod
+ def run(cmd):
+ try:
+ p = subprocess.run(
+ cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ text=True,
+ timeout=6
+ )
+ return p.returncode, (p.stdout or "").strip()
+ except Exception as e:
+ return 1, f"{type(e).__name__}: {e}"
+
+ # ---------- StyleSheet Handling ----------
+ def load_stylesheet(self, rel_path: str) -> str | None:
+ """
+ Load a stylesheet from a relative path.
+ """
+ p = self.get_path(rel_path)
+ if not p:
+ return None
+ try:
+ with open(p, "r", encoding="utf-8") as f:
+ return f.read()
+ except Exception as e:
+ print(f"[Helper] Failed to load stylesheet {rel_path}: {e}")
+ return None
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/0-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/0-circle-fill.svg
new file mode 100644
index 00000000..e17575cd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/0-circle-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/0-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/0-circle.svg
new file mode 100644
index 00000000..b3b4ce3d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/0-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/0-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/0-square-fill.svg
new file mode 100644
index 00000000..4da93699
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/0-square-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/0-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/0-square.svg
new file mode 100644
index 00000000..1c76aa4b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/0-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/1-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/1-circle-fill.svg
new file mode 100644
index 00000000..7bb27ab3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/1-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/1-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/1-circle.svg
new file mode 100644
index 00000000..6349aa1c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/1-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/1-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/1-square-fill.svg
new file mode 100644
index 00000000..f3aff4c7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/1-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/1-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/1-square.svg
new file mode 100644
index 00000000..218c0133
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/1-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/123.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/123.svg
new file mode 100644
index 00000000..47a886fa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/123.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/2-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/2-circle-fill.svg
new file mode 100644
index 00000000..19b53162
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/2-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/2-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/2-circle.svg
new file mode 100644
index 00000000..35c78c0b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/2-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/2-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/2-square-fill.svg
new file mode 100644
index 00000000..7d1af27e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/2-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/2-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/2-square.svg
new file mode 100644
index 00000000..2b0787b8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/2-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/3-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/3-circle-fill.svg
new file mode 100644
index 00000000..5dccb79a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/3-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/3-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/3-circle.svg
new file mode 100644
index 00000000..34d70f25
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/3-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/3-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/3-square-fill.svg
new file mode 100644
index 00000000..97e0d8eb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/3-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/3-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/3-square.svg
new file mode 100644
index 00000000..9366c9b9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/3-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/4-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/4-circle-fill.svg
new file mode 100644
index 00000000..895307cf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/4-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/4-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/4-circle.svg
new file mode 100644
index 00000000..7fab0bbf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/4-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/4-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/4-square-fill.svg
new file mode 100644
index 00000000..56bccf93
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/4-square-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/4-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/4-square.svg
new file mode 100644
index 00000000..478e909c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/4-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/5-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/5-circle-fill.svg
new file mode 100644
index 00000000..576a0879
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/5-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/5-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/5-circle.svg
new file mode 100644
index 00000000..b1e9060a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/5-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/5-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/5-square-fill.svg
new file mode 100644
index 00000000..d8e7940d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/5-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/5-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/5-square.svg
new file mode 100644
index 00000000..a3d26093
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/5-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/6-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/6-circle-fill.svg
new file mode 100644
index 00000000..a22914b7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/6-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/6-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/6-circle.svg
new file mode 100644
index 00000000..f9be6c54
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/6-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/6-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/6-square-fill.svg
new file mode 100644
index 00000000..8ae78384
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/6-square-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/6-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/6-square.svg
new file mode 100644
index 00000000..53a8ab46
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/6-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/7-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/7-circle-fill.svg
new file mode 100644
index 00000000..7ff2cd4c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/7-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/7-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/7-circle.svg
new file mode 100644
index 00000000..56039ba6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/7-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/7-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/7-square-fill.svg
new file mode 100644
index 00000000..c2cdc624
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/7-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/7-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/7-square.svg
new file mode 100644
index 00000000..257f8d05
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/7-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/8-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/8-circle-fill.svg
new file mode 100644
index 00000000..8964170f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/8-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/8-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/8-circle.svg
new file mode 100644
index 00000000..3a68a2da
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/8-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/8-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/8-square-fill.svg
new file mode 100644
index 00000000..84854522
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/8-square-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/8-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/8-square.svg
new file mode 100644
index 00000000..2ae63555
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/8-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/9-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/9-circle-fill.svg
new file mode 100644
index 00000000..f251ce3e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/9-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/9-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/9-circle.svg
new file mode 100644
index 00000000..5ace6bd8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/9-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/9-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/9-square-fill.svg
new file mode 100644
index 00000000..154faca2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/9-square-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/9-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/9-square.svg
new file mode 100644
index 00000000..98f9ac6e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/9-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/activity.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/activity.svg
new file mode 100644
index 00000000..c9d3bb65
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/activity.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/airplane-engines-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/airplane-engines-fill.svg
new file mode 100644
index 00000000..a79a83e5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/airplane-engines-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/airplane-engines.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/airplane-engines.svg
new file mode 100644
index 00000000..b49f2414
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/airplane-engines.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/airplane-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/airplane-fill.svg
new file mode 100644
index 00000000..0d84afcc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/airplane-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/airplane.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/airplane.svg
new file mode 100644
index 00000000..90cea15f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/airplane.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/alarm-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/alarm-fill.svg
new file mode 100644
index 00000000..6d50427d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/alarm-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/alarm.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/alarm.svg
new file mode 100644
index 00000000..e45b01ae
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/alarm.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/alexa.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/alexa.svg
new file mode 100644
index 00000000..42639938
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/alexa.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/align-bottom.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/align-bottom.svg
new file mode 100644
index 00000000..02a8534d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/align-bottom.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/align-center.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/align-center.svg
new file mode 100644
index 00000000..9edc0dca
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/align-center.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/align-end.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/align-end.svg
new file mode 100644
index 00000000..a8282591
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/align-end.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/align-middle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/align-middle.svg
new file mode 100644
index 00000000..552515e2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/align-middle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/align-start.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/align-start.svg
new file mode 100644
index 00000000..d3f1a531
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/align-start.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/align-top.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/align-top.svg
new file mode 100644
index 00000000..fbae04db
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/align-top.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/alipay.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/alipay.svg
new file mode 100644
index 00000000..c8eaaae7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/alipay.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/alphabet-uppercase.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/alphabet-uppercase.svg
new file mode 100644
index 00000000..a7f55ffd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/alphabet-uppercase.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/alphabet.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/alphabet.svg
new file mode 100644
index 00000000..667a14ae
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/alphabet.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/alt.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/alt.svg
new file mode 100644
index 00000000..cd136689
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/alt.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/amazon.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/amazon.svg
new file mode 100644
index 00000000..d2d9618a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/amazon.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/amd.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/amd.svg
new file mode 100644
index 00000000..35bc7e41
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/amd.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/android.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/android.svg
new file mode 100644
index 00000000..81f9d2bd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/android.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/android2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/android2.svg
new file mode 100644
index 00000000..16d52154
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/android2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/anthropic.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/anthropic.svg
new file mode 100644
index 00000000..4a2df0df
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/anthropic.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/app-indicator.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/app-indicator.svg
new file mode 100644
index 00000000..b720a400
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/app-indicator.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/app.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/app.svg
new file mode 100644
index 00000000..597cca3b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/app.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/apple-music.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/apple-music.svg
new file mode 100644
index 00000000..28f3c582
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/apple-music.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/apple.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/apple.svg
new file mode 100644
index 00000000..a2acef2b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/apple.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/archive-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/archive-fill.svg
new file mode 100644
index 00000000..85ea9e7b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/archive-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/archive.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/archive.svg
new file mode 100644
index 00000000..06c03fec
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/archive.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-90deg-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-90deg-down.svg
new file mode 100644
index 00000000..e6ab5a39
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-90deg-down.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-90deg-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-90deg-left.svg
new file mode 100644
index 00000000..e46a840f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-90deg-left.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-90deg-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-90deg-right.svg
new file mode 100644
index 00000000..abd788c2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-90deg-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-90deg-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-90deg-up.svg
new file mode 100644
index 00000000..c1c8dece
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-90deg-up.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-bar-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-bar-down.svg
new file mode 100644
index 00000000..77e8a322
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-bar-down.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-bar-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-bar-left.svg
new file mode 100644
index 00000000..7d9c376c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-bar-left.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-bar-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-bar-right.svg
new file mode 100644
index 00000000..a1f8b5f1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-bar-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-bar-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-bar-up.svg
new file mode 100644
index 00000000..af49eee0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-bar-up.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-clockwise.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-clockwise.svg
new file mode 100644
index 00000000..d191029a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-clockwise.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-counterclockwise.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-counterclockwise.svg
new file mode 100644
index 00000000..2f1df604
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-counterclockwise.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-circle-fill.svg
new file mode 100644
index 00000000..4bee2fd3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-circle.svg
new file mode 100644
index 00000000..ed256319
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-left-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-left-circle-fill.svg
new file mode 100644
index 00000000..fd7d7501
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-left-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-left-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-left-circle.svg
new file mode 100644
index 00000000..c2ea60a9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-left-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-left-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-left-square-fill.svg
new file mode 100644
index 00000000..135695e4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-left-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-left-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-left-square.svg
new file mode 100644
index 00000000..5f292e48
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-left-square.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-left.svg
new file mode 100644
index 00000000..b1b7ad1f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-left.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-right-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-right-circle-fill.svg
new file mode 100644
index 00000000..2a19ac4b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-right-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-right-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-right-circle.svg
new file mode 100644
index 00000000..21dd81ee
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-right-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-right-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-right-square-fill.svg
new file mode 100644
index 00000000..40109aa0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-right-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-right-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-right-square.svg
new file mode 100644
index 00000000..e1449658
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-right-square.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-right.svg
new file mode 100644
index 00000000..0c4ca1a0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-short.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-short.svg
new file mode 100644
index 00000000..214c54d4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-short.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-square-fill.svg
new file mode 100644
index 00000000..42dfed3b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-square.svg
new file mode 100644
index 00000000..d33f1b80
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-square.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-up.svg
new file mode 100644
index 00000000..46578dfd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down-up.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down.svg
new file mode 100644
index 00000000..fa3a1ff2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-down.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-circle-fill.svg
new file mode 100644
index 00000000..c550c553
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-circle.svg
new file mode 100644
index 00000000..4e4d71cb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-right.svg
new file mode 100644
index 00000000..25eba34e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-short.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-short.svg
new file mode 100644
index 00000000..1adfaf29
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-short.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-square-fill.svg
new file mode 100644
index 00000000..d8830057
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-square.svg
new file mode 100644
index 00000000..a0df7050
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left-square.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left.svg
new file mode 100644
index 00000000..3599fe3a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-left.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-repeat.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-repeat.svg
new file mode 100644
index 00000000..d448df7e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-repeat.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-return-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-return-left.svg
new file mode 100644
index 00000000..b058ffbe
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-return-left.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-return-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-return-right.svg
new file mode 100644
index 00000000..5ad5ff17
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-return-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right-circle-fill.svg
new file mode 100644
index 00000000..cec28527
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right-circle.svg
new file mode 100644
index 00000000..7b3271a2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right-short.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right-short.svg
new file mode 100644
index 00000000..c068ec02
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right-short.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right-square-fill.svg
new file mode 100644
index 00000000..99e1ec42
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right-square.svg
new file mode 100644
index 00000000..0eafc08c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right-square.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right.svg
new file mode 100644
index 00000000..2a1a0514
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-through-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-through-heart-fill.svg
new file mode 100644
index 00000000..18d576ce
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-through-heart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-through-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-through-heart.svg
new file mode 100644
index 00000000..27fdbb74
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-through-heart.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-circle-fill.svg
new file mode 100644
index 00000000..833888df
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-circle.svg
new file mode 100644
index 00000000..9aef366d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-left-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-left-circle-fill.svg
new file mode 100644
index 00000000..b5065a38
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-left-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-left-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-left-circle.svg
new file mode 100644
index 00000000..dd0fdfa8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-left-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-left-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-left-square-fill.svg
new file mode 100644
index 00000000..75f471c0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-left-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-left-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-left-square.svg
new file mode 100644
index 00000000..56fbb1e5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-left-square.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-left.svg
new file mode 100644
index 00000000..8b1704e6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-left.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-right-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-right-circle-fill.svg
new file mode 100644
index 00000000..7c191562
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-right-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-right-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-right-circle.svg
new file mode 100644
index 00000000..381ab1de
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-right-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-right-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-right-square-fill.svg
new file mode 100644
index 00000000..a6fc9ae7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-right-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-right-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-right-square.svg
new file mode 100644
index 00000000..c54d2321
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-right-square.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-right.svg
new file mode 100644
index 00000000..45504520
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-short.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-short.svg
new file mode 100644
index 00000000..09387cb7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-short.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-square-fill.svg
new file mode 100644
index 00000000..5cad3c67
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-square.svg
new file mode 100644
index 00000000..0329da58
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up-square.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up.svg
new file mode 100644
index 00000000..20033f9d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrow-up.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-angle-contract.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-angle-contract.svg
new file mode 100644
index 00000000..7828deff
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-angle-contract.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-angle-expand.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-angle-expand.svg
new file mode 100644
index 00000000..aae25f45
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-angle-expand.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-collapse-vertical.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-collapse-vertical.svg
new file mode 100644
index 00000000..5da861fc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-collapse-vertical.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-collapse.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-collapse.svg
new file mode 100644
index 00000000..a4b7c648
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-collapse.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-expand-vertical.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-expand-vertical.svg
new file mode 100644
index 00000000..13b8db29
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-expand-vertical.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-expand.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-expand.svg
new file mode 100644
index 00000000..7ee3c4a8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-expand.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-fullscreen.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-fullscreen.svg
new file mode 100644
index 00000000..04b08df5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-fullscreen.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-move.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-move.svg
new file mode 100644
index 00000000..3783f04b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-move.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-vertical.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-vertical.svg
new file mode 100644
index 00000000..6c3386a8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows-vertical.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows.svg
new file mode 100644
index 00000000..12e56492
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/arrows.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/aspect-ratio-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/aspect-ratio-fill.svg
new file mode 100644
index 00000000..47c33a72
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/aspect-ratio-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/aspect-ratio.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/aspect-ratio.svg
new file mode 100644
index 00000000..8cb89876
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/aspect-ratio.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/asterisk.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/asterisk.svg
new file mode 100644
index 00000000..408c80a6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/asterisk.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/at.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/at.svg
new file mode 100644
index 00000000..73d85964
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/at.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/award-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/award-fill.svg
new file mode 100644
index 00000000..7c484a69
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/award-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/award.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/award.svg
new file mode 100644
index 00000000..f24db7b0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/award.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/back.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/back.svg
new file mode 100644
index 00000000..78333a7b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/back.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack-fill.svg
new file mode 100644
index 00000000..e002e853
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack.svg
new file mode 100644
index 00000000..377011a3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack2-fill.svg
new file mode 100644
index 00000000..f97c5498
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack2-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack2.svg
new file mode 100644
index 00000000..51ca845c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack2.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack3-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack3-fill.svg
new file mode 100644
index 00000000..8eead8d8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack3-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack3.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack3.svg
new file mode 100644
index 00000000..7debe2d1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack3.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack4-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack4-fill.svg
new file mode 100644
index 00000000..e8473134
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack4-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack4.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack4.svg
new file mode 100644
index 00000000..355923f5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/backpack4.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/backspace-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/backspace-fill.svg
new file mode 100644
index 00000000..fe14f289
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/backspace-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/backspace-reverse-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/backspace-reverse-fill.svg
new file mode 100644
index 00000000..bdfaf30b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/backspace-reverse-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/backspace-reverse.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/backspace-reverse.svg
new file mode 100644
index 00000000..7745329e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/backspace-reverse.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/backspace.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/backspace.svg
new file mode 100644
index 00000000..987aaa1b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/backspace.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-3d-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-3d-fill.svg
new file mode 100644
index 00000000..bdf92e92
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-3d-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-3d.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-3d.svg
new file mode 100644
index 00000000..cfeec162
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-3d.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-4k-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-4k-fill.svg
new file mode 100644
index 00000000..920101b0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-4k-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-4k.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-4k.svg
new file mode 100644
index 00000000..35d76a86
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-4k.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-8k-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-8k-fill.svg
new file mode 100644
index 00000000..0e065416
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-8k-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-8k.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-8k.svg
new file mode 100644
index 00000000..63798a39
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-8k.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-ad-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-ad-fill.svg
new file mode 100644
index 00000000..34113ed2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-ad-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-ad.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-ad.svg
new file mode 100644
index 00000000..0cf2533c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-ad.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-ar-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-ar-fill.svg
new file mode 100644
index 00000000..38371125
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-ar-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-ar.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-ar.svg
new file mode 100644
index 00000000..c0344974
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-ar.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-cc-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-cc-fill.svg
new file mode 100644
index 00000000..4676e2a7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-cc-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-cc.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-cc.svg
new file mode 100644
index 00000000..35459b94
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-cc.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-hd-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-hd-fill.svg
new file mode 100644
index 00000000..ef2773e6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-hd-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-hd.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-hd.svg
new file mode 100644
index 00000000..3fb7c774
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-hd.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-sd-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-sd-fill.svg
new file mode 100644
index 00000000..f1fda89e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-sd-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-sd.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-sd.svg
new file mode 100644
index 00000000..3993e401
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-sd.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-tm-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-tm-fill.svg
new file mode 100644
index 00000000..d9bbd656
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-tm-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-tm.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-tm.svg
new file mode 100644
index 00000000..5c0b6d8b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-tm.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-vo-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-vo-fill.svg
new file mode 100644
index 00000000..90be502a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-vo-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-vo.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-vo.svg
new file mode 100644
index 00000000..5082ed7d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-vo.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-vr-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-vr-fill.svg
new file mode 100644
index 00000000..edae16f0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-vr-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-vr.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-vr.svg
new file mode 100644
index 00000000..814e5721
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-vr.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-wc-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-wc-fill.svg
new file mode 100644
index 00000000..83b8367b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-wc-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-wc.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-wc.svg
new file mode 100644
index 00000000..7b16cf33
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/badge-wc.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-check-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-check-fill.svg
new file mode 100644
index 00000000..e09113e8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-check-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-check.svg
new file mode 100644
index 00000000..7f031c61
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-dash-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-dash-fill.svg
new file mode 100644
index 00000000..118994ba
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-dash-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-dash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-dash.svg
new file mode 100644
index 00000000..c857b827
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-dash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-fill.svg
new file mode 100644
index 00000000..04215d20
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-heart-fill.svg
new file mode 100644
index 00000000..ef00bbf6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-heart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-heart.svg
new file mode 100644
index 00000000..3037e6d2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-heart.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-plus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-plus-fill.svg
new file mode 100644
index 00000000..b438daeb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-plus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-plus.svg
new file mode 100644
index 00000000..545950b5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-plus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-x-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-x-fill.svg
new file mode 100644
index 00000000..07eea18a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-x-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-x.svg
new file mode 100644
index 00000000..1a2436d6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bag.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag.svg
new file mode 100644
index 00000000..8e7537a1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bag.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/balloon-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/balloon-fill.svg
new file mode 100644
index 00000000..c8e82edd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/balloon-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/balloon-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/balloon-heart-fill.svg
new file mode 100644
index 00000000..9edb3d7f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/balloon-heart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/balloon-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/balloon-heart.svg
new file mode 100644
index 00000000..af91c689
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/balloon-heart.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/balloon.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/balloon.svg
new file mode 100644
index 00000000..79c64138
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/balloon.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ban-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ban-fill.svg
new file mode 100644
index 00000000..f7435bde
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ban-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ban.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ban.svg
new file mode 100644
index 00000000..87514d9b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ban.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bandaid-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bandaid-fill.svg
new file mode 100644
index 00000000..df13e1f1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bandaid-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bandaid.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bandaid.svg
new file mode 100644
index 00000000..77f805e1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bandaid.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bank.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bank.svg
new file mode 100644
index 00000000..c3689206
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bank.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bank2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bank2.svg
new file mode 100644
index 00000000..94ab825f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bank2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bar-chart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bar-chart-fill.svg
new file mode 100644
index 00000000..a841929c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bar-chart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bar-chart-line-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bar-chart-line-fill.svg
new file mode 100644
index 00000000..37efe41e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bar-chart-line-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bar-chart-line.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bar-chart-line.svg
new file mode 100644
index 00000000..ee4a972b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bar-chart-line.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bar-chart-steps.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bar-chart-steps.svg
new file mode 100644
index 00000000..654aa0d9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bar-chart-steps.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bar-chart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bar-chart.svg
new file mode 100644
index 00000000..f4f8fda7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bar-chart.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/basket-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/basket-fill.svg
new file mode 100644
index 00000000..120ddf39
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/basket-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/basket.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/basket.svg
new file mode 100644
index 00000000..db15a997
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/basket.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/basket2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/basket2-fill.svg
new file mode 100644
index 00000000..7620f978
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/basket2-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/basket2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/basket2.svg
new file mode 100644
index 00000000..4a511b38
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/basket2.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/basket3-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/basket3-fill.svg
new file mode 100644
index 00000000..3d127bf0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/basket3-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/basket3.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/basket3.svg
new file mode 100644
index 00000000..43faea37
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/basket3.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/battery-charging.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/battery-charging.svg
new file mode 100644
index 00000000..c1240094
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/battery-charging.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/battery-full.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/battery-full.svg
new file mode 100644
index 00000000..33dbca9b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/battery-full.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/battery-half.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/battery-half.svg
new file mode 100644
index 00000000..2299b6ca
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/battery-half.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/battery-low.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/battery-low.svg
new file mode 100644
index 00000000..a550a394
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/battery-low.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/battery.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/battery.svg
new file mode 100644
index 00000000..db8b6662
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/battery.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/beaker-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/beaker-fill.svg
new file mode 100644
index 00000000..87f1e8aa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/beaker-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/beaker.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/beaker.svg
new file mode 100644
index 00000000..b9458de0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/beaker.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/behance.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/behance.svg
new file mode 100644
index 00000000..a5fe8738
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/behance.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bell-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bell-fill.svg
new file mode 100644
index 00000000..d8ed0a14
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bell-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bell-slash-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bell-slash-fill.svg
new file mode 100644
index 00000000..b0c21eaf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bell-slash-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bell-slash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bell-slash.svg
new file mode 100644
index 00000000..4f9a4f68
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bell-slash.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bell.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bell.svg
new file mode 100644
index 00000000..9645d67d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bell.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bezier.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bezier.svg
new file mode 100644
index 00000000..c2f2fadb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bezier.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bezier2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bezier2.svg
new file mode 100644
index 00000000..fe553536
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bezier2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bicycle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bicycle.svg
new file mode 100644
index 00000000..2a1794d3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bicycle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bing.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bing.svg
new file mode 100644
index 00000000..a6f4e57a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bing.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/binoculars-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/binoculars-fill.svg
new file mode 100644
index 00000000..a4cd4b6c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/binoculars-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/binoculars.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/binoculars.svg
new file mode 100644
index 00000000..aea3ce61
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/binoculars.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/blockquote-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/blockquote-left.svg
new file mode 100644
index 00000000..2b035d70
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/blockquote-left.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/blockquote-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/blockquote-right.svg
new file mode 100644
index 00000000..b7ba5544
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/blockquote-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bluesky.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bluesky.svg
new file mode 100644
index 00000000..ecae00fc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bluesky.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bluetooth.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bluetooth.svg
new file mode 100644
index 00000000..d72a384c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bluetooth.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/body-text.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/body-text.svg
new file mode 100644
index 00000000..c2e9072b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/body-text.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/book-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/book-fill.svg
new file mode 100644
index 00000000..5ceac950
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/book-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/book-half.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/book-half.svg
new file mode 100644
index 00000000..f5fc89b6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/book-half.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/book.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/book.svg
new file mode 100644
index 00000000..e8ad0a48
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/book.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-check-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-check-fill.svg
new file mode 100644
index 00000000..b2062307
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-check-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-check.svg
new file mode 100644
index 00000000..15dc47ae
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-dash-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-dash-fill.svg
new file mode 100644
index 00000000..c7cee4d2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-dash-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-dash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-dash.svg
new file mode 100644
index 00000000..34d50d78
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-dash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-fill.svg
new file mode 100644
index 00000000..ff293b70
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-heart-fill.svg
new file mode 100644
index 00000000..e4e0f368
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-heart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-heart.svg
new file mode 100644
index 00000000..d8a05461
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-heart.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-plus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-plus-fill.svg
new file mode 100644
index 00000000..286983f4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-plus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-plus.svg
new file mode 100644
index 00000000..51024239
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-plus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-star-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-star-fill.svg
new file mode 100644
index 00000000..c6469d67
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-star-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-star.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-star.svg
new file mode 100644
index 00000000..4c53fddb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-star.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-x-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-x-fill.svg
new file mode 100644
index 00000000..3b7c86c0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-x-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-x.svg
new file mode 100644
index 00000000..edb6cc27
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark.svg
new file mode 100644
index 00000000..3fb22877
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmark.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmarks-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmarks-fill.svg
new file mode 100644
index 00000000..3ee1ebfb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmarks-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmarks.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmarks.svg
new file mode 100644
index 00000000..3db26993
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookmarks.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bookshelf.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookshelf.svg
new file mode 100644
index 00000000..027a182c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bookshelf.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/boombox-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/boombox-fill.svg
new file mode 100644
index 00000000..c22f41f7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/boombox-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/boombox.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/boombox.svg
new file mode 100644
index 00000000..9341caeb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/boombox.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-fill.svg
new file mode 100644
index 00000000..117a3f3d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-icons.css b/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-icons.css
new file mode 100644
index 00000000..5f7ae28e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-icons.css
@@ -0,0 +1,2106 @@
+/*!
+ * Bootstrap Icons v1.13.1 (https://icons.getbootstrap.com/)
+ * Copyright 2019-2024 The Bootstrap Authors
+ * Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE)
+ */
+
+@font-face {
+ font-display: block;
+ font-family: "bootstrap-icons";
+ src: url("./fonts/bootstrap-icons.woff2?e34853135f9e39acf64315236852cd5a") format("woff2"),
+url("./fonts/bootstrap-icons.woff?e34853135f9e39acf64315236852cd5a") format("woff");
+}
+
+.bi::before,
+[class^="bi-"]::before,
+[class*=" bi-"]::before {
+ display: inline-block;
+ font-family: bootstrap-icons !important;
+ font-style: normal;
+ font-weight: normal !important;
+ font-variant: normal;
+ text-transform: none;
+ line-height: 1;
+ vertical-align: -.125em;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.bi-123::before { content: "\f67f"; }
+.bi-alarm-fill::before { content: "\f101"; }
+.bi-alarm::before { content: "\f102"; }
+.bi-align-bottom::before { content: "\f103"; }
+.bi-align-center::before { content: "\f104"; }
+.bi-align-end::before { content: "\f105"; }
+.bi-align-middle::before { content: "\f106"; }
+.bi-align-start::before { content: "\f107"; }
+.bi-align-top::before { content: "\f108"; }
+.bi-alt::before { content: "\f109"; }
+.bi-app-indicator::before { content: "\f10a"; }
+.bi-app::before { content: "\f10b"; }
+.bi-archive-fill::before { content: "\f10c"; }
+.bi-archive::before { content: "\f10d"; }
+.bi-arrow-90deg-down::before { content: "\f10e"; }
+.bi-arrow-90deg-left::before { content: "\f10f"; }
+.bi-arrow-90deg-right::before { content: "\f110"; }
+.bi-arrow-90deg-up::before { content: "\f111"; }
+.bi-arrow-bar-down::before { content: "\f112"; }
+.bi-arrow-bar-left::before { content: "\f113"; }
+.bi-arrow-bar-right::before { content: "\f114"; }
+.bi-arrow-bar-up::before { content: "\f115"; }
+.bi-arrow-clockwise::before { content: "\f116"; }
+.bi-arrow-counterclockwise::before { content: "\f117"; }
+.bi-arrow-down-circle-fill::before { content: "\f118"; }
+.bi-arrow-down-circle::before { content: "\f119"; }
+.bi-arrow-down-left-circle-fill::before { content: "\f11a"; }
+.bi-arrow-down-left-circle::before { content: "\f11b"; }
+.bi-arrow-down-left-square-fill::before { content: "\f11c"; }
+.bi-arrow-down-left-square::before { content: "\f11d"; }
+.bi-arrow-down-left::before { content: "\f11e"; }
+.bi-arrow-down-right-circle-fill::before { content: "\f11f"; }
+.bi-arrow-down-right-circle::before { content: "\f120"; }
+.bi-arrow-down-right-square-fill::before { content: "\f121"; }
+.bi-arrow-down-right-square::before { content: "\f122"; }
+.bi-arrow-down-right::before { content: "\f123"; }
+.bi-arrow-down-short::before { content: "\f124"; }
+.bi-arrow-down-square-fill::before { content: "\f125"; }
+.bi-arrow-down-square::before { content: "\f126"; }
+.bi-arrow-down-up::before { content: "\f127"; }
+.bi-arrow-down::before { content: "\f128"; }
+.bi-arrow-left-circle-fill::before { content: "\f129"; }
+.bi-arrow-left-circle::before { content: "\f12a"; }
+.bi-arrow-left-right::before { content: "\f12b"; }
+.bi-arrow-left-short::before { content: "\f12c"; }
+.bi-arrow-left-square-fill::before { content: "\f12d"; }
+.bi-arrow-left-square::before { content: "\f12e"; }
+.bi-arrow-left::before { content: "\f12f"; }
+.bi-arrow-repeat::before { content: "\f130"; }
+.bi-arrow-return-left::before { content: "\f131"; }
+.bi-arrow-return-right::before { content: "\f132"; }
+.bi-arrow-right-circle-fill::before { content: "\f133"; }
+.bi-arrow-right-circle::before { content: "\f134"; }
+.bi-arrow-right-short::before { content: "\f135"; }
+.bi-arrow-right-square-fill::before { content: "\f136"; }
+.bi-arrow-right-square::before { content: "\f137"; }
+.bi-arrow-right::before { content: "\f138"; }
+.bi-arrow-up-circle-fill::before { content: "\f139"; }
+.bi-arrow-up-circle::before { content: "\f13a"; }
+.bi-arrow-up-left-circle-fill::before { content: "\f13b"; }
+.bi-arrow-up-left-circle::before { content: "\f13c"; }
+.bi-arrow-up-left-square-fill::before { content: "\f13d"; }
+.bi-arrow-up-left-square::before { content: "\f13e"; }
+.bi-arrow-up-left::before { content: "\f13f"; }
+.bi-arrow-up-right-circle-fill::before { content: "\f140"; }
+.bi-arrow-up-right-circle::before { content: "\f141"; }
+.bi-arrow-up-right-square-fill::before { content: "\f142"; }
+.bi-arrow-up-right-square::before { content: "\f143"; }
+.bi-arrow-up-right::before { content: "\f144"; }
+.bi-arrow-up-short::before { content: "\f145"; }
+.bi-arrow-up-square-fill::before { content: "\f146"; }
+.bi-arrow-up-square::before { content: "\f147"; }
+.bi-arrow-up::before { content: "\f148"; }
+.bi-arrows-angle-contract::before { content: "\f149"; }
+.bi-arrows-angle-expand::before { content: "\f14a"; }
+.bi-arrows-collapse::before { content: "\f14b"; }
+.bi-arrows-expand::before { content: "\f14c"; }
+.bi-arrows-fullscreen::before { content: "\f14d"; }
+.bi-arrows-move::before { content: "\f14e"; }
+.bi-aspect-ratio-fill::before { content: "\f14f"; }
+.bi-aspect-ratio::before { content: "\f150"; }
+.bi-asterisk::before { content: "\f151"; }
+.bi-at::before { content: "\f152"; }
+.bi-award-fill::before { content: "\f153"; }
+.bi-award::before { content: "\f154"; }
+.bi-back::before { content: "\f155"; }
+.bi-backspace-fill::before { content: "\f156"; }
+.bi-backspace-reverse-fill::before { content: "\f157"; }
+.bi-backspace-reverse::before { content: "\f158"; }
+.bi-backspace::before { content: "\f159"; }
+.bi-badge-3d-fill::before { content: "\f15a"; }
+.bi-badge-3d::before { content: "\f15b"; }
+.bi-badge-4k-fill::before { content: "\f15c"; }
+.bi-badge-4k::before { content: "\f15d"; }
+.bi-badge-8k-fill::before { content: "\f15e"; }
+.bi-badge-8k::before { content: "\f15f"; }
+.bi-badge-ad-fill::before { content: "\f160"; }
+.bi-badge-ad::before { content: "\f161"; }
+.bi-badge-ar-fill::before { content: "\f162"; }
+.bi-badge-ar::before { content: "\f163"; }
+.bi-badge-cc-fill::before { content: "\f164"; }
+.bi-badge-cc::before { content: "\f165"; }
+.bi-badge-hd-fill::before { content: "\f166"; }
+.bi-badge-hd::before { content: "\f167"; }
+.bi-badge-tm-fill::before { content: "\f168"; }
+.bi-badge-tm::before { content: "\f169"; }
+.bi-badge-vo-fill::before { content: "\f16a"; }
+.bi-badge-vo::before { content: "\f16b"; }
+.bi-badge-vr-fill::before { content: "\f16c"; }
+.bi-badge-vr::before { content: "\f16d"; }
+.bi-badge-wc-fill::before { content: "\f16e"; }
+.bi-badge-wc::before { content: "\f16f"; }
+.bi-bag-check-fill::before { content: "\f170"; }
+.bi-bag-check::before { content: "\f171"; }
+.bi-bag-dash-fill::before { content: "\f172"; }
+.bi-bag-dash::before { content: "\f173"; }
+.bi-bag-fill::before { content: "\f174"; }
+.bi-bag-plus-fill::before { content: "\f175"; }
+.bi-bag-plus::before { content: "\f176"; }
+.bi-bag-x-fill::before { content: "\f177"; }
+.bi-bag-x::before { content: "\f178"; }
+.bi-bag::before { content: "\f179"; }
+.bi-bar-chart-fill::before { content: "\f17a"; }
+.bi-bar-chart-line-fill::before { content: "\f17b"; }
+.bi-bar-chart-line::before { content: "\f17c"; }
+.bi-bar-chart-steps::before { content: "\f17d"; }
+.bi-bar-chart::before { content: "\f17e"; }
+.bi-basket-fill::before { content: "\f17f"; }
+.bi-basket::before { content: "\f180"; }
+.bi-basket2-fill::before { content: "\f181"; }
+.bi-basket2::before { content: "\f182"; }
+.bi-basket3-fill::before { content: "\f183"; }
+.bi-basket3::before { content: "\f184"; }
+.bi-battery-charging::before { content: "\f185"; }
+.bi-battery-full::before { content: "\f186"; }
+.bi-battery-half::before { content: "\f187"; }
+.bi-battery::before { content: "\f188"; }
+.bi-bell-fill::before { content: "\f189"; }
+.bi-bell::before { content: "\f18a"; }
+.bi-bezier::before { content: "\f18b"; }
+.bi-bezier2::before { content: "\f18c"; }
+.bi-bicycle::before { content: "\f18d"; }
+.bi-binoculars-fill::before { content: "\f18e"; }
+.bi-binoculars::before { content: "\f18f"; }
+.bi-blockquote-left::before { content: "\f190"; }
+.bi-blockquote-right::before { content: "\f191"; }
+.bi-book-fill::before { content: "\f192"; }
+.bi-book-half::before { content: "\f193"; }
+.bi-book::before { content: "\f194"; }
+.bi-bookmark-check-fill::before { content: "\f195"; }
+.bi-bookmark-check::before { content: "\f196"; }
+.bi-bookmark-dash-fill::before { content: "\f197"; }
+.bi-bookmark-dash::before { content: "\f198"; }
+.bi-bookmark-fill::before { content: "\f199"; }
+.bi-bookmark-heart-fill::before { content: "\f19a"; }
+.bi-bookmark-heart::before { content: "\f19b"; }
+.bi-bookmark-plus-fill::before { content: "\f19c"; }
+.bi-bookmark-plus::before { content: "\f19d"; }
+.bi-bookmark-star-fill::before { content: "\f19e"; }
+.bi-bookmark-star::before { content: "\f19f"; }
+.bi-bookmark-x-fill::before { content: "\f1a0"; }
+.bi-bookmark-x::before { content: "\f1a1"; }
+.bi-bookmark::before { content: "\f1a2"; }
+.bi-bookmarks-fill::before { content: "\f1a3"; }
+.bi-bookmarks::before { content: "\f1a4"; }
+.bi-bookshelf::before { content: "\f1a5"; }
+.bi-bootstrap-fill::before { content: "\f1a6"; }
+.bi-bootstrap-reboot::before { content: "\f1a7"; }
+.bi-bootstrap::before { content: "\f1a8"; }
+.bi-border-all::before { content: "\f1a9"; }
+.bi-border-bottom::before { content: "\f1aa"; }
+.bi-border-center::before { content: "\f1ab"; }
+.bi-border-inner::before { content: "\f1ac"; }
+.bi-border-left::before { content: "\f1ad"; }
+.bi-border-middle::before { content: "\f1ae"; }
+.bi-border-outer::before { content: "\f1af"; }
+.bi-border-right::before { content: "\f1b0"; }
+.bi-border-style::before { content: "\f1b1"; }
+.bi-border-top::before { content: "\f1b2"; }
+.bi-border-width::before { content: "\f1b3"; }
+.bi-border::before { content: "\f1b4"; }
+.bi-bounding-box-circles::before { content: "\f1b5"; }
+.bi-bounding-box::before { content: "\f1b6"; }
+.bi-box-arrow-down-left::before { content: "\f1b7"; }
+.bi-box-arrow-down-right::before { content: "\f1b8"; }
+.bi-box-arrow-down::before { content: "\f1b9"; }
+.bi-box-arrow-in-down-left::before { content: "\f1ba"; }
+.bi-box-arrow-in-down-right::before { content: "\f1bb"; }
+.bi-box-arrow-in-down::before { content: "\f1bc"; }
+.bi-box-arrow-in-left::before { content: "\f1bd"; }
+.bi-box-arrow-in-right::before { content: "\f1be"; }
+.bi-box-arrow-in-up-left::before { content: "\f1bf"; }
+.bi-box-arrow-in-up-right::before { content: "\f1c0"; }
+.bi-box-arrow-in-up::before { content: "\f1c1"; }
+.bi-box-arrow-left::before { content: "\f1c2"; }
+.bi-box-arrow-right::before { content: "\f1c3"; }
+.bi-box-arrow-up-left::before { content: "\f1c4"; }
+.bi-box-arrow-up-right::before { content: "\f1c5"; }
+.bi-box-arrow-up::before { content: "\f1c6"; }
+.bi-box-seam::before { content: "\f1c7"; }
+.bi-box::before { content: "\f1c8"; }
+.bi-braces::before { content: "\f1c9"; }
+.bi-bricks::before { content: "\f1ca"; }
+.bi-briefcase-fill::before { content: "\f1cb"; }
+.bi-briefcase::before { content: "\f1cc"; }
+.bi-brightness-alt-high-fill::before { content: "\f1cd"; }
+.bi-brightness-alt-high::before { content: "\f1ce"; }
+.bi-brightness-alt-low-fill::before { content: "\f1cf"; }
+.bi-brightness-alt-low::before { content: "\f1d0"; }
+.bi-brightness-high-fill::before { content: "\f1d1"; }
+.bi-brightness-high::before { content: "\f1d2"; }
+.bi-brightness-low-fill::before { content: "\f1d3"; }
+.bi-brightness-low::before { content: "\f1d4"; }
+.bi-broadcast-pin::before { content: "\f1d5"; }
+.bi-broadcast::before { content: "\f1d6"; }
+.bi-brush-fill::before { content: "\f1d7"; }
+.bi-brush::before { content: "\f1d8"; }
+.bi-bucket-fill::before { content: "\f1d9"; }
+.bi-bucket::before { content: "\f1da"; }
+.bi-bug-fill::before { content: "\f1db"; }
+.bi-bug::before { content: "\f1dc"; }
+.bi-building::before { content: "\f1dd"; }
+.bi-bullseye::before { content: "\f1de"; }
+.bi-calculator-fill::before { content: "\f1df"; }
+.bi-calculator::before { content: "\f1e0"; }
+.bi-calendar-check-fill::before { content: "\f1e1"; }
+.bi-calendar-check::before { content: "\f1e2"; }
+.bi-calendar-date-fill::before { content: "\f1e3"; }
+.bi-calendar-date::before { content: "\f1e4"; }
+.bi-calendar-day-fill::before { content: "\f1e5"; }
+.bi-calendar-day::before { content: "\f1e6"; }
+.bi-calendar-event-fill::before { content: "\f1e7"; }
+.bi-calendar-event::before { content: "\f1e8"; }
+.bi-calendar-fill::before { content: "\f1e9"; }
+.bi-calendar-minus-fill::before { content: "\f1ea"; }
+.bi-calendar-minus::before { content: "\f1eb"; }
+.bi-calendar-month-fill::before { content: "\f1ec"; }
+.bi-calendar-month::before { content: "\f1ed"; }
+.bi-calendar-plus-fill::before { content: "\f1ee"; }
+.bi-calendar-plus::before { content: "\f1ef"; }
+.bi-calendar-range-fill::before { content: "\f1f0"; }
+.bi-calendar-range::before { content: "\f1f1"; }
+.bi-calendar-week-fill::before { content: "\f1f2"; }
+.bi-calendar-week::before { content: "\f1f3"; }
+.bi-calendar-x-fill::before { content: "\f1f4"; }
+.bi-calendar-x::before { content: "\f1f5"; }
+.bi-calendar::before { content: "\f1f6"; }
+.bi-calendar2-check-fill::before { content: "\f1f7"; }
+.bi-calendar2-check::before { content: "\f1f8"; }
+.bi-calendar2-date-fill::before { content: "\f1f9"; }
+.bi-calendar2-date::before { content: "\f1fa"; }
+.bi-calendar2-day-fill::before { content: "\f1fb"; }
+.bi-calendar2-day::before { content: "\f1fc"; }
+.bi-calendar2-event-fill::before { content: "\f1fd"; }
+.bi-calendar2-event::before { content: "\f1fe"; }
+.bi-calendar2-fill::before { content: "\f1ff"; }
+.bi-calendar2-minus-fill::before { content: "\f200"; }
+.bi-calendar2-minus::before { content: "\f201"; }
+.bi-calendar2-month-fill::before { content: "\f202"; }
+.bi-calendar2-month::before { content: "\f203"; }
+.bi-calendar2-plus-fill::before { content: "\f204"; }
+.bi-calendar2-plus::before { content: "\f205"; }
+.bi-calendar2-range-fill::before { content: "\f206"; }
+.bi-calendar2-range::before { content: "\f207"; }
+.bi-calendar2-week-fill::before { content: "\f208"; }
+.bi-calendar2-week::before { content: "\f209"; }
+.bi-calendar2-x-fill::before { content: "\f20a"; }
+.bi-calendar2-x::before { content: "\f20b"; }
+.bi-calendar2::before { content: "\f20c"; }
+.bi-calendar3-event-fill::before { content: "\f20d"; }
+.bi-calendar3-event::before { content: "\f20e"; }
+.bi-calendar3-fill::before { content: "\f20f"; }
+.bi-calendar3-range-fill::before { content: "\f210"; }
+.bi-calendar3-range::before { content: "\f211"; }
+.bi-calendar3-week-fill::before { content: "\f212"; }
+.bi-calendar3-week::before { content: "\f213"; }
+.bi-calendar3::before { content: "\f214"; }
+.bi-calendar4-event::before { content: "\f215"; }
+.bi-calendar4-range::before { content: "\f216"; }
+.bi-calendar4-week::before { content: "\f217"; }
+.bi-calendar4::before { content: "\f218"; }
+.bi-camera-fill::before { content: "\f219"; }
+.bi-camera-reels-fill::before { content: "\f21a"; }
+.bi-camera-reels::before { content: "\f21b"; }
+.bi-camera-video-fill::before { content: "\f21c"; }
+.bi-camera-video-off-fill::before { content: "\f21d"; }
+.bi-camera-video-off::before { content: "\f21e"; }
+.bi-camera-video::before { content: "\f21f"; }
+.bi-camera::before { content: "\f220"; }
+.bi-camera2::before { content: "\f221"; }
+.bi-capslock-fill::before { content: "\f222"; }
+.bi-capslock::before { content: "\f223"; }
+.bi-card-checklist::before { content: "\f224"; }
+.bi-card-heading::before { content: "\f225"; }
+.bi-card-image::before { content: "\f226"; }
+.bi-card-list::before { content: "\f227"; }
+.bi-card-text::before { content: "\f228"; }
+.bi-caret-down-fill::before { content: "\f229"; }
+.bi-caret-down-square-fill::before { content: "\f22a"; }
+.bi-caret-down-square::before { content: "\f22b"; }
+.bi-caret-down::before { content: "\f22c"; }
+.bi-caret-left-fill::before { content: "\f22d"; }
+.bi-caret-left-square-fill::before { content: "\f22e"; }
+.bi-caret-left-square::before { content: "\f22f"; }
+.bi-caret-left::before { content: "\f230"; }
+.bi-caret-right-fill::before { content: "\f231"; }
+.bi-caret-right-square-fill::before { content: "\f232"; }
+.bi-caret-right-square::before { content: "\f233"; }
+.bi-caret-right::before { content: "\f234"; }
+.bi-caret-up-fill::before { content: "\f235"; }
+.bi-caret-up-square-fill::before { content: "\f236"; }
+.bi-caret-up-square::before { content: "\f237"; }
+.bi-caret-up::before { content: "\f238"; }
+.bi-cart-check-fill::before { content: "\f239"; }
+.bi-cart-check::before { content: "\f23a"; }
+.bi-cart-dash-fill::before { content: "\f23b"; }
+.bi-cart-dash::before { content: "\f23c"; }
+.bi-cart-fill::before { content: "\f23d"; }
+.bi-cart-plus-fill::before { content: "\f23e"; }
+.bi-cart-plus::before { content: "\f23f"; }
+.bi-cart-x-fill::before { content: "\f240"; }
+.bi-cart-x::before { content: "\f241"; }
+.bi-cart::before { content: "\f242"; }
+.bi-cart2::before { content: "\f243"; }
+.bi-cart3::before { content: "\f244"; }
+.bi-cart4::before { content: "\f245"; }
+.bi-cash-stack::before { content: "\f246"; }
+.bi-cash::before { content: "\f247"; }
+.bi-cast::before { content: "\f248"; }
+.bi-chat-dots-fill::before { content: "\f249"; }
+.bi-chat-dots::before { content: "\f24a"; }
+.bi-chat-fill::before { content: "\f24b"; }
+.bi-chat-left-dots-fill::before { content: "\f24c"; }
+.bi-chat-left-dots::before { content: "\f24d"; }
+.bi-chat-left-fill::before { content: "\f24e"; }
+.bi-chat-left-quote-fill::before { content: "\f24f"; }
+.bi-chat-left-quote::before { content: "\f250"; }
+.bi-chat-left-text-fill::before { content: "\f251"; }
+.bi-chat-left-text::before { content: "\f252"; }
+.bi-chat-left::before { content: "\f253"; }
+.bi-chat-quote-fill::before { content: "\f254"; }
+.bi-chat-quote::before { content: "\f255"; }
+.bi-chat-right-dots-fill::before { content: "\f256"; }
+.bi-chat-right-dots::before { content: "\f257"; }
+.bi-chat-right-fill::before { content: "\f258"; }
+.bi-chat-right-quote-fill::before { content: "\f259"; }
+.bi-chat-right-quote::before { content: "\f25a"; }
+.bi-chat-right-text-fill::before { content: "\f25b"; }
+.bi-chat-right-text::before { content: "\f25c"; }
+.bi-chat-right::before { content: "\f25d"; }
+.bi-chat-square-dots-fill::before { content: "\f25e"; }
+.bi-chat-square-dots::before { content: "\f25f"; }
+.bi-chat-square-fill::before { content: "\f260"; }
+.bi-chat-square-quote-fill::before { content: "\f261"; }
+.bi-chat-square-quote::before { content: "\f262"; }
+.bi-chat-square-text-fill::before { content: "\f263"; }
+.bi-chat-square-text::before { content: "\f264"; }
+.bi-chat-square::before { content: "\f265"; }
+.bi-chat-text-fill::before { content: "\f266"; }
+.bi-chat-text::before { content: "\f267"; }
+.bi-chat::before { content: "\f268"; }
+.bi-check-all::before { content: "\f269"; }
+.bi-check-circle-fill::before { content: "\f26a"; }
+.bi-check-circle::before { content: "\f26b"; }
+.bi-check-square-fill::before { content: "\f26c"; }
+.bi-check-square::before { content: "\f26d"; }
+.bi-check::before { content: "\f26e"; }
+.bi-check2-all::before { content: "\f26f"; }
+.bi-check2-circle::before { content: "\f270"; }
+.bi-check2-square::before { content: "\f271"; }
+.bi-check2::before { content: "\f272"; }
+.bi-chevron-bar-contract::before { content: "\f273"; }
+.bi-chevron-bar-down::before { content: "\f274"; }
+.bi-chevron-bar-expand::before { content: "\f275"; }
+.bi-chevron-bar-left::before { content: "\f276"; }
+.bi-chevron-bar-right::before { content: "\f277"; }
+.bi-chevron-bar-up::before { content: "\f278"; }
+.bi-chevron-compact-down::before { content: "\f279"; }
+.bi-chevron-compact-left::before { content: "\f27a"; }
+.bi-chevron-compact-right::before { content: "\f27b"; }
+.bi-chevron-compact-up::before { content: "\f27c"; }
+.bi-chevron-contract::before { content: "\f27d"; }
+.bi-chevron-double-down::before { content: "\f27e"; }
+.bi-chevron-double-left::before { content: "\f27f"; }
+.bi-chevron-double-right::before { content: "\f280"; }
+.bi-chevron-double-up::before { content: "\f281"; }
+.bi-chevron-down::before { content: "\f282"; }
+.bi-chevron-expand::before { content: "\f283"; }
+.bi-chevron-left::before { content: "\f284"; }
+.bi-chevron-right::before { content: "\f285"; }
+.bi-chevron-up::before { content: "\f286"; }
+.bi-circle-fill::before { content: "\f287"; }
+.bi-circle-half::before { content: "\f288"; }
+.bi-circle-square::before { content: "\f289"; }
+.bi-circle::before { content: "\f28a"; }
+.bi-clipboard-check::before { content: "\f28b"; }
+.bi-clipboard-data::before { content: "\f28c"; }
+.bi-clipboard-minus::before { content: "\f28d"; }
+.bi-clipboard-plus::before { content: "\f28e"; }
+.bi-clipboard-x::before { content: "\f28f"; }
+.bi-clipboard::before { content: "\f290"; }
+.bi-clock-fill::before { content: "\f291"; }
+.bi-clock-history::before { content: "\f292"; }
+.bi-clock::before { content: "\f293"; }
+.bi-cloud-arrow-down-fill::before { content: "\f294"; }
+.bi-cloud-arrow-down::before { content: "\f295"; }
+.bi-cloud-arrow-up-fill::before { content: "\f296"; }
+.bi-cloud-arrow-up::before { content: "\f297"; }
+.bi-cloud-check-fill::before { content: "\f298"; }
+.bi-cloud-check::before { content: "\f299"; }
+.bi-cloud-download-fill::before { content: "\f29a"; }
+.bi-cloud-download::before { content: "\f29b"; }
+.bi-cloud-drizzle-fill::before { content: "\f29c"; }
+.bi-cloud-drizzle::before { content: "\f29d"; }
+.bi-cloud-fill::before { content: "\f29e"; }
+.bi-cloud-fog-fill::before { content: "\f29f"; }
+.bi-cloud-fog::before { content: "\f2a0"; }
+.bi-cloud-fog2-fill::before { content: "\f2a1"; }
+.bi-cloud-fog2::before { content: "\f2a2"; }
+.bi-cloud-hail-fill::before { content: "\f2a3"; }
+.bi-cloud-hail::before { content: "\f2a4"; }
+.bi-cloud-haze-fill::before { content: "\f2a6"; }
+.bi-cloud-haze::before { content: "\f2a7"; }
+.bi-cloud-haze2-fill::before { content: "\f2a8"; }
+.bi-cloud-lightning-fill::before { content: "\f2a9"; }
+.bi-cloud-lightning-rain-fill::before { content: "\f2aa"; }
+.bi-cloud-lightning-rain::before { content: "\f2ab"; }
+.bi-cloud-lightning::before { content: "\f2ac"; }
+.bi-cloud-minus-fill::before { content: "\f2ad"; }
+.bi-cloud-minus::before { content: "\f2ae"; }
+.bi-cloud-moon-fill::before { content: "\f2af"; }
+.bi-cloud-moon::before { content: "\f2b0"; }
+.bi-cloud-plus-fill::before { content: "\f2b1"; }
+.bi-cloud-plus::before { content: "\f2b2"; }
+.bi-cloud-rain-fill::before { content: "\f2b3"; }
+.bi-cloud-rain-heavy-fill::before { content: "\f2b4"; }
+.bi-cloud-rain-heavy::before { content: "\f2b5"; }
+.bi-cloud-rain::before { content: "\f2b6"; }
+.bi-cloud-slash-fill::before { content: "\f2b7"; }
+.bi-cloud-slash::before { content: "\f2b8"; }
+.bi-cloud-sleet-fill::before { content: "\f2b9"; }
+.bi-cloud-sleet::before { content: "\f2ba"; }
+.bi-cloud-snow-fill::before { content: "\f2bb"; }
+.bi-cloud-snow::before { content: "\f2bc"; }
+.bi-cloud-sun-fill::before { content: "\f2bd"; }
+.bi-cloud-sun::before { content: "\f2be"; }
+.bi-cloud-upload-fill::before { content: "\f2bf"; }
+.bi-cloud-upload::before { content: "\f2c0"; }
+.bi-cloud::before { content: "\f2c1"; }
+.bi-clouds-fill::before { content: "\f2c2"; }
+.bi-clouds::before { content: "\f2c3"; }
+.bi-cloudy-fill::before { content: "\f2c4"; }
+.bi-cloudy::before { content: "\f2c5"; }
+.bi-code-slash::before { content: "\f2c6"; }
+.bi-code-square::before { content: "\f2c7"; }
+.bi-code::before { content: "\f2c8"; }
+.bi-collection-fill::before { content: "\f2c9"; }
+.bi-collection-play-fill::before { content: "\f2ca"; }
+.bi-collection-play::before { content: "\f2cb"; }
+.bi-collection::before { content: "\f2cc"; }
+.bi-columns-gap::before { content: "\f2cd"; }
+.bi-columns::before { content: "\f2ce"; }
+.bi-command::before { content: "\f2cf"; }
+.bi-compass-fill::before { content: "\f2d0"; }
+.bi-compass::before { content: "\f2d1"; }
+.bi-cone-striped::before { content: "\f2d2"; }
+.bi-cone::before { content: "\f2d3"; }
+.bi-controller::before { content: "\f2d4"; }
+.bi-cpu-fill::before { content: "\f2d5"; }
+.bi-cpu::before { content: "\f2d6"; }
+.bi-credit-card-2-back-fill::before { content: "\f2d7"; }
+.bi-credit-card-2-back::before { content: "\f2d8"; }
+.bi-credit-card-2-front-fill::before { content: "\f2d9"; }
+.bi-credit-card-2-front::before { content: "\f2da"; }
+.bi-credit-card-fill::before { content: "\f2db"; }
+.bi-credit-card::before { content: "\f2dc"; }
+.bi-crop::before { content: "\f2dd"; }
+.bi-cup-fill::before { content: "\f2de"; }
+.bi-cup-straw::before { content: "\f2df"; }
+.bi-cup::before { content: "\f2e0"; }
+.bi-cursor-fill::before { content: "\f2e1"; }
+.bi-cursor-text::before { content: "\f2e2"; }
+.bi-cursor::before { content: "\f2e3"; }
+.bi-dash-circle-dotted::before { content: "\f2e4"; }
+.bi-dash-circle-fill::before { content: "\f2e5"; }
+.bi-dash-circle::before { content: "\f2e6"; }
+.bi-dash-square-dotted::before { content: "\f2e7"; }
+.bi-dash-square-fill::before { content: "\f2e8"; }
+.bi-dash-square::before { content: "\f2e9"; }
+.bi-dash::before { content: "\f2ea"; }
+.bi-diagram-2-fill::before { content: "\f2eb"; }
+.bi-diagram-2::before { content: "\f2ec"; }
+.bi-diagram-3-fill::before { content: "\f2ed"; }
+.bi-diagram-3::before { content: "\f2ee"; }
+.bi-diamond-fill::before { content: "\f2ef"; }
+.bi-diamond-half::before { content: "\f2f0"; }
+.bi-diamond::before { content: "\f2f1"; }
+.bi-dice-1-fill::before { content: "\f2f2"; }
+.bi-dice-1::before { content: "\f2f3"; }
+.bi-dice-2-fill::before { content: "\f2f4"; }
+.bi-dice-2::before { content: "\f2f5"; }
+.bi-dice-3-fill::before { content: "\f2f6"; }
+.bi-dice-3::before { content: "\f2f7"; }
+.bi-dice-4-fill::before { content: "\f2f8"; }
+.bi-dice-4::before { content: "\f2f9"; }
+.bi-dice-5-fill::before { content: "\f2fa"; }
+.bi-dice-5::before { content: "\f2fb"; }
+.bi-dice-6-fill::before { content: "\f2fc"; }
+.bi-dice-6::before { content: "\f2fd"; }
+.bi-disc-fill::before { content: "\f2fe"; }
+.bi-disc::before { content: "\f2ff"; }
+.bi-discord::before { content: "\f300"; }
+.bi-display-fill::before { content: "\f301"; }
+.bi-display::before { content: "\f302"; }
+.bi-distribute-horizontal::before { content: "\f303"; }
+.bi-distribute-vertical::before { content: "\f304"; }
+.bi-door-closed-fill::before { content: "\f305"; }
+.bi-door-closed::before { content: "\f306"; }
+.bi-door-open-fill::before { content: "\f307"; }
+.bi-door-open::before { content: "\f308"; }
+.bi-dot::before { content: "\f309"; }
+.bi-download::before { content: "\f30a"; }
+.bi-droplet-fill::before { content: "\f30b"; }
+.bi-droplet-half::before { content: "\f30c"; }
+.bi-droplet::before { content: "\f30d"; }
+.bi-earbuds::before { content: "\f30e"; }
+.bi-easel-fill::before { content: "\f30f"; }
+.bi-easel::before { content: "\f310"; }
+.bi-egg-fill::before { content: "\f311"; }
+.bi-egg-fried::before { content: "\f312"; }
+.bi-egg::before { content: "\f313"; }
+.bi-eject-fill::before { content: "\f314"; }
+.bi-eject::before { content: "\f315"; }
+.bi-emoji-angry-fill::before { content: "\f316"; }
+.bi-emoji-angry::before { content: "\f317"; }
+.bi-emoji-dizzy-fill::before { content: "\f318"; }
+.bi-emoji-dizzy::before { content: "\f319"; }
+.bi-emoji-expressionless-fill::before { content: "\f31a"; }
+.bi-emoji-expressionless::before { content: "\f31b"; }
+.bi-emoji-frown-fill::before { content: "\f31c"; }
+.bi-emoji-frown::before { content: "\f31d"; }
+.bi-emoji-heart-eyes-fill::before { content: "\f31e"; }
+.bi-emoji-heart-eyes::before { content: "\f31f"; }
+.bi-emoji-laughing-fill::before { content: "\f320"; }
+.bi-emoji-laughing::before { content: "\f321"; }
+.bi-emoji-neutral-fill::before { content: "\f322"; }
+.bi-emoji-neutral::before { content: "\f323"; }
+.bi-emoji-smile-fill::before { content: "\f324"; }
+.bi-emoji-smile-upside-down-fill::before { content: "\f325"; }
+.bi-emoji-smile-upside-down::before { content: "\f326"; }
+.bi-emoji-smile::before { content: "\f327"; }
+.bi-emoji-sunglasses-fill::before { content: "\f328"; }
+.bi-emoji-sunglasses::before { content: "\f329"; }
+.bi-emoji-wink-fill::before { content: "\f32a"; }
+.bi-emoji-wink::before { content: "\f32b"; }
+.bi-envelope-fill::before { content: "\f32c"; }
+.bi-envelope-open-fill::before { content: "\f32d"; }
+.bi-envelope-open::before { content: "\f32e"; }
+.bi-envelope::before { content: "\f32f"; }
+.bi-eraser-fill::before { content: "\f330"; }
+.bi-eraser::before { content: "\f331"; }
+.bi-exclamation-circle-fill::before { content: "\f332"; }
+.bi-exclamation-circle::before { content: "\f333"; }
+.bi-exclamation-diamond-fill::before { content: "\f334"; }
+.bi-exclamation-diamond::before { content: "\f335"; }
+.bi-exclamation-octagon-fill::before { content: "\f336"; }
+.bi-exclamation-octagon::before { content: "\f337"; }
+.bi-exclamation-square-fill::before { content: "\f338"; }
+.bi-exclamation-square::before { content: "\f339"; }
+.bi-exclamation-triangle-fill::before { content: "\f33a"; }
+.bi-exclamation-triangle::before { content: "\f33b"; }
+.bi-exclamation::before { content: "\f33c"; }
+.bi-exclude::before { content: "\f33d"; }
+.bi-eye-fill::before { content: "\f33e"; }
+.bi-eye-slash-fill::before { content: "\f33f"; }
+.bi-eye-slash::before { content: "\f340"; }
+.bi-eye::before { content: "\f341"; }
+.bi-eyedropper::before { content: "\f342"; }
+.bi-eyeglasses::before { content: "\f343"; }
+.bi-facebook::before { content: "\f344"; }
+.bi-file-arrow-down-fill::before { content: "\f345"; }
+.bi-file-arrow-down::before { content: "\f346"; }
+.bi-file-arrow-up-fill::before { content: "\f347"; }
+.bi-file-arrow-up::before { content: "\f348"; }
+.bi-file-bar-graph-fill::before { content: "\f349"; }
+.bi-file-bar-graph::before { content: "\f34a"; }
+.bi-file-binary-fill::before { content: "\f34b"; }
+.bi-file-binary::before { content: "\f34c"; }
+.bi-file-break-fill::before { content: "\f34d"; }
+.bi-file-break::before { content: "\f34e"; }
+.bi-file-check-fill::before { content: "\f34f"; }
+.bi-file-check::before { content: "\f350"; }
+.bi-file-code-fill::before { content: "\f351"; }
+.bi-file-code::before { content: "\f352"; }
+.bi-file-diff-fill::before { content: "\f353"; }
+.bi-file-diff::before { content: "\f354"; }
+.bi-file-earmark-arrow-down-fill::before { content: "\f355"; }
+.bi-file-earmark-arrow-down::before { content: "\f356"; }
+.bi-file-earmark-arrow-up-fill::before { content: "\f357"; }
+.bi-file-earmark-arrow-up::before { content: "\f358"; }
+.bi-file-earmark-bar-graph-fill::before { content: "\f359"; }
+.bi-file-earmark-bar-graph::before { content: "\f35a"; }
+.bi-file-earmark-binary-fill::before { content: "\f35b"; }
+.bi-file-earmark-binary::before { content: "\f35c"; }
+.bi-file-earmark-break-fill::before { content: "\f35d"; }
+.bi-file-earmark-break::before { content: "\f35e"; }
+.bi-file-earmark-check-fill::before { content: "\f35f"; }
+.bi-file-earmark-check::before { content: "\f360"; }
+.bi-file-earmark-code-fill::before { content: "\f361"; }
+.bi-file-earmark-code::before { content: "\f362"; }
+.bi-file-earmark-diff-fill::before { content: "\f363"; }
+.bi-file-earmark-diff::before { content: "\f364"; }
+.bi-file-earmark-easel-fill::before { content: "\f365"; }
+.bi-file-earmark-easel::before { content: "\f366"; }
+.bi-file-earmark-excel-fill::before { content: "\f367"; }
+.bi-file-earmark-excel::before { content: "\f368"; }
+.bi-file-earmark-fill::before { content: "\f369"; }
+.bi-file-earmark-font-fill::before { content: "\f36a"; }
+.bi-file-earmark-font::before { content: "\f36b"; }
+.bi-file-earmark-image-fill::before { content: "\f36c"; }
+.bi-file-earmark-image::before { content: "\f36d"; }
+.bi-file-earmark-lock-fill::before { content: "\f36e"; }
+.bi-file-earmark-lock::before { content: "\f36f"; }
+.bi-file-earmark-lock2-fill::before { content: "\f370"; }
+.bi-file-earmark-lock2::before { content: "\f371"; }
+.bi-file-earmark-medical-fill::before { content: "\f372"; }
+.bi-file-earmark-medical::before { content: "\f373"; }
+.bi-file-earmark-minus-fill::before { content: "\f374"; }
+.bi-file-earmark-minus::before { content: "\f375"; }
+.bi-file-earmark-music-fill::before { content: "\f376"; }
+.bi-file-earmark-music::before { content: "\f377"; }
+.bi-file-earmark-person-fill::before { content: "\f378"; }
+.bi-file-earmark-person::before { content: "\f379"; }
+.bi-file-earmark-play-fill::before { content: "\f37a"; }
+.bi-file-earmark-play::before { content: "\f37b"; }
+.bi-file-earmark-plus-fill::before { content: "\f37c"; }
+.bi-file-earmark-plus::before { content: "\f37d"; }
+.bi-file-earmark-post-fill::before { content: "\f37e"; }
+.bi-file-earmark-post::before { content: "\f37f"; }
+.bi-file-earmark-ppt-fill::before { content: "\f380"; }
+.bi-file-earmark-ppt::before { content: "\f381"; }
+.bi-file-earmark-richtext-fill::before { content: "\f382"; }
+.bi-file-earmark-richtext::before { content: "\f383"; }
+.bi-file-earmark-ruled-fill::before { content: "\f384"; }
+.bi-file-earmark-ruled::before { content: "\f385"; }
+.bi-file-earmark-slides-fill::before { content: "\f386"; }
+.bi-file-earmark-slides::before { content: "\f387"; }
+.bi-file-earmark-spreadsheet-fill::before { content: "\f388"; }
+.bi-file-earmark-spreadsheet::before { content: "\f389"; }
+.bi-file-earmark-text-fill::before { content: "\f38a"; }
+.bi-file-earmark-text::before { content: "\f38b"; }
+.bi-file-earmark-word-fill::before { content: "\f38c"; }
+.bi-file-earmark-word::before { content: "\f38d"; }
+.bi-file-earmark-x-fill::before { content: "\f38e"; }
+.bi-file-earmark-x::before { content: "\f38f"; }
+.bi-file-earmark-zip-fill::before { content: "\f390"; }
+.bi-file-earmark-zip::before { content: "\f391"; }
+.bi-file-earmark::before { content: "\f392"; }
+.bi-file-easel-fill::before { content: "\f393"; }
+.bi-file-easel::before { content: "\f394"; }
+.bi-file-excel-fill::before { content: "\f395"; }
+.bi-file-excel::before { content: "\f396"; }
+.bi-file-fill::before { content: "\f397"; }
+.bi-file-font-fill::before { content: "\f398"; }
+.bi-file-font::before { content: "\f399"; }
+.bi-file-image-fill::before { content: "\f39a"; }
+.bi-file-image::before { content: "\f39b"; }
+.bi-file-lock-fill::before { content: "\f39c"; }
+.bi-file-lock::before { content: "\f39d"; }
+.bi-file-lock2-fill::before { content: "\f39e"; }
+.bi-file-lock2::before { content: "\f39f"; }
+.bi-file-medical-fill::before { content: "\f3a0"; }
+.bi-file-medical::before { content: "\f3a1"; }
+.bi-file-minus-fill::before { content: "\f3a2"; }
+.bi-file-minus::before { content: "\f3a3"; }
+.bi-file-music-fill::before { content: "\f3a4"; }
+.bi-file-music::before { content: "\f3a5"; }
+.bi-file-person-fill::before { content: "\f3a6"; }
+.bi-file-person::before { content: "\f3a7"; }
+.bi-file-play-fill::before { content: "\f3a8"; }
+.bi-file-play::before { content: "\f3a9"; }
+.bi-file-plus-fill::before { content: "\f3aa"; }
+.bi-file-plus::before { content: "\f3ab"; }
+.bi-file-post-fill::before { content: "\f3ac"; }
+.bi-file-post::before { content: "\f3ad"; }
+.bi-file-ppt-fill::before { content: "\f3ae"; }
+.bi-file-ppt::before { content: "\f3af"; }
+.bi-file-richtext-fill::before { content: "\f3b0"; }
+.bi-file-richtext::before { content: "\f3b1"; }
+.bi-file-ruled-fill::before { content: "\f3b2"; }
+.bi-file-ruled::before { content: "\f3b3"; }
+.bi-file-slides-fill::before { content: "\f3b4"; }
+.bi-file-slides::before { content: "\f3b5"; }
+.bi-file-spreadsheet-fill::before { content: "\f3b6"; }
+.bi-file-spreadsheet::before { content: "\f3b7"; }
+.bi-file-text-fill::before { content: "\f3b8"; }
+.bi-file-text::before { content: "\f3b9"; }
+.bi-file-word-fill::before { content: "\f3ba"; }
+.bi-file-word::before { content: "\f3bb"; }
+.bi-file-x-fill::before { content: "\f3bc"; }
+.bi-file-x::before { content: "\f3bd"; }
+.bi-file-zip-fill::before { content: "\f3be"; }
+.bi-file-zip::before { content: "\f3bf"; }
+.bi-file::before { content: "\f3c0"; }
+.bi-files-alt::before { content: "\f3c1"; }
+.bi-files::before { content: "\f3c2"; }
+.bi-film::before { content: "\f3c3"; }
+.bi-filter-circle-fill::before { content: "\f3c4"; }
+.bi-filter-circle::before { content: "\f3c5"; }
+.bi-filter-left::before { content: "\f3c6"; }
+.bi-filter-right::before { content: "\f3c7"; }
+.bi-filter-square-fill::before { content: "\f3c8"; }
+.bi-filter-square::before { content: "\f3c9"; }
+.bi-filter::before { content: "\f3ca"; }
+.bi-flag-fill::before { content: "\f3cb"; }
+.bi-flag::before { content: "\f3cc"; }
+.bi-flower1::before { content: "\f3cd"; }
+.bi-flower2::before { content: "\f3ce"; }
+.bi-flower3::before { content: "\f3cf"; }
+.bi-folder-check::before { content: "\f3d0"; }
+.bi-folder-fill::before { content: "\f3d1"; }
+.bi-folder-minus::before { content: "\f3d2"; }
+.bi-folder-plus::before { content: "\f3d3"; }
+.bi-folder-symlink-fill::before { content: "\f3d4"; }
+.bi-folder-symlink::before { content: "\f3d5"; }
+.bi-folder-x::before { content: "\f3d6"; }
+.bi-folder::before { content: "\f3d7"; }
+.bi-folder2-open::before { content: "\f3d8"; }
+.bi-folder2::before { content: "\f3d9"; }
+.bi-fonts::before { content: "\f3da"; }
+.bi-forward-fill::before { content: "\f3db"; }
+.bi-forward::before { content: "\f3dc"; }
+.bi-front::before { content: "\f3dd"; }
+.bi-fullscreen-exit::before { content: "\f3de"; }
+.bi-fullscreen::before { content: "\f3df"; }
+.bi-funnel-fill::before { content: "\f3e0"; }
+.bi-funnel::before { content: "\f3e1"; }
+.bi-gear-fill::before { content: "\f3e2"; }
+.bi-gear-wide-connected::before { content: "\f3e3"; }
+.bi-gear-wide::before { content: "\f3e4"; }
+.bi-gear::before { content: "\f3e5"; }
+.bi-gem::before { content: "\f3e6"; }
+.bi-geo-alt-fill::before { content: "\f3e7"; }
+.bi-geo-alt::before { content: "\f3e8"; }
+.bi-geo-fill::before { content: "\f3e9"; }
+.bi-geo::before { content: "\f3ea"; }
+.bi-gift-fill::before { content: "\f3eb"; }
+.bi-gift::before { content: "\f3ec"; }
+.bi-github::before { content: "\f3ed"; }
+.bi-globe::before { content: "\f3ee"; }
+.bi-globe2::before { content: "\f3ef"; }
+.bi-google::before { content: "\f3f0"; }
+.bi-graph-down::before { content: "\f3f1"; }
+.bi-graph-up::before { content: "\f3f2"; }
+.bi-grid-1x2-fill::before { content: "\f3f3"; }
+.bi-grid-1x2::before { content: "\f3f4"; }
+.bi-grid-3x2-gap-fill::before { content: "\f3f5"; }
+.bi-grid-3x2-gap::before { content: "\f3f6"; }
+.bi-grid-3x2::before { content: "\f3f7"; }
+.bi-grid-3x3-gap-fill::before { content: "\f3f8"; }
+.bi-grid-3x3-gap::before { content: "\f3f9"; }
+.bi-grid-3x3::before { content: "\f3fa"; }
+.bi-grid-fill::before { content: "\f3fb"; }
+.bi-grid::before { content: "\f3fc"; }
+.bi-grip-horizontal::before { content: "\f3fd"; }
+.bi-grip-vertical::before { content: "\f3fe"; }
+.bi-hammer::before { content: "\f3ff"; }
+.bi-hand-index-fill::before { content: "\f400"; }
+.bi-hand-index-thumb-fill::before { content: "\f401"; }
+.bi-hand-index-thumb::before { content: "\f402"; }
+.bi-hand-index::before { content: "\f403"; }
+.bi-hand-thumbs-down-fill::before { content: "\f404"; }
+.bi-hand-thumbs-down::before { content: "\f405"; }
+.bi-hand-thumbs-up-fill::before { content: "\f406"; }
+.bi-hand-thumbs-up::before { content: "\f407"; }
+.bi-handbag-fill::before { content: "\f408"; }
+.bi-handbag::before { content: "\f409"; }
+.bi-hash::before { content: "\f40a"; }
+.bi-hdd-fill::before { content: "\f40b"; }
+.bi-hdd-network-fill::before { content: "\f40c"; }
+.bi-hdd-network::before { content: "\f40d"; }
+.bi-hdd-rack-fill::before { content: "\f40e"; }
+.bi-hdd-rack::before { content: "\f40f"; }
+.bi-hdd-stack-fill::before { content: "\f410"; }
+.bi-hdd-stack::before { content: "\f411"; }
+.bi-hdd::before { content: "\f412"; }
+.bi-headphones::before { content: "\f413"; }
+.bi-headset::before { content: "\f414"; }
+.bi-heart-fill::before { content: "\f415"; }
+.bi-heart-half::before { content: "\f416"; }
+.bi-heart::before { content: "\f417"; }
+.bi-heptagon-fill::before { content: "\f418"; }
+.bi-heptagon-half::before { content: "\f419"; }
+.bi-heptagon::before { content: "\f41a"; }
+.bi-hexagon-fill::before { content: "\f41b"; }
+.bi-hexagon-half::before { content: "\f41c"; }
+.bi-hexagon::before { content: "\f41d"; }
+.bi-hourglass-bottom::before { content: "\f41e"; }
+.bi-hourglass-split::before { content: "\f41f"; }
+.bi-hourglass-top::before { content: "\f420"; }
+.bi-hourglass::before { content: "\f421"; }
+.bi-house-door-fill::before { content: "\f422"; }
+.bi-house-door::before { content: "\f423"; }
+.bi-house-fill::before { content: "\f424"; }
+.bi-house::before { content: "\f425"; }
+.bi-hr::before { content: "\f426"; }
+.bi-hurricane::before { content: "\f427"; }
+.bi-image-alt::before { content: "\f428"; }
+.bi-image-fill::before { content: "\f429"; }
+.bi-image::before { content: "\f42a"; }
+.bi-images::before { content: "\f42b"; }
+.bi-inbox-fill::before { content: "\f42c"; }
+.bi-inbox::before { content: "\f42d"; }
+.bi-inboxes-fill::before { content: "\f42e"; }
+.bi-inboxes::before { content: "\f42f"; }
+.bi-info-circle-fill::before { content: "\f430"; }
+.bi-info-circle::before { content: "\f431"; }
+.bi-info-square-fill::before { content: "\f432"; }
+.bi-info-square::before { content: "\f433"; }
+.bi-info::before { content: "\f434"; }
+.bi-input-cursor-text::before { content: "\f435"; }
+.bi-input-cursor::before { content: "\f436"; }
+.bi-instagram::before { content: "\f437"; }
+.bi-intersect::before { content: "\f438"; }
+.bi-journal-album::before { content: "\f439"; }
+.bi-journal-arrow-down::before { content: "\f43a"; }
+.bi-journal-arrow-up::before { content: "\f43b"; }
+.bi-journal-bookmark-fill::before { content: "\f43c"; }
+.bi-journal-bookmark::before { content: "\f43d"; }
+.bi-journal-check::before { content: "\f43e"; }
+.bi-journal-code::before { content: "\f43f"; }
+.bi-journal-medical::before { content: "\f440"; }
+.bi-journal-minus::before { content: "\f441"; }
+.bi-journal-plus::before { content: "\f442"; }
+.bi-journal-richtext::before { content: "\f443"; }
+.bi-journal-text::before { content: "\f444"; }
+.bi-journal-x::before { content: "\f445"; }
+.bi-journal::before { content: "\f446"; }
+.bi-journals::before { content: "\f447"; }
+.bi-joystick::before { content: "\f448"; }
+.bi-justify-left::before { content: "\f449"; }
+.bi-justify-right::before { content: "\f44a"; }
+.bi-justify::before { content: "\f44b"; }
+.bi-kanban-fill::before { content: "\f44c"; }
+.bi-kanban::before { content: "\f44d"; }
+.bi-key-fill::before { content: "\f44e"; }
+.bi-key::before { content: "\f44f"; }
+.bi-keyboard-fill::before { content: "\f450"; }
+.bi-keyboard::before { content: "\f451"; }
+.bi-ladder::before { content: "\f452"; }
+.bi-lamp-fill::before { content: "\f453"; }
+.bi-lamp::before { content: "\f454"; }
+.bi-laptop-fill::before { content: "\f455"; }
+.bi-laptop::before { content: "\f456"; }
+.bi-layer-backward::before { content: "\f457"; }
+.bi-layer-forward::before { content: "\f458"; }
+.bi-layers-fill::before { content: "\f459"; }
+.bi-layers-half::before { content: "\f45a"; }
+.bi-layers::before { content: "\f45b"; }
+.bi-layout-sidebar-inset-reverse::before { content: "\f45c"; }
+.bi-layout-sidebar-inset::before { content: "\f45d"; }
+.bi-layout-sidebar-reverse::before { content: "\f45e"; }
+.bi-layout-sidebar::before { content: "\f45f"; }
+.bi-layout-split::before { content: "\f460"; }
+.bi-layout-text-sidebar-reverse::before { content: "\f461"; }
+.bi-layout-text-sidebar::before { content: "\f462"; }
+.bi-layout-text-window-reverse::before { content: "\f463"; }
+.bi-layout-text-window::before { content: "\f464"; }
+.bi-layout-three-columns::before { content: "\f465"; }
+.bi-layout-wtf::before { content: "\f466"; }
+.bi-life-preserver::before { content: "\f467"; }
+.bi-lightbulb-fill::before { content: "\f468"; }
+.bi-lightbulb-off-fill::before { content: "\f469"; }
+.bi-lightbulb-off::before { content: "\f46a"; }
+.bi-lightbulb::before { content: "\f46b"; }
+.bi-lightning-charge-fill::before { content: "\f46c"; }
+.bi-lightning-charge::before { content: "\f46d"; }
+.bi-lightning-fill::before { content: "\f46e"; }
+.bi-lightning::before { content: "\f46f"; }
+.bi-link-45deg::before { content: "\f470"; }
+.bi-link::before { content: "\f471"; }
+.bi-linkedin::before { content: "\f472"; }
+.bi-list-check::before { content: "\f473"; }
+.bi-list-nested::before { content: "\f474"; }
+.bi-list-ol::before { content: "\f475"; }
+.bi-list-stars::before { content: "\f476"; }
+.bi-list-task::before { content: "\f477"; }
+.bi-list-ul::before { content: "\f478"; }
+.bi-list::before { content: "\f479"; }
+.bi-lock-fill::before { content: "\f47a"; }
+.bi-lock::before { content: "\f47b"; }
+.bi-mailbox::before { content: "\f47c"; }
+.bi-mailbox2::before { content: "\f47d"; }
+.bi-map-fill::before { content: "\f47e"; }
+.bi-map::before { content: "\f47f"; }
+.bi-markdown-fill::before { content: "\f480"; }
+.bi-markdown::before { content: "\f481"; }
+.bi-mask::before { content: "\f482"; }
+.bi-megaphone-fill::before { content: "\f483"; }
+.bi-megaphone::before { content: "\f484"; }
+.bi-menu-app-fill::before { content: "\f485"; }
+.bi-menu-app::before { content: "\f486"; }
+.bi-menu-button-fill::before { content: "\f487"; }
+.bi-menu-button-wide-fill::before { content: "\f488"; }
+.bi-menu-button-wide::before { content: "\f489"; }
+.bi-menu-button::before { content: "\f48a"; }
+.bi-menu-down::before { content: "\f48b"; }
+.bi-menu-up::before { content: "\f48c"; }
+.bi-mic-fill::before { content: "\f48d"; }
+.bi-mic-mute-fill::before { content: "\f48e"; }
+.bi-mic-mute::before { content: "\f48f"; }
+.bi-mic::before { content: "\f490"; }
+.bi-minecart-loaded::before { content: "\f491"; }
+.bi-minecart::before { content: "\f492"; }
+.bi-moisture::before { content: "\f493"; }
+.bi-moon-fill::before { content: "\f494"; }
+.bi-moon-stars-fill::before { content: "\f495"; }
+.bi-moon-stars::before { content: "\f496"; }
+.bi-moon::before { content: "\f497"; }
+.bi-mouse-fill::before { content: "\f498"; }
+.bi-mouse::before { content: "\f499"; }
+.bi-mouse2-fill::before { content: "\f49a"; }
+.bi-mouse2::before { content: "\f49b"; }
+.bi-mouse3-fill::before { content: "\f49c"; }
+.bi-mouse3::before { content: "\f49d"; }
+.bi-music-note-beamed::before { content: "\f49e"; }
+.bi-music-note-list::before { content: "\f49f"; }
+.bi-music-note::before { content: "\f4a0"; }
+.bi-music-player-fill::before { content: "\f4a1"; }
+.bi-music-player::before { content: "\f4a2"; }
+.bi-newspaper::before { content: "\f4a3"; }
+.bi-node-minus-fill::before { content: "\f4a4"; }
+.bi-node-minus::before { content: "\f4a5"; }
+.bi-node-plus-fill::before { content: "\f4a6"; }
+.bi-node-plus::before { content: "\f4a7"; }
+.bi-nut-fill::before { content: "\f4a8"; }
+.bi-nut::before { content: "\f4a9"; }
+.bi-octagon-fill::before { content: "\f4aa"; }
+.bi-octagon-half::before { content: "\f4ab"; }
+.bi-octagon::before { content: "\f4ac"; }
+.bi-option::before { content: "\f4ad"; }
+.bi-outlet::before { content: "\f4ae"; }
+.bi-paint-bucket::before { content: "\f4af"; }
+.bi-palette-fill::before { content: "\f4b0"; }
+.bi-palette::before { content: "\f4b1"; }
+.bi-palette2::before { content: "\f4b2"; }
+.bi-paperclip::before { content: "\f4b3"; }
+.bi-paragraph::before { content: "\f4b4"; }
+.bi-patch-check-fill::before { content: "\f4b5"; }
+.bi-patch-check::before { content: "\f4b6"; }
+.bi-patch-exclamation-fill::before { content: "\f4b7"; }
+.bi-patch-exclamation::before { content: "\f4b8"; }
+.bi-patch-minus-fill::before { content: "\f4b9"; }
+.bi-patch-minus::before { content: "\f4ba"; }
+.bi-patch-plus-fill::before { content: "\f4bb"; }
+.bi-patch-plus::before { content: "\f4bc"; }
+.bi-patch-question-fill::before { content: "\f4bd"; }
+.bi-patch-question::before { content: "\f4be"; }
+.bi-pause-btn-fill::before { content: "\f4bf"; }
+.bi-pause-btn::before { content: "\f4c0"; }
+.bi-pause-circle-fill::before { content: "\f4c1"; }
+.bi-pause-circle::before { content: "\f4c2"; }
+.bi-pause-fill::before { content: "\f4c3"; }
+.bi-pause::before { content: "\f4c4"; }
+.bi-peace-fill::before { content: "\f4c5"; }
+.bi-peace::before { content: "\f4c6"; }
+.bi-pen-fill::before { content: "\f4c7"; }
+.bi-pen::before { content: "\f4c8"; }
+.bi-pencil-fill::before { content: "\f4c9"; }
+.bi-pencil-square::before { content: "\f4ca"; }
+.bi-pencil::before { content: "\f4cb"; }
+.bi-pentagon-fill::before { content: "\f4cc"; }
+.bi-pentagon-half::before { content: "\f4cd"; }
+.bi-pentagon::before { content: "\f4ce"; }
+.bi-people-fill::before { content: "\f4cf"; }
+.bi-people::before { content: "\f4d0"; }
+.bi-percent::before { content: "\f4d1"; }
+.bi-person-badge-fill::before { content: "\f4d2"; }
+.bi-person-badge::before { content: "\f4d3"; }
+.bi-person-bounding-box::before { content: "\f4d4"; }
+.bi-person-check-fill::before { content: "\f4d5"; }
+.bi-person-check::before { content: "\f4d6"; }
+.bi-person-circle::before { content: "\f4d7"; }
+.bi-person-dash-fill::before { content: "\f4d8"; }
+.bi-person-dash::before { content: "\f4d9"; }
+.bi-person-fill::before { content: "\f4da"; }
+.bi-person-lines-fill::before { content: "\f4db"; }
+.bi-person-plus-fill::before { content: "\f4dc"; }
+.bi-person-plus::before { content: "\f4dd"; }
+.bi-person-square::before { content: "\f4de"; }
+.bi-person-x-fill::before { content: "\f4df"; }
+.bi-person-x::before { content: "\f4e0"; }
+.bi-person::before { content: "\f4e1"; }
+.bi-phone-fill::before { content: "\f4e2"; }
+.bi-phone-landscape-fill::before { content: "\f4e3"; }
+.bi-phone-landscape::before { content: "\f4e4"; }
+.bi-phone-vibrate-fill::before { content: "\f4e5"; }
+.bi-phone-vibrate::before { content: "\f4e6"; }
+.bi-phone::before { content: "\f4e7"; }
+.bi-pie-chart-fill::before { content: "\f4e8"; }
+.bi-pie-chart::before { content: "\f4e9"; }
+.bi-pin-angle-fill::before { content: "\f4ea"; }
+.bi-pin-angle::before { content: "\f4eb"; }
+.bi-pin-fill::before { content: "\f4ec"; }
+.bi-pin::before { content: "\f4ed"; }
+.bi-pip-fill::before { content: "\f4ee"; }
+.bi-pip::before { content: "\f4ef"; }
+.bi-play-btn-fill::before { content: "\f4f0"; }
+.bi-play-btn::before { content: "\f4f1"; }
+.bi-play-circle-fill::before { content: "\f4f2"; }
+.bi-play-circle::before { content: "\f4f3"; }
+.bi-play-fill::before { content: "\f4f4"; }
+.bi-play::before { content: "\f4f5"; }
+.bi-plug-fill::before { content: "\f4f6"; }
+.bi-plug::before { content: "\f4f7"; }
+.bi-plus-circle-dotted::before { content: "\f4f8"; }
+.bi-plus-circle-fill::before { content: "\f4f9"; }
+.bi-plus-circle::before { content: "\f4fa"; }
+.bi-plus-square-dotted::before { content: "\f4fb"; }
+.bi-plus-square-fill::before { content: "\f4fc"; }
+.bi-plus-square::before { content: "\f4fd"; }
+.bi-plus::before { content: "\f4fe"; }
+.bi-power::before { content: "\f4ff"; }
+.bi-printer-fill::before { content: "\f500"; }
+.bi-printer::before { content: "\f501"; }
+.bi-puzzle-fill::before { content: "\f502"; }
+.bi-puzzle::before { content: "\f503"; }
+.bi-question-circle-fill::before { content: "\f504"; }
+.bi-question-circle::before { content: "\f505"; }
+.bi-question-diamond-fill::before { content: "\f506"; }
+.bi-question-diamond::before { content: "\f507"; }
+.bi-question-octagon-fill::before { content: "\f508"; }
+.bi-question-octagon::before { content: "\f509"; }
+.bi-question-square-fill::before { content: "\f50a"; }
+.bi-question-square::before { content: "\f50b"; }
+.bi-question::before { content: "\f50c"; }
+.bi-rainbow::before { content: "\f50d"; }
+.bi-receipt-cutoff::before { content: "\f50e"; }
+.bi-receipt::before { content: "\f50f"; }
+.bi-reception-0::before { content: "\f510"; }
+.bi-reception-1::before { content: "\f511"; }
+.bi-reception-2::before { content: "\f512"; }
+.bi-reception-3::before { content: "\f513"; }
+.bi-reception-4::before { content: "\f514"; }
+.bi-record-btn-fill::before { content: "\f515"; }
+.bi-record-btn::before { content: "\f516"; }
+.bi-record-circle-fill::before { content: "\f517"; }
+.bi-record-circle::before { content: "\f518"; }
+.bi-record-fill::before { content: "\f519"; }
+.bi-record::before { content: "\f51a"; }
+.bi-record2-fill::before { content: "\f51b"; }
+.bi-record2::before { content: "\f51c"; }
+.bi-reply-all-fill::before { content: "\f51d"; }
+.bi-reply-all::before { content: "\f51e"; }
+.bi-reply-fill::before { content: "\f51f"; }
+.bi-reply::before { content: "\f520"; }
+.bi-rss-fill::before { content: "\f521"; }
+.bi-rss::before { content: "\f522"; }
+.bi-rulers::before { content: "\f523"; }
+.bi-save-fill::before { content: "\f524"; }
+.bi-save::before { content: "\f525"; }
+.bi-save2-fill::before { content: "\f526"; }
+.bi-save2::before { content: "\f527"; }
+.bi-scissors::before { content: "\f528"; }
+.bi-screwdriver::before { content: "\f529"; }
+.bi-search::before { content: "\f52a"; }
+.bi-segmented-nav::before { content: "\f52b"; }
+.bi-server::before { content: "\f52c"; }
+.bi-share-fill::before { content: "\f52d"; }
+.bi-share::before { content: "\f52e"; }
+.bi-shield-check::before { content: "\f52f"; }
+.bi-shield-exclamation::before { content: "\f530"; }
+.bi-shield-fill-check::before { content: "\f531"; }
+.bi-shield-fill-exclamation::before { content: "\f532"; }
+.bi-shield-fill-minus::before { content: "\f533"; }
+.bi-shield-fill-plus::before { content: "\f534"; }
+.bi-shield-fill-x::before { content: "\f535"; }
+.bi-shield-fill::before { content: "\f536"; }
+.bi-shield-lock-fill::before { content: "\f537"; }
+.bi-shield-lock::before { content: "\f538"; }
+.bi-shield-minus::before { content: "\f539"; }
+.bi-shield-plus::before { content: "\f53a"; }
+.bi-shield-shaded::before { content: "\f53b"; }
+.bi-shield-slash-fill::before { content: "\f53c"; }
+.bi-shield-slash::before { content: "\f53d"; }
+.bi-shield-x::before { content: "\f53e"; }
+.bi-shield::before { content: "\f53f"; }
+.bi-shift-fill::before { content: "\f540"; }
+.bi-shift::before { content: "\f541"; }
+.bi-shop-window::before { content: "\f542"; }
+.bi-shop::before { content: "\f543"; }
+.bi-shuffle::before { content: "\f544"; }
+.bi-signpost-2-fill::before { content: "\f545"; }
+.bi-signpost-2::before { content: "\f546"; }
+.bi-signpost-fill::before { content: "\f547"; }
+.bi-signpost-split-fill::before { content: "\f548"; }
+.bi-signpost-split::before { content: "\f549"; }
+.bi-signpost::before { content: "\f54a"; }
+.bi-sim-fill::before { content: "\f54b"; }
+.bi-sim::before { content: "\f54c"; }
+.bi-skip-backward-btn-fill::before { content: "\f54d"; }
+.bi-skip-backward-btn::before { content: "\f54e"; }
+.bi-skip-backward-circle-fill::before { content: "\f54f"; }
+.bi-skip-backward-circle::before { content: "\f550"; }
+.bi-skip-backward-fill::before { content: "\f551"; }
+.bi-skip-backward::before { content: "\f552"; }
+.bi-skip-end-btn-fill::before { content: "\f553"; }
+.bi-skip-end-btn::before { content: "\f554"; }
+.bi-skip-end-circle-fill::before { content: "\f555"; }
+.bi-skip-end-circle::before { content: "\f556"; }
+.bi-skip-end-fill::before { content: "\f557"; }
+.bi-skip-end::before { content: "\f558"; }
+.bi-skip-forward-btn-fill::before { content: "\f559"; }
+.bi-skip-forward-btn::before { content: "\f55a"; }
+.bi-skip-forward-circle-fill::before { content: "\f55b"; }
+.bi-skip-forward-circle::before { content: "\f55c"; }
+.bi-skip-forward-fill::before { content: "\f55d"; }
+.bi-skip-forward::before { content: "\f55e"; }
+.bi-skip-start-btn-fill::before { content: "\f55f"; }
+.bi-skip-start-btn::before { content: "\f560"; }
+.bi-skip-start-circle-fill::before { content: "\f561"; }
+.bi-skip-start-circle::before { content: "\f562"; }
+.bi-skip-start-fill::before { content: "\f563"; }
+.bi-skip-start::before { content: "\f564"; }
+.bi-slack::before { content: "\f565"; }
+.bi-slash-circle-fill::before { content: "\f566"; }
+.bi-slash-circle::before { content: "\f567"; }
+.bi-slash-square-fill::before { content: "\f568"; }
+.bi-slash-square::before { content: "\f569"; }
+.bi-slash::before { content: "\f56a"; }
+.bi-sliders::before { content: "\f56b"; }
+.bi-smartwatch::before { content: "\f56c"; }
+.bi-snow::before { content: "\f56d"; }
+.bi-snow2::before { content: "\f56e"; }
+.bi-snow3::before { content: "\f56f"; }
+.bi-sort-alpha-down-alt::before { content: "\f570"; }
+.bi-sort-alpha-down::before { content: "\f571"; }
+.bi-sort-alpha-up-alt::before { content: "\f572"; }
+.bi-sort-alpha-up::before { content: "\f573"; }
+.bi-sort-down-alt::before { content: "\f574"; }
+.bi-sort-down::before { content: "\f575"; }
+.bi-sort-numeric-down-alt::before { content: "\f576"; }
+.bi-sort-numeric-down::before { content: "\f577"; }
+.bi-sort-numeric-up-alt::before { content: "\f578"; }
+.bi-sort-numeric-up::before { content: "\f579"; }
+.bi-sort-up-alt::before { content: "\f57a"; }
+.bi-sort-up::before { content: "\f57b"; }
+.bi-soundwave::before { content: "\f57c"; }
+.bi-speaker-fill::before { content: "\f57d"; }
+.bi-speaker::before { content: "\f57e"; }
+.bi-speedometer::before { content: "\f57f"; }
+.bi-speedometer2::before { content: "\f580"; }
+.bi-spellcheck::before { content: "\f581"; }
+.bi-square-fill::before { content: "\f582"; }
+.bi-square-half::before { content: "\f583"; }
+.bi-square::before { content: "\f584"; }
+.bi-stack::before { content: "\f585"; }
+.bi-star-fill::before { content: "\f586"; }
+.bi-star-half::before { content: "\f587"; }
+.bi-star::before { content: "\f588"; }
+.bi-stars::before { content: "\f589"; }
+.bi-stickies-fill::before { content: "\f58a"; }
+.bi-stickies::before { content: "\f58b"; }
+.bi-sticky-fill::before { content: "\f58c"; }
+.bi-sticky::before { content: "\f58d"; }
+.bi-stop-btn-fill::before { content: "\f58e"; }
+.bi-stop-btn::before { content: "\f58f"; }
+.bi-stop-circle-fill::before { content: "\f590"; }
+.bi-stop-circle::before { content: "\f591"; }
+.bi-stop-fill::before { content: "\f592"; }
+.bi-stop::before { content: "\f593"; }
+.bi-stoplights-fill::before { content: "\f594"; }
+.bi-stoplights::before { content: "\f595"; }
+.bi-stopwatch-fill::before { content: "\f596"; }
+.bi-stopwatch::before { content: "\f597"; }
+.bi-subtract::before { content: "\f598"; }
+.bi-suit-club-fill::before { content: "\f599"; }
+.bi-suit-club::before { content: "\f59a"; }
+.bi-suit-diamond-fill::before { content: "\f59b"; }
+.bi-suit-diamond::before { content: "\f59c"; }
+.bi-suit-heart-fill::before { content: "\f59d"; }
+.bi-suit-heart::before { content: "\f59e"; }
+.bi-suit-spade-fill::before { content: "\f59f"; }
+.bi-suit-spade::before { content: "\f5a0"; }
+.bi-sun-fill::before { content: "\f5a1"; }
+.bi-sun::before { content: "\f5a2"; }
+.bi-sunglasses::before { content: "\f5a3"; }
+.bi-sunrise-fill::before { content: "\f5a4"; }
+.bi-sunrise::before { content: "\f5a5"; }
+.bi-sunset-fill::before { content: "\f5a6"; }
+.bi-sunset::before { content: "\f5a7"; }
+.bi-symmetry-horizontal::before { content: "\f5a8"; }
+.bi-symmetry-vertical::before { content: "\f5a9"; }
+.bi-table::before { content: "\f5aa"; }
+.bi-tablet-fill::before { content: "\f5ab"; }
+.bi-tablet-landscape-fill::before { content: "\f5ac"; }
+.bi-tablet-landscape::before { content: "\f5ad"; }
+.bi-tablet::before { content: "\f5ae"; }
+.bi-tag-fill::before { content: "\f5af"; }
+.bi-tag::before { content: "\f5b0"; }
+.bi-tags-fill::before { content: "\f5b1"; }
+.bi-tags::before { content: "\f5b2"; }
+.bi-telegram::before { content: "\f5b3"; }
+.bi-telephone-fill::before { content: "\f5b4"; }
+.bi-telephone-forward-fill::before { content: "\f5b5"; }
+.bi-telephone-forward::before { content: "\f5b6"; }
+.bi-telephone-inbound-fill::before { content: "\f5b7"; }
+.bi-telephone-inbound::before { content: "\f5b8"; }
+.bi-telephone-minus-fill::before { content: "\f5b9"; }
+.bi-telephone-minus::before { content: "\f5ba"; }
+.bi-telephone-outbound-fill::before { content: "\f5bb"; }
+.bi-telephone-outbound::before { content: "\f5bc"; }
+.bi-telephone-plus-fill::before { content: "\f5bd"; }
+.bi-telephone-plus::before { content: "\f5be"; }
+.bi-telephone-x-fill::before { content: "\f5bf"; }
+.bi-telephone-x::before { content: "\f5c0"; }
+.bi-telephone::before { content: "\f5c1"; }
+.bi-terminal-fill::before { content: "\f5c2"; }
+.bi-terminal::before { content: "\f5c3"; }
+.bi-text-center::before { content: "\f5c4"; }
+.bi-text-indent-left::before { content: "\f5c5"; }
+.bi-text-indent-right::before { content: "\f5c6"; }
+.bi-text-left::before { content: "\f5c7"; }
+.bi-text-paragraph::before { content: "\f5c8"; }
+.bi-text-right::before { content: "\f5c9"; }
+.bi-textarea-resize::before { content: "\f5ca"; }
+.bi-textarea-t::before { content: "\f5cb"; }
+.bi-textarea::before { content: "\f5cc"; }
+.bi-thermometer-half::before { content: "\f5cd"; }
+.bi-thermometer-high::before { content: "\f5ce"; }
+.bi-thermometer-low::before { content: "\f5cf"; }
+.bi-thermometer-snow::before { content: "\f5d0"; }
+.bi-thermometer-sun::before { content: "\f5d1"; }
+.bi-thermometer::before { content: "\f5d2"; }
+.bi-three-dots-vertical::before { content: "\f5d3"; }
+.bi-three-dots::before { content: "\f5d4"; }
+.bi-toggle-off::before { content: "\f5d5"; }
+.bi-toggle-on::before { content: "\f5d6"; }
+.bi-toggle2-off::before { content: "\f5d7"; }
+.bi-toggle2-on::before { content: "\f5d8"; }
+.bi-toggles::before { content: "\f5d9"; }
+.bi-toggles2::before { content: "\f5da"; }
+.bi-tools::before { content: "\f5db"; }
+.bi-tornado::before { content: "\f5dc"; }
+.bi-trash-fill::before { content: "\f5dd"; }
+.bi-trash::before { content: "\f5de"; }
+.bi-trash2-fill::before { content: "\f5df"; }
+.bi-trash2::before { content: "\f5e0"; }
+.bi-tree-fill::before { content: "\f5e1"; }
+.bi-tree::before { content: "\f5e2"; }
+.bi-triangle-fill::before { content: "\f5e3"; }
+.bi-triangle-half::before { content: "\f5e4"; }
+.bi-triangle::before { content: "\f5e5"; }
+.bi-trophy-fill::before { content: "\f5e6"; }
+.bi-trophy::before { content: "\f5e7"; }
+.bi-tropical-storm::before { content: "\f5e8"; }
+.bi-truck-flatbed::before { content: "\f5e9"; }
+.bi-truck::before { content: "\f5ea"; }
+.bi-tsunami::before { content: "\f5eb"; }
+.bi-tv-fill::before { content: "\f5ec"; }
+.bi-tv::before { content: "\f5ed"; }
+.bi-twitch::before { content: "\f5ee"; }
+.bi-twitter::before { content: "\f5ef"; }
+.bi-type-bold::before { content: "\f5f0"; }
+.bi-type-h1::before { content: "\f5f1"; }
+.bi-type-h2::before { content: "\f5f2"; }
+.bi-type-h3::before { content: "\f5f3"; }
+.bi-type-italic::before { content: "\f5f4"; }
+.bi-type-strikethrough::before { content: "\f5f5"; }
+.bi-type-underline::before { content: "\f5f6"; }
+.bi-type::before { content: "\f5f7"; }
+.bi-ui-checks-grid::before { content: "\f5f8"; }
+.bi-ui-checks::before { content: "\f5f9"; }
+.bi-ui-radios-grid::before { content: "\f5fa"; }
+.bi-ui-radios::before { content: "\f5fb"; }
+.bi-umbrella-fill::before { content: "\f5fc"; }
+.bi-umbrella::before { content: "\f5fd"; }
+.bi-union::before { content: "\f5fe"; }
+.bi-unlock-fill::before { content: "\f5ff"; }
+.bi-unlock::before { content: "\f600"; }
+.bi-upc-scan::before { content: "\f601"; }
+.bi-upc::before { content: "\f602"; }
+.bi-upload::before { content: "\f603"; }
+.bi-vector-pen::before { content: "\f604"; }
+.bi-view-list::before { content: "\f605"; }
+.bi-view-stacked::before { content: "\f606"; }
+.bi-vinyl-fill::before { content: "\f607"; }
+.bi-vinyl::before { content: "\f608"; }
+.bi-voicemail::before { content: "\f609"; }
+.bi-volume-down-fill::before { content: "\f60a"; }
+.bi-volume-down::before { content: "\f60b"; }
+.bi-volume-mute-fill::before { content: "\f60c"; }
+.bi-volume-mute::before { content: "\f60d"; }
+.bi-volume-off-fill::before { content: "\f60e"; }
+.bi-volume-off::before { content: "\f60f"; }
+.bi-volume-up-fill::before { content: "\f610"; }
+.bi-volume-up::before { content: "\f611"; }
+.bi-vr::before { content: "\f612"; }
+.bi-wallet-fill::before { content: "\f613"; }
+.bi-wallet::before { content: "\f614"; }
+.bi-wallet2::before { content: "\f615"; }
+.bi-watch::before { content: "\f616"; }
+.bi-water::before { content: "\f617"; }
+.bi-whatsapp::before { content: "\f618"; }
+.bi-wifi-1::before { content: "\f619"; }
+.bi-wifi-2::before { content: "\f61a"; }
+.bi-wifi-off::before { content: "\f61b"; }
+.bi-wifi::before { content: "\f61c"; }
+.bi-wind::before { content: "\f61d"; }
+.bi-window-dock::before { content: "\f61e"; }
+.bi-window-sidebar::before { content: "\f61f"; }
+.bi-window::before { content: "\f620"; }
+.bi-wrench::before { content: "\f621"; }
+.bi-x-circle-fill::before { content: "\f622"; }
+.bi-x-circle::before { content: "\f623"; }
+.bi-x-diamond-fill::before { content: "\f624"; }
+.bi-x-diamond::before { content: "\f625"; }
+.bi-x-octagon-fill::before { content: "\f626"; }
+.bi-x-octagon::before { content: "\f627"; }
+.bi-x-square-fill::before { content: "\f628"; }
+.bi-x-square::before { content: "\f629"; }
+.bi-x::before { content: "\f62a"; }
+.bi-youtube::before { content: "\f62b"; }
+.bi-zoom-in::before { content: "\f62c"; }
+.bi-zoom-out::before { content: "\f62d"; }
+.bi-bank::before { content: "\f62e"; }
+.bi-bank2::before { content: "\f62f"; }
+.bi-bell-slash-fill::before { content: "\f630"; }
+.bi-bell-slash::before { content: "\f631"; }
+.bi-cash-coin::before { content: "\f632"; }
+.bi-check-lg::before { content: "\f633"; }
+.bi-coin::before { content: "\f634"; }
+.bi-currency-bitcoin::before { content: "\f635"; }
+.bi-currency-dollar::before { content: "\f636"; }
+.bi-currency-euro::before { content: "\f637"; }
+.bi-currency-exchange::before { content: "\f638"; }
+.bi-currency-pound::before { content: "\f639"; }
+.bi-currency-yen::before { content: "\f63a"; }
+.bi-dash-lg::before { content: "\f63b"; }
+.bi-exclamation-lg::before { content: "\f63c"; }
+.bi-file-earmark-pdf-fill::before { content: "\f63d"; }
+.bi-file-earmark-pdf::before { content: "\f63e"; }
+.bi-file-pdf-fill::before { content: "\f63f"; }
+.bi-file-pdf::before { content: "\f640"; }
+.bi-gender-ambiguous::before { content: "\f641"; }
+.bi-gender-female::before { content: "\f642"; }
+.bi-gender-male::before { content: "\f643"; }
+.bi-gender-trans::before { content: "\f644"; }
+.bi-headset-vr::before { content: "\f645"; }
+.bi-info-lg::before { content: "\f646"; }
+.bi-mastodon::before { content: "\f647"; }
+.bi-messenger::before { content: "\f648"; }
+.bi-piggy-bank-fill::before { content: "\f649"; }
+.bi-piggy-bank::before { content: "\f64a"; }
+.bi-pin-map-fill::before { content: "\f64b"; }
+.bi-pin-map::before { content: "\f64c"; }
+.bi-plus-lg::before { content: "\f64d"; }
+.bi-question-lg::before { content: "\f64e"; }
+.bi-recycle::before { content: "\f64f"; }
+.bi-reddit::before { content: "\f650"; }
+.bi-safe-fill::before { content: "\f651"; }
+.bi-safe2-fill::before { content: "\f652"; }
+.bi-safe2::before { content: "\f653"; }
+.bi-sd-card-fill::before { content: "\f654"; }
+.bi-sd-card::before { content: "\f655"; }
+.bi-skype::before { content: "\f656"; }
+.bi-slash-lg::before { content: "\f657"; }
+.bi-translate::before { content: "\f658"; }
+.bi-x-lg::before { content: "\f659"; }
+.bi-safe::before { content: "\f65a"; }
+.bi-apple::before { content: "\f65b"; }
+.bi-microsoft::before { content: "\f65d"; }
+.bi-windows::before { content: "\f65e"; }
+.bi-behance::before { content: "\f65c"; }
+.bi-dribbble::before { content: "\f65f"; }
+.bi-line::before { content: "\f660"; }
+.bi-medium::before { content: "\f661"; }
+.bi-paypal::before { content: "\f662"; }
+.bi-pinterest::before { content: "\f663"; }
+.bi-signal::before { content: "\f664"; }
+.bi-snapchat::before { content: "\f665"; }
+.bi-spotify::before { content: "\f666"; }
+.bi-stack-overflow::before { content: "\f667"; }
+.bi-strava::before { content: "\f668"; }
+.bi-wordpress::before { content: "\f669"; }
+.bi-vimeo::before { content: "\f66a"; }
+.bi-activity::before { content: "\f66b"; }
+.bi-easel2-fill::before { content: "\f66c"; }
+.bi-easel2::before { content: "\f66d"; }
+.bi-easel3-fill::before { content: "\f66e"; }
+.bi-easel3::before { content: "\f66f"; }
+.bi-fan::before { content: "\f670"; }
+.bi-fingerprint::before { content: "\f671"; }
+.bi-graph-down-arrow::before { content: "\f672"; }
+.bi-graph-up-arrow::before { content: "\f673"; }
+.bi-hypnotize::before { content: "\f674"; }
+.bi-magic::before { content: "\f675"; }
+.bi-person-rolodex::before { content: "\f676"; }
+.bi-person-video::before { content: "\f677"; }
+.bi-person-video2::before { content: "\f678"; }
+.bi-person-video3::before { content: "\f679"; }
+.bi-person-workspace::before { content: "\f67a"; }
+.bi-radioactive::before { content: "\f67b"; }
+.bi-webcam-fill::before { content: "\f67c"; }
+.bi-webcam::before { content: "\f67d"; }
+.bi-yin-yang::before { content: "\f67e"; }
+.bi-bandaid-fill::before { content: "\f680"; }
+.bi-bandaid::before { content: "\f681"; }
+.bi-bluetooth::before { content: "\f682"; }
+.bi-body-text::before { content: "\f683"; }
+.bi-boombox::before { content: "\f684"; }
+.bi-boxes::before { content: "\f685"; }
+.bi-dpad-fill::before { content: "\f686"; }
+.bi-dpad::before { content: "\f687"; }
+.bi-ear-fill::before { content: "\f688"; }
+.bi-ear::before { content: "\f689"; }
+.bi-envelope-check-fill::before { content: "\f68b"; }
+.bi-envelope-check::before { content: "\f68c"; }
+.bi-envelope-dash-fill::before { content: "\f68e"; }
+.bi-envelope-dash::before { content: "\f68f"; }
+.bi-envelope-exclamation-fill::before { content: "\f691"; }
+.bi-envelope-exclamation::before { content: "\f692"; }
+.bi-envelope-plus-fill::before { content: "\f693"; }
+.bi-envelope-plus::before { content: "\f694"; }
+.bi-envelope-slash-fill::before { content: "\f696"; }
+.bi-envelope-slash::before { content: "\f697"; }
+.bi-envelope-x-fill::before { content: "\f699"; }
+.bi-envelope-x::before { content: "\f69a"; }
+.bi-explicit-fill::before { content: "\f69b"; }
+.bi-explicit::before { content: "\f69c"; }
+.bi-git::before { content: "\f69d"; }
+.bi-infinity::before { content: "\f69e"; }
+.bi-list-columns-reverse::before { content: "\f69f"; }
+.bi-list-columns::before { content: "\f6a0"; }
+.bi-meta::before { content: "\f6a1"; }
+.bi-nintendo-switch::before { content: "\f6a4"; }
+.bi-pc-display-horizontal::before { content: "\f6a5"; }
+.bi-pc-display::before { content: "\f6a6"; }
+.bi-pc-horizontal::before { content: "\f6a7"; }
+.bi-pc::before { content: "\f6a8"; }
+.bi-playstation::before { content: "\f6a9"; }
+.bi-plus-slash-minus::before { content: "\f6aa"; }
+.bi-projector-fill::before { content: "\f6ab"; }
+.bi-projector::before { content: "\f6ac"; }
+.bi-qr-code-scan::before { content: "\f6ad"; }
+.bi-qr-code::before { content: "\f6ae"; }
+.bi-quora::before { content: "\f6af"; }
+.bi-quote::before { content: "\f6b0"; }
+.bi-robot::before { content: "\f6b1"; }
+.bi-send-check-fill::before { content: "\f6b2"; }
+.bi-send-check::before { content: "\f6b3"; }
+.bi-send-dash-fill::before { content: "\f6b4"; }
+.bi-send-dash::before { content: "\f6b5"; }
+.bi-send-exclamation-fill::before { content: "\f6b7"; }
+.bi-send-exclamation::before { content: "\f6b8"; }
+.bi-send-fill::before { content: "\f6b9"; }
+.bi-send-plus-fill::before { content: "\f6ba"; }
+.bi-send-plus::before { content: "\f6bb"; }
+.bi-send-slash-fill::before { content: "\f6bc"; }
+.bi-send-slash::before { content: "\f6bd"; }
+.bi-send-x-fill::before { content: "\f6be"; }
+.bi-send-x::before { content: "\f6bf"; }
+.bi-send::before { content: "\f6c0"; }
+.bi-steam::before { content: "\f6c1"; }
+.bi-terminal-dash::before { content: "\f6c3"; }
+.bi-terminal-plus::before { content: "\f6c4"; }
+.bi-terminal-split::before { content: "\f6c5"; }
+.bi-ticket-detailed-fill::before { content: "\f6c6"; }
+.bi-ticket-detailed::before { content: "\f6c7"; }
+.bi-ticket-fill::before { content: "\f6c8"; }
+.bi-ticket-perforated-fill::before { content: "\f6c9"; }
+.bi-ticket-perforated::before { content: "\f6ca"; }
+.bi-ticket::before { content: "\f6cb"; }
+.bi-tiktok::before { content: "\f6cc"; }
+.bi-window-dash::before { content: "\f6cd"; }
+.bi-window-desktop::before { content: "\f6ce"; }
+.bi-window-fullscreen::before { content: "\f6cf"; }
+.bi-window-plus::before { content: "\f6d0"; }
+.bi-window-split::before { content: "\f6d1"; }
+.bi-window-stack::before { content: "\f6d2"; }
+.bi-window-x::before { content: "\f6d3"; }
+.bi-xbox::before { content: "\f6d4"; }
+.bi-ethernet::before { content: "\f6d5"; }
+.bi-hdmi-fill::before { content: "\f6d6"; }
+.bi-hdmi::before { content: "\f6d7"; }
+.bi-usb-c-fill::before { content: "\f6d8"; }
+.bi-usb-c::before { content: "\f6d9"; }
+.bi-usb-fill::before { content: "\f6da"; }
+.bi-usb-plug-fill::before { content: "\f6db"; }
+.bi-usb-plug::before { content: "\f6dc"; }
+.bi-usb-symbol::before { content: "\f6dd"; }
+.bi-usb::before { content: "\f6de"; }
+.bi-boombox-fill::before { content: "\f6df"; }
+.bi-displayport::before { content: "\f6e1"; }
+.bi-gpu-card::before { content: "\f6e2"; }
+.bi-memory::before { content: "\f6e3"; }
+.bi-modem-fill::before { content: "\f6e4"; }
+.bi-modem::before { content: "\f6e5"; }
+.bi-motherboard-fill::before { content: "\f6e6"; }
+.bi-motherboard::before { content: "\f6e7"; }
+.bi-optical-audio-fill::before { content: "\f6e8"; }
+.bi-optical-audio::before { content: "\f6e9"; }
+.bi-pci-card::before { content: "\f6ea"; }
+.bi-router-fill::before { content: "\f6eb"; }
+.bi-router::before { content: "\f6ec"; }
+.bi-thunderbolt-fill::before { content: "\f6ef"; }
+.bi-thunderbolt::before { content: "\f6f0"; }
+.bi-usb-drive-fill::before { content: "\f6f1"; }
+.bi-usb-drive::before { content: "\f6f2"; }
+.bi-usb-micro-fill::before { content: "\f6f3"; }
+.bi-usb-micro::before { content: "\f6f4"; }
+.bi-usb-mini-fill::before { content: "\f6f5"; }
+.bi-usb-mini::before { content: "\f6f6"; }
+.bi-cloud-haze2::before { content: "\f6f7"; }
+.bi-device-hdd-fill::before { content: "\f6f8"; }
+.bi-device-hdd::before { content: "\f6f9"; }
+.bi-device-ssd-fill::before { content: "\f6fa"; }
+.bi-device-ssd::before { content: "\f6fb"; }
+.bi-displayport-fill::before { content: "\f6fc"; }
+.bi-mortarboard-fill::before { content: "\f6fd"; }
+.bi-mortarboard::before { content: "\f6fe"; }
+.bi-terminal-x::before { content: "\f6ff"; }
+.bi-arrow-through-heart-fill::before { content: "\f700"; }
+.bi-arrow-through-heart::before { content: "\f701"; }
+.bi-badge-sd-fill::before { content: "\f702"; }
+.bi-badge-sd::before { content: "\f703"; }
+.bi-bag-heart-fill::before { content: "\f704"; }
+.bi-bag-heart::before { content: "\f705"; }
+.bi-balloon-fill::before { content: "\f706"; }
+.bi-balloon-heart-fill::before { content: "\f707"; }
+.bi-balloon-heart::before { content: "\f708"; }
+.bi-balloon::before { content: "\f709"; }
+.bi-box2-fill::before { content: "\f70a"; }
+.bi-box2-heart-fill::before { content: "\f70b"; }
+.bi-box2-heart::before { content: "\f70c"; }
+.bi-box2::before { content: "\f70d"; }
+.bi-braces-asterisk::before { content: "\f70e"; }
+.bi-calendar-heart-fill::before { content: "\f70f"; }
+.bi-calendar-heart::before { content: "\f710"; }
+.bi-calendar2-heart-fill::before { content: "\f711"; }
+.bi-calendar2-heart::before { content: "\f712"; }
+.bi-chat-heart-fill::before { content: "\f713"; }
+.bi-chat-heart::before { content: "\f714"; }
+.bi-chat-left-heart-fill::before { content: "\f715"; }
+.bi-chat-left-heart::before { content: "\f716"; }
+.bi-chat-right-heart-fill::before { content: "\f717"; }
+.bi-chat-right-heart::before { content: "\f718"; }
+.bi-chat-square-heart-fill::before { content: "\f719"; }
+.bi-chat-square-heart::before { content: "\f71a"; }
+.bi-clipboard-check-fill::before { content: "\f71b"; }
+.bi-clipboard-data-fill::before { content: "\f71c"; }
+.bi-clipboard-fill::before { content: "\f71d"; }
+.bi-clipboard-heart-fill::before { content: "\f71e"; }
+.bi-clipboard-heart::before { content: "\f71f"; }
+.bi-clipboard-minus-fill::before { content: "\f720"; }
+.bi-clipboard-plus-fill::before { content: "\f721"; }
+.bi-clipboard-pulse::before { content: "\f722"; }
+.bi-clipboard-x-fill::before { content: "\f723"; }
+.bi-clipboard2-check-fill::before { content: "\f724"; }
+.bi-clipboard2-check::before { content: "\f725"; }
+.bi-clipboard2-data-fill::before { content: "\f726"; }
+.bi-clipboard2-data::before { content: "\f727"; }
+.bi-clipboard2-fill::before { content: "\f728"; }
+.bi-clipboard2-heart-fill::before { content: "\f729"; }
+.bi-clipboard2-heart::before { content: "\f72a"; }
+.bi-clipboard2-minus-fill::before { content: "\f72b"; }
+.bi-clipboard2-minus::before { content: "\f72c"; }
+.bi-clipboard2-plus-fill::before { content: "\f72d"; }
+.bi-clipboard2-plus::before { content: "\f72e"; }
+.bi-clipboard2-pulse-fill::before { content: "\f72f"; }
+.bi-clipboard2-pulse::before { content: "\f730"; }
+.bi-clipboard2-x-fill::before { content: "\f731"; }
+.bi-clipboard2-x::before { content: "\f732"; }
+.bi-clipboard2::before { content: "\f733"; }
+.bi-emoji-kiss-fill::before { content: "\f734"; }
+.bi-emoji-kiss::before { content: "\f735"; }
+.bi-envelope-heart-fill::before { content: "\f736"; }
+.bi-envelope-heart::before { content: "\f737"; }
+.bi-envelope-open-heart-fill::before { content: "\f738"; }
+.bi-envelope-open-heart::before { content: "\f739"; }
+.bi-envelope-paper-fill::before { content: "\f73a"; }
+.bi-envelope-paper-heart-fill::before { content: "\f73b"; }
+.bi-envelope-paper-heart::before { content: "\f73c"; }
+.bi-envelope-paper::before { content: "\f73d"; }
+.bi-filetype-aac::before { content: "\f73e"; }
+.bi-filetype-ai::before { content: "\f73f"; }
+.bi-filetype-bmp::before { content: "\f740"; }
+.bi-filetype-cs::before { content: "\f741"; }
+.bi-filetype-css::before { content: "\f742"; }
+.bi-filetype-csv::before { content: "\f743"; }
+.bi-filetype-doc::before { content: "\f744"; }
+.bi-filetype-docx::before { content: "\f745"; }
+.bi-filetype-exe::before { content: "\f746"; }
+.bi-filetype-gif::before { content: "\f747"; }
+.bi-filetype-heic::before { content: "\f748"; }
+.bi-filetype-html::before { content: "\f749"; }
+.bi-filetype-java::before { content: "\f74a"; }
+.bi-filetype-jpg::before { content: "\f74b"; }
+.bi-filetype-js::before { content: "\f74c"; }
+.bi-filetype-jsx::before { content: "\f74d"; }
+.bi-filetype-key::before { content: "\f74e"; }
+.bi-filetype-m4p::before { content: "\f74f"; }
+.bi-filetype-md::before { content: "\f750"; }
+.bi-filetype-mdx::before { content: "\f751"; }
+.bi-filetype-mov::before { content: "\f752"; }
+.bi-filetype-mp3::before { content: "\f753"; }
+.bi-filetype-mp4::before { content: "\f754"; }
+.bi-filetype-otf::before { content: "\f755"; }
+.bi-filetype-pdf::before { content: "\f756"; }
+.bi-filetype-php::before { content: "\f757"; }
+.bi-filetype-png::before { content: "\f758"; }
+.bi-filetype-ppt::before { content: "\f75a"; }
+.bi-filetype-psd::before { content: "\f75b"; }
+.bi-filetype-py::before { content: "\f75c"; }
+.bi-filetype-raw::before { content: "\f75d"; }
+.bi-filetype-rb::before { content: "\f75e"; }
+.bi-filetype-sass::before { content: "\f75f"; }
+.bi-filetype-scss::before { content: "\f760"; }
+.bi-filetype-sh::before { content: "\f761"; }
+.bi-filetype-svg::before { content: "\f762"; }
+.bi-filetype-tiff::before { content: "\f763"; }
+.bi-filetype-tsx::before { content: "\f764"; }
+.bi-filetype-ttf::before { content: "\f765"; }
+.bi-filetype-txt::before { content: "\f766"; }
+.bi-filetype-wav::before { content: "\f767"; }
+.bi-filetype-woff::before { content: "\f768"; }
+.bi-filetype-xls::before { content: "\f76a"; }
+.bi-filetype-xml::before { content: "\f76b"; }
+.bi-filetype-yml::before { content: "\f76c"; }
+.bi-heart-arrow::before { content: "\f76d"; }
+.bi-heart-pulse-fill::before { content: "\f76e"; }
+.bi-heart-pulse::before { content: "\f76f"; }
+.bi-heartbreak-fill::before { content: "\f770"; }
+.bi-heartbreak::before { content: "\f771"; }
+.bi-hearts::before { content: "\f772"; }
+.bi-hospital-fill::before { content: "\f773"; }
+.bi-hospital::before { content: "\f774"; }
+.bi-house-heart-fill::before { content: "\f775"; }
+.bi-house-heart::before { content: "\f776"; }
+.bi-incognito::before { content: "\f777"; }
+.bi-magnet-fill::before { content: "\f778"; }
+.bi-magnet::before { content: "\f779"; }
+.bi-person-heart::before { content: "\f77a"; }
+.bi-person-hearts::before { content: "\f77b"; }
+.bi-phone-flip::before { content: "\f77c"; }
+.bi-plugin::before { content: "\f77d"; }
+.bi-postage-fill::before { content: "\f77e"; }
+.bi-postage-heart-fill::before { content: "\f77f"; }
+.bi-postage-heart::before { content: "\f780"; }
+.bi-postage::before { content: "\f781"; }
+.bi-postcard-fill::before { content: "\f782"; }
+.bi-postcard-heart-fill::before { content: "\f783"; }
+.bi-postcard-heart::before { content: "\f784"; }
+.bi-postcard::before { content: "\f785"; }
+.bi-search-heart-fill::before { content: "\f786"; }
+.bi-search-heart::before { content: "\f787"; }
+.bi-sliders2-vertical::before { content: "\f788"; }
+.bi-sliders2::before { content: "\f789"; }
+.bi-trash3-fill::before { content: "\f78a"; }
+.bi-trash3::before { content: "\f78b"; }
+.bi-valentine::before { content: "\f78c"; }
+.bi-valentine2::before { content: "\f78d"; }
+.bi-wrench-adjustable-circle-fill::before { content: "\f78e"; }
+.bi-wrench-adjustable-circle::before { content: "\f78f"; }
+.bi-wrench-adjustable::before { content: "\f790"; }
+.bi-filetype-json::before { content: "\f791"; }
+.bi-filetype-pptx::before { content: "\f792"; }
+.bi-filetype-xlsx::before { content: "\f793"; }
+.bi-1-circle-fill::before { content: "\f796"; }
+.bi-1-circle::before { content: "\f797"; }
+.bi-1-square-fill::before { content: "\f798"; }
+.bi-1-square::before { content: "\f799"; }
+.bi-2-circle-fill::before { content: "\f79c"; }
+.bi-2-circle::before { content: "\f79d"; }
+.bi-2-square-fill::before { content: "\f79e"; }
+.bi-2-square::before { content: "\f79f"; }
+.bi-3-circle-fill::before { content: "\f7a2"; }
+.bi-3-circle::before { content: "\f7a3"; }
+.bi-3-square-fill::before { content: "\f7a4"; }
+.bi-3-square::before { content: "\f7a5"; }
+.bi-4-circle-fill::before { content: "\f7a8"; }
+.bi-4-circle::before { content: "\f7a9"; }
+.bi-4-square-fill::before { content: "\f7aa"; }
+.bi-4-square::before { content: "\f7ab"; }
+.bi-5-circle-fill::before { content: "\f7ae"; }
+.bi-5-circle::before { content: "\f7af"; }
+.bi-5-square-fill::before { content: "\f7b0"; }
+.bi-5-square::before { content: "\f7b1"; }
+.bi-6-circle-fill::before { content: "\f7b4"; }
+.bi-6-circle::before { content: "\f7b5"; }
+.bi-6-square-fill::before { content: "\f7b6"; }
+.bi-6-square::before { content: "\f7b7"; }
+.bi-7-circle-fill::before { content: "\f7ba"; }
+.bi-7-circle::before { content: "\f7bb"; }
+.bi-7-square-fill::before { content: "\f7bc"; }
+.bi-7-square::before { content: "\f7bd"; }
+.bi-8-circle-fill::before { content: "\f7c0"; }
+.bi-8-circle::before { content: "\f7c1"; }
+.bi-8-square-fill::before { content: "\f7c2"; }
+.bi-8-square::before { content: "\f7c3"; }
+.bi-9-circle-fill::before { content: "\f7c6"; }
+.bi-9-circle::before { content: "\f7c7"; }
+.bi-9-square-fill::before { content: "\f7c8"; }
+.bi-9-square::before { content: "\f7c9"; }
+.bi-airplane-engines-fill::before { content: "\f7ca"; }
+.bi-airplane-engines::before { content: "\f7cb"; }
+.bi-airplane-fill::before { content: "\f7cc"; }
+.bi-airplane::before { content: "\f7cd"; }
+.bi-alexa::before { content: "\f7ce"; }
+.bi-alipay::before { content: "\f7cf"; }
+.bi-android::before { content: "\f7d0"; }
+.bi-android2::before { content: "\f7d1"; }
+.bi-box-fill::before { content: "\f7d2"; }
+.bi-box-seam-fill::before { content: "\f7d3"; }
+.bi-browser-chrome::before { content: "\f7d4"; }
+.bi-browser-edge::before { content: "\f7d5"; }
+.bi-browser-firefox::before { content: "\f7d6"; }
+.bi-browser-safari::before { content: "\f7d7"; }
+.bi-c-circle-fill::before { content: "\f7da"; }
+.bi-c-circle::before { content: "\f7db"; }
+.bi-c-square-fill::before { content: "\f7dc"; }
+.bi-c-square::before { content: "\f7dd"; }
+.bi-capsule-pill::before { content: "\f7de"; }
+.bi-capsule::before { content: "\f7df"; }
+.bi-car-front-fill::before { content: "\f7e0"; }
+.bi-car-front::before { content: "\f7e1"; }
+.bi-cassette-fill::before { content: "\f7e2"; }
+.bi-cassette::before { content: "\f7e3"; }
+.bi-cc-circle-fill::before { content: "\f7e6"; }
+.bi-cc-circle::before { content: "\f7e7"; }
+.bi-cc-square-fill::before { content: "\f7e8"; }
+.bi-cc-square::before { content: "\f7e9"; }
+.bi-cup-hot-fill::before { content: "\f7ea"; }
+.bi-cup-hot::before { content: "\f7eb"; }
+.bi-currency-rupee::before { content: "\f7ec"; }
+.bi-dropbox::before { content: "\f7ed"; }
+.bi-escape::before { content: "\f7ee"; }
+.bi-fast-forward-btn-fill::before { content: "\f7ef"; }
+.bi-fast-forward-btn::before { content: "\f7f0"; }
+.bi-fast-forward-circle-fill::before { content: "\f7f1"; }
+.bi-fast-forward-circle::before { content: "\f7f2"; }
+.bi-fast-forward-fill::before { content: "\f7f3"; }
+.bi-fast-forward::before { content: "\f7f4"; }
+.bi-filetype-sql::before { content: "\f7f5"; }
+.bi-fire::before { content: "\f7f6"; }
+.bi-google-play::before { content: "\f7f7"; }
+.bi-h-circle-fill::before { content: "\f7fa"; }
+.bi-h-circle::before { content: "\f7fb"; }
+.bi-h-square-fill::before { content: "\f7fc"; }
+.bi-h-square::before { content: "\f7fd"; }
+.bi-indent::before { content: "\f7fe"; }
+.bi-lungs-fill::before { content: "\f7ff"; }
+.bi-lungs::before { content: "\f800"; }
+.bi-microsoft-teams::before { content: "\f801"; }
+.bi-p-circle-fill::before { content: "\f804"; }
+.bi-p-circle::before { content: "\f805"; }
+.bi-p-square-fill::before { content: "\f806"; }
+.bi-p-square::before { content: "\f807"; }
+.bi-pass-fill::before { content: "\f808"; }
+.bi-pass::before { content: "\f809"; }
+.bi-prescription::before { content: "\f80a"; }
+.bi-prescription2::before { content: "\f80b"; }
+.bi-r-circle-fill::before { content: "\f80e"; }
+.bi-r-circle::before { content: "\f80f"; }
+.bi-r-square-fill::before { content: "\f810"; }
+.bi-r-square::before { content: "\f811"; }
+.bi-repeat-1::before { content: "\f812"; }
+.bi-repeat::before { content: "\f813"; }
+.bi-rewind-btn-fill::before { content: "\f814"; }
+.bi-rewind-btn::before { content: "\f815"; }
+.bi-rewind-circle-fill::before { content: "\f816"; }
+.bi-rewind-circle::before { content: "\f817"; }
+.bi-rewind-fill::before { content: "\f818"; }
+.bi-rewind::before { content: "\f819"; }
+.bi-train-freight-front-fill::before { content: "\f81a"; }
+.bi-train-freight-front::before { content: "\f81b"; }
+.bi-train-front-fill::before { content: "\f81c"; }
+.bi-train-front::before { content: "\f81d"; }
+.bi-train-lightrail-front-fill::before { content: "\f81e"; }
+.bi-train-lightrail-front::before { content: "\f81f"; }
+.bi-truck-front-fill::before { content: "\f820"; }
+.bi-truck-front::before { content: "\f821"; }
+.bi-ubuntu::before { content: "\f822"; }
+.bi-unindent::before { content: "\f823"; }
+.bi-unity::before { content: "\f824"; }
+.bi-universal-access-circle::before { content: "\f825"; }
+.bi-universal-access::before { content: "\f826"; }
+.bi-virus::before { content: "\f827"; }
+.bi-virus2::before { content: "\f828"; }
+.bi-wechat::before { content: "\f829"; }
+.bi-yelp::before { content: "\f82a"; }
+.bi-sign-stop-fill::before { content: "\f82b"; }
+.bi-sign-stop-lights-fill::before { content: "\f82c"; }
+.bi-sign-stop-lights::before { content: "\f82d"; }
+.bi-sign-stop::before { content: "\f82e"; }
+.bi-sign-turn-left-fill::before { content: "\f82f"; }
+.bi-sign-turn-left::before { content: "\f830"; }
+.bi-sign-turn-right-fill::before { content: "\f831"; }
+.bi-sign-turn-right::before { content: "\f832"; }
+.bi-sign-turn-slight-left-fill::before { content: "\f833"; }
+.bi-sign-turn-slight-left::before { content: "\f834"; }
+.bi-sign-turn-slight-right-fill::before { content: "\f835"; }
+.bi-sign-turn-slight-right::before { content: "\f836"; }
+.bi-sign-yield-fill::before { content: "\f837"; }
+.bi-sign-yield::before { content: "\f838"; }
+.bi-ev-station-fill::before { content: "\f839"; }
+.bi-ev-station::before { content: "\f83a"; }
+.bi-fuel-pump-diesel-fill::before { content: "\f83b"; }
+.bi-fuel-pump-diesel::before { content: "\f83c"; }
+.bi-fuel-pump-fill::before { content: "\f83d"; }
+.bi-fuel-pump::before { content: "\f83e"; }
+.bi-0-circle-fill::before { content: "\f83f"; }
+.bi-0-circle::before { content: "\f840"; }
+.bi-0-square-fill::before { content: "\f841"; }
+.bi-0-square::before { content: "\f842"; }
+.bi-rocket-fill::before { content: "\f843"; }
+.bi-rocket-takeoff-fill::before { content: "\f844"; }
+.bi-rocket-takeoff::before { content: "\f845"; }
+.bi-rocket::before { content: "\f846"; }
+.bi-stripe::before { content: "\f847"; }
+.bi-subscript::before { content: "\f848"; }
+.bi-superscript::before { content: "\f849"; }
+.bi-trello::before { content: "\f84a"; }
+.bi-envelope-at-fill::before { content: "\f84b"; }
+.bi-envelope-at::before { content: "\f84c"; }
+.bi-regex::before { content: "\f84d"; }
+.bi-text-wrap::before { content: "\f84e"; }
+.bi-sign-dead-end-fill::before { content: "\f84f"; }
+.bi-sign-dead-end::before { content: "\f850"; }
+.bi-sign-do-not-enter-fill::before { content: "\f851"; }
+.bi-sign-do-not-enter::before { content: "\f852"; }
+.bi-sign-intersection-fill::before { content: "\f853"; }
+.bi-sign-intersection-side-fill::before { content: "\f854"; }
+.bi-sign-intersection-side::before { content: "\f855"; }
+.bi-sign-intersection-t-fill::before { content: "\f856"; }
+.bi-sign-intersection-t::before { content: "\f857"; }
+.bi-sign-intersection-y-fill::before { content: "\f858"; }
+.bi-sign-intersection-y::before { content: "\f859"; }
+.bi-sign-intersection::before { content: "\f85a"; }
+.bi-sign-merge-left-fill::before { content: "\f85b"; }
+.bi-sign-merge-left::before { content: "\f85c"; }
+.bi-sign-merge-right-fill::before { content: "\f85d"; }
+.bi-sign-merge-right::before { content: "\f85e"; }
+.bi-sign-no-left-turn-fill::before { content: "\f85f"; }
+.bi-sign-no-left-turn::before { content: "\f860"; }
+.bi-sign-no-parking-fill::before { content: "\f861"; }
+.bi-sign-no-parking::before { content: "\f862"; }
+.bi-sign-no-right-turn-fill::before { content: "\f863"; }
+.bi-sign-no-right-turn::before { content: "\f864"; }
+.bi-sign-railroad-fill::before { content: "\f865"; }
+.bi-sign-railroad::before { content: "\f866"; }
+.bi-building-add::before { content: "\f867"; }
+.bi-building-check::before { content: "\f868"; }
+.bi-building-dash::before { content: "\f869"; }
+.bi-building-down::before { content: "\f86a"; }
+.bi-building-exclamation::before { content: "\f86b"; }
+.bi-building-fill-add::before { content: "\f86c"; }
+.bi-building-fill-check::before { content: "\f86d"; }
+.bi-building-fill-dash::before { content: "\f86e"; }
+.bi-building-fill-down::before { content: "\f86f"; }
+.bi-building-fill-exclamation::before { content: "\f870"; }
+.bi-building-fill-gear::before { content: "\f871"; }
+.bi-building-fill-lock::before { content: "\f872"; }
+.bi-building-fill-slash::before { content: "\f873"; }
+.bi-building-fill-up::before { content: "\f874"; }
+.bi-building-fill-x::before { content: "\f875"; }
+.bi-building-fill::before { content: "\f876"; }
+.bi-building-gear::before { content: "\f877"; }
+.bi-building-lock::before { content: "\f878"; }
+.bi-building-slash::before { content: "\f879"; }
+.bi-building-up::before { content: "\f87a"; }
+.bi-building-x::before { content: "\f87b"; }
+.bi-buildings-fill::before { content: "\f87c"; }
+.bi-buildings::before { content: "\f87d"; }
+.bi-bus-front-fill::before { content: "\f87e"; }
+.bi-bus-front::before { content: "\f87f"; }
+.bi-ev-front-fill::before { content: "\f880"; }
+.bi-ev-front::before { content: "\f881"; }
+.bi-globe-americas::before { content: "\f882"; }
+.bi-globe-asia-australia::before { content: "\f883"; }
+.bi-globe-central-south-asia::before { content: "\f884"; }
+.bi-globe-europe-africa::before { content: "\f885"; }
+.bi-house-add-fill::before { content: "\f886"; }
+.bi-house-add::before { content: "\f887"; }
+.bi-house-check-fill::before { content: "\f888"; }
+.bi-house-check::before { content: "\f889"; }
+.bi-house-dash-fill::before { content: "\f88a"; }
+.bi-house-dash::before { content: "\f88b"; }
+.bi-house-down-fill::before { content: "\f88c"; }
+.bi-house-down::before { content: "\f88d"; }
+.bi-house-exclamation-fill::before { content: "\f88e"; }
+.bi-house-exclamation::before { content: "\f88f"; }
+.bi-house-gear-fill::before { content: "\f890"; }
+.bi-house-gear::before { content: "\f891"; }
+.bi-house-lock-fill::before { content: "\f892"; }
+.bi-house-lock::before { content: "\f893"; }
+.bi-house-slash-fill::before { content: "\f894"; }
+.bi-house-slash::before { content: "\f895"; }
+.bi-house-up-fill::before { content: "\f896"; }
+.bi-house-up::before { content: "\f897"; }
+.bi-house-x-fill::before { content: "\f898"; }
+.bi-house-x::before { content: "\f899"; }
+.bi-person-add::before { content: "\f89a"; }
+.bi-person-down::before { content: "\f89b"; }
+.bi-person-exclamation::before { content: "\f89c"; }
+.bi-person-fill-add::before { content: "\f89d"; }
+.bi-person-fill-check::before { content: "\f89e"; }
+.bi-person-fill-dash::before { content: "\f89f"; }
+.bi-person-fill-down::before { content: "\f8a0"; }
+.bi-person-fill-exclamation::before { content: "\f8a1"; }
+.bi-person-fill-gear::before { content: "\f8a2"; }
+.bi-person-fill-lock::before { content: "\f8a3"; }
+.bi-person-fill-slash::before { content: "\f8a4"; }
+.bi-person-fill-up::before { content: "\f8a5"; }
+.bi-person-fill-x::before { content: "\f8a6"; }
+.bi-person-gear::before { content: "\f8a7"; }
+.bi-person-lock::before { content: "\f8a8"; }
+.bi-person-slash::before { content: "\f8a9"; }
+.bi-person-up::before { content: "\f8aa"; }
+.bi-scooter::before { content: "\f8ab"; }
+.bi-taxi-front-fill::before { content: "\f8ac"; }
+.bi-taxi-front::before { content: "\f8ad"; }
+.bi-amd::before { content: "\f8ae"; }
+.bi-database-add::before { content: "\f8af"; }
+.bi-database-check::before { content: "\f8b0"; }
+.bi-database-dash::before { content: "\f8b1"; }
+.bi-database-down::before { content: "\f8b2"; }
+.bi-database-exclamation::before { content: "\f8b3"; }
+.bi-database-fill-add::before { content: "\f8b4"; }
+.bi-database-fill-check::before { content: "\f8b5"; }
+.bi-database-fill-dash::before { content: "\f8b6"; }
+.bi-database-fill-down::before { content: "\f8b7"; }
+.bi-database-fill-exclamation::before { content: "\f8b8"; }
+.bi-database-fill-gear::before { content: "\f8b9"; }
+.bi-database-fill-lock::before { content: "\f8ba"; }
+.bi-database-fill-slash::before { content: "\f8bb"; }
+.bi-database-fill-up::before { content: "\f8bc"; }
+.bi-database-fill-x::before { content: "\f8bd"; }
+.bi-database-fill::before { content: "\f8be"; }
+.bi-database-gear::before { content: "\f8bf"; }
+.bi-database-lock::before { content: "\f8c0"; }
+.bi-database-slash::before { content: "\f8c1"; }
+.bi-database-up::before { content: "\f8c2"; }
+.bi-database-x::before { content: "\f8c3"; }
+.bi-database::before { content: "\f8c4"; }
+.bi-houses-fill::before { content: "\f8c5"; }
+.bi-houses::before { content: "\f8c6"; }
+.bi-nvidia::before { content: "\f8c7"; }
+.bi-person-vcard-fill::before { content: "\f8c8"; }
+.bi-person-vcard::before { content: "\f8c9"; }
+.bi-sina-weibo::before { content: "\f8ca"; }
+.bi-tencent-qq::before { content: "\f8cb"; }
+.bi-wikipedia::before { content: "\f8cc"; }
+.bi-alphabet-uppercase::before { content: "\f2a5"; }
+.bi-alphabet::before { content: "\f68a"; }
+.bi-amazon::before { content: "\f68d"; }
+.bi-arrows-collapse-vertical::before { content: "\f690"; }
+.bi-arrows-expand-vertical::before { content: "\f695"; }
+.bi-arrows-vertical::before { content: "\f698"; }
+.bi-arrows::before { content: "\f6a2"; }
+.bi-ban-fill::before { content: "\f6a3"; }
+.bi-ban::before { content: "\f6b6"; }
+.bi-bing::before { content: "\f6c2"; }
+.bi-cake::before { content: "\f6e0"; }
+.bi-cake2::before { content: "\f6ed"; }
+.bi-cookie::before { content: "\f6ee"; }
+.bi-copy::before { content: "\f759"; }
+.bi-crosshair::before { content: "\f769"; }
+.bi-crosshair2::before { content: "\f794"; }
+.bi-emoji-astonished-fill::before { content: "\f795"; }
+.bi-emoji-astonished::before { content: "\f79a"; }
+.bi-emoji-grimace-fill::before { content: "\f79b"; }
+.bi-emoji-grimace::before { content: "\f7a0"; }
+.bi-emoji-grin-fill::before { content: "\f7a1"; }
+.bi-emoji-grin::before { content: "\f7a6"; }
+.bi-emoji-surprise-fill::before { content: "\f7a7"; }
+.bi-emoji-surprise::before { content: "\f7ac"; }
+.bi-emoji-tear-fill::before { content: "\f7ad"; }
+.bi-emoji-tear::before { content: "\f7b2"; }
+.bi-envelope-arrow-down-fill::before { content: "\f7b3"; }
+.bi-envelope-arrow-down::before { content: "\f7b8"; }
+.bi-envelope-arrow-up-fill::before { content: "\f7b9"; }
+.bi-envelope-arrow-up::before { content: "\f7be"; }
+.bi-feather::before { content: "\f7bf"; }
+.bi-feather2::before { content: "\f7c4"; }
+.bi-floppy-fill::before { content: "\f7c5"; }
+.bi-floppy::before { content: "\f7d8"; }
+.bi-floppy2-fill::before { content: "\f7d9"; }
+.bi-floppy2::before { content: "\f7e4"; }
+.bi-gitlab::before { content: "\f7e5"; }
+.bi-highlighter::before { content: "\f7f8"; }
+.bi-marker-tip::before { content: "\f802"; }
+.bi-nvme-fill::before { content: "\f803"; }
+.bi-nvme::before { content: "\f80c"; }
+.bi-opencollective::before { content: "\f80d"; }
+.bi-pci-card-network::before { content: "\f8cd"; }
+.bi-pci-card-sound::before { content: "\f8ce"; }
+.bi-radar::before { content: "\f8cf"; }
+.bi-send-arrow-down-fill::before { content: "\f8d0"; }
+.bi-send-arrow-down::before { content: "\f8d1"; }
+.bi-send-arrow-up-fill::before { content: "\f8d2"; }
+.bi-send-arrow-up::before { content: "\f8d3"; }
+.bi-sim-slash-fill::before { content: "\f8d4"; }
+.bi-sim-slash::before { content: "\f8d5"; }
+.bi-sourceforge::before { content: "\f8d6"; }
+.bi-substack::before { content: "\f8d7"; }
+.bi-threads-fill::before { content: "\f8d8"; }
+.bi-threads::before { content: "\f8d9"; }
+.bi-transparency::before { content: "\f8da"; }
+.bi-twitter-x::before { content: "\f8db"; }
+.bi-type-h4::before { content: "\f8dc"; }
+.bi-type-h5::before { content: "\f8dd"; }
+.bi-type-h6::before { content: "\f8de"; }
+.bi-backpack-fill::before { content: "\f8df"; }
+.bi-backpack::before { content: "\f8e0"; }
+.bi-backpack2-fill::before { content: "\f8e1"; }
+.bi-backpack2::before { content: "\f8e2"; }
+.bi-backpack3-fill::before { content: "\f8e3"; }
+.bi-backpack3::before { content: "\f8e4"; }
+.bi-backpack4-fill::before { content: "\f8e5"; }
+.bi-backpack4::before { content: "\f8e6"; }
+.bi-brilliance::before { content: "\f8e7"; }
+.bi-cake-fill::before { content: "\f8e8"; }
+.bi-cake2-fill::before { content: "\f8e9"; }
+.bi-duffle-fill::before { content: "\f8ea"; }
+.bi-duffle::before { content: "\f8eb"; }
+.bi-exposure::before { content: "\f8ec"; }
+.bi-gender-neuter::before { content: "\f8ed"; }
+.bi-highlights::before { content: "\f8ee"; }
+.bi-luggage-fill::before { content: "\f8ef"; }
+.bi-luggage::before { content: "\f8f0"; }
+.bi-mailbox-flag::before { content: "\f8f1"; }
+.bi-mailbox2-flag::before { content: "\f8f2"; }
+.bi-noise-reduction::before { content: "\f8f3"; }
+.bi-passport-fill::before { content: "\f8f4"; }
+.bi-passport::before { content: "\f8f5"; }
+.bi-person-arms-up::before { content: "\f8f6"; }
+.bi-person-raised-hand::before { content: "\f8f7"; }
+.bi-person-standing-dress::before { content: "\f8f8"; }
+.bi-person-standing::before { content: "\f8f9"; }
+.bi-person-walking::before { content: "\f8fa"; }
+.bi-person-wheelchair::before { content: "\f8fb"; }
+.bi-shadows::before { content: "\f8fc"; }
+.bi-suitcase-fill::before { content: "\f8fd"; }
+.bi-suitcase-lg-fill::before { content: "\f8fe"; }
+.bi-suitcase-lg::before { content: "\f8ff"; }
+.bi-suitcase::before { content: "\f900"; }
+.bi-suitcase2-fill::before { content: "\f901"; }
+.bi-suitcase2::before { content: "\f902"; }
+.bi-vignette::before { content: "\f903"; }
+.bi-bluesky::before { content: "\f7f9"; }
+.bi-tux::before { content: "\f904"; }
+.bi-beaker-fill::before { content: "\f905"; }
+.bi-beaker::before { content: "\f906"; }
+.bi-flask-fill::before { content: "\f907"; }
+.bi-flask-florence-fill::before { content: "\f908"; }
+.bi-flask-florence::before { content: "\f909"; }
+.bi-flask::before { content: "\f90a"; }
+.bi-leaf-fill::before { content: "\f90b"; }
+.bi-leaf::before { content: "\f90c"; }
+.bi-measuring-cup-fill::before { content: "\f90d"; }
+.bi-measuring-cup::before { content: "\f90e"; }
+.bi-unlock2-fill::before { content: "\f90f"; }
+.bi-unlock2::before { content: "\f910"; }
+.bi-battery-low::before { content: "\f911"; }
+.bi-anthropic::before { content: "\f912"; }
+.bi-apple-music::before { content: "\f913"; }
+.bi-claude::before { content: "\f914"; }
+.bi-openai::before { content: "\f915"; }
+.bi-perplexity::before { content: "\f916"; }
+.bi-css::before { content: "\f917"; }
+.bi-javascript::before { content: "\f918"; }
+.bi-typescript::before { content: "\f919"; }
+.bi-fork-knife::before { content: "\f91a"; }
+.bi-globe-americas-fill::before { content: "\f91b"; }
+.bi-globe-asia-australia-fill::before { content: "\f91c"; }
+.bi-globe-central-south-asia-fill::before { content: "\f91d"; }
+.bi-globe-europe-africa-fill::before { content: "\f91e"; }
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-icons.json b/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-icons.json
new file mode 100644
index 00000000..9d8873b1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-icons.json
@@ -0,0 +1,2080 @@
+{
+ "123": 63103,
+ "alarm-fill": 61697,
+ "alarm": 61698,
+ "align-bottom": 61699,
+ "align-center": 61700,
+ "align-end": 61701,
+ "align-middle": 61702,
+ "align-start": 61703,
+ "align-top": 61704,
+ "alt": 61705,
+ "app-indicator": 61706,
+ "app": 61707,
+ "archive-fill": 61708,
+ "archive": 61709,
+ "arrow-90deg-down": 61710,
+ "arrow-90deg-left": 61711,
+ "arrow-90deg-right": 61712,
+ "arrow-90deg-up": 61713,
+ "arrow-bar-down": 61714,
+ "arrow-bar-left": 61715,
+ "arrow-bar-right": 61716,
+ "arrow-bar-up": 61717,
+ "arrow-clockwise": 61718,
+ "arrow-counterclockwise": 61719,
+ "arrow-down-circle-fill": 61720,
+ "arrow-down-circle": 61721,
+ "arrow-down-left-circle-fill": 61722,
+ "arrow-down-left-circle": 61723,
+ "arrow-down-left-square-fill": 61724,
+ "arrow-down-left-square": 61725,
+ "arrow-down-left": 61726,
+ "arrow-down-right-circle-fill": 61727,
+ "arrow-down-right-circle": 61728,
+ "arrow-down-right-square-fill": 61729,
+ "arrow-down-right-square": 61730,
+ "arrow-down-right": 61731,
+ "arrow-down-short": 61732,
+ "arrow-down-square-fill": 61733,
+ "arrow-down-square": 61734,
+ "arrow-down-up": 61735,
+ "arrow-down": 61736,
+ "arrow-left-circle-fill": 61737,
+ "arrow-left-circle": 61738,
+ "arrow-left-right": 61739,
+ "arrow-left-short": 61740,
+ "arrow-left-square-fill": 61741,
+ "arrow-left-square": 61742,
+ "arrow-left": 61743,
+ "arrow-repeat": 61744,
+ "arrow-return-left": 61745,
+ "arrow-return-right": 61746,
+ "arrow-right-circle-fill": 61747,
+ "arrow-right-circle": 61748,
+ "arrow-right-short": 61749,
+ "arrow-right-square-fill": 61750,
+ "arrow-right-square": 61751,
+ "arrow-right": 61752,
+ "arrow-up-circle-fill": 61753,
+ "arrow-up-circle": 61754,
+ "arrow-up-left-circle-fill": 61755,
+ "arrow-up-left-circle": 61756,
+ "arrow-up-left-square-fill": 61757,
+ "arrow-up-left-square": 61758,
+ "arrow-up-left": 61759,
+ "arrow-up-right-circle-fill": 61760,
+ "arrow-up-right-circle": 61761,
+ "arrow-up-right-square-fill": 61762,
+ "arrow-up-right-square": 61763,
+ "arrow-up-right": 61764,
+ "arrow-up-short": 61765,
+ "arrow-up-square-fill": 61766,
+ "arrow-up-square": 61767,
+ "arrow-up": 61768,
+ "arrows-angle-contract": 61769,
+ "arrows-angle-expand": 61770,
+ "arrows-collapse": 61771,
+ "arrows-expand": 61772,
+ "arrows-fullscreen": 61773,
+ "arrows-move": 61774,
+ "aspect-ratio-fill": 61775,
+ "aspect-ratio": 61776,
+ "asterisk": 61777,
+ "at": 61778,
+ "award-fill": 61779,
+ "award": 61780,
+ "back": 61781,
+ "backspace-fill": 61782,
+ "backspace-reverse-fill": 61783,
+ "backspace-reverse": 61784,
+ "backspace": 61785,
+ "badge-3d-fill": 61786,
+ "badge-3d": 61787,
+ "badge-4k-fill": 61788,
+ "badge-4k": 61789,
+ "badge-8k-fill": 61790,
+ "badge-8k": 61791,
+ "badge-ad-fill": 61792,
+ "badge-ad": 61793,
+ "badge-ar-fill": 61794,
+ "badge-ar": 61795,
+ "badge-cc-fill": 61796,
+ "badge-cc": 61797,
+ "badge-hd-fill": 61798,
+ "badge-hd": 61799,
+ "badge-tm-fill": 61800,
+ "badge-tm": 61801,
+ "badge-vo-fill": 61802,
+ "badge-vo": 61803,
+ "badge-vr-fill": 61804,
+ "badge-vr": 61805,
+ "badge-wc-fill": 61806,
+ "badge-wc": 61807,
+ "bag-check-fill": 61808,
+ "bag-check": 61809,
+ "bag-dash-fill": 61810,
+ "bag-dash": 61811,
+ "bag-fill": 61812,
+ "bag-plus-fill": 61813,
+ "bag-plus": 61814,
+ "bag-x-fill": 61815,
+ "bag-x": 61816,
+ "bag": 61817,
+ "bar-chart-fill": 61818,
+ "bar-chart-line-fill": 61819,
+ "bar-chart-line": 61820,
+ "bar-chart-steps": 61821,
+ "bar-chart": 61822,
+ "basket-fill": 61823,
+ "basket": 61824,
+ "basket2-fill": 61825,
+ "basket2": 61826,
+ "basket3-fill": 61827,
+ "basket3": 61828,
+ "battery-charging": 61829,
+ "battery-full": 61830,
+ "battery-half": 61831,
+ "battery": 61832,
+ "bell-fill": 61833,
+ "bell": 61834,
+ "bezier": 61835,
+ "bezier2": 61836,
+ "bicycle": 61837,
+ "binoculars-fill": 61838,
+ "binoculars": 61839,
+ "blockquote-left": 61840,
+ "blockquote-right": 61841,
+ "book-fill": 61842,
+ "book-half": 61843,
+ "book": 61844,
+ "bookmark-check-fill": 61845,
+ "bookmark-check": 61846,
+ "bookmark-dash-fill": 61847,
+ "bookmark-dash": 61848,
+ "bookmark-fill": 61849,
+ "bookmark-heart-fill": 61850,
+ "bookmark-heart": 61851,
+ "bookmark-plus-fill": 61852,
+ "bookmark-plus": 61853,
+ "bookmark-star-fill": 61854,
+ "bookmark-star": 61855,
+ "bookmark-x-fill": 61856,
+ "bookmark-x": 61857,
+ "bookmark": 61858,
+ "bookmarks-fill": 61859,
+ "bookmarks": 61860,
+ "bookshelf": 61861,
+ "bootstrap-fill": 61862,
+ "bootstrap-reboot": 61863,
+ "bootstrap": 61864,
+ "border-all": 61865,
+ "border-bottom": 61866,
+ "border-center": 61867,
+ "border-inner": 61868,
+ "border-left": 61869,
+ "border-middle": 61870,
+ "border-outer": 61871,
+ "border-right": 61872,
+ "border-style": 61873,
+ "border-top": 61874,
+ "border-width": 61875,
+ "border": 61876,
+ "bounding-box-circles": 61877,
+ "bounding-box": 61878,
+ "box-arrow-down-left": 61879,
+ "box-arrow-down-right": 61880,
+ "box-arrow-down": 61881,
+ "box-arrow-in-down-left": 61882,
+ "box-arrow-in-down-right": 61883,
+ "box-arrow-in-down": 61884,
+ "box-arrow-in-left": 61885,
+ "box-arrow-in-right": 61886,
+ "box-arrow-in-up-left": 61887,
+ "box-arrow-in-up-right": 61888,
+ "box-arrow-in-up": 61889,
+ "box-arrow-left": 61890,
+ "box-arrow-right": 61891,
+ "box-arrow-up-left": 61892,
+ "box-arrow-up-right": 61893,
+ "box-arrow-up": 61894,
+ "box-seam": 61895,
+ "box": 61896,
+ "braces": 61897,
+ "bricks": 61898,
+ "briefcase-fill": 61899,
+ "briefcase": 61900,
+ "brightness-alt-high-fill": 61901,
+ "brightness-alt-high": 61902,
+ "brightness-alt-low-fill": 61903,
+ "brightness-alt-low": 61904,
+ "brightness-high-fill": 61905,
+ "brightness-high": 61906,
+ "brightness-low-fill": 61907,
+ "brightness-low": 61908,
+ "broadcast-pin": 61909,
+ "broadcast": 61910,
+ "brush-fill": 61911,
+ "brush": 61912,
+ "bucket-fill": 61913,
+ "bucket": 61914,
+ "bug-fill": 61915,
+ "bug": 61916,
+ "building": 61917,
+ "bullseye": 61918,
+ "calculator-fill": 61919,
+ "calculator": 61920,
+ "calendar-check-fill": 61921,
+ "calendar-check": 61922,
+ "calendar-date-fill": 61923,
+ "calendar-date": 61924,
+ "calendar-day-fill": 61925,
+ "calendar-day": 61926,
+ "calendar-event-fill": 61927,
+ "calendar-event": 61928,
+ "calendar-fill": 61929,
+ "calendar-minus-fill": 61930,
+ "calendar-minus": 61931,
+ "calendar-month-fill": 61932,
+ "calendar-month": 61933,
+ "calendar-plus-fill": 61934,
+ "calendar-plus": 61935,
+ "calendar-range-fill": 61936,
+ "calendar-range": 61937,
+ "calendar-week-fill": 61938,
+ "calendar-week": 61939,
+ "calendar-x-fill": 61940,
+ "calendar-x": 61941,
+ "calendar": 61942,
+ "calendar2-check-fill": 61943,
+ "calendar2-check": 61944,
+ "calendar2-date-fill": 61945,
+ "calendar2-date": 61946,
+ "calendar2-day-fill": 61947,
+ "calendar2-day": 61948,
+ "calendar2-event-fill": 61949,
+ "calendar2-event": 61950,
+ "calendar2-fill": 61951,
+ "calendar2-minus-fill": 61952,
+ "calendar2-minus": 61953,
+ "calendar2-month-fill": 61954,
+ "calendar2-month": 61955,
+ "calendar2-plus-fill": 61956,
+ "calendar2-plus": 61957,
+ "calendar2-range-fill": 61958,
+ "calendar2-range": 61959,
+ "calendar2-week-fill": 61960,
+ "calendar2-week": 61961,
+ "calendar2-x-fill": 61962,
+ "calendar2-x": 61963,
+ "calendar2": 61964,
+ "calendar3-event-fill": 61965,
+ "calendar3-event": 61966,
+ "calendar3-fill": 61967,
+ "calendar3-range-fill": 61968,
+ "calendar3-range": 61969,
+ "calendar3-week-fill": 61970,
+ "calendar3-week": 61971,
+ "calendar3": 61972,
+ "calendar4-event": 61973,
+ "calendar4-range": 61974,
+ "calendar4-week": 61975,
+ "calendar4": 61976,
+ "camera-fill": 61977,
+ "camera-reels-fill": 61978,
+ "camera-reels": 61979,
+ "camera-video-fill": 61980,
+ "camera-video-off-fill": 61981,
+ "camera-video-off": 61982,
+ "camera-video": 61983,
+ "camera": 61984,
+ "camera2": 61985,
+ "capslock-fill": 61986,
+ "capslock": 61987,
+ "card-checklist": 61988,
+ "card-heading": 61989,
+ "card-image": 61990,
+ "card-list": 61991,
+ "card-text": 61992,
+ "caret-down-fill": 61993,
+ "caret-down-square-fill": 61994,
+ "caret-down-square": 61995,
+ "caret-down": 61996,
+ "caret-left-fill": 61997,
+ "caret-left-square-fill": 61998,
+ "caret-left-square": 61999,
+ "caret-left": 62000,
+ "caret-right-fill": 62001,
+ "caret-right-square-fill": 62002,
+ "caret-right-square": 62003,
+ "caret-right": 62004,
+ "caret-up-fill": 62005,
+ "caret-up-square-fill": 62006,
+ "caret-up-square": 62007,
+ "caret-up": 62008,
+ "cart-check-fill": 62009,
+ "cart-check": 62010,
+ "cart-dash-fill": 62011,
+ "cart-dash": 62012,
+ "cart-fill": 62013,
+ "cart-plus-fill": 62014,
+ "cart-plus": 62015,
+ "cart-x-fill": 62016,
+ "cart-x": 62017,
+ "cart": 62018,
+ "cart2": 62019,
+ "cart3": 62020,
+ "cart4": 62021,
+ "cash-stack": 62022,
+ "cash": 62023,
+ "cast": 62024,
+ "chat-dots-fill": 62025,
+ "chat-dots": 62026,
+ "chat-fill": 62027,
+ "chat-left-dots-fill": 62028,
+ "chat-left-dots": 62029,
+ "chat-left-fill": 62030,
+ "chat-left-quote-fill": 62031,
+ "chat-left-quote": 62032,
+ "chat-left-text-fill": 62033,
+ "chat-left-text": 62034,
+ "chat-left": 62035,
+ "chat-quote-fill": 62036,
+ "chat-quote": 62037,
+ "chat-right-dots-fill": 62038,
+ "chat-right-dots": 62039,
+ "chat-right-fill": 62040,
+ "chat-right-quote-fill": 62041,
+ "chat-right-quote": 62042,
+ "chat-right-text-fill": 62043,
+ "chat-right-text": 62044,
+ "chat-right": 62045,
+ "chat-square-dots-fill": 62046,
+ "chat-square-dots": 62047,
+ "chat-square-fill": 62048,
+ "chat-square-quote-fill": 62049,
+ "chat-square-quote": 62050,
+ "chat-square-text-fill": 62051,
+ "chat-square-text": 62052,
+ "chat-square": 62053,
+ "chat-text-fill": 62054,
+ "chat-text": 62055,
+ "chat": 62056,
+ "check-all": 62057,
+ "check-circle-fill": 62058,
+ "check-circle": 62059,
+ "check-square-fill": 62060,
+ "check-square": 62061,
+ "check": 62062,
+ "check2-all": 62063,
+ "check2-circle": 62064,
+ "check2-square": 62065,
+ "check2": 62066,
+ "chevron-bar-contract": 62067,
+ "chevron-bar-down": 62068,
+ "chevron-bar-expand": 62069,
+ "chevron-bar-left": 62070,
+ "chevron-bar-right": 62071,
+ "chevron-bar-up": 62072,
+ "chevron-compact-down": 62073,
+ "chevron-compact-left": 62074,
+ "chevron-compact-right": 62075,
+ "chevron-compact-up": 62076,
+ "chevron-contract": 62077,
+ "chevron-double-down": 62078,
+ "chevron-double-left": 62079,
+ "chevron-double-right": 62080,
+ "chevron-double-up": 62081,
+ "chevron-down": 62082,
+ "chevron-expand": 62083,
+ "chevron-left": 62084,
+ "chevron-right": 62085,
+ "chevron-up": 62086,
+ "circle-fill": 62087,
+ "circle-half": 62088,
+ "circle-square": 62089,
+ "circle": 62090,
+ "clipboard-check": 62091,
+ "clipboard-data": 62092,
+ "clipboard-minus": 62093,
+ "clipboard-plus": 62094,
+ "clipboard-x": 62095,
+ "clipboard": 62096,
+ "clock-fill": 62097,
+ "clock-history": 62098,
+ "clock": 62099,
+ "cloud-arrow-down-fill": 62100,
+ "cloud-arrow-down": 62101,
+ "cloud-arrow-up-fill": 62102,
+ "cloud-arrow-up": 62103,
+ "cloud-check-fill": 62104,
+ "cloud-check": 62105,
+ "cloud-download-fill": 62106,
+ "cloud-download": 62107,
+ "cloud-drizzle-fill": 62108,
+ "cloud-drizzle": 62109,
+ "cloud-fill": 62110,
+ "cloud-fog-fill": 62111,
+ "cloud-fog": 62112,
+ "cloud-fog2-fill": 62113,
+ "cloud-fog2": 62114,
+ "cloud-hail-fill": 62115,
+ "cloud-hail": 62116,
+ "cloud-haze-fill": 62118,
+ "cloud-haze": 62119,
+ "cloud-haze2-fill": 62120,
+ "cloud-lightning-fill": 62121,
+ "cloud-lightning-rain-fill": 62122,
+ "cloud-lightning-rain": 62123,
+ "cloud-lightning": 62124,
+ "cloud-minus-fill": 62125,
+ "cloud-minus": 62126,
+ "cloud-moon-fill": 62127,
+ "cloud-moon": 62128,
+ "cloud-plus-fill": 62129,
+ "cloud-plus": 62130,
+ "cloud-rain-fill": 62131,
+ "cloud-rain-heavy-fill": 62132,
+ "cloud-rain-heavy": 62133,
+ "cloud-rain": 62134,
+ "cloud-slash-fill": 62135,
+ "cloud-slash": 62136,
+ "cloud-sleet-fill": 62137,
+ "cloud-sleet": 62138,
+ "cloud-snow-fill": 62139,
+ "cloud-snow": 62140,
+ "cloud-sun-fill": 62141,
+ "cloud-sun": 62142,
+ "cloud-upload-fill": 62143,
+ "cloud-upload": 62144,
+ "cloud": 62145,
+ "clouds-fill": 62146,
+ "clouds": 62147,
+ "cloudy-fill": 62148,
+ "cloudy": 62149,
+ "code-slash": 62150,
+ "code-square": 62151,
+ "code": 62152,
+ "collection-fill": 62153,
+ "collection-play-fill": 62154,
+ "collection-play": 62155,
+ "collection": 62156,
+ "columns-gap": 62157,
+ "columns": 62158,
+ "command": 62159,
+ "compass-fill": 62160,
+ "compass": 62161,
+ "cone-striped": 62162,
+ "cone": 62163,
+ "controller": 62164,
+ "cpu-fill": 62165,
+ "cpu": 62166,
+ "credit-card-2-back-fill": 62167,
+ "credit-card-2-back": 62168,
+ "credit-card-2-front-fill": 62169,
+ "credit-card-2-front": 62170,
+ "credit-card-fill": 62171,
+ "credit-card": 62172,
+ "crop": 62173,
+ "cup-fill": 62174,
+ "cup-straw": 62175,
+ "cup": 62176,
+ "cursor-fill": 62177,
+ "cursor-text": 62178,
+ "cursor": 62179,
+ "dash-circle-dotted": 62180,
+ "dash-circle-fill": 62181,
+ "dash-circle": 62182,
+ "dash-square-dotted": 62183,
+ "dash-square-fill": 62184,
+ "dash-square": 62185,
+ "dash": 62186,
+ "diagram-2-fill": 62187,
+ "diagram-2": 62188,
+ "diagram-3-fill": 62189,
+ "diagram-3": 62190,
+ "diamond-fill": 62191,
+ "diamond-half": 62192,
+ "diamond": 62193,
+ "dice-1-fill": 62194,
+ "dice-1": 62195,
+ "dice-2-fill": 62196,
+ "dice-2": 62197,
+ "dice-3-fill": 62198,
+ "dice-3": 62199,
+ "dice-4-fill": 62200,
+ "dice-4": 62201,
+ "dice-5-fill": 62202,
+ "dice-5": 62203,
+ "dice-6-fill": 62204,
+ "dice-6": 62205,
+ "disc-fill": 62206,
+ "disc": 62207,
+ "discord": 62208,
+ "display-fill": 62209,
+ "display": 62210,
+ "distribute-horizontal": 62211,
+ "distribute-vertical": 62212,
+ "door-closed-fill": 62213,
+ "door-closed": 62214,
+ "door-open-fill": 62215,
+ "door-open": 62216,
+ "dot": 62217,
+ "download": 62218,
+ "droplet-fill": 62219,
+ "droplet-half": 62220,
+ "droplet": 62221,
+ "earbuds": 62222,
+ "easel-fill": 62223,
+ "easel": 62224,
+ "egg-fill": 62225,
+ "egg-fried": 62226,
+ "egg": 62227,
+ "eject-fill": 62228,
+ "eject": 62229,
+ "emoji-angry-fill": 62230,
+ "emoji-angry": 62231,
+ "emoji-dizzy-fill": 62232,
+ "emoji-dizzy": 62233,
+ "emoji-expressionless-fill": 62234,
+ "emoji-expressionless": 62235,
+ "emoji-frown-fill": 62236,
+ "emoji-frown": 62237,
+ "emoji-heart-eyes-fill": 62238,
+ "emoji-heart-eyes": 62239,
+ "emoji-laughing-fill": 62240,
+ "emoji-laughing": 62241,
+ "emoji-neutral-fill": 62242,
+ "emoji-neutral": 62243,
+ "emoji-smile-fill": 62244,
+ "emoji-smile-upside-down-fill": 62245,
+ "emoji-smile-upside-down": 62246,
+ "emoji-smile": 62247,
+ "emoji-sunglasses-fill": 62248,
+ "emoji-sunglasses": 62249,
+ "emoji-wink-fill": 62250,
+ "emoji-wink": 62251,
+ "envelope-fill": 62252,
+ "envelope-open-fill": 62253,
+ "envelope-open": 62254,
+ "envelope": 62255,
+ "eraser-fill": 62256,
+ "eraser": 62257,
+ "exclamation-circle-fill": 62258,
+ "exclamation-circle": 62259,
+ "exclamation-diamond-fill": 62260,
+ "exclamation-diamond": 62261,
+ "exclamation-octagon-fill": 62262,
+ "exclamation-octagon": 62263,
+ "exclamation-square-fill": 62264,
+ "exclamation-square": 62265,
+ "exclamation-triangle-fill": 62266,
+ "exclamation-triangle": 62267,
+ "exclamation": 62268,
+ "exclude": 62269,
+ "eye-fill": 62270,
+ "eye-slash-fill": 62271,
+ "eye-slash": 62272,
+ "eye": 62273,
+ "eyedropper": 62274,
+ "eyeglasses": 62275,
+ "facebook": 62276,
+ "file-arrow-down-fill": 62277,
+ "file-arrow-down": 62278,
+ "file-arrow-up-fill": 62279,
+ "file-arrow-up": 62280,
+ "file-bar-graph-fill": 62281,
+ "file-bar-graph": 62282,
+ "file-binary-fill": 62283,
+ "file-binary": 62284,
+ "file-break-fill": 62285,
+ "file-break": 62286,
+ "file-check-fill": 62287,
+ "file-check": 62288,
+ "file-code-fill": 62289,
+ "file-code": 62290,
+ "file-diff-fill": 62291,
+ "file-diff": 62292,
+ "file-earmark-arrow-down-fill": 62293,
+ "file-earmark-arrow-down": 62294,
+ "file-earmark-arrow-up-fill": 62295,
+ "file-earmark-arrow-up": 62296,
+ "file-earmark-bar-graph-fill": 62297,
+ "file-earmark-bar-graph": 62298,
+ "file-earmark-binary-fill": 62299,
+ "file-earmark-binary": 62300,
+ "file-earmark-break-fill": 62301,
+ "file-earmark-break": 62302,
+ "file-earmark-check-fill": 62303,
+ "file-earmark-check": 62304,
+ "file-earmark-code-fill": 62305,
+ "file-earmark-code": 62306,
+ "file-earmark-diff-fill": 62307,
+ "file-earmark-diff": 62308,
+ "file-earmark-easel-fill": 62309,
+ "file-earmark-easel": 62310,
+ "file-earmark-excel-fill": 62311,
+ "file-earmark-excel": 62312,
+ "file-earmark-fill": 62313,
+ "file-earmark-font-fill": 62314,
+ "file-earmark-font": 62315,
+ "file-earmark-image-fill": 62316,
+ "file-earmark-image": 62317,
+ "file-earmark-lock-fill": 62318,
+ "file-earmark-lock": 62319,
+ "file-earmark-lock2-fill": 62320,
+ "file-earmark-lock2": 62321,
+ "file-earmark-medical-fill": 62322,
+ "file-earmark-medical": 62323,
+ "file-earmark-minus-fill": 62324,
+ "file-earmark-minus": 62325,
+ "file-earmark-music-fill": 62326,
+ "file-earmark-music": 62327,
+ "file-earmark-person-fill": 62328,
+ "file-earmark-person": 62329,
+ "file-earmark-play-fill": 62330,
+ "file-earmark-play": 62331,
+ "file-earmark-plus-fill": 62332,
+ "file-earmark-plus": 62333,
+ "file-earmark-post-fill": 62334,
+ "file-earmark-post": 62335,
+ "file-earmark-ppt-fill": 62336,
+ "file-earmark-ppt": 62337,
+ "file-earmark-richtext-fill": 62338,
+ "file-earmark-richtext": 62339,
+ "file-earmark-ruled-fill": 62340,
+ "file-earmark-ruled": 62341,
+ "file-earmark-slides-fill": 62342,
+ "file-earmark-slides": 62343,
+ "file-earmark-spreadsheet-fill": 62344,
+ "file-earmark-spreadsheet": 62345,
+ "file-earmark-text-fill": 62346,
+ "file-earmark-text": 62347,
+ "file-earmark-word-fill": 62348,
+ "file-earmark-word": 62349,
+ "file-earmark-x-fill": 62350,
+ "file-earmark-x": 62351,
+ "file-earmark-zip-fill": 62352,
+ "file-earmark-zip": 62353,
+ "file-earmark": 62354,
+ "file-easel-fill": 62355,
+ "file-easel": 62356,
+ "file-excel-fill": 62357,
+ "file-excel": 62358,
+ "file-fill": 62359,
+ "file-font-fill": 62360,
+ "file-font": 62361,
+ "file-image-fill": 62362,
+ "file-image": 62363,
+ "file-lock-fill": 62364,
+ "file-lock": 62365,
+ "file-lock2-fill": 62366,
+ "file-lock2": 62367,
+ "file-medical-fill": 62368,
+ "file-medical": 62369,
+ "file-minus-fill": 62370,
+ "file-minus": 62371,
+ "file-music-fill": 62372,
+ "file-music": 62373,
+ "file-person-fill": 62374,
+ "file-person": 62375,
+ "file-play-fill": 62376,
+ "file-play": 62377,
+ "file-plus-fill": 62378,
+ "file-plus": 62379,
+ "file-post-fill": 62380,
+ "file-post": 62381,
+ "file-ppt-fill": 62382,
+ "file-ppt": 62383,
+ "file-richtext-fill": 62384,
+ "file-richtext": 62385,
+ "file-ruled-fill": 62386,
+ "file-ruled": 62387,
+ "file-slides-fill": 62388,
+ "file-slides": 62389,
+ "file-spreadsheet-fill": 62390,
+ "file-spreadsheet": 62391,
+ "file-text-fill": 62392,
+ "file-text": 62393,
+ "file-word-fill": 62394,
+ "file-word": 62395,
+ "file-x-fill": 62396,
+ "file-x": 62397,
+ "file-zip-fill": 62398,
+ "file-zip": 62399,
+ "file": 62400,
+ "files-alt": 62401,
+ "files": 62402,
+ "film": 62403,
+ "filter-circle-fill": 62404,
+ "filter-circle": 62405,
+ "filter-left": 62406,
+ "filter-right": 62407,
+ "filter-square-fill": 62408,
+ "filter-square": 62409,
+ "filter": 62410,
+ "flag-fill": 62411,
+ "flag": 62412,
+ "flower1": 62413,
+ "flower2": 62414,
+ "flower3": 62415,
+ "folder-check": 62416,
+ "folder-fill": 62417,
+ "folder-minus": 62418,
+ "folder-plus": 62419,
+ "folder-symlink-fill": 62420,
+ "folder-symlink": 62421,
+ "folder-x": 62422,
+ "folder": 62423,
+ "folder2-open": 62424,
+ "folder2": 62425,
+ "fonts": 62426,
+ "forward-fill": 62427,
+ "forward": 62428,
+ "front": 62429,
+ "fullscreen-exit": 62430,
+ "fullscreen": 62431,
+ "funnel-fill": 62432,
+ "funnel": 62433,
+ "gear-fill": 62434,
+ "gear-wide-connected": 62435,
+ "gear-wide": 62436,
+ "gear": 62437,
+ "gem": 62438,
+ "geo-alt-fill": 62439,
+ "geo-alt": 62440,
+ "geo-fill": 62441,
+ "geo": 62442,
+ "gift-fill": 62443,
+ "gift": 62444,
+ "github": 62445,
+ "globe": 62446,
+ "globe2": 62447,
+ "google": 62448,
+ "graph-down": 62449,
+ "graph-up": 62450,
+ "grid-1x2-fill": 62451,
+ "grid-1x2": 62452,
+ "grid-3x2-gap-fill": 62453,
+ "grid-3x2-gap": 62454,
+ "grid-3x2": 62455,
+ "grid-3x3-gap-fill": 62456,
+ "grid-3x3-gap": 62457,
+ "grid-3x3": 62458,
+ "grid-fill": 62459,
+ "grid": 62460,
+ "grip-horizontal": 62461,
+ "grip-vertical": 62462,
+ "hammer": 62463,
+ "hand-index-fill": 62464,
+ "hand-index-thumb-fill": 62465,
+ "hand-index-thumb": 62466,
+ "hand-index": 62467,
+ "hand-thumbs-down-fill": 62468,
+ "hand-thumbs-down": 62469,
+ "hand-thumbs-up-fill": 62470,
+ "hand-thumbs-up": 62471,
+ "handbag-fill": 62472,
+ "handbag": 62473,
+ "hash": 62474,
+ "hdd-fill": 62475,
+ "hdd-network-fill": 62476,
+ "hdd-network": 62477,
+ "hdd-rack-fill": 62478,
+ "hdd-rack": 62479,
+ "hdd-stack-fill": 62480,
+ "hdd-stack": 62481,
+ "hdd": 62482,
+ "headphones": 62483,
+ "headset": 62484,
+ "heart-fill": 62485,
+ "heart-half": 62486,
+ "heart": 62487,
+ "heptagon-fill": 62488,
+ "heptagon-half": 62489,
+ "heptagon": 62490,
+ "hexagon-fill": 62491,
+ "hexagon-half": 62492,
+ "hexagon": 62493,
+ "hourglass-bottom": 62494,
+ "hourglass-split": 62495,
+ "hourglass-top": 62496,
+ "hourglass": 62497,
+ "house-door-fill": 62498,
+ "house-door": 62499,
+ "house-fill": 62500,
+ "house": 62501,
+ "hr": 62502,
+ "hurricane": 62503,
+ "image-alt": 62504,
+ "image-fill": 62505,
+ "image": 62506,
+ "images": 62507,
+ "inbox-fill": 62508,
+ "inbox": 62509,
+ "inboxes-fill": 62510,
+ "inboxes": 62511,
+ "info-circle-fill": 62512,
+ "info-circle": 62513,
+ "info-square-fill": 62514,
+ "info-square": 62515,
+ "info": 62516,
+ "input-cursor-text": 62517,
+ "input-cursor": 62518,
+ "instagram": 62519,
+ "intersect": 62520,
+ "journal-album": 62521,
+ "journal-arrow-down": 62522,
+ "journal-arrow-up": 62523,
+ "journal-bookmark-fill": 62524,
+ "journal-bookmark": 62525,
+ "journal-check": 62526,
+ "journal-code": 62527,
+ "journal-medical": 62528,
+ "journal-minus": 62529,
+ "journal-plus": 62530,
+ "journal-richtext": 62531,
+ "journal-text": 62532,
+ "journal-x": 62533,
+ "journal": 62534,
+ "journals": 62535,
+ "joystick": 62536,
+ "justify-left": 62537,
+ "justify-right": 62538,
+ "justify": 62539,
+ "kanban-fill": 62540,
+ "kanban": 62541,
+ "key-fill": 62542,
+ "key": 62543,
+ "keyboard-fill": 62544,
+ "keyboard": 62545,
+ "ladder": 62546,
+ "lamp-fill": 62547,
+ "lamp": 62548,
+ "laptop-fill": 62549,
+ "laptop": 62550,
+ "layer-backward": 62551,
+ "layer-forward": 62552,
+ "layers-fill": 62553,
+ "layers-half": 62554,
+ "layers": 62555,
+ "layout-sidebar-inset-reverse": 62556,
+ "layout-sidebar-inset": 62557,
+ "layout-sidebar-reverse": 62558,
+ "layout-sidebar": 62559,
+ "layout-split": 62560,
+ "layout-text-sidebar-reverse": 62561,
+ "layout-text-sidebar": 62562,
+ "layout-text-window-reverse": 62563,
+ "layout-text-window": 62564,
+ "layout-three-columns": 62565,
+ "layout-wtf": 62566,
+ "life-preserver": 62567,
+ "lightbulb-fill": 62568,
+ "lightbulb-off-fill": 62569,
+ "lightbulb-off": 62570,
+ "lightbulb": 62571,
+ "lightning-charge-fill": 62572,
+ "lightning-charge": 62573,
+ "lightning-fill": 62574,
+ "lightning": 62575,
+ "link-45deg": 62576,
+ "link": 62577,
+ "linkedin": 62578,
+ "list-check": 62579,
+ "list-nested": 62580,
+ "list-ol": 62581,
+ "list-stars": 62582,
+ "list-task": 62583,
+ "list-ul": 62584,
+ "list": 62585,
+ "lock-fill": 62586,
+ "lock": 62587,
+ "mailbox": 62588,
+ "mailbox2": 62589,
+ "map-fill": 62590,
+ "map": 62591,
+ "markdown-fill": 62592,
+ "markdown": 62593,
+ "mask": 62594,
+ "megaphone-fill": 62595,
+ "megaphone": 62596,
+ "menu-app-fill": 62597,
+ "menu-app": 62598,
+ "menu-button-fill": 62599,
+ "menu-button-wide-fill": 62600,
+ "menu-button-wide": 62601,
+ "menu-button": 62602,
+ "menu-down": 62603,
+ "menu-up": 62604,
+ "mic-fill": 62605,
+ "mic-mute-fill": 62606,
+ "mic-mute": 62607,
+ "mic": 62608,
+ "minecart-loaded": 62609,
+ "minecart": 62610,
+ "moisture": 62611,
+ "moon-fill": 62612,
+ "moon-stars-fill": 62613,
+ "moon-stars": 62614,
+ "moon": 62615,
+ "mouse-fill": 62616,
+ "mouse": 62617,
+ "mouse2-fill": 62618,
+ "mouse2": 62619,
+ "mouse3-fill": 62620,
+ "mouse3": 62621,
+ "music-note-beamed": 62622,
+ "music-note-list": 62623,
+ "music-note": 62624,
+ "music-player-fill": 62625,
+ "music-player": 62626,
+ "newspaper": 62627,
+ "node-minus-fill": 62628,
+ "node-minus": 62629,
+ "node-plus-fill": 62630,
+ "node-plus": 62631,
+ "nut-fill": 62632,
+ "nut": 62633,
+ "octagon-fill": 62634,
+ "octagon-half": 62635,
+ "octagon": 62636,
+ "option": 62637,
+ "outlet": 62638,
+ "paint-bucket": 62639,
+ "palette-fill": 62640,
+ "palette": 62641,
+ "palette2": 62642,
+ "paperclip": 62643,
+ "paragraph": 62644,
+ "patch-check-fill": 62645,
+ "patch-check": 62646,
+ "patch-exclamation-fill": 62647,
+ "patch-exclamation": 62648,
+ "patch-minus-fill": 62649,
+ "patch-minus": 62650,
+ "patch-plus-fill": 62651,
+ "patch-plus": 62652,
+ "patch-question-fill": 62653,
+ "patch-question": 62654,
+ "pause-btn-fill": 62655,
+ "pause-btn": 62656,
+ "pause-circle-fill": 62657,
+ "pause-circle": 62658,
+ "pause-fill": 62659,
+ "pause": 62660,
+ "peace-fill": 62661,
+ "peace": 62662,
+ "pen-fill": 62663,
+ "pen": 62664,
+ "pencil-fill": 62665,
+ "pencil-square": 62666,
+ "pencil": 62667,
+ "pentagon-fill": 62668,
+ "pentagon-half": 62669,
+ "pentagon": 62670,
+ "people-fill": 62671,
+ "people": 62672,
+ "percent": 62673,
+ "person-badge-fill": 62674,
+ "person-badge": 62675,
+ "person-bounding-box": 62676,
+ "person-check-fill": 62677,
+ "person-check": 62678,
+ "person-circle": 62679,
+ "person-dash-fill": 62680,
+ "person-dash": 62681,
+ "person-fill": 62682,
+ "person-lines-fill": 62683,
+ "person-plus-fill": 62684,
+ "person-plus": 62685,
+ "person-square": 62686,
+ "person-x-fill": 62687,
+ "person-x": 62688,
+ "person": 62689,
+ "phone-fill": 62690,
+ "phone-landscape-fill": 62691,
+ "phone-landscape": 62692,
+ "phone-vibrate-fill": 62693,
+ "phone-vibrate": 62694,
+ "phone": 62695,
+ "pie-chart-fill": 62696,
+ "pie-chart": 62697,
+ "pin-angle-fill": 62698,
+ "pin-angle": 62699,
+ "pin-fill": 62700,
+ "pin": 62701,
+ "pip-fill": 62702,
+ "pip": 62703,
+ "play-btn-fill": 62704,
+ "play-btn": 62705,
+ "play-circle-fill": 62706,
+ "play-circle": 62707,
+ "play-fill": 62708,
+ "play": 62709,
+ "plug-fill": 62710,
+ "plug": 62711,
+ "plus-circle-dotted": 62712,
+ "plus-circle-fill": 62713,
+ "plus-circle": 62714,
+ "plus-square-dotted": 62715,
+ "plus-square-fill": 62716,
+ "plus-square": 62717,
+ "plus": 62718,
+ "power": 62719,
+ "printer-fill": 62720,
+ "printer": 62721,
+ "puzzle-fill": 62722,
+ "puzzle": 62723,
+ "question-circle-fill": 62724,
+ "question-circle": 62725,
+ "question-diamond-fill": 62726,
+ "question-diamond": 62727,
+ "question-octagon-fill": 62728,
+ "question-octagon": 62729,
+ "question-square-fill": 62730,
+ "question-square": 62731,
+ "question": 62732,
+ "rainbow": 62733,
+ "receipt-cutoff": 62734,
+ "receipt": 62735,
+ "reception-0": 62736,
+ "reception-1": 62737,
+ "reception-2": 62738,
+ "reception-3": 62739,
+ "reception-4": 62740,
+ "record-btn-fill": 62741,
+ "record-btn": 62742,
+ "record-circle-fill": 62743,
+ "record-circle": 62744,
+ "record-fill": 62745,
+ "record": 62746,
+ "record2-fill": 62747,
+ "record2": 62748,
+ "reply-all-fill": 62749,
+ "reply-all": 62750,
+ "reply-fill": 62751,
+ "reply": 62752,
+ "rss-fill": 62753,
+ "rss": 62754,
+ "rulers": 62755,
+ "save-fill": 62756,
+ "save": 62757,
+ "save2-fill": 62758,
+ "save2": 62759,
+ "scissors": 62760,
+ "screwdriver": 62761,
+ "search": 62762,
+ "segmented-nav": 62763,
+ "server": 62764,
+ "share-fill": 62765,
+ "share": 62766,
+ "shield-check": 62767,
+ "shield-exclamation": 62768,
+ "shield-fill-check": 62769,
+ "shield-fill-exclamation": 62770,
+ "shield-fill-minus": 62771,
+ "shield-fill-plus": 62772,
+ "shield-fill-x": 62773,
+ "shield-fill": 62774,
+ "shield-lock-fill": 62775,
+ "shield-lock": 62776,
+ "shield-minus": 62777,
+ "shield-plus": 62778,
+ "shield-shaded": 62779,
+ "shield-slash-fill": 62780,
+ "shield-slash": 62781,
+ "shield-x": 62782,
+ "shield": 62783,
+ "shift-fill": 62784,
+ "shift": 62785,
+ "shop-window": 62786,
+ "shop": 62787,
+ "shuffle": 62788,
+ "signpost-2-fill": 62789,
+ "signpost-2": 62790,
+ "signpost-fill": 62791,
+ "signpost-split-fill": 62792,
+ "signpost-split": 62793,
+ "signpost": 62794,
+ "sim-fill": 62795,
+ "sim": 62796,
+ "skip-backward-btn-fill": 62797,
+ "skip-backward-btn": 62798,
+ "skip-backward-circle-fill": 62799,
+ "skip-backward-circle": 62800,
+ "skip-backward-fill": 62801,
+ "skip-backward": 62802,
+ "skip-end-btn-fill": 62803,
+ "skip-end-btn": 62804,
+ "skip-end-circle-fill": 62805,
+ "skip-end-circle": 62806,
+ "skip-end-fill": 62807,
+ "skip-end": 62808,
+ "skip-forward-btn-fill": 62809,
+ "skip-forward-btn": 62810,
+ "skip-forward-circle-fill": 62811,
+ "skip-forward-circle": 62812,
+ "skip-forward-fill": 62813,
+ "skip-forward": 62814,
+ "skip-start-btn-fill": 62815,
+ "skip-start-btn": 62816,
+ "skip-start-circle-fill": 62817,
+ "skip-start-circle": 62818,
+ "skip-start-fill": 62819,
+ "skip-start": 62820,
+ "slack": 62821,
+ "slash-circle-fill": 62822,
+ "slash-circle": 62823,
+ "slash-square-fill": 62824,
+ "slash-square": 62825,
+ "slash": 62826,
+ "sliders": 62827,
+ "smartwatch": 62828,
+ "snow": 62829,
+ "snow2": 62830,
+ "snow3": 62831,
+ "sort-alpha-down-alt": 62832,
+ "sort-alpha-down": 62833,
+ "sort-alpha-up-alt": 62834,
+ "sort-alpha-up": 62835,
+ "sort-down-alt": 62836,
+ "sort-down": 62837,
+ "sort-numeric-down-alt": 62838,
+ "sort-numeric-down": 62839,
+ "sort-numeric-up-alt": 62840,
+ "sort-numeric-up": 62841,
+ "sort-up-alt": 62842,
+ "sort-up": 62843,
+ "soundwave": 62844,
+ "speaker-fill": 62845,
+ "speaker": 62846,
+ "speedometer": 62847,
+ "speedometer2": 62848,
+ "spellcheck": 62849,
+ "square-fill": 62850,
+ "square-half": 62851,
+ "square": 62852,
+ "stack": 62853,
+ "star-fill": 62854,
+ "star-half": 62855,
+ "star": 62856,
+ "stars": 62857,
+ "stickies-fill": 62858,
+ "stickies": 62859,
+ "sticky-fill": 62860,
+ "sticky": 62861,
+ "stop-btn-fill": 62862,
+ "stop-btn": 62863,
+ "stop-circle-fill": 62864,
+ "stop-circle": 62865,
+ "stop-fill": 62866,
+ "stop": 62867,
+ "stoplights-fill": 62868,
+ "stoplights": 62869,
+ "stopwatch-fill": 62870,
+ "stopwatch": 62871,
+ "subtract": 62872,
+ "suit-club-fill": 62873,
+ "suit-club": 62874,
+ "suit-diamond-fill": 62875,
+ "suit-diamond": 62876,
+ "suit-heart-fill": 62877,
+ "suit-heart": 62878,
+ "suit-spade-fill": 62879,
+ "suit-spade": 62880,
+ "sun-fill": 62881,
+ "sun": 62882,
+ "sunglasses": 62883,
+ "sunrise-fill": 62884,
+ "sunrise": 62885,
+ "sunset-fill": 62886,
+ "sunset": 62887,
+ "symmetry-horizontal": 62888,
+ "symmetry-vertical": 62889,
+ "table": 62890,
+ "tablet-fill": 62891,
+ "tablet-landscape-fill": 62892,
+ "tablet-landscape": 62893,
+ "tablet": 62894,
+ "tag-fill": 62895,
+ "tag": 62896,
+ "tags-fill": 62897,
+ "tags": 62898,
+ "telegram": 62899,
+ "telephone-fill": 62900,
+ "telephone-forward-fill": 62901,
+ "telephone-forward": 62902,
+ "telephone-inbound-fill": 62903,
+ "telephone-inbound": 62904,
+ "telephone-minus-fill": 62905,
+ "telephone-minus": 62906,
+ "telephone-outbound-fill": 62907,
+ "telephone-outbound": 62908,
+ "telephone-plus-fill": 62909,
+ "telephone-plus": 62910,
+ "telephone-x-fill": 62911,
+ "telephone-x": 62912,
+ "telephone": 62913,
+ "terminal-fill": 62914,
+ "terminal": 62915,
+ "text-center": 62916,
+ "text-indent-left": 62917,
+ "text-indent-right": 62918,
+ "text-left": 62919,
+ "text-paragraph": 62920,
+ "text-right": 62921,
+ "textarea-resize": 62922,
+ "textarea-t": 62923,
+ "textarea": 62924,
+ "thermometer-half": 62925,
+ "thermometer-high": 62926,
+ "thermometer-low": 62927,
+ "thermometer-snow": 62928,
+ "thermometer-sun": 62929,
+ "thermometer": 62930,
+ "three-dots-vertical": 62931,
+ "three-dots": 62932,
+ "toggle-off": 62933,
+ "toggle-on": 62934,
+ "toggle2-off": 62935,
+ "toggle2-on": 62936,
+ "toggles": 62937,
+ "toggles2": 62938,
+ "tools": 62939,
+ "tornado": 62940,
+ "trash-fill": 62941,
+ "trash": 62942,
+ "trash2-fill": 62943,
+ "trash2": 62944,
+ "tree-fill": 62945,
+ "tree": 62946,
+ "triangle-fill": 62947,
+ "triangle-half": 62948,
+ "triangle": 62949,
+ "trophy-fill": 62950,
+ "trophy": 62951,
+ "tropical-storm": 62952,
+ "truck-flatbed": 62953,
+ "truck": 62954,
+ "tsunami": 62955,
+ "tv-fill": 62956,
+ "tv": 62957,
+ "twitch": 62958,
+ "twitter": 62959,
+ "type-bold": 62960,
+ "type-h1": 62961,
+ "type-h2": 62962,
+ "type-h3": 62963,
+ "type-italic": 62964,
+ "type-strikethrough": 62965,
+ "type-underline": 62966,
+ "type": 62967,
+ "ui-checks-grid": 62968,
+ "ui-checks": 62969,
+ "ui-radios-grid": 62970,
+ "ui-radios": 62971,
+ "umbrella-fill": 62972,
+ "umbrella": 62973,
+ "union": 62974,
+ "unlock-fill": 62975,
+ "unlock": 62976,
+ "upc-scan": 62977,
+ "upc": 62978,
+ "upload": 62979,
+ "vector-pen": 62980,
+ "view-list": 62981,
+ "view-stacked": 62982,
+ "vinyl-fill": 62983,
+ "vinyl": 62984,
+ "voicemail": 62985,
+ "volume-down-fill": 62986,
+ "volume-down": 62987,
+ "volume-mute-fill": 62988,
+ "volume-mute": 62989,
+ "volume-off-fill": 62990,
+ "volume-off": 62991,
+ "volume-up-fill": 62992,
+ "volume-up": 62993,
+ "vr": 62994,
+ "wallet-fill": 62995,
+ "wallet": 62996,
+ "wallet2": 62997,
+ "watch": 62998,
+ "water": 62999,
+ "whatsapp": 63000,
+ "wifi-1": 63001,
+ "wifi-2": 63002,
+ "wifi-off": 63003,
+ "wifi": 63004,
+ "wind": 63005,
+ "window-dock": 63006,
+ "window-sidebar": 63007,
+ "window": 63008,
+ "wrench": 63009,
+ "x-circle-fill": 63010,
+ "x-circle": 63011,
+ "x-diamond-fill": 63012,
+ "x-diamond": 63013,
+ "x-octagon-fill": 63014,
+ "x-octagon": 63015,
+ "x-square-fill": 63016,
+ "x-square": 63017,
+ "x": 63018,
+ "youtube": 63019,
+ "zoom-in": 63020,
+ "zoom-out": 63021,
+ "bank": 63022,
+ "bank2": 63023,
+ "bell-slash-fill": 63024,
+ "bell-slash": 63025,
+ "cash-coin": 63026,
+ "check-lg": 63027,
+ "coin": 63028,
+ "currency-bitcoin": 63029,
+ "currency-dollar": 63030,
+ "currency-euro": 63031,
+ "currency-exchange": 63032,
+ "currency-pound": 63033,
+ "currency-yen": 63034,
+ "dash-lg": 63035,
+ "exclamation-lg": 63036,
+ "file-earmark-pdf-fill": 63037,
+ "file-earmark-pdf": 63038,
+ "file-pdf-fill": 63039,
+ "file-pdf": 63040,
+ "gender-ambiguous": 63041,
+ "gender-female": 63042,
+ "gender-male": 63043,
+ "gender-trans": 63044,
+ "headset-vr": 63045,
+ "info-lg": 63046,
+ "mastodon": 63047,
+ "messenger": 63048,
+ "piggy-bank-fill": 63049,
+ "piggy-bank": 63050,
+ "pin-map-fill": 63051,
+ "pin-map": 63052,
+ "plus-lg": 63053,
+ "question-lg": 63054,
+ "recycle": 63055,
+ "reddit": 63056,
+ "safe-fill": 63057,
+ "safe2-fill": 63058,
+ "safe2": 63059,
+ "sd-card-fill": 63060,
+ "sd-card": 63061,
+ "skype": 63062,
+ "slash-lg": 63063,
+ "translate": 63064,
+ "x-lg": 63065,
+ "safe": 63066,
+ "apple": 63067,
+ "microsoft": 63069,
+ "windows": 63070,
+ "behance": 63068,
+ "dribbble": 63071,
+ "line": 63072,
+ "medium": 63073,
+ "paypal": 63074,
+ "pinterest": 63075,
+ "signal": 63076,
+ "snapchat": 63077,
+ "spotify": 63078,
+ "stack-overflow": 63079,
+ "strava": 63080,
+ "wordpress": 63081,
+ "vimeo": 63082,
+ "activity": 63083,
+ "easel2-fill": 63084,
+ "easel2": 63085,
+ "easel3-fill": 63086,
+ "easel3": 63087,
+ "fan": 63088,
+ "fingerprint": 63089,
+ "graph-down-arrow": 63090,
+ "graph-up-arrow": 63091,
+ "hypnotize": 63092,
+ "magic": 63093,
+ "person-rolodex": 63094,
+ "person-video": 63095,
+ "person-video2": 63096,
+ "person-video3": 63097,
+ "person-workspace": 63098,
+ "radioactive": 63099,
+ "webcam-fill": 63100,
+ "webcam": 63101,
+ "yin-yang": 63102,
+ "bandaid-fill": 63104,
+ "bandaid": 63105,
+ "bluetooth": 63106,
+ "body-text": 63107,
+ "boombox": 63108,
+ "boxes": 63109,
+ "dpad-fill": 63110,
+ "dpad": 63111,
+ "ear-fill": 63112,
+ "ear": 63113,
+ "envelope-check-fill": 63115,
+ "envelope-check": 63116,
+ "envelope-dash-fill": 63118,
+ "envelope-dash": 63119,
+ "envelope-exclamation-fill": 63121,
+ "envelope-exclamation": 63122,
+ "envelope-plus-fill": 63123,
+ "envelope-plus": 63124,
+ "envelope-slash-fill": 63126,
+ "envelope-slash": 63127,
+ "envelope-x-fill": 63129,
+ "envelope-x": 63130,
+ "explicit-fill": 63131,
+ "explicit": 63132,
+ "git": 63133,
+ "infinity": 63134,
+ "list-columns-reverse": 63135,
+ "list-columns": 63136,
+ "meta": 63137,
+ "nintendo-switch": 63140,
+ "pc-display-horizontal": 63141,
+ "pc-display": 63142,
+ "pc-horizontal": 63143,
+ "pc": 63144,
+ "playstation": 63145,
+ "plus-slash-minus": 63146,
+ "projector-fill": 63147,
+ "projector": 63148,
+ "qr-code-scan": 63149,
+ "qr-code": 63150,
+ "quora": 63151,
+ "quote": 63152,
+ "robot": 63153,
+ "send-check-fill": 63154,
+ "send-check": 63155,
+ "send-dash-fill": 63156,
+ "send-dash": 63157,
+ "send-exclamation-fill": 63159,
+ "send-exclamation": 63160,
+ "send-fill": 63161,
+ "send-plus-fill": 63162,
+ "send-plus": 63163,
+ "send-slash-fill": 63164,
+ "send-slash": 63165,
+ "send-x-fill": 63166,
+ "send-x": 63167,
+ "send": 63168,
+ "steam": 63169,
+ "terminal-dash": 63171,
+ "terminal-plus": 63172,
+ "terminal-split": 63173,
+ "ticket-detailed-fill": 63174,
+ "ticket-detailed": 63175,
+ "ticket-fill": 63176,
+ "ticket-perforated-fill": 63177,
+ "ticket-perforated": 63178,
+ "ticket": 63179,
+ "tiktok": 63180,
+ "window-dash": 63181,
+ "window-desktop": 63182,
+ "window-fullscreen": 63183,
+ "window-plus": 63184,
+ "window-split": 63185,
+ "window-stack": 63186,
+ "window-x": 63187,
+ "xbox": 63188,
+ "ethernet": 63189,
+ "hdmi-fill": 63190,
+ "hdmi": 63191,
+ "usb-c-fill": 63192,
+ "usb-c": 63193,
+ "usb-fill": 63194,
+ "usb-plug-fill": 63195,
+ "usb-plug": 63196,
+ "usb-symbol": 63197,
+ "usb": 63198,
+ "boombox-fill": 63199,
+ "displayport": 63201,
+ "gpu-card": 63202,
+ "memory": 63203,
+ "modem-fill": 63204,
+ "modem": 63205,
+ "motherboard-fill": 63206,
+ "motherboard": 63207,
+ "optical-audio-fill": 63208,
+ "optical-audio": 63209,
+ "pci-card": 63210,
+ "router-fill": 63211,
+ "router": 63212,
+ "thunderbolt-fill": 63215,
+ "thunderbolt": 63216,
+ "usb-drive-fill": 63217,
+ "usb-drive": 63218,
+ "usb-micro-fill": 63219,
+ "usb-micro": 63220,
+ "usb-mini-fill": 63221,
+ "usb-mini": 63222,
+ "cloud-haze2": 63223,
+ "device-hdd-fill": 63224,
+ "device-hdd": 63225,
+ "device-ssd-fill": 63226,
+ "device-ssd": 63227,
+ "displayport-fill": 63228,
+ "mortarboard-fill": 63229,
+ "mortarboard": 63230,
+ "terminal-x": 63231,
+ "arrow-through-heart-fill": 63232,
+ "arrow-through-heart": 63233,
+ "badge-sd-fill": 63234,
+ "badge-sd": 63235,
+ "bag-heart-fill": 63236,
+ "bag-heart": 63237,
+ "balloon-fill": 63238,
+ "balloon-heart-fill": 63239,
+ "balloon-heart": 63240,
+ "balloon": 63241,
+ "box2-fill": 63242,
+ "box2-heart-fill": 63243,
+ "box2-heart": 63244,
+ "box2": 63245,
+ "braces-asterisk": 63246,
+ "calendar-heart-fill": 63247,
+ "calendar-heart": 63248,
+ "calendar2-heart-fill": 63249,
+ "calendar2-heart": 63250,
+ "chat-heart-fill": 63251,
+ "chat-heart": 63252,
+ "chat-left-heart-fill": 63253,
+ "chat-left-heart": 63254,
+ "chat-right-heart-fill": 63255,
+ "chat-right-heart": 63256,
+ "chat-square-heart-fill": 63257,
+ "chat-square-heart": 63258,
+ "clipboard-check-fill": 63259,
+ "clipboard-data-fill": 63260,
+ "clipboard-fill": 63261,
+ "clipboard-heart-fill": 63262,
+ "clipboard-heart": 63263,
+ "clipboard-minus-fill": 63264,
+ "clipboard-plus-fill": 63265,
+ "clipboard-pulse": 63266,
+ "clipboard-x-fill": 63267,
+ "clipboard2-check-fill": 63268,
+ "clipboard2-check": 63269,
+ "clipboard2-data-fill": 63270,
+ "clipboard2-data": 63271,
+ "clipboard2-fill": 63272,
+ "clipboard2-heart-fill": 63273,
+ "clipboard2-heart": 63274,
+ "clipboard2-minus-fill": 63275,
+ "clipboard2-minus": 63276,
+ "clipboard2-plus-fill": 63277,
+ "clipboard2-plus": 63278,
+ "clipboard2-pulse-fill": 63279,
+ "clipboard2-pulse": 63280,
+ "clipboard2-x-fill": 63281,
+ "clipboard2-x": 63282,
+ "clipboard2": 63283,
+ "emoji-kiss-fill": 63284,
+ "emoji-kiss": 63285,
+ "envelope-heart-fill": 63286,
+ "envelope-heart": 63287,
+ "envelope-open-heart-fill": 63288,
+ "envelope-open-heart": 63289,
+ "envelope-paper-fill": 63290,
+ "envelope-paper-heart-fill": 63291,
+ "envelope-paper-heart": 63292,
+ "envelope-paper": 63293,
+ "filetype-aac": 63294,
+ "filetype-ai": 63295,
+ "filetype-bmp": 63296,
+ "filetype-cs": 63297,
+ "filetype-css": 63298,
+ "filetype-csv": 63299,
+ "filetype-doc": 63300,
+ "filetype-docx": 63301,
+ "filetype-exe": 63302,
+ "filetype-gif": 63303,
+ "filetype-heic": 63304,
+ "filetype-html": 63305,
+ "filetype-java": 63306,
+ "filetype-jpg": 63307,
+ "filetype-js": 63308,
+ "filetype-jsx": 63309,
+ "filetype-key": 63310,
+ "filetype-m4p": 63311,
+ "filetype-md": 63312,
+ "filetype-mdx": 63313,
+ "filetype-mov": 63314,
+ "filetype-mp3": 63315,
+ "filetype-mp4": 63316,
+ "filetype-otf": 63317,
+ "filetype-pdf": 63318,
+ "filetype-php": 63319,
+ "filetype-png": 63320,
+ "filetype-ppt": 63322,
+ "filetype-psd": 63323,
+ "filetype-py": 63324,
+ "filetype-raw": 63325,
+ "filetype-rb": 63326,
+ "filetype-sass": 63327,
+ "filetype-scss": 63328,
+ "filetype-sh": 63329,
+ "filetype-svg": 63330,
+ "filetype-tiff": 63331,
+ "filetype-tsx": 63332,
+ "filetype-ttf": 63333,
+ "filetype-txt": 63334,
+ "filetype-wav": 63335,
+ "filetype-woff": 63336,
+ "filetype-xls": 63338,
+ "filetype-xml": 63339,
+ "filetype-yml": 63340,
+ "heart-arrow": 63341,
+ "heart-pulse-fill": 63342,
+ "heart-pulse": 63343,
+ "heartbreak-fill": 63344,
+ "heartbreak": 63345,
+ "hearts": 63346,
+ "hospital-fill": 63347,
+ "hospital": 63348,
+ "house-heart-fill": 63349,
+ "house-heart": 63350,
+ "incognito": 63351,
+ "magnet-fill": 63352,
+ "magnet": 63353,
+ "person-heart": 63354,
+ "person-hearts": 63355,
+ "phone-flip": 63356,
+ "plugin": 63357,
+ "postage-fill": 63358,
+ "postage-heart-fill": 63359,
+ "postage-heart": 63360,
+ "postage": 63361,
+ "postcard-fill": 63362,
+ "postcard-heart-fill": 63363,
+ "postcard-heart": 63364,
+ "postcard": 63365,
+ "search-heart-fill": 63366,
+ "search-heart": 63367,
+ "sliders2-vertical": 63368,
+ "sliders2": 63369,
+ "trash3-fill": 63370,
+ "trash3": 63371,
+ "valentine": 63372,
+ "valentine2": 63373,
+ "wrench-adjustable-circle-fill": 63374,
+ "wrench-adjustable-circle": 63375,
+ "wrench-adjustable": 63376,
+ "filetype-json": 63377,
+ "filetype-pptx": 63378,
+ "filetype-xlsx": 63379,
+ "1-circle-fill": 63382,
+ "1-circle": 63383,
+ "1-square-fill": 63384,
+ "1-square": 63385,
+ "2-circle-fill": 63388,
+ "2-circle": 63389,
+ "2-square-fill": 63390,
+ "2-square": 63391,
+ "3-circle-fill": 63394,
+ "3-circle": 63395,
+ "3-square-fill": 63396,
+ "3-square": 63397,
+ "4-circle-fill": 63400,
+ "4-circle": 63401,
+ "4-square-fill": 63402,
+ "4-square": 63403,
+ "5-circle-fill": 63406,
+ "5-circle": 63407,
+ "5-square-fill": 63408,
+ "5-square": 63409,
+ "6-circle-fill": 63412,
+ "6-circle": 63413,
+ "6-square-fill": 63414,
+ "6-square": 63415,
+ "7-circle-fill": 63418,
+ "7-circle": 63419,
+ "7-square-fill": 63420,
+ "7-square": 63421,
+ "8-circle-fill": 63424,
+ "8-circle": 63425,
+ "8-square-fill": 63426,
+ "8-square": 63427,
+ "9-circle-fill": 63430,
+ "9-circle": 63431,
+ "9-square-fill": 63432,
+ "9-square": 63433,
+ "airplane-engines-fill": 63434,
+ "airplane-engines": 63435,
+ "airplane-fill": 63436,
+ "airplane": 63437,
+ "alexa": 63438,
+ "alipay": 63439,
+ "android": 63440,
+ "android2": 63441,
+ "box-fill": 63442,
+ "box-seam-fill": 63443,
+ "browser-chrome": 63444,
+ "browser-edge": 63445,
+ "browser-firefox": 63446,
+ "browser-safari": 63447,
+ "c-circle-fill": 63450,
+ "c-circle": 63451,
+ "c-square-fill": 63452,
+ "c-square": 63453,
+ "capsule-pill": 63454,
+ "capsule": 63455,
+ "car-front-fill": 63456,
+ "car-front": 63457,
+ "cassette-fill": 63458,
+ "cassette": 63459,
+ "cc-circle-fill": 63462,
+ "cc-circle": 63463,
+ "cc-square-fill": 63464,
+ "cc-square": 63465,
+ "cup-hot-fill": 63466,
+ "cup-hot": 63467,
+ "currency-rupee": 63468,
+ "dropbox": 63469,
+ "escape": 63470,
+ "fast-forward-btn-fill": 63471,
+ "fast-forward-btn": 63472,
+ "fast-forward-circle-fill": 63473,
+ "fast-forward-circle": 63474,
+ "fast-forward-fill": 63475,
+ "fast-forward": 63476,
+ "filetype-sql": 63477,
+ "fire": 63478,
+ "google-play": 63479,
+ "h-circle-fill": 63482,
+ "h-circle": 63483,
+ "h-square-fill": 63484,
+ "h-square": 63485,
+ "indent": 63486,
+ "lungs-fill": 63487,
+ "lungs": 63488,
+ "microsoft-teams": 63489,
+ "p-circle-fill": 63492,
+ "p-circle": 63493,
+ "p-square-fill": 63494,
+ "p-square": 63495,
+ "pass-fill": 63496,
+ "pass": 63497,
+ "prescription": 63498,
+ "prescription2": 63499,
+ "r-circle-fill": 63502,
+ "r-circle": 63503,
+ "r-square-fill": 63504,
+ "r-square": 63505,
+ "repeat-1": 63506,
+ "repeat": 63507,
+ "rewind-btn-fill": 63508,
+ "rewind-btn": 63509,
+ "rewind-circle-fill": 63510,
+ "rewind-circle": 63511,
+ "rewind-fill": 63512,
+ "rewind": 63513,
+ "train-freight-front-fill": 63514,
+ "train-freight-front": 63515,
+ "train-front-fill": 63516,
+ "train-front": 63517,
+ "train-lightrail-front-fill": 63518,
+ "train-lightrail-front": 63519,
+ "truck-front-fill": 63520,
+ "truck-front": 63521,
+ "ubuntu": 63522,
+ "unindent": 63523,
+ "unity": 63524,
+ "universal-access-circle": 63525,
+ "universal-access": 63526,
+ "virus": 63527,
+ "virus2": 63528,
+ "wechat": 63529,
+ "yelp": 63530,
+ "sign-stop-fill": 63531,
+ "sign-stop-lights-fill": 63532,
+ "sign-stop-lights": 63533,
+ "sign-stop": 63534,
+ "sign-turn-left-fill": 63535,
+ "sign-turn-left": 63536,
+ "sign-turn-right-fill": 63537,
+ "sign-turn-right": 63538,
+ "sign-turn-slight-left-fill": 63539,
+ "sign-turn-slight-left": 63540,
+ "sign-turn-slight-right-fill": 63541,
+ "sign-turn-slight-right": 63542,
+ "sign-yield-fill": 63543,
+ "sign-yield": 63544,
+ "ev-station-fill": 63545,
+ "ev-station": 63546,
+ "fuel-pump-diesel-fill": 63547,
+ "fuel-pump-diesel": 63548,
+ "fuel-pump-fill": 63549,
+ "fuel-pump": 63550,
+ "0-circle-fill": 63551,
+ "0-circle": 63552,
+ "0-square-fill": 63553,
+ "0-square": 63554,
+ "rocket-fill": 63555,
+ "rocket-takeoff-fill": 63556,
+ "rocket-takeoff": 63557,
+ "rocket": 63558,
+ "stripe": 63559,
+ "subscript": 63560,
+ "superscript": 63561,
+ "trello": 63562,
+ "envelope-at-fill": 63563,
+ "envelope-at": 63564,
+ "regex": 63565,
+ "text-wrap": 63566,
+ "sign-dead-end-fill": 63567,
+ "sign-dead-end": 63568,
+ "sign-do-not-enter-fill": 63569,
+ "sign-do-not-enter": 63570,
+ "sign-intersection-fill": 63571,
+ "sign-intersection-side-fill": 63572,
+ "sign-intersection-side": 63573,
+ "sign-intersection-t-fill": 63574,
+ "sign-intersection-t": 63575,
+ "sign-intersection-y-fill": 63576,
+ "sign-intersection-y": 63577,
+ "sign-intersection": 63578,
+ "sign-merge-left-fill": 63579,
+ "sign-merge-left": 63580,
+ "sign-merge-right-fill": 63581,
+ "sign-merge-right": 63582,
+ "sign-no-left-turn-fill": 63583,
+ "sign-no-left-turn": 63584,
+ "sign-no-parking-fill": 63585,
+ "sign-no-parking": 63586,
+ "sign-no-right-turn-fill": 63587,
+ "sign-no-right-turn": 63588,
+ "sign-railroad-fill": 63589,
+ "sign-railroad": 63590,
+ "building-add": 63591,
+ "building-check": 63592,
+ "building-dash": 63593,
+ "building-down": 63594,
+ "building-exclamation": 63595,
+ "building-fill-add": 63596,
+ "building-fill-check": 63597,
+ "building-fill-dash": 63598,
+ "building-fill-down": 63599,
+ "building-fill-exclamation": 63600,
+ "building-fill-gear": 63601,
+ "building-fill-lock": 63602,
+ "building-fill-slash": 63603,
+ "building-fill-up": 63604,
+ "building-fill-x": 63605,
+ "building-fill": 63606,
+ "building-gear": 63607,
+ "building-lock": 63608,
+ "building-slash": 63609,
+ "building-up": 63610,
+ "building-x": 63611,
+ "buildings-fill": 63612,
+ "buildings": 63613,
+ "bus-front-fill": 63614,
+ "bus-front": 63615,
+ "ev-front-fill": 63616,
+ "ev-front": 63617,
+ "globe-americas": 63618,
+ "globe-asia-australia": 63619,
+ "globe-central-south-asia": 63620,
+ "globe-europe-africa": 63621,
+ "house-add-fill": 63622,
+ "house-add": 63623,
+ "house-check-fill": 63624,
+ "house-check": 63625,
+ "house-dash-fill": 63626,
+ "house-dash": 63627,
+ "house-down-fill": 63628,
+ "house-down": 63629,
+ "house-exclamation-fill": 63630,
+ "house-exclamation": 63631,
+ "house-gear-fill": 63632,
+ "house-gear": 63633,
+ "house-lock-fill": 63634,
+ "house-lock": 63635,
+ "house-slash-fill": 63636,
+ "house-slash": 63637,
+ "house-up-fill": 63638,
+ "house-up": 63639,
+ "house-x-fill": 63640,
+ "house-x": 63641,
+ "person-add": 63642,
+ "person-down": 63643,
+ "person-exclamation": 63644,
+ "person-fill-add": 63645,
+ "person-fill-check": 63646,
+ "person-fill-dash": 63647,
+ "person-fill-down": 63648,
+ "person-fill-exclamation": 63649,
+ "person-fill-gear": 63650,
+ "person-fill-lock": 63651,
+ "person-fill-slash": 63652,
+ "person-fill-up": 63653,
+ "person-fill-x": 63654,
+ "person-gear": 63655,
+ "person-lock": 63656,
+ "person-slash": 63657,
+ "person-up": 63658,
+ "scooter": 63659,
+ "taxi-front-fill": 63660,
+ "taxi-front": 63661,
+ "amd": 63662,
+ "database-add": 63663,
+ "database-check": 63664,
+ "database-dash": 63665,
+ "database-down": 63666,
+ "database-exclamation": 63667,
+ "database-fill-add": 63668,
+ "database-fill-check": 63669,
+ "database-fill-dash": 63670,
+ "database-fill-down": 63671,
+ "database-fill-exclamation": 63672,
+ "database-fill-gear": 63673,
+ "database-fill-lock": 63674,
+ "database-fill-slash": 63675,
+ "database-fill-up": 63676,
+ "database-fill-x": 63677,
+ "database-fill": 63678,
+ "database-gear": 63679,
+ "database-lock": 63680,
+ "database-slash": 63681,
+ "database-up": 63682,
+ "database-x": 63683,
+ "database": 63684,
+ "houses-fill": 63685,
+ "houses": 63686,
+ "nvidia": 63687,
+ "person-vcard-fill": 63688,
+ "person-vcard": 63689,
+ "sina-weibo": 63690,
+ "tencent-qq": 63691,
+ "wikipedia": 63692,
+ "alphabet-uppercase": 62117,
+ "alphabet": 63114,
+ "amazon": 63117,
+ "arrows-collapse-vertical": 63120,
+ "arrows-expand-vertical": 63125,
+ "arrows-vertical": 63128,
+ "arrows": 63138,
+ "ban-fill": 63139,
+ "ban": 63158,
+ "bing": 63170,
+ "cake": 63200,
+ "cake2": 63213,
+ "cookie": 63214,
+ "copy": 63321,
+ "crosshair": 63337,
+ "crosshair2": 63380,
+ "emoji-astonished-fill": 63381,
+ "emoji-astonished": 63386,
+ "emoji-grimace-fill": 63387,
+ "emoji-grimace": 63392,
+ "emoji-grin-fill": 63393,
+ "emoji-grin": 63398,
+ "emoji-surprise-fill": 63399,
+ "emoji-surprise": 63404,
+ "emoji-tear-fill": 63405,
+ "emoji-tear": 63410,
+ "envelope-arrow-down-fill": 63411,
+ "envelope-arrow-down": 63416,
+ "envelope-arrow-up-fill": 63417,
+ "envelope-arrow-up": 63422,
+ "feather": 63423,
+ "feather2": 63428,
+ "floppy-fill": 63429,
+ "floppy": 63448,
+ "floppy2-fill": 63449,
+ "floppy2": 63460,
+ "gitlab": 63461,
+ "highlighter": 63480,
+ "marker-tip": 63490,
+ "nvme-fill": 63491,
+ "nvme": 63500,
+ "opencollective": 63501,
+ "pci-card-network": 63693,
+ "pci-card-sound": 63694,
+ "radar": 63695,
+ "send-arrow-down-fill": 63696,
+ "send-arrow-down": 63697,
+ "send-arrow-up-fill": 63698,
+ "send-arrow-up": 63699,
+ "sim-slash-fill": 63700,
+ "sim-slash": 63701,
+ "sourceforge": 63702,
+ "substack": 63703,
+ "threads-fill": 63704,
+ "threads": 63705,
+ "transparency": 63706,
+ "twitter-x": 63707,
+ "type-h4": 63708,
+ "type-h5": 63709,
+ "type-h6": 63710,
+ "backpack-fill": 63711,
+ "backpack": 63712,
+ "backpack2-fill": 63713,
+ "backpack2": 63714,
+ "backpack3-fill": 63715,
+ "backpack3": 63716,
+ "backpack4-fill": 63717,
+ "backpack4": 63718,
+ "brilliance": 63719,
+ "cake-fill": 63720,
+ "cake2-fill": 63721,
+ "duffle-fill": 63722,
+ "duffle": 63723,
+ "exposure": 63724,
+ "gender-neuter": 63725,
+ "highlights": 63726,
+ "luggage-fill": 63727,
+ "luggage": 63728,
+ "mailbox-flag": 63729,
+ "mailbox2-flag": 63730,
+ "noise-reduction": 63731,
+ "passport-fill": 63732,
+ "passport": 63733,
+ "person-arms-up": 63734,
+ "person-raised-hand": 63735,
+ "person-standing-dress": 63736,
+ "person-standing": 63737,
+ "person-walking": 63738,
+ "person-wheelchair": 63739,
+ "shadows": 63740,
+ "suitcase-fill": 63741,
+ "suitcase-lg-fill": 63742,
+ "suitcase-lg": 63743,
+ "suitcase": 63744,
+ "suitcase2-fill": 63745,
+ "suitcase2": 63746,
+ "vignette": 63747,
+ "bluesky": 63481,
+ "tux": 63748,
+ "beaker-fill": 63749,
+ "beaker": 63750,
+ "flask-fill": 63751,
+ "flask-florence-fill": 63752,
+ "flask-florence": 63753,
+ "flask": 63754,
+ "leaf-fill": 63755,
+ "leaf": 63756,
+ "measuring-cup-fill": 63757,
+ "measuring-cup": 63758,
+ "unlock2-fill": 63759,
+ "unlock2": 63760,
+ "battery-low": 63761,
+ "anthropic": 63762,
+ "apple-music": 63763,
+ "claude": 63764,
+ "openai": 63765,
+ "perplexity": 63766,
+ "css": 63767,
+ "javascript": 63768,
+ "typescript": 63769,
+ "fork-knife": 63770,
+ "globe-americas-fill": 63771,
+ "globe-asia-australia-fill": 63772,
+ "globe-central-south-asia-fill": 63773,
+ "globe-europe-africa-fill": 63774
+}
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-icons.min.css b/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-icons.min.css
new file mode 100644
index 00000000..706a5c8b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-icons.min.css
@@ -0,0 +1,5 @@
+/*!
+ * Bootstrap Icons v1.13.1 (https://icons.getbootstrap.com/)
+ * Copyright 2019-2024 The Bootstrap Authors
+ * Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE)
+ */@font-face{font-display:block;font-family:bootstrap-icons;src:url("fonts/bootstrap-icons.woff2?e34853135f9e39acf64315236852cd5a") format("woff2"),url("fonts/bootstrap-icons.woff?e34853135f9e39acf64315236852cd5a") format("woff")}.bi::before,[class*=" bi-"]::before,[class^=bi-]::before{display:inline-block;font-family:bootstrap-icons!important;font-style:normal;font-weight:400!important;font-variant:normal;text-transform:none;line-height:1;vertical-align:-.125em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.bi-123::before{content:"\f67f"}.bi-alarm-fill::before{content:"\f101"}.bi-alarm::before{content:"\f102"}.bi-align-bottom::before{content:"\f103"}.bi-align-center::before{content:"\f104"}.bi-align-end::before{content:"\f105"}.bi-align-middle::before{content:"\f106"}.bi-align-start::before{content:"\f107"}.bi-align-top::before{content:"\f108"}.bi-alt::before{content:"\f109"}.bi-app-indicator::before{content:"\f10a"}.bi-app::before{content:"\f10b"}.bi-archive-fill::before{content:"\f10c"}.bi-archive::before{content:"\f10d"}.bi-arrow-90deg-down::before{content:"\f10e"}.bi-arrow-90deg-left::before{content:"\f10f"}.bi-arrow-90deg-right::before{content:"\f110"}.bi-arrow-90deg-up::before{content:"\f111"}.bi-arrow-bar-down::before{content:"\f112"}.bi-arrow-bar-left::before{content:"\f113"}.bi-arrow-bar-right::before{content:"\f114"}.bi-arrow-bar-up::before{content:"\f115"}.bi-arrow-clockwise::before{content:"\f116"}.bi-arrow-counterclockwise::before{content:"\f117"}.bi-arrow-down-circle-fill::before{content:"\f118"}.bi-arrow-down-circle::before{content:"\f119"}.bi-arrow-down-left-circle-fill::before{content:"\f11a"}.bi-arrow-down-left-circle::before{content:"\f11b"}.bi-arrow-down-left-square-fill::before{content:"\f11c"}.bi-arrow-down-left-square::before{content:"\f11d"}.bi-arrow-down-left::before{content:"\f11e"}.bi-arrow-down-right-circle-fill::before{content:"\f11f"}.bi-arrow-down-right-circle::before{content:"\f120"}.bi-arrow-down-right-square-fill::before{content:"\f121"}.bi-arrow-down-right-square::before{content:"\f122"}.bi-arrow-down-right::before{content:"\f123"}.bi-arrow-down-short::before{content:"\f124"}.bi-arrow-down-square-fill::before{content:"\f125"}.bi-arrow-down-square::before{content:"\f126"}.bi-arrow-down-up::before{content:"\f127"}.bi-arrow-down::before{content:"\f128"}.bi-arrow-left-circle-fill::before{content:"\f129"}.bi-arrow-left-circle::before{content:"\f12a"}.bi-arrow-left-right::before{content:"\f12b"}.bi-arrow-left-short::before{content:"\f12c"}.bi-arrow-left-square-fill::before{content:"\f12d"}.bi-arrow-left-square::before{content:"\f12e"}.bi-arrow-left::before{content:"\f12f"}.bi-arrow-repeat::before{content:"\f130"}.bi-arrow-return-left::before{content:"\f131"}.bi-arrow-return-right::before{content:"\f132"}.bi-arrow-right-circle-fill::before{content:"\f133"}.bi-arrow-right-circle::before{content:"\f134"}.bi-arrow-right-short::before{content:"\f135"}.bi-arrow-right-square-fill::before{content:"\f136"}.bi-arrow-right-square::before{content:"\f137"}.bi-arrow-right::before{content:"\f138"}.bi-arrow-up-circle-fill::before{content:"\f139"}.bi-arrow-up-circle::before{content:"\f13a"}.bi-arrow-up-left-circle-fill::before{content:"\f13b"}.bi-arrow-up-left-circle::before{content:"\f13c"}.bi-arrow-up-left-square-fill::before{content:"\f13d"}.bi-arrow-up-left-square::before{content:"\f13e"}.bi-arrow-up-left::before{content:"\f13f"}.bi-arrow-up-right-circle-fill::before{content:"\f140"}.bi-arrow-up-right-circle::before{content:"\f141"}.bi-arrow-up-right-square-fill::before{content:"\f142"}.bi-arrow-up-right-square::before{content:"\f143"}.bi-arrow-up-right::before{content:"\f144"}.bi-arrow-up-short::before{content:"\f145"}.bi-arrow-up-square-fill::before{content:"\f146"}.bi-arrow-up-square::before{content:"\f147"}.bi-arrow-up::before{content:"\f148"}.bi-arrows-angle-contract::before{content:"\f149"}.bi-arrows-angle-expand::before{content:"\f14a"}.bi-arrows-collapse::before{content:"\f14b"}.bi-arrows-expand::before{content:"\f14c"}.bi-arrows-fullscreen::before{content:"\f14d"}.bi-arrows-move::before{content:"\f14e"}.bi-aspect-ratio-fill::before{content:"\f14f"}.bi-aspect-ratio::before{content:"\f150"}.bi-asterisk::before{content:"\f151"}.bi-at::before{content:"\f152"}.bi-award-fill::before{content:"\f153"}.bi-award::before{content:"\f154"}.bi-back::before{content:"\f155"}.bi-backspace-fill::before{content:"\f156"}.bi-backspace-reverse-fill::before{content:"\f157"}.bi-backspace-reverse::before{content:"\f158"}.bi-backspace::before{content:"\f159"}.bi-badge-3d-fill::before{content:"\f15a"}.bi-badge-3d::before{content:"\f15b"}.bi-badge-4k-fill::before{content:"\f15c"}.bi-badge-4k::before{content:"\f15d"}.bi-badge-8k-fill::before{content:"\f15e"}.bi-badge-8k::before{content:"\f15f"}.bi-badge-ad-fill::before{content:"\f160"}.bi-badge-ad::before{content:"\f161"}.bi-badge-ar-fill::before{content:"\f162"}.bi-badge-ar::before{content:"\f163"}.bi-badge-cc-fill::before{content:"\f164"}.bi-badge-cc::before{content:"\f165"}.bi-badge-hd-fill::before{content:"\f166"}.bi-badge-hd::before{content:"\f167"}.bi-badge-tm-fill::before{content:"\f168"}.bi-badge-tm::before{content:"\f169"}.bi-badge-vo-fill::before{content:"\f16a"}.bi-badge-vo::before{content:"\f16b"}.bi-badge-vr-fill::before{content:"\f16c"}.bi-badge-vr::before{content:"\f16d"}.bi-badge-wc-fill::before{content:"\f16e"}.bi-badge-wc::before{content:"\f16f"}.bi-bag-check-fill::before{content:"\f170"}.bi-bag-check::before{content:"\f171"}.bi-bag-dash-fill::before{content:"\f172"}.bi-bag-dash::before{content:"\f173"}.bi-bag-fill::before{content:"\f174"}.bi-bag-plus-fill::before{content:"\f175"}.bi-bag-plus::before{content:"\f176"}.bi-bag-x-fill::before{content:"\f177"}.bi-bag-x::before{content:"\f178"}.bi-bag::before{content:"\f179"}.bi-bar-chart-fill::before{content:"\f17a"}.bi-bar-chart-line-fill::before{content:"\f17b"}.bi-bar-chart-line::before{content:"\f17c"}.bi-bar-chart-steps::before{content:"\f17d"}.bi-bar-chart::before{content:"\f17e"}.bi-basket-fill::before{content:"\f17f"}.bi-basket::before{content:"\f180"}.bi-basket2-fill::before{content:"\f181"}.bi-basket2::before{content:"\f182"}.bi-basket3-fill::before{content:"\f183"}.bi-basket3::before{content:"\f184"}.bi-battery-charging::before{content:"\f185"}.bi-battery-full::before{content:"\f186"}.bi-battery-half::before{content:"\f187"}.bi-battery::before{content:"\f188"}.bi-bell-fill::before{content:"\f189"}.bi-bell::before{content:"\f18a"}.bi-bezier::before{content:"\f18b"}.bi-bezier2::before{content:"\f18c"}.bi-bicycle::before{content:"\f18d"}.bi-binoculars-fill::before{content:"\f18e"}.bi-binoculars::before{content:"\f18f"}.bi-blockquote-left::before{content:"\f190"}.bi-blockquote-right::before{content:"\f191"}.bi-book-fill::before{content:"\f192"}.bi-book-half::before{content:"\f193"}.bi-book::before{content:"\f194"}.bi-bookmark-check-fill::before{content:"\f195"}.bi-bookmark-check::before{content:"\f196"}.bi-bookmark-dash-fill::before{content:"\f197"}.bi-bookmark-dash::before{content:"\f198"}.bi-bookmark-fill::before{content:"\f199"}.bi-bookmark-heart-fill::before{content:"\f19a"}.bi-bookmark-heart::before{content:"\f19b"}.bi-bookmark-plus-fill::before{content:"\f19c"}.bi-bookmark-plus::before{content:"\f19d"}.bi-bookmark-star-fill::before{content:"\f19e"}.bi-bookmark-star::before{content:"\f19f"}.bi-bookmark-x-fill::before{content:"\f1a0"}.bi-bookmark-x::before{content:"\f1a1"}.bi-bookmark::before{content:"\f1a2"}.bi-bookmarks-fill::before{content:"\f1a3"}.bi-bookmarks::before{content:"\f1a4"}.bi-bookshelf::before{content:"\f1a5"}.bi-bootstrap-fill::before{content:"\f1a6"}.bi-bootstrap-reboot::before{content:"\f1a7"}.bi-bootstrap::before{content:"\f1a8"}.bi-border-all::before{content:"\f1a9"}.bi-border-bottom::before{content:"\f1aa"}.bi-border-center::before{content:"\f1ab"}.bi-border-inner::before{content:"\f1ac"}.bi-border-left::before{content:"\f1ad"}.bi-border-middle::before{content:"\f1ae"}.bi-border-outer::before{content:"\f1af"}.bi-border-right::before{content:"\f1b0"}.bi-border-style::before{content:"\f1b1"}.bi-border-top::before{content:"\f1b2"}.bi-border-width::before{content:"\f1b3"}.bi-border::before{content:"\f1b4"}.bi-bounding-box-circles::before{content:"\f1b5"}.bi-bounding-box::before{content:"\f1b6"}.bi-box-arrow-down-left::before{content:"\f1b7"}.bi-box-arrow-down-right::before{content:"\f1b8"}.bi-box-arrow-down::before{content:"\f1b9"}.bi-box-arrow-in-down-left::before{content:"\f1ba"}.bi-box-arrow-in-down-right::before{content:"\f1bb"}.bi-box-arrow-in-down::before{content:"\f1bc"}.bi-box-arrow-in-left::before{content:"\f1bd"}.bi-box-arrow-in-right::before{content:"\f1be"}.bi-box-arrow-in-up-left::before{content:"\f1bf"}.bi-box-arrow-in-up-right::before{content:"\f1c0"}.bi-box-arrow-in-up::before{content:"\f1c1"}.bi-box-arrow-left::before{content:"\f1c2"}.bi-box-arrow-right::before{content:"\f1c3"}.bi-box-arrow-up-left::before{content:"\f1c4"}.bi-box-arrow-up-right::before{content:"\f1c5"}.bi-box-arrow-up::before{content:"\f1c6"}.bi-box-seam::before{content:"\f1c7"}.bi-box::before{content:"\f1c8"}.bi-braces::before{content:"\f1c9"}.bi-bricks::before{content:"\f1ca"}.bi-briefcase-fill::before{content:"\f1cb"}.bi-briefcase::before{content:"\f1cc"}.bi-brightness-alt-high-fill::before{content:"\f1cd"}.bi-brightness-alt-high::before{content:"\f1ce"}.bi-brightness-alt-low-fill::before{content:"\f1cf"}.bi-brightness-alt-low::before{content:"\f1d0"}.bi-brightness-high-fill::before{content:"\f1d1"}.bi-brightness-high::before{content:"\f1d2"}.bi-brightness-low-fill::before{content:"\f1d3"}.bi-brightness-low::before{content:"\f1d4"}.bi-broadcast-pin::before{content:"\f1d5"}.bi-broadcast::before{content:"\f1d6"}.bi-brush-fill::before{content:"\f1d7"}.bi-brush::before{content:"\f1d8"}.bi-bucket-fill::before{content:"\f1d9"}.bi-bucket::before{content:"\f1da"}.bi-bug-fill::before{content:"\f1db"}.bi-bug::before{content:"\f1dc"}.bi-building::before{content:"\f1dd"}.bi-bullseye::before{content:"\f1de"}.bi-calculator-fill::before{content:"\f1df"}.bi-calculator::before{content:"\f1e0"}.bi-calendar-check-fill::before{content:"\f1e1"}.bi-calendar-check::before{content:"\f1e2"}.bi-calendar-date-fill::before{content:"\f1e3"}.bi-calendar-date::before{content:"\f1e4"}.bi-calendar-day-fill::before{content:"\f1e5"}.bi-calendar-day::before{content:"\f1e6"}.bi-calendar-event-fill::before{content:"\f1e7"}.bi-calendar-event::before{content:"\f1e8"}.bi-calendar-fill::before{content:"\f1e9"}.bi-calendar-minus-fill::before{content:"\f1ea"}.bi-calendar-minus::before{content:"\f1eb"}.bi-calendar-month-fill::before{content:"\f1ec"}.bi-calendar-month::before{content:"\f1ed"}.bi-calendar-plus-fill::before{content:"\f1ee"}.bi-calendar-plus::before{content:"\f1ef"}.bi-calendar-range-fill::before{content:"\f1f0"}.bi-calendar-range::before{content:"\f1f1"}.bi-calendar-week-fill::before{content:"\f1f2"}.bi-calendar-week::before{content:"\f1f3"}.bi-calendar-x-fill::before{content:"\f1f4"}.bi-calendar-x::before{content:"\f1f5"}.bi-calendar::before{content:"\f1f6"}.bi-calendar2-check-fill::before{content:"\f1f7"}.bi-calendar2-check::before{content:"\f1f8"}.bi-calendar2-date-fill::before{content:"\f1f9"}.bi-calendar2-date::before{content:"\f1fa"}.bi-calendar2-day-fill::before{content:"\f1fb"}.bi-calendar2-day::before{content:"\f1fc"}.bi-calendar2-event-fill::before{content:"\f1fd"}.bi-calendar2-event::before{content:"\f1fe"}.bi-calendar2-fill::before{content:"\f1ff"}.bi-calendar2-minus-fill::before{content:"\f200"}.bi-calendar2-minus::before{content:"\f201"}.bi-calendar2-month-fill::before{content:"\f202"}.bi-calendar2-month::before{content:"\f203"}.bi-calendar2-plus-fill::before{content:"\f204"}.bi-calendar2-plus::before{content:"\f205"}.bi-calendar2-range-fill::before{content:"\f206"}.bi-calendar2-range::before{content:"\f207"}.bi-calendar2-week-fill::before{content:"\f208"}.bi-calendar2-week::before{content:"\f209"}.bi-calendar2-x-fill::before{content:"\f20a"}.bi-calendar2-x::before{content:"\f20b"}.bi-calendar2::before{content:"\f20c"}.bi-calendar3-event-fill::before{content:"\f20d"}.bi-calendar3-event::before{content:"\f20e"}.bi-calendar3-fill::before{content:"\f20f"}.bi-calendar3-range-fill::before{content:"\f210"}.bi-calendar3-range::before{content:"\f211"}.bi-calendar3-week-fill::before{content:"\f212"}.bi-calendar3-week::before{content:"\f213"}.bi-calendar3::before{content:"\f214"}.bi-calendar4-event::before{content:"\f215"}.bi-calendar4-range::before{content:"\f216"}.bi-calendar4-week::before{content:"\f217"}.bi-calendar4::before{content:"\f218"}.bi-camera-fill::before{content:"\f219"}.bi-camera-reels-fill::before{content:"\f21a"}.bi-camera-reels::before{content:"\f21b"}.bi-camera-video-fill::before{content:"\f21c"}.bi-camera-video-off-fill::before{content:"\f21d"}.bi-camera-video-off::before{content:"\f21e"}.bi-camera-video::before{content:"\f21f"}.bi-camera::before{content:"\f220"}.bi-camera2::before{content:"\f221"}.bi-capslock-fill::before{content:"\f222"}.bi-capslock::before{content:"\f223"}.bi-card-checklist::before{content:"\f224"}.bi-card-heading::before{content:"\f225"}.bi-card-image::before{content:"\f226"}.bi-card-list::before{content:"\f227"}.bi-card-text::before{content:"\f228"}.bi-caret-down-fill::before{content:"\f229"}.bi-caret-down-square-fill::before{content:"\f22a"}.bi-caret-down-square::before{content:"\f22b"}.bi-caret-down::before{content:"\f22c"}.bi-caret-left-fill::before{content:"\f22d"}.bi-caret-left-square-fill::before{content:"\f22e"}.bi-caret-left-square::before{content:"\f22f"}.bi-caret-left::before{content:"\f230"}.bi-caret-right-fill::before{content:"\f231"}.bi-caret-right-square-fill::before{content:"\f232"}.bi-caret-right-square::before{content:"\f233"}.bi-caret-right::before{content:"\f234"}.bi-caret-up-fill::before{content:"\f235"}.bi-caret-up-square-fill::before{content:"\f236"}.bi-caret-up-square::before{content:"\f237"}.bi-caret-up::before{content:"\f238"}.bi-cart-check-fill::before{content:"\f239"}.bi-cart-check::before{content:"\f23a"}.bi-cart-dash-fill::before{content:"\f23b"}.bi-cart-dash::before{content:"\f23c"}.bi-cart-fill::before{content:"\f23d"}.bi-cart-plus-fill::before{content:"\f23e"}.bi-cart-plus::before{content:"\f23f"}.bi-cart-x-fill::before{content:"\f240"}.bi-cart-x::before{content:"\f241"}.bi-cart::before{content:"\f242"}.bi-cart2::before{content:"\f243"}.bi-cart3::before{content:"\f244"}.bi-cart4::before{content:"\f245"}.bi-cash-stack::before{content:"\f246"}.bi-cash::before{content:"\f247"}.bi-cast::before{content:"\f248"}.bi-chat-dots-fill::before{content:"\f249"}.bi-chat-dots::before{content:"\f24a"}.bi-chat-fill::before{content:"\f24b"}.bi-chat-left-dots-fill::before{content:"\f24c"}.bi-chat-left-dots::before{content:"\f24d"}.bi-chat-left-fill::before{content:"\f24e"}.bi-chat-left-quote-fill::before{content:"\f24f"}.bi-chat-left-quote::before{content:"\f250"}.bi-chat-left-text-fill::before{content:"\f251"}.bi-chat-left-text::before{content:"\f252"}.bi-chat-left::before{content:"\f253"}.bi-chat-quote-fill::before{content:"\f254"}.bi-chat-quote::before{content:"\f255"}.bi-chat-right-dots-fill::before{content:"\f256"}.bi-chat-right-dots::before{content:"\f257"}.bi-chat-right-fill::before{content:"\f258"}.bi-chat-right-quote-fill::before{content:"\f259"}.bi-chat-right-quote::before{content:"\f25a"}.bi-chat-right-text-fill::before{content:"\f25b"}.bi-chat-right-text::before{content:"\f25c"}.bi-chat-right::before{content:"\f25d"}.bi-chat-square-dots-fill::before{content:"\f25e"}.bi-chat-square-dots::before{content:"\f25f"}.bi-chat-square-fill::before{content:"\f260"}.bi-chat-square-quote-fill::before{content:"\f261"}.bi-chat-square-quote::before{content:"\f262"}.bi-chat-square-text-fill::before{content:"\f263"}.bi-chat-square-text::before{content:"\f264"}.bi-chat-square::before{content:"\f265"}.bi-chat-text-fill::before{content:"\f266"}.bi-chat-text::before{content:"\f267"}.bi-chat::before{content:"\f268"}.bi-check-all::before{content:"\f269"}.bi-check-circle-fill::before{content:"\f26a"}.bi-check-circle::before{content:"\f26b"}.bi-check-square-fill::before{content:"\f26c"}.bi-check-square::before{content:"\f26d"}.bi-check::before{content:"\f26e"}.bi-check2-all::before{content:"\f26f"}.bi-check2-circle::before{content:"\f270"}.bi-check2-square::before{content:"\f271"}.bi-check2::before{content:"\f272"}.bi-chevron-bar-contract::before{content:"\f273"}.bi-chevron-bar-down::before{content:"\f274"}.bi-chevron-bar-expand::before{content:"\f275"}.bi-chevron-bar-left::before{content:"\f276"}.bi-chevron-bar-right::before{content:"\f277"}.bi-chevron-bar-up::before{content:"\f278"}.bi-chevron-compact-down::before{content:"\f279"}.bi-chevron-compact-left::before{content:"\f27a"}.bi-chevron-compact-right::before{content:"\f27b"}.bi-chevron-compact-up::before{content:"\f27c"}.bi-chevron-contract::before{content:"\f27d"}.bi-chevron-double-down::before{content:"\f27e"}.bi-chevron-double-left::before{content:"\f27f"}.bi-chevron-double-right::before{content:"\f280"}.bi-chevron-double-up::before{content:"\f281"}.bi-chevron-down::before{content:"\f282"}.bi-chevron-expand::before{content:"\f283"}.bi-chevron-left::before{content:"\f284"}.bi-chevron-right::before{content:"\f285"}.bi-chevron-up::before{content:"\f286"}.bi-circle-fill::before{content:"\f287"}.bi-circle-half::before{content:"\f288"}.bi-circle-square::before{content:"\f289"}.bi-circle::before{content:"\f28a"}.bi-clipboard-check::before{content:"\f28b"}.bi-clipboard-data::before{content:"\f28c"}.bi-clipboard-minus::before{content:"\f28d"}.bi-clipboard-plus::before{content:"\f28e"}.bi-clipboard-x::before{content:"\f28f"}.bi-clipboard::before{content:"\f290"}.bi-clock-fill::before{content:"\f291"}.bi-clock-history::before{content:"\f292"}.bi-clock::before{content:"\f293"}.bi-cloud-arrow-down-fill::before{content:"\f294"}.bi-cloud-arrow-down::before{content:"\f295"}.bi-cloud-arrow-up-fill::before{content:"\f296"}.bi-cloud-arrow-up::before{content:"\f297"}.bi-cloud-check-fill::before{content:"\f298"}.bi-cloud-check::before{content:"\f299"}.bi-cloud-download-fill::before{content:"\f29a"}.bi-cloud-download::before{content:"\f29b"}.bi-cloud-drizzle-fill::before{content:"\f29c"}.bi-cloud-drizzle::before{content:"\f29d"}.bi-cloud-fill::before{content:"\f29e"}.bi-cloud-fog-fill::before{content:"\f29f"}.bi-cloud-fog::before{content:"\f2a0"}.bi-cloud-fog2-fill::before{content:"\f2a1"}.bi-cloud-fog2::before{content:"\f2a2"}.bi-cloud-hail-fill::before{content:"\f2a3"}.bi-cloud-hail::before{content:"\f2a4"}.bi-cloud-haze-fill::before{content:"\f2a6"}.bi-cloud-haze::before{content:"\f2a7"}.bi-cloud-haze2-fill::before{content:"\f2a8"}.bi-cloud-lightning-fill::before{content:"\f2a9"}.bi-cloud-lightning-rain-fill::before{content:"\f2aa"}.bi-cloud-lightning-rain::before{content:"\f2ab"}.bi-cloud-lightning::before{content:"\f2ac"}.bi-cloud-minus-fill::before{content:"\f2ad"}.bi-cloud-minus::before{content:"\f2ae"}.bi-cloud-moon-fill::before{content:"\f2af"}.bi-cloud-moon::before{content:"\f2b0"}.bi-cloud-plus-fill::before{content:"\f2b1"}.bi-cloud-plus::before{content:"\f2b2"}.bi-cloud-rain-fill::before{content:"\f2b3"}.bi-cloud-rain-heavy-fill::before{content:"\f2b4"}.bi-cloud-rain-heavy::before{content:"\f2b5"}.bi-cloud-rain::before{content:"\f2b6"}.bi-cloud-slash-fill::before{content:"\f2b7"}.bi-cloud-slash::before{content:"\f2b8"}.bi-cloud-sleet-fill::before{content:"\f2b9"}.bi-cloud-sleet::before{content:"\f2ba"}.bi-cloud-snow-fill::before{content:"\f2bb"}.bi-cloud-snow::before{content:"\f2bc"}.bi-cloud-sun-fill::before{content:"\f2bd"}.bi-cloud-sun::before{content:"\f2be"}.bi-cloud-upload-fill::before{content:"\f2bf"}.bi-cloud-upload::before{content:"\f2c0"}.bi-cloud::before{content:"\f2c1"}.bi-clouds-fill::before{content:"\f2c2"}.bi-clouds::before{content:"\f2c3"}.bi-cloudy-fill::before{content:"\f2c4"}.bi-cloudy::before{content:"\f2c5"}.bi-code-slash::before{content:"\f2c6"}.bi-code-square::before{content:"\f2c7"}.bi-code::before{content:"\f2c8"}.bi-collection-fill::before{content:"\f2c9"}.bi-collection-play-fill::before{content:"\f2ca"}.bi-collection-play::before{content:"\f2cb"}.bi-collection::before{content:"\f2cc"}.bi-columns-gap::before{content:"\f2cd"}.bi-columns::before{content:"\f2ce"}.bi-command::before{content:"\f2cf"}.bi-compass-fill::before{content:"\f2d0"}.bi-compass::before{content:"\f2d1"}.bi-cone-striped::before{content:"\f2d2"}.bi-cone::before{content:"\f2d3"}.bi-controller::before{content:"\f2d4"}.bi-cpu-fill::before{content:"\f2d5"}.bi-cpu::before{content:"\f2d6"}.bi-credit-card-2-back-fill::before{content:"\f2d7"}.bi-credit-card-2-back::before{content:"\f2d8"}.bi-credit-card-2-front-fill::before{content:"\f2d9"}.bi-credit-card-2-front::before{content:"\f2da"}.bi-credit-card-fill::before{content:"\f2db"}.bi-credit-card::before{content:"\f2dc"}.bi-crop::before{content:"\f2dd"}.bi-cup-fill::before{content:"\f2de"}.bi-cup-straw::before{content:"\f2df"}.bi-cup::before{content:"\f2e0"}.bi-cursor-fill::before{content:"\f2e1"}.bi-cursor-text::before{content:"\f2e2"}.bi-cursor::before{content:"\f2e3"}.bi-dash-circle-dotted::before{content:"\f2e4"}.bi-dash-circle-fill::before{content:"\f2e5"}.bi-dash-circle::before{content:"\f2e6"}.bi-dash-square-dotted::before{content:"\f2e7"}.bi-dash-square-fill::before{content:"\f2e8"}.bi-dash-square::before{content:"\f2e9"}.bi-dash::before{content:"\f2ea"}.bi-diagram-2-fill::before{content:"\f2eb"}.bi-diagram-2::before{content:"\f2ec"}.bi-diagram-3-fill::before{content:"\f2ed"}.bi-diagram-3::before{content:"\f2ee"}.bi-diamond-fill::before{content:"\f2ef"}.bi-diamond-half::before{content:"\f2f0"}.bi-diamond::before{content:"\f2f1"}.bi-dice-1-fill::before{content:"\f2f2"}.bi-dice-1::before{content:"\f2f3"}.bi-dice-2-fill::before{content:"\f2f4"}.bi-dice-2::before{content:"\f2f5"}.bi-dice-3-fill::before{content:"\f2f6"}.bi-dice-3::before{content:"\f2f7"}.bi-dice-4-fill::before{content:"\f2f8"}.bi-dice-4::before{content:"\f2f9"}.bi-dice-5-fill::before{content:"\f2fa"}.bi-dice-5::before{content:"\f2fb"}.bi-dice-6-fill::before{content:"\f2fc"}.bi-dice-6::before{content:"\f2fd"}.bi-disc-fill::before{content:"\f2fe"}.bi-disc::before{content:"\f2ff"}.bi-discord::before{content:"\f300"}.bi-display-fill::before{content:"\f301"}.bi-display::before{content:"\f302"}.bi-distribute-horizontal::before{content:"\f303"}.bi-distribute-vertical::before{content:"\f304"}.bi-door-closed-fill::before{content:"\f305"}.bi-door-closed::before{content:"\f306"}.bi-door-open-fill::before{content:"\f307"}.bi-door-open::before{content:"\f308"}.bi-dot::before{content:"\f309"}.bi-download::before{content:"\f30a"}.bi-droplet-fill::before{content:"\f30b"}.bi-droplet-half::before{content:"\f30c"}.bi-droplet::before{content:"\f30d"}.bi-earbuds::before{content:"\f30e"}.bi-easel-fill::before{content:"\f30f"}.bi-easel::before{content:"\f310"}.bi-egg-fill::before{content:"\f311"}.bi-egg-fried::before{content:"\f312"}.bi-egg::before{content:"\f313"}.bi-eject-fill::before{content:"\f314"}.bi-eject::before{content:"\f315"}.bi-emoji-angry-fill::before{content:"\f316"}.bi-emoji-angry::before{content:"\f317"}.bi-emoji-dizzy-fill::before{content:"\f318"}.bi-emoji-dizzy::before{content:"\f319"}.bi-emoji-expressionless-fill::before{content:"\f31a"}.bi-emoji-expressionless::before{content:"\f31b"}.bi-emoji-frown-fill::before{content:"\f31c"}.bi-emoji-frown::before{content:"\f31d"}.bi-emoji-heart-eyes-fill::before{content:"\f31e"}.bi-emoji-heart-eyes::before{content:"\f31f"}.bi-emoji-laughing-fill::before{content:"\f320"}.bi-emoji-laughing::before{content:"\f321"}.bi-emoji-neutral-fill::before{content:"\f322"}.bi-emoji-neutral::before{content:"\f323"}.bi-emoji-smile-fill::before{content:"\f324"}.bi-emoji-smile-upside-down-fill::before{content:"\f325"}.bi-emoji-smile-upside-down::before{content:"\f326"}.bi-emoji-smile::before{content:"\f327"}.bi-emoji-sunglasses-fill::before{content:"\f328"}.bi-emoji-sunglasses::before{content:"\f329"}.bi-emoji-wink-fill::before{content:"\f32a"}.bi-emoji-wink::before{content:"\f32b"}.bi-envelope-fill::before{content:"\f32c"}.bi-envelope-open-fill::before{content:"\f32d"}.bi-envelope-open::before{content:"\f32e"}.bi-envelope::before{content:"\f32f"}.bi-eraser-fill::before{content:"\f330"}.bi-eraser::before{content:"\f331"}.bi-exclamation-circle-fill::before{content:"\f332"}.bi-exclamation-circle::before{content:"\f333"}.bi-exclamation-diamond-fill::before{content:"\f334"}.bi-exclamation-diamond::before{content:"\f335"}.bi-exclamation-octagon-fill::before{content:"\f336"}.bi-exclamation-octagon::before{content:"\f337"}.bi-exclamation-square-fill::before{content:"\f338"}.bi-exclamation-square::before{content:"\f339"}.bi-exclamation-triangle-fill::before{content:"\f33a"}.bi-exclamation-triangle::before{content:"\f33b"}.bi-exclamation::before{content:"\f33c"}.bi-exclude::before{content:"\f33d"}.bi-eye-fill::before{content:"\f33e"}.bi-eye-slash-fill::before{content:"\f33f"}.bi-eye-slash::before{content:"\f340"}.bi-eye::before{content:"\f341"}.bi-eyedropper::before{content:"\f342"}.bi-eyeglasses::before{content:"\f343"}.bi-facebook::before{content:"\f344"}.bi-file-arrow-down-fill::before{content:"\f345"}.bi-file-arrow-down::before{content:"\f346"}.bi-file-arrow-up-fill::before{content:"\f347"}.bi-file-arrow-up::before{content:"\f348"}.bi-file-bar-graph-fill::before{content:"\f349"}.bi-file-bar-graph::before{content:"\f34a"}.bi-file-binary-fill::before{content:"\f34b"}.bi-file-binary::before{content:"\f34c"}.bi-file-break-fill::before{content:"\f34d"}.bi-file-break::before{content:"\f34e"}.bi-file-check-fill::before{content:"\f34f"}.bi-file-check::before{content:"\f350"}.bi-file-code-fill::before{content:"\f351"}.bi-file-code::before{content:"\f352"}.bi-file-diff-fill::before{content:"\f353"}.bi-file-diff::before{content:"\f354"}.bi-file-earmark-arrow-down-fill::before{content:"\f355"}.bi-file-earmark-arrow-down::before{content:"\f356"}.bi-file-earmark-arrow-up-fill::before{content:"\f357"}.bi-file-earmark-arrow-up::before{content:"\f358"}.bi-file-earmark-bar-graph-fill::before{content:"\f359"}.bi-file-earmark-bar-graph::before{content:"\f35a"}.bi-file-earmark-binary-fill::before{content:"\f35b"}.bi-file-earmark-binary::before{content:"\f35c"}.bi-file-earmark-break-fill::before{content:"\f35d"}.bi-file-earmark-break::before{content:"\f35e"}.bi-file-earmark-check-fill::before{content:"\f35f"}.bi-file-earmark-check::before{content:"\f360"}.bi-file-earmark-code-fill::before{content:"\f361"}.bi-file-earmark-code::before{content:"\f362"}.bi-file-earmark-diff-fill::before{content:"\f363"}.bi-file-earmark-diff::before{content:"\f364"}.bi-file-earmark-easel-fill::before{content:"\f365"}.bi-file-earmark-easel::before{content:"\f366"}.bi-file-earmark-excel-fill::before{content:"\f367"}.bi-file-earmark-excel::before{content:"\f368"}.bi-file-earmark-fill::before{content:"\f369"}.bi-file-earmark-font-fill::before{content:"\f36a"}.bi-file-earmark-font::before{content:"\f36b"}.bi-file-earmark-image-fill::before{content:"\f36c"}.bi-file-earmark-image::before{content:"\f36d"}.bi-file-earmark-lock-fill::before{content:"\f36e"}.bi-file-earmark-lock::before{content:"\f36f"}.bi-file-earmark-lock2-fill::before{content:"\f370"}.bi-file-earmark-lock2::before{content:"\f371"}.bi-file-earmark-medical-fill::before{content:"\f372"}.bi-file-earmark-medical::before{content:"\f373"}.bi-file-earmark-minus-fill::before{content:"\f374"}.bi-file-earmark-minus::before{content:"\f375"}.bi-file-earmark-music-fill::before{content:"\f376"}.bi-file-earmark-music::before{content:"\f377"}.bi-file-earmark-person-fill::before{content:"\f378"}.bi-file-earmark-person::before{content:"\f379"}.bi-file-earmark-play-fill::before{content:"\f37a"}.bi-file-earmark-play::before{content:"\f37b"}.bi-file-earmark-plus-fill::before{content:"\f37c"}.bi-file-earmark-plus::before{content:"\f37d"}.bi-file-earmark-post-fill::before{content:"\f37e"}.bi-file-earmark-post::before{content:"\f37f"}.bi-file-earmark-ppt-fill::before{content:"\f380"}.bi-file-earmark-ppt::before{content:"\f381"}.bi-file-earmark-richtext-fill::before{content:"\f382"}.bi-file-earmark-richtext::before{content:"\f383"}.bi-file-earmark-ruled-fill::before{content:"\f384"}.bi-file-earmark-ruled::before{content:"\f385"}.bi-file-earmark-slides-fill::before{content:"\f386"}.bi-file-earmark-slides::before{content:"\f387"}.bi-file-earmark-spreadsheet-fill::before{content:"\f388"}.bi-file-earmark-spreadsheet::before{content:"\f389"}.bi-file-earmark-text-fill::before{content:"\f38a"}.bi-file-earmark-text::before{content:"\f38b"}.bi-file-earmark-word-fill::before{content:"\f38c"}.bi-file-earmark-word::before{content:"\f38d"}.bi-file-earmark-x-fill::before{content:"\f38e"}.bi-file-earmark-x::before{content:"\f38f"}.bi-file-earmark-zip-fill::before{content:"\f390"}.bi-file-earmark-zip::before{content:"\f391"}.bi-file-earmark::before{content:"\f392"}.bi-file-easel-fill::before{content:"\f393"}.bi-file-easel::before{content:"\f394"}.bi-file-excel-fill::before{content:"\f395"}.bi-file-excel::before{content:"\f396"}.bi-file-fill::before{content:"\f397"}.bi-file-font-fill::before{content:"\f398"}.bi-file-font::before{content:"\f399"}.bi-file-image-fill::before{content:"\f39a"}.bi-file-image::before{content:"\f39b"}.bi-file-lock-fill::before{content:"\f39c"}.bi-file-lock::before{content:"\f39d"}.bi-file-lock2-fill::before{content:"\f39e"}.bi-file-lock2::before{content:"\f39f"}.bi-file-medical-fill::before{content:"\f3a0"}.bi-file-medical::before{content:"\f3a1"}.bi-file-minus-fill::before{content:"\f3a2"}.bi-file-minus::before{content:"\f3a3"}.bi-file-music-fill::before{content:"\f3a4"}.bi-file-music::before{content:"\f3a5"}.bi-file-person-fill::before{content:"\f3a6"}.bi-file-person::before{content:"\f3a7"}.bi-file-play-fill::before{content:"\f3a8"}.bi-file-play::before{content:"\f3a9"}.bi-file-plus-fill::before{content:"\f3aa"}.bi-file-plus::before{content:"\f3ab"}.bi-file-post-fill::before{content:"\f3ac"}.bi-file-post::before{content:"\f3ad"}.bi-file-ppt-fill::before{content:"\f3ae"}.bi-file-ppt::before{content:"\f3af"}.bi-file-richtext-fill::before{content:"\f3b0"}.bi-file-richtext::before{content:"\f3b1"}.bi-file-ruled-fill::before{content:"\f3b2"}.bi-file-ruled::before{content:"\f3b3"}.bi-file-slides-fill::before{content:"\f3b4"}.bi-file-slides::before{content:"\f3b5"}.bi-file-spreadsheet-fill::before{content:"\f3b6"}.bi-file-spreadsheet::before{content:"\f3b7"}.bi-file-text-fill::before{content:"\f3b8"}.bi-file-text::before{content:"\f3b9"}.bi-file-word-fill::before{content:"\f3ba"}.bi-file-word::before{content:"\f3bb"}.bi-file-x-fill::before{content:"\f3bc"}.bi-file-x::before{content:"\f3bd"}.bi-file-zip-fill::before{content:"\f3be"}.bi-file-zip::before{content:"\f3bf"}.bi-file::before{content:"\f3c0"}.bi-files-alt::before{content:"\f3c1"}.bi-files::before{content:"\f3c2"}.bi-film::before{content:"\f3c3"}.bi-filter-circle-fill::before{content:"\f3c4"}.bi-filter-circle::before{content:"\f3c5"}.bi-filter-left::before{content:"\f3c6"}.bi-filter-right::before{content:"\f3c7"}.bi-filter-square-fill::before{content:"\f3c8"}.bi-filter-square::before{content:"\f3c9"}.bi-filter::before{content:"\f3ca"}.bi-flag-fill::before{content:"\f3cb"}.bi-flag::before{content:"\f3cc"}.bi-flower1::before{content:"\f3cd"}.bi-flower2::before{content:"\f3ce"}.bi-flower3::before{content:"\f3cf"}.bi-folder-check::before{content:"\f3d0"}.bi-folder-fill::before{content:"\f3d1"}.bi-folder-minus::before{content:"\f3d2"}.bi-folder-plus::before{content:"\f3d3"}.bi-folder-symlink-fill::before{content:"\f3d4"}.bi-folder-symlink::before{content:"\f3d5"}.bi-folder-x::before{content:"\f3d6"}.bi-folder::before{content:"\f3d7"}.bi-folder2-open::before{content:"\f3d8"}.bi-folder2::before{content:"\f3d9"}.bi-fonts::before{content:"\f3da"}.bi-forward-fill::before{content:"\f3db"}.bi-forward::before{content:"\f3dc"}.bi-front::before{content:"\f3dd"}.bi-fullscreen-exit::before{content:"\f3de"}.bi-fullscreen::before{content:"\f3df"}.bi-funnel-fill::before{content:"\f3e0"}.bi-funnel::before{content:"\f3e1"}.bi-gear-fill::before{content:"\f3e2"}.bi-gear-wide-connected::before{content:"\f3e3"}.bi-gear-wide::before{content:"\f3e4"}.bi-gear::before{content:"\f3e5"}.bi-gem::before{content:"\f3e6"}.bi-geo-alt-fill::before{content:"\f3e7"}.bi-geo-alt::before{content:"\f3e8"}.bi-geo-fill::before{content:"\f3e9"}.bi-geo::before{content:"\f3ea"}.bi-gift-fill::before{content:"\f3eb"}.bi-gift::before{content:"\f3ec"}.bi-github::before{content:"\f3ed"}.bi-globe::before{content:"\f3ee"}.bi-globe2::before{content:"\f3ef"}.bi-google::before{content:"\f3f0"}.bi-graph-down::before{content:"\f3f1"}.bi-graph-up::before{content:"\f3f2"}.bi-grid-1x2-fill::before{content:"\f3f3"}.bi-grid-1x2::before{content:"\f3f4"}.bi-grid-3x2-gap-fill::before{content:"\f3f5"}.bi-grid-3x2-gap::before{content:"\f3f6"}.bi-grid-3x2::before{content:"\f3f7"}.bi-grid-3x3-gap-fill::before{content:"\f3f8"}.bi-grid-3x3-gap::before{content:"\f3f9"}.bi-grid-3x3::before{content:"\f3fa"}.bi-grid-fill::before{content:"\f3fb"}.bi-grid::before{content:"\f3fc"}.bi-grip-horizontal::before{content:"\f3fd"}.bi-grip-vertical::before{content:"\f3fe"}.bi-hammer::before{content:"\f3ff"}.bi-hand-index-fill::before{content:"\f400"}.bi-hand-index-thumb-fill::before{content:"\f401"}.bi-hand-index-thumb::before{content:"\f402"}.bi-hand-index::before{content:"\f403"}.bi-hand-thumbs-down-fill::before{content:"\f404"}.bi-hand-thumbs-down::before{content:"\f405"}.bi-hand-thumbs-up-fill::before{content:"\f406"}.bi-hand-thumbs-up::before{content:"\f407"}.bi-handbag-fill::before{content:"\f408"}.bi-handbag::before{content:"\f409"}.bi-hash::before{content:"\f40a"}.bi-hdd-fill::before{content:"\f40b"}.bi-hdd-network-fill::before{content:"\f40c"}.bi-hdd-network::before{content:"\f40d"}.bi-hdd-rack-fill::before{content:"\f40e"}.bi-hdd-rack::before{content:"\f40f"}.bi-hdd-stack-fill::before{content:"\f410"}.bi-hdd-stack::before{content:"\f411"}.bi-hdd::before{content:"\f412"}.bi-headphones::before{content:"\f413"}.bi-headset::before{content:"\f414"}.bi-heart-fill::before{content:"\f415"}.bi-heart-half::before{content:"\f416"}.bi-heart::before{content:"\f417"}.bi-heptagon-fill::before{content:"\f418"}.bi-heptagon-half::before{content:"\f419"}.bi-heptagon::before{content:"\f41a"}.bi-hexagon-fill::before{content:"\f41b"}.bi-hexagon-half::before{content:"\f41c"}.bi-hexagon::before{content:"\f41d"}.bi-hourglass-bottom::before{content:"\f41e"}.bi-hourglass-split::before{content:"\f41f"}.bi-hourglass-top::before{content:"\f420"}.bi-hourglass::before{content:"\f421"}.bi-house-door-fill::before{content:"\f422"}.bi-house-door::before{content:"\f423"}.bi-house-fill::before{content:"\f424"}.bi-house::before{content:"\f425"}.bi-hr::before{content:"\f426"}.bi-hurricane::before{content:"\f427"}.bi-image-alt::before{content:"\f428"}.bi-image-fill::before{content:"\f429"}.bi-image::before{content:"\f42a"}.bi-images::before{content:"\f42b"}.bi-inbox-fill::before{content:"\f42c"}.bi-inbox::before{content:"\f42d"}.bi-inboxes-fill::before{content:"\f42e"}.bi-inboxes::before{content:"\f42f"}.bi-info-circle-fill::before{content:"\f430"}.bi-info-circle::before{content:"\f431"}.bi-info-square-fill::before{content:"\f432"}.bi-info-square::before{content:"\f433"}.bi-info::before{content:"\f434"}.bi-input-cursor-text::before{content:"\f435"}.bi-input-cursor::before{content:"\f436"}.bi-instagram::before{content:"\f437"}.bi-intersect::before{content:"\f438"}.bi-journal-album::before{content:"\f439"}.bi-journal-arrow-down::before{content:"\f43a"}.bi-journal-arrow-up::before{content:"\f43b"}.bi-journal-bookmark-fill::before{content:"\f43c"}.bi-journal-bookmark::before{content:"\f43d"}.bi-journal-check::before{content:"\f43e"}.bi-journal-code::before{content:"\f43f"}.bi-journal-medical::before{content:"\f440"}.bi-journal-minus::before{content:"\f441"}.bi-journal-plus::before{content:"\f442"}.bi-journal-richtext::before{content:"\f443"}.bi-journal-text::before{content:"\f444"}.bi-journal-x::before{content:"\f445"}.bi-journal::before{content:"\f446"}.bi-journals::before{content:"\f447"}.bi-joystick::before{content:"\f448"}.bi-justify-left::before{content:"\f449"}.bi-justify-right::before{content:"\f44a"}.bi-justify::before{content:"\f44b"}.bi-kanban-fill::before{content:"\f44c"}.bi-kanban::before{content:"\f44d"}.bi-key-fill::before{content:"\f44e"}.bi-key::before{content:"\f44f"}.bi-keyboard-fill::before{content:"\f450"}.bi-keyboard::before{content:"\f451"}.bi-ladder::before{content:"\f452"}.bi-lamp-fill::before{content:"\f453"}.bi-lamp::before{content:"\f454"}.bi-laptop-fill::before{content:"\f455"}.bi-laptop::before{content:"\f456"}.bi-layer-backward::before{content:"\f457"}.bi-layer-forward::before{content:"\f458"}.bi-layers-fill::before{content:"\f459"}.bi-layers-half::before{content:"\f45a"}.bi-layers::before{content:"\f45b"}.bi-layout-sidebar-inset-reverse::before{content:"\f45c"}.bi-layout-sidebar-inset::before{content:"\f45d"}.bi-layout-sidebar-reverse::before{content:"\f45e"}.bi-layout-sidebar::before{content:"\f45f"}.bi-layout-split::before{content:"\f460"}.bi-layout-text-sidebar-reverse::before{content:"\f461"}.bi-layout-text-sidebar::before{content:"\f462"}.bi-layout-text-window-reverse::before{content:"\f463"}.bi-layout-text-window::before{content:"\f464"}.bi-layout-three-columns::before{content:"\f465"}.bi-layout-wtf::before{content:"\f466"}.bi-life-preserver::before{content:"\f467"}.bi-lightbulb-fill::before{content:"\f468"}.bi-lightbulb-off-fill::before{content:"\f469"}.bi-lightbulb-off::before{content:"\f46a"}.bi-lightbulb::before{content:"\f46b"}.bi-lightning-charge-fill::before{content:"\f46c"}.bi-lightning-charge::before{content:"\f46d"}.bi-lightning-fill::before{content:"\f46e"}.bi-lightning::before{content:"\f46f"}.bi-link-45deg::before{content:"\f470"}.bi-link::before{content:"\f471"}.bi-linkedin::before{content:"\f472"}.bi-list-check::before{content:"\f473"}.bi-list-nested::before{content:"\f474"}.bi-list-ol::before{content:"\f475"}.bi-list-stars::before{content:"\f476"}.bi-list-task::before{content:"\f477"}.bi-list-ul::before{content:"\f478"}.bi-list::before{content:"\f479"}.bi-lock-fill::before{content:"\f47a"}.bi-lock::before{content:"\f47b"}.bi-mailbox::before{content:"\f47c"}.bi-mailbox2::before{content:"\f47d"}.bi-map-fill::before{content:"\f47e"}.bi-map::before{content:"\f47f"}.bi-markdown-fill::before{content:"\f480"}.bi-markdown::before{content:"\f481"}.bi-mask::before{content:"\f482"}.bi-megaphone-fill::before{content:"\f483"}.bi-megaphone::before{content:"\f484"}.bi-menu-app-fill::before{content:"\f485"}.bi-menu-app::before{content:"\f486"}.bi-menu-button-fill::before{content:"\f487"}.bi-menu-button-wide-fill::before{content:"\f488"}.bi-menu-button-wide::before{content:"\f489"}.bi-menu-button::before{content:"\f48a"}.bi-menu-down::before{content:"\f48b"}.bi-menu-up::before{content:"\f48c"}.bi-mic-fill::before{content:"\f48d"}.bi-mic-mute-fill::before{content:"\f48e"}.bi-mic-mute::before{content:"\f48f"}.bi-mic::before{content:"\f490"}.bi-minecart-loaded::before{content:"\f491"}.bi-minecart::before{content:"\f492"}.bi-moisture::before{content:"\f493"}.bi-moon-fill::before{content:"\f494"}.bi-moon-stars-fill::before{content:"\f495"}.bi-moon-stars::before{content:"\f496"}.bi-moon::before{content:"\f497"}.bi-mouse-fill::before{content:"\f498"}.bi-mouse::before{content:"\f499"}.bi-mouse2-fill::before{content:"\f49a"}.bi-mouse2::before{content:"\f49b"}.bi-mouse3-fill::before{content:"\f49c"}.bi-mouse3::before{content:"\f49d"}.bi-music-note-beamed::before{content:"\f49e"}.bi-music-note-list::before{content:"\f49f"}.bi-music-note::before{content:"\f4a0"}.bi-music-player-fill::before{content:"\f4a1"}.bi-music-player::before{content:"\f4a2"}.bi-newspaper::before{content:"\f4a3"}.bi-node-minus-fill::before{content:"\f4a4"}.bi-node-minus::before{content:"\f4a5"}.bi-node-plus-fill::before{content:"\f4a6"}.bi-node-plus::before{content:"\f4a7"}.bi-nut-fill::before{content:"\f4a8"}.bi-nut::before{content:"\f4a9"}.bi-octagon-fill::before{content:"\f4aa"}.bi-octagon-half::before{content:"\f4ab"}.bi-octagon::before{content:"\f4ac"}.bi-option::before{content:"\f4ad"}.bi-outlet::before{content:"\f4ae"}.bi-paint-bucket::before{content:"\f4af"}.bi-palette-fill::before{content:"\f4b0"}.bi-palette::before{content:"\f4b1"}.bi-palette2::before{content:"\f4b2"}.bi-paperclip::before{content:"\f4b3"}.bi-paragraph::before{content:"\f4b4"}.bi-patch-check-fill::before{content:"\f4b5"}.bi-patch-check::before{content:"\f4b6"}.bi-patch-exclamation-fill::before{content:"\f4b7"}.bi-patch-exclamation::before{content:"\f4b8"}.bi-patch-minus-fill::before{content:"\f4b9"}.bi-patch-minus::before{content:"\f4ba"}.bi-patch-plus-fill::before{content:"\f4bb"}.bi-patch-plus::before{content:"\f4bc"}.bi-patch-question-fill::before{content:"\f4bd"}.bi-patch-question::before{content:"\f4be"}.bi-pause-btn-fill::before{content:"\f4bf"}.bi-pause-btn::before{content:"\f4c0"}.bi-pause-circle-fill::before{content:"\f4c1"}.bi-pause-circle::before{content:"\f4c2"}.bi-pause-fill::before{content:"\f4c3"}.bi-pause::before{content:"\f4c4"}.bi-peace-fill::before{content:"\f4c5"}.bi-peace::before{content:"\f4c6"}.bi-pen-fill::before{content:"\f4c7"}.bi-pen::before{content:"\f4c8"}.bi-pencil-fill::before{content:"\f4c9"}.bi-pencil-square::before{content:"\f4ca"}.bi-pencil::before{content:"\f4cb"}.bi-pentagon-fill::before{content:"\f4cc"}.bi-pentagon-half::before{content:"\f4cd"}.bi-pentagon::before{content:"\f4ce"}.bi-people-fill::before{content:"\f4cf"}.bi-people::before{content:"\f4d0"}.bi-percent::before{content:"\f4d1"}.bi-person-badge-fill::before{content:"\f4d2"}.bi-person-badge::before{content:"\f4d3"}.bi-person-bounding-box::before{content:"\f4d4"}.bi-person-check-fill::before{content:"\f4d5"}.bi-person-check::before{content:"\f4d6"}.bi-person-circle::before{content:"\f4d7"}.bi-person-dash-fill::before{content:"\f4d8"}.bi-person-dash::before{content:"\f4d9"}.bi-person-fill::before{content:"\f4da"}.bi-person-lines-fill::before{content:"\f4db"}.bi-person-plus-fill::before{content:"\f4dc"}.bi-person-plus::before{content:"\f4dd"}.bi-person-square::before{content:"\f4de"}.bi-person-x-fill::before{content:"\f4df"}.bi-person-x::before{content:"\f4e0"}.bi-person::before{content:"\f4e1"}.bi-phone-fill::before{content:"\f4e2"}.bi-phone-landscape-fill::before{content:"\f4e3"}.bi-phone-landscape::before{content:"\f4e4"}.bi-phone-vibrate-fill::before{content:"\f4e5"}.bi-phone-vibrate::before{content:"\f4e6"}.bi-phone::before{content:"\f4e7"}.bi-pie-chart-fill::before{content:"\f4e8"}.bi-pie-chart::before{content:"\f4e9"}.bi-pin-angle-fill::before{content:"\f4ea"}.bi-pin-angle::before{content:"\f4eb"}.bi-pin-fill::before{content:"\f4ec"}.bi-pin::before{content:"\f4ed"}.bi-pip-fill::before{content:"\f4ee"}.bi-pip::before{content:"\f4ef"}.bi-play-btn-fill::before{content:"\f4f0"}.bi-play-btn::before{content:"\f4f1"}.bi-play-circle-fill::before{content:"\f4f2"}.bi-play-circle::before{content:"\f4f3"}.bi-play-fill::before{content:"\f4f4"}.bi-play::before{content:"\f4f5"}.bi-plug-fill::before{content:"\f4f6"}.bi-plug::before{content:"\f4f7"}.bi-plus-circle-dotted::before{content:"\f4f8"}.bi-plus-circle-fill::before{content:"\f4f9"}.bi-plus-circle::before{content:"\f4fa"}.bi-plus-square-dotted::before{content:"\f4fb"}.bi-plus-square-fill::before{content:"\f4fc"}.bi-plus-square::before{content:"\f4fd"}.bi-plus::before{content:"\f4fe"}.bi-power::before{content:"\f4ff"}.bi-printer-fill::before{content:"\f500"}.bi-printer::before{content:"\f501"}.bi-puzzle-fill::before{content:"\f502"}.bi-puzzle::before{content:"\f503"}.bi-question-circle-fill::before{content:"\f504"}.bi-question-circle::before{content:"\f505"}.bi-question-diamond-fill::before{content:"\f506"}.bi-question-diamond::before{content:"\f507"}.bi-question-octagon-fill::before{content:"\f508"}.bi-question-octagon::before{content:"\f509"}.bi-question-square-fill::before{content:"\f50a"}.bi-question-square::before{content:"\f50b"}.bi-question::before{content:"\f50c"}.bi-rainbow::before{content:"\f50d"}.bi-receipt-cutoff::before{content:"\f50e"}.bi-receipt::before{content:"\f50f"}.bi-reception-0::before{content:"\f510"}.bi-reception-1::before{content:"\f511"}.bi-reception-2::before{content:"\f512"}.bi-reception-3::before{content:"\f513"}.bi-reception-4::before{content:"\f514"}.bi-record-btn-fill::before{content:"\f515"}.bi-record-btn::before{content:"\f516"}.bi-record-circle-fill::before{content:"\f517"}.bi-record-circle::before{content:"\f518"}.bi-record-fill::before{content:"\f519"}.bi-record::before{content:"\f51a"}.bi-record2-fill::before{content:"\f51b"}.bi-record2::before{content:"\f51c"}.bi-reply-all-fill::before{content:"\f51d"}.bi-reply-all::before{content:"\f51e"}.bi-reply-fill::before{content:"\f51f"}.bi-reply::before{content:"\f520"}.bi-rss-fill::before{content:"\f521"}.bi-rss::before{content:"\f522"}.bi-rulers::before{content:"\f523"}.bi-save-fill::before{content:"\f524"}.bi-save::before{content:"\f525"}.bi-save2-fill::before{content:"\f526"}.bi-save2::before{content:"\f527"}.bi-scissors::before{content:"\f528"}.bi-screwdriver::before{content:"\f529"}.bi-search::before{content:"\f52a"}.bi-segmented-nav::before{content:"\f52b"}.bi-server::before{content:"\f52c"}.bi-share-fill::before{content:"\f52d"}.bi-share::before{content:"\f52e"}.bi-shield-check::before{content:"\f52f"}.bi-shield-exclamation::before{content:"\f530"}.bi-shield-fill-check::before{content:"\f531"}.bi-shield-fill-exclamation::before{content:"\f532"}.bi-shield-fill-minus::before{content:"\f533"}.bi-shield-fill-plus::before{content:"\f534"}.bi-shield-fill-x::before{content:"\f535"}.bi-shield-fill::before{content:"\f536"}.bi-shield-lock-fill::before{content:"\f537"}.bi-shield-lock::before{content:"\f538"}.bi-shield-minus::before{content:"\f539"}.bi-shield-plus::before{content:"\f53a"}.bi-shield-shaded::before{content:"\f53b"}.bi-shield-slash-fill::before{content:"\f53c"}.bi-shield-slash::before{content:"\f53d"}.bi-shield-x::before{content:"\f53e"}.bi-shield::before{content:"\f53f"}.bi-shift-fill::before{content:"\f540"}.bi-shift::before{content:"\f541"}.bi-shop-window::before{content:"\f542"}.bi-shop::before{content:"\f543"}.bi-shuffle::before{content:"\f544"}.bi-signpost-2-fill::before{content:"\f545"}.bi-signpost-2::before{content:"\f546"}.bi-signpost-fill::before{content:"\f547"}.bi-signpost-split-fill::before{content:"\f548"}.bi-signpost-split::before{content:"\f549"}.bi-signpost::before{content:"\f54a"}.bi-sim-fill::before{content:"\f54b"}.bi-sim::before{content:"\f54c"}.bi-skip-backward-btn-fill::before{content:"\f54d"}.bi-skip-backward-btn::before{content:"\f54e"}.bi-skip-backward-circle-fill::before{content:"\f54f"}.bi-skip-backward-circle::before{content:"\f550"}.bi-skip-backward-fill::before{content:"\f551"}.bi-skip-backward::before{content:"\f552"}.bi-skip-end-btn-fill::before{content:"\f553"}.bi-skip-end-btn::before{content:"\f554"}.bi-skip-end-circle-fill::before{content:"\f555"}.bi-skip-end-circle::before{content:"\f556"}.bi-skip-end-fill::before{content:"\f557"}.bi-skip-end::before{content:"\f558"}.bi-skip-forward-btn-fill::before{content:"\f559"}.bi-skip-forward-btn::before{content:"\f55a"}.bi-skip-forward-circle-fill::before{content:"\f55b"}.bi-skip-forward-circle::before{content:"\f55c"}.bi-skip-forward-fill::before{content:"\f55d"}.bi-skip-forward::before{content:"\f55e"}.bi-skip-start-btn-fill::before{content:"\f55f"}.bi-skip-start-btn::before{content:"\f560"}.bi-skip-start-circle-fill::before{content:"\f561"}.bi-skip-start-circle::before{content:"\f562"}.bi-skip-start-fill::before{content:"\f563"}.bi-skip-start::before{content:"\f564"}.bi-slack::before{content:"\f565"}.bi-slash-circle-fill::before{content:"\f566"}.bi-slash-circle::before{content:"\f567"}.bi-slash-square-fill::before{content:"\f568"}.bi-slash-square::before{content:"\f569"}.bi-slash::before{content:"\f56a"}.bi-sliders::before{content:"\f56b"}.bi-smartwatch::before{content:"\f56c"}.bi-snow::before{content:"\f56d"}.bi-snow2::before{content:"\f56e"}.bi-snow3::before{content:"\f56f"}.bi-sort-alpha-down-alt::before{content:"\f570"}.bi-sort-alpha-down::before{content:"\f571"}.bi-sort-alpha-up-alt::before{content:"\f572"}.bi-sort-alpha-up::before{content:"\f573"}.bi-sort-down-alt::before{content:"\f574"}.bi-sort-down::before{content:"\f575"}.bi-sort-numeric-down-alt::before{content:"\f576"}.bi-sort-numeric-down::before{content:"\f577"}.bi-sort-numeric-up-alt::before{content:"\f578"}.bi-sort-numeric-up::before{content:"\f579"}.bi-sort-up-alt::before{content:"\f57a"}.bi-sort-up::before{content:"\f57b"}.bi-soundwave::before{content:"\f57c"}.bi-speaker-fill::before{content:"\f57d"}.bi-speaker::before{content:"\f57e"}.bi-speedometer::before{content:"\f57f"}.bi-speedometer2::before{content:"\f580"}.bi-spellcheck::before{content:"\f581"}.bi-square-fill::before{content:"\f582"}.bi-square-half::before{content:"\f583"}.bi-square::before{content:"\f584"}.bi-stack::before{content:"\f585"}.bi-star-fill::before{content:"\f586"}.bi-star-half::before{content:"\f587"}.bi-star::before{content:"\f588"}.bi-stars::before{content:"\f589"}.bi-stickies-fill::before{content:"\f58a"}.bi-stickies::before{content:"\f58b"}.bi-sticky-fill::before{content:"\f58c"}.bi-sticky::before{content:"\f58d"}.bi-stop-btn-fill::before{content:"\f58e"}.bi-stop-btn::before{content:"\f58f"}.bi-stop-circle-fill::before{content:"\f590"}.bi-stop-circle::before{content:"\f591"}.bi-stop-fill::before{content:"\f592"}.bi-stop::before{content:"\f593"}.bi-stoplights-fill::before{content:"\f594"}.bi-stoplights::before{content:"\f595"}.bi-stopwatch-fill::before{content:"\f596"}.bi-stopwatch::before{content:"\f597"}.bi-subtract::before{content:"\f598"}.bi-suit-club-fill::before{content:"\f599"}.bi-suit-club::before{content:"\f59a"}.bi-suit-diamond-fill::before{content:"\f59b"}.bi-suit-diamond::before{content:"\f59c"}.bi-suit-heart-fill::before{content:"\f59d"}.bi-suit-heart::before{content:"\f59e"}.bi-suit-spade-fill::before{content:"\f59f"}.bi-suit-spade::before{content:"\f5a0"}.bi-sun-fill::before{content:"\f5a1"}.bi-sun::before{content:"\f5a2"}.bi-sunglasses::before{content:"\f5a3"}.bi-sunrise-fill::before{content:"\f5a4"}.bi-sunrise::before{content:"\f5a5"}.bi-sunset-fill::before{content:"\f5a6"}.bi-sunset::before{content:"\f5a7"}.bi-symmetry-horizontal::before{content:"\f5a8"}.bi-symmetry-vertical::before{content:"\f5a9"}.bi-table::before{content:"\f5aa"}.bi-tablet-fill::before{content:"\f5ab"}.bi-tablet-landscape-fill::before{content:"\f5ac"}.bi-tablet-landscape::before{content:"\f5ad"}.bi-tablet::before{content:"\f5ae"}.bi-tag-fill::before{content:"\f5af"}.bi-tag::before{content:"\f5b0"}.bi-tags-fill::before{content:"\f5b1"}.bi-tags::before{content:"\f5b2"}.bi-telegram::before{content:"\f5b3"}.bi-telephone-fill::before{content:"\f5b4"}.bi-telephone-forward-fill::before{content:"\f5b5"}.bi-telephone-forward::before{content:"\f5b6"}.bi-telephone-inbound-fill::before{content:"\f5b7"}.bi-telephone-inbound::before{content:"\f5b8"}.bi-telephone-minus-fill::before{content:"\f5b9"}.bi-telephone-minus::before{content:"\f5ba"}.bi-telephone-outbound-fill::before{content:"\f5bb"}.bi-telephone-outbound::before{content:"\f5bc"}.bi-telephone-plus-fill::before{content:"\f5bd"}.bi-telephone-plus::before{content:"\f5be"}.bi-telephone-x-fill::before{content:"\f5bf"}.bi-telephone-x::before{content:"\f5c0"}.bi-telephone::before{content:"\f5c1"}.bi-terminal-fill::before{content:"\f5c2"}.bi-terminal::before{content:"\f5c3"}.bi-text-center::before{content:"\f5c4"}.bi-text-indent-left::before{content:"\f5c5"}.bi-text-indent-right::before{content:"\f5c6"}.bi-text-left::before{content:"\f5c7"}.bi-text-paragraph::before{content:"\f5c8"}.bi-text-right::before{content:"\f5c9"}.bi-textarea-resize::before{content:"\f5ca"}.bi-textarea-t::before{content:"\f5cb"}.bi-textarea::before{content:"\f5cc"}.bi-thermometer-half::before{content:"\f5cd"}.bi-thermometer-high::before{content:"\f5ce"}.bi-thermometer-low::before{content:"\f5cf"}.bi-thermometer-snow::before{content:"\f5d0"}.bi-thermometer-sun::before{content:"\f5d1"}.bi-thermometer::before{content:"\f5d2"}.bi-three-dots-vertical::before{content:"\f5d3"}.bi-three-dots::before{content:"\f5d4"}.bi-toggle-off::before{content:"\f5d5"}.bi-toggle-on::before{content:"\f5d6"}.bi-toggle2-off::before{content:"\f5d7"}.bi-toggle2-on::before{content:"\f5d8"}.bi-toggles::before{content:"\f5d9"}.bi-toggles2::before{content:"\f5da"}.bi-tools::before{content:"\f5db"}.bi-tornado::before{content:"\f5dc"}.bi-trash-fill::before{content:"\f5dd"}.bi-trash::before{content:"\f5de"}.bi-trash2-fill::before{content:"\f5df"}.bi-trash2::before{content:"\f5e0"}.bi-tree-fill::before{content:"\f5e1"}.bi-tree::before{content:"\f5e2"}.bi-triangle-fill::before{content:"\f5e3"}.bi-triangle-half::before{content:"\f5e4"}.bi-triangle::before{content:"\f5e5"}.bi-trophy-fill::before{content:"\f5e6"}.bi-trophy::before{content:"\f5e7"}.bi-tropical-storm::before{content:"\f5e8"}.bi-truck-flatbed::before{content:"\f5e9"}.bi-truck::before{content:"\f5ea"}.bi-tsunami::before{content:"\f5eb"}.bi-tv-fill::before{content:"\f5ec"}.bi-tv::before{content:"\f5ed"}.bi-twitch::before{content:"\f5ee"}.bi-twitter::before{content:"\f5ef"}.bi-type-bold::before{content:"\f5f0"}.bi-type-h1::before{content:"\f5f1"}.bi-type-h2::before{content:"\f5f2"}.bi-type-h3::before{content:"\f5f3"}.bi-type-italic::before{content:"\f5f4"}.bi-type-strikethrough::before{content:"\f5f5"}.bi-type-underline::before{content:"\f5f6"}.bi-type::before{content:"\f5f7"}.bi-ui-checks-grid::before{content:"\f5f8"}.bi-ui-checks::before{content:"\f5f9"}.bi-ui-radios-grid::before{content:"\f5fa"}.bi-ui-radios::before{content:"\f5fb"}.bi-umbrella-fill::before{content:"\f5fc"}.bi-umbrella::before{content:"\f5fd"}.bi-union::before{content:"\f5fe"}.bi-unlock-fill::before{content:"\f5ff"}.bi-unlock::before{content:"\f600"}.bi-upc-scan::before{content:"\f601"}.bi-upc::before{content:"\f602"}.bi-upload::before{content:"\f603"}.bi-vector-pen::before{content:"\f604"}.bi-view-list::before{content:"\f605"}.bi-view-stacked::before{content:"\f606"}.bi-vinyl-fill::before{content:"\f607"}.bi-vinyl::before{content:"\f608"}.bi-voicemail::before{content:"\f609"}.bi-volume-down-fill::before{content:"\f60a"}.bi-volume-down::before{content:"\f60b"}.bi-volume-mute-fill::before{content:"\f60c"}.bi-volume-mute::before{content:"\f60d"}.bi-volume-off-fill::before{content:"\f60e"}.bi-volume-off::before{content:"\f60f"}.bi-volume-up-fill::before{content:"\f610"}.bi-volume-up::before{content:"\f611"}.bi-vr::before{content:"\f612"}.bi-wallet-fill::before{content:"\f613"}.bi-wallet::before{content:"\f614"}.bi-wallet2::before{content:"\f615"}.bi-watch::before{content:"\f616"}.bi-water::before{content:"\f617"}.bi-whatsapp::before{content:"\f618"}.bi-wifi-1::before{content:"\f619"}.bi-wifi-2::before{content:"\f61a"}.bi-wifi-off::before{content:"\f61b"}.bi-wifi::before{content:"\f61c"}.bi-wind::before{content:"\f61d"}.bi-window-dock::before{content:"\f61e"}.bi-window-sidebar::before{content:"\f61f"}.bi-window::before{content:"\f620"}.bi-wrench::before{content:"\f621"}.bi-x-circle-fill::before{content:"\f622"}.bi-x-circle::before{content:"\f623"}.bi-x-diamond-fill::before{content:"\f624"}.bi-x-diamond::before{content:"\f625"}.bi-x-octagon-fill::before{content:"\f626"}.bi-x-octagon::before{content:"\f627"}.bi-x-square-fill::before{content:"\f628"}.bi-x-square::before{content:"\f629"}.bi-x::before{content:"\f62a"}.bi-youtube::before{content:"\f62b"}.bi-zoom-in::before{content:"\f62c"}.bi-zoom-out::before{content:"\f62d"}.bi-bank::before{content:"\f62e"}.bi-bank2::before{content:"\f62f"}.bi-bell-slash-fill::before{content:"\f630"}.bi-bell-slash::before{content:"\f631"}.bi-cash-coin::before{content:"\f632"}.bi-check-lg::before{content:"\f633"}.bi-coin::before{content:"\f634"}.bi-currency-bitcoin::before{content:"\f635"}.bi-currency-dollar::before{content:"\f636"}.bi-currency-euro::before{content:"\f637"}.bi-currency-exchange::before{content:"\f638"}.bi-currency-pound::before{content:"\f639"}.bi-currency-yen::before{content:"\f63a"}.bi-dash-lg::before{content:"\f63b"}.bi-exclamation-lg::before{content:"\f63c"}.bi-file-earmark-pdf-fill::before{content:"\f63d"}.bi-file-earmark-pdf::before{content:"\f63e"}.bi-file-pdf-fill::before{content:"\f63f"}.bi-file-pdf::before{content:"\f640"}.bi-gender-ambiguous::before{content:"\f641"}.bi-gender-female::before{content:"\f642"}.bi-gender-male::before{content:"\f643"}.bi-gender-trans::before{content:"\f644"}.bi-headset-vr::before{content:"\f645"}.bi-info-lg::before{content:"\f646"}.bi-mastodon::before{content:"\f647"}.bi-messenger::before{content:"\f648"}.bi-piggy-bank-fill::before{content:"\f649"}.bi-piggy-bank::before{content:"\f64a"}.bi-pin-map-fill::before{content:"\f64b"}.bi-pin-map::before{content:"\f64c"}.bi-plus-lg::before{content:"\f64d"}.bi-question-lg::before{content:"\f64e"}.bi-recycle::before{content:"\f64f"}.bi-reddit::before{content:"\f650"}.bi-safe-fill::before{content:"\f651"}.bi-safe2-fill::before{content:"\f652"}.bi-safe2::before{content:"\f653"}.bi-sd-card-fill::before{content:"\f654"}.bi-sd-card::before{content:"\f655"}.bi-skype::before{content:"\f656"}.bi-slash-lg::before{content:"\f657"}.bi-translate::before{content:"\f658"}.bi-x-lg::before{content:"\f659"}.bi-safe::before{content:"\f65a"}.bi-apple::before{content:"\f65b"}.bi-microsoft::before{content:"\f65d"}.bi-windows::before{content:"\f65e"}.bi-behance::before{content:"\f65c"}.bi-dribbble::before{content:"\f65f"}.bi-line::before{content:"\f660"}.bi-medium::before{content:"\f661"}.bi-paypal::before{content:"\f662"}.bi-pinterest::before{content:"\f663"}.bi-signal::before{content:"\f664"}.bi-snapchat::before{content:"\f665"}.bi-spotify::before{content:"\f666"}.bi-stack-overflow::before{content:"\f667"}.bi-strava::before{content:"\f668"}.bi-wordpress::before{content:"\f669"}.bi-vimeo::before{content:"\f66a"}.bi-activity::before{content:"\f66b"}.bi-easel2-fill::before{content:"\f66c"}.bi-easel2::before{content:"\f66d"}.bi-easel3-fill::before{content:"\f66e"}.bi-easel3::before{content:"\f66f"}.bi-fan::before{content:"\f670"}.bi-fingerprint::before{content:"\f671"}.bi-graph-down-arrow::before{content:"\f672"}.bi-graph-up-arrow::before{content:"\f673"}.bi-hypnotize::before{content:"\f674"}.bi-magic::before{content:"\f675"}.bi-person-rolodex::before{content:"\f676"}.bi-person-video::before{content:"\f677"}.bi-person-video2::before{content:"\f678"}.bi-person-video3::before{content:"\f679"}.bi-person-workspace::before{content:"\f67a"}.bi-radioactive::before{content:"\f67b"}.bi-webcam-fill::before{content:"\f67c"}.bi-webcam::before{content:"\f67d"}.bi-yin-yang::before{content:"\f67e"}.bi-bandaid-fill::before{content:"\f680"}.bi-bandaid::before{content:"\f681"}.bi-bluetooth::before{content:"\f682"}.bi-body-text::before{content:"\f683"}.bi-boombox::before{content:"\f684"}.bi-boxes::before{content:"\f685"}.bi-dpad-fill::before{content:"\f686"}.bi-dpad::before{content:"\f687"}.bi-ear-fill::before{content:"\f688"}.bi-ear::before{content:"\f689"}.bi-envelope-check-fill::before{content:"\f68b"}.bi-envelope-check::before{content:"\f68c"}.bi-envelope-dash-fill::before{content:"\f68e"}.bi-envelope-dash::before{content:"\f68f"}.bi-envelope-exclamation-fill::before{content:"\f691"}.bi-envelope-exclamation::before{content:"\f692"}.bi-envelope-plus-fill::before{content:"\f693"}.bi-envelope-plus::before{content:"\f694"}.bi-envelope-slash-fill::before{content:"\f696"}.bi-envelope-slash::before{content:"\f697"}.bi-envelope-x-fill::before{content:"\f699"}.bi-envelope-x::before{content:"\f69a"}.bi-explicit-fill::before{content:"\f69b"}.bi-explicit::before{content:"\f69c"}.bi-git::before{content:"\f69d"}.bi-infinity::before{content:"\f69e"}.bi-list-columns-reverse::before{content:"\f69f"}.bi-list-columns::before{content:"\f6a0"}.bi-meta::before{content:"\f6a1"}.bi-nintendo-switch::before{content:"\f6a4"}.bi-pc-display-horizontal::before{content:"\f6a5"}.bi-pc-display::before{content:"\f6a6"}.bi-pc-horizontal::before{content:"\f6a7"}.bi-pc::before{content:"\f6a8"}.bi-playstation::before{content:"\f6a9"}.bi-plus-slash-minus::before{content:"\f6aa"}.bi-projector-fill::before{content:"\f6ab"}.bi-projector::before{content:"\f6ac"}.bi-qr-code-scan::before{content:"\f6ad"}.bi-qr-code::before{content:"\f6ae"}.bi-quora::before{content:"\f6af"}.bi-quote::before{content:"\f6b0"}.bi-robot::before{content:"\f6b1"}.bi-send-check-fill::before{content:"\f6b2"}.bi-send-check::before{content:"\f6b3"}.bi-send-dash-fill::before{content:"\f6b4"}.bi-send-dash::before{content:"\f6b5"}.bi-send-exclamation-fill::before{content:"\f6b7"}.bi-send-exclamation::before{content:"\f6b8"}.bi-send-fill::before{content:"\f6b9"}.bi-send-plus-fill::before{content:"\f6ba"}.bi-send-plus::before{content:"\f6bb"}.bi-send-slash-fill::before{content:"\f6bc"}.bi-send-slash::before{content:"\f6bd"}.bi-send-x-fill::before{content:"\f6be"}.bi-send-x::before{content:"\f6bf"}.bi-send::before{content:"\f6c0"}.bi-steam::before{content:"\f6c1"}.bi-terminal-dash::before{content:"\f6c3"}.bi-terminal-plus::before{content:"\f6c4"}.bi-terminal-split::before{content:"\f6c5"}.bi-ticket-detailed-fill::before{content:"\f6c6"}.bi-ticket-detailed::before{content:"\f6c7"}.bi-ticket-fill::before{content:"\f6c8"}.bi-ticket-perforated-fill::before{content:"\f6c9"}.bi-ticket-perforated::before{content:"\f6ca"}.bi-ticket::before{content:"\f6cb"}.bi-tiktok::before{content:"\f6cc"}.bi-window-dash::before{content:"\f6cd"}.bi-window-desktop::before{content:"\f6ce"}.bi-window-fullscreen::before{content:"\f6cf"}.bi-window-plus::before{content:"\f6d0"}.bi-window-split::before{content:"\f6d1"}.bi-window-stack::before{content:"\f6d2"}.bi-window-x::before{content:"\f6d3"}.bi-xbox::before{content:"\f6d4"}.bi-ethernet::before{content:"\f6d5"}.bi-hdmi-fill::before{content:"\f6d6"}.bi-hdmi::before{content:"\f6d7"}.bi-usb-c-fill::before{content:"\f6d8"}.bi-usb-c::before{content:"\f6d9"}.bi-usb-fill::before{content:"\f6da"}.bi-usb-plug-fill::before{content:"\f6db"}.bi-usb-plug::before{content:"\f6dc"}.bi-usb-symbol::before{content:"\f6dd"}.bi-usb::before{content:"\f6de"}.bi-boombox-fill::before{content:"\f6df"}.bi-displayport::before{content:"\f6e1"}.bi-gpu-card::before{content:"\f6e2"}.bi-memory::before{content:"\f6e3"}.bi-modem-fill::before{content:"\f6e4"}.bi-modem::before{content:"\f6e5"}.bi-motherboard-fill::before{content:"\f6e6"}.bi-motherboard::before{content:"\f6e7"}.bi-optical-audio-fill::before{content:"\f6e8"}.bi-optical-audio::before{content:"\f6e9"}.bi-pci-card::before{content:"\f6ea"}.bi-router-fill::before{content:"\f6eb"}.bi-router::before{content:"\f6ec"}.bi-thunderbolt-fill::before{content:"\f6ef"}.bi-thunderbolt::before{content:"\f6f0"}.bi-usb-drive-fill::before{content:"\f6f1"}.bi-usb-drive::before{content:"\f6f2"}.bi-usb-micro-fill::before{content:"\f6f3"}.bi-usb-micro::before{content:"\f6f4"}.bi-usb-mini-fill::before{content:"\f6f5"}.bi-usb-mini::before{content:"\f6f6"}.bi-cloud-haze2::before{content:"\f6f7"}.bi-device-hdd-fill::before{content:"\f6f8"}.bi-device-hdd::before{content:"\f6f9"}.bi-device-ssd-fill::before{content:"\f6fa"}.bi-device-ssd::before{content:"\f6fb"}.bi-displayport-fill::before{content:"\f6fc"}.bi-mortarboard-fill::before{content:"\f6fd"}.bi-mortarboard::before{content:"\f6fe"}.bi-terminal-x::before{content:"\f6ff"}.bi-arrow-through-heart-fill::before{content:"\f700"}.bi-arrow-through-heart::before{content:"\f701"}.bi-badge-sd-fill::before{content:"\f702"}.bi-badge-sd::before{content:"\f703"}.bi-bag-heart-fill::before{content:"\f704"}.bi-bag-heart::before{content:"\f705"}.bi-balloon-fill::before{content:"\f706"}.bi-balloon-heart-fill::before{content:"\f707"}.bi-balloon-heart::before{content:"\f708"}.bi-balloon::before{content:"\f709"}.bi-box2-fill::before{content:"\f70a"}.bi-box2-heart-fill::before{content:"\f70b"}.bi-box2-heart::before{content:"\f70c"}.bi-box2::before{content:"\f70d"}.bi-braces-asterisk::before{content:"\f70e"}.bi-calendar-heart-fill::before{content:"\f70f"}.bi-calendar-heart::before{content:"\f710"}.bi-calendar2-heart-fill::before{content:"\f711"}.bi-calendar2-heart::before{content:"\f712"}.bi-chat-heart-fill::before{content:"\f713"}.bi-chat-heart::before{content:"\f714"}.bi-chat-left-heart-fill::before{content:"\f715"}.bi-chat-left-heart::before{content:"\f716"}.bi-chat-right-heart-fill::before{content:"\f717"}.bi-chat-right-heart::before{content:"\f718"}.bi-chat-square-heart-fill::before{content:"\f719"}.bi-chat-square-heart::before{content:"\f71a"}.bi-clipboard-check-fill::before{content:"\f71b"}.bi-clipboard-data-fill::before{content:"\f71c"}.bi-clipboard-fill::before{content:"\f71d"}.bi-clipboard-heart-fill::before{content:"\f71e"}.bi-clipboard-heart::before{content:"\f71f"}.bi-clipboard-minus-fill::before{content:"\f720"}.bi-clipboard-plus-fill::before{content:"\f721"}.bi-clipboard-pulse::before{content:"\f722"}.bi-clipboard-x-fill::before{content:"\f723"}.bi-clipboard2-check-fill::before{content:"\f724"}.bi-clipboard2-check::before{content:"\f725"}.bi-clipboard2-data-fill::before{content:"\f726"}.bi-clipboard2-data::before{content:"\f727"}.bi-clipboard2-fill::before{content:"\f728"}.bi-clipboard2-heart-fill::before{content:"\f729"}.bi-clipboard2-heart::before{content:"\f72a"}.bi-clipboard2-minus-fill::before{content:"\f72b"}.bi-clipboard2-minus::before{content:"\f72c"}.bi-clipboard2-plus-fill::before{content:"\f72d"}.bi-clipboard2-plus::before{content:"\f72e"}.bi-clipboard2-pulse-fill::before{content:"\f72f"}.bi-clipboard2-pulse::before{content:"\f730"}.bi-clipboard2-x-fill::before{content:"\f731"}.bi-clipboard2-x::before{content:"\f732"}.bi-clipboard2::before{content:"\f733"}.bi-emoji-kiss-fill::before{content:"\f734"}.bi-emoji-kiss::before{content:"\f735"}.bi-envelope-heart-fill::before{content:"\f736"}.bi-envelope-heart::before{content:"\f737"}.bi-envelope-open-heart-fill::before{content:"\f738"}.bi-envelope-open-heart::before{content:"\f739"}.bi-envelope-paper-fill::before{content:"\f73a"}.bi-envelope-paper-heart-fill::before{content:"\f73b"}.bi-envelope-paper-heart::before{content:"\f73c"}.bi-envelope-paper::before{content:"\f73d"}.bi-filetype-aac::before{content:"\f73e"}.bi-filetype-ai::before{content:"\f73f"}.bi-filetype-bmp::before{content:"\f740"}.bi-filetype-cs::before{content:"\f741"}.bi-filetype-css::before{content:"\f742"}.bi-filetype-csv::before{content:"\f743"}.bi-filetype-doc::before{content:"\f744"}.bi-filetype-docx::before{content:"\f745"}.bi-filetype-exe::before{content:"\f746"}.bi-filetype-gif::before{content:"\f747"}.bi-filetype-heic::before{content:"\f748"}.bi-filetype-html::before{content:"\f749"}.bi-filetype-java::before{content:"\f74a"}.bi-filetype-jpg::before{content:"\f74b"}.bi-filetype-js::before{content:"\f74c"}.bi-filetype-jsx::before{content:"\f74d"}.bi-filetype-key::before{content:"\f74e"}.bi-filetype-m4p::before{content:"\f74f"}.bi-filetype-md::before{content:"\f750"}.bi-filetype-mdx::before{content:"\f751"}.bi-filetype-mov::before{content:"\f752"}.bi-filetype-mp3::before{content:"\f753"}.bi-filetype-mp4::before{content:"\f754"}.bi-filetype-otf::before{content:"\f755"}.bi-filetype-pdf::before{content:"\f756"}.bi-filetype-php::before{content:"\f757"}.bi-filetype-png::before{content:"\f758"}.bi-filetype-ppt::before{content:"\f75a"}.bi-filetype-psd::before{content:"\f75b"}.bi-filetype-py::before{content:"\f75c"}.bi-filetype-raw::before{content:"\f75d"}.bi-filetype-rb::before{content:"\f75e"}.bi-filetype-sass::before{content:"\f75f"}.bi-filetype-scss::before{content:"\f760"}.bi-filetype-sh::before{content:"\f761"}.bi-filetype-svg::before{content:"\f762"}.bi-filetype-tiff::before{content:"\f763"}.bi-filetype-tsx::before{content:"\f764"}.bi-filetype-ttf::before{content:"\f765"}.bi-filetype-txt::before{content:"\f766"}.bi-filetype-wav::before{content:"\f767"}.bi-filetype-woff::before{content:"\f768"}.bi-filetype-xls::before{content:"\f76a"}.bi-filetype-xml::before{content:"\f76b"}.bi-filetype-yml::before{content:"\f76c"}.bi-heart-arrow::before{content:"\f76d"}.bi-heart-pulse-fill::before{content:"\f76e"}.bi-heart-pulse::before{content:"\f76f"}.bi-heartbreak-fill::before{content:"\f770"}.bi-heartbreak::before{content:"\f771"}.bi-hearts::before{content:"\f772"}.bi-hospital-fill::before{content:"\f773"}.bi-hospital::before{content:"\f774"}.bi-house-heart-fill::before{content:"\f775"}.bi-house-heart::before{content:"\f776"}.bi-incognito::before{content:"\f777"}.bi-magnet-fill::before{content:"\f778"}.bi-magnet::before{content:"\f779"}.bi-person-heart::before{content:"\f77a"}.bi-person-hearts::before{content:"\f77b"}.bi-phone-flip::before{content:"\f77c"}.bi-plugin::before{content:"\f77d"}.bi-postage-fill::before{content:"\f77e"}.bi-postage-heart-fill::before{content:"\f77f"}.bi-postage-heart::before{content:"\f780"}.bi-postage::before{content:"\f781"}.bi-postcard-fill::before{content:"\f782"}.bi-postcard-heart-fill::before{content:"\f783"}.bi-postcard-heart::before{content:"\f784"}.bi-postcard::before{content:"\f785"}.bi-search-heart-fill::before{content:"\f786"}.bi-search-heart::before{content:"\f787"}.bi-sliders2-vertical::before{content:"\f788"}.bi-sliders2::before{content:"\f789"}.bi-trash3-fill::before{content:"\f78a"}.bi-trash3::before{content:"\f78b"}.bi-valentine::before{content:"\f78c"}.bi-valentine2::before{content:"\f78d"}.bi-wrench-adjustable-circle-fill::before{content:"\f78e"}.bi-wrench-adjustable-circle::before{content:"\f78f"}.bi-wrench-adjustable::before{content:"\f790"}.bi-filetype-json::before{content:"\f791"}.bi-filetype-pptx::before{content:"\f792"}.bi-filetype-xlsx::before{content:"\f793"}.bi-1-circle-fill::before{content:"\f796"}.bi-1-circle::before{content:"\f797"}.bi-1-square-fill::before{content:"\f798"}.bi-1-square::before{content:"\f799"}.bi-2-circle-fill::before{content:"\f79c"}.bi-2-circle::before{content:"\f79d"}.bi-2-square-fill::before{content:"\f79e"}.bi-2-square::before{content:"\f79f"}.bi-3-circle-fill::before{content:"\f7a2"}.bi-3-circle::before{content:"\f7a3"}.bi-3-square-fill::before{content:"\f7a4"}.bi-3-square::before{content:"\f7a5"}.bi-4-circle-fill::before{content:"\f7a8"}.bi-4-circle::before{content:"\f7a9"}.bi-4-square-fill::before{content:"\f7aa"}.bi-4-square::before{content:"\f7ab"}.bi-5-circle-fill::before{content:"\f7ae"}.bi-5-circle::before{content:"\f7af"}.bi-5-square-fill::before{content:"\f7b0"}.bi-5-square::before{content:"\f7b1"}.bi-6-circle-fill::before{content:"\f7b4"}.bi-6-circle::before{content:"\f7b5"}.bi-6-square-fill::before{content:"\f7b6"}.bi-6-square::before{content:"\f7b7"}.bi-7-circle-fill::before{content:"\f7ba"}.bi-7-circle::before{content:"\f7bb"}.bi-7-square-fill::before{content:"\f7bc"}.bi-7-square::before{content:"\f7bd"}.bi-8-circle-fill::before{content:"\f7c0"}.bi-8-circle::before{content:"\f7c1"}.bi-8-square-fill::before{content:"\f7c2"}.bi-8-square::before{content:"\f7c3"}.bi-9-circle-fill::before{content:"\f7c6"}.bi-9-circle::before{content:"\f7c7"}.bi-9-square-fill::before{content:"\f7c8"}.bi-9-square::before{content:"\f7c9"}.bi-airplane-engines-fill::before{content:"\f7ca"}.bi-airplane-engines::before{content:"\f7cb"}.bi-airplane-fill::before{content:"\f7cc"}.bi-airplane::before{content:"\f7cd"}.bi-alexa::before{content:"\f7ce"}.bi-alipay::before{content:"\f7cf"}.bi-android::before{content:"\f7d0"}.bi-android2::before{content:"\f7d1"}.bi-box-fill::before{content:"\f7d2"}.bi-box-seam-fill::before{content:"\f7d3"}.bi-browser-chrome::before{content:"\f7d4"}.bi-browser-edge::before{content:"\f7d5"}.bi-browser-firefox::before{content:"\f7d6"}.bi-browser-safari::before{content:"\f7d7"}.bi-c-circle-fill::before{content:"\f7da"}.bi-c-circle::before{content:"\f7db"}.bi-c-square-fill::before{content:"\f7dc"}.bi-c-square::before{content:"\f7dd"}.bi-capsule-pill::before{content:"\f7de"}.bi-capsule::before{content:"\f7df"}.bi-car-front-fill::before{content:"\f7e0"}.bi-car-front::before{content:"\f7e1"}.bi-cassette-fill::before{content:"\f7e2"}.bi-cassette::before{content:"\f7e3"}.bi-cc-circle-fill::before{content:"\f7e6"}.bi-cc-circle::before{content:"\f7e7"}.bi-cc-square-fill::before{content:"\f7e8"}.bi-cc-square::before{content:"\f7e9"}.bi-cup-hot-fill::before{content:"\f7ea"}.bi-cup-hot::before{content:"\f7eb"}.bi-currency-rupee::before{content:"\f7ec"}.bi-dropbox::before{content:"\f7ed"}.bi-escape::before{content:"\f7ee"}.bi-fast-forward-btn-fill::before{content:"\f7ef"}.bi-fast-forward-btn::before{content:"\f7f0"}.bi-fast-forward-circle-fill::before{content:"\f7f1"}.bi-fast-forward-circle::before{content:"\f7f2"}.bi-fast-forward-fill::before{content:"\f7f3"}.bi-fast-forward::before{content:"\f7f4"}.bi-filetype-sql::before{content:"\f7f5"}.bi-fire::before{content:"\f7f6"}.bi-google-play::before{content:"\f7f7"}.bi-h-circle-fill::before{content:"\f7fa"}.bi-h-circle::before{content:"\f7fb"}.bi-h-square-fill::before{content:"\f7fc"}.bi-h-square::before{content:"\f7fd"}.bi-indent::before{content:"\f7fe"}.bi-lungs-fill::before{content:"\f7ff"}.bi-lungs::before{content:"\f800"}.bi-microsoft-teams::before{content:"\f801"}.bi-p-circle-fill::before{content:"\f804"}.bi-p-circle::before{content:"\f805"}.bi-p-square-fill::before{content:"\f806"}.bi-p-square::before{content:"\f807"}.bi-pass-fill::before{content:"\f808"}.bi-pass::before{content:"\f809"}.bi-prescription::before{content:"\f80a"}.bi-prescription2::before{content:"\f80b"}.bi-r-circle-fill::before{content:"\f80e"}.bi-r-circle::before{content:"\f80f"}.bi-r-square-fill::before{content:"\f810"}.bi-r-square::before{content:"\f811"}.bi-repeat-1::before{content:"\f812"}.bi-repeat::before{content:"\f813"}.bi-rewind-btn-fill::before{content:"\f814"}.bi-rewind-btn::before{content:"\f815"}.bi-rewind-circle-fill::before{content:"\f816"}.bi-rewind-circle::before{content:"\f817"}.bi-rewind-fill::before{content:"\f818"}.bi-rewind::before{content:"\f819"}.bi-train-freight-front-fill::before{content:"\f81a"}.bi-train-freight-front::before{content:"\f81b"}.bi-train-front-fill::before{content:"\f81c"}.bi-train-front::before{content:"\f81d"}.bi-train-lightrail-front-fill::before{content:"\f81e"}.bi-train-lightrail-front::before{content:"\f81f"}.bi-truck-front-fill::before{content:"\f820"}.bi-truck-front::before{content:"\f821"}.bi-ubuntu::before{content:"\f822"}.bi-unindent::before{content:"\f823"}.bi-unity::before{content:"\f824"}.bi-universal-access-circle::before{content:"\f825"}.bi-universal-access::before{content:"\f826"}.bi-virus::before{content:"\f827"}.bi-virus2::before{content:"\f828"}.bi-wechat::before{content:"\f829"}.bi-yelp::before{content:"\f82a"}.bi-sign-stop-fill::before{content:"\f82b"}.bi-sign-stop-lights-fill::before{content:"\f82c"}.bi-sign-stop-lights::before{content:"\f82d"}.bi-sign-stop::before{content:"\f82e"}.bi-sign-turn-left-fill::before{content:"\f82f"}.bi-sign-turn-left::before{content:"\f830"}.bi-sign-turn-right-fill::before{content:"\f831"}.bi-sign-turn-right::before{content:"\f832"}.bi-sign-turn-slight-left-fill::before{content:"\f833"}.bi-sign-turn-slight-left::before{content:"\f834"}.bi-sign-turn-slight-right-fill::before{content:"\f835"}.bi-sign-turn-slight-right::before{content:"\f836"}.bi-sign-yield-fill::before{content:"\f837"}.bi-sign-yield::before{content:"\f838"}.bi-ev-station-fill::before{content:"\f839"}.bi-ev-station::before{content:"\f83a"}.bi-fuel-pump-diesel-fill::before{content:"\f83b"}.bi-fuel-pump-diesel::before{content:"\f83c"}.bi-fuel-pump-fill::before{content:"\f83d"}.bi-fuel-pump::before{content:"\f83e"}.bi-0-circle-fill::before{content:"\f83f"}.bi-0-circle::before{content:"\f840"}.bi-0-square-fill::before{content:"\f841"}.bi-0-square::before{content:"\f842"}.bi-rocket-fill::before{content:"\f843"}.bi-rocket-takeoff-fill::before{content:"\f844"}.bi-rocket-takeoff::before{content:"\f845"}.bi-rocket::before{content:"\f846"}.bi-stripe::before{content:"\f847"}.bi-subscript::before{content:"\f848"}.bi-superscript::before{content:"\f849"}.bi-trello::before{content:"\f84a"}.bi-envelope-at-fill::before{content:"\f84b"}.bi-envelope-at::before{content:"\f84c"}.bi-regex::before{content:"\f84d"}.bi-text-wrap::before{content:"\f84e"}.bi-sign-dead-end-fill::before{content:"\f84f"}.bi-sign-dead-end::before{content:"\f850"}.bi-sign-do-not-enter-fill::before{content:"\f851"}.bi-sign-do-not-enter::before{content:"\f852"}.bi-sign-intersection-fill::before{content:"\f853"}.bi-sign-intersection-side-fill::before{content:"\f854"}.bi-sign-intersection-side::before{content:"\f855"}.bi-sign-intersection-t-fill::before{content:"\f856"}.bi-sign-intersection-t::before{content:"\f857"}.bi-sign-intersection-y-fill::before{content:"\f858"}.bi-sign-intersection-y::before{content:"\f859"}.bi-sign-intersection::before{content:"\f85a"}.bi-sign-merge-left-fill::before{content:"\f85b"}.bi-sign-merge-left::before{content:"\f85c"}.bi-sign-merge-right-fill::before{content:"\f85d"}.bi-sign-merge-right::before{content:"\f85e"}.bi-sign-no-left-turn-fill::before{content:"\f85f"}.bi-sign-no-left-turn::before{content:"\f860"}.bi-sign-no-parking-fill::before{content:"\f861"}.bi-sign-no-parking::before{content:"\f862"}.bi-sign-no-right-turn-fill::before{content:"\f863"}.bi-sign-no-right-turn::before{content:"\f864"}.bi-sign-railroad-fill::before{content:"\f865"}.bi-sign-railroad::before{content:"\f866"}.bi-building-add::before{content:"\f867"}.bi-building-check::before{content:"\f868"}.bi-building-dash::before{content:"\f869"}.bi-building-down::before{content:"\f86a"}.bi-building-exclamation::before{content:"\f86b"}.bi-building-fill-add::before{content:"\f86c"}.bi-building-fill-check::before{content:"\f86d"}.bi-building-fill-dash::before{content:"\f86e"}.bi-building-fill-down::before{content:"\f86f"}.bi-building-fill-exclamation::before{content:"\f870"}.bi-building-fill-gear::before{content:"\f871"}.bi-building-fill-lock::before{content:"\f872"}.bi-building-fill-slash::before{content:"\f873"}.bi-building-fill-up::before{content:"\f874"}.bi-building-fill-x::before{content:"\f875"}.bi-building-fill::before{content:"\f876"}.bi-building-gear::before{content:"\f877"}.bi-building-lock::before{content:"\f878"}.bi-building-slash::before{content:"\f879"}.bi-building-up::before{content:"\f87a"}.bi-building-x::before{content:"\f87b"}.bi-buildings-fill::before{content:"\f87c"}.bi-buildings::before{content:"\f87d"}.bi-bus-front-fill::before{content:"\f87e"}.bi-bus-front::before{content:"\f87f"}.bi-ev-front-fill::before{content:"\f880"}.bi-ev-front::before{content:"\f881"}.bi-globe-americas::before{content:"\f882"}.bi-globe-asia-australia::before{content:"\f883"}.bi-globe-central-south-asia::before{content:"\f884"}.bi-globe-europe-africa::before{content:"\f885"}.bi-house-add-fill::before{content:"\f886"}.bi-house-add::before{content:"\f887"}.bi-house-check-fill::before{content:"\f888"}.bi-house-check::before{content:"\f889"}.bi-house-dash-fill::before{content:"\f88a"}.bi-house-dash::before{content:"\f88b"}.bi-house-down-fill::before{content:"\f88c"}.bi-house-down::before{content:"\f88d"}.bi-house-exclamation-fill::before{content:"\f88e"}.bi-house-exclamation::before{content:"\f88f"}.bi-house-gear-fill::before{content:"\f890"}.bi-house-gear::before{content:"\f891"}.bi-house-lock-fill::before{content:"\f892"}.bi-house-lock::before{content:"\f893"}.bi-house-slash-fill::before{content:"\f894"}.bi-house-slash::before{content:"\f895"}.bi-house-up-fill::before{content:"\f896"}.bi-house-up::before{content:"\f897"}.bi-house-x-fill::before{content:"\f898"}.bi-house-x::before{content:"\f899"}.bi-person-add::before{content:"\f89a"}.bi-person-down::before{content:"\f89b"}.bi-person-exclamation::before{content:"\f89c"}.bi-person-fill-add::before{content:"\f89d"}.bi-person-fill-check::before{content:"\f89e"}.bi-person-fill-dash::before{content:"\f89f"}.bi-person-fill-down::before{content:"\f8a0"}.bi-person-fill-exclamation::before{content:"\f8a1"}.bi-person-fill-gear::before{content:"\f8a2"}.bi-person-fill-lock::before{content:"\f8a3"}.bi-person-fill-slash::before{content:"\f8a4"}.bi-person-fill-up::before{content:"\f8a5"}.bi-person-fill-x::before{content:"\f8a6"}.bi-person-gear::before{content:"\f8a7"}.bi-person-lock::before{content:"\f8a8"}.bi-person-slash::before{content:"\f8a9"}.bi-person-up::before{content:"\f8aa"}.bi-scooter::before{content:"\f8ab"}.bi-taxi-front-fill::before{content:"\f8ac"}.bi-taxi-front::before{content:"\f8ad"}.bi-amd::before{content:"\f8ae"}.bi-database-add::before{content:"\f8af"}.bi-database-check::before{content:"\f8b0"}.bi-database-dash::before{content:"\f8b1"}.bi-database-down::before{content:"\f8b2"}.bi-database-exclamation::before{content:"\f8b3"}.bi-database-fill-add::before{content:"\f8b4"}.bi-database-fill-check::before{content:"\f8b5"}.bi-database-fill-dash::before{content:"\f8b6"}.bi-database-fill-down::before{content:"\f8b7"}.bi-database-fill-exclamation::before{content:"\f8b8"}.bi-database-fill-gear::before{content:"\f8b9"}.bi-database-fill-lock::before{content:"\f8ba"}.bi-database-fill-slash::before{content:"\f8bb"}.bi-database-fill-up::before{content:"\f8bc"}.bi-database-fill-x::before{content:"\f8bd"}.bi-database-fill::before{content:"\f8be"}.bi-database-gear::before{content:"\f8bf"}.bi-database-lock::before{content:"\f8c0"}.bi-database-slash::before{content:"\f8c1"}.bi-database-up::before{content:"\f8c2"}.bi-database-x::before{content:"\f8c3"}.bi-database::before{content:"\f8c4"}.bi-houses-fill::before{content:"\f8c5"}.bi-houses::before{content:"\f8c6"}.bi-nvidia::before{content:"\f8c7"}.bi-person-vcard-fill::before{content:"\f8c8"}.bi-person-vcard::before{content:"\f8c9"}.bi-sina-weibo::before{content:"\f8ca"}.bi-tencent-qq::before{content:"\f8cb"}.bi-wikipedia::before{content:"\f8cc"}.bi-alphabet-uppercase::before{content:"\f2a5"}.bi-alphabet::before{content:"\f68a"}.bi-amazon::before{content:"\f68d"}.bi-arrows-collapse-vertical::before{content:"\f690"}.bi-arrows-expand-vertical::before{content:"\f695"}.bi-arrows-vertical::before{content:"\f698"}.bi-arrows::before{content:"\f6a2"}.bi-ban-fill::before{content:"\f6a3"}.bi-ban::before{content:"\f6b6"}.bi-bing::before{content:"\f6c2"}.bi-cake::before{content:"\f6e0"}.bi-cake2::before{content:"\f6ed"}.bi-cookie::before{content:"\f6ee"}.bi-copy::before{content:"\f759"}.bi-crosshair::before{content:"\f769"}.bi-crosshair2::before{content:"\f794"}.bi-emoji-astonished-fill::before{content:"\f795"}.bi-emoji-astonished::before{content:"\f79a"}.bi-emoji-grimace-fill::before{content:"\f79b"}.bi-emoji-grimace::before{content:"\f7a0"}.bi-emoji-grin-fill::before{content:"\f7a1"}.bi-emoji-grin::before{content:"\f7a6"}.bi-emoji-surprise-fill::before{content:"\f7a7"}.bi-emoji-surprise::before{content:"\f7ac"}.bi-emoji-tear-fill::before{content:"\f7ad"}.bi-emoji-tear::before{content:"\f7b2"}.bi-envelope-arrow-down-fill::before{content:"\f7b3"}.bi-envelope-arrow-down::before{content:"\f7b8"}.bi-envelope-arrow-up-fill::before{content:"\f7b9"}.bi-envelope-arrow-up::before{content:"\f7be"}.bi-feather::before{content:"\f7bf"}.bi-feather2::before{content:"\f7c4"}.bi-floppy-fill::before{content:"\f7c5"}.bi-floppy::before{content:"\f7d8"}.bi-floppy2-fill::before{content:"\f7d9"}.bi-floppy2::before{content:"\f7e4"}.bi-gitlab::before{content:"\f7e5"}.bi-highlighter::before{content:"\f7f8"}.bi-marker-tip::before{content:"\f802"}.bi-nvme-fill::before{content:"\f803"}.bi-nvme::before{content:"\f80c"}.bi-opencollective::before{content:"\f80d"}.bi-pci-card-network::before{content:"\f8cd"}.bi-pci-card-sound::before{content:"\f8ce"}.bi-radar::before{content:"\f8cf"}.bi-send-arrow-down-fill::before{content:"\f8d0"}.bi-send-arrow-down::before{content:"\f8d1"}.bi-send-arrow-up-fill::before{content:"\f8d2"}.bi-send-arrow-up::before{content:"\f8d3"}.bi-sim-slash-fill::before{content:"\f8d4"}.bi-sim-slash::before{content:"\f8d5"}.bi-sourceforge::before{content:"\f8d6"}.bi-substack::before{content:"\f8d7"}.bi-threads-fill::before{content:"\f8d8"}.bi-threads::before{content:"\f8d9"}.bi-transparency::before{content:"\f8da"}.bi-twitter-x::before{content:"\f8db"}.bi-type-h4::before{content:"\f8dc"}.bi-type-h5::before{content:"\f8dd"}.bi-type-h6::before{content:"\f8de"}.bi-backpack-fill::before{content:"\f8df"}.bi-backpack::before{content:"\f8e0"}.bi-backpack2-fill::before{content:"\f8e1"}.bi-backpack2::before{content:"\f8e2"}.bi-backpack3-fill::before{content:"\f8e3"}.bi-backpack3::before{content:"\f8e4"}.bi-backpack4-fill::before{content:"\f8e5"}.bi-backpack4::before{content:"\f8e6"}.bi-brilliance::before{content:"\f8e7"}.bi-cake-fill::before{content:"\f8e8"}.bi-cake2-fill::before{content:"\f8e9"}.bi-duffle-fill::before{content:"\f8ea"}.bi-duffle::before{content:"\f8eb"}.bi-exposure::before{content:"\f8ec"}.bi-gender-neuter::before{content:"\f8ed"}.bi-highlights::before{content:"\f8ee"}.bi-luggage-fill::before{content:"\f8ef"}.bi-luggage::before{content:"\f8f0"}.bi-mailbox-flag::before{content:"\f8f1"}.bi-mailbox2-flag::before{content:"\f8f2"}.bi-noise-reduction::before{content:"\f8f3"}.bi-passport-fill::before{content:"\f8f4"}.bi-passport::before{content:"\f8f5"}.bi-person-arms-up::before{content:"\f8f6"}.bi-person-raised-hand::before{content:"\f8f7"}.bi-person-standing-dress::before{content:"\f8f8"}.bi-person-standing::before{content:"\f8f9"}.bi-person-walking::before{content:"\f8fa"}.bi-person-wheelchair::before{content:"\f8fb"}.bi-shadows::before{content:"\f8fc"}.bi-suitcase-fill::before{content:"\f8fd"}.bi-suitcase-lg-fill::before{content:"\f8fe"}.bi-suitcase-lg::before{content:"\f8ff"}.bi-suitcase::before{content:"\f900"}.bi-suitcase2-fill::before{content:"\f901"}.bi-suitcase2::before{content:"\f902"}.bi-vignette::before{content:"\f903"}.bi-bluesky::before{content:"\f7f9"}.bi-tux::before{content:"\f904"}.bi-beaker-fill::before{content:"\f905"}.bi-beaker::before{content:"\f906"}.bi-flask-fill::before{content:"\f907"}.bi-flask-florence-fill::before{content:"\f908"}.bi-flask-florence::before{content:"\f909"}.bi-flask::before{content:"\f90a"}.bi-leaf-fill::before{content:"\f90b"}.bi-leaf::before{content:"\f90c"}.bi-measuring-cup-fill::before{content:"\f90d"}.bi-measuring-cup::before{content:"\f90e"}.bi-unlock2-fill::before{content:"\f90f"}.bi-unlock2::before{content:"\f910"}.bi-battery-low::before{content:"\f911"}.bi-anthropic::before{content:"\f912"}.bi-apple-music::before{content:"\f913"}.bi-claude::before{content:"\f914"}.bi-openai::before{content:"\f915"}.bi-perplexity::before{content:"\f916"}.bi-css::before{content:"\f917"}.bi-javascript::before{content:"\f918"}.bi-typescript::before{content:"\f919"}.bi-fork-knife::before{content:"\f91a"}.bi-globe-americas-fill::before{content:"\f91b"}.bi-globe-asia-australia-fill::before{content:"\f91c"}.bi-globe-central-south-asia-fill::before{content:"\f91d"}.bi-globe-europe-africa-fill::before{content:"\f91e"}
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-icons.scss b/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-icons.scss
new file mode 100644
index 00000000..19735c42
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-icons.scss
@@ -0,0 +1,2118 @@
+/*!
+ * Bootstrap Icons v1.13.1 (https://icons.getbootstrap.com/)
+ * Copyright 2019-2024 The Bootstrap Authors
+ * Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE)
+ */
+
+$bootstrap-icons-font: "bootstrap-icons" !default;
+$bootstrap-icons-font-dir: "./fonts" !default;
+$bootstrap-icons-font-file: "#{$bootstrap-icons-font-dir}/#{$bootstrap-icons-font}" !default;
+$bootstrap-icons-font-hash: "24e3eb84d0bcaf83d77f904c78ac1f47" !default;
+$bootstrap-icons-font-src: url("#{$bootstrap-icons-font-file}.woff2?#{$bootstrap-icons-font-hash}") format("woff2"),
+ url("#{$bootstrap-icons-font-file}.woff?#{$bootstrap-icons-font-hash}") format("woff") !default;
+
+@font-face {
+ font-display: block;
+ font-family: $bootstrap-icons-font;
+ src: $bootstrap-icons-font-src;
+}
+
+.bi::before,
+[class^="bi-"]::before,
+[class*=" bi-"]::before {
+ display: inline-block;
+ font-family: $bootstrap-icons-font !important;
+ font-style: normal;
+ font-weight: normal !important;
+ font-variant: normal;
+ text-transform: none;
+ line-height: 1;
+ vertical-align: -.125em;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+$bootstrap-icons-map: (
+ "123": "\f67f",
+ "alarm-fill": "\f101",
+ "alarm": "\f102",
+ "align-bottom": "\f103",
+ "align-center": "\f104",
+ "align-end": "\f105",
+ "align-middle": "\f106",
+ "align-start": "\f107",
+ "align-top": "\f108",
+ "alt": "\f109",
+ "app-indicator": "\f10a",
+ "app": "\f10b",
+ "archive-fill": "\f10c",
+ "archive": "\f10d",
+ "arrow-90deg-down": "\f10e",
+ "arrow-90deg-left": "\f10f",
+ "arrow-90deg-right": "\f110",
+ "arrow-90deg-up": "\f111",
+ "arrow-bar-down": "\f112",
+ "arrow-bar-left": "\f113",
+ "arrow-bar-right": "\f114",
+ "arrow-bar-up": "\f115",
+ "arrow-clockwise": "\f116",
+ "arrow-counterclockwise": "\f117",
+ "arrow-down-circle-fill": "\f118",
+ "arrow-down-circle": "\f119",
+ "arrow-down-left-circle-fill": "\f11a",
+ "arrow-down-left-circle": "\f11b",
+ "arrow-down-left-square-fill": "\f11c",
+ "arrow-down-left-square": "\f11d",
+ "arrow-down-left": "\f11e",
+ "arrow-down-right-circle-fill": "\f11f",
+ "arrow-down-right-circle": "\f120",
+ "arrow-down-right-square-fill": "\f121",
+ "arrow-down-right-square": "\f122",
+ "arrow-down-right": "\f123",
+ "arrow-down-short": "\f124",
+ "arrow-down-square-fill": "\f125",
+ "arrow-down-square": "\f126",
+ "arrow-down-up": "\f127",
+ "arrow-down": "\f128",
+ "arrow-left-circle-fill": "\f129",
+ "arrow-left-circle": "\f12a",
+ "arrow-left-right": "\f12b",
+ "arrow-left-short": "\f12c",
+ "arrow-left-square-fill": "\f12d",
+ "arrow-left-square": "\f12e",
+ "arrow-left": "\f12f",
+ "arrow-repeat": "\f130",
+ "arrow-return-left": "\f131",
+ "arrow-return-right": "\f132",
+ "arrow-right-circle-fill": "\f133",
+ "arrow-right-circle": "\f134",
+ "arrow-right-short": "\f135",
+ "arrow-right-square-fill": "\f136",
+ "arrow-right-square": "\f137",
+ "arrow-right": "\f138",
+ "arrow-up-circle-fill": "\f139",
+ "arrow-up-circle": "\f13a",
+ "arrow-up-left-circle-fill": "\f13b",
+ "arrow-up-left-circle": "\f13c",
+ "arrow-up-left-square-fill": "\f13d",
+ "arrow-up-left-square": "\f13e",
+ "arrow-up-left": "\f13f",
+ "arrow-up-right-circle-fill": "\f140",
+ "arrow-up-right-circle": "\f141",
+ "arrow-up-right-square-fill": "\f142",
+ "arrow-up-right-square": "\f143",
+ "arrow-up-right": "\f144",
+ "arrow-up-short": "\f145",
+ "arrow-up-square-fill": "\f146",
+ "arrow-up-square": "\f147",
+ "arrow-up": "\f148",
+ "arrows-angle-contract": "\f149",
+ "arrows-angle-expand": "\f14a",
+ "arrows-collapse": "\f14b",
+ "arrows-expand": "\f14c",
+ "arrows-fullscreen": "\f14d",
+ "arrows-move": "\f14e",
+ "aspect-ratio-fill": "\f14f",
+ "aspect-ratio": "\f150",
+ "asterisk": "\f151",
+ "at": "\f152",
+ "award-fill": "\f153",
+ "award": "\f154",
+ "back": "\f155",
+ "backspace-fill": "\f156",
+ "backspace-reverse-fill": "\f157",
+ "backspace-reverse": "\f158",
+ "backspace": "\f159",
+ "badge-3d-fill": "\f15a",
+ "badge-3d": "\f15b",
+ "badge-4k-fill": "\f15c",
+ "badge-4k": "\f15d",
+ "badge-8k-fill": "\f15e",
+ "badge-8k": "\f15f",
+ "badge-ad-fill": "\f160",
+ "badge-ad": "\f161",
+ "badge-ar-fill": "\f162",
+ "badge-ar": "\f163",
+ "badge-cc-fill": "\f164",
+ "badge-cc": "\f165",
+ "badge-hd-fill": "\f166",
+ "badge-hd": "\f167",
+ "badge-tm-fill": "\f168",
+ "badge-tm": "\f169",
+ "badge-vo-fill": "\f16a",
+ "badge-vo": "\f16b",
+ "badge-vr-fill": "\f16c",
+ "badge-vr": "\f16d",
+ "badge-wc-fill": "\f16e",
+ "badge-wc": "\f16f",
+ "bag-check-fill": "\f170",
+ "bag-check": "\f171",
+ "bag-dash-fill": "\f172",
+ "bag-dash": "\f173",
+ "bag-fill": "\f174",
+ "bag-plus-fill": "\f175",
+ "bag-plus": "\f176",
+ "bag-x-fill": "\f177",
+ "bag-x": "\f178",
+ "bag": "\f179",
+ "bar-chart-fill": "\f17a",
+ "bar-chart-line-fill": "\f17b",
+ "bar-chart-line": "\f17c",
+ "bar-chart-steps": "\f17d",
+ "bar-chart": "\f17e",
+ "basket-fill": "\f17f",
+ "basket": "\f180",
+ "basket2-fill": "\f181",
+ "basket2": "\f182",
+ "basket3-fill": "\f183",
+ "basket3": "\f184",
+ "battery-charging": "\f185",
+ "battery-full": "\f186",
+ "battery-half": "\f187",
+ "battery": "\f188",
+ "bell-fill": "\f189",
+ "bell": "\f18a",
+ "bezier": "\f18b",
+ "bezier2": "\f18c",
+ "bicycle": "\f18d",
+ "binoculars-fill": "\f18e",
+ "binoculars": "\f18f",
+ "blockquote-left": "\f190",
+ "blockquote-right": "\f191",
+ "book-fill": "\f192",
+ "book-half": "\f193",
+ "book": "\f194",
+ "bookmark-check-fill": "\f195",
+ "bookmark-check": "\f196",
+ "bookmark-dash-fill": "\f197",
+ "bookmark-dash": "\f198",
+ "bookmark-fill": "\f199",
+ "bookmark-heart-fill": "\f19a",
+ "bookmark-heart": "\f19b",
+ "bookmark-plus-fill": "\f19c",
+ "bookmark-plus": "\f19d",
+ "bookmark-star-fill": "\f19e",
+ "bookmark-star": "\f19f",
+ "bookmark-x-fill": "\f1a0",
+ "bookmark-x": "\f1a1",
+ "bookmark": "\f1a2",
+ "bookmarks-fill": "\f1a3",
+ "bookmarks": "\f1a4",
+ "bookshelf": "\f1a5",
+ "bootstrap-fill": "\f1a6",
+ "bootstrap-reboot": "\f1a7",
+ "bootstrap": "\f1a8",
+ "border-all": "\f1a9",
+ "border-bottom": "\f1aa",
+ "border-center": "\f1ab",
+ "border-inner": "\f1ac",
+ "border-left": "\f1ad",
+ "border-middle": "\f1ae",
+ "border-outer": "\f1af",
+ "border-right": "\f1b0",
+ "border-style": "\f1b1",
+ "border-top": "\f1b2",
+ "border-width": "\f1b3",
+ "border": "\f1b4",
+ "bounding-box-circles": "\f1b5",
+ "bounding-box": "\f1b6",
+ "box-arrow-down-left": "\f1b7",
+ "box-arrow-down-right": "\f1b8",
+ "box-arrow-down": "\f1b9",
+ "box-arrow-in-down-left": "\f1ba",
+ "box-arrow-in-down-right": "\f1bb",
+ "box-arrow-in-down": "\f1bc",
+ "box-arrow-in-left": "\f1bd",
+ "box-arrow-in-right": "\f1be",
+ "box-arrow-in-up-left": "\f1bf",
+ "box-arrow-in-up-right": "\f1c0",
+ "box-arrow-in-up": "\f1c1",
+ "box-arrow-left": "\f1c2",
+ "box-arrow-right": "\f1c3",
+ "box-arrow-up-left": "\f1c4",
+ "box-arrow-up-right": "\f1c5",
+ "box-arrow-up": "\f1c6",
+ "box-seam": "\f1c7",
+ "box": "\f1c8",
+ "braces": "\f1c9",
+ "bricks": "\f1ca",
+ "briefcase-fill": "\f1cb",
+ "briefcase": "\f1cc",
+ "brightness-alt-high-fill": "\f1cd",
+ "brightness-alt-high": "\f1ce",
+ "brightness-alt-low-fill": "\f1cf",
+ "brightness-alt-low": "\f1d0",
+ "brightness-high-fill": "\f1d1",
+ "brightness-high": "\f1d2",
+ "brightness-low-fill": "\f1d3",
+ "brightness-low": "\f1d4",
+ "broadcast-pin": "\f1d5",
+ "broadcast": "\f1d6",
+ "brush-fill": "\f1d7",
+ "brush": "\f1d8",
+ "bucket-fill": "\f1d9",
+ "bucket": "\f1da",
+ "bug-fill": "\f1db",
+ "bug": "\f1dc",
+ "building": "\f1dd",
+ "bullseye": "\f1de",
+ "calculator-fill": "\f1df",
+ "calculator": "\f1e0",
+ "calendar-check-fill": "\f1e1",
+ "calendar-check": "\f1e2",
+ "calendar-date-fill": "\f1e3",
+ "calendar-date": "\f1e4",
+ "calendar-day-fill": "\f1e5",
+ "calendar-day": "\f1e6",
+ "calendar-event-fill": "\f1e7",
+ "calendar-event": "\f1e8",
+ "calendar-fill": "\f1e9",
+ "calendar-minus-fill": "\f1ea",
+ "calendar-minus": "\f1eb",
+ "calendar-month-fill": "\f1ec",
+ "calendar-month": "\f1ed",
+ "calendar-plus-fill": "\f1ee",
+ "calendar-plus": "\f1ef",
+ "calendar-range-fill": "\f1f0",
+ "calendar-range": "\f1f1",
+ "calendar-week-fill": "\f1f2",
+ "calendar-week": "\f1f3",
+ "calendar-x-fill": "\f1f4",
+ "calendar-x": "\f1f5",
+ "calendar": "\f1f6",
+ "calendar2-check-fill": "\f1f7",
+ "calendar2-check": "\f1f8",
+ "calendar2-date-fill": "\f1f9",
+ "calendar2-date": "\f1fa",
+ "calendar2-day-fill": "\f1fb",
+ "calendar2-day": "\f1fc",
+ "calendar2-event-fill": "\f1fd",
+ "calendar2-event": "\f1fe",
+ "calendar2-fill": "\f1ff",
+ "calendar2-minus-fill": "\f200",
+ "calendar2-minus": "\f201",
+ "calendar2-month-fill": "\f202",
+ "calendar2-month": "\f203",
+ "calendar2-plus-fill": "\f204",
+ "calendar2-plus": "\f205",
+ "calendar2-range-fill": "\f206",
+ "calendar2-range": "\f207",
+ "calendar2-week-fill": "\f208",
+ "calendar2-week": "\f209",
+ "calendar2-x-fill": "\f20a",
+ "calendar2-x": "\f20b",
+ "calendar2": "\f20c",
+ "calendar3-event-fill": "\f20d",
+ "calendar3-event": "\f20e",
+ "calendar3-fill": "\f20f",
+ "calendar3-range-fill": "\f210",
+ "calendar3-range": "\f211",
+ "calendar3-week-fill": "\f212",
+ "calendar3-week": "\f213",
+ "calendar3": "\f214",
+ "calendar4-event": "\f215",
+ "calendar4-range": "\f216",
+ "calendar4-week": "\f217",
+ "calendar4": "\f218",
+ "camera-fill": "\f219",
+ "camera-reels-fill": "\f21a",
+ "camera-reels": "\f21b",
+ "camera-video-fill": "\f21c",
+ "camera-video-off-fill": "\f21d",
+ "camera-video-off": "\f21e",
+ "camera-video": "\f21f",
+ "camera": "\f220",
+ "camera2": "\f221",
+ "capslock-fill": "\f222",
+ "capslock": "\f223",
+ "card-checklist": "\f224",
+ "card-heading": "\f225",
+ "card-image": "\f226",
+ "card-list": "\f227",
+ "card-text": "\f228",
+ "caret-down-fill": "\f229",
+ "caret-down-square-fill": "\f22a",
+ "caret-down-square": "\f22b",
+ "caret-down": "\f22c",
+ "caret-left-fill": "\f22d",
+ "caret-left-square-fill": "\f22e",
+ "caret-left-square": "\f22f",
+ "caret-left": "\f230",
+ "caret-right-fill": "\f231",
+ "caret-right-square-fill": "\f232",
+ "caret-right-square": "\f233",
+ "caret-right": "\f234",
+ "caret-up-fill": "\f235",
+ "caret-up-square-fill": "\f236",
+ "caret-up-square": "\f237",
+ "caret-up": "\f238",
+ "cart-check-fill": "\f239",
+ "cart-check": "\f23a",
+ "cart-dash-fill": "\f23b",
+ "cart-dash": "\f23c",
+ "cart-fill": "\f23d",
+ "cart-plus-fill": "\f23e",
+ "cart-plus": "\f23f",
+ "cart-x-fill": "\f240",
+ "cart-x": "\f241",
+ "cart": "\f242",
+ "cart2": "\f243",
+ "cart3": "\f244",
+ "cart4": "\f245",
+ "cash-stack": "\f246",
+ "cash": "\f247",
+ "cast": "\f248",
+ "chat-dots-fill": "\f249",
+ "chat-dots": "\f24a",
+ "chat-fill": "\f24b",
+ "chat-left-dots-fill": "\f24c",
+ "chat-left-dots": "\f24d",
+ "chat-left-fill": "\f24e",
+ "chat-left-quote-fill": "\f24f",
+ "chat-left-quote": "\f250",
+ "chat-left-text-fill": "\f251",
+ "chat-left-text": "\f252",
+ "chat-left": "\f253",
+ "chat-quote-fill": "\f254",
+ "chat-quote": "\f255",
+ "chat-right-dots-fill": "\f256",
+ "chat-right-dots": "\f257",
+ "chat-right-fill": "\f258",
+ "chat-right-quote-fill": "\f259",
+ "chat-right-quote": "\f25a",
+ "chat-right-text-fill": "\f25b",
+ "chat-right-text": "\f25c",
+ "chat-right": "\f25d",
+ "chat-square-dots-fill": "\f25e",
+ "chat-square-dots": "\f25f",
+ "chat-square-fill": "\f260",
+ "chat-square-quote-fill": "\f261",
+ "chat-square-quote": "\f262",
+ "chat-square-text-fill": "\f263",
+ "chat-square-text": "\f264",
+ "chat-square": "\f265",
+ "chat-text-fill": "\f266",
+ "chat-text": "\f267",
+ "chat": "\f268",
+ "check-all": "\f269",
+ "check-circle-fill": "\f26a",
+ "check-circle": "\f26b",
+ "check-square-fill": "\f26c",
+ "check-square": "\f26d",
+ "check": "\f26e",
+ "check2-all": "\f26f",
+ "check2-circle": "\f270",
+ "check2-square": "\f271",
+ "check2": "\f272",
+ "chevron-bar-contract": "\f273",
+ "chevron-bar-down": "\f274",
+ "chevron-bar-expand": "\f275",
+ "chevron-bar-left": "\f276",
+ "chevron-bar-right": "\f277",
+ "chevron-bar-up": "\f278",
+ "chevron-compact-down": "\f279",
+ "chevron-compact-left": "\f27a",
+ "chevron-compact-right": "\f27b",
+ "chevron-compact-up": "\f27c",
+ "chevron-contract": "\f27d",
+ "chevron-double-down": "\f27e",
+ "chevron-double-left": "\f27f",
+ "chevron-double-right": "\f280",
+ "chevron-double-up": "\f281",
+ "chevron-down": "\f282",
+ "chevron-expand": "\f283",
+ "chevron-left": "\f284",
+ "chevron-right": "\f285",
+ "chevron-up": "\f286",
+ "circle-fill": "\f287",
+ "circle-half": "\f288",
+ "circle-square": "\f289",
+ "circle": "\f28a",
+ "clipboard-check": "\f28b",
+ "clipboard-data": "\f28c",
+ "clipboard-minus": "\f28d",
+ "clipboard-plus": "\f28e",
+ "clipboard-x": "\f28f",
+ "clipboard": "\f290",
+ "clock-fill": "\f291",
+ "clock-history": "\f292",
+ "clock": "\f293",
+ "cloud-arrow-down-fill": "\f294",
+ "cloud-arrow-down": "\f295",
+ "cloud-arrow-up-fill": "\f296",
+ "cloud-arrow-up": "\f297",
+ "cloud-check-fill": "\f298",
+ "cloud-check": "\f299",
+ "cloud-download-fill": "\f29a",
+ "cloud-download": "\f29b",
+ "cloud-drizzle-fill": "\f29c",
+ "cloud-drizzle": "\f29d",
+ "cloud-fill": "\f29e",
+ "cloud-fog-fill": "\f29f",
+ "cloud-fog": "\f2a0",
+ "cloud-fog2-fill": "\f2a1",
+ "cloud-fog2": "\f2a2",
+ "cloud-hail-fill": "\f2a3",
+ "cloud-hail": "\f2a4",
+ "cloud-haze-fill": "\f2a6",
+ "cloud-haze": "\f2a7",
+ "cloud-haze2-fill": "\f2a8",
+ "cloud-lightning-fill": "\f2a9",
+ "cloud-lightning-rain-fill": "\f2aa",
+ "cloud-lightning-rain": "\f2ab",
+ "cloud-lightning": "\f2ac",
+ "cloud-minus-fill": "\f2ad",
+ "cloud-minus": "\f2ae",
+ "cloud-moon-fill": "\f2af",
+ "cloud-moon": "\f2b0",
+ "cloud-plus-fill": "\f2b1",
+ "cloud-plus": "\f2b2",
+ "cloud-rain-fill": "\f2b3",
+ "cloud-rain-heavy-fill": "\f2b4",
+ "cloud-rain-heavy": "\f2b5",
+ "cloud-rain": "\f2b6",
+ "cloud-slash-fill": "\f2b7",
+ "cloud-slash": "\f2b8",
+ "cloud-sleet-fill": "\f2b9",
+ "cloud-sleet": "\f2ba",
+ "cloud-snow-fill": "\f2bb",
+ "cloud-snow": "\f2bc",
+ "cloud-sun-fill": "\f2bd",
+ "cloud-sun": "\f2be",
+ "cloud-upload-fill": "\f2bf",
+ "cloud-upload": "\f2c0",
+ "cloud": "\f2c1",
+ "clouds-fill": "\f2c2",
+ "clouds": "\f2c3",
+ "cloudy-fill": "\f2c4",
+ "cloudy": "\f2c5",
+ "code-slash": "\f2c6",
+ "code-square": "\f2c7",
+ "code": "\f2c8",
+ "collection-fill": "\f2c9",
+ "collection-play-fill": "\f2ca",
+ "collection-play": "\f2cb",
+ "collection": "\f2cc",
+ "columns-gap": "\f2cd",
+ "columns": "\f2ce",
+ "command": "\f2cf",
+ "compass-fill": "\f2d0",
+ "compass": "\f2d1",
+ "cone-striped": "\f2d2",
+ "cone": "\f2d3",
+ "controller": "\f2d4",
+ "cpu-fill": "\f2d5",
+ "cpu": "\f2d6",
+ "credit-card-2-back-fill": "\f2d7",
+ "credit-card-2-back": "\f2d8",
+ "credit-card-2-front-fill": "\f2d9",
+ "credit-card-2-front": "\f2da",
+ "credit-card-fill": "\f2db",
+ "credit-card": "\f2dc",
+ "crop": "\f2dd",
+ "cup-fill": "\f2de",
+ "cup-straw": "\f2df",
+ "cup": "\f2e0",
+ "cursor-fill": "\f2e1",
+ "cursor-text": "\f2e2",
+ "cursor": "\f2e3",
+ "dash-circle-dotted": "\f2e4",
+ "dash-circle-fill": "\f2e5",
+ "dash-circle": "\f2e6",
+ "dash-square-dotted": "\f2e7",
+ "dash-square-fill": "\f2e8",
+ "dash-square": "\f2e9",
+ "dash": "\f2ea",
+ "diagram-2-fill": "\f2eb",
+ "diagram-2": "\f2ec",
+ "diagram-3-fill": "\f2ed",
+ "diagram-3": "\f2ee",
+ "diamond-fill": "\f2ef",
+ "diamond-half": "\f2f0",
+ "diamond": "\f2f1",
+ "dice-1-fill": "\f2f2",
+ "dice-1": "\f2f3",
+ "dice-2-fill": "\f2f4",
+ "dice-2": "\f2f5",
+ "dice-3-fill": "\f2f6",
+ "dice-3": "\f2f7",
+ "dice-4-fill": "\f2f8",
+ "dice-4": "\f2f9",
+ "dice-5-fill": "\f2fa",
+ "dice-5": "\f2fb",
+ "dice-6-fill": "\f2fc",
+ "dice-6": "\f2fd",
+ "disc-fill": "\f2fe",
+ "disc": "\f2ff",
+ "discord": "\f300",
+ "display-fill": "\f301",
+ "display": "\f302",
+ "distribute-horizontal": "\f303",
+ "distribute-vertical": "\f304",
+ "door-closed-fill": "\f305",
+ "door-closed": "\f306",
+ "door-open-fill": "\f307",
+ "door-open": "\f308",
+ "dot": "\f309",
+ "download": "\f30a",
+ "droplet-fill": "\f30b",
+ "droplet-half": "\f30c",
+ "droplet": "\f30d",
+ "earbuds": "\f30e",
+ "easel-fill": "\f30f",
+ "easel": "\f310",
+ "egg-fill": "\f311",
+ "egg-fried": "\f312",
+ "egg": "\f313",
+ "eject-fill": "\f314",
+ "eject": "\f315",
+ "emoji-angry-fill": "\f316",
+ "emoji-angry": "\f317",
+ "emoji-dizzy-fill": "\f318",
+ "emoji-dizzy": "\f319",
+ "emoji-expressionless-fill": "\f31a",
+ "emoji-expressionless": "\f31b",
+ "emoji-frown-fill": "\f31c",
+ "emoji-frown": "\f31d",
+ "emoji-heart-eyes-fill": "\f31e",
+ "emoji-heart-eyes": "\f31f",
+ "emoji-laughing-fill": "\f320",
+ "emoji-laughing": "\f321",
+ "emoji-neutral-fill": "\f322",
+ "emoji-neutral": "\f323",
+ "emoji-smile-fill": "\f324",
+ "emoji-smile-upside-down-fill": "\f325",
+ "emoji-smile-upside-down": "\f326",
+ "emoji-smile": "\f327",
+ "emoji-sunglasses-fill": "\f328",
+ "emoji-sunglasses": "\f329",
+ "emoji-wink-fill": "\f32a",
+ "emoji-wink": "\f32b",
+ "envelope-fill": "\f32c",
+ "envelope-open-fill": "\f32d",
+ "envelope-open": "\f32e",
+ "envelope": "\f32f",
+ "eraser-fill": "\f330",
+ "eraser": "\f331",
+ "exclamation-circle-fill": "\f332",
+ "exclamation-circle": "\f333",
+ "exclamation-diamond-fill": "\f334",
+ "exclamation-diamond": "\f335",
+ "exclamation-octagon-fill": "\f336",
+ "exclamation-octagon": "\f337",
+ "exclamation-square-fill": "\f338",
+ "exclamation-square": "\f339",
+ "exclamation-triangle-fill": "\f33a",
+ "exclamation-triangle": "\f33b",
+ "exclamation": "\f33c",
+ "exclude": "\f33d",
+ "eye-fill": "\f33e",
+ "eye-slash-fill": "\f33f",
+ "eye-slash": "\f340",
+ "eye": "\f341",
+ "eyedropper": "\f342",
+ "eyeglasses": "\f343",
+ "facebook": "\f344",
+ "file-arrow-down-fill": "\f345",
+ "file-arrow-down": "\f346",
+ "file-arrow-up-fill": "\f347",
+ "file-arrow-up": "\f348",
+ "file-bar-graph-fill": "\f349",
+ "file-bar-graph": "\f34a",
+ "file-binary-fill": "\f34b",
+ "file-binary": "\f34c",
+ "file-break-fill": "\f34d",
+ "file-break": "\f34e",
+ "file-check-fill": "\f34f",
+ "file-check": "\f350",
+ "file-code-fill": "\f351",
+ "file-code": "\f352",
+ "file-diff-fill": "\f353",
+ "file-diff": "\f354",
+ "file-earmark-arrow-down-fill": "\f355",
+ "file-earmark-arrow-down": "\f356",
+ "file-earmark-arrow-up-fill": "\f357",
+ "file-earmark-arrow-up": "\f358",
+ "file-earmark-bar-graph-fill": "\f359",
+ "file-earmark-bar-graph": "\f35a",
+ "file-earmark-binary-fill": "\f35b",
+ "file-earmark-binary": "\f35c",
+ "file-earmark-break-fill": "\f35d",
+ "file-earmark-break": "\f35e",
+ "file-earmark-check-fill": "\f35f",
+ "file-earmark-check": "\f360",
+ "file-earmark-code-fill": "\f361",
+ "file-earmark-code": "\f362",
+ "file-earmark-diff-fill": "\f363",
+ "file-earmark-diff": "\f364",
+ "file-earmark-easel-fill": "\f365",
+ "file-earmark-easel": "\f366",
+ "file-earmark-excel-fill": "\f367",
+ "file-earmark-excel": "\f368",
+ "file-earmark-fill": "\f369",
+ "file-earmark-font-fill": "\f36a",
+ "file-earmark-font": "\f36b",
+ "file-earmark-image-fill": "\f36c",
+ "file-earmark-image": "\f36d",
+ "file-earmark-lock-fill": "\f36e",
+ "file-earmark-lock": "\f36f",
+ "file-earmark-lock2-fill": "\f370",
+ "file-earmark-lock2": "\f371",
+ "file-earmark-medical-fill": "\f372",
+ "file-earmark-medical": "\f373",
+ "file-earmark-minus-fill": "\f374",
+ "file-earmark-minus": "\f375",
+ "file-earmark-music-fill": "\f376",
+ "file-earmark-music": "\f377",
+ "file-earmark-person-fill": "\f378",
+ "file-earmark-person": "\f379",
+ "file-earmark-play-fill": "\f37a",
+ "file-earmark-play": "\f37b",
+ "file-earmark-plus-fill": "\f37c",
+ "file-earmark-plus": "\f37d",
+ "file-earmark-post-fill": "\f37e",
+ "file-earmark-post": "\f37f",
+ "file-earmark-ppt-fill": "\f380",
+ "file-earmark-ppt": "\f381",
+ "file-earmark-richtext-fill": "\f382",
+ "file-earmark-richtext": "\f383",
+ "file-earmark-ruled-fill": "\f384",
+ "file-earmark-ruled": "\f385",
+ "file-earmark-slides-fill": "\f386",
+ "file-earmark-slides": "\f387",
+ "file-earmark-spreadsheet-fill": "\f388",
+ "file-earmark-spreadsheet": "\f389",
+ "file-earmark-text-fill": "\f38a",
+ "file-earmark-text": "\f38b",
+ "file-earmark-word-fill": "\f38c",
+ "file-earmark-word": "\f38d",
+ "file-earmark-x-fill": "\f38e",
+ "file-earmark-x": "\f38f",
+ "file-earmark-zip-fill": "\f390",
+ "file-earmark-zip": "\f391",
+ "file-earmark": "\f392",
+ "file-easel-fill": "\f393",
+ "file-easel": "\f394",
+ "file-excel-fill": "\f395",
+ "file-excel": "\f396",
+ "file-fill": "\f397",
+ "file-font-fill": "\f398",
+ "file-font": "\f399",
+ "file-image-fill": "\f39a",
+ "file-image": "\f39b",
+ "file-lock-fill": "\f39c",
+ "file-lock": "\f39d",
+ "file-lock2-fill": "\f39e",
+ "file-lock2": "\f39f",
+ "file-medical-fill": "\f3a0",
+ "file-medical": "\f3a1",
+ "file-minus-fill": "\f3a2",
+ "file-minus": "\f3a3",
+ "file-music-fill": "\f3a4",
+ "file-music": "\f3a5",
+ "file-person-fill": "\f3a6",
+ "file-person": "\f3a7",
+ "file-play-fill": "\f3a8",
+ "file-play": "\f3a9",
+ "file-plus-fill": "\f3aa",
+ "file-plus": "\f3ab",
+ "file-post-fill": "\f3ac",
+ "file-post": "\f3ad",
+ "file-ppt-fill": "\f3ae",
+ "file-ppt": "\f3af",
+ "file-richtext-fill": "\f3b0",
+ "file-richtext": "\f3b1",
+ "file-ruled-fill": "\f3b2",
+ "file-ruled": "\f3b3",
+ "file-slides-fill": "\f3b4",
+ "file-slides": "\f3b5",
+ "file-spreadsheet-fill": "\f3b6",
+ "file-spreadsheet": "\f3b7",
+ "file-text-fill": "\f3b8",
+ "file-text": "\f3b9",
+ "file-word-fill": "\f3ba",
+ "file-word": "\f3bb",
+ "file-x-fill": "\f3bc",
+ "file-x": "\f3bd",
+ "file-zip-fill": "\f3be",
+ "file-zip": "\f3bf",
+ "file": "\f3c0",
+ "files-alt": "\f3c1",
+ "files": "\f3c2",
+ "film": "\f3c3",
+ "filter-circle-fill": "\f3c4",
+ "filter-circle": "\f3c5",
+ "filter-left": "\f3c6",
+ "filter-right": "\f3c7",
+ "filter-square-fill": "\f3c8",
+ "filter-square": "\f3c9",
+ "filter": "\f3ca",
+ "flag-fill": "\f3cb",
+ "flag": "\f3cc",
+ "flower1": "\f3cd",
+ "flower2": "\f3ce",
+ "flower3": "\f3cf",
+ "folder-check": "\f3d0",
+ "folder-fill": "\f3d1",
+ "folder-minus": "\f3d2",
+ "folder-plus": "\f3d3",
+ "folder-symlink-fill": "\f3d4",
+ "folder-symlink": "\f3d5",
+ "folder-x": "\f3d6",
+ "folder": "\f3d7",
+ "folder2-open": "\f3d8",
+ "folder2": "\f3d9",
+ "fonts": "\f3da",
+ "forward-fill": "\f3db",
+ "forward": "\f3dc",
+ "front": "\f3dd",
+ "fullscreen-exit": "\f3de",
+ "fullscreen": "\f3df",
+ "funnel-fill": "\f3e0",
+ "funnel": "\f3e1",
+ "gear-fill": "\f3e2",
+ "gear-wide-connected": "\f3e3",
+ "gear-wide": "\f3e4",
+ "gear": "\f3e5",
+ "gem": "\f3e6",
+ "geo-alt-fill": "\f3e7",
+ "geo-alt": "\f3e8",
+ "geo-fill": "\f3e9",
+ "geo": "\f3ea",
+ "gift-fill": "\f3eb",
+ "gift": "\f3ec",
+ "github": "\f3ed",
+ "globe": "\f3ee",
+ "globe2": "\f3ef",
+ "google": "\f3f0",
+ "graph-down": "\f3f1",
+ "graph-up": "\f3f2",
+ "grid-1x2-fill": "\f3f3",
+ "grid-1x2": "\f3f4",
+ "grid-3x2-gap-fill": "\f3f5",
+ "grid-3x2-gap": "\f3f6",
+ "grid-3x2": "\f3f7",
+ "grid-3x3-gap-fill": "\f3f8",
+ "grid-3x3-gap": "\f3f9",
+ "grid-3x3": "\f3fa",
+ "grid-fill": "\f3fb",
+ "grid": "\f3fc",
+ "grip-horizontal": "\f3fd",
+ "grip-vertical": "\f3fe",
+ "hammer": "\f3ff",
+ "hand-index-fill": "\f400",
+ "hand-index-thumb-fill": "\f401",
+ "hand-index-thumb": "\f402",
+ "hand-index": "\f403",
+ "hand-thumbs-down-fill": "\f404",
+ "hand-thumbs-down": "\f405",
+ "hand-thumbs-up-fill": "\f406",
+ "hand-thumbs-up": "\f407",
+ "handbag-fill": "\f408",
+ "handbag": "\f409",
+ "hash": "\f40a",
+ "hdd-fill": "\f40b",
+ "hdd-network-fill": "\f40c",
+ "hdd-network": "\f40d",
+ "hdd-rack-fill": "\f40e",
+ "hdd-rack": "\f40f",
+ "hdd-stack-fill": "\f410",
+ "hdd-stack": "\f411",
+ "hdd": "\f412",
+ "headphones": "\f413",
+ "headset": "\f414",
+ "heart-fill": "\f415",
+ "heart-half": "\f416",
+ "heart": "\f417",
+ "heptagon-fill": "\f418",
+ "heptagon-half": "\f419",
+ "heptagon": "\f41a",
+ "hexagon-fill": "\f41b",
+ "hexagon-half": "\f41c",
+ "hexagon": "\f41d",
+ "hourglass-bottom": "\f41e",
+ "hourglass-split": "\f41f",
+ "hourglass-top": "\f420",
+ "hourglass": "\f421",
+ "house-door-fill": "\f422",
+ "house-door": "\f423",
+ "house-fill": "\f424",
+ "house": "\f425",
+ "hr": "\f426",
+ "hurricane": "\f427",
+ "image-alt": "\f428",
+ "image-fill": "\f429",
+ "image": "\f42a",
+ "images": "\f42b",
+ "inbox-fill": "\f42c",
+ "inbox": "\f42d",
+ "inboxes-fill": "\f42e",
+ "inboxes": "\f42f",
+ "info-circle-fill": "\f430",
+ "info-circle": "\f431",
+ "info-square-fill": "\f432",
+ "info-square": "\f433",
+ "info": "\f434",
+ "input-cursor-text": "\f435",
+ "input-cursor": "\f436",
+ "instagram": "\f437",
+ "intersect": "\f438",
+ "journal-album": "\f439",
+ "journal-arrow-down": "\f43a",
+ "journal-arrow-up": "\f43b",
+ "journal-bookmark-fill": "\f43c",
+ "journal-bookmark": "\f43d",
+ "journal-check": "\f43e",
+ "journal-code": "\f43f",
+ "journal-medical": "\f440",
+ "journal-minus": "\f441",
+ "journal-plus": "\f442",
+ "journal-richtext": "\f443",
+ "journal-text": "\f444",
+ "journal-x": "\f445",
+ "journal": "\f446",
+ "journals": "\f447",
+ "joystick": "\f448",
+ "justify-left": "\f449",
+ "justify-right": "\f44a",
+ "justify": "\f44b",
+ "kanban-fill": "\f44c",
+ "kanban": "\f44d",
+ "key-fill": "\f44e",
+ "key": "\f44f",
+ "keyboard-fill": "\f450",
+ "keyboard": "\f451",
+ "ladder": "\f452",
+ "lamp-fill": "\f453",
+ "lamp": "\f454",
+ "laptop-fill": "\f455",
+ "laptop": "\f456",
+ "layer-backward": "\f457",
+ "layer-forward": "\f458",
+ "layers-fill": "\f459",
+ "layers-half": "\f45a",
+ "layers": "\f45b",
+ "layout-sidebar-inset-reverse": "\f45c",
+ "layout-sidebar-inset": "\f45d",
+ "layout-sidebar-reverse": "\f45e",
+ "layout-sidebar": "\f45f",
+ "layout-split": "\f460",
+ "layout-text-sidebar-reverse": "\f461",
+ "layout-text-sidebar": "\f462",
+ "layout-text-window-reverse": "\f463",
+ "layout-text-window": "\f464",
+ "layout-three-columns": "\f465",
+ "layout-wtf": "\f466",
+ "life-preserver": "\f467",
+ "lightbulb-fill": "\f468",
+ "lightbulb-off-fill": "\f469",
+ "lightbulb-off": "\f46a",
+ "lightbulb": "\f46b",
+ "lightning-charge-fill": "\f46c",
+ "lightning-charge": "\f46d",
+ "lightning-fill": "\f46e",
+ "lightning": "\f46f",
+ "link-45deg": "\f470",
+ "link": "\f471",
+ "linkedin": "\f472",
+ "list-check": "\f473",
+ "list-nested": "\f474",
+ "list-ol": "\f475",
+ "list-stars": "\f476",
+ "list-task": "\f477",
+ "list-ul": "\f478",
+ "list": "\f479",
+ "lock-fill": "\f47a",
+ "lock": "\f47b",
+ "mailbox": "\f47c",
+ "mailbox2": "\f47d",
+ "map-fill": "\f47e",
+ "map": "\f47f",
+ "markdown-fill": "\f480",
+ "markdown": "\f481",
+ "mask": "\f482",
+ "megaphone-fill": "\f483",
+ "megaphone": "\f484",
+ "menu-app-fill": "\f485",
+ "menu-app": "\f486",
+ "menu-button-fill": "\f487",
+ "menu-button-wide-fill": "\f488",
+ "menu-button-wide": "\f489",
+ "menu-button": "\f48a",
+ "menu-down": "\f48b",
+ "menu-up": "\f48c",
+ "mic-fill": "\f48d",
+ "mic-mute-fill": "\f48e",
+ "mic-mute": "\f48f",
+ "mic": "\f490",
+ "minecart-loaded": "\f491",
+ "minecart": "\f492",
+ "moisture": "\f493",
+ "moon-fill": "\f494",
+ "moon-stars-fill": "\f495",
+ "moon-stars": "\f496",
+ "moon": "\f497",
+ "mouse-fill": "\f498",
+ "mouse": "\f499",
+ "mouse2-fill": "\f49a",
+ "mouse2": "\f49b",
+ "mouse3-fill": "\f49c",
+ "mouse3": "\f49d",
+ "music-note-beamed": "\f49e",
+ "music-note-list": "\f49f",
+ "music-note": "\f4a0",
+ "music-player-fill": "\f4a1",
+ "music-player": "\f4a2",
+ "newspaper": "\f4a3",
+ "node-minus-fill": "\f4a4",
+ "node-minus": "\f4a5",
+ "node-plus-fill": "\f4a6",
+ "node-plus": "\f4a7",
+ "nut-fill": "\f4a8",
+ "nut": "\f4a9",
+ "octagon-fill": "\f4aa",
+ "octagon-half": "\f4ab",
+ "octagon": "\f4ac",
+ "option": "\f4ad",
+ "outlet": "\f4ae",
+ "paint-bucket": "\f4af",
+ "palette-fill": "\f4b0",
+ "palette": "\f4b1",
+ "palette2": "\f4b2",
+ "paperclip": "\f4b3",
+ "paragraph": "\f4b4",
+ "patch-check-fill": "\f4b5",
+ "patch-check": "\f4b6",
+ "patch-exclamation-fill": "\f4b7",
+ "patch-exclamation": "\f4b8",
+ "patch-minus-fill": "\f4b9",
+ "patch-minus": "\f4ba",
+ "patch-plus-fill": "\f4bb",
+ "patch-plus": "\f4bc",
+ "patch-question-fill": "\f4bd",
+ "patch-question": "\f4be",
+ "pause-btn-fill": "\f4bf",
+ "pause-btn": "\f4c0",
+ "pause-circle-fill": "\f4c1",
+ "pause-circle": "\f4c2",
+ "pause-fill": "\f4c3",
+ "pause": "\f4c4",
+ "peace-fill": "\f4c5",
+ "peace": "\f4c6",
+ "pen-fill": "\f4c7",
+ "pen": "\f4c8",
+ "pencil-fill": "\f4c9",
+ "pencil-square": "\f4ca",
+ "pencil": "\f4cb",
+ "pentagon-fill": "\f4cc",
+ "pentagon-half": "\f4cd",
+ "pentagon": "\f4ce",
+ "people-fill": "\f4cf",
+ "people": "\f4d0",
+ "percent": "\f4d1",
+ "person-badge-fill": "\f4d2",
+ "person-badge": "\f4d3",
+ "person-bounding-box": "\f4d4",
+ "person-check-fill": "\f4d5",
+ "person-check": "\f4d6",
+ "person-circle": "\f4d7",
+ "person-dash-fill": "\f4d8",
+ "person-dash": "\f4d9",
+ "person-fill": "\f4da",
+ "person-lines-fill": "\f4db",
+ "person-plus-fill": "\f4dc",
+ "person-plus": "\f4dd",
+ "person-square": "\f4de",
+ "person-x-fill": "\f4df",
+ "person-x": "\f4e0",
+ "person": "\f4e1",
+ "phone-fill": "\f4e2",
+ "phone-landscape-fill": "\f4e3",
+ "phone-landscape": "\f4e4",
+ "phone-vibrate-fill": "\f4e5",
+ "phone-vibrate": "\f4e6",
+ "phone": "\f4e7",
+ "pie-chart-fill": "\f4e8",
+ "pie-chart": "\f4e9",
+ "pin-angle-fill": "\f4ea",
+ "pin-angle": "\f4eb",
+ "pin-fill": "\f4ec",
+ "pin": "\f4ed",
+ "pip-fill": "\f4ee",
+ "pip": "\f4ef",
+ "play-btn-fill": "\f4f0",
+ "play-btn": "\f4f1",
+ "play-circle-fill": "\f4f2",
+ "play-circle": "\f4f3",
+ "play-fill": "\f4f4",
+ "play": "\f4f5",
+ "plug-fill": "\f4f6",
+ "plug": "\f4f7",
+ "plus-circle-dotted": "\f4f8",
+ "plus-circle-fill": "\f4f9",
+ "plus-circle": "\f4fa",
+ "plus-square-dotted": "\f4fb",
+ "plus-square-fill": "\f4fc",
+ "plus-square": "\f4fd",
+ "plus": "\f4fe",
+ "power": "\f4ff",
+ "printer-fill": "\f500",
+ "printer": "\f501",
+ "puzzle-fill": "\f502",
+ "puzzle": "\f503",
+ "question-circle-fill": "\f504",
+ "question-circle": "\f505",
+ "question-diamond-fill": "\f506",
+ "question-diamond": "\f507",
+ "question-octagon-fill": "\f508",
+ "question-octagon": "\f509",
+ "question-square-fill": "\f50a",
+ "question-square": "\f50b",
+ "question": "\f50c",
+ "rainbow": "\f50d",
+ "receipt-cutoff": "\f50e",
+ "receipt": "\f50f",
+ "reception-0": "\f510",
+ "reception-1": "\f511",
+ "reception-2": "\f512",
+ "reception-3": "\f513",
+ "reception-4": "\f514",
+ "record-btn-fill": "\f515",
+ "record-btn": "\f516",
+ "record-circle-fill": "\f517",
+ "record-circle": "\f518",
+ "record-fill": "\f519",
+ "record": "\f51a",
+ "record2-fill": "\f51b",
+ "record2": "\f51c",
+ "reply-all-fill": "\f51d",
+ "reply-all": "\f51e",
+ "reply-fill": "\f51f",
+ "reply": "\f520",
+ "rss-fill": "\f521",
+ "rss": "\f522",
+ "rulers": "\f523",
+ "save-fill": "\f524",
+ "save": "\f525",
+ "save2-fill": "\f526",
+ "save2": "\f527",
+ "scissors": "\f528",
+ "screwdriver": "\f529",
+ "search": "\f52a",
+ "segmented-nav": "\f52b",
+ "server": "\f52c",
+ "share-fill": "\f52d",
+ "share": "\f52e",
+ "shield-check": "\f52f",
+ "shield-exclamation": "\f530",
+ "shield-fill-check": "\f531",
+ "shield-fill-exclamation": "\f532",
+ "shield-fill-minus": "\f533",
+ "shield-fill-plus": "\f534",
+ "shield-fill-x": "\f535",
+ "shield-fill": "\f536",
+ "shield-lock-fill": "\f537",
+ "shield-lock": "\f538",
+ "shield-minus": "\f539",
+ "shield-plus": "\f53a",
+ "shield-shaded": "\f53b",
+ "shield-slash-fill": "\f53c",
+ "shield-slash": "\f53d",
+ "shield-x": "\f53e",
+ "shield": "\f53f",
+ "shift-fill": "\f540",
+ "shift": "\f541",
+ "shop-window": "\f542",
+ "shop": "\f543",
+ "shuffle": "\f544",
+ "signpost-2-fill": "\f545",
+ "signpost-2": "\f546",
+ "signpost-fill": "\f547",
+ "signpost-split-fill": "\f548",
+ "signpost-split": "\f549",
+ "signpost": "\f54a",
+ "sim-fill": "\f54b",
+ "sim": "\f54c",
+ "skip-backward-btn-fill": "\f54d",
+ "skip-backward-btn": "\f54e",
+ "skip-backward-circle-fill": "\f54f",
+ "skip-backward-circle": "\f550",
+ "skip-backward-fill": "\f551",
+ "skip-backward": "\f552",
+ "skip-end-btn-fill": "\f553",
+ "skip-end-btn": "\f554",
+ "skip-end-circle-fill": "\f555",
+ "skip-end-circle": "\f556",
+ "skip-end-fill": "\f557",
+ "skip-end": "\f558",
+ "skip-forward-btn-fill": "\f559",
+ "skip-forward-btn": "\f55a",
+ "skip-forward-circle-fill": "\f55b",
+ "skip-forward-circle": "\f55c",
+ "skip-forward-fill": "\f55d",
+ "skip-forward": "\f55e",
+ "skip-start-btn-fill": "\f55f",
+ "skip-start-btn": "\f560",
+ "skip-start-circle-fill": "\f561",
+ "skip-start-circle": "\f562",
+ "skip-start-fill": "\f563",
+ "skip-start": "\f564",
+ "slack": "\f565",
+ "slash-circle-fill": "\f566",
+ "slash-circle": "\f567",
+ "slash-square-fill": "\f568",
+ "slash-square": "\f569",
+ "slash": "\f56a",
+ "sliders": "\f56b",
+ "smartwatch": "\f56c",
+ "snow": "\f56d",
+ "snow2": "\f56e",
+ "snow3": "\f56f",
+ "sort-alpha-down-alt": "\f570",
+ "sort-alpha-down": "\f571",
+ "sort-alpha-up-alt": "\f572",
+ "sort-alpha-up": "\f573",
+ "sort-down-alt": "\f574",
+ "sort-down": "\f575",
+ "sort-numeric-down-alt": "\f576",
+ "sort-numeric-down": "\f577",
+ "sort-numeric-up-alt": "\f578",
+ "sort-numeric-up": "\f579",
+ "sort-up-alt": "\f57a",
+ "sort-up": "\f57b",
+ "soundwave": "\f57c",
+ "speaker-fill": "\f57d",
+ "speaker": "\f57e",
+ "speedometer": "\f57f",
+ "speedometer2": "\f580",
+ "spellcheck": "\f581",
+ "square-fill": "\f582",
+ "square-half": "\f583",
+ "square": "\f584",
+ "stack": "\f585",
+ "star-fill": "\f586",
+ "star-half": "\f587",
+ "star": "\f588",
+ "stars": "\f589",
+ "stickies-fill": "\f58a",
+ "stickies": "\f58b",
+ "sticky-fill": "\f58c",
+ "sticky": "\f58d",
+ "stop-btn-fill": "\f58e",
+ "stop-btn": "\f58f",
+ "stop-circle-fill": "\f590",
+ "stop-circle": "\f591",
+ "stop-fill": "\f592",
+ "stop": "\f593",
+ "stoplights-fill": "\f594",
+ "stoplights": "\f595",
+ "stopwatch-fill": "\f596",
+ "stopwatch": "\f597",
+ "subtract": "\f598",
+ "suit-club-fill": "\f599",
+ "suit-club": "\f59a",
+ "suit-diamond-fill": "\f59b",
+ "suit-diamond": "\f59c",
+ "suit-heart-fill": "\f59d",
+ "suit-heart": "\f59e",
+ "suit-spade-fill": "\f59f",
+ "suit-spade": "\f5a0",
+ "sun-fill": "\f5a1",
+ "sun": "\f5a2",
+ "sunglasses": "\f5a3",
+ "sunrise-fill": "\f5a4",
+ "sunrise": "\f5a5",
+ "sunset-fill": "\f5a6",
+ "sunset": "\f5a7",
+ "symmetry-horizontal": "\f5a8",
+ "symmetry-vertical": "\f5a9",
+ "table": "\f5aa",
+ "tablet-fill": "\f5ab",
+ "tablet-landscape-fill": "\f5ac",
+ "tablet-landscape": "\f5ad",
+ "tablet": "\f5ae",
+ "tag-fill": "\f5af",
+ "tag": "\f5b0",
+ "tags-fill": "\f5b1",
+ "tags": "\f5b2",
+ "telegram": "\f5b3",
+ "telephone-fill": "\f5b4",
+ "telephone-forward-fill": "\f5b5",
+ "telephone-forward": "\f5b6",
+ "telephone-inbound-fill": "\f5b7",
+ "telephone-inbound": "\f5b8",
+ "telephone-minus-fill": "\f5b9",
+ "telephone-minus": "\f5ba",
+ "telephone-outbound-fill": "\f5bb",
+ "telephone-outbound": "\f5bc",
+ "telephone-plus-fill": "\f5bd",
+ "telephone-plus": "\f5be",
+ "telephone-x-fill": "\f5bf",
+ "telephone-x": "\f5c0",
+ "telephone": "\f5c1",
+ "terminal-fill": "\f5c2",
+ "terminal": "\f5c3",
+ "text-center": "\f5c4",
+ "text-indent-left": "\f5c5",
+ "text-indent-right": "\f5c6",
+ "text-left": "\f5c7",
+ "text-paragraph": "\f5c8",
+ "text-right": "\f5c9",
+ "textarea-resize": "\f5ca",
+ "textarea-t": "\f5cb",
+ "textarea": "\f5cc",
+ "thermometer-half": "\f5cd",
+ "thermometer-high": "\f5ce",
+ "thermometer-low": "\f5cf",
+ "thermometer-snow": "\f5d0",
+ "thermometer-sun": "\f5d1",
+ "thermometer": "\f5d2",
+ "three-dots-vertical": "\f5d3",
+ "three-dots": "\f5d4",
+ "toggle-off": "\f5d5",
+ "toggle-on": "\f5d6",
+ "toggle2-off": "\f5d7",
+ "toggle2-on": "\f5d8",
+ "toggles": "\f5d9",
+ "toggles2": "\f5da",
+ "tools": "\f5db",
+ "tornado": "\f5dc",
+ "trash-fill": "\f5dd",
+ "trash": "\f5de",
+ "trash2-fill": "\f5df",
+ "trash2": "\f5e0",
+ "tree-fill": "\f5e1",
+ "tree": "\f5e2",
+ "triangle-fill": "\f5e3",
+ "triangle-half": "\f5e4",
+ "triangle": "\f5e5",
+ "trophy-fill": "\f5e6",
+ "trophy": "\f5e7",
+ "tropical-storm": "\f5e8",
+ "truck-flatbed": "\f5e9",
+ "truck": "\f5ea",
+ "tsunami": "\f5eb",
+ "tv-fill": "\f5ec",
+ "tv": "\f5ed",
+ "twitch": "\f5ee",
+ "twitter": "\f5ef",
+ "type-bold": "\f5f0",
+ "type-h1": "\f5f1",
+ "type-h2": "\f5f2",
+ "type-h3": "\f5f3",
+ "type-italic": "\f5f4",
+ "type-strikethrough": "\f5f5",
+ "type-underline": "\f5f6",
+ "type": "\f5f7",
+ "ui-checks-grid": "\f5f8",
+ "ui-checks": "\f5f9",
+ "ui-radios-grid": "\f5fa",
+ "ui-radios": "\f5fb",
+ "umbrella-fill": "\f5fc",
+ "umbrella": "\f5fd",
+ "union": "\f5fe",
+ "unlock-fill": "\f5ff",
+ "unlock": "\f600",
+ "upc-scan": "\f601",
+ "upc": "\f602",
+ "upload": "\f603",
+ "vector-pen": "\f604",
+ "view-list": "\f605",
+ "view-stacked": "\f606",
+ "vinyl-fill": "\f607",
+ "vinyl": "\f608",
+ "voicemail": "\f609",
+ "volume-down-fill": "\f60a",
+ "volume-down": "\f60b",
+ "volume-mute-fill": "\f60c",
+ "volume-mute": "\f60d",
+ "volume-off-fill": "\f60e",
+ "volume-off": "\f60f",
+ "volume-up-fill": "\f610",
+ "volume-up": "\f611",
+ "vr": "\f612",
+ "wallet-fill": "\f613",
+ "wallet": "\f614",
+ "wallet2": "\f615",
+ "watch": "\f616",
+ "water": "\f617",
+ "whatsapp": "\f618",
+ "wifi-1": "\f619",
+ "wifi-2": "\f61a",
+ "wifi-off": "\f61b",
+ "wifi": "\f61c",
+ "wind": "\f61d",
+ "window-dock": "\f61e",
+ "window-sidebar": "\f61f",
+ "window": "\f620",
+ "wrench": "\f621",
+ "x-circle-fill": "\f622",
+ "x-circle": "\f623",
+ "x-diamond-fill": "\f624",
+ "x-diamond": "\f625",
+ "x-octagon-fill": "\f626",
+ "x-octagon": "\f627",
+ "x-square-fill": "\f628",
+ "x-square": "\f629",
+ "x": "\f62a",
+ "youtube": "\f62b",
+ "zoom-in": "\f62c",
+ "zoom-out": "\f62d",
+ "bank": "\f62e",
+ "bank2": "\f62f",
+ "bell-slash-fill": "\f630",
+ "bell-slash": "\f631",
+ "cash-coin": "\f632",
+ "check-lg": "\f633",
+ "coin": "\f634",
+ "currency-bitcoin": "\f635",
+ "currency-dollar": "\f636",
+ "currency-euro": "\f637",
+ "currency-exchange": "\f638",
+ "currency-pound": "\f639",
+ "currency-yen": "\f63a",
+ "dash-lg": "\f63b",
+ "exclamation-lg": "\f63c",
+ "file-earmark-pdf-fill": "\f63d",
+ "file-earmark-pdf": "\f63e",
+ "file-pdf-fill": "\f63f",
+ "file-pdf": "\f640",
+ "gender-ambiguous": "\f641",
+ "gender-female": "\f642",
+ "gender-male": "\f643",
+ "gender-trans": "\f644",
+ "headset-vr": "\f645",
+ "info-lg": "\f646",
+ "mastodon": "\f647",
+ "messenger": "\f648",
+ "piggy-bank-fill": "\f649",
+ "piggy-bank": "\f64a",
+ "pin-map-fill": "\f64b",
+ "pin-map": "\f64c",
+ "plus-lg": "\f64d",
+ "question-lg": "\f64e",
+ "recycle": "\f64f",
+ "reddit": "\f650",
+ "safe-fill": "\f651",
+ "safe2-fill": "\f652",
+ "safe2": "\f653",
+ "sd-card-fill": "\f654",
+ "sd-card": "\f655",
+ "skype": "\f656",
+ "slash-lg": "\f657",
+ "translate": "\f658",
+ "x-lg": "\f659",
+ "safe": "\f65a",
+ "apple": "\f65b",
+ "microsoft": "\f65d",
+ "windows": "\f65e",
+ "behance": "\f65c",
+ "dribbble": "\f65f",
+ "line": "\f660",
+ "medium": "\f661",
+ "paypal": "\f662",
+ "pinterest": "\f663",
+ "signal": "\f664",
+ "snapchat": "\f665",
+ "spotify": "\f666",
+ "stack-overflow": "\f667",
+ "strava": "\f668",
+ "wordpress": "\f669",
+ "vimeo": "\f66a",
+ "activity": "\f66b",
+ "easel2-fill": "\f66c",
+ "easel2": "\f66d",
+ "easel3-fill": "\f66e",
+ "easel3": "\f66f",
+ "fan": "\f670",
+ "fingerprint": "\f671",
+ "graph-down-arrow": "\f672",
+ "graph-up-arrow": "\f673",
+ "hypnotize": "\f674",
+ "magic": "\f675",
+ "person-rolodex": "\f676",
+ "person-video": "\f677",
+ "person-video2": "\f678",
+ "person-video3": "\f679",
+ "person-workspace": "\f67a",
+ "radioactive": "\f67b",
+ "webcam-fill": "\f67c",
+ "webcam": "\f67d",
+ "yin-yang": "\f67e",
+ "bandaid-fill": "\f680",
+ "bandaid": "\f681",
+ "bluetooth": "\f682",
+ "body-text": "\f683",
+ "boombox": "\f684",
+ "boxes": "\f685",
+ "dpad-fill": "\f686",
+ "dpad": "\f687",
+ "ear-fill": "\f688",
+ "ear": "\f689",
+ "envelope-check-fill": "\f68b",
+ "envelope-check": "\f68c",
+ "envelope-dash-fill": "\f68e",
+ "envelope-dash": "\f68f",
+ "envelope-exclamation-fill": "\f691",
+ "envelope-exclamation": "\f692",
+ "envelope-plus-fill": "\f693",
+ "envelope-plus": "\f694",
+ "envelope-slash-fill": "\f696",
+ "envelope-slash": "\f697",
+ "envelope-x-fill": "\f699",
+ "envelope-x": "\f69a",
+ "explicit-fill": "\f69b",
+ "explicit": "\f69c",
+ "git": "\f69d",
+ "infinity": "\f69e",
+ "list-columns-reverse": "\f69f",
+ "list-columns": "\f6a0",
+ "meta": "\f6a1",
+ "nintendo-switch": "\f6a4",
+ "pc-display-horizontal": "\f6a5",
+ "pc-display": "\f6a6",
+ "pc-horizontal": "\f6a7",
+ "pc": "\f6a8",
+ "playstation": "\f6a9",
+ "plus-slash-minus": "\f6aa",
+ "projector-fill": "\f6ab",
+ "projector": "\f6ac",
+ "qr-code-scan": "\f6ad",
+ "qr-code": "\f6ae",
+ "quora": "\f6af",
+ "quote": "\f6b0",
+ "robot": "\f6b1",
+ "send-check-fill": "\f6b2",
+ "send-check": "\f6b3",
+ "send-dash-fill": "\f6b4",
+ "send-dash": "\f6b5",
+ "send-exclamation-fill": "\f6b7",
+ "send-exclamation": "\f6b8",
+ "send-fill": "\f6b9",
+ "send-plus-fill": "\f6ba",
+ "send-plus": "\f6bb",
+ "send-slash-fill": "\f6bc",
+ "send-slash": "\f6bd",
+ "send-x-fill": "\f6be",
+ "send-x": "\f6bf",
+ "send": "\f6c0",
+ "steam": "\f6c1",
+ "terminal-dash": "\f6c3",
+ "terminal-plus": "\f6c4",
+ "terminal-split": "\f6c5",
+ "ticket-detailed-fill": "\f6c6",
+ "ticket-detailed": "\f6c7",
+ "ticket-fill": "\f6c8",
+ "ticket-perforated-fill": "\f6c9",
+ "ticket-perforated": "\f6ca",
+ "ticket": "\f6cb",
+ "tiktok": "\f6cc",
+ "window-dash": "\f6cd",
+ "window-desktop": "\f6ce",
+ "window-fullscreen": "\f6cf",
+ "window-plus": "\f6d0",
+ "window-split": "\f6d1",
+ "window-stack": "\f6d2",
+ "window-x": "\f6d3",
+ "xbox": "\f6d4",
+ "ethernet": "\f6d5",
+ "hdmi-fill": "\f6d6",
+ "hdmi": "\f6d7",
+ "usb-c-fill": "\f6d8",
+ "usb-c": "\f6d9",
+ "usb-fill": "\f6da",
+ "usb-plug-fill": "\f6db",
+ "usb-plug": "\f6dc",
+ "usb-symbol": "\f6dd",
+ "usb": "\f6de",
+ "boombox-fill": "\f6df",
+ "displayport": "\f6e1",
+ "gpu-card": "\f6e2",
+ "memory": "\f6e3",
+ "modem-fill": "\f6e4",
+ "modem": "\f6e5",
+ "motherboard-fill": "\f6e6",
+ "motherboard": "\f6e7",
+ "optical-audio-fill": "\f6e8",
+ "optical-audio": "\f6e9",
+ "pci-card": "\f6ea",
+ "router-fill": "\f6eb",
+ "router": "\f6ec",
+ "thunderbolt-fill": "\f6ef",
+ "thunderbolt": "\f6f0",
+ "usb-drive-fill": "\f6f1",
+ "usb-drive": "\f6f2",
+ "usb-micro-fill": "\f6f3",
+ "usb-micro": "\f6f4",
+ "usb-mini-fill": "\f6f5",
+ "usb-mini": "\f6f6",
+ "cloud-haze2": "\f6f7",
+ "device-hdd-fill": "\f6f8",
+ "device-hdd": "\f6f9",
+ "device-ssd-fill": "\f6fa",
+ "device-ssd": "\f6fb",
+ "displayport-fill": "\f6fc",
+ "mortarboard-fill": "\f6fd",
+ "mortarboard": "\f6fe",
+ "terminal-x": "\f6ff",
+ "arrow-through-heart-fill": "\f700",
+ "arrow-through-heart": "\f701",
+ "badge-sd-fill": "\f702",
+ "badge-sd": "\f703",
+ "bag-heart-fill": "\f704",
+ "bag-heart": "\f705",
+ "balloon-fill": "\f706",
+ "balloon-heart-fill": "\f707",
+ "balloon-heart": "\f708",
+ "balloon": "\f709",
+ "box2-fill": "\f70a",
+ "box2-heart-fill": "\f70b",
+ "box2-heart": "\f70c",
+ "box2": "\f70d",
+ "braces-asterisk": "\f70e",
+ "calendar-heart-fill": "\f70f",
+ "calendar-heart": "\f710",
+ "calendar2-heart-fill": "\f711",
+ "calendar2-heart": "\f712",
+ "chat-heart-fill": "\f713",
+ "chat-heart": "\f714",
+ "chat-left-heart-fill": "\f715",
+ "chat-left-heart": "\f716",
+ "chat-right-heart-fill": "\f717",
+ "chat-right-heart": "\f718",
+ "chat-square-heart-fill": "\f719",
+ "chat-square-heart": "\f71a",
+ "clipboard-check-fill": "\f71b",
+ "clipboard-data-fill": "\f71c",
+ "clipboard-fill": "\f71d",
+ "clipboard-heart-fill": "\f71e",
+ "clipboard-heart": "\f71f",
+ "clipboard-minus-fill": "\f720",
+ "clipboard-plus-fill": "\f721",
+ "clipboard-pulse": "\f722",
+ "clipboard-x-fill": "\f723",
+ "clipboard2-check-fill": "\f724",
+ "clipboard2-check": "\f725",
+ "clipboard2-data-fill": "\f726",
+ "clipboard2-data": "\f727",
+ "clipboard2-fill": "\f728",
+ "clipboard2-heart-fill": "\f729",
+ "clipboard2-heart": "\f72a",
+ "clipboard2-minus-fill": "\f72b",
+ "clipboard2-minus": "\f72c",
+ "clipboard2-plus-fill": "\f72d",
+ "clipboard2-plus": "\f72e",
+ "clipboard2-pulse-fill": "\f72f",
+ "clipboard2-pulse": "\f730",
+ "clipboard2-x-fill": "\f731",
+ "clipboard2-x": "\f732",
+ "clipboard2": "\f733",
+ "emoji-kiss-fill": "\f734",
+ "emoji-kiss": "\f735",
+ "envelope-heart-fill": "\f736",
+ "envelope-heart": "\f737",
+ "envelope-open-heart-fill": "\f738",
+ "envelope-open-heart": "\f739",
+ "envelope-paper-fill": "\f73a",
+ "envelope-paper-heart-fill": "\f73b",
+ "envelope-paper-heart": "\f73c",
+ "envelope-paper": "\f73d",
+ "filetype-aac": "\f73e",
+ "filetype-ai": "\f73f",
+ "filetype-bmp": "\f740",
+ "filetype-cs": "\f741",
+ "filetype-css": "\f742",
+ "filetype-csv": "\f743",
+ "filetype-doc": "\f744",
+ "filetype-docx": "\f745",
+ "filetype-exe": "\f746",
+ "filetype-gif": "\f747",
+ "filetype-heic": "\f748",
+ "filetype-html": "\f749",
+ "filetype-java": "\f74a",
+ "filetype-jpg": "\f74b",
+ "filetype-js": "\f74c",
+ "filetype-jsx": "\f74d",
+ "filetype-key": "\f74e",
+ "filetype-m4p": "\f74f",
+ "filetype-md": "\f750",
+ "filetype-mdx": "\f751",
+ "filetype-mov": "\f752",
+ "filetype-mp3": "\f753",
+ "filetype-mp4": "\f754",
+ "filetype-otf": "\f755",
+ "filetype-pdf": "\f756",
+ "filetype-php": "\f757",
+ "filetype-png": "\f758",
+ "filetype-ppt": "\f75a",
+ "filetype-psd": "\f75b",
+ "filetype-py": "\f75c",
+ "filetype-raw": "\f75d",
+ "filetype-rb": "\f75e",
+ "filetype-sass": "\f75f",
+ "filetype-scss": "\f760",
+ "filetype-sh": "\f761",
+ "filetype-svg": "\f762",
+ "filetype-tiff": "\f763",
+ "filetype-tsx": "\f764",
+ "filetype-ttf": "\f765",
+ "filetype-txt": "\f766",
+ "filetype-wav": "\f767",
+ "filetype-woff": "\f768",
+ "filetype-xls": "\f76a",
+ "filetype-xml": "\f76b",
+ "filetype-yml": "\f76c",
+ "heart-arrow": "\f76d",
+ "heart-pulse-fill": "\f76e",
+ "heart-pulse": "\f76f",
+ "heartbreak-fill": "\f770",
+ "heartbreak": "\f771",
+ "hearts": "\f772",
+ "hospital-fill": "\f773",
+ "hospital": "\f774",
+ "house-heart-fill": "\f775",
+ "house-heart": "\f776",
+ "incognito": "\f777",
+ "magnet-fill": "\f778",
+ "magnet": "\f779",
+ "person-heart": "\f77a",
+ "person-hearts": "\f77b",
+ "phone-flip": "\f77c",
+ "plugin": "\f77d",
+ "postage-fill": "\f77e",
+ "postage-heart-fill": "\f77f",
+ "postage-heart": "\f780",
+ "postage": "\f781",
+ "postcard-fill": "\f782",
+ "postcard-heart-fill": "\f783",
+ "postcard-heart": "\f784",
+ "postcard": "\f785",
+ "search-heart-fill": "\f786",
+ "search-heart": "\f787",
+ "sliders2-vertical": "\f788",
+ "sliders2": "\f789",
+ "trash3-fill": "\f78a",
+ "trash3": "\f78b",
+ "valentine": "\f78c",
+ "valentine2": "\f78d",
+ "wrench-adjustable-circle-fill": "\f78e",
+ "wrench-adjustable-circle": "\f78f",
+ "wrench-adjustable": "\f790",
+ "filetype-json": "\f791",
+ "filetype-pptx": "\f792",
+ "filetype-xlsx": "\f793",
+ "1-circle-fill": "\f796",
+ "1-circle": "\f797",
+ "1-square-fill": "\f798",
+ "1-square": "\f799",
+ "2-circle-fill": "\f79c",
+ "2-circle": "\f79d",
+ "2-square-fill": "\f79e",
+ "2-square": "\f79f",
+ "3-circle-fill": "\f7a2",
+ "3-circle": "\f7a3",
+ "3-square-fill": "\f7a4",
+ "3-square": "\f7a5",
+ "4-circle-fill": "\f7a8",
+ "4-circle": "\f7a9",
+ "4-square-fill": "\f7aa",
+ "4-square": "\f7ab",
+ "5-circle-fill": "\f7ae",
+ "5-circle": "\f7af",
+ "5-square-fill": "\f7b0",
+ "5-square": "\f7b1",
+ "6-circle-fill": "\f7b4",
+ "6-circle": "\f7b5",
+ "6-square-fill": "\f7b6",
+ "6-square": "\f7b7",
+ "7-circle-fill": "\f7ba",
+ "7-circle": "\f7bb",
+ "7-square-fill": "\f7bc",
+ "7-square": "\f7bd",
+ "8-circle-fill": "\f7c0",
+ "8-circle": "\f7c1",
+ "8-square-fill": "\f7c2",
+ "8-square": "\f7c3",
+ "9-circle-fill": "\f7c6",
+ "9-circle": "\f7c7",
+ "9-square-fill": "\f7c8",
+ "9-square": "\f7c9",
+ "airplane-engines-fill": "\f7ca",
+ "airplane-engines": "\f7cb",
+ "airplane-fill": "\f7cc",
+ "airplane": "\f7cd",
+ "alexa": "\f7ce",
+ "alipay": "\f7cf",
+ "android": "\f7d0",
+ "android2": "\f7d1",
+ "box-fill": "\f7d2",
+ "box-seam-fill": "\f7d3",
+ "browser-chrome": "\f7d4",
+ "browser-edge": "\f7d5",
+ "browser-firefox": "\f7d6",
+ "browser-safari": "\f7d7",
+ "c-circle-fill": "\f7da",
+ "c-circle": "\f7db",
+ "c-square-fill": "\f7dc",
+ "c-square": "\f7dd",
+ "capsule-pill": "\f7de",
+ "capsule": "\f7df",
+ "car-front-fill": "\f7e0",
+ "car-front": "\f7e1",
+ "cassette-fill": "\f7e2",
+ "cassette": "\f7e3",
+ "cc-circle-fill": "\f7e6",
+ "cc-circle": "\f7e7",
+ "cc-square-fill": "\f7e8",
+ "cc-square": "\f7e9",
+ "cup-hot-fill": "\f7ea",
+ "cup-hot": "\f7eb",
+ "currency-rupee": "\f7ec",
+ "dropbox": "\f7ed",
+ "escape": "\f7ee",
+ "fast-forward-btn-fill": "\f7ef",
+ "fast-forward-btn": "\f7f0",
+ "fast-forward-circle-fill": "\f7f1",
+ "fast-forward-circle": "\f7f2",
+ "fast-forward-fill": "\f7f3",
+ "fast-forward": "\f7f4",
+ "filetype-sql": "\f7f5",
+ "fire": "\f7f6",
+ "google-play": "\f7f7",
+ "h-circle-fill": "\f7fa",
+ "h-circle": "\f7fb",
+ "h-square-fill": "\f7fc",
+ "h-square": "\f7fd",
+ "indent": "\f7fe",
+ "lungs-fill": "\f7ff",
+ "lungs": "\f800",
+ "microsoft-teams": "\f801",
+ "p-circle-fill": "\f804",
+ "p-circle": "\f805",
+ "p-square-fill": "\f806",
+ "p-square": "\f807",
+ "pass-fill": "\f808",
+ "pass": "\f809",
+ "prescription": "\f80a",
+ "prescription2": "\f80b",
+ "r-circle-fill": "\f80e",
+ "r-circle": "\f80f",
+ "r-square-fill": "\f810",
+ "r-square": "\f811",
+ "repeat-1": "\f812",
+ "repeat": "\f813",
+ "rewind-btn-fill": "\f814",
+ "rewind-btn": "\f815",
+ "rewind-circle-fill": "\f816",
+ "rewind-circle": "\f817",
+ "rewind-fill": "\f818",
+ "rewind": "\f819",
+ "train-freight-front-fill": "\f81a",
+ "train-freight-front": "\f81b",
+ "train-front-fill": "\f81c",
+ "train-front": "\f81d",
+ "train-lightrail-front-fill": "\f81e",
+ "train-lightrail-front": "\f81f",
+ "truck-front-fill": "\f820",
+ "truck-front": "\f821",
+ "ubuntu": "\f822",
+ "unindent": "\f823",
+ "unity": "\f824",
+ "universal-access-circle": "\f825",
+ "universal-access": "\f826",
+ "virus": "\f827",
+ "virus2": "\f828",
+ "wechat": "\f829",
+ "yelp": "\f82a",
+ "sign-stop-fill": "\f82b",
+ "sign-stop-lights-fill": "\f82c",
+ "sign-stop-lights": "\f82d",
+ "sign-stop": "\f82e",
+ "sign-turn-left-fill": "\f82f",
+ "sign-turn-left": "\f830",
+ "sign-turn-right-fill": "\f831",
+ "sign-turn-right": "\f832",
+ "sign-turn-slight-left-fill": "\f833",
+ "sign-turn-slight-left": "\f834",
+ "sign-turn-slight-right-fill": "\f835",
+ "sign-turn-slight-right": "\f836",
+ "sign-yield-fill": "\f837",
+ "sign-yield": "\f838",
+ "ev-station-fill": "\f839",
+ "ev-station": "\f83a",
+ "fuel-pump-diesel-fill": "\f83b",
+ "fuel-pump-diesel": "\f83c",
+ "fuel-pump-fill": "\f83d",
+ "fuel-pump": "\f83e",
+ "0-circle-fill": "\f83f",
+ "0-circle": "\f840",
+ "0-square-fill": "\f841",
+ "0-square": "\f842",
+ "rocket-fill": "\f843",
+ "rocket-takeoff-fill": "\f844",
+ "rocket-takeoff": "\f845",
+ "rocket": "\f846",
+ "stripe": "\f847",
+ "subscript": "\f848",
+ "superscript": "\f849",
+ "trello": "\f84a",
+ "envelope-at-fill": "\f84b",
+ "envelope-at": "\f84c",
+ "regex": "\f84d",
+ "text-wrap": "\f84e",
+ "sign-dead-end-fill": "\f84f",
+ "sign-dead-end": "\f850",
+ "sign-do-not-enter-fill": "\f851",
+ "sign-do-not-enter": "\f852",
+ "sign-intersection-fill": "\f853",
+ "sign-intersection-side-fill": "\f854",
+ "sign-intersection-side": "\f855",
+ "sign-intersection-t-fill": "\f856",
+ "sign-intersection-t": "\f857",
+ "sign-intersection-y-fill": "\f858",
+ "sign-intersection-y": "\f859",
+ "sign-intersection": "\f85a",
+ "sign-merge-left-fill": "\f85b",
+ "sign-merge-left": "\f85c",
+ "sign-merge-right-fill": "\f85d",
+ "sign-merge-right": "\f85e",
+ "sign-no-left-turn-fill": "\f85f",
+ "sign-no-left-turn": "\f860",
+ "sign-no-parking-fill": "\f861",
+ "sign-no-parking": "\f862",
+ "sign-no-right-turn-fill": "\f863",
+ "sign-no-right-turn": "\f864",
+ "sign-railroad-fill": "\f865",
+ "sign-railroad": "\f866",
+ "building-add": "\f867",
+ "building-check": "\f868",
+ "building-dash": "\f869",
+ "building-down": "\f86a",
+ "building-exclamation": "\f86b",
+ "building-fill-add": "\f86c",
+ "building-fill-check": "\f86d",
+ "building-fill-dash": "\f86e",
+ "building-fill-down": "\f86f",
+ "building-fill-exclamation": "\f870",
+ "building-fill-gear": "\f871",
+ "building-fill-lock": "\f872",
+ "building-fill-slash": "\f873",
+ "building-fill-up": "\f874",
+ "building-fill-x": "\f875",
+ "building-fill": "\f876",
+ "building-gear": "\f877",
+ "building-lock": "\f878",
+ "building-slash": "\f879",
+ "building-up": "\f87a",
+ "building-x": "\f87b",
+ "buildings-fill": "\f87c",
+ "buildings": "\f87d",
+ "bus-front-fill": "\f87e",
+ "bus-front": "\f87f",
+ "ev-front-fill": "\f880",
+ "ev-front": "\f881",
+ "globe-americas": "\f882",
+ "globe-asia-australia": "\f883",
+ "globe-central-south-asia": "\f884",
+ "globe-europe-africa": "\f885",
+ "house-add-fill": "\f886",
+ "house-add": "\f887",
+ "house-check-fill": "\f888",
+ "house-check": "\f889",
+ "house-dash-fill": "\f88a",
+ "house-dash": "\f88b",
+ "house-down-fill": "\f88c",
+ "house-down": "\f88d",
+ "house-exclamation-fill": "\f88e",
+ "house-exclamation": "\f88f",
+ "house-gear-fill": "\f890",
+ "house-gear": "\f891",
+ "house-lock-fill": "\f892",
+ "house-lock": "\f893",
+ "house-slash-fill": "\f894",
+ "house-slash": "\f895",
+ "house-up-fill": "\f896",
+ "house-up": "\f897",
+ "house-x-fill": "\f898",
+ "house-x": "\f899",
+ "person-add": "\f89a",
+ "person-down": "\f89b",
+ "person-exclamation": "\f89c",
+ "person-fill-add": "\f89d",
+ "person-fill-check": "\f89e",
+ "person-fill-dash": "\f89f",
+ "person-fill-down": "\f8a0",
+ "person-fill-exclamation": "\f8a1",
+ "person-fill-gear": "\f8a2",
+ "person-fill-lock": "\f8a3",
+ "person-fill-slash": "\f8a4",
+ "person-fill-up": "\f8a5",
+ "person-fill-x": "\f8a6",
+ "person-gear": "\f8a7",
+ "person-lock": "\f8a8",
+ "person-slash": "\f8a9",
+ "person-up": "\f8aa",
+ "scooter": "\f8ab",
+ "taxi-front-fill": "\f8ac",
+ "taxi-front": "\f8ad",
+ "amd": "\f8ae",
+ "database-add": "\f8af",
+ "database-check": "\f8b0",
+ "database-dash": "\f8b1",
+ "database-down": "\f8b2",
+ "database-exclamation": "\f8b3",
+ "database-fill-add": "\f8b4",
+ "database-fill-check": "\f8b5",
+ "database-fill-dash": "\f8b6",
+ "database-fill-down": "\f8b7",
+ "database-fill-exclamation": "\f8b8",
+ "database-fill-gear": "\f8b9",
+ "database-fill-lock": "\f8ba",
+ "database-fill-slash": "\f8bb",
+ "database-fill-up": "\f8bc",
+ "database-fill-x": "\f8bd",
+ "database-fill": "\f8be",
+ "database-gear": "\f8bf",
+ "database-lock": "\f8c0",
+ "database-slash": "\f8c1",
+ "database-up": "\f8c2",
+ "database-x": "\f8c3",
+ "database": "\f8c4",
+ "houses-fill": "\f8c5",
+ "houses": "\f8c6",
+ "nvidia": "\f8c7",
+ "person-vcard-fill": "\f8c8",
+ "person-vcard": "\f8c9",
+ "sina-weibo": "\f8ca",
+ "tencent-qq": "\f8cb",
+ "wikipedia": "\f8cc",
+ "alphabet-uppercase": "\f2a5",
+ "alphabet": "\f68a",
+ "amazon": "\f68d",
+ "arrows-collapse-vertical": "\f690",
+ "arrows-expand-vertical": "\f695",
+ "arrows-vertical": "\f698",
+ "arrows": "\f6a2",
+ "ban-fill": "\f6a3",
+ "ban": "\f6b6",
+ "bing": "\f6c2",
+ "cake": "\f6e0",
+ "cake2": "\f6ed",
+ "cookie": "\f6ee",
+ "copy": "\f759",
+ "crosshair": "\f769",
+ "crosshair2": "\f794",
+ "emoji-astonished-fill": "\f795",
+ "emoji-astonished": "\f79a",
+ "emoji-grimace-fill": "\f79b",
+ "emoji-grimace": "\f7a0",
+ "emoji-grin-fill": "\f7a1",
+ "emoji-grin": "\f7a6",
+ "emoji-surprise-fill": "\f7a7",
+ "emoji-surprise": "\f7ac",
+ "emoji-tear-fill": "\f7ad",
+ "emoji-tear": "\f7b2",
+ "envelope-arrow-down-fill": "\f7b3",
+ "envelope-arrow-down": "\f7b8",
+ "envelope-arrow-up-fill": "\f7b9",
+ "envelope-arrow-up": "\f7be",
+ "feather": "\f7bf",
+ "feather2": "\f7c4",
+ "floppy-fill": "\f7c5",
+ "floppy": "\f7d8",
+ "floppy2-fill": "\f7d9",
+ "floppy2": "\f7e4",
+ "gitlab": "\f7e5",
+ "highlighter": "\f7f8",
+ "marker-tip": "\f802",
+ "nvme-fill": "\f803",
+ "nvme": "\f80c",
+ "opencollective": "\f80d",
+ "pci-card-network": "\f8cd",
+ "pci-card-sound": "\f8ce",
+ "radar": "\f8cf",
+ "send-arrow-down-fill": "\f8d0",
+ "send-arrow-down": "\f8d1",
+ "send-arrow-up-fill": "\f8d2",
+ "send-arrow-up": "\f8d3",
+ "sim-slash-fill": "\f8d4",
+ "sim-slash": "\f8d5",
+ "sourceforge": "\f8d6",
+ "substack": "\f8d7",
+ "threads-fill": "\f8d8",
+ "threads": "\f8d9",
+ "transparency": "\f8da",
+ "twitter-x": "\f8db",
+ "type-h4": "\f8dc",
+ "type-h5": "\f8dd",
+ "type-h6": "\f8de",
+ "backpack-fill": "\f8df",
+ "backpack": "\f8e0",
+ "backpack2-fill": "\f8e1",
+ "backpack2": "\f8e2",
+ "backpack3-fill": "\f8e3",
+ "backpack3": "\f8e4",
+ "backpack4-fill": "\f8e5",
+ "backpack4": "\f8e6",
+ "brilliance": "\f8e7",
+ "cake-fill": "\f8e8",
+ "cake2-fill": "\f8e9",
+ "duffle-fill": "\f8ea",
+ "duffle": "\f8eb",
+ "exposure": "\f8ec",
+ "gender-neuter": "\f8ed",
+ "highlights": "\f8ee",
+ "luggage-fill": "\f8ef",
+ "luggage": "\f8f0",
+ "mailbox-flag": "\f8f1",
+ "mailbox2-flag": "\f8f2",
+ "noise-reduction": "\f8f3",
+ "passport-fill": "\f8f4",
+ "passport": "\f8f5",
+ "person-arms-up": "\f8f6",
+ "person-raised-hand": "\f8f7",
+ "person-standing-dress": "\f8f8",
+ "person-standing": "\f8f9",
+ "person-walking": "\f8fa",
+ "person-wheelchair": "\f8fb",
+ "shadows": "\f8fc",
+ "suitcase-fill": "\f8fd",
+ "suitcase-lg-fill": "\f8fe",
+ "suitcase-lg": "\f8ff",
+ "suitcase": "\f900",
+ "suitcase2-fill": "\f901",
+ "suitcase2": "\f902",
+ "vignette": "\f903",
+ "bluesky": "\f7f9",
+ "tux": "\f904",
+ "beaker-fill": "\f905",
+ "beaker": "\f906",
+ "flask-fill": "\f907",
+ "flask-florence-fill": "\f908",
+ "flask-florence": "\f909",
+ "flask": "\f90a",
+ "leaf-fill": "\f90b",
+ "leaf": "\f90c",
+ "measuring-cup-fill": "\f90d",
+ "measuring-cup": "\f90e",
+ "unlock2-fill": "\f90f",
+ "unlock2": "\f910",
+ "battery-low": "\f911",
+ "anthropic": "\f912",
+ "apple-music": "\f913",
+ "claude": "\f914",
+ "openai": "\f915",
+ "perplexity": "\f916",
+ "css": "\f917",
+ "javascript": "\f918",
+ "typescript": "\f919",
+ "fork-knife": "\f91a",
+ "globe-americas-fill": "\f91b",
+ "globe-asia-australia-fill": "\f91c",
+ "globe-central-south-asia-fill": "\f91d",
+ "globe-europe-africa-fill": "\f91e",
+);
+
+@each $icon, $codepoint in $bootstrap-icons-map {
+ .bi-#{$icon}::before { content: $codepoint; }
+}
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-icons.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-icons.svg
new file mode 100644
index 00000000..fa7e6203
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-icons.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-reboot.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-reboot.svg
new file mode 100644
index 00000000..14e08033
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap-reboot.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap.svg
new file mode 100644
index 00000000..1fa9b570
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bootstrap.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/border-all.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-all.svg
new file mode 100644
index 00000000..b54e1d98
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-all.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/border-bottom.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-bottom.svg
new file mode 100644
index 00000000..5d8ddc25
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-bottom.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/border-center.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-center.svg
new file mode 100644
index 00000000..15eb06e9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-center.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/border-inner.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-inner.svg
new file mode 100644
index 00000000..047ad0d3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-inner.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/border-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-left.svg
new file mode 100644
index 00000000..5e1f5ce4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-left.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/border-middle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-middle.svg
new file mode 100644
index 00000000..808cb6f9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-middle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/border-outer.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-outer.svg
new file mode 100644
index 00000000..a2df853e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-outer.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/border-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-right.svg
new file mode 100644
index 00000000..151f0046
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/border-style.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-style.svg
new file mode 100644
index 00000000..5dd22e8c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-style.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/border-top.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-top.svg
new file mode 100644
index 00000000..fcbbdc35
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-top.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/border-width.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-width.svg
new file mode 100644
index 00000000..24a320de
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/border-width.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/border.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/border.svg
new file mode 100644
index 00000000..60b861ab
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/border.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bounding-box-circles.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bounding-box-circles.svg
new file mode 100644
index 00000000..8e75142a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bounding-box-circles.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bounding-box.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bounding-box.svg
new file mode 100644
index 00000000..e83c49d7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bounding-box.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-down-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-down-left.svg
new file mode 100644
index 00000000..4892efcf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-down-left.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-down-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-down-right.svg
new file mode 100644
index 00000000..97e982da
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-down-right.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-down.svg
new file mode 100644
index 00000000..2a22ebbf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-down.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-down-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-down-left.svg
new file mode 100644
index 00000000..b403ec98
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-down-left.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-down-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-down-right.svg
new file mode 100644
index 00000000..c838b073
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-down-right.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-down.svg
new file mode 100644
index 00000000..dc69a506
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-down.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-left.svg
new file mode 100644
index 00000000..04bdf09c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-left.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-right.svg
new file mode 100644
index 00000000..7d28872e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-right.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-up-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-up-left.svg
new file mode 100644
index 00000000..06ac0e2d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-up-left.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-up-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-up-right.svg
new file mode 100644
index 00000000..e9a770e6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-up-right.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-up.svg
new file mode 100644
index 00000000..88664cd0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-in-up.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-left.svg
new file mode 100644
index 00000000..e4375f2d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-left.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-right.svg
new file mode 100644
index 00000000..e4b88d16
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-right.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-up-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-up-left.svg
new file mode 100644
index 00000000..9359bcf1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-up-left.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-up-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-up-right.svg
new file mode 100644
index 00000000..25149c5d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-up-right.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-up.svg
new file mode 100644
index 00000000..9a05ea8a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-arrow-up.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-fill.svg
new file mode 100644
index 00000000..c3e46ae7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-seam-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-seam-fill.svg
new file mode 100644
index 00000000..5ca92bc4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-seam-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box-seam.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-seam.svg
new file mode 100644
index 00000000..e1509400
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box-seam.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box.svg
new file mode 100644
index 00000000..5ca8d162
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box2-fill.svg
new file mode 100644
index 00000000..1b9f226c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box2-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box2-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box2-heart-fill.svg
new file mode 100644
index 00000000..d3ee4817
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box2-heart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box2-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box2-heart.svg
new file mode 100644
index 00000000..fefb98d5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box2-heart.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/box2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/box2.svg
new file mode 100644
index 00000000..fe2d9f53
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/box2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/boxes.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/boxes.svg
new file mode 100644
index 00000000..fbc84a6c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/boxes.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/braces-asterisk.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/braces-asterisk.svg
new file mode 100644
index 00000000..45f87162
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/braces-asterisk.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/braces.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/braces.svg
new file mode 100644
index 00000000..5614720b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/braces.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bricks.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bricks.svg
new file mode 100644
index 00000000..78a4c8ca
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bricks.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/briefcase-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/briefcase-fill.svg
new file mode 100644
index 00000000..4d7fadb5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/briefcase-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/briefcase.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/briefcase.svg
new file mode 100644
index 00000000..c5bcde0c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/briefcase.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-alt-high-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-alt-high-fill.svg
new file mode 100644
index 00000000..1fa22311
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-alt-high-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-alt-high.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-alt-high.svg
new file mode 100644
index 00000000..33e19d01
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-alt-high.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-alt-low-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-alt-low-fill.svg
new file mode 100644
index 00000000..0d427a89
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-alt-low-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-alt-low.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-alt-low.svg
new file mode 100644
index 00000000..153199b8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-alt-low.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-high-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-high-fill.svg
new file mode 100644
index 00000000..6130b6cd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-high-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-high.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-high.svg
new file mode 100644
index 00000000..3def0ddb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-high.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-low-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-low-fill.svg
new file mode 100644
index 00000000..c473ca7e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-low-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-low.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-low.svg
new file mode 100644
index 00000000..18a87bfe
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/brightness-low.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/brilliance.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/brilliance.svg
new file mode 100644
index 00000000..b2dd8e5e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/brilliance.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/broadcast-pin.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/broadcast-pin.svg
new file mode 100644
index 00000000..37e571bc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/broadcast-pin.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/broadcast.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/broadcast.svg
new file mode 100644
index 00000000..f554b40f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/broadcast.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/browser-chrome.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/browser-chrome.svg
new file mode 100644
index 00000000..68596d02
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/browser-chrome.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/browser-edge.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/browser-edge.svg
new file mode 100644
index 00000000..fb6e9434
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/browser-edge.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/browser-firefox.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/browser-firefox.svg
new file mode 100644
index 00000000..7a100e35
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/browser-firefox.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/browser-safari.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/browser-safari.svg
new file mode 100644
index 00000000..e8454e3c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/browser-safari.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/brush-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/brush-fill.svg
new file mode 100644
index 00000000..33a4500a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/brush-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/brush.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/brush.svg
new file mode 100644
index 00000000..a8485969
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/brush.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bucket-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bucket-fill.svg
new file mode 100644
index 00000000..d90878fe
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bucket-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bucket.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bucket.svg
new file mode 100644
index 00000000..70b3d133
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bucket.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bug-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bug-fill.svg
new file mode 100644
index 00000000..ec3f6052
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bug-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bug.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bug.svg
new file mode 100644
index 00000000..2d9d1e8d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bug.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-add.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-add.svg
new file mode 100644
index 00000000..2208c9ee
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-add.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-check.svg
new file mode 100644
index 00000000..fb682fcd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-check.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-dash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-dash.svg
new file mode 100644
index 00000000..77ce4c6a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-dash.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-down.svg
new file mode 100644
index 00000000..4db08e9c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-down.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-exclamation.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-exclamation.svg
new file mode 100644
index 00000000..6165c6a1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-exclamation.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-add.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-add.svg
new file mode 100644
index 00000000..c7bd588e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-add.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-check.svg
new file mode 100644
index 00000000..19cfc595
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-dash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-dash.svg
new file mode 100644
index 00000000..2c4aad06
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-dash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-down.svg
new file mode 100644
index 00000000..d1dea024
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-down.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-exclamation.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-exclamation.svg
new file mode 100644
index 00000000..7990ecb4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-exclamation.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-gear.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-gear.svg
new file mode 100644
index 00000000..ff16c9db
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-gear.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-lock.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-lock.svg
new file mode 100644
index 00000000..b9fde69c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-lock.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-slash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-slash.svg
new file mode 100644
index 00000000..8d8be4dd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-slash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-up.svg
new file mode 100644
index 00000000..5f7f5370
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-up.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-x.svg
new file mode 100644
index 00000000..ee28c3a1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill.svg
new file mode 100644
index 00000000..357c61b5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-gear.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-gear.svg
new file mode 100644
index 00000000..423bde05
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-gear.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-lock.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-lock.svg
new file mode 100644
index 00000000..2f949c7d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-lock.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-slash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-slash.svg
new file mode 100644
index 00000000..b495936e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-slash.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-up.svg
new file mode 100644
index 00000000..8c7ad9fe
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-up.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-x.svg
new file mode 100644
index 00000000..8d761aac
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/building.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/building.svg
new file mode 100644
index 00000000..55ded2dc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/building.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/buildings-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/buildings-fill.svg
new file mode 100644
index 00000000..76b070c5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/buildings-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/buildings.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/buildings.svg
new file mode 100644
index 00000000..c7634757
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/buildings.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bullseye.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bullseye.svg
new file mode 100644
index 00000000..748b6e27
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bullseye.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bus-front-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bus-front-fill.svg
new file mode 100644
index 00000000..f2caa1b2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bus-front-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/bus-front.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/bus-front.svg
new file mode 100644
index 00000000..19aaa238
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/bus-front.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/c-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/c-circle-fill.svg
new file mode 100644
index 00000000..3cbadbef
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/c-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/c-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/c-circle.svg
new file mode 100644
index 00000000..4632807a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/c-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/c-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/c-square-fill.svg
new file mode 100644
index 00000000..7dad0796
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/c-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/c-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/c-square.svg
new file mode 100644
index 00000000..4c857402
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/c-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cake-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cake-fill.svg
new file mode 100644
index 00000000..dc2899a9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cake-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cake.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cake.svg
new file mode 100644
index 00000000..f6c3899e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cake.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cake2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cake2-fill.svg
new file mode 100644
index 00000000..c5f36633
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cake2-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cake2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cake2.svg
new file mode 100644
index 00000000..8bc96d4b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cake2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calculator-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calculator-fill.svg
new file mode 100644
index 00000000..bdd0b9f3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calculator-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calculator.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calculator.svg
new file mode 100644
index 00000000..d2143f1d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calculator.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-check-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-check-fill.svg
new file mode 100644
index 00000000..decfb3f3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-check-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-check.svg
new file mode 100644
index 00000000..9aeffbea
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-date-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-date-fill.svg
new file mode 100644
index 00000000..f796e575
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-date-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-date.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-date.svg
new file mode 100644
index 00000000..2f659281
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-date.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-day-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-day-fill.svg
new file mode 100644
index 00000000..94a699c6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-day-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-day.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-day.svg
new file mode 100644
index 00000000..ce4eb287
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-day.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-event-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-event-fill.svg
new file mode 100644
index 00000000..659278bb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-event-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-event.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-event.svg
new file mode 100644
index 00000000..451e7680
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-event.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-fill.svg
new file mode 100644
index 00000000..76c7536b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-heart-fill.svg
new file mode 100644
index 00000000..a4a254f8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-heart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-heart.svg
new file mode 100644
index 00000000..07d0f942
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-heart.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-minus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-minus-fill.svg
new file mode 100644
index 00000000..a43d5318
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-minus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-minus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-minus.svg
new file mode 100644
index 00000000..04299e20
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-minus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-month-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-month-fill.svg
new file mode 100644
index 00000000..feda7121
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-month-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-month.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-month.svg
new file mode 100644
index 00000000..d452105d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-month.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-plus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-plus-fill.svg
new file mode 100644
index 00000000..a250d8e1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-plus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-plus.svg
new file mode 100644
index 00000000..d9e8fdd1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-plus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-range-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-range-fill.svg
new file mode 100644
index 00000000..2b2dfcc8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-range-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-range.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-range.svg
new file mode 100644
index 00000000..40c85b47
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-range.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-week-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-week-fill.svg
new file mode 100644
index 00000000..ba06206f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-week-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-week.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-week.svg
new file mode 100644
index 00000000..bba866c8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-week.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-x-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-x-fill.svg
new file mode 100644
index 00000000..bc1c519f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-x-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-x.svg
new file mode 100644
index 00000000..89de321b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar.svg
new file mode 100644
index 00000000..f19331c5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-check-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-check-fill.svg
new file mode 100644
index 00000000..83592eef
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-check-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-check.svg
new file mode 100644
index 00000000..c96c4219
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-check.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-date-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-date-fill.svg
new file mode 100644
index 00000000..7d149e9d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-date-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-date.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-date.svg
new file mode 100644
index 00000000..3997f23b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-date.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-day-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-day-fill.svg
new file mode 100644
index 00000000..aefdc5fd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-day-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-day.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-day.svg
new file mode 100644
index 00000000..b5d68150
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-day.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-event-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-event-fill.svg
new file mode 100644
index 00000000..1219bf19
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-event-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-event.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-event.svg
new file mode 100644
index 00000000..bc0c4a28
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-event.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-fill.svg
new file mode 100644
index 00000000..2a66d984
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-heart-fill.svg
new file mode 100644
index 00000000..ca803daa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-heart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-heart.svg
new file mode 100644
index 00000000..11195abb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-heart.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-minus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-minus-fill.svg
new file mode 100644
index 00000000..b5db63d5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-minus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-minus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-minus.svg
new file mode 100644
index 00000000..6cb395df
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-minus.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-month-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-month-fill.svg
new file mode 100644
index 00000000..ab5f022b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-month-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-month.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-month.svg
new file mode 100644
index 00000000..270494c2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-month.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-plus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-plus-fill.svg
new file mode 100644
index 00000000..68d6b812
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-plus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-plus.svg
new file mode 100644
index 00000000..6da40eb5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-plus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-range-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-range-fill.svg
new file mode 100644
index 00000000..e06ce8d0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-range-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-range.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-range.svg
new file mode 100644
index 00000000..f480d84c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-range.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-week-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-week-fill.svg
new file mode 100644
index 00000000..7d736b96
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-week-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-week.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-week.svg
new file mode 100644
index 00000000..90720a0f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-week.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-x-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-x-fill.svg
new file mode 100644
index 00000000..ce7e325b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-x-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-x.svg
new file mode 100644
index 00000000..a4e46aec
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2-x.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2.svg
new file mode 100644
index 00000000..46d5922e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar2.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-event-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-event-fill.svg
new file mode 100644
index 00000000..5052d10c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-event-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-event.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-event.svg
new file mode 100644
index 00000000..b164ddf2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-event.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-fill.svg
new file mode 100644
index 00000000..1857b044
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-range-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-range-fill.svg
new file mode 100644
index 00000000..415d24eb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-range-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-range.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-range.svg
new file mode 100644
index 00000000..631ba4d5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-range.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-week-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-week-fill.svg
new file mode 100644
index 00000000..d214220d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-week-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-week.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-week.svg
new file mode 100644
index 00000000..0836df41
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3-week.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3.svg
new file mode 100644
index 00000000..d9a60912
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar3.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar4-event.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar4-event.svg
new file mode 100644
index 00000000..cb83cf35
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar4-event.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar4-range.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar4-range.svg
new file mode 100644
index 00000000..2d85a813
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar4-range.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar4-week.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar4-week.svg
new file mode 100644
index 00000000..4adf34d2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar4-week.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar4.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar4.svg
new file mode 100644
index 00000000..c545638c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/calendar4.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-fill.svg
new file mode 100644
index 00000000..ddf669a3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-reels-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-reels-fill.svg
new file mode 100644
index 00000000..fd5cc669
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-reels-fill.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-reels.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-reels.svg
new file mode 100644
index 00000000..ef886e61
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-reels.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-video-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-video-fill.svg
new file mode 100644
index 00000000..60228c2f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-video-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-video-off-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-video-off-fill.svg
new file mode 100644
index 00000000..60454c62
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-video-off-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-video-off.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-video-off.svg
new file mode 100644
index 00000000..15ec07cf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-video-off.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-video.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-video.svg
new file mode 100644
index 00000000..215c047d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera-video.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/camera.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera.svg
new file mode 100644
index 00000000..d78e612d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/camera2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera2.svg
new file mode 100644
index 00000000..0cc1adef
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/camera2.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/capslock-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/capslock-fill.svg
new file mode 100644
index 00000000..5f6671bb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/capslock-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/capslock.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/capslock.svg
new file mode 100644
index 00000000..067b96d9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/capslock.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/capsule-pill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/capsule-pill.svg
new file mode 100644
index 00000000..9753f445
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/capsule-pill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/capsule.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/capsule.svg
new file mode 100644
index 00000000..f4529cfa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/capsule.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/car-front-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/car-front-fill.svg
new file mode 100644
index 00000000..005c9c98
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/car-front-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/car-front.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/car-front.svg
new file mode 100644
index 00000000..1ca898a1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/car-front.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/card-checklist.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/card-checklist.svg
new file mode 100644
index 00000000..353d355d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/card-checklist.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/card-heading.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/card-heading.svg
new file mode 100644
index 00000000..71b5b5e2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/card-heading.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/card-image.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/card-image.svg
new file mode 100644
index 00000000..21a994f9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/card-image.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/card-list.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/card-list.svg
new file mode 100644
index 00000000..c4ce1bf9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/card-list.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/card-text.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/card-text.svg
new file mode 100644
index 00000000..4308a847
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/card-text.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-down-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-down-fill.svg
new file mode 100644
index 00000000..858c977c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-down-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-down-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-down-square-fill.svg
new file mode 100644
index 00000000..bfbf2ad4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-down-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-down-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-down-square.svg
new file mode 100644
index 00000000..829d359c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-down-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-down.svg
new file mode 100644
index 00000000..3941331d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-down.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-left-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-left-fill.svg
new file mode 100644
index 00000000..6a0516e3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-left-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-left-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-left-square-fill.svg
new file mode 100644
index 00000000..eb236fff
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-left-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-left-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-left-square.svg
new file mode 100644
index 00000000..4684818c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-left-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-left.svg
new file mode 100644
index 00000000..72a876b5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-left.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-right-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-right-fill.svg
new file mode 100644
index 00000000..ab4126ae
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-right-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-right-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-right-square-fill.svg
new file mode 100644
index 00000000..c89c9842
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-right-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-right-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-right-square.svg
new file mode 100644
index 00000000..36674e38
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-right-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-right.svg
new file mode 100644
index 00000000..a67b120a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-up-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-up-fill.svg
new file mode 100644
index 00000000..590dcadd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-up-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-up-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-up-square-fill.svg
new file mode 100644
index 00000000..e2a2c32a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-up-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-up-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-up-square.svg
new file mode 100644
index 00000000..01e225d3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-up-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-up.svg
new file mode 100644
index 00000000..7717f922
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/caret-up.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-check-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-check-fill.svg
new file mode 100644
index 00000000..945f4a79
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-check-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-check.svg
new file mode 100644
index 00000000..201db0cf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-dash-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-dash-fill.svg
new file mode 100644
index 00000000..95bbff68
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-dash-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-dash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-dash.svg
new file mode 100644
index 00000000..49f4d87f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-dash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-fill.svg
new file mode 100644
index 00000000..be4b70c8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-plus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-plus-fill.svg
new file mode 100644
index 00000000..fa03ccb9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-plus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-plus.svg
new file mode 100644
index 00000000..cd59dea2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-plus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-x-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-x-fill.svg
new file mode 100644
index 00000000..a30bb6f3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-x-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-x.svg
new file mode 100644
index 00000000..26ed84b2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart.svg
new file mode 100644
index 00000000..20286d92
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cart2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart2.svg
new file mode 100644
index 00000000..d83596b0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cart3.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart3.svg
new file mode 100644
index 00000000..ad2f7504
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart3.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cart4.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart4.svg
new file mode 100644
index 00000000..7225d157
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cart4.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cash-coin.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cash-coin.svg
new file mode 100644
index 00000000..4e6e96ec
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cash-coin.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cash-stack.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cash-stack.svg
new file mode 100644
index 00000000..f469075c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cash-stack.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cash.svg
new file mode 100644
index 00000000..11dc3ec9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cassette-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cassette-fill.svg
new file mode 100644
index 00000000..5c524452
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cassette-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cassette.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cassette.svg
new file mode 100644
index 00000000..c6d20434
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cassette.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cast.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cast.svg
new file mode 100644
index 00000000..166fc67f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cast.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cc-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cc-circle-fill.svg
new file mode 100644
index 00000000..5371b8d6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cc-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cc-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cc-circle.svg
new file mode 100644
index 00000000..2f0a7245
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cc-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cc-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cc-square-fill.svg
new file mode 100644
index 00000000..c66eda27
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cc-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cc-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cc-square.svg
new file mode 100644
index 00000000..949ff516
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cc-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-dots-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-dots-fill.svg
new file mode 100644
index 00000000..c77ca6ee
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-dots-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-dots.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-dots.svg
new file mode 100644
index 00000000..41f9f4fa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-dots.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-fill.svg
new file mode 100644
index 00000000..33de8b2d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-heart-fill.svg
new file mode 100644
index 00000000..3278c028
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-heart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-heart.svg
new file mode 100644
index 00000000..d6f6fd77
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-heart.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-dots-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-dots-fill.svg
new file mode 100644
index 00000000..1d9c47db
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-dots-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-dots.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-dots.svg
new file mode 100644
index 00000000..0c6fe2c4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-dots.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-fill.svg
new file mode 100644
index 00000000..db32f54c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-heart-fill.svg
new file mode 100644
index 00000000..ea3a81cd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-heart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-heart.svg
new file mode 100644
index 00000000..99375346
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-heart.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-quote-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-quote-fill.svg
new file mode 100644
index 00000000..89e5a0d0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-quote-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-quote.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-quote.svg
new file mode 100644
index 00000000..8be4a33e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-quote.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-text-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-text-fill.svg
new file mode 100644
index 00000000..86d17cb4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-text-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-text.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-text.svg
new file mode 100644
index 00000000..5055e398
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left-text.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left.svg
new file mode 100644
index 00000000..c1bf82bc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-left.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-quote-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-quote-fill.svg
new file mode 100644
index 00000000..448c5b0d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-quote-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-quote.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-quote.svg
new file mode 100644
index 00000000..635dbecd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-quote.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-dots-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-dots-fill.svg
new file mode 100644
index 00000000..320e1767
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-dots-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-dots.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-dots.svg
new file mode 100644
index 00000000..7be9b5bf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-dots.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-fill.svg
new file mode 100644
index 00000000..3c8c3cd9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-heart-fill.svg
new file mode 100644
index 00000000..4384503f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-heart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-heart.svg
new file mode 100644
index 00000000..81c6db13
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-heart.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-quote-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-quote-fill.svg
new file mode 100644
index 00000000..f537056a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-quote-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-quote.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-quote.svg
new file mode 100644
index 00000000..3065100f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-quote.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-text-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-text-fill.svg
new file mode 100644
index 00000000..bcf97b0d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-text-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-text.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-text.svg
new file mode 100644
index 00000000..ff7ce2a6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right-text.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right.svg
new file mode 100644
index 00000000..b8a2357a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-dots-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-dots-fill.svg
new file mode 100644
index 00000000..2f6c1f4c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-dots-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-dots.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-dots.svg
new file mode 100644
index 00000000..1495b930
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-dots.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-fill.svg
new file mode 100644
index 00000000..0013fa47
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-heart-fill.svg
new file mode 100644
index 00000000..02bf7b7c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-heart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-heart.svg
new file mode 100644
index 00000000..315b8377
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-heart.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-quote-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-quote-fill.svg
new file mode 100644
index 00000000..8e80e232
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-quote-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-quote.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-quote.svg
new file mode 100644
index 00000000..14b51c52
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-quote.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-text-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-text-fill.svg
new file mode 100644
index 00000000..87cecdb4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-text-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-text.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-text.svg
new file mode 100644
index 00000000..1d9a359c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square-text.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square.svg
new file mode 100644
index 00000000..f77c2be1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-square.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-text-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-text-fill.svg
new file mode 100644
index 00000000..f6add9f0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-text-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-text.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-text.svg
new file mode 100644
index 00000000..89786ca0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat-text.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chat.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat.svg
new file mode 100644
index 00000000..7154a0e5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chat.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/check-all.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/check-all.svg
new file mode 100644
index 00000000..8abe30e2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/check-all.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/check-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/check-circle-fill.svg
new file mode 100644
index 00000000..f24df1b7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/check-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/check-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/check-circle.svg
new file mode 100644
index 00000000..4a6916c8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/check-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/check-lg.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/check-lg.svg
new file mode 100644
index 00000000..1682c5e6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/check-lg.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/check-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/check-square-fill.svg
new file mode 100644
index 00000000..160726ba
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/check-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/check-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/check-square.svg
new file mode 100644
index 00000000..5fdae93a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/check-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/check.svg
new file mode 100644
index 00000000..e187a15f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/check.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/check2-all.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/check2-all.svg
new file mode 100644
index 00000000..49f8b10d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/check2-all.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/check2-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/check2-circle.svg
new file mode 100644
index 00000000..c3943eb5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/check2-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/check2-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/check2-square.svg
new file mode 100644
index 00000000..03ea4513
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/check2-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/check2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/check2.svg
new file mode 100644
index 00000000..07ea4452
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/check2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-contract.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-contract.svg
new file mode 100644
index 00000000..a7113d74
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-contract.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-down.svg
new file mode 100644
index 00000000..59bfeec6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-down.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-expand.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-expand.svg
new file mode 100644
index 00000000..c717c969
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-expand.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-left.svg
new file mode 100644
index 00000000..9719adb0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-left.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-right.svg
new file mode 100644
index 00000000..d894417a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-up.svg
new file mode 100644
index 00000000..32cf1966
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-bar-up.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-compact-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-compact-down.svg
new file mode 100644
index 00000000..0ddee55a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-compact-down.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-compact-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-compact-left.svg
new file mode 100644
index 00000000..523d5dab
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-compact-left.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-compact-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-compact-right.svg
new file mode 100644
index 00000000..7240fd37
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-compact-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-compact-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-compact-up.svg
new file mode 100644
index 00000000..ffa680bd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-compact-up.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-contract.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-contract.svg
new file mode 100644
index 00000000..041bcf9a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-contract.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-double-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-double-down.svg
new file mode 100644
index 00000000..043e0803
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-double-down.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-double-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-double-left.svg
new file mode 100644
index 00000000..0e0a8761
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-double-left.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-double-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-double-right.svg
new file mode 100644
index 00000000..d6601dfb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-double-right.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-double-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-double-up.svg
new file mode 100644
index 00000000..93014ae1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-double-up.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-down.svg
new file mode 100644
index 00000000..7a955095
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-down.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-expand.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-expand.svg
new file mode 100644
index 00000000..2a7619e5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-expand.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-left.svg
new file mode 100644
index 00000000..9a631cfb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-left.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-right.svg
new file mode 100644
index 00000000..1fbcea4b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-up.svg
new file mode 100644
index 00000000..b62d77c9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/chevron-up.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/circle-fill.svg
new file mode 100644
index 00000000..3b35841f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/circle-half.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/circle-half.svg
new file mode 100644
index 00000000..f7df10fa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/circle-half.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/circle-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/circle-square.svg
new file mode 100644
index 00000000..e2c04c42
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/circle-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/circle.svg
new file mode 100644
index 00000000..0c591f6e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/claude.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/claude.svg
new file mode 100644
index 00000000..99f86d48
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/claude.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-check-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-check-fill.svg
new file mode 100644
index 00000000..ed665241
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-check-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-check.svg
new file mode 100644
index 00000000..1efdad80
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-check.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-data-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-data-fill.svg
new file mode 100644
index 00000000..c8bc02fb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-data-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-data.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-data.svg
new file mode 100644
index 00000000..708c21b8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-data.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-fill.svg
new file mode 100644
index 00000000..e8c8e78d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-heart-fill.svg
new file mode 100644
index 00000000..c8815e00
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-heart-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-heart.svg
new file mode 100644
index 00000000..897be92f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-heart.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-minus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-minus-fill.svg
new file mode 100644
index 00000000..c56c0925
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-minus-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-minus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-minus.svg
new file mode 100644
index 00000000..1b36d3d0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-minus.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-plus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-plus-fill.svg
new file mode 100644
index 00000000..e4ad26a8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-plus-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-plus.svg
new file mode 100644
index 00000000..de9bae10
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-plus.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-pulse.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-pulse.svg
new file mode 100644
index 00000000..7891093c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-pulse.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-x-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-x-fill.svg
new file mode 100644
index 00000000..7a0011ac
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-x-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-x.svg
new file mode 100644
index 00000000..75adb261
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard-x.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard.svg
new file mode 100644
index 00000000..cca45eb6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-check-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-check-fill.svg
new file mode 100644
index 00000000..0f7657d8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-check-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-check.svg
new file mode 100644
index 00000000..b026b80e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-check.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-data-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-data-fill.svg
new file mode 100644
index 00000000..dce3785f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-data-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-data.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-data.svg
new file mode 100644
index 00000000..933d6d84
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-data.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-fill.svg
new file mode 100644
index 00000000..b9124ddd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-heart-fill.svg
new file mode 100644
index 00000000..9694cd21
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-heart-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-heart.svg
new file mode 100644
index 00000000..c2381cd6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-heart.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-minus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-minus-fill.svg
new file mode 100644
index 00000000..6a5ba9e1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-minus-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-minus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-minus.svg
new file mode 100644
index 00000000..78a47560
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-minus.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-plus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-plus-fill.svg
new file mode 100644
index 00000000..9221823b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-plus-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-plus.svg
new file mode 100644
index 00000000..d8b30707
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-plus.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-pulse-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-pulse-fill.svg
new file mode 100644
index 00000000..4087f19a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-pulse-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-pulse.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-pulse.svg
new file mode 100644
index 00000000..3662bc23
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-pulse.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-x-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-x-fill.svg
new file mode 100644
index 00000000..ac443d2a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-x-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-x.svg
new file mode 100644
index 00000000..750f4cb0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2-x.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2.svg
new file mode 100644
index 00000000..3f549687
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clipboard2.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clock-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clock-fill.svg
new file mode 100644
index 00000000..109be162
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clock-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clock-history.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clock-history.svg
new file mode 100644
index 00000000..7342b0a9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clock-history.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clock.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clock.svg
new file mode 100644
index 00000000..e210180c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clock.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-arrow-down-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-arrow-down-fill.svg
new file mode 100644
index 00000000..20b546a6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-arrow-down-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-arrow-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-arrow-down.svg
new file mode 100644
index 00000000..8b0033ac
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-arrow-down.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-arrow-up-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-arrow-up-fill.svg
new file mode 100644
index 00000000..8bb7abcf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-arrow-up-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-arrow-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-arrow-up.svg
new file mode 100644
index 00000000..d48d6a4a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-arrow-up.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-check-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-check-fill.svg
new file mode 100644
index 00000000..58f5ee5e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-check-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-check.svg
new file mode 100644
index 00000000..bf751c2d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-download-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-download-fill.svg
new file mode 100644
index 00000000..e75765f5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-download-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-download.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-download.svg
new file mode 100644
index 00000000..23d7027f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-download.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-drizzle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-drizzle-fill.svg
new file mode 100644
index 00000000..0b1c6dca
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-drizzle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-drizzle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-drizzle.svg
new file mode 100644
index 00000000..edf9deac
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-drizzle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-fill.svg
new file mode 100644
index 00000000..cef7cace
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-fog-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-fog-fill.svg
new file mode 100644
index 00000000..e8f6b50b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-fog-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-fog.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-fog.svg
new file mode 100644
index 00000000..7d4abc2f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-fog.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-fog2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-fog2-fill.svg
new file mode 100644
index 00000000..08daeefd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-fog2-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-fog2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-fog2.svg
new file mode 100644
index 00000000..0597aea9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-fog2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-hail-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-hail-fill.svg
new file mode 100644
index 00000000..71020cfb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-hail-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-hail.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-hail.svg
new file mode 100644
index 00000000..33539801
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-hail.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-haze-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-haze-fill.svg
new file mode 100644
index 00000000..bca757b8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-haze-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-haze.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-haze.svg
new file mode 100644
index 00000000..40754fb4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-haze.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-haze2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-haze2-fill.svg
new file mode 100644
index 00000000..0c7c8c61
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-haze2-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-haze2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-haze2.svg
new file mode 100644
index 00000000..8e28430f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-haze2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-lightning-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-lightning-fill.svg
new file mode 100644
index 00000000..40a047fc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-lightning-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-lightning-rain-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-lightning-rain-fill.svg
new file mode 100644
index 00000000..f9586462
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-lightning-rain-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-lightning-rain.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-lightning-rain.svg
new file mode 100644
index 00000000..c2e31381
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-lightning-rain.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-lightning.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-lightning.svg
new file mode 100644
index 00000000..247fb423
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-lightning.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-minus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-minus-fill.svg
new file mode 100644
index 00000000..bc391965
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-minus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-minus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-minus.svg
new file mode 100644
index 00000000..0834442c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-minus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-moon-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-moon-fill.svg
new file mode 100644
index 00000000..356e4102
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-moon-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-moon.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-moon.svg
new file mode 100644
index 00000000..5ea2e2eb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-moon.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-plus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-plus-fill.svg
new file mode 100644
index 00000000..21dae384
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-plus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-plus.svg
new file mode 100644
index 00000000..ded12a4d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-plus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-rain-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-rain-fill.svg
new file mode 100644
index 00000000..67d5901e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-rain-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-rain-heavy-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-rain-heavy-fill.svg
new file mode 100644
index 00000000..fa039cde
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-rain-heavy-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-rain-heavy.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-rain-heavy.svg
new file mode 100644
index 00000000..76b27ce3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-rain-heavy.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-rain.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-rain.svg
new file mode 100644
index 00000000..7d3d6dbb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-rain.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-slash-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-slash-fill.svg
new file mode 100644
index 00000000..5ef39d16
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-slash-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-slash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-slash.svg
new file mode 100644
index 00000000..e6072e4c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-slash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-sleet-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-sleet-fill.svg
new file mode 100644
index 00000000..44ecfcb7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-sleet-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-sleet.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-sleet.svg
new file mode 100644
index 00000000..b04c9e83
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-sleet.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-snow-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-snow-fill.svg
new file mode 100644
index 00000000..c12f3e1e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-snow-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-snow.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-snow.svg
new file mode 100644
index 00000000..6273ad9b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-snow.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-sun-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-sun-fill.svg
new file mode 100644
index 00000000..a6f81cfe
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-sun-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-sun.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-sun.svg
new file mode 100644
index 00000000..29aee9bb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-sun.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-upload-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-upload-fill.svg
new file mode 100644
index 00000000..9434e356
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-upload-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-upload.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-upload.svg
new file mode 100644
index 00000000..0c41b568
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud-upload.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud.svg
new file mode 100644
index 00000000..c5b14ebf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloud.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clouds-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clouds-fill.svg
new file mode 100644
index 00000000..b5feceee
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clouds-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/clouds.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/clouds.svg
new file mode 100644
index 00000000..37b040a7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/clouds.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloudy-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloudy-fill.svg
new file mode 100644
index 00000000..41c02138
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloudy-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cloudy.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloudy.svg
new file mode 100644
index 00000000..828ff5b7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cloudy.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/code-slash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/code-slash.svg
new file mode 100644
index 00000000..4f54e1e8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/code-slash.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/code-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/code-square.svg
new file mode 100644
index 00000000..6e8b7174
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/code-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/code.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/code.svg
new file mode 100644
index 00000000..f797d6a5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/code.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/coin.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/coin.svg
new file mode 100644
index 00000000..1cf4e315
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/coin.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/collection-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/collection-fill.svg
new file mode 100644
index 00000000..e3e3b9ea
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/collection-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/collection-play-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/collection-play-fill.svg
new file mode 100644
index 00000000..399cd088
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/collection-play-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/collection-play.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/collection-play.svg
new file mode 100644
index 00000000..23e29d34
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/collection-play.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/collection.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/collection.svg
new file mode 100644
index 00000000..dee267d6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/collection.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/columns-gap.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/columns-gap.svg
new file mode 100644
index 00000000..a1054290
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/columns-gap.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/columns.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/columns.svg
new file mode 100644
index 00000000..cfeebbd9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/columns.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/command.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/command.svg
new file mode 100644
index 00000000..12dc1ff5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/command.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/compass-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/compass-fill.svg
new file mode 100644
index 00000000..99c5db09
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/compass-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/compass.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/compass.svg
new file mode 100644
index 00000000..57671977
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/compass.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cone-striped.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cone-striped.svg
new file mode 100644
index 00000000..476de8b1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cone-striped.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cone.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cone.svg
new file mode 100644
index 00000000..5286d347
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cone.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/controller.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/controller.svg
new file mode 100644
index 00000000..ee9426c5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/controller.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cookie.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cookie.svg
new file mode 100644
index 00000000..685ed5e0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cookie.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/copy.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/copy.svg
new file mode 100644
index 00000000..5246858c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/copy.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cpu-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cpu-fill.svg
new file mode 100644
index 00000000..eb817c62
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cpu-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cpu.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cpu.svg
new file mode 100644
index 00000000..02a5a504
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cpu.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card-2-back-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card-2-back-fill.svg
new file mode 100644
index 00000000..062c2362
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card-2-back-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card-2-back.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card-2-back.svg
new file mode 100644
index 00000000..886baaba
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card-2-back.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card-2-front-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card-2-front-fill.svg
new file mode 100644
index 00000000..deb6206d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card-2-front-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card-2-front.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card-2-front.svg
new file mode 100644
index 00000000..554298f0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card-2-front.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card-fill.svg
new file mode 100644
index 00000000..4621b90e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card.svg
new file mode 100644
index 00000000..d0179fba
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/credit-card.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/crop.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/crop.svg
new file mode 100644
index 00000000..72d60436
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/crop.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/crosshair.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/crosshair.svg
new file mode 100644
index 00000000..e0637510
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/crosshair.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/crosshair2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/crosshair2.svg
new file mode 100644
index 00000000..e014f0bb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/crosshair2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/css.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/css.svg
new file mode 100644
index 00000000..7ab7bc21
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/css.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cup-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cup-fill.svg
new file mode 100644
index 00000000..dcaf39a4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cup-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cup-hot-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cup-hot-fill.svg
new file mode 100644
index 00000000..afc1f2b1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cup-hot-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cup-hot.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cup-hot.svg
new file mode 100644
index 00000000..fb8988ed
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cup-hot.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cup-straw.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cup-straw.svg
new file mode 100644
index 00000000..80c4cabe
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cup-straw.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cup.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cup.svg
new file mode 100644
index 00000000..33d16624
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cup.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-bitcoin.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-bitcoin.svg
new file mode 100644
index 00000000..7957db91
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-bitcoin.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-dollar.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-dollar.svg
new file mode 100644
index 00000000..e0bfb5e8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-dollar.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-euro.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-euro.svg
new file mode 100644
index 00000000..5183beb0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-euro.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-exchange.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-exchange.svg
new file mode 100644
index 00000000..0f0f31d0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-exchange.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-pound.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-pound.svg
new file mode 100644
index 00000000..0495178e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-pound.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-rupee.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-rupee.svg
new file mode 100644
index 00000000..39054796
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-rupee.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-yen.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-yen.svg
new file mode 100644
index 00000000..f17f1d55
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/currency-yen.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cursor-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cursor-fill.svg
new file mode 100644
index 00000000..b8416937
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cursor-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cursor-text.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cursor-text.svg
new file mode 100644
index 00000000..a10c49a8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cursor-text.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/cursor.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/cursor.svg
new file mode 100644
index 00000000..8cede99b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/cursor.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-circle-dotted.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-circle-dotted.svg
new file mode 100644
index 00000000..78dd897e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-circle-dotted.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-circle-fill.svg
new file mode 100644
index 00000000..8bf35b0e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-circle.svg
new file mode 100644
index 00000000..f73d0122
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-lg.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-lg.svg
new file mode 100644
index 00000000..7a315e25
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-lg.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-square-dotted.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-square-dotted.svg
new file mode 100644
index 00000000..892355b6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-square-dotted.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-square-fill.svg
new file mode 100644
index 00000000..d59b7494
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-square.svg
new file mode 100644
index 00000000..eb0de725
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dash-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dash.svg
new file mode 100644
index 00000000..3f38f65e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dash.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-add.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-add.svg
new file mode 100644
index 00000000..3b4af52d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-add.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-check.svg
new file mode 100644
index 00000000..06b14b67
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-dash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-dash.svg
new file mode 100644
index 00000000..58a6e3c3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-dash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-down.svg
new file mode 100644
index 00000000..8671e71e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-down.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-exclamation.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-exclamation.svg
new file mode 100644
index 00000000..70a766e2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-exclamation.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-add.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-add.svg
new file mode 100644
index 00000000..889aed92
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-add.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-check.svg
new file mode 100644
index 00000000..34c6f822
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-dash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-dash.svg
new file mode 100644
index 00000000..2f84510e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-dash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-down.svg
new file mode 100644
index 00000000..fd45dac9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-down.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-exclamation.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-exclamation.svg
new file mode 100644
index 00000000..daf40d6c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-exclamation.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-gear.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-gear.svg
new file mode 100644
index 00000000..33bf3f61
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-gear.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-lock.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-lock.svg
new file mode 100644
index 00000000..9fb764fc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-lock.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-slash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-slash.svg
new file mode 100644
index 00000000..00b2208f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-slash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-up.svg
new file mode 100644
index 00000000..99183127
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-up.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-x.svg
new file mode 100644
index 00000000..3e89ef82
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill-x.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill.svg
new file mode 100644
index 00000000..834e9125
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-fill.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-gear.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-gear.svg
new file mode 100644
index 00000000..408c6871
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-gear.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-lock.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-lock.svg
new file mode 100644
index 00000000..10e1189b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-lock.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-slash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-slash.svg
new file mode 100644
index 00000000..35fccf59
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-slash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-up.svg
new file mode 100644
index 00000000..d497048f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-up.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-x.svg
new file mode 100644
index 00000000..0ac750b6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/database.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/database.svg
new file mode 100644
index 00000000..6586741c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/database.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/device-hdd-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/device-hdd-fill.svg
new file mode 100644
index 00000000..25295e46
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/device-hdd-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/device-hdd.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/device-hdd.svg
new file mode 100644
index 00000000..ecc9e50f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/device-hdd.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/device-ssd-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/device-ssd-fill.svg
new file mode 100644
index 00000000..ed23318d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/device-ssd-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/device-ssd.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/device-ssd.svg
new file mode 100644
index 00000000..6bedbd5f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/device-ssd.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/diagram-2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/diagram-2-fill.svg
new file mode 100644
index 00000000..7879ef6b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/diagram-2-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/diagram-2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/diagram-2.svg
new file mode 100644
index 00000000..31f990ed
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/diagram-2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/diagram-3-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/diagram-3-fill.svg
new file mode 100644
index 00000000..3326357c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/diagram-3-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/diagram-3.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/diagram-3.svg
new file mode 100644
index 00000000..5ed4479f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/diagram-3.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/diamond-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/diamond-fill.svg
new file mode 100644
index 00000000..bc4bf2f8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/diamond-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/diamond-half.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/diamond-half.svg
new file mode 100644
index 00000000..7c2ac0ff
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/diamond-half.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/diamond.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/diamond.svg
new file mode 100644
index 00000000..8217aac7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/diamond.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-1-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-1-fill.svg
new file mode 100644
index 00000000..b0c7d226
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-1-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-1.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-1.svg
new file mode 100644
index 00000000..ab490ea4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-1.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-2-fill.svg
new file mode 100644
index 00000000..119f3db2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-2-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-2.svg
new file mode 100644
index 00000000..16235ff6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-2.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-3-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-3-fill.svg
new file mode 100644
index 00000000..bfcb505a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-3-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-3.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-3.svg
new file mode 100644
index 00000000..b300de48
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-3.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-4-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-4-fill.svg
new file mode 100644
index 00000000..df35bb8a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-4-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-4.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-4.svg
new file mode 100644
index 00000000..8f029c06
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-4.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-5-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-5-fill.svg
new file mode 100644
index 00000000..5aee8ab7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-5-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-5.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-5.svg
new file mode 100644
index 00000000..b6537d0a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-5.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-6-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-6-fill.svg
new file mode 100644
index 00000000..893ba987
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-6-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-6.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-6.svg
new file mode 100644
index 00000000..7951fd8a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dice-6.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/disc-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/disc-fill.svg
new file mode 100644
index 00000000..894afd7e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/disc-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/disc.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/disc.svg
new file mode 100644
index 00000000..6f34164d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/disc.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/discord.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/discord.svg
new file mode 100644
index 00000000..336f0742
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/discord.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/display-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/display-fill.svg
new file mode 100644
index 00000000..74bb3453
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/display-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/display.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/display.svg
new file mode 100644
index 00000000..02fada9e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/display.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/displayport-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/displayport-fill.svg
new file mode 100644
index 00000000..20d62b66
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/displayport-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/displayport.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/displayport.svg
new file mode 100644
index 00000000..23d40242
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/displayport.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/distribute-horizontal.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/distribute-horizontal.svg
new file mode 100644
index 00000000..f9b2a763
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/distribute-horizontal.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/distribute-vertical.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/distribute-vertical.svg
new file mode 100644
index 00000000..3581aacb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/distribute-vertical.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/door-closed-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/door-closed-fill.svg
new file mode 100644
index 00000000..dbe79f99
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/door-closed-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/door-closed.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/door-closed.svg
new file mode 100644
index 00000000..248a3119
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/door-closed.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/door-open-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/door-open-fill.svg
new file mode 100644
index 00000000..80b5aa82
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/door-open-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/door-open.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/door-open.svg
new file mode 100644
index 00000000..9d91c618
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/door-open.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dot.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dot.svg
new file mode 100644
index 00000000..7fd6d746
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dot.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/download.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/download.svg
new file mode 100644
index 00000000..48521962
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/download.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dpad-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dpad-fill.svg
new file mode 100644
index 00000000..ad2c72e7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dpad-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dpad.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dpad.svg
new file mode 100644
index 00000000..92b1d81b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dpad.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dribbble.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dribbble.svg
new file mode 100644
index 00000000..13a3fd96
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dribbble.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/dropbox.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/dropbox.svg
new file mode 100644
index 00000000..a3fe91cf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/dropbox.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/droplet-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/droplet-fill.svg
new file mode 100644
index 00000000..a9c43403
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/droplet-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/droplet-half.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/droplet-half.svg
new file mode 100644
index 00000000..315c84c2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/droplet-half.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/droplet.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/droplet.svg
new file mode 100644
index 00000000..0d2cc20f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/droplet.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/duffle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/duffle-fill.svg
new file mode 100644
index 00000000..d1cc5861
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/duffle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/duffle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/duffle.svg
new file mode 100644
index 00000000..ba4bb9c9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/duffle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ear-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ear-fill.svg
new file mode 100644
index 00000000..b0bd553d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ear-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ear.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ear.svg
new file mode 100644
index 00000000..0313233e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ear.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/earbuds.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/earbuds.svg
new file mode 100644
index 00000000..a7cb730e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/earbuds.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/easel-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/easel-fill.svg
new file mode 100644
index 00000000..7dcff1d2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/easel-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/easel.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/easel.svg
new file mode 100644
index 00000000..efe2aa0b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/easel.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/easel2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/easel2-fill.svg
new file mode 100644
index 00000000..dcc33f34
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/easel2-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/easel2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/easel2.svg
new file mode 100644
index 00000000..23a73db9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/easel2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/easel3-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/easel3-fill.svg
new file mode 100644
index 00000000..d4ae7354
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/easel3-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/easel3.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/easel3.svg
new file mode 100644
index 00000000..27a43d60
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/easel3.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/egg-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/egg-fill.svg
new file mode 100644
index 00000000..4e951888
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/egg-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/egg-fried.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/egg-fried.svg
new file mode 100644
index 00000000..03753184
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/egg-fried.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/egg.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/egg.svg
new file mode 100644
index 00000000..664535d3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/egg.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/eject-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/eject-fill.svg
new file mode 100644
index 00000000..87378f92
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/eject-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/eject.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/eject.svg
new file mode 100644
index 00000000..c2d6dde5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/eject.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-angry-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-angry-fill.svg
new file mode 100644
index 00000000..535e7c6f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-angry-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-angry.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-angry.svg
new file mode 100644
index 00000000..88768fd0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-angry.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-astonished-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-astonished-fill.svg
new file mode 100644
index 00000000..67974cb8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-astonished-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-astonished.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-astonished.svg
new file mode 100644
index 00000000..f30aaaf9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-astonished.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-dizzy-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-dizzy-fill.svg
new file mode 100644
index 00000000..271e0311
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-dizzy-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-dizzy.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-dizzy.svg
new file mode 100644
index 00000000..15cd8e74
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-dizzy.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-expressionless-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-expressionless-fill.svg
new file mode 100644
index 00000000..cd311cf6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-expressionless-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-expressionless.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-expressionless.svg
new file mode 100644
index 00000000..6dbd1ebb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-expressionless.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-frown-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-frown-fill.svg
new file mode 100644
index 00000000..7e420e22
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-frown-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-frown.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-frown.svg
new file mode 100644
index 00000000..98a3bff9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-frown.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-grimace-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-grimace-fill.svg
new file mode 100644
index 00000000..2a1d7382
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-grimace-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-grimace.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-grimace.svg
new file mode 100644
index 00000000..f1118af8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-grimace.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-grin-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-grin-fill.svg
new file mode 100644
index 00000000..5f113908
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-grin-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-grin.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-grin.svg
new file mode 100644
index 00000000..31354adf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-grin.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-heart-eyes-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-heart-eyes-fill.svg
new file mode 100644
index 00000000..1436d762
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-heart-eyes-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-heart-eyes.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-heart-eyes.svg
new file mode 100644
index 00000000..8eadfc6c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-heart-eyes.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-kiss-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-kiss-fill.svg
new file mode 100644
index 00000000..31d5056d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-kiss-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-kiss.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-kiss.svg
new file mode 100644
index 00000000..dd3fd4b0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-kiss.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-laughing-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-laughing-fill.svg
new file mode 100644
index 00000000..2c81386b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-laughing-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-laughing.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-laughing.svg
new file mode 100644
index 00000000..eed9f847
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-laughing.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-neutral-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-neutral-fill.svg
new file mode 100644
index 00000000..6e1f05db
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-neutral-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-neutral.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-neutral.svg
new file mode 100644
index 00000000..509e9d1e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-neutral.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-smile-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-smile-fill.svg
new file mode 100644
index 00000000..f267edf1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-smile-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-smile-upside-down-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-smile-upside-down-fill.svg
new file mode 100644
index 00000000..e88567a4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-smile-upside-down-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-smile-upside-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-smile-upside-down.svg
new file mode 100644
index 00000000..f0eedc29
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-smile-upside-down.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-smile.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-smile.svg
new file mode 100644
index 00000000..c35f1a18
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-smile.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-sunglasses-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-sunglasses-fill.svg
new file mode 100644
index 00000000..44e9cb81
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-sunglasses-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-sunglasses.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-sunglasses.svg
new file mode 100644
index 00000000..ee910dce
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-sunglasses.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-surprise-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-surprise-fill.svg
new file mode 100644
index 00000000..d5e76523
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-surprise-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-surprise.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-surprise.svg
new file mode 100644
index 00000000..cbb0a94f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-surprise.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-tear-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-tear-fill.svg
new file mode 100644
index 00000000..6fdf98ee
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-tear-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-tear.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-tear.svg
new file mode 100644
index 00000000..fc724551
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-tear.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-wink-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-wink-fill.svg
new file mode 100644
index 00000000..bba38004
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-wink-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-wink.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-wink.svg
new file mode 100644
index 00000000..8d8f764f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/emoji-wink.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-arrow-down-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-arrow-down-fill.svg
new file mode 100644
index 00000000..abc8e0d0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-arrow-down-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-arrow-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-arrow-down.svg
new file mode 100644
index 00000000..65dd4c6b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-arrow-down.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-arrow-up-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-arrow-up-fill.svg
new file mode 100644
index 00000000..9d276c2f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-arrow-up-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-arrow-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-arrow-up.svg
new file mode 100644
index 00000000..2ebe82c2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-arrow-up.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-at-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-at-fill.svg
new file mode 100644
index 00000000..2a1d4923
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-at-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-at.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-at.svg
new file mode 100644
index 00000000..7a31442a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-at.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-check-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-check-fill.svg
new file mode 100644
index 00000000..261f7b3f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-check-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-check.svg
new file mode 100644
index 00000000..aa4bf8b8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-dash-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-dash-fill.svg
new file mode 100644
index 00000000..dd1cc6ad
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-dash-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-dash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-dash.svg
new file mode 100644
index 00000000..6e1c3fc2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-dash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-exclamation-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-exclamation-fill.svg
new file mode 100644
index 00000000..89ab3529
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-exclamation-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-exclamation.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-exclamation.svg
new file mode 100644
index 00000000..86423d8d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-exclamation.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-fill.svg
new file mode 100644
index 00000000..bda5ad12
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-heart-fill.svg
new file mode 100644
index 00000000..4a30e409
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-heart-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-heart.svg
new file mode 100644
index 00000000..bec48583
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-heart.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-open-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-open-fill.svg
new file mode 100644
index 00000000..f15f28e9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-open-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-open-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-open-heart-fill.svg
new file mode 100644
index 00000000..fee0dc82
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-open-heart-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-open-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-open-heart.svg
new file mode 100644
index 00000000..0a038da1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-open-heart.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-open.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-open.svg
new file mode 100644
index 00000000..1882d9f8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-open.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-paper-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-paper-fill.svg
new file mode 100644
index 00000000..6d47d0cb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-paper-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-paper-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-paper-heart-fill.svg
new file mode 100644
index 00000000..15e5a254
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-paper-heart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-paper-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-paper-heart.svg
new file mode 100644
index 00000000..363a32e1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-paper-heart.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-paper.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-paper.svg
new file mode 100644
index 00000000..6f2609f5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-paper.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-plus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-plus-fill.svg
new file mode 100644
index 00000000..f71b6c1b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-plus-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-plus.svg
new file mode 100644
index 00000000..53905284
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-plus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-slash-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-slash-fill.svg
new file mode 100644
index 00000000..9535d6b9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-slash-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-slash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-slash.svg
new file mode 100644
index 00000000..f3890923
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-slash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-x-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-x-fill.svg
new file mode 100644
index 00000000..4342bdc4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-x-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-x.svg
new file mode 100644
index 00000000..3da0e722
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope.svg
new file mode 100644
index 00000000..a007ad24
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/envelope.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/eraser-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/eraser-fill.svg
new file mode 100644
index 00000000..7cf55ddb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/eraser-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/eraser.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/eraser.svg
new file mode 100644
index 00000000..266b769d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/eraser.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/error.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/error.svg
new file mode 100644
index 00000000..25547844
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/error.svg
@@ -0,0 +1,4 @@
+
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/escape.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/escape.svg
new file mode 100644
index 00000000..34093a15
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/escape.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ethernet.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ethernet.svg
new file mode 100644
index 00000000..66b2e742
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ethernet.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ev-front-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ev-front-fill.svg
new file mode 100644
index 00000000..d55d1cb5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ev-front-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ev-front.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ev-front.svg
new file mode 100644
index 00000000..c50c0ab4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ev-front.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ev-station-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ev-station-fill.svg
new file mode 100644
index 00000000..9187f78b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ev-station-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ev-station.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ev-station.svg
new file mode 100644
index 00000000..56a0ba00
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ev-station.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-circle-fill.svg
new file mode 100644
index 00000000..4b06ee70
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-circle.svg
new file mode 100644
index 00000000..052a224e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-diamond-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-diamond-fill.svg
new file mode 100644
index 00000000..ceea6412
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-diamond-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-diamond.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-diamond.svg
new file mode 100644
index 00000000..affc7220
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-diamond.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-lg.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-lg.svg
new file mode 100644
index 00000000..8d60bd43
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-lg.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-octagon-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-octagon-fill.svg
new file mode 100644
index 00000000..e712c7b1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-octagon-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-octagon.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-octagon.svg
new file mode 100644
index 00000000..e3784405
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-octagon.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-square-fill.svg
new file mode 100644
index 00000000..7850fdb0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-square.svg
new file mode 100644
index 00000000..ede05a28
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-triangle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-triangle-fill.svg
new file mode 100644
index 00000000..b5779676
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-triangle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-triangle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-triangle.svg
new file mode 100644
index 00000000..8a446a92
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation-triangle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation.svg
new file mode 100644
index 00000000..44b6c6ee
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclamation.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/exclude.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclude.svg
new file mode 100644
index 00000000..488e25b1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/exclude.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/explicit-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/explicit-fill.svg
new file mode 100644
index 00000000..da66b73b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/explicit-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/explicit.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/explicit.svg
new file mode 100644
index 00000000..dc77a976
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/explicit.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/exposure.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/exposure.svg
new file mode 100644
index 00000000..44211f95
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/exposure.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/eye-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/eye-fill.svg
new file mode 100644
index 00000000..41a537fd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/eye-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/eye-slash-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/eye-slash-fill.svg
new file mode 100644
index 00000000..7a1b0691
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/eye-slash-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/eye-slash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/eye-slash.svg
new file mode 100644
index 00000000..6d6b01e2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/eye-slash.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/eye.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/eye.svg
new file mode 100644
index 00000000..5c18be17
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/eye.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/eyedropper.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/eyedropper.svg
new file mode 100644
index 00000000..e1518083
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/eyedropper.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/eyeglasses.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/eyeglasses.svg
new file mode 100644
index 00000000..cfd2e86a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/eyeglasses.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/facebook.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/facebook.svg
new file mode 100644
index 00000000..0c62bfe3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/facebook.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fan.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/fan.svg
new file mode 100644
index 00000000..eebf25c5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/fan.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward-btn-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward-btn-fill.svg
new file mode 100644
index 00000000..29e272fc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward-btn-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward-btn.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward-btn.svg
new file mode 100644
index 00000000..0f5c88a9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward-btn.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward-circle-fill.svg
new file mode 100644
index 00000000..ed127c2b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward-circle.svg
new file mode 100644
index 00000000..1144ae26
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward-fill.svg
new file mode 100644
index 00000000..79db4a7b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward.svg
new file mode 100644
index 00000000..bacaf150
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/fast-forward.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/feather.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/feather.svg
new file mode 100644
index 00000000..606e7504
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/feather.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/feather2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/feather2.svg
new file mode 100644
index 00000000..3df4a826
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/feather2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-arrow-down-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-arrow-down-fill.svg
new file mode 100644
index 00000000..634b3b73
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-arrow-down-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-arrow-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-arrow-down.svg
new file mode 100644
index 00000000..fca01f6d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-arrow-down.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-arrow-up-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-arrow-up-fill.svg
new file mode 100644
index 00000000..3ccae5a6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-arrow-up-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-arrow-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-arrow-up.svg
new file mode 100644
index 00000000..3426ae1e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-arrow-up.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-bar-graph-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-bar-graph-fill.svg
new file mode 100644
index 00000000..61ea5305
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-bar-graph-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-bar-graph.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-bar-graph.svg
new file mode 100644
index 00000000..073d1122
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-bar-graph.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-binary-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-binary-fill.svg
new file mode 100644
index 00000000..7b95ff49
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-binary-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-binary.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-binary.svg
new file mode 100644
index 00000000..a5d48251
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-binary.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-break-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-break-fill.svg
new file mode 100644
index 00000000..6a40d8a0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-break-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-break.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-break.svg
new file mode 100644
index 00000000..f65b897b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-break.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-check-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-check-fill.svg
new file mode 100644
index 00000000..c972d822
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-check-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-check.svg
new file mode 100644
index 00000000..57a2e39e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-code-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-code-fill.svg
new file mode 100644
index 00000000..c90f6f30
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-code-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-code.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-code.svg
new file mode 100644
index 00000000..e49ca8c1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-code.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-diff-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-diff-fill.svg
new file mode 100644
index 00000000..54ea881a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-diff-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-diff.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-diff.svg
new file mode 100644
index 00000000..2cb7fe40
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-diff.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-arrow-down-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-arrow-down-fill.svg
new file mode 100644
index 00000000..dd152730
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-arrow-down-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-arrow-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-arrow-down.svg
new file mode 100644
index 00000000..906884e0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-arrow-down.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-arrow-up-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-arrow-up-fill.svg
new file mode 100644
index 00000000..3d3119ec
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-arrow-up-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-arrow-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-arrow-up.svg
new file mode 100644
index 00000000..73e6de2a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-arrow-up.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-bar-graph-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-bar-graph-fill.svg
new file mode 100644
index 00000000..9cba979d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-bar-graph-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-bar-graph.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-bar-graph.svg
new file mode 100644
index 00000000..5bd274d6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-bar-graph.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-binary-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-binary-fill.svg
new file mode 100644
index 00000000..813be71f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-binary-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-binary.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-binary.svg
new file mode 100644
index 00000000..68c4fcc7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-binary.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-break-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-break-fill.svg
new file mode 100644
index 00000000..e045834c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-break-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-break.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-break.svg
new file mode 100644
index 00000000..45a38e96
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-break.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-check-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-check-fill.svg
new file mode 100644
index 00000000..daf36eb7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-check-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-check.svg
new file mode 100644
index 00000000..514caf37
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-code-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-code-fill.svg
new file mode 100644
index 00000000..689ad4e1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-code-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-code.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-code.svg
new file mode 100644
index 00000000..8751019e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-code.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-diff-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-diff-fill.svg
new file mode 100644
index 00000000..9aeb2117
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-diff-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-diff.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-diff.svg
new file mode 100644
index 00000000..14d6b7c9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-diff.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-easel-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-easel-fill.svg
new file mode 100644
index 00000000..82b21ace
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-easel-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-easel.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-easel.svg
new file mode 100644
index 00000000..a76b14fb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-easel.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-excel-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-excel-fill.svg
new file mode 100644
index 00000000..57937252
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-excel-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-excel.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-excel.svg
new file mode 100644
index 00000000..81237c5b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-excel.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-fill.svg
new file mode 100644
index 00000000..caf37371
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-font-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-font-fill.svg
new file mode 100644
index 00000000..1c29751b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-font-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-font.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-font.svg
new file mode 100644
index 00000000..2ba19e14
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-font.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-image-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-image-fill.svg
new file mode 100644
index 00000000..02a8318c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-image-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-image.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-image.svg
new file mode 100644
index 00000000..40a6b4ad
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-image.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-lock-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-lock-fill.svg
new file mode 100644
index 00000000..8836a82b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-lock-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-lock.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-lock.svg
new file mode 100644
index 00000000..fa1455ba
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-lock.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-lock2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-lock2-fill.svg
new file mode 100644
index 00000000..2be3fd6c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-lock2-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-lock2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-lock2.svg
new file mode 100644
index 00000000..ce87e8e0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-lock2.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-medical-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-medical-fill.svg
new file mode 100644
index 00000000..b93cb85a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-medical-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-medical.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-medical.svg
new file mode 100644
index 00000000..56ca0054
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-medical.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-minus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-minus-fill.svg
new file mode 100644
index 00000000..bee9808c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-minus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-minus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-minus.svg
new file mode 100644
index 00000000..d56a6822
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-minus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-music-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-music-fill.svg
new file mode 100644
index 00000000..96585322
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-music-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-music.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-music.svg
new file mode 100644
index 00000000..ab7e02eb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-music.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-pdf-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-pdf-fill.svg
new file mode 100644
index 00000000..9f796cb2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-pdf-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-pdf.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-pdf.svg
new file mode 100644
index 00000000..839d6bc4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-pdf.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-person-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-person-fill.svg
new file mode 100644
index 00000000..7504d217
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-person-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-person.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-person.svg
new file mode 100644
index 00000000..7397ceb9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-person.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-play-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-play-fill.svg
new file mode 100644
index 00000000..3a6ad9ba
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-play-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-play.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-play.svg
new file mode 100644
index 00000000..ac5df5ca
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-play.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-plus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-plus-fill.svg
new file mode 100644
index 00000000..3139b310
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-plus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-plus.svg
new file mode 100644
index 00000000..d9f4c9b0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-plus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-post-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-post-fill.svg
new file mode 100644
index 00000000..7934a440
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-post-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-post.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-post.svg
new file mode 100644
index 00000000..cb1507cf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-post.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-ppt-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-ppt-fill.svg
new file mode 100644
index 00000000..3b8efa99
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-ppt-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-ppt.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-ppt.svg
new file mode 100644
index 00000000..9cefb5e9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-ppt.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-richtext-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-richtext-fill.svg
new file mode 100644
index 00000000..71f8e0b4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-richtext-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-richtext.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-richtext.svg
new file mode 100644
index 00000000..7a2081ae
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-richtext.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-ruled-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-ruled-fill.svg
new file mode 100644
index 00000000..e5e52aa7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-ruled-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-ruled.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-ruled.svg
new file mode 100644
index 00000000..3777fdd2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-ruled.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-slides-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-slides-fill.svg
new file mode 100644
index 00000000..92a07158
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-slides-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-slides.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-slides.svg
new file mode 100644
index 00000000..dca4246b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-slides.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-spreadsheet-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-spreadsheet-fill.svg
new file mode 100644
index 00000000..57b9a9bb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-spreadsheet-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-spreadsheet.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-spreadsheet.svg
new file mode 100644
index 00000000..530400f0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-spreadsheet.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-text-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-text-fill.svg
new file mode 100644
index 00000000..f3e19fce
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-text-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-text.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-text.svg
new file mode 100644
index 00000000..e8c822a5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-text.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-word-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-word-fill.svg
new file mode 100644
index 00000000..ce8feb51
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-word-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-word.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-word.svg
new file mode 100644
index 00000000..084460dd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-word.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-x-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-x-fill.svg
new file mode 100644
index 00000000..87440cd2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-x-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-x.svg
new file mode 100644
index 00000000..cd3e03a3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-zip-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-zip-fill.svg
new file mode 100644
index 00000000..7260f7c1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-zip-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-zip.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-zip.svg
new file mode 100644
index 00000000..0bd3bef5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark-zip.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark.svg
new file mode 100644
index 00000000..6b3016f1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-earmark.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-easel-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-easel-fill.svg
new file mode 100644
index 00000000..89bef306
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-easel-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-easel.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-easel.svg
new file mode 100644
index 00000000..b9484af8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-easel.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-excel-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-excel-fill.svg
new file mode 100644
index 00000000..cb64546f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-excel-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-excel.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-excel.svg
new file mode 100644
index 00000000..d88a5a50
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-excel.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-fill.svg
new file mode 100644
index 00000000..edeeebe8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-font-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-font-fill.svg
new file mode 100644
index 00000000..4405d2be
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-font-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-font.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-font.svg
new file mode 100644
index 00000000..e45a9069
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-font.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-image-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-image-fill.svg
new file mode 100644
index 00000000..66a6b14a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-image-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-image.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-image.svg
new file mode 100644
index 00000000..54af5f1f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-image.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-lock-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-lock-fill.svg
new file mode 100644
index 00000000..e549c40f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-lock-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-lock.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-lock.svg
new file mode 100644
index 00000000..a9d45c85
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-lock.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-lock2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-lock2-fill.svg
new file mode 100644
index 00000000..f02b9998
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-lock2-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-lock2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-lock2.svg
new file mode 100644
index 00000000..500453ad
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-lock2.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-medical-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-medical-fill.svg
new file mode 100644
index 00000000..3e6b2088
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-medical-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-medical.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-medical.svg
new file mode 100644
index 00000000..d1a1147b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-medical.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-minus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-minus-fill.svg
new file mode 100644
index 00000000..43a55582
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-minus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-minus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-minus.svg
new file mode 100644
index 00000000..97a81b8e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-minus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-music-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-music-fill.svg
new file mode 100644
index 00000000..55eb8418
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-music-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-music.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-music.svg
new file mode 100644
index 00000000..d9cebb81
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-music.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-pdf-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-pdf-fill.svg
new file mode 100644
index 00000000..8113eb84
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-pdf-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-pdf.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-pdf.svg
new file mode 100644
index 00000000..52651123
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-pdf.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-person-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-person-fill.svg
new file mode 100644
index 00000000..b48e7b2e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-person-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-person.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-person.svg
new file mode 100644
index 00000000..c0a90bb4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-person.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-play-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-play-fill.svg
new file mode 100644
index 00000000..58f570de
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-play-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-play.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-play.svg
new file mode 100644
index 00000000..60629ee3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-play.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-plus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-plus-fill.svg
new file mode 100644
index 00000000..bdc48781
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-plus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-plus.svg
new file mode 100644
index 00000000..b500d7a1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-plus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-post-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-post-fill.svg
new file mode 100644
index 00000000..4ebb9542
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-post-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-post.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-post.svg
new file mode 100644
index 00000000..4b0dc1a0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-post.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-ppt-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-ppt-fill.svg
new file mode 100644
index 00000000..9b84c050
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-ppt-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-ppt.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-ppt.svg
new file mode 100644
index 00000000..9f6302e6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-ppt.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-richtext-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-richtext-fill.svg
new file mode 100644
index 00000000..8f4f90df
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-richtext-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-richtext.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-richtext.svg
new file mode 100644
index 00000000..277b00db
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-richtext.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-ruled-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-ruled-fill.svg
new file mode 100644
index 00000000..c90b2648
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-ruled-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-ruled.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-ruled.svg
new file mode 100644
index 00000000..ebf2df2a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-ruled.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-slides-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-slides-fill.svg
new file mode 100644
index 00000000..fcba9632
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-slides-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-slides.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-slides.svg
new file mode 100644
index 00000000..0b5a8716
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-slides.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-spreadsheet-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-spreadsheet-fill.svg
new file mode 100644
index 00000000..2ce68734
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-spreadsheet-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-spreadsheet.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-spreadsheet.svg
new file mode 100644
index 00000000..36ee79fb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-spreadsheet.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-text-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-text-fill.svg
new file mode 100644
index 00000000..5e25373a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-text-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-text.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-text.svg
new file mode 100644
index 00000000..10277208
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-text.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-word-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-word-fill.svg
new file mode 100644
index 00000000..41a5ad45
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-word-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-word.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-word.svg
new file mode 100644
index 00000000..1cb7ab83
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-word.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-x-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-x-fill.svg
new file mode 100644
index 00000000..4c5b5b39
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-x-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-x.svg
new file mode 100644
index 00000000..fc27740c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-zip-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-zip-fill.svg
new file mode 100644
index 00000000..33f6c82e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-zip-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file-zip.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-zip.svg
new file mode 100644
index 00000000..7860a4ee
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file-zip.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/file.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/file.svg
new file mode 100644
index 00000000..6504fd45
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/file.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/files-alt.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/files-alt.svg
new file mode 100644
index 00000000..a019ec03
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/files-alt.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/files.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/files.svg
new file mode 100644
index 00000000..e08eb2fc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/files.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-aac.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-aac.svg
new file mode 100644
index 00000000..a5e58d50
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-aac.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-ai.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-ai.svg
new file mode 100644
index 00000000..5116a7d1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-ai.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-bmp.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-bmp.svg
new file mode 100644
index 00000000..41f8dde7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-bmp.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-cs.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-cs.svg
new file mode 100644
index 00000000..2f9d98d0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-cs.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-css.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-css.svg
new file mode 100644
index 00000000..69e00c3f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-css.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-csv.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-csv.svg
new file mode 100644
index 00000000..a800f151
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-csv.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-doc.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-doc.svg
new file mode 100644
index 00000000..035795fe
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-doc.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-docx.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-docx.svg
new file mode 100644
index 00000000..6186a5ca
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-docx.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-exe.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-exe.svg
new file mode 100644
index 00000000..eb53980d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-exe.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-gif.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-gif.svg
new file mode 100644
index 00000000..a9db28f2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-gif.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-heic.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-heic.svg
new file mode 100644
index 00000000..29278528
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-heic.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-html.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-html.svg
new file mode 100644
index 00000000..4f6a3325
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-html.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-java.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-java.svg
new file mode 100644
index 00000000..907504cd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-java.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-jpg.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-jpg.svg
new file mode 100644
index 00000000..17c9c519
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-jpg.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-js.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-js.svg
new file mode 100644
index 00000000..2b56f006
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-js.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-json.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-json.svg
new file mode 100644
index 00000000..47582ba8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-json.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-jsx.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-jsx.svg
new file mode 100644
index 00000000..34e2401e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-jsx.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-key.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-key.svg
new file mode 100644
index 00000000..53c98730
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-key.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-m4p.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-m4p.svg
new file mode 100644
index 00000000..61856a2e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-m4p.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-md.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-md.svg
new file mode 100644
index 00000000..cacd0ae2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-md.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-mdx.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-mdx.svg
new file mode 100644
index 00000000..154017ee
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-mdx.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-mov.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-mov.svg
new file mode 100644
index 00000000..ccaea6d7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-mov.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-mp3.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-mp3.svg
new file mode 100644
index 00000000..611cda84
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-mp3.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-mp4.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-mp4.svg
new file mode 100644
index 00000000..ea2d9518
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-mp4.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-otf.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-otf.svg
new file mode 100644
index 00000000..ef991ffb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-otf.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-pdf.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-pdf.svg
new file mode 100644
index 00000000..093affe7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-pdf.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-php.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-php.svg
new file mode 100644
index 00000000..22da3d4f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-php.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-png.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-png.svg
new file mode 100644
index 00000000..f1232306
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-png.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-ppt.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-ppt.svg
new file mode 100644
index 00000000..9c852717
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-ppt.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-pptx.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-pptx.svg
new file mode 100644
index 00000000..7ae9adea
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-pptx.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-psd.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-psd.svg
new file mode 100644
index 00000000..3b66c801
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-psd.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-py.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-py.svg
new file mode 100644
index 00000000..bf490ff7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-py.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-raw.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-raw.svg
new file mode 100644
index 00000000..6e3abdc2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-raw.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-rb.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-rb.svg
new file mode 100644
index 00000000..5b4b09fc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-rb.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-sass.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-sass.svg
new file mode 100644
index 00000000..f377204e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-sass.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-scss.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-scss.svg
new file mode 100644
index 00000000..b9ddfb82
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-scss.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-sh.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-sh.svg
new file mode 100644
index 00000000..a99f8291
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-sh.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-sql.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-sql.svg
new file mode 100644
index 00000000..37ecdf76
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-sql.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-svg.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-svg.svg
new file mode 100644
index 00000000..31eef0bf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-svg.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-tiff.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-tiff.svg
new file mode 100644
index 00000000..df5f8974
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-tiff.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-tsx.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-tsx.svg
new file mode 100644
index 00000000..fd60cff0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-tsx.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-ttf.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-ttf.svg
new file mode 100644
index 00000000..4f66f1d7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-ttf.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-txt.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-txt.svg
new file mode 100644
index 00000000..24a98ba3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-txt.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-wav.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-wav.svg
new file mode 100644
index 00000000..539e73c0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-wav.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-woff.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-woff.svg
new file mode 100644
index 00000000..2db38ac2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-woff.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-xls.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-xls.svg
new file mode 100644
index 00000000..7c3669c4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-xls.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-xlsx.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-xlsx.svg
new file mode 100644
index 00000000..73925540
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-xlsx.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-xml.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-xml.svg
new file mode 100644
index 00000000..80edbfd0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-xml.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-yml.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-yml.svg
new file mode 100644
index 00000000..c0c6e79b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filetype-yml.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/film.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/film.svg
new file mode 100644
index 00000000..fa8ba5fe
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/film.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-circle-fill.svg
new file mode 100644
index 00000000..338c54f8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-circle.svg
new file mode 100644
index 00000000..096eeaa5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-left.svg
new file mode 100644
index 00000000..6ca2224c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-left.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-right.svg
new file mode 100644
index 00000000..724f248d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-square-fill.svg
new file mode 100644
index 00000000..bcab13eb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-square.svg
new file mode 100644
index 00000000..5fa1698f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filter-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/filter.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/filter.svg
new file mode 100644
index 00000000..fc1ae38a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/filter.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fingerprint.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/fingerprint.svg
new file mode 100644
index 00000000..5b9375f2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/fingerprint.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fire.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/fire.svg
new file mode 100644
index 00000000..889eaf16
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/fire.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/flag-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/flag-fill.svg
new file mode 100644
index 00000000..1387cb20
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/flag-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/flag.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/flag.svg
new file mode 100644
index 00000000..980008f5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/flag.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/flask-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/flask-fill.svg
new file mode 100644
index 00000000..a907fb95
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/flask-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/flask-florence-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/flask-florence-fill.svg
new file mode 100644
index 00000000..6d163c98
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/flask-florence-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/flask-florence.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/flask-florence.svg
new file mode 100644
index 00000000..f68452d3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/flask-florence.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/flask.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/flask.svg
new file mode 100644
index 00000000..d310ba73
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/flask.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/floppy-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/floppy-fill.svg
new file mode 100644
index 00000000..273238fb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/floppy-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/floppy.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/floppy.svg
new file mode 100644
index 00000000..a43865fb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/floppy.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/floppy2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/floppy2-fill.svg
new file mode 100644
index 00000000..fa2800f9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/floppy2-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/floppy2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/floppy2.svg
new file mode 100644
index 00000000..08c29780
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/floppy2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/flower1.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/flower1.svg
new file mode 100644
index 00000000..4328ee38
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/flower1.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/flower2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/flower2.svg
new file mode 100644
index 00000000..c53a3cc6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/flower2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/flower3.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/flower3.svg
new file mode 100644
index 00000000..c7dbe5a8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/flower3.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-check.svg
new file mode 100644
index 00000000..a0e0e6df
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-fill.svg
new file mode 100644
index 00000000..1fcf91e9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-minus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-minus.svg
new file mode 100644
index 00000000..54d3de75
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-minus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-plus.svg
new file mode 100644
index 00000000..2cb0d18b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-plus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-symlink-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-symlink-fill.svg
new file mode 100644
index 00000000..b82da98f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-symlink-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-symlink.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-symlink.svg
new file mode 100644
index 00000000..583fa1aa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-symlink.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-x.svg
new file mode 100644
index 00000000..e0009bdd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/folder.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder.svg
new file mode 100644
index 00000000..667f5a6b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/folder2-open.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder2-open.svg
new file mode 100644
index 00000000..bc9e1a79
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder2-open.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/folder2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder2.svg
new file mode 100644
index 00000000..39f8911c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/folder2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fonts.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/fonts.svg
new file mode 100644
index 00000000..7f7b4faf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/fonts.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fonts/bootstrap-icons.woff b/dist/macos/Replicator.app/Contents/Resources/core/icons/fonts/bootstrap-icons.woff
new file mode 100644
index 00000000..a4fa4f02
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Resources/core/icons/fonts/bootstrap-icons.woff differ
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fonts/bootstrap-icons.woff2 b/dist/macos/Replicator.app/Contents/Resources/core/icons/fonts/bootstrap-icons.woff2
new file mode 100644
index 00000000..4d8c490e
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Resources/core/icons/fonts/bootstrap-icons.woff2 differ
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fork-knife.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/fork-knife.svg
new file mode 100644
index 00000000..8ec2a066
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/fork-knife.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/forward-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/forward-fill.svg
new file mode 100644
index 00000000..6413d7dd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/forward-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/forward.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/forward.svg
new file mode 100644
index 00000000..20cc05d0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/forward.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/front.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/front.svg
new file mode 100644
index 00000000..c64f3aa5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/front.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fuel-pump-diesel-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/fuel-pump-diesel-fill.svg
new file mode 100644
index 00000000..c3038ed7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/fuel-pump-diesel-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fuel-pump-diesel.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/fuel-pump-diesel.svg
new file mode 100644
index 00000000..3ea136e3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/fuel-pump-diesel.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fuel-pump-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/fuel-pump-fill.svg
new file mode 100644
index 00000000..3dcb7a2f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/fuel-pump-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fuel-pump.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/fuel-pump.svg
new file mode 100644
index 00000000..eb98a976
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/fuel-pump.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fullscreen-exit.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/fullscreen-exit.svg
new file mode 100644
index 00000000..3fb6655c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/fullscreen-exit.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/fullscreen.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/fullscreen.svg
new file mode 100644
index 00000000..f1701ee3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/fullscreen.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/funnel-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/funnel-fill.svg
new file mode 100644
index 00000000..440cd285
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/funnel-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/funnel.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/funnel.svg
new file mode 100644
index 00000000..f8906a59
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/funnel.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/gear-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/gear-fill.svg
new file mode 100644
index 00000000..a0e88a05
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/gear-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/gear-wide-connected.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/gear-wide-connected.svg
new file mode 100644
index 00000000..88e6ff7b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/gear-wide-connected.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/gear-wide.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/gear-wide.svg
new file mode 100644
index 00000000..f962f34b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/gear-wide.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/gear.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/gear.svg
new file mode 100644
index 00000000..6cd0bc84
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/gear.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/gem.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/gem.svg
new file mode 100644
index 00000000..30e15990
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/gem.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/gender-ambiguous.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/gender-ambiguous.svg
new file mode 100644
index 00000000..f6737d61
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/gender-ambiguous.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/gender-female.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/gender-female.svg
new file mode 100644
index 00000000..d152bdd2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/gender-female.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/gender-male.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/gender-male.svg
new file mode 100644
index 00000000..838ad00c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/gender-male.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/gender-neuter.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/gender-neuter.svg
new file mode 100644
index 00000000..5d175acf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/gender-neuter.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/gender-trans.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/gender-trans.svg
new file mode 100644
index 00000000..6d2369ba
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/gender-trans.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/geo-alt-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/geo-alt-fill.svg
new file mode 100644
index 00000000..ba95e33d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/geo-alt-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/geo-alt.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/geo-alt.svg
new file mode 100644
index 00000000..046251c8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/geo-alt.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/geo-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/geo-fill.svg
new file mode 100644
index 00000000..64faefe2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/geo-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/geo.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/geo.svg
new file mode 100644
index 00000000..178caaed
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/geo.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/gift-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/gift-fill.svg
new file mode 100644
index 00000000..3d831129
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/gift-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/gift.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/gift.svg
new file mode 100644
index 00000000..ceec17cc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/gift.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/git.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/git.svg
new file mode 100644
index 00000000..abf5db37
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/git.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/github.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/github.svg
new file mode 100644
index 00000000..b3e91c88
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/github.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/gitlab.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/gitlab.svg
new file mode 100644
index 00000000..51d36d58
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/gitlab.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-americas-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-americas-fill.svg
new file mode 100644
index 00000000..df4740fa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-americas-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-americas.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-americas.svg
new file mode 100644
index 00000000..4acda64b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-americas.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-asia-australia-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-asia-australia-fill.svg
new file mode 100644
index 00000000..63411998
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-asia-australia-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-asia-australia.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-asia-australia.svg
new file mode 100644
index 00000000..e9cd1e4d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-asia-australia.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-central-south-asia-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-central-south-asia-fill.svg
new file mode 100644
index 00000000..7956ccd6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-central-south-asia-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-central-south-asia.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-central-south-asia.svg
new file mode 100644
index 00000000..1f70d7f0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-central-south-asia.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-europe-africa-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-europe-africa-fill.svg
new file mode 100644
index 00000000..621bc566
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-europe-africa-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-europe-africa.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-europe-africa.svg
new file mode 100644
index 00000000..4e5a5e1f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe-europe-africa.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/globe.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe.svg
new file mode 100644
index 00000000..4fdbb1f1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/globe2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe2.svg
new file mode 100644
index 00000000..e2b07880
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/globe2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/google-play.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/google-play.svg
new file mode 100644
index 00000000..10c6649f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/google-play.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/google.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/google.svg
new file mode 100644
index 00000000..be86f6e1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/google.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/gpu-card.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/gpu-card.svg
new file mode 100644
index 00000000..41663050
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/gpu-card.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/graph-down-arrow.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/graph-down-arrow.svg
new file mode 100644
index 00000000..3fee3999
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/graph-down-arrow.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/graph-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/graph-down.svg
new file mode 100644
index 00000000..a78d2f15
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/graph-down.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/graph-up-arrow.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/graph-up-arrow.svg
new file mode 100644
index 00000000..90980fd5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/graph-up-arrow.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/graph-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/graph-up.svg
new file mode 100644
index 00000000..6c6203d6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/graph-up.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-1x2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-1x2-fill.svg
new file mode 100644
index 00000000..5424db93
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-1x2-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-1x2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-1x2.svg
new file mode 100644
index 00000000..14b3381e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-1x2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x2-gap-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x2-gap-fill.svg
new file mode 100644
index 00000000..6b4ccab0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x2-gap-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x2-gap.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x2-gap.svg
new file mode 100644
index 00000000..705ea842
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x2-gap.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x2.svg
new file mode 100644
index 00000000..7191c9fe
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x3-gap-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x3-gap-fill.svg
new file mode 100644
index 00000000..1af175d4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x3-gap-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x3-gap.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x3-gap.svg
new file mode 100644
index 00000000..4c010dda
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x3-gap.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x3.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x3.svg
new file mode 100644
index 00000000..67d1ed5c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-3x3.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-fill.svg
new file mode 100644
index 00000000..2ae61efd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/grid.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid.svg
new file mode 100644
index 00000000..0174fbf2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/grid.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/grip-horizontal.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/grip-horizontal.svg
new file mode 100644
index 00000000..faca8516
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/grip-horizontal.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/grip-vertical.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/grip-vertical.svg
new file mode 100644
index 00000000..b84346f6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/grip-vertical.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/h-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/h-circle-fill.svg
new file mode 100644
index 00000000..6b6760a0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/h-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/h-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/h-circle.svg
new file mode 100644
index 00000000..93803be6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/h-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/h-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/h-square-fill.svg
new file mode 100644
index 00000000..071904aa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/h-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/h-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/h-square.svg
new file mode 100644
index 00000000..19933fdc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/h-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hammer.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hammer.svg
new file mode 100644
index 00000000..d1f5ecfd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hammer.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-index-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-index-fill.svg
new file mode 100644
index 00000000..234e3cfb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-index-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-index-thumb-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-index-thumb-fill.svg
new file mode 100644
index 00000000..cd37f39a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-index-thumb-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-index-thumb.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-index-thumb.svg
new file mode 100644
index 00000000..b96dea55
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-index-thumb.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-index.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-index.svg
new file mode 100644
index 00000000..3532e65c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-index.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-thumbs-down-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-thumbs-down-fill.svg
new file mode 100644
index 00000000..156f8f13
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-thumbs-down-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-thumbs-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-thumbs-down.svg
new file mode 100644
index 00000000..50bb0af7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-thumbs-down.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-thumbs-up-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-thumbs-up-fill.svg
new file mode 100644
index 00000000..da08e560
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-thumbs-up-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-thumbs-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-thumbs-up.svg
new file mode 100644
index 00000000..2f1e3bf6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hand-thumbs-up.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/handbag-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/handbag-fill.svg
new file mode 100644
index 00000000..caf651f5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/handbag-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/handbag.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/handbag.svg
new file mode 100644
index 00000000..be7699a3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/handbag.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hash.svg
new file mode 100644
index 00000000..0388f214
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hash.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-fill.svg
new file mode 100644
index 00000000..682f2bc1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-network-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-network-fill.svg
new file mode 100644
index 00000000..8adf6484
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-network-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-network.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-network.svg
new file mode 100644
index 00000000..690bc0fb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-network.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-rack-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-rack-fill.svg
new file mode 100644
index 00000000..06966949
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-rack-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-rack.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-rack.svg
new file mode 100644
index 00000000..c98d8e20
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-rack.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-stack-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-stack-fill.svg
new file mode 100644
index 00000000..4d67e5e8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-stack-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-stack.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-stack.svg
new file mode 100644
index 00000000..3b4fbab3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd-stack.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd.svg
new file mode 100644
index 00000000..596dc38c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdd.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hdmi-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdmi-fill.svg
new file mode 100644
index 00000000..1929b1be
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdmi-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hdmi.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdmi.svg
new file mode 100644
index 00000000..2427a9ce
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hdmi.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/headphones.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/headphones.svg
new file mode 100644
index 00000000..1696ead9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/headphones.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/headset-vr.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/headset-vr.svg
new file mode 100644
index 00000000..ca8e7300
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/headset-vr.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/headset.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/headset.svg
new file mode 100644
index 00000000..82e2634b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/headset.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/heart-arrow.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/heart-arrow.svg
new file mode 100644
index 00000000..1bc2b95a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/heart-arrow.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/heart-fill.svg
new file mode 100644
index 00000000..e2eb9807
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/heart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/heart-half.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/heart-half.svg
new file mode 100644
index 00000000..09517d44
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/heart-half.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/heart-pulse-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/heart-pulse-fill.svg
new file mode 100644
index 00000000..75a00f34
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/heart-pulse-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/heart-pulse.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/heart-pulse.svg
new file mode 100644
index 00000000..77e966a7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/heart-pulse.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/heart.svg
new file mode 100644
index 00000000..f0fdd80f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/heart.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/heartbreak-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/heartbreak-fill.svg
new file mode 100644
index 00000000..14228096
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/heartbreak-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/heartbreak.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/heartbreak.svg
new file mode 100644
index 00000000..7b654ca1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/heartbreak.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hearts.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hearts.svg
new file mode 100644
index 00000000..2bb9b77d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hearts.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/heptagon-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/heptagon-fill.svg
new file mode 100644
index 00000000..863928d3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/heptagon-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/heptagon-half.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/heptagon-half.svg
new file mode 100644
index 00000000..59f08166
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/heptagon-half.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/heptagon.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/heptagon.svg
new file mode 100644
index 00000000..bd97be8c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/heptagon.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hexagon-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hexagon-fill.svg
new file mode 100644
index 00000000..0b217f83
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hexagon-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hexagon-half.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hexagon-half.svg
new file mode 100644
index 00000000..c61e536d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hexagon-half.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hexagon.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hexagon.svg
new file mode 100644
index 00000000..febd0929
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hexagon.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/highlighter.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/highlighter.svg
new file mode 100644
index 00000000..f73c070c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/highlighter.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/highlights.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/highlights.svg
new file mode 100644
index 00000000..ae39ba9b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/highlights.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hospital-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hospital-fill.svg
new file mode 100644
index 00000000..dc5a3989
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hospital-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hospital.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hospital.svg
new file mode 100644
index 00000000..c03e7bd8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hospital.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hourglass-bottom.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hourglass-bottom.svg
new file mode 100644
index 00000000..43bcc948
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hourglass-bottom.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hourglass-split.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hourglass-split.svg
new file mode 100644
index 00000000..3f2bf1a4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hourglass-split.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hourglass-top.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hourglass-top.svg
new file mode 100644
index 00000000..744689e7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hourglass-top.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hourglass.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hourglass.svg
new file mode 100644
index 00000000..391344af
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hourglass.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-add-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-add-fill.svg
new file mode 100644
index 00000000..791e1c5d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-add-fill.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-add.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-add.svg
new file mode 100644
index 00000000..32e9933c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-add.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-check-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-check-fill.svg
new file mode 100644
index 00000000..b55c22cf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-check-fill.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-check.svg
new file mode 100644
index 00000000..89cb7b1c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-dash-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-dash-fill.svg
new file mode 100644
index 00000000..23e6239e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-dash-fill.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-dash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-dash.svg
new file mode 100644
index 00000000..a9f43a57
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-dash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-door-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-door-fill.svg
new file mode 100644
index 00000000..6bf50cab
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-door-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-door.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-door.svg
new file mode 100644
index 00000000..1805cb18
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-door.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-down-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-down-fill.svg
new file mode 100644
index 00000000..c442663f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-down-fill.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-down.svg
new file mode 100644
index 00000000..1434079b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-down.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-exclamation-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-exclamation-fill.svg
new file mode 100644
index 00000000..fabbef4b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-exclamation-fill.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-exclamation.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-exclamation.svg
new file mode 100644
index 00000000..fe624631
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-exclamation.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-fill.svg
new file mode 100644
index 00000000..78600749
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-gear-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-gear-fill.svg
new file mode 100644
index 00000000..6d713d27
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-gear-fill.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-gear.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-gear.svg
new file mode 100644
index 00000000..4816aba9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-gear.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-heart-fill.svg
new file mode 100644
index 00000000..a9a59524
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-heart-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-heart.svg
new file mode 100644
index 00000000..b71f191c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-heart.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-lock-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-lock-fill.svg
new file mode 100644
index 00000000..9a52b5bf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-lock-fill.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-lock.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-lock.svg
new file mode 100644
index 00000000..b85f0533
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-lock.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-slash-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-slash-fill.svg
new file mode 100644
index 00000000..1978aeef
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-slash-fill.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-slash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-slash.svg
new file mode 100644
index 00000000..81f51b4f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-slash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-up-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-up-fill.svg
new file mode 100644
index 00000000..4583df29
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-up-fill.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-up.svg
new file mode 100644
index 00000000..a6dc1f05
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-up.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-x-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-x-fill.svg
new file mode 100644
index 00000000..843fe3cb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-x-fill.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-x.svg
new file mode 100644
index 00000000..b131fd71
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/house.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/house.svg
new file mode 100644
index 00000000..0441f47c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/house.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/houses-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/houses-fill.svg
new file mode 100644
index 00000000..180daabe
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/houses-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/houses.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/houses.svg
new file mode 100644
index 00000000..55072a67
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/houses.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hr.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hr.svg
new file mode 100644
index 00000000..449a959e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hr.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hurricane.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hurricane.svg
new file mode 100644
index 00000000..3fb068e4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hurricane.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/hypnotize.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/hypnotize.svg
new file mode 100644
index 00000000..ac4857ac
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/hypnotize.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/image-alt.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/image-alt.svg
new file mode 100644
index 00000000..d0d3360d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/image-alt.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/image-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/image-fill.svg
new file mode 100644
index 00000000..c7f206da
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/image-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/image.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/image.svg
new file mode 100644
index 00000000..b72aa821
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/image.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/images.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/images.svg
new file mode 100644
index 00000000..096a178b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/images.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/inbox-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/inbox-fill.svg
new file mode 100644
index 00000000..e26d5e0a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/inbox-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/inbox.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/inbox.svg
new file mode 100644
index 00000000..ac018f26
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/inbox.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/inboxes-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/inboxes-fill.svg
new file mode 100644
index 00000000..621e6fa7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/inboxes-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/inboxes.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/inboxes.svg
new file mode 100644
index 00000000..2a5ed7b4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/inboxes.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/incognito.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/incognito.svg
new file mode 100644
index 00000000..b2ebb921
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/incognito.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/indent.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/indent.svg
new file mode 100644
index 00000000..735978a3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/indent.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/infinity.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/infinity.svg
new file mode 100644
index 00000000..897c5c4a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/infinity.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/info-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/info-circle-fill.svg
new file mode 100644
index 00000000..f5679410
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/info-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/info-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/info-circle.svg
new file mode 100644
index 00000000..4a82b5eb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/info-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/info-lg.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/info-lg.svg
new file mode 100644
index 00000000..c42d7cdf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/info-lg.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/info-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/info-square-fill.svg
new file mode 100644
index 00000000..fe74563e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/info-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/info-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/info-square.svg
new file mode 100644
index 00000000..50650fba
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/info-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/info.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/info.svg
new file mode 100644
index 00000000..40bc6dec
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/info.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/input-cursor-text.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/input-cursor-text.svg
new file mode 100644
index 00000000..408da037
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/input-cursor-text.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/input-cursor.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/input-cursor.svg
new file mode 100644
index 00000000..2aae276e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/input-cursor.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/instagram.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/instagram.svg
new file mode 100644
index 00000000..3e852866
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/instagram.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/intersect.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/intersect.svg
new file mode 100644
index 00000000..3a5568c9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/intersect.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/javascript.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/javascript.svg
new file mode 100644
index 00000000..ea1a3e54
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/javascript.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-album.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-album.svg
new file mode 100644
index 00000000..34fd21ca
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-album.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-arrow-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-arrow-down.svg
new file mode 100644
index 00000000..aed79bdf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-arrow-down.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-arrow-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-arrow-up.svg
new file mode 100644
index 00000000..5276fe53
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-arrow-up.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-bookmark-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-bookmark-fill.svg
new file mode 100644
index 00000000..d4eae576
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-bookmark-fill.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-bookmark.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-bookmark.svg
new file mode 100644
index 00000000..db4129fb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-bookmark.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-check.svg
new file mode 100644
index 00000000..f7663346
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-check.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-code.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-code.svg
new file mode 100644
index 00000000..97704cb2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-code.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-medical.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-medical.svg
new file mode 100644
index 00000000..90579a74
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-medical.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-minus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-minus.svg
new file mode 100644
index 00000000..ea541e1f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-minus.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-plus.svg
new file mode 100644
index 00000000..0710e3fb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-plus.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-richtext.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-richtext.svg
new file mode 100644
index 00000000..d3449133
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-richtext.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-text.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-text.svg
new file mode 100644
index 00000000..44e8bc08
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-text.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-x.svg
new file mode 100644
index 00000000..9ede12cd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal-x.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/journal.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal.svg
new file mode 100644
index 00000000..3516aa04
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/journal.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/journals.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/journals.svg
new file mode 100644
index 00000000..a3d2b425
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/journals.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/joystick.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/joystick.svg
new file mode 100644
index 00000000..bea38879
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/joystick.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/justify-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/justify-left.svg
new file mode 100644
index 00000000..58572b00
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/justify-left.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/justify-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/justify-right.svg
new file mode 100644
index 00000000..3183a645
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/justify-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/justify.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/justify.svg
new file mode 100644
index 00000000..7513201c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/justify.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/kanban-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/kanban-fill.svg
new file mode 100644
index 00000000..d4746784
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/kanban-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/kanban.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/kanban.svg
new file mode 100644
index 00000000..2b362880
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/kanban.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/key-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/key-fill.svg
new file mode 100644
index 00000000..c75ff769
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/key-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/key.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/key.svg
new file mode 100644
index 00000000..1f8f002f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/key.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/keyboard-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/keyboard-fill.svg
new file mode 100644
index 00000000..c1449709
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/keyboard-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/keyboard.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/keyboard.svg
new file mode 100644
index 00000000..970b5c28
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/keyboard.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ladder.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ladder.svg
new file mode 100644
index 00000000..d2d6d248
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ladder.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/lamp-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/lamp-fill.svg
new file mode 100644
index 00000000..1703462b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/lamp-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/lamp.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/lamp.svg
new file mode 100644
index 00000000..01e4a116
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/lamp.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/laptop-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/laptop-fill.svg
new file mode 100644
index 00000000..b2cbcd22
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/laptop-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/laptop.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/laptop.svg
new file mode 100644
index 00000000..81f02d43
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/laptop.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/layer-backward.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/layer-backward.svg
new file mode 100644
index 00000000..15604afe
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/layer-backward.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/layer-forward.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/layer-forward.svg
new file mode 100644
index 00000000..9721632f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/layer-forward.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/layers-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/layers-fill.svg
new file mode 100644
index 00000000..ad6bdbc8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/layers-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/layers-half.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/layers-half.svg
new file mode 100644
index 00000000..6e6a5d9c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/layers-half.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/layers.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/layers.svg
new file mode 100644
index 00000000..aaeb5d29
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/layers.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-sidebar-inset-reverse.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-sidebar-inset-reverse.svg
new file mode 100644
index 00000000..7d936c8e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-sidebar-inset-reverse.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-sidebar-inset.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-sidebar-inset.svg
new file mode 100644
index 00000000..029169cf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-sidebar-inset.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-sidebar-reverse.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-sidebar-reverse.svg
new file mode 100644
index 00000000..0f891d41
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-sidebar-reverse.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-sidebar.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-sidebar.svg
new file mode 100644
index 00000000..09dcd0ef
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-sidebar.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-split.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-split.svg
new file mode 100644
index 00000000..4e4a1181
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-split.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-text-sidebar-reverse.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-text-sidebar-reverse.svg
new file mode 100644
index 00000000..4cfa0b55
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-text-sidebar-reverse.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-text-sidebar.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-text-sidebar.svg
new file mode 100644
index 00000000..a8452e6f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-text-sidebar.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-text-window-reverse.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-text-window-reverse.svg
new file mode 100644
index 00000000..bc6d53a1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-text-window-reverse.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-text-window.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-text-window.svg
new file mode 100644
index 00000000..3197a45e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-text-window.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-three-columns.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-three-columns.svg
new file mode 100644
index 00000000..0015852a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-three-columns.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-wtf.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-wtf.svg
new file mode 100644
index 00000000..349396df
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/layout-wtf.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/leaf-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/leaf-fill.svg
new file mode 100644
index 00000000..9aec52f7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/leaf-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/leaf.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/leaf.svg
new file mode 100644
index 00000000..4499411c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/leaf.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/life-preserver.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/life-preserver.svg
new file mode 100644
index 00000000..d27e1f4c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/life-preserver.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/lightbulb-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/lightbulb-fill.svg
new file mode 100644
index 00000000..74c071c0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/lightbulb-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/lightbulb-off-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/lightbulb-off-fill.svg
new file mode 100644
index 00000000..757ca59f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/lightbulb-off-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/lightbulb-off.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/lightbulb-off.svg
new file mode 100644
index 00000000..2d2d14a3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/lightbulb-off.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/lightbulb.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/lightbulb.svg
new file mode 100644
index 00000000..bea8b867
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/lightbulb.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/lightning-charge-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/lightning-charge-fill.svg
new file mode 100644
index 00000000..7021f2da
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/lightning-charge-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/lightning-charge.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/lightning-charge.svg
new file mode 100644
index 00000000..45a8397c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/lightning-charge.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/lightning-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/lightning-fill.svg
new file mode 100644
index 00000000..2e4d6850
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/lightning-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/lightning.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/lightning.svg
new file mode 100644
index 00000000..003e8bed
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/lightning.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/line.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/line.svg
new file mode 100644
index 00000000..c477de7a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/line.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/link-45deg.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/link-45deg.svg
new file mode 100644
index 00000000..1ce37411
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/link-45deg.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/link.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/link.svg
new file mode 100644
index 00000000..fdc879d5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/link.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/linkedin.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/linkedin.svg
new file mode 100644
index 00000000..d740ffa5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/linkedin.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/list-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/list-check.svg
new file mode 100644
index 00000000..27d500f4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/list-check.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/list-columns-reverse.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/list-columns-reverse.svg
new file mode 100644
index 00000000..3d530915
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/list-columns-reverse.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/list-columns.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/list-columns.svg
new file mode 100644
index 00000000..7f290d2a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/list-columns.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/list-nested.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/list-nested.svg
new file mode 100644
index 00000000..99b526b8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/list-nested.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/list-ol.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/list-ol.svg
new file mode 100644
index 00000000..15b29441
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/list-ol.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/list-stars.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/list-stars.svg
new file mode 100644
index 00000000..9bfc5f09
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/list-stars.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/list-task.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/list-task.svg
new file mode 100644
index 00000000..c13e1d71
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/list-task.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/list-ul.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/list-ul.svg
new file mode 100644
index 00000000..7e7b28b2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/list-ul.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/list.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/list.svg
new file mode 100644
index 00000000..ce9e86b0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/list.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/lock-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/lock-fill.svg
new file mode 100644
index 00000000..deef2bae
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/lock-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/lock.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/lock.svg
new file mode 100644
index 00000000..602a5afb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/lock.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/luggage-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/luggage-fill.svg
new file mode 100644
index 00000000..833aa318
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/luggage-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/luggage.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/luggage.svg
new file mode 100644
index 00000000..e2b86ef7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/luggage.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/lungs-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/lungs-fill.svg
new file mode 100644
index 00000000..af4edf1b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/lungs-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/lungs.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/lungs.svg
new file mode 100644
index 00000000..b53a3c45
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/lungs.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/magic.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/magic.svg
new file mode 100644
index 00000000..c67996cc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/magic.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/magnet-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/magnet-fill.svg
new file mode 100644
index 00000000..31ca4e55
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/magnet-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/magnet.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/magnet.svg
new file mode 100644
index 00000000..fef8fd0b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/magnet.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mailbox-flag.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mailbox-flag.svg
new file mode 100644
index 00000000..1211ab9a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mailbox-flag.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mailbox.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mailbox.svg
new file mode 100644
index 00000000..5dce6a39
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mailbox.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mailbox2-flag.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mailbox2-flag.svg
new file mode 100644
index 00000000..560dc03b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mailbox2-flag.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mailbox2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mailbox2.svg
new file mode 100644
index 00000000..44c090d0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mailbox2.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/map-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/map-fill.svg
new file mode 100644
index 00000000..73031ff9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/map-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/map.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/map.svg
new file mode 100644
index 00000000..351042e8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/map.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/markdown-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/markdown-fill.svg
new file mode 100644
index 00000000..4a22f840
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/markdown-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/markdown.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/markdown.svg
new file mode 100644
index 00000000..bac4ecb0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/markdown.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/marker-tip.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/marker-tip.svg
new file mode 100644
index 00000000..8770cc82
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/marker-tip.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mask.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mask.svg
new file mode 100644
index 00000000..99e06187
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mask.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mastodon.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mastodon.svg
new file mode 100644
index 00000000..437b2144
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mastodon.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/measuring-cup-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/measuring-cup-fill.svg
new file mode 100644
index 00000000..d4f4b861
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/measuring-cup-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/measuring-cup.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/measuring-cup.svg
new file mode 100644
index 00000000..8d9e56a2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/measuring-cup.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/medium.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/medium.svg
new file mode 100644
index 00000000..55c79b28
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/medium.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/megaphone-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/megaphone-fill.svg
new file mode 100644
index 00000000..1ce52280
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/megaphone-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/megaphone.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/megaphone.svg
new file mode 100644
index 00000000..8ae2587c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/megaphone.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/memory.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/memory.svg
new file mode 100644
index 00000000..fb0cba82
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/memory.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-app-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-app-fill.svg
new file mode 100644
index 00000000..826e4405
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-app-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-app.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-app.svg
new file mode 100644
index 00000000..c1ebe2b2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-app.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-button-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-button-fill.svg
new file mode 100644
index 00000000..b9ffcfd8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-button-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-button-wide-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-button-wide-fill.svg
new file mode 100644
index 00000000..7733cef6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-button-wide-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-button-wide.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-button-wide.svg
new file mode 100644
index 00000000..118dad33
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-button-wide.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-button.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-button.svg
new file mode 100644
index 00000000..17de9912
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-button.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-down.svg
new file mode 100644
index 00000000..bf709e6d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-down.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-up.svg
new file mode 100644
index 00000000..23c4a820
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/menu-up.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/messenger.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/messenger.svg
new file mode 100644
index 00000000..ceee32f0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/messenger.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/meta.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/meta.svg
new file mode 100644
index 00000000..cb082c10
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/meta.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mic-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mic-fill.svg
new file mode 100644
index 00000000..e18f419d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mic-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mic-mute-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mic-mute-fill.svg
new file mode 100644
index 00000000..3ff86309
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mic-mute-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mic-mute.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mic-mute.svg
new file mode 100644
index 00000000..23ddb03f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mic-mute.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mic.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mic.svg
new file mode 100644
index 00000000..5792f8e8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mic.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/microsoft-teams.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/microsoft-teams.svg
new file mode 100644
index 00000000..c7f99ac9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/microsoft-teams.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/microsoft.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/microsoft.svg
new file mode 100644
index 00000000..ca7379b5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/microsoft.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/minecart-loaded.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/minecart-loaded.svg
new file mode 100644
index 00000000..07fe80cb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/minecart-loaded.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/minecart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/minecart.svg
new file mode 100644
index 00000000..8de1f1ce
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/minecart.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/modem-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/modem-fill.svg
new file mode 100644
index 00000000..7d512c22
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/modem-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/modem.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/modem.svg
new file mode 100644
index 00000000..b9266bfe
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/modem.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/moisture.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/moisture.svg
new file mode 100644
index 00000000..8867b3c4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/moisture.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/moon-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/moon-fill.svg
new file mode 100644
index 00000000..a9d2e158
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/moon-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/moon-stars-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/moon-stars-fill.svg
new file mode 100644
index 00000000..89ea0db1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/moon-stars-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/moon-stars.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/moon-stars.svg
new file mode 100644
index 00000000..2e25448f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/moon-stars.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/moon.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/moon.svg
new file mode 100644
index 00000000..dbbe637d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/moon.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mortarboard-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mortarboard-fill.svg
new file mode 100644
index 00000000..3f9a8fe9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mortarboard-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mortarboard.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mortarboard.svg
new file mode 100644
index 00000000..f9e41242
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mortarboard.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/motherboard-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/motherboard-fill.svg
new file mode 100644
index 00000000..89c68ba5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/motherboard-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/motherboard.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/motherboard.svg
new file mode 100644
index 00000000..d904e525
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/motherboard.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse-fill.svg
new file mode 100644
index 00000000..716f73dc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse.svg
new file mode 100644
index 00000000..becf38b4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse2-fill.svg
new file mode 100644
index 00000000..d8c5b9e7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse2-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse2.svg
new file mode 100644
index 00000000..7848c2bf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse3-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse3-fill.svg
new file mode 100644
index 00000000..a726d0d2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse3-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse3.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse3.svg
new file mode 100644
index 00000000..52f6912a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/mouse3.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/music-note-beamed.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/music-note-beamed.svg
new file mode 100644
index 00000000..718e5073
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/music-note-beamed.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/music-note-list.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/music-note-list.svg
new file mode 100644
index 00000000..f65e230b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/music-note-list.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/music-note.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/music-note.svg
new file mode 100644
index 00000000..b2aedb52
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/music-note.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/music-player-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/music-player-fill.svg
new file mode 100644
index 00000000..6f703d03
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/music-player-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/music-player.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/music-player.svg
new file mode 100644
index 00000000..fdca6047
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/music-player.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/newspaper.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/newspaper.svg
new file mode 100644
index 00000000..ab7fc7b9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/newspaper.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/nintendo-switch.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/nintendo-switch.svg
new file mode 100644
index 00000000..54311d7d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/nintendo-switch.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/node-minus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/node-minus-fill.svg
new file mode 100644
index 00000000..690b7ac0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/node-minus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/node-minus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/node-minus.svg
new file mode 100644
index 00000000..b021918e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/node-minus.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/node-plus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/node-plus-fill.svg
new file mode 100644
index 00000000..6f98a60e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/node-plus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/node-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/node-plus.svg
new file mode 100644
index 00000000..4eb1ef1d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/node-plus.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/noise-reduction.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/noise-reduction.svg
new file mode 100644
index 00000000..21462a8e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/noise-reduction.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/nut-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/nut-fill.svg
new file mode 100644
index 00000000..ae49fc89
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/nut-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/nut.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/nut.svg
new file mode 100644
index 00000000..1266e5ec
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/nut.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/nvidia.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/nvidia.svg
new file mode 100644
index 00000000..d13952a5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/nvidia.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/nvme-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/nvme-fill.svg
new file mode 100644
index 00000000..71b360c6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/nvme-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/nvme.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/nvme.svg
new file mode 100644
index 00000000..1f614c35
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/nvme.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/octagon-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/octagon-fill.svg
new file mode 100644
index 00000000..722a5a59
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/octagon-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/octagon-half.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/octagon-half.svg
new file mode 100644
index 00000000..07822481
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/octagon-half.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/octagon.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/octagon.svg
new file mode 100644
index 00000000..98a86af2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/octagon.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/openai.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/openai.svg
new file mode 100644
index 00000000..fe7c602f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/openai.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/opencollective.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/opencollective.svg
new file mode 100644
index 00000000..0113a922
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/opencollective.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/optical-audio-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/optical-audio-fill.svg
new file mode 100644
index 00000000..055c5fe7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/optical-audio-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/optical-audio.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/optical-audio.svg
new file mode 100644
index 00000000..aa431b3e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/optical-audio.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/option.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/option.svg
new file mode 100644
index 00000000..ba10d25d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/option.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/outlet.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/outlet.svg
new file mode 100644
index 00000000..473dc1e6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/outlet.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/p-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/p-circle-fill.svg
new file mode 100644
index 00000000..cfc4347d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/p-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/p-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/p-circle.svg
new file mode 100644
index 00000000..89048864
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/p-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/p-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/p-square-fill.svg
new file mode 100644
index 00000000..09db5b83
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/p-square-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/p-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/p-square.svg
new file mode 100644
index 00000000..12274fbb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/p-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/paint-bucket.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/paint-bucket.svg
new file mode 100644
index 00000000..f1f6c97c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/paint-bucket.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/palette-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/palette-fill.svg
new file mode 100644
index 00000000..7f47cf6c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/palette-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/palette.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/palette.svg
new file mode 100644
index 00000000..941a7e44
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/palette.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/palette2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/palette2.svg
new file mode 100644
index 00000000..29436e3e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/palette2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/paperclip.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/paperclip.svg
new file mode 100644
index 00000000..087fb329
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/paperclip.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/paragraph.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/paragraph.svg
new file mode 100644
index 00000000..6f694d0e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/paragraph.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pass-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pass-fill.svg
new file mode 100644
index 00000000..41d11ee8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pass-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pass.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pass.svg
new file mode 100644
index 00000000..283198e9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pass.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/passport-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/passport-fill.svg
new file mode 100644
index 00000000..a7a38e33
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/passport-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/passport.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/passport.svg
new file mode 100644
index 00000000..0b541bff
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/passport.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-check-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-check-fill.svg
new file mode 100644
index 00000000..0c090725
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-check-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-check.svg
new file mode 100644
index 00000000..c2909f54
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-exclamation-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-exclamation-fill.svg
new file mode 100644
index 00000000..e0545529
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-exclamation-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-exclamation.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-exclamation.svg
new file mode 100644
index 00000000..7cd27af3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-exclamation.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-minus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-minus-fill.svg
new file mode 100644
index 00000000..d230be0d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-minus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-minus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-minus.svg
new file mode 100644
index 00000000..5e871d7d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-minus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-plus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-plus-fill.svg
new file mode 100644
index 00000000..0022e971
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-plus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-plus.svg
new file mode 100644
index 00000000..2b35e9ca
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-plus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-question-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-question-fill.svg
new file mode 100644
index 00000000..108157a2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-question-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-question.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-question.svg
new file mode 100644
index 00000000..8ccf8804
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/patch-question.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pause-btn-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pause-btn-fill.svg
new file mode 100644
index 00000000..b6c90b83
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pause-btn-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pause-btn.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pause-btn.svg
new file mode 100644
index 00000000..6daf1cda
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pause-btn.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pause-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pause-circle-fill.svg
new file mode 100644
index 00000000..970ce484
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pause-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pause-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pause-circle.svg
new file mode 100644
index 00000000..7387a254
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pause-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pause-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pause-fill.svg
new file mode 100644
index 00000000..e817993c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pause-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pause.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pause.svg
new file mode 100644
index 00000000..90f59b94
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pause.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/paypal.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/paypal.svg
new file mode 100644
index 00000000..a8b189b5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/paypal.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pc-display-horizontal.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pc-display-horizontal.svg
new file mode 100644
index 00000000..99c04b49
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pc-display-horizontal.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pc-display.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pc-display.svg
new file mode 100644
index 00000000..edb76c12
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pc-display.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pc-horizontal.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pc-horizontal.svg
new file mode 100644
index 00000000..e4769f67
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pc-horizontal.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pc.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pc.svg
new file mode 100644
index 00000000..eb4ae795
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pc.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pci-card-network.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pci-card-network.svg
new file mode 100644
index 00000000..ed42fd50
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pci-card-network.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pci-card-sound.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pci-card-sound.svg
new file mode 100644
index 00000000..93fc5242
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pci-card-sound.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pci-card.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pci-card.svg
new file mode 100644
index 00000000..2d66ad00
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pci-card.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/peace-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/peace-fill.svg
new file mode 100644
index 00000000..731c78b6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/peace-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/peace.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/peace.svg
new file mode 100644
index 00000000..7f6343fc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/peace.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pen-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pen-fill.svg
new file mode 100644
index 00000000..b8909165
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pen-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pen.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pen.svg
new file mode 100644
index 00000000..6efa5a54
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pen.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pencil-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pencil-fill.svg
new file mode 100644
index 00000000..a39b0cd7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pencil-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pencil-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pencil-square.svg
new file mode 100644
index 00000000..a0da03ee
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pencil-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pencil.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pencil.svg
new file mode 100644
index 00000000..b863138d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pencil.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pentagon-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pentagon-fill.svg
new file mode 100644
index 00000000..bf27f66c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pentagon-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pentagon-half.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pentagon-half.svg
new file mode 100644
index 00000000..42018d15
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pentagon-half.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pentagon.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pentagon.svg
new file mode 100644
index 00000000..52e85a56
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pentagon.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/people-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/people-fill.svg
new file mode 100644
index 00000000..57e3b54d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/people-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/people.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/people.svg
new file mode 100644
index 00000000..9308209a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/people.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/percent.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/percent.svg
new file mode 100644
index 00000000..aed6ab4c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/percent.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/perplexity.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/perplexity.svg
new file mode 100644
index 00000000..9b9002cb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/perplexity.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-add.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-add.svg
new file mode 100644
index 00000000..252a9edd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-add.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-arms-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-arms-up.svg
new file mode 100644
index 00000000..5f6d8427
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-arms-up.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-badge-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-badge-fill.svg
new file mode 100644
index 00000000..1d3cc0ef
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-badge-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-badge.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-badge.svg
new file mode 100644
index 00000000..51871708
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-badge.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-bounding-box.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-bounding-box.svg
new file mode 100644
index 00000000..123d4504
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-bounding-box.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-check-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-check-fill.svg
new file mode 100644
index 00000000..a223b5c9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-check-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-check.svg
new file mode 100644
index 00000000..9cbfcb26
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-circle.svg
new file mode 100644
index 00000000..087050a3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-dash-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-dash-fill.svg
new file mode 100644
index 00000000..08042e13
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-dash-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-dash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-dash.svg
new file mode 100644
index 00000000..402c58ef
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-dash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-down.svg
new file mode 100644
index 00000000..11977579
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-down.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-exclamation.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-exclamation.svg
new file mode 100644
index 00000000..7ccba549
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-exclamation.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-add.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-add.svg
new file mode 100644
index 00000000..08b843c2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-add.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-check.svg
new file mode 100644
index 00000000..f60bcbfa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-dash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-dash.svg
new file mode 100644
index 00000000..8704b9ed
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-dash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-down.svg
new file mode 100644
index 00000000..cf950a7a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-down.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-exclamation.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-exclamation.svg
new file mode 100644
index 00000000..5007532e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-exclamation.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-gear.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-gear.svg
new file mode 100644
index 00000000..e9be1d16
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-gear.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-lock.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-lock.svg
new file mode 100644
index 00000000..7f9cfd99
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-lock.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-slash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-slash.svg
new file mode 100644
index 00000000..980edde3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-slash.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-up.svg
new file mode 100644
index 00000000..a9b8d505
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-up.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-x.svg
new file mode 100644
index 00000000..7d48cf22
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill.svg
new file mode 100644
index 00000000..45a37930
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-gear.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-gear.svg
new file mode 100644
index 00000000..b0acb1c1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-gear.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-heart.svg
new file mode 100644
index 00000000..3d734680
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-heart.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-hearts.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-hearts.svg
new file mode 100644
index 00000000..cf9d17d5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-hearts.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-lines-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-lines-fill.svg
new file mode 100644
index 00000000..403e503c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-lines-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-lock.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-lock.svg
new file mode 100644
index 00000000..69cfea59
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-lock.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-plus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-plus-fill.svg
new file mode 100644
index 00000000..c35194b1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-plus-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-plus.svg
new file mode 100644
index 00000000..3be8a9f9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-plus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-raised-hand.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-raised-hand.svg
new file mode 100644
index 00000000..41ba2714
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-raised-hand.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-rolodex.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-rolodex.svg
new file mode 100644
index 00000000..c380e91b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-rolodex.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-slash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-slash.svg
new file mode 100644
index 00000000..4be54164
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-slash.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-square.svg
new file mode 100644
index 00000000..b4404ebf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-standing-dress.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-standing-dress.svg
new file mode 100644
index 00000000..4801868c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-standing-dress.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-standing.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-standing.svg
new file mode 100644
index 00000000..67f2c7c7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-standing.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-up.svg
new file mode 100644
index 00000000..c431fced
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-up.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-vcard-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-vcard-fill.svg
new file mode 100644
index 00000000..cfed2696
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-vcard-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-vcard.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-vcard.svg
new file mode 100644
index 00000000..88a7e98a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-vcard.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-video.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-video.svg
new file mode 100644
index 00000000..fe2f39e8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-video.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-video2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-video2.svg
new file mode 100644
index 00000000..f1d6d341
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-video2.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-video3.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-video3.svg
new file mode 100644
index 00000000..7468a920
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-video3.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-walking.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-walking.svg
new file mode 100644
index 00000000..d9c110c6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-walking.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-wheelchair.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-wheelchair.svg
new file mode 100644
index 00000000..016118c2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-wheelchair.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-workspace.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-workspace.svg
new file mode 100644
index 00000000..829574c2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-workspace.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-x-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-x-fill.svg
new file mode 100644
index 00000000..c2982caa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-x-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-x.svg
new file mode 100644
index 00000000..a9869858
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/person.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/person.svg
new file mode 100644
index 00000000..fe025814
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/person.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-fill.svg
new file mode 100644
index 00000000..5d3c1599
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-flip.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-flip.svg
new file mode 100644
index 00000000..b1b6457d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-flip.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-landscape-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-landscape-fill.svg
new file mode 100644
index 00000000..52a0e724
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-landscape-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-landscape.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-landscape.svg
new file mode 100644
index 00000000..23e00a03
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-landscape.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-vibrate-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-vibrate-fill.svg
new file mode 100644
index 00000000..2a3cbc54
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-vibrate-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-vibrate.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-vibrate.svg
new file mode 100644
index 00000000..91878d12
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/phone-vibrate.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/phone.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/phone.svg
new file mode 100644
index 00000000..ae87d935
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/phone.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pie-chart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pie-chart-fill.svg
new file mode 100644
index 00000000..a60814a7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pie-chart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pie-chart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pie-chart.svg
new file mode 100644
index 00000000..5863ac1e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pie-chart.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/piggy-bank-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/piggy-bank-fill.svg
new file mode 100644
index 00000000..13812f0c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/piggy-bank-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/piggy-bank.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/piggy-bank.svg
new file mode 100644
index 00000000..5526df20
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/piggy-bank.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pin-angle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pin-angle-fill.svg
new file mode 100644
index 00000000..3463f0a4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pin-angle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pin-angle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pin-angle.svg
new file mode 100644
index 00000000..4c227338
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pin-angle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pin-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pin-fill.svg
new file mode 100644
index 00000000..8b2d6e36
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pin-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pin-map-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pin-map-fill.svg
new file mode 100644
index 00000000..e081d46a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pin-map-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pin-map.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pin-map.svg
new file mode 100644
index 00000000..2a5ae62d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pin-map.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pin.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pin.svg
new file mode 100644
index 00000000..9946219f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pin.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pinterest.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pinterest.svg
new file mode 100644
index 00000000..109f30bf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pinterest.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pip-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pip-fill.svg
new file mode 100644
index 00000000..ff0dae7c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pip-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/pip.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/pip.svg
new file mode 100644
index 00000000..a0850086
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/pip.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/play-btn-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/play-btn-fill.svg
new file mode 100644
index 00000000..473884a0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/play-btn-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/play-btn.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/play-btn.svg
new file mode 100644
index 00000000..0aa0db68
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/play-btn.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/play-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/play-circle-fill.svg
new file mode 100644
index 00000000..fd2fd724
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/play-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/play-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/play-circle.svg
new file mode 100644
index 00000000..dba4b622
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/play-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/play-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/play-fill.svg
new file mode 100644
index 00000000..b8d41baf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/play-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/play.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/play.svg
new file mode 100644
index 00000000..668ea669
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/play.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/playstation.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/playstation.svg
new file mode 100644
index 00000000..a62a2b84
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/playstation.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/plug-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/plug-fill.svg
new file mode 100644
index 00000000..c268eab4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/plug-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/plug.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/plug.svg
new file mode 100644
index 00000000..24b65084
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/plug.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/plugin.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/plugin.svg
new file mode 100644
index 00000000..414547c6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/plugin.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-circle-dotted.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-circle-dotted.svg
new file mode 100644
index 00000000..b4a689b6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-circle-dotted.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-circle-fill.svg
new file mode 100644
index 00000000..c32e677a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-circle.svg
new file mode 100644
index 00000000..fe99ebbb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-lg.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-lg.svg
new file mode 100644
index 00000000..5ebf3d9b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-lg.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-slash-minus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-slash-minus.svg
new file mode 100644
index 00000000..565fb89f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-slash-minus.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-square-dotted.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-square-dotted.svg
new file mode 100644
index 00000000..fd0fe3de
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-square-dotted.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-square-fill.svg
new file mode 100644
index 00000000..8147bac1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-square.svg
new file mode 100644
index 00000000..8a34b8b7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus.svg
new file mode 100644
index 00000000..15d2b327
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/plus.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/postage-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/postage-fill.svg
new file mode 100644
index 00000000..2e16e121
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/postage-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/postage-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/postage-heart-fill.svg
new file mode 100644
index 00000000..5df10ff0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/postage-heart-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/postage-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/postage-heart.svg
new file mode 100644
index 00000000..2398a22f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/postage-heart.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/postage.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/postage.svg
new file mode 100644
index 00000000..43bb3104
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/postage.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/postcard-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/postcard-fill.svg
new file mode 100644
index 00000000..12eb76ed
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/postcard-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/postcard-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/postcard-heart-fill.svg
new file mode 100644
index 00000000..155dddfc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/postcard-heart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/postcard-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/postcard-heart.svg
new file mode 100644
index 00000000..d96b49e9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/postcard-heart.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/postcard.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/postcard.svg
new file mode 100644
index 00000000..afdc2e4b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/postcard.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/power.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/power.svg
new file mode 100644
index 00000000..f940b370
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/power.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/prescription.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/prescription.svg
new file mode 100644
index 00000000..75b82272
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/prescription.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/prescription2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/prescription2.svg
new file mode 100644
index 00000000..512f7553
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/prescription2.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/printer-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/printer-fill.svg
new file mode 100644
index 00000000..0fe977b6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/printer-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/printer.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/printer.svg
new file mode 100644
index 00000000..6ef0f8ba
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/printer.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/projector-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/projector-fill.svg
new file mode 100644
index 00000000..ee6bee8c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/projector-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/projector.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/projector.svg
new file mode 100644
index 00000000..c5f0f98e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/projector.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/puzzle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/puzzle-fill.svg
new file mode 100644
index 00000000..e629e93d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/puzzle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/puzzle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/puzzle.svg
new file mode 100644
index 00000000..afa67b91
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/puzzle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/qr-code-scan.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/qr-code-scan.svg
new file mode 100644
index 00000000..f887bced
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/qr-code-scan.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/qr-code.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/qr-code.svg
new file mode 100644
index 00000000..961bab19
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/qr-code.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/question-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-circle-fill.svg
new file mode 100644
index 00000000..0ad62bc7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/question-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-circle.svg
new file mode 100644
index 00000000..26ab0700
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/question-diamond-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-diamond-fill.svg
new file mode 100644
index 00000000..1ec85dc3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-diamond-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/question-diamond.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-diamond.svg
new file mode 100644
index 00000000..202bacb8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-diamond.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/question-lg.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-lg.svg
new file mode 100644
index 00000000..ed408cc0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-lg.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/question-octagon-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-octagon-fill.svg
new file mode 100644
index 00000000..d0fa3c8f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-octagon-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/question-octagon.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-octagon.svg
new file mode 100644
index 00000000..15ec4e70
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-octagon.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/question-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-square-fill.svg
new file mode 100644
index 00000000..519bfbaa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/question-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-square.svg
new file mode 100644
index 00000000..6d634689
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/question-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/question.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/question.svg
new file mode 100644
index 00000000..e63ac325
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/question.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/quora.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/quora.svg
new file mode 100644
index 00000000..f1778a63
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/quora.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/quote.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/quote.svg
new file mode 100644
index 00000000..e82e43dd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/quote.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/r-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/r-circle-fill.svg
new file mode 100644
index 00000000..c6aaf9d4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/r-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/r-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/r-circle.svg
new file mode 100644
index 00000000..94134047
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/r-circle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/r-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/r-square-fill.svg
new file mode 100644
index 00000000..5f417738
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/r-square-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/r-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/r-square.svg
new file mode 100644
index 00000000..5bf5622e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/r-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/radar.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/radar.svg
new file mode 100644
index 00000000..0dfcc7f4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/radar.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/radioactive.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/radioactive.svg
new file mode 100644
index 00000000..df9af279
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/radioactive.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/rainbow.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/rainbow.svg
new file mode 100644
index 00000000..7c57b2b2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/rainbow.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/receipt-cutoff.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/receipt-cutoff.svg
new file mode 100644
index 00000000..3ebe01f8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/receipt-cutoff.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/receipt.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/receipt.svg
new file mode 100644
index 00000000..8c8da0d0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/receipt.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/reception-0.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/reception-0.svg
new file mode 100644
index 00000000..cca1a18f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/reception-0.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/reception-1.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/reception-1.svg
new file mode 100644
index 00000000..e8926a04
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/reception-1.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/reception-2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/reception-2.svg
new file mode 100644
index 00000000..e53906c6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/reception-2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/reception-3.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/reception-3.svg
new file mode 100644
index 00000000..8470e9ec
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/reception-3.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/reception-4.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/reception-4.svg
new file mode 100644
index 00000000..87da1c7f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/reception-4.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/record-btn-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/record-btn-fill.svg
new file mode 100644
index 00000000..42fcdbe9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/record-btn-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/record-btn.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/record-btn.svg
new file mode 100644
index 00000000..154e9281
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/record-btn.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/record-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/record-circle-fill.svg
new file mode 100644
index 00000000..f7b0956e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/record-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/record-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/record-circle.svg
new file mode 100644
index 00000000..04b3c918
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/record-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/record-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/record-fill.svg
new file mode 100644
index 00000000..f2d619f2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/record-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/record.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/record.svg
new file mode 100644
index 00000000..bbb8c614
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/record.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/record2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/record2-fill.svg
new file mode 100644
index 00000000..2a704018
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/record2-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/record2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/record2.svg
new file mode 100644
index 00000000..61da17b3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/record2.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/recycle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/recycle.svg
new file mode 100644
index 00000000..8cc7a4b7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/recycle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/reddit.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/reddit.svg
new file mode 100644
index 00000000..62e7255e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/reddit.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/regex.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/regex.svg
new file mode 100644
index 00000000..4ddcea80
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/regex.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/repeat-1.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/repeat-1.svg
new file mode 100644
index 00000000..8f4f6a6f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/repeat-1.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/repeat.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/repeat.svg
new file mode 100644
index 00000000..9729e28c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/repeat.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/reply-all-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/reply-all-fill.svg
new file mode 100644
index 00000000..b3d4768e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/reply-all-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/reply-all.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/reply-all.svg
new file mode 100644
index 00000000..2ca3f9df
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/reply-all.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/reply-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/reply-fill.svg
new file mode 100644
index 00000000..bd0e72df
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/reply-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/reply.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/reply.svg
new file mode 100644
index 00000000..10583c83
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/reply.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind-btn-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind-btn-fill.svg
new file mode 100644
index 00000000..b2ba3661
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind-btn-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind-btn.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind-btn.svg
new file mode 100644
index 00000000..f2d5ad4f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind-btn.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind-circle-fill.svg
new file mode 100644
index 00000000..9af9e6ad
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind-circle.svg
new file mode 100644
index 00000000..cbaa5d1e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind-fill.svg
new file mode 100644
index 00000000..8f73ae29
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind.svg
new file mode 100644
index 00000000..f3773654
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/rewind.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/robot.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/robot.svg
new file mode 100644
index 00000000..58d0f614
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/robot.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/rocket-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/rocket-fill.svg
new file mode 100644
index 00000000..a89bef00
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/rocket-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/rocket-takeoff-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/rocket-takeoff-fill.svg
new file mode 100644
index 00000000..adbd3152
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/rocket-takeoff-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/rocket-takeoff.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/rocket-takeoff.svg
new file mode 100644
index 00000000..a6622b46
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/rocket-takeoff.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/rocket.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/rocket.svg
new file mode 100644
index 00000000..8dd9bd09
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/rocket.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/router-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/router-fill.svg
new file mode 100644
index 00000000..33250986
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/router-fill.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/router.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/router.svg
new file mode 100644
index 00000000..436946d0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/router.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/rss-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/rss-fill.svg
new file mode 100644
index 00000000..ecd7f6dd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/rss-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/rss.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/rss.svg
new file mode 100644
index 00000000..8986a907
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/rss.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/rulers.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/rulers.svg
new file mode 100644
index 00000000..2a2012f6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/rulers.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/safe-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/safe-fill.svg
new file mode 100644
index 00000000..e3f4cd8b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/safe-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/safe.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/safe.svg
new file mode 100644
index 00000000..579d4fbc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/safe.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/safe2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/safe2-fill.svg
new file mode 100644
index 00000000..bf98ee7d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/safe2-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/safe2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/safe2.svg
new file mode 100644
index 00000000..57f45985
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/safe2.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/save-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/save-fill.svg
new file mode 100644
index 00000000..a4ccc663
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/save-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/save.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/save.svg
new file mode 100644
index 00000000..401e0994
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/save.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/save2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/save2-fill.svg
new file mode 100644
index 00000000..f8bcfcbb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/save2-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/save2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/save2.svg
new file mode 100644
index 00000000..17777946
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/save2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/scissors.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/scissors.svg
new file mode 100644
index 00000000..abab05a1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/scissors.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/scooter.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/scooter.svg
new file mode 100644
index 00000000..94434afa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/scooter.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/screwdriver.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/screwdriver.svg
new file mode 100644
index 00000000..f06d6306
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/screwdriver.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sd-card-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sd-card-fill.svg
new file mode 100644
index 00000000..f17886d7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sd-card-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sd-card.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sd-card.svg
new file mode 100644
index 00000000..a123f40d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sd-card.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/search-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/search-heart-fill.svg
new file mode 100644
index 00000000..71a099e9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/search-heart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/search-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/search-heart.svg
new file mode 100644
index 00000000..630a7a0b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/search-heart.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/search.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/search.svg
new file mode 100644
index 00000000..9798fc43
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/search.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/segmented-nav.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/segmented-nav.svg
new file mode 100644
index 00000000..dbe519e9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/segmented-nav.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send-arrow-down-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-arrow-down-fill.svg
new file mode 100644
index 00000000..cfa6f75d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-arrow-down-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send-arrow-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-arrow-down.svg
new file mode 100644
index 00000000..65401a45
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-arrow-down.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send-arrow-up-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-arrow-up-fill.svg
new file mode 100644
index 00000000..5d67d2b4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-arrow-up-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send-arrow-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-arrow-up.svg
new file mode 100644
index 00000000..d51f9692
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-arrow-up.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send-check-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-check-fill.svg
new file mode 100644
index 00000000..7c7dccfb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-check-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-check.svg
new file mode 100644
index 00000000..b212bcdc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send-dash-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-dash-fill.svg
new file mode 100644
index 00000000..72a61b43
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-dash-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send-dash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-dash.svg
new file mode 100644
index 00000000..1b1a86e2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-dash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send-exclamation-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-exclamation-fill.svg
new file mode 100644
index 00000000..19717e19
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-exclamation-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send-exclamation.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-exclamation.svg
new file mode 100644
index 00000000..7932e746
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-exclamation.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-fill.svg
new file mode 100644
index 00000000..dfe4c2bf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send-plus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-plus-fill.svg
new file mode 100644
index 00000000..fbaa48e2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-plus-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-plus.svg
new file mode 100644
index 00000000..ab92d6e3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-plus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send-slash-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-slash-fill.svg
new file mode 100644
index 00000000..b70ff7b8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-slash-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send-slash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-slash.svg
new file mode 100644
index 00000000..871975b5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-slash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send-x-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-x-fill.svg
new file mode 100644
index 00000000..fd92beca
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-x-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-x.svg
new file mode 100644
index 00000000..6b12a323
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/send.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/send.svg
new file mode 100644
index 00000000..6bc7761f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/send.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/server.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/server.svg
new file mode 100644
index 00000000..4f07ad73
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/server.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shadows.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shadows.svg
new file mode 100644
index 00000000..d78db1eb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shadows.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/share-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/share-fill.svg
new file mode 100644
index 00000000..ee432ecb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/share-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/share.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/share.svg
new file mode 100644
index 00000000..9d9718ec
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/share.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-check.svg
new file mode 100644
index 00000000..efac2af5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-check.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-exclamation.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-exclamation.svg
new file mode 100644
index 00000000..60aaafd6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-exclamation.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill-check.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill-check.svg
new file mode 100644
index 00000000..4ad83226
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill-check.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill-exclamation.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill-exclamation.svg
new file mode 100644
index 00000000..f8312c1a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill-exclamation.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill-minus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill-minus.svg
new file mode 100644
index 00000000..6c5abc51
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill-minus.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill-plus.svg
new file mode 100644
index 00000000..0e4dc325
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill-plus.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill-x.svg
new file mode 100644
index 00000000..75815db2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill-x.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill.svg
new file mode 100644
index 00000000..2b7dc114
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-lock-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-lock-fill.svg
new file mode 100644
index 00000000..9d2b94f4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-lock-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-lock.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-lock.svg
new file mode 100644
index 00000000..b1eb415c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-lock.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-minus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-minus.svg
new file mode 100644
index 00000000..61a513a0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-minus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-plus.svg
new file mode 100644
index 00000000..9c7a962a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-plus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-shaded.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-shaded.svg
new file mode 100644
index 00000000..2acba875
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-shaded.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-slash-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-slash-fill.svg
new file mode 100644
index 00000000..a11b0c21
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-slash-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-slash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-slash.svg
new file mode 100644
index 00000000..abfbbd6c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-slash.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-x.svg
new file mode 100644
index 00000000..038263ea
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shield.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield.svg
new file mode 100644
index 00000000..97d95c08
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shield.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shift-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shift-fill.svg
new file mode 100644
index 00000000..2146340f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shift-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shift.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shift.svg
new file mode 100644
index 00000000..dfef17ad
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shift.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shop-window.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shop-window.svg
new file mode 100644
index 00000000..d135934f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shop-window.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shop.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shop.svg
new file mode 100644
index 00000000..73fb3d03
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shop.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/shuffle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/shuffle.svg
new file mode 100644
index 00000000..3a0716fc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/shuffle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-dead-end-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-dead-end-fill.svg
new file mode 100644
index 00000000..665a938c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-dead-end-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-dead-end.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-dead-end.svg
new file mode 100644
index 00000000..9de2e968
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-dead-end.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-do-not-enter-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-do-not-enter-fill.svg
new file mode 100644
index 00000000..8d9d39fc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-do-not-enter-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-do-not-enter.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-do-not-enter.svg
new file mode 100644
index 00000000..78c9b89a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-do-not-enter.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-fill.svg
new file mode 100644
index 00000000..4e990dc0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-side-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-side-fill.svg
new file mode 100644
index 00000000..4b75b789
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-side-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-side.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-side.svg
new file mode 100644
index 00000000..d5a9bd96
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-side.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-t-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-t-fill.svg
new file mode 100644
index 00000000..6fb9f617
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-t-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-t.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-t.svg
new file mode 100644
index 00000000..130866f0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-t.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-y-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-y-fill.svg
new file mode 100644
index 00000000..316d16e1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-y-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-y.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-y.svg
new file mode 100644
index 00000000..ca1d26a1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection-y.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection.svg
new file mode 100644
index 00000000..5403c901
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-intersection.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-merge-left-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-merge-left-fill.svg
new file mode 100644
index 00000000..6f1a2d3a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-merge-left-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-merge-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-merge-left.svg
new file mode 100644
index 00000000..e1378213
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-merge-left.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-merge-right-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-merge-right-fill.svg
new file mode 100644
index 00000000..d84a22f4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-merge-right-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-merge-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-merge-right.svg
new file mode 100644
index 00000000..bb5992e5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-merge-right.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-left-turn-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-left-turn-fill.svg
new file mode 100644
index 00000000..2a74d7d2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-left-turn-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-left-turn.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-left-turn.svg
new file mode 100644
index 00000000..658f5160
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-left-turn.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-parking-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-parking-fill.svg
new file mode 100644
index 00000000..db8a80a2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-parking-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-parking.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-parking.svg
new file mode 100644
index 00000000..559e73ef
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-parking.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-right-turn-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-right-turn-fill.svg
new file mode 100644
index 00000000..05dd6667
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-right-turn-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-right-turn.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-right-turn.svg
new file mode 100644
index 00000000..f55cd9a5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-no-right-turn.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-railroad-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-railroad-fill.svg
new file mode 100644
index 00000000..b51b8e9e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-railroad-fill.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-railroad.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-railroad.svg
new file mode 100644
index 00000000..2c4065d8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-railroad.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-stop-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-stop-fill.svg
new file mode 100644
index 00000000..76ad911a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-stop-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-stop-lights-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-stop-lights-fill.svg
new file mode 100644
index 00000000..6c1470a9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-stop-lights-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-stop-lights.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-stop-lights.svg
new file mode 100644
index 00000000..5465d815
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-stop-lights.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-stop.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-stop.svg
new file mode 100644
index 00000000..5dae2e30
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-stop.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-left-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-left-fill.svg
new file mode 100644
index 00000000..e001fd58
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-left-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-left.svg
new file mode 100644
index 00000000..20bcd3a3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-left.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-right-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-right-fill.svg
new file mode 100644
index 00000000..ce68a34a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-right-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-right.svg
new file mode 100644
index 00000000..3d0e3d60
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-right.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-slight-left-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-slight-left-fill.svg
new file mode 100644
index 00000000..e891308f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-slight-left-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-slight-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-slight-left.svg
new file mode 100644
index 00000000..9f97ba05
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-slight-left.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-slight-right-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-slight-right-fill.svg
new file mode 100644
index 00000000..e7acbb2a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-slight-right-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-slight-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-slight-right.svg
new file mode 100644
index 00000000..7e105fa4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-turn-slight-right.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-yield-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-yield-fill.svg
new file mode 100644
index 00000000..31e65d15
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-yield-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-yield.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-yield.svg
new file mode 100644
index 00000000..6b1d6b90
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sign-yield.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/signal.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/signal.svg
new file mode 100644
index 00000000..a1720c08
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/signal.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost-2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost-2-fill.svg
new file mode 100644
index 00000000..acf0243b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost-2-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost-2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost-2.svg
new file mode 100644
index 00000000..b4e92f11
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost-2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost-fill.svg
new file mode 100644
index 00000000..bb8e97b1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost-split-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost-split-fill.svg
new file mode 100644
index 00000000..bc164a7e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost-split-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost-split.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost-split.svg
new file mode 100644
index 00000000..431ed7d7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost-split.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost.svg
new file mode 100644
index 00000000..be4623ec
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/signpost.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sim-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sim-fill.svg
new file mode 100644
index 00000000..5ef90796
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sim-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sim-slash-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sim-slash-fill.svg
new file mode 100644
index 00000000..46fa2a41
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sim-slash-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sim-slash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sim-slash.svg
new file mode 100644
index 00000000..f58ba052
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sim-slash.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sim.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sim.svg
new file mode 100644
index 00000000..d5d26ad0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sim.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sina-weibo.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sina-weibo.svg
new file mode 100644
index 00000000..a150dff5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sina-weibo.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward-btn-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward-btn-fill.svg
new file mode 100644
index 00000000..69495300
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward-btn-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward-btn.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward-btn.svg
new file mode 100644
index 00000000..079e453d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward-btn.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward-circle-fill.svg
new file mode 100644
index 00000000..d069136d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward-circle.svg
new file mode 100644
index 00000000..6afde382
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward-fill.svg
new file mode 100644
index 00000000..f0813b54
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward.svg
new file mode 100644
index 00000000..1322cabb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-backward.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end-btn-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end-btn-fill.svg
new file mode 100644
index 00000000..aaf2b54b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end-btn-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end-btn.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end-btn.svg
new file mode 100644
index 00000000..86e88807
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end-btn.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end-circle-fill.svg
new file mode 100644
index 00000000..a45aa192
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end-circle.svg
new file mode 100644
index 00000000..565d2050
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end-fill.svg
new file mode 100644
index 00000000..122d4bde
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end.svg
new file mode 100644
index 00000000..ecf7d569
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-end.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward-btn-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward-btn-fill.svg
new file mode 100644
index 00000000..99d51ab9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward-btn-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward-btn.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward-btn.svg
new file mode 100644
index 00000000..8b97dc47
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward-btn.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward-circle-fill.svg
new file mode 100644
index 00000000..c9a8fd7d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward-circle.svg
new file mode 100644
index 00000000..9dacaedc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward-fill.svg
new file mode 100644
index 00000000..20534ece
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward.svg
new file mode 100644
index 00000000..16d6a82c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-forward.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start-btn-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start-btn-fill.svg
new file mode 100644
index 00000000..09230dde
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start-btn-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start-btn.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start-btn.svg
new file mode 100644
index 00000000..ca8ced25
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start-btn.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start-circle-fill.svg
new file mode 100644
index 00000000..8aa2ef96
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start-circle.svg
new file mode 100644
index 00000000..65e67c84
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start-fill.svg
new file mode 100644
index 00000000..930c664a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start.svg
new file mode 100644
index 00000000..d953a15c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skip-start.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/skype.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/skype.svg
new file mode 100644
index 00000000..97d85ba2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/skype.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/slack.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/slack.svg
new file mode 100644
index 00000000..69c3febe
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/slack.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/slash-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/slash-circle-fill.svg
new file mode 100644
index 00000000..ed608226
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/slash-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/slash-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/slash-circle.svg
new file mode 100644
index 00000000..f6756f82
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/slash-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/slash-lg.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/slash-lg.svg
new file mode 100644
index 00000000..b75b776f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/slash-lg.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/slash-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/slash-square-fill.svg
new file mode 100644
index 00000000..0f31d482
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/slash-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/slash-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/slash-square.svg
new file mode 100644
index 00000000..c85b1ccc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/slash-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/slash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/slash.svg
new file mode 100644
index 00000000..586a92ac
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/slash.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sliders.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sliders.svg
new file mode 100644
index 00000000..beff9e40
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sliders.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sliders2-vertical.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sliders2-vertical.svg
new file mode 100644
index 00000000..bdc828d5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sliders2-vertical.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sliders2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sliders2.svg
new file mode 100644
index 00000000..0adce19b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sliders2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/smartwatch.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/smartwatch.svg
new file mode 100644
index 00000000..9e4a0247
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/smartwatch.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/snapchat.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/snapchat.svg
new file mode 100644
index 00000000..b175731d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/snapchat.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/snow.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/snow.svg
new file mode 100644
index 00000000..70bc89c3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/snow.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/snow2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/snow2.svg
new file mode 100644
index 00000000..ac4706a1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/snow2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/snow3.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/snow3.svg
new file mode 100644
index 00000000..62fd5150
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/snow3.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-alpha-down-alt.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-alpha-down-alt.svg
new file mode 100644
index 00000000..ebf01eda
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-alpha-down-alt.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-alpha-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-alpha-down.svg
new file mode 100644
index 00000000..869f880f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-alpha-down.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-alpha-up-alt.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-alpha-up-alt.svg
new file mode 100644
index 00000000..588e058f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-alpha-up-alt.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-alpha-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-alpha-up.svg
new file mode 100644
index 00000000..199766f8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-alpha-up.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-down-alt.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-down-alt.svg
new file mode 100644
index 00000000..5ff1617e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-down-alt.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-down.svg
new file mode 100644
index 00000000..91ffd736
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-down.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-numeric-down-alt.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-numeric-down-alt.svg
new file mode 100644
index 00000000..74d6122d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-numeric-down-alt.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-numeric-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-numeric-down.svg
new file mode 100644
index 00000000..49637c5c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-numeric-down.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-numeric-up-alt.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-numeric-up-alt.svg
new file mode 100644
index 00000000..307ced49
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-numeric-up-alt.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-numeric-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-numeric-up.svg
new file mode 100644
index 00000000..2758ee97
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-numeric-up.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-up-alt.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-up-alt.svg
new file mode 100644
index 00000000..6ac5260b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-up-alt.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-up.svg
new file mode 100644
index 00000000..06a991f6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sort-up.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/soundwave.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/soundwave.svg
new file mode 100644
index 00000000..e48a5e43
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/soundwave.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sourceforge.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sourceforge.svg
new file mode 100644
index 00000000..b0f2a09b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sourceforge.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/speaker-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/speaker-fill.svg
new file mode 100644
index 00000000..79f4739e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/speaker-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/speaker.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/speaker.svg
new file mode 100644
index 00000000..5e4e33d8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/speaker.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/speedometer.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/speedometer.svg
new file mode 100644
index 00000000..7ca71ad1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/speedometer.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/speedometer2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/speedometer2.svg
new file mode 100644
index 00000000..7ff7e212
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/speedometer2.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/spellcheck.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/spellcheck.svg
new file mode 100644
index 00000000..ca37840c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/spellcheck.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/spinner.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/spinner.svg
new file mode 100644
index 00000000..b8944ac5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/spinner.svg
@@ -0,0 +1,3 @@
+
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/spotify.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/spotify.svg
new file mode 100644
index 00000000..3ba29744
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/spotify.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/square-fill.svg
new file mode 100644
index 00000000..32c32498
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/square-half.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/square-half.svg
new file mode 100644
index 00000000..789eb614
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/square-half.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/square.svg
new file mode 100644
index 00000000..d1ca3c58
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/square.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/stack-overflow.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/stack-overflow.svg
new file mode 100644
index 00000000..e3e149c4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/stack-overflow.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/stack.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/stack.svg
new file mode 100644
index 00000000..a689b21e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/stack.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/star-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/star-fill.svg
new file mode 100644
index 00000000..3252816e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/star-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/star-half.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/star-half.svg
new file mode 100644
index 00000000..5a7fa242
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/star-half.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/star.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/star.svg
new file mode 100644
index 00000000..73296f30
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/star.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/stars.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/stars.svg
new file mode 100644
index 00000000..a8ded4df
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/stars.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/steam.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/steam.svg
new file mode 100644
index 00000000..6db96878
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/steam.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/stickies-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/stickies-fill.svg
new file mode 100644
index 00000000..568326ef
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/stickies-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/stickies.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/stickies.svg
new file mode 100644
index 00000000..034440e9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/stickies.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sticky-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sticky-fill.svg
new file mode 100644
index 00000000..83140742
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sticky-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sticky.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sticky.svg
new file mode 100644
index 00000000..9a087806
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sticky.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/stop-btn-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/stop-btn-fill.svg
new file mode 100644
index 00000000..20352df3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/stop-btn-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/stop-btn.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/stop-btn.svg
new file mode 100644
index 00000000..a3b87433
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/stop-btn.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/stop-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/stop-circle-fill.svg
new file mode 100644
index 00000000..8b4aba1d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/stop-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/stop-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/stop-circle.svg
new file mode 100644
index 00000000..819ab288
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/stop-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/stop-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/stop-fill.svg
new file mode 100644
index 00000000..510ca14d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/stop-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/stop.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/stop.svg
new file mode 100644
index 00000000..28c9ca1e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/stop.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/stoplights-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/stoplights-fill.svg
new file mode 100644
index 00000000..0d4fbeaa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/stoplights-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/stoplights.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/stoplights.svg
new file mode 100644
index 00000000..8845ed48
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/stoplights.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/stopwatch-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/stopwatch-fill.svg
new file mode 100644
index 00000000..d286a50c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/stopwatch-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/stopwatch.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/stopwatch.svg
new file mode 100644
index 00000000..d20fc8e7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/stopwatch.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/strava.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/strava.svg
new file mode 100644
index 00000000..7f1f9fbc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/strava.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/stripe.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/stripe.svg
new file mode 100644
index 00000000..4b69c80d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/stripe.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/subscript.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/subscript.svg
new file mode 100644
index 00000000..8492a335
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/subscript.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/substack.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/substack.svg
new file mode 100644
index 00000000..266d00a3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/substack.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/subtract.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/subtract.svg
new file mode 100644
index 00000000..b5314c4a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/subtract.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/success.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/success.svg
new file mode 100644
index 00000000..85dad728
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/success.svg
@@ -0,0 +1,4 @@
+
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-club-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-club-fill.svg
new file mode 100644
index 00000000..413575d6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-club-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-club.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-club.svg
new file mode 100644
index 00000000..2ef4205a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-club.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-diamond-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-diamond-fill.svg
new file mode 100644
index 00000000..cfb4a203
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-diamond-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-diamond.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-diamond.svg
new file mode 100644
index 00000000..ef69774c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-diamond.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-heart-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-heart-fill.svg
new file mode 100644
index 00000000..ce197506
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-heart-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-heart.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-heart.svg
new file mode 100644
index 00000000..7a4328f9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-heart.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-spade-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-spade-fill.svg
new file mode 100644
index 00000000..3651b53e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-spade-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-spade.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-spade.svg
new file mode 100644
index 00000000..d29ac58a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/suit-spade.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase-fill.svg
new file mode 100644
index 00000000..04667c45
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase-lg-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase-lg-fill.svg
new file mode 100644
index 00000000..a6ccef38
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase-lg-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase-lg.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase-lg.svg
new file mode 100644
index 00000000..e68b21f4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase-lg.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase.svg
new file mode 100644
index 00000000..6bb4744a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase2-fill.svg
new file mode 100644
index 00000000..e678003f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase2-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase2.svg
new file mode 100644
index 00000000..6126c821
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/suitcase2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sun-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sun-fill.svg
new file mode 100644
index 00000000..10ef7aee
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sun-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sun.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sun.svg
new file mode 100644
index 00000000..35088f68
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sun.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sunglasses.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sunglasses.svg
new file mode 100644
index 00000000..83417d7b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sunglasses.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sunrise-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sunrise-fill.svg
new file mode 100644
index 00000000..6f33f8e1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sunrise-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sunrise.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sunrise.svg
new file mode 100644
index 00000000..4136e195
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sunrise.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sunset-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sunset-fill.svg
new file mode 100644
index 00000000..0cc2ac7a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sunset-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/sunset.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/sunset.svg
new file mode 100644
index 00000000..cc83e155
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/sunset.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/superscript.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/superscript.svg
new file mode 100644
index 00000000..36095d3c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/superscript.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/symmetry-horizontal.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/symmetry-horizontal.svg
new file mode 100644
index 00000000..bb1742ba
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/symmetry-horizontal.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/symmetry-vertical.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/symmetry-vertical.svg
new file mode 100644
index 00000000..a89412b6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/symmetry-vertical.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/table.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/table.svg
new file mode 100644
index 00000000..9de46f13
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/table.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tablet-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tablet-fill.svg
new file mode 100644
index 00000000..d2b65f3a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tablet-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tablet-landscape-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tablet-landscape-fill.svg
new file mode 100644
index 00000000..8e2bac5d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tablet-landscape-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tablet-landscape.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tablet-landscape.svg
new file mode 100644
index 00000000..fd0efac9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tablet-landscape.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tablet.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tablet.svg
new file mode 100644
index 00000000..05b9d8ee
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tablet.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tag-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tag-fill.svg
new file mode 100644
index 00000000..5871f00f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tag-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tag.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tag.svg
new file mode 100644
index 00000000..192c0e23
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tag.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tags-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tags-fill.svg
new file mode 100644
index 00000000..9d45e3b0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tags-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tags.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tags.svg
new file mode 100644
index 00000000..493a20da
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tags.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/taxi-front-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/taxi-front-fill.svg
new file mode 100644
index 00000000..3425346f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/taxi-front-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/taxi-front.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/taxi-front.svg
new file mode 100644
index 00000000..f87d0faf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/taxi-front.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/telegram.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/telegram.svg
new file mode 100644
index 00000000..f16eace5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/telegram.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-fill.svg
new file mode 100644
index 00000000..9882b232
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-forward-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-forward-fill.svg
new file mode 100644
index 00000000..20ac8d3a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-forward-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-forward.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-forward.svg
new file mode 100644
index 00000000..cb58fcdf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-forward.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-inbound-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-inbound-fill.svg
new file mode 100644
index 00000000..fef3e264
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-inbound-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-inbound.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-inbound.svg
new file mode 100644
index 00000000..4a86476b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-inbound.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-minus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-minus-fill.svg
new file mode 100644
index 00000000..277cae8e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-minus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-minus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-minus.svg
new file mode 100644
index 00000000..254b6332
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-minus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-outbound-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-outbound-fill.svg
new file mode 100644
index 00000000..0530d649
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-outbound-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-outbound.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-outbound.svg
new file mode 100644
index 00000000..da39faae
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-outbound.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-plus-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-plus-fill.svg
new file mode 100644
index 00000000..25742d9c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-plus-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-plus.svg
new file mode 100644
index 00000000..ed0f6012
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-plus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-x-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-x-fill.svg
new file mode 100644
index 00000000..a7e881c2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-x-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-x.svg
new file mode 100644
index 00000000..e21c6620
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone.svg
new file mode 100644
index 00000000..201bcff4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/telephone.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tencent-qq.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tencent-qq.svg
new file mode 100644
index 00000000..f11f5e4d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tencent-qq.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal-dash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal-dash.svg
new file mode 100644
index 00000000..bd0b3db6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal-dash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal-fill.svg
new file mode 100644
index 00000000..a06303f0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal-plus.svg
new file mode 100644
index 00000000..c53fdffa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal-plus.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal-split.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal-split.svg
new file mode 100644
index 00000000..4b959756
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal-split.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal-x.svg
new file mode 100644
index 00000000..ca799a4a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal-x.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal.svg
new file mode 100644
index 00000000..44b39e53
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/terminal.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/text-center.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/text-center.svg
new file mode 100644
index 00000000..057abc43
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/text-center.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/text-indent-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/text-indent-left.svg
new file mode 100644
index 00000000..3a226612
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/text-indent-left.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/text-indent-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/text-indent-right.svg
new file mode 100644
index 00000000..09512d3f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/text-indent-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/text-left.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/text-left.svg
new file mode 100644
index 00000000..615db68b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/text-left.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/text-paragraph.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/text-paragraph.svg
new file mode 100644
index 00000000..ba6296db
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/text-paragraph.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/text-right.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/text-right.svg
new file mode 100644
index 00000000..b6148f88
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/text-right.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/text-wrap.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/text-wrap.svg
new file mode 100644
index 00000000..6bf272cf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/text-wrap.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/textarea-resize.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/textarea-resize.svg
new file mode 100644
index 00000000..9872a784
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/textarea-resize.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/textarea-t.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/textarea-t.svg
new file mode 100644
index 00000000..2a04d25f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/textarea-t.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/textarea.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/textarea.svg
new file mode 100644
index 00000000..b9823b23
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/textarea.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer-half.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer-half.svg
new file mode 100644
index 00000000..0c878152
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer-half.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer-high.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer-high.svg
new file mode 100644
index 00000000..4b23a489
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer-high.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer-low.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer-low.svg
new file mode 100644
index 00000000..7f2f5b9c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer-low.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer-snow.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer-snow.svg
new file mode 100644
index 00000000..509c79dc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer-snow.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer-sun.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer-sun.svg
new file mode 100644
index 00000000..a0289760
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer-sun.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer.svg
new file mode 100644
index 00000000..26a0b829
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/thermometer.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/threads-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/threads-fill.svg
new file mode 100644
index 00000000..5125e60b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/threads-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/threads.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/threads.svg
new file mode 100644
index 00000000..aaff8406
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/threads.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/three-dots-vertical.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/three-dots-vertical.svg
new file mode 100644
index 00000000..66f2528e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/three-dots-vertical.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/three-dots.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/three-dots.svg
new file mode 100644
index 00000000..9f7667a3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/three-dots.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/thunderbolt-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/thunderbolt-fill.svg
new file mode 100644
index 00000000..6bea1f51
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/thunderbolt-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/thunderbolt.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/thunderbolt.svg
new file mode 100644
index 00000000..86c0d1dc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/thunderbolt.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket-detailed-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket-detailed-fill.svg
new file mode 100644
index 00000000..ecde8579
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket-detailed-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket-detailed.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket-detailed.svg
new file mode 100644
index 00000000..21f6d5e4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket-detailed.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket-fill.svg
new file mode 100644
index 00000000..26b0dd34
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket-perforated-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket-perforated-fill.svg
new file mode 100644
index 00000000..d41dfc90
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket-perforated-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket-perforated.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket-perforated.svg
new file mode 100644
index 00000000..d84999c7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket-perforated.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket.svg
new file mode 100644
index 00000000..547b83d3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ticket.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tiktok.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tiktok.svg
new file mode 100644
index 00000000..6332ae6a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tiktok.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/toggle-off.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/toggle-off.svg
new file mode 100644
index 00000000..299ede31
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/toggle-off.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/toggle-on.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/toggle-on.svg
new file mode 100644
index 00000000..a18b2c02
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/toggle-on.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/toggle2-off.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/toggle2-off.svg
new file mode 100644
index 00000000..bbfe3fd2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/toggle2-off.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/toggle2-on.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/toggle2-on.svg
new file mode 100644
index 00000000..ab47a954
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/toggle2-on.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/toggles.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/toggles.svg
new file mode 100644
index 00000000..3e782a19
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/toggles.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/toggles2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/toggles2.svg
new file mode 100644
index 00000000..c3e7f3d7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/toggles2.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tools.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tools.svg
new file mode 100644
index 00000000..1e0d2796
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tools.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tornado.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tornado.svg
new file mode 100644
index 00000000..cf0c66b4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tornado.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/train-freight-front-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/train-freight-front-fill.svg
new file mode 100644
index 00000000..336c3b36
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/train-freight-front-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/train-freight-front.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/train-freight-front.svg
new file mode 100644
index 00000000..b54986d8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/train-freight-front.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/train-front-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/train-front-fill.svg
new file mode 100644
index 00000000..44d40231
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/train-front-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/train-front.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/train-front.svg
new file mode 100644
index 00000000..14155d8c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/train-front.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/train-lightrail-front-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/train-lightrail-front-fill.svg
new file mode 100644
index 00000000..09c8ad2e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/train-lightrail-front-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/train-lightrail-front.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/train-lightrail-front.svg
new file mode 100644
index 00000000..9e129105
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/train-lightrail-front.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/translate.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/translate.svg
new file mode 100644
index 00000000..4654968f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/translate.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/transparency.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/transparency.svg
new file mode 100644
index 00000000..2d0429bf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/transparency.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/trash-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/trash-fill.svg
new file mode 100644
index 00000000..de5eb645
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/trash-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/trash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/trash.svg
new file mode 100644
index 00000000..94b98b5e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/trash.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/trash2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/trash2-fill.svg
new file mode 100644
index 00000000..466e1c9d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/trash2-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/trash2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/trash2.svg
new file mode 100644
index 00000000..17d6ed80
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/trash2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/trash3-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/trash3-fill.svg
new file mode 100644
index 00000000..4874933d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/trash3-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/trash3.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/trash3.svg
new file mode 100644
index 00000000..496ed18d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/trash3.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tree-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tree-fill.svg
new file mode 100644
index 00000000..9cbf3b11
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tree-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tree.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tree.svg
new file mode 100644
index 00000000..c22a39da
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tree.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/trello.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/trello.svg
new file mode 100644
index 00000000..5d95b849
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/trello.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/triangle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/triangle-fill.svg
new file mode 100644
index 00000000..cb21f60f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/triangle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/triangle-half.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/triangle-half.svg
new file mode 100644
index 00000000..6a8dac37
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/triangle-half.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/triangle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/triangle.svg
new file mode 100644
index 00000000..d3d7312c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/triangle.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/trophy-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/trophy-fill.svg
new file mode 100644
index 00000000..38c9ef5a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/trophy-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/trophy.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/trophy.svg
new file mode 100644
index 00000000..84c369f6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/trophy.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tropical-storm.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tropical-storm.svg
new file mode 100644
index 00000000..6bfaa11d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tropical-storm.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/truck-flatbed.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/truck-flatbed.svg
new file mode 100644
index 00000000..5d99cfe0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/truck-flatbed.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/truck-front-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/truck-front-fill.svg
new file mode 100644
index 00000000..f9746836
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/truck-front-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/truck-front.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/truck-front.svg
new file mode 100644
index 00000000..fb6abd15
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/truck-front.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/truck.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/truck.svg
new file mode 100644
index 00000000..7b313c91
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/truck.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tsunami.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tsunami.svg
new file mode 100644
index 00000000..7f8ee166
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tsunami.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tux.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tux.svg
new file mode 100644
index 00000000..59e505eb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tux.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tv-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tv-fill.svg
new file mode 100644
index 00000000..ac56fc2c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tv-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/tv.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/tv.svg
new file mode 100644
index 00000000..126d3423
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/tv.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/twitch.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/twitch.svg
new file mode 100644
index 00000000..e7107d99
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/twitch.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/twitter-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/twitter-x.svg
new file mode 100644
index 00000000..1af978c9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/twitter-x.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/twitter.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/twitter.svg
new file mode 100644
index 00000000..0bee5dbc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/twitter.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/type-bold.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-bold.svg
new file mode 100644
index 00000000..9ca85bcc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-bold.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h1.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h1.svg
new file mode 100644
index 00000000..924bc1f5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h1.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h2.svg
new file mode 100644
index 00000000..4b1df696
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h3.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h3.svg
new file mode 100644
index 00000000..d97df9e7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h3.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h4.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h4.svg
new file mode 100644
index 00000000..037d7fa2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h4.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h5.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h5.svg
new file mode 100644
index 00000000..9cc56e96
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h5.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h6.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h6.svg
new file mode 100644
index 00000000..b3437818
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-h6.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/type-italic.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-italic.svg
new file mode 100644
index 00000000..9fa7a863
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-italic.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/type-strikethrough.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-strikethrough.svg
new file mode 100644
index 00000000..441beebf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-strikethrough.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/type-underline.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-underline.svg
new file mode 100644
index 00000000..6390c75b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/type-underline.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/type.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/type.svg
new file mode 100644
index 00000000..c0803cc3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/type.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/typescript.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/typescript.svg
new file mode 100644
index 00000000..b9ca36bd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/typescript.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ubuntu.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ubuntu.svg
new file mode 100644
index 00000000..6e5c37b7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ubuntu.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ui-checks-grid.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ui-checks-grid.svg
new file mode 100644
index 00000000..b0a2ea4e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ui-checks-grid.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ui-checks.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ui-checks.svg
new file mode 100644
index 00000000..32b30533
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ui-checks.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ui-radios-grid.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ui-radios-grid.svg
new file mode 100644
index 00000000..f9ac229d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ui-radios-grid.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/ui-radios.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/ui-radios.svg
new file mode 100644
index 00000000..0f50bfc1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/ui-radios.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/umbrella-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/umbrella-fill.svg
new file mode 100644
index 00000000..0a13fe93
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/umbrella-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/umbrella.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/umbrella.svg
new file mode 100644
index 00000000..3c77651e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/umbrella.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/unindent.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/unindent.svg
new file mode 100644
index 00000000..1ac0b986
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/unindent.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/union.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/union.svg
new file mode 100644
index 00000000..ab9bce3b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/union.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/unity.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/unity.svg
new file mode 100644
index 00000000..8ddd5ef9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/unity.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/universal-access-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/universal-access-circle.svg
new file mode 100644
index 00000000..0621ebd6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/universal-access-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/universal-access.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/universal-access.svg
new file mode 100644
index 00000000..71975509
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/universal-access.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/unlock-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/unlock-fill.svg
new file mode 100644
index 00000000..3b490676
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/unlock-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/unlock.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/unlock.svg
new file mode 100644
index 00000000..30d0a938
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/unlock.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/unlock2-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/unlock2-fill.svg
new file mode 100644
index 00000000..ed59ca6c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/unlock2-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/unlock2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/unlock2.svg
new file mode 100644
index 00000000..510ef053
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/unlock2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/upc-scan.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/upc-scan.svg
new file mode 100644
index 00000000..9d3ac1b4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/upc-scan.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/upc.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/upc.svg
new file mode 100644
index 00000000..fb00bc52
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/upc.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/upload.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/upload.svg
new file mode 100644
index 00000000..17e71248
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/upload.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-c-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-c-fill.svg
new file mode 100644
index 00000000..7dd9851f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-c-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-c.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-c.svg
new file mode 100644
index 00000000..a693d4e0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-c.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-drive-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-drive-fill.svg
new file mode 100644
index 00000000..f181bb6f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-drive-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-drive.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-drive.svg
new file mode 100644
index 00000000..6eaa6ef9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-drive.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-fill.svg
new file mode 100644
index 00000000..98214c4d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-micro-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-micro-fill.svg
new file mode 100644
index 00000000..7f396486
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-micro-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-micro.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-micro.svg
new file mode 100644
index 00000000..4f4803f1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-micro.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-mini-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-mini-fill.svg
new file mode 100644
index 00000000..a1dd7580
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-mini-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-mini.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-mini.svg
new file mode 100644
index 00000000..2f80c6fb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-mini.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-plug-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-plug-fill.svg
new file mode 100644
index 00000000..9231bcf0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-plug-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-plug.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-plug.svg
new file mode 100644
index 00000000..0f107765
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-plug.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-symbol.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-symbol.svg
new file mode 100644
index 00000000..86730acb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb-symbol.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/usb.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb.svg
new file mode 100644
index 00000000..76964053
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/usb.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/valentine.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/valentine.svg
new file mode 100644
index 00000000..7e3276cf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/valentine.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/valentine2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/valentine2.svg
new file mode 100644
index 00000000..91ed70bb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/valentine2.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/vector-pen.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/vector-pen.svg
new file mode 100644
index 00000000..f0bb0905
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/vector-pen.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/view-list.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/view-list.svg
new file mode 100644
index 00000000..41f4421a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/view-list.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/view-stacked.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/view-stacked.svg
new file mode 100644
index 00000000..51cc0d3b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/view-stacked.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/vignette.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/vignette.svg
new file mode 100644
index 00000000..cac04cfb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/vignette.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/vimeo.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/vimeo.svg
new file mode 100644
index 00000000..18436f0a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/vimeo.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/vinyl-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/vinyl-fill.svg
new file mode 100644
index 00000000..6652047c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/vinyl-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/vinyl.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/vinyl.svg
new file mode 100644
index 00000000..d4be3d2c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/vinyl.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/virus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/virus.svg
new file mode 100644
index 00000000..1cb12307
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/virus.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/virus2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/virus2.svg
new file mode 100644
index 00000000..f58d7ac7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/virus2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/voicemail.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/voicemail.svg
new file mode 100644
index 00000000..67e18e10
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/voicemail.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-down-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-down-fill.svg
new file mode 100644
index 00000000..28051899
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-down-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-down.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-down.svg
new file mode 100644
index 00000000..f539e3bf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-down.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-mute-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-mute-fill.svg
new file mode 100644
index 00000000..4fd27486
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-mute-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-mute.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-mute.svg
new file mode 100644
index 00000000..1c3bf64d
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-mute.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-off-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-off-fill.svg
new file mode 100644
index 00000000..2dacfa73
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-off-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-off.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-off.svg
new file mode 100644
index 00000000..ccaf7be5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-off.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-up-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-up-fill.svg
new file mode 100644
index 00000000..59100c2c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-up-fill.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-up.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-up.svg
new file mode 100644
index 00000000..ad8163b6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/volume-up.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/vr.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/vr.svg
new file mode 100644
index 00000000..e32863af
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/vr.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/wallet-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/wallet-fill.svg
new file mode 100644
index 00000000..916c91ca
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/wallet-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/wallet.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/wallet.svg
new file mode 100644
index 00000000..046e4c1b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/wallet.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/wallet2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/wallet2.svg
new file mode 100644
index 00000000..7dba9c08
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/wallet2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/warning.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/warning.svg
new file mode 100644
index 00000000..586a83c4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/warning.svg
@@ -0,0 +1,4 @@
+
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/watch.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/watch.svg
new file mode 100644
index 00000000..f42ff67c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/watch.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/water.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/water.svg
new file mode 100644
index 00000000..583b4c6a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/water.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/webcam-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/webcam-fill.svg
new file mode 100644
index 00000000..b6039bb2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/webcam-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/webcam.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/webcam.svg
new file mode 100644
index 00000000..b5a706a6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/webcam.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/wechat.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/wechat.svg
new file mode 100644
index 00000000..8fb4871e
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/wechat.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/whatsapp.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/whatsapp.svg
new file mode 100644
index 00000000..d39e47f4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/whatsapp.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/wifi-1.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/wifi-1.svg
new file mode 100644
index 00000000..98e7d482
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/wifi-1.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/wifi-2.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/wifi-2.svg
new file mode 100644
index 00000000..850826c7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/wifi-2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/wifi-off.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/wifi-off.svg
new file mode 100644
index 00000000..6915c9dc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/wifi-off.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/wifi.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/wifi.svg
new file mode 100644
index 00000000..f1671063
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/wifi.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/wikipedia.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/wikipedia.svg
new file mode 100644
index 00000000..a0c567ca
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/wikipedia.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/wind.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/wind.svg
new file mode 100644
index 00000000..a32f78e7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/wind.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/window-dash.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-dash.svg
new file mode 100644
index 00000000..46436fd6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-dash.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/window-desktop.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-desktop.svg
new file mode 100644
index 00000000..d62148aa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-desktop.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/window-dock.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-dock.svg
new file mode 100644
index 00000000..279064dd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-dock.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/window-fullscreen.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-fullscreen.svg
new file mode 100644
index 00000000..309aded3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-fullscreen.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/window-plus.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-plus.svg
new file mode 100644
index 00000000..8bdca678
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-plus.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/window-sidebar.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-sidebar.svg
new file mode 100644
index 00000000..d2c593fb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-sidebar.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/window-split.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-split.svg
new file mode 100644
index 00000000..ce50bd0b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-split.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/window-stack.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-stack.svg
new file mode 100644
index 00000000..77fcb8a4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-stack.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/window-x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-x.svg
new file mode 100644
index 00000000..744fcd13
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/window-x.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/window.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/window.svg
new file mode 100644
index 00000000..c7c5b0e9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/window.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/windows.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/windows.svg
new file mode 100644
index 00000000..b52dd477
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/windows.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/wordpress.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/wordpress.svg
new file mode 100644
index 00000000..f36f5959
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/wordpress.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/wrench-adjustable-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/wrench-adjustable-circle-fill.svg
new file mode 100644
index 00000000..19bf7c87
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/wrench-adjustable-circle-fill.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/wrench-adjustable-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/wrench-adjustable-circle.svg
new file mode 100644
index 00000000..b3de78d3
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/wrench-adjustable-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/wrench-adjustable.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/wrench-adjustable.svg
new file mode 100644
index 00000000..4e2ec5eb
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/wrench-adjustable.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/wrench.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/wrench.svg
new file mode 100644
index 00000000..652f4fb5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/wrench.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/x-circle-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-circle-fill.svg
new file mode 100644
index 00000000..33b90e8c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-circle-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/x-circle.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-circle.svg
new file mode 100644
index 00000000..dbf7eff0
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-circle.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/x-diamond-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-diamond-fill.svg
new file mode 100644
index 00000000..6307e2ce
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-diamond-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/x-diamond.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-diamond.svg
new file mode 100644
index 00000000..004a709b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-diamond.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/x-lg.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-lg.svg
new file mode 100644
index 00000000..8b8027ca
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-lg.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/x-octagon-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-octagon-fill.svg
new file mode 100644
index 00000000..8337450a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-octagon-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/x-octagon.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-octagon.svg
new file mode 100644
index 00000000..de6b88bc
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-octagon.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/x-square-fill.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-square-fill.svg
new file mode 100644
index 00000000..79aeb6d4
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-square-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/x-square.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-square.svg
new file mode 100644
index 00000000..f05e3b0a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/x-square.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/x.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/x.svg
new file mode 100644
index 00000000..d4312c8a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/x.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/xbox.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/xbox.svg
new file mode 100644
index 00000000..00082cbf
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/xbox.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/yelp.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/yelp.svg
new file mode 100644
index 00000000..2053e86f
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/yelp.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/yin-yang.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/yin-yang.svg
new file mode 100644
index 00000000..7bf06f8b
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/yin-yang.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/youtube.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/youtube.svg
new file mode 100644
index 00000000..a5d76742
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/youtube.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/zoom-in.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/zoom-in.svg
new file mode 100644
index 00000000..61ea3871
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/zoom-in.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/icons/zoom-out.svg b/dist/macos/Replicator.app/Contents/Resources/core/icons/zoom-out.svg
new file mode 100644
index 00000000..98d474c5
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/icons/zoom-out.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/log.py b/dist/macos/Replicator.app/Contents/Resources/core/log.py
new file mode 100644
index 00000000..bf178f17
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/log.py
@@ -0,0 +1,459 @@
+#!/usr/bin/env python3
+# src/core/log.py
+import os
+import re
+import threading
+from datetime import datetime
+from collections import defaultdict
+from typing import Iterable, Optional, TYPE_CHECKING
+
+from PyQt5.QtCore import Qt, QRegExp
+from PyQt5.QtGui import QTextCharFormat, QColor
+from PyQt5.QtWidgets import (
+ QDialog, QTextEdit, QLineEdit, QPushButton, QHBoxLayout,
+ QVBoxLayout, QFileDialog, QWidget, QComboBox, QApplication
+)
+
+from .helper import Helper
+from .ui import MsgBox
+
+if TYPE_CHECKING:
+ # For type hints only, avoids circular import at runtime
+ try:
+ from .application import Application
+ from .configuration import Configuration
+ except ImportError:
+ from application import Application
+ from configuration import Configuration
+
+class Log:
+
+ def __init__(self, helper: Helper | None = None):
+
+ # Retrieve the application instance (may be None in CLI usage)
+ self._app: Application | None = QApplication.instance() # type: ignore[valid-type]
+
+ # Ensure we have either an Application or an explicit Helper
+ if self._app is None and helper is None:
+ raise RuntimeError(
+ "Configuration must be created after QApplication/Application or with an explicit Helper."
+ )
+
+ # --- auto-wire from QApplication if not provided ---
+ if helper is None and self._app is not None:
+ helper = self._app.helper # type: ignore[attr-defined]
+
+ # Helper
+ self._helper: Helper = helper
+
+ # Configuration
+ self._configuration: Configuration = self._app.configuration
+ self._configuration.add("log.level", "info", "select", choices=["debug", "info", "warning", "error", "none"])
+ self._configuration.add("log.enabled", True, "checkbox")
+ self._configuration.add("log.open", None, "button", label="Open Log", action=self.show)
+ self._configuration.add("log.clear", False, "checkbox", label="Allow clearing log")
+ self._configuration.add("log.verbose", False, "checkbox", label="Verbose logging")
+
+ # Save any new defaults
+ self._configuration.save()
+
+ # Parent
+ self._parent = None
+
+ # Channel
+ self._channel = None
+
+ # Core storage
+ self._lock = threading.Lock()
+ self._buffers: dict[str, list[str]] = defaultdict(list)
+
+ # ---------- file persistence ----------
+ # Save log lines to disk under /log/
+ self._log_dir = self._helper.get_data_path("log",scope="system")
+ try:
+ os.makedirs(self._log_dir, exist_ok=True)
+ except Exception:
+ # If the directory cannot be created, we keep logging in memory.
+ pass
+
+ # Optional configuration toggle for file logging
+ self._configuration.add("log.persist", True, "checkbox", label="Save log to file")
+ self._configuration.save()
+
+ # Track currently used daily log file
+ self._current_log_path: str | None = None
+
+ # ---------- file persistence helpers ----------
+
+ def _safe_filename(self, name: str) -> str:
+ name = (name or "app").strip()
+ # Keep only safe characters for filenames
+ name = re.sub(r"[^A-Za-z0-9._-]+", "_", name)
+ return name or "app"
+
+ def _get_log_path(self) -> str | None:
+ # If directory creation failed earlier, bail out
+ if not os.path.isdir(self._log_dir):
+ return None
+
+ app_name = "app"
+ if self._app is not None:
+ # Prefer application name when available
+ app_name = getattr(self._app, "name", None) or getattr(self._app, "applicationName", None) or "app"
+
+ app_name = self._safe_filename(str(app_name))
+ date_str = datetime.now().strftime("%Y-%m-%d")
+ return os.path.join(self._log_dir, f"{app_name}-{date_str}.log")
+
+ def _append_to_file(self, lines: list[str]) -> None:
+ if not lines:
+ return
+ if not self._configuration.get("log.persist"):
+ return
+
+ path = self._get_log_path()
+ if not path:
+ return
+
+ # Write is protected by the same lock as in-memory buffers to keep ordering
+ try:
+ with open(path, "a", encoding="utf-8") as f:
+ for ln in lines:
+ f.write(ln)
+ if not ln.endswith("\n"):
+ f.write("\n")
+ self._current_log_path = path
+ except Exception:
+ # Never crash the app because file logging failed
+ return
+
+ # ---------- core storage ----------
+
+ def append(self, message: str, channel: str = "default", level: str = "info") -> None:
+
+ # Check if logging is enabled and level is sufficient
+ if not self._configuration.get("log.enabled"):
+ return
+ if self._configuration.get("log.level") == "none":
+ return
+ if self._configuration.get("log.level") == "error" and level != "error":
+ return
+ if self._configuration.get("log.level") == "warning" and level not in ("warning", "error"):
+ return
+ if self._configuration.get("log.level") == "info" and level not in ("info", "warning", "error"):
+ return
+ if self._configuration.get("log.level") == "debug" and level not in ("debug", "info", "warning", "error"):
+ return
+ if not message:
+ return
+
+ # Normalize to individual lines
+ raw_lines = message.splitlines() or [message]
+
+ # Format each line with timestamp, level, channel
+ lines: list[str] = []
+ now = self._helper.get_now()
+ for ln in raw_lines:
+ line = f"[{now}][{level.upper()}][{channel}] {ln}"
+ lines.append(line)
+
+ # Check if verbose logging is enabled and print to console
+ if self._configuration.get("log.verbose"):
+ print(line)
+
+ # Append lines to the buffer and persist to file (keeps ordering)
+ with self._lock:
+ self._buffers[channel].extend(lines)
+ self._append_to_file(lines)
+
+ def extend(self, lines: Iterable[str], channel: str = "default") -> None:
+ with self._lock:
+ for ln in lines:
+ if ln:
+ self._buffers[channel].append(str(ln))
+
+ def get_text(self, channel: Optional[str] = None) -> str:
+ with self._lock:
+ # All channels
+ if channel is None:
+ if not self._buffers:
+ return ""
+ # Just concatenate all logs in insertion order of channels
+ parts: list[str] = []
+ for ch, lines in self._buffers.items():
+ if not lines:
+ continue
+ # Optional header per channel (helps when viewing “All”)
+ parts.append(f"[{ch}]")
+ parts.extend(lines)
+ parts.append("") # blank line between channels
+ return "\n".join(parts).rstrip()
+ # Single channel
+ if channel not in self._buffers:
+ return ""
+ return "\n".join(self._buffers[channel])
+
+ def clear(self, channel: Optional[str] = None) -> None:
+
+ # Check if clearing is allowed
+ if not self._configuration.get("log.clear"):
+ return
+
+ # Clear all channels if channel is None
+ with self._lock:
+ if channel is None:
+ self._buffers.clear()
+ else:
+ self._buffers.pop(channel, None)
+
+ def has_data(self, channel: str = "default") -> bool:
+ with self._lock:
+ return bool(self._buffers.get(channel))
+
+ def has_any_data(self) -> bool:
+ with self._lock:
+ return any(bool(buf) for buf in self._buffers.values())
+
+ # ---------- UI helper ----------
+
+ def show(self, parent=None, channel: Optional[str] = None, filter_text: str | None = None):
+ # If called from a clicked(bool) signal, parent may be a bool; ignore non-widget parents
+ if not isinstance(parent, QWidget):
+ parent = None
+
+ if parent is not None and isinstance(parent, QWidget):
+ self._parent = parent
+ elif self._parent is None:
+ # Try to parent to the currently active window (e.g. the configuration dialog)
+ aw = QApplication.activeWindow()
+ if isinstance(aw, QWidget):
+ self._parent = aw
+
+ # Remember last requested channel (can be None = all)
+ self._channel = channel
+
+ if channel is None:
+ # All channels
+ if not self.has_any_data():
+ MsgBox.show(
+ title="No log available",
+ message="There is no log yet. Perform some actions first.",
+ icon="info",
+ buttons="OK",
+ default="OK",
+ icon_lookup_fn=self._helper.get_sys_path,
+ )
+ return
+
+ # Snapshot buffers for the dialog
+ with self._lock:
+ channels_text = {
+ ch: "\n".join(lines) for ch, lines in self._buffers.items()
+ }
+ text = self.get_text(None) # all channels, formatted
+ else:
+ # Single channel
+ if not self.has_data(channel):
+ MsgBox.show(
+ title="No log available",
+ message="There is no log yet for this channel. Perform some actions first.",
+ icon="info",
+ buttons="OK",
+ default="OK",
+ icon_lookup_fn=self._helper.get_sys_path,
+ )
+ return
+
+ with self._lock:
+ channels_text = {
+ ch: "\n".join(lines) for ch, lines in self._buffers.items()
+ }
+ text = self.get_text(channel)
+
+ dlg = LogDialog(
+ self._parent,
+ text=text,
+ filter_text=filter_text,
+ channels_text=channels_text,
+ initial_channel=channel,
+ default_save_dir=getattr(self, "_log_dir", None),
+ )
+ # Run the log window as a modal dialog so it appears on top and is interactive
+ dlg.exec_()
+ return dlg
+
+class LogDialog(QDialog):
+
+ def __init__(
+ self,
+ parent=None,
+ text: str = "",
+ filter_text: str | None = None,
+ channels_text: Optional[dict[str, str]] = None,
+ initial_channel: Optional[str] = None,
+ default_save_dir: str | None = None,
+ ):
+ super().__init__(parent)
+ self.setWindowTitle("Log")
+ self.setObjectName("logWindow")
+ self.setMinimumSize(720, 420)
+
+ # Store per-channel logs (may be empty dict)
+ self._channels_text: dict[str, str] = channels_text or {}
+ self._current_channel: Optional[str] = initial_channel
+ self._default_save_dir = default_save_dir
+
+ self._full_text = text or ""
+
+ self.text = QTextEdit(self)
+ self.text.setReadOnly(True)
+ self.text.setPlainText(self._full_text)
+
+ # Channel selector
+ self.channel_box = QComboBox(self)
+ self.channel_box.addItem("All channels", userData=None)
+ for ch in sorted(self._channels_text.keys()):
+ self.channel_box.addItem(ch, userData=ch)
+
+ # Set initial selection
+ if initial_channel is None:
+ self.channel_box.setCurrentIndex(0)
+ else:
+ idx = self.channel_box.findData(initial_channel)
+ if idx >= 0:
+ self.channel_box.setCurrentIndex(idx)
+
+ self.channel_box.currentIndexChanged.connect(self._on_channel_change)
+
+ self.find_box = QLineEdit(self)
+ self.find_box.setPlaceholderText("Filter...")
+ self.find_box.textChanged.connect(self.apply_filter)
+
+ self.copy_btn = QPushButton("Copy all")
+ self.copy_btn.clicked.connect(
+ lambda: self._copy_all()
+ )
+ self.save_btn = QPushButton("Save as...")
+ self.save_btn.clicked.connect(self.save_as)
+
+ top = QHBoxLayout()
+ top.addWidget(self.channel_box) # ⬅️ add channel selector to the toolbar row
+ top.addWidget(self.find_box)
+ top.addWidget(self.copy_btn)
+ top.addWidget(self.save_btn)
+
+ root = QVBoxLayout(self)
+ root.addLayout(top)
+ root.addWidget(self.text)
+
+ # If opened with an initial filter, apply it right away
+ if filter_text:
+ self.find_box.setText(filter_text)
+ else:
+ self.apply_filter()
+
+ # ------ basic operations ------
+
+ def set_text(self, text: str):
+ self._full_text = text or ""
+ self.apply_filter()
+
+ def _copy_all(self):
+ QApplication.clipboard().setText(self.text.toPlainText())
+
+ def save_as(self):
+ default_name = "log.log"
+ if self._default_save_dir:
+ default_name = os.path.join(self._default_save_dir, default_name)
+
+ path, _ = QFileDialog.getSaveFileName(
+ self, "Save log", default_name,
+ "Log Files (*.log);;Text Files (*.txt);;All Files (*)"
+ )
+ if path:
+ with open(path, "w", encoding="utf-8") as f:
+ f.write(self.text.toPlainText())
+
+ # ------ filtering + highlighting ------
+
+ def apply_filter(self):
+
+ needle = self.find_box.text().strip()
+ if not needle:
+ self._set_view_text(self._full_text)
+ return
+
+ filtered_lines = [
+ ln for ln in self._full_text.splitlines()
+ if needle.lower() in ln.lower()
+ ]
+ self._set_view_text("\n".join(filtered_lines))
+ self._highlight_all(needle)
+
+ def _set_view_text(self, s: str):
+ self.text.blockSignals(True)
+ self.text.setPlainText(s)
+ self.text.blockSignals(False)
+
+ def _highlight_all(self, needle: str):
+
+ if not needle:
+ return
+
+ doc = self.text.document()
+ cursor = self.text.textCursor()
+ cursor.beginEditBlock()
+
+ # clear previous formats
+ clear = QTextCharFormat()
+ rng = self.text.textCursor()
+ rng.movePosition(rng.Start)
+ rng.movePosition(rng.End, rng.KeepAnchor)
+ rng.setCharFormat(clear)
+
+ fmt = QTextCharFormat()
+ fmt.setBackground(QColor("yellow"))
+ fmt.setForeground(QColor("black"))
+
+ rx = QRegExp(needle)
+ rx.setCaseSensitivity(Qt.CaseInsensitive)
+
+ pos = 0
+ plain = doc.toPlainText()
+ while True:
+ pos = rx.indexIn(plain, pos)
+ if pos < 0:
+ break
+ match_cursor = self.text.textCursor()
+ match_cursor.setPosition(pos)
+ match_cursor.setPosition(pos + rx.matchedLength(), match_cursor.KeepAnchor)
+ match_cursor.mergeCharFormat(fmt)
+ pos += max(1, rx.matchedLength())
+
+ cursor.endEditBlock()
+
+ # ----- event handlers ------
+
+ def _on_channel_change(self, idx: int):
+ ch = self.channel_box.itemData(idx) # None = All channels
+ self._current_channel = ch
+
+ if ch is None:
+ # All channels: rebuild from the per-channel dict if available,
+ # otherwise fall back to whatever initial text we had.
+ if self._channels_text:
+ parts: list[str] = []
+ for name in sorted(self._channels_text.keys()):
+ txt = (self._channels_text.get(name) or "").strip()
+ if not txt:
+ continue
+ parts.append(f"[{name}]")
+ parts.append(txt)
+ parts.append("")
+ self._full_text = "\n".join(parts).rstrip()
+ else:
+ self._full_text = self._full_text or ""
+ else:
+ self._full_text = self._channels_text.get(ch, "")
+
+ self.apply_filter()
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/network/diagnostic.py b/dist/macos/Replicator.app/Contents/Resources/core/network/diagnostic.py
new file mode 100644
index 00000000..16645c81
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/network/diagnostic.py
@@ -0,0 +1,330 @@
+#!/usr/bin/env python3
+# src/core/network/diagnostic.py
+
+from __future__ import annotations
+
+from typing import Any, Callable, Optional, Iterable, TYPE_CHECKING
+
+from PyQt5.QtCore import QThread, pyqtSignal, Qt, QObject
+from PyQt5.QtWidgets import (
+ QDialog, QVBoxLayout, QHBoxLayout,
+ QLabel, QPushButton, QTextEdit, QWidget,
+ QApplication
+)
+from PyQt5.QtGui import QPixmap, QIcon
+
+try:
+ from core.helper import Helper
+ from core.log import Log
+ from core.ui import SpinningIconLabel
+except ImportError:
+ from helper import Helper
+ from log import Log
+ from ui import SpinningIconLabel
+
+from .tools import Tools
+
+if TYPE_CHECKING:
+ # For type hints only, avoids circular import at runtime
+ try:
+ from core.application import Application
+ except ImportError:
+ from application import Application
+
+class DiagnosticStep:
+ def __init__(
+ self,
+ name: str,
+ label: str,
+ icon: Optional[str],
+ func: Callable[[Callable[[str], None]], bool]
+ ):
+ self.name = name
+ self.label = label
+ self.icon = icon
+ self.func = func
+
+class DiagnosticThread(QThread):
+ log = pyqtSignal(str)
+ step_state = pyqtSignal(str, str) # name, state: idle|running|ok|fail
+ finished = pyqtSignal(dict) # {step_name: bool}
+
+ def __init__(self, steps: Iterable[DiagnosticStep], parent: Optional[QWidget] = None):
+ app = QApplication.instance()
+ if app is not None and hasattr(app, "logger"):
+ self._logger = app.logger
+ else:
+ self._logger = Log()
+ self._logger.append(f"[DiagnosticThread] Initializing with {len(list(steps))} steps", channel="diagnostic", level="debug")
+ super().__init__(parent)
+ self._steps = list(steps)
+
+ def run(self):
+ self._logger.append(f"[DiagnosticThread] run() started with {len(self._steps)} steps", channel="diagnostic", level="debug")
+ results = {}
+
+ step_names = [step.name for step in self._steps]
+ self._logger.append(f"[DiagnosticThread] Steps to run: {step_names}", channel="diagnostic", level="debug")
+
+ def printer(msg: str) -> None:
+ self.log.emit(msg)
+
+ for step in self._steps:
+ self._logger.append(f"[DiagnosticThread] Starting step {step.name}", channel="diagnostic", level="debug")
+ self.step_state.emit(step.name, "running")
+ try:
+ res = step.func(printer)
+ ok = bool(res)
+ self._logger.append(f"[DiagnosticThread] Step {step.name} returned {ok}", channel="diagnostic", level="debug")
+ except Exception as e:
+ printer(f"[{step.label}] error: {e!r}")
+ self._logger.append(f"[DiagnosticThread] Step {step.name} raised exception: {e!r}", channel="diagnostic", level="error")
+ ok = False
+
+ results[step.name] = ok
+ self.step_state.emit(step.name, "ok" if ok else "fail")
+
+ self.finished.emit(results)
+ self._logger.append(f"[DiagnosticThread] run() completed with results: {results}", channel="diagnostic", level="debug")
+
+class Diagnostic(QObject):
+ def __init__(self, host: str, ports: Any, parent: Optional[QWidget] = None):
+ super().__init__(parent)
+
+ # Retrieve the application instance
+ self._app: Application = QApplication.instance()
+
+ # Ensure Client is created after Application
+ if self._app is None:
+ raise RuntimeError("Client must be created after QApplication/Application.")
+
+ self._logger = self._app.logger or Log()
+ self._helper = Helper()
+ self._tools = Tools()
+ self.host = (host or "").strip()
+ self.ports = ports
+ self._parent: Optional[QWidget] = None
+ self._logger.append(f"[Diagnostic] Initialized with host={self.host!r}, ports={self.ports!r}", channel="diagnostic", level="debug")
+
+ self._steps: list[DiagnosticStep] = []
+ self._finished_listeners: list[Callable[[bool], None]] = []
+
+ def add(
+ self,
+ name: str,
+ label: str,
+ icon: Optional[str],
+ func: Callable[[Callable[[str], None]], bool]
+ ) -> None:
+ self._steps.append(DiagnosticStep(name, label, icon, func))
+ self._logger.append(f"[Diagnostic] Added step: name={name}, label={label}, icon_provided={icon is not None}", channel="diagnostic", level="debug")
+
+ def on_finish(self, fn: Callable[[bool], None]) -> None:
+ """
+ Register a listener to be called when the diagnostic run finishes.
+
+ The listener will receive a single bool argument indicating overall success.
+ """
+ if callable(fn):
+ self._finished_listeners.append(fn)
+ self._logger.append(f"[Diagnostic] Added finished listener: {fn!r}", channel="diagnostic", level="debug")
+
+ def show(
+ self,
+ parent: Optional[QWidget] = None,
+ finished: Optional[Callable[[bool], None]] = None,
+ ) -> QDialog:
+ self._logger.append(f"[Diagnostic] show() called with parent type: {type(parent)}", channel="diagnostic", level="debug")
+ if parent is not None and isinstance(parent, QWidget):
+ self._parent = parent
+
+ dlg = DiagnosticDialog(self._steps, parent=self._parent)
+ self._logger.append(f"[Diagnostic] DiagnosticDialog created with {len(self._steps)} steps", channel="diagnostic", level="debug")
+
+ for fn in self._finished_listeners:
+ dlg.finished.connect(fn)
+
+ if finished is not None:
+ dlg.finished.connect(finished)
+
+ self._logger.append("[Diagnostic] Diagnostics dialog about to be executed modally", channel="diagnostic", level="debug")
+ dlg.exec_()
+ self._logger.append("[Diagnostic] Diagnostics dialog returned from exec_", channel="diagnostic", level="debug")
+ dlg.raise_()
+ dlg.activateWindow()
+ return dlg
+
+class DiagnosticDialog(QDialog):
+ finished = pyqtSignal(bool)
+
+ def __init__(self, steps: Iterable[DiagnosticStep], parent: Optional[QWidget] = None):
+ app = QApplication.instance()
+ if app is not None and hasattr(app, "logger"):
+ self._logger = app.logger
+ else:
+ self._logger = Log()
+
+ super().__init__(parent)
+
+ self._helper = Helper()
+ self._steps = list(steps)
+ self._logger.append(f"[DiagnosticDialog] Initialized with steps: {[step.name for step in self._steps]} (count={len(self._steps)})", channel="diagnostic", level="debug")
+
+ self.setWindowTitle("Diagnostic")
+ self.setObjectName("DiagnosticDialog")
+ self.setWindowFlags(
+ Qt.Dialog
+ | Qt.WindowTitleHint
+ | Qt.CustomizeWindowHint
+ | Qt.WindowCloseButtonHint
+ )
+ self.setMinimumSize(700, 420)
+
+ icons_path = self._helper.get_path("core/icons")
+ circle_path = self._helper.join(icons_path, "circle.svg")
+ spinner_path = self._helper.join(icons_path, "spinner.svg")
+ error_path = self._helper.join(icons_path, "error.svg")
+ success_path = self._helper.join(icons_path, "success.svg")
+
+ self._status_icons = {
+ "idle": QIcon(circle_path).pixmap(32, 32),
+ "running": QIcon(spinner_path).pixmap(32, 32),
+ "fail": QIcon(error_path).pixmap(32, 32),
+ "ok": QIcon(success_path).pixmap(32, 32),
+ "success": QIcon(success_path).pixmap(32, 32),
+ "error": QIcon(error_path).pixmap(32, 32),
+ }
+
+ self._step_icon_labels: dict[str, QLabel] = {}
+
+ top_layout = QHBoxLayout()
+ top_layout.setSpacing(24)
+ top_layout.setContentsMargins(16, 16, 16, 8)
+
+ for step in self._steps:
+ icon_label = SpinningIconLabel(spinner_path, size=32)
+ icon_label.setStyleSheet("padding: 0px; margin: 0px; border: none;")
+ icon_label.setFixedSize(32, 32)
+ icon_label.setAlignment(Qt.AlignCenter)
+ icon_label.setScaledContents(True)
+
+ # initial state = idle (static)
+ icon_label.setPixmap(self._status_icons["idle"])
+ icon_label.stop() # make sure timer isn't running
+
+ text_label = QLabel(step.label)
+ text_label.setAlignment(Qt.AlignCenter)
+
+ step_layout = QVBoxLayout()
+ step_layout.setSpacing(4)
+ step_layout.addWidget(icon_label, alignment=Qt.AlignCenter)
+ step_layout.addWidget(text_label, alignment=Qt.AlignCenter)
+ step_layout.addStretch(1)
+
+ container = QWidget()
+ container.setLayout(step_layout)
+ top_layout.addWidget(container, 1)
+
+ self._step_icon_labels[step.name] = icon_label
+
+ self.log = QTextEdit()
+ self.log.setReadOnly(True)
+
+ btns = QHBoxLayout()
+ btns.setContentsMargins(16, 8, 16, 16)
+ self.run_btn = QPushButton("Run Diagnostics")
+ self.run_btn.clicked.connect(self.start_diagnostic)
+ self.close_btn = QPushButton("Close")
+ self.close_btn.clicked.connect(self.close)
+ btns.addStretch(1)
+ btns.addWidget(self.run_btn)
+ if self._logger is not None:
+ self.log_btn = QPushButton("Open Log")
+ self.log_btn.clicked.connect(self._logger.show)
+ btns.addWidget(self.log_btn)
+ btns.addWidget(self.close_btn)
+
+ root = QVBoxLayout(self)
+ root.addLayout(top_layout)
+ root.addWidget(self.log)
+ root.addLayout(btns)
+
+ self._thr: Optional[DiagnosticThread] = None
+
+ def start_diagnostic(self):
+ self._logger.append(f"[DiagnosticDialog] Starting diagnostics with {len(self._steps)} steps...", channel="diagnostic", level="debug")
+ self.log.clear()
+
+ for label in self._step_icon_labels.values():
+ if isinstance(label, SpinningIconLabel):
+ label.stop()
+ label.setPixmap(self._status_icons["idle"])
+
+ self.run_btn.setEnabled(False)
+
+ self._thr = DiagnosticThread(self._steps, parent=self)
+ self._thr.log.connect(self._on_log)
+ self._thr.step_state.connect(self._on_step_state)
+ self._thr.finished.connect(self._on_finished)
+ self._thr.finished.connect(lambda _: self.run_btn.setEnabled(True))
+ self._thr.start()
+ self._logger.append("[DiagnosticDialog] DiagnosticThread created and started", channel="diagnostic", level="debug")
+
+ def _on_log(self, s: str) -> None:
+ self._logger.append(s, channel="diagnostic", level="debug")
+ self.log.append(s)
+
+ def _on_step_state(self, name: str, state: str) -> None:
+ self._logger.append(
+ f"[DiagnosticDialog] Step {name} state changed to {state}",
+ channel="diagnostic",
+ level="debug",
+ )
+
+ label = self._step_icon_labels.get(name)
+ if not label:
+ return
+
+ # We only created SpinningIconLabel instances, but be defensive:
+ if isinstance(label, SpinningIconLabel):
+ if state == "idle":
+ # QLabel behavior (static idle icon)
+ label.stop()
+ label.setPixmap(self._status_icons["idle"])
+
+ elif state == "running":
+ # SpinningIconLabel behavior (spinner icon + animation)
+ label.setPixmap(self._status_icons["running"])
+ label.start()
+
+ elif state == "ok":
+ # QLabel behavior (static success icon)
+ label.stop()
+ label.setPixmap(self._status_icons["ok"])
+
+ elif state == "fail":
+ # QLabel behavior (static error icon)
+ label.stop()
+ label.setPixmap(self._status_icons["fail"])
+
+ else:
+ # Unknown state -> default back to idle, no animation
+ self._logger.append(
+ f"[DiagnosticDialog] Unknown state '{state}' for step '{name}', defaulting to idle",
+ channel="diagnostic",
+ level="warning",
+ )
+ label.stop()
+ label.setPixmap(self._status_icons["idle"])
+
+ return
+
+ # Fallback for plain QLabel (just in case)
+ pix = self._status_icons.get(state)
+ if pix is not None:
+ label.setPixmap(pix)
+
+ def _on_finished(self, results: dict) -> None:
+ success = all(bool(v) for v in results.values()) if results else False
+ self._logger.append(f"[DiagnosticDialog] Diagnostics finished with results: {results}, success={success}", channel="diagnostic", level="debug")
+ self.finished.emit(success)
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/network/tools.py b/dist/macos/Replicator.app/Contents/Resources/core/network/tools.py
new file mode 100644
index 00000000..25bb9819
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/network/tools.py
@@ -0,0 +1,478 @@
+#!/usr/bin/env python3
+# src/core/network/tools.py
+
+import shutil
+import socket
+import re
+import subprocess
+from typing import Optional, List
+from PyQt5.QtWidgets import QApplication
+
+try:
+ from core.helper import Helper
+except ImportError:
+ from helper import Helper
+
+class Tools:
+
+ def __init__(
+ self,
+ helper: Optional[Helper] = None
+ ):
+
+ # Initialize QObject
+ super().__init__()
+
+ # --- auto-wire from QApplication if not provided ---
+ if helper is None:
+ app = QApplication.instance()
+ if app is None:
+ raise RuntimeError("Client must be created after QApplication/Application.")
+ # narrow the type for linters / IDEs
+ # no runtime import to avoid circular imports
+ helper = helper or app.helper # type: ignore[attr-defined]
+
+ # Helper
+ self._helper: Helper = helper
+
+ # OS type
+ self._os: str = self._helper.get_os()
+
+ def ping(self, host):
+ args = []
+ if self._os == "windows":
+ args = ["ping", "-n", "1", "-w", "1000"]
+ elif self._os == "linux":
+ args = [shutil.which("ping") or "ping", "-c", "1", "-W", "1"]
+ else: # macOS
+ args = ["/sbin/ping", "-c", "1", "-t", "1"]
+ return self._helper.run(args + [host])[0] == 0
+
+ def traceroute(self,host):
+ args = []
+ if self._os == "windows":
+ args = ["tracert", "-d", "-h", "30", "-w", "1000", host]
+ elif self._os == "macos":
+ args = ["/usr/sbin/traceroute", "-n", "-m", "30", "-w", "1", host]
+ else: # linux
+ args = [shutil.which("traceroute") or "traceroute", "-n", "-m", "30", "-w", "1", host]
+ rc, out = self._helper.run(args)
+ if rc != 0:
+ return None
+ hops = []
+ for line in out.splitlines():
+ line = line.strip()
+ if re.match(r"^\d+\s+", line):
+ parts = line.split()
+ if len(parts) >= 2:
+ hop_ip = parts[1]
+ hops.append(hop_ip)
+ return hops
+
+ def nslookup(self, host):
+ """
+ Perform a DNS lookup for `host`.
+
+ Returns:
+ - list[str]: All resolved IPv4 addresses (unique, in order of appearance),
+ excluding the DNS server IP (e.g. 127.0.0.53#53 after "Server:").
+ - False: If the lookup failed or no valid addresses were found.
+ """
+ # Build command
+ if self._os == "windows":
+ args = ["nslookup", host]
+ else:
+ args = [shutil.which("nslookup") or "nslookup", host]
+
+ rc, out = self._helper.run(args)
+ if rc != 0:
+ return False
+
+ ips: List[str] = []
+ prev_line = ""
+
+ # Typical Linux/systemd-resolved output looks like:
+ # Server: 127.0.0.53
+ # Address: 127.0.0.53#53
+ #
+ # We want to *skip* that "server" address and only keep the
+ # addresses from the answer section.
+ for line in out.splitlines():
+ stripped = line.strip()
+
+ m = re.search(r"Address:\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)", stripped)
+ if m:
+ ip = m.group(1)
+
+ # Skip the resolver/server IP line that immediately follows a "Server:" line
+ if prev_line.strip().lower().startswith("server:"):
+ prev_line = stripped
+ continue
+
+ if ip not in ips:
+ ips.append(ip)
+
+ prev_line = stripped
+
+ return ips if ips else False
+
+ def gateway(self):
+ # Windows: parse ipconfig output
+ if self._os == "windows":
+ rc, out = self._helper.run(["ipconfig"])
+ if rc != 0:
+ return None
+
+ # Typical line: "Default Gateway . . . . . . . . . : 192.168.1.1"
+ m = re.search(r"Default Gateway[ .:]*([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)", out)
+ if m:
+ return m.group(1)
+ return None
+
+ # Linux: use "ip route"
+ if self._os == "linux":
+ rc, out = self._helper.run(["ip", "route"])
+ if rc != 0:
+ return None
+
+ for line in out.splitlines():
+ line = line.strip()
+ # e.g. "default via 192.168.1.1 dev eth0 ..."
+ if line.startswith("default "):
+ m = re.search(r"default\s+via\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)", line)
+ if m:
+ return m.group(1)
+ return None
+
+ # macOS: use "route -n get default"
+ # self._os assumed "macos" for your helper
+ rc, out = self._helper.run(["route", "-n", "get", "default"])
+ if rc != 0:
+ return None
+
+ # Typical line: "gateway: 192.168.1.1"
+ m = re.search(r"gateway:\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)", out)
+ if m:
+ return m.group(1)
+ return None
+
+ def host(self):
+ try:
+ return socket.gethostname()
+ except Exception:
+ return None
+
+ def ip(self):
+ ips = []
+
+ # ---------------------------
+ # Windows
+ # ---------------------------
+ if self._os == "windows":
+ rc, out = self._helper.run(["ipconfig"])
+ if rc == 0:
+ # "IPv4 Address. . . . . . . . . . . : 192.168.1.24"
+ for line in out.splitlines():
+ m = re.search(r"IPv4 Address[.\s:]*([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)", line)
+ if m:
+ ip = m.group(1)
+ if not ip.startswith("127.") and not self.apipa(ip):
+ ips.append(ip)
+ return ips
+
+ # ---------------------------
+ # Linux
+ # ---------------------------
+ if self._os == "linux":
+ rc, out = self._helper.run([shutil.which("ip") or "ip", "addr"])
+ if rc == 0:
+ # Lines like: "inet 192.168.1.24/24 brd ... "
+ for line in out.splitlines():
+ line = line.strip()
+ if line.startswith("inet "):
+ m = re.search(r"inet\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)", line)
+ if m:
+ ip = m.group(1)
+ if not ip.startswith("127.") and not self.apipa(ip):
+ ips.append(ip)
+ return ips
+
+ # Fallback to ifconfig if ip fails
+ rc, out = self._helper.run([shutil.which("ifconfig") or "ifconfig", "-a"])
+ if rc == 0:
+ for line in out.splitlines():
+ line = line.strip()
+ # "inet 192.168.1.24 netmask 0xffffff00 ..."
+ if line.startswith("inet "):
+ m = re.search(r"inet\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)", line)
+ if m:
+ ip = m.group(1)
+ if not ip.startswith("127.") and not self.apipa(ip):
+ ips.append(ip)
+ return ips
+
+ # ---------------------------
+ # macOS
+ # ---------------------------
+ if self._os == "macos":
+ # macOS standard is ifconfig; there is usually no ip(8)
+ rc, out = self._helper.run([shutil.which("ifconfig") or "ifconfig"])
+ if rc == 0:
+ for line in out.splitlines():
+ line = line.strip()
+ # Lines with IPv4 usually look like:
+ # "inet 192.168.1.24 netmask 0xffffff00 broadcast 192.168.1.255"
+ if line.startswith("inet "):
+ m = re.search(r"inet\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)", line)
+ if m:
+ ip = m.group(1)
+ if not ip.startswith("127.") and not self.apipa(ip):
+ ips.append(ip)
+ return ips
+
+ # ---------------------------
+ # Fallback: use gethostbyname_ex
+ # ---------------------------
+ try:
+ hostname = socket.gethostname()
+ host_info = socket.gethostbyname_ex(hostname)
+ for ip in host_info[2]:
+ if not ip.startswith("127.") and not self.apipa(ip):
+ ips.append(ip)
+ except Exception:
+ pass
+
+ return ips
+
+ def mac(self):
+ macs = set()
+
+ def _norm(m: str) -> str:
+ # Normalize to aa:bb:cc:dd:ee:ff
+ m = m.strip().lower().replace('-', ':')
+ parts = m.split(':')
+ if len(parts) != 6:
+ return m
+ return ':'.join(p.zfill(2) for p in parts)
+
+ # ---------------------------
+ # Windows: use getmac
+ # ---------------------------
+ if self._os == "windows":
+ rc, out = self._helper.run(["getmac", "/v", "/fo", "csv"])
+ if rc == 0:
+ # Lines contain something like:
+ # "Connection Name","Network Adapter","Physical Address","Transport Name"
+ for line in out.splitlines():
+ # Look for MAC patterns
+ for m in re.findall(r'([0-9A-Fa-f]{2}([-:])[0-9A-Fa-f]{2}(?:\2[0-9A-Fa-f]{2}){4})', line):
+ macs.add(_norm(m[0]))
+ return sorted(macs)
+
+ # Fallback: ipconfig /all
+ rc, out = self._helper.run(["ipconfig", "/all"])
+ if rc == 0:
+ for line in out.splitlines():
+ # "Physical Address. . . . . . . . . : 00-11-22-33-44-55"
+ mm = re.search(r'Physical Address[.\s:]*([0-9A-Fa-f\-:]{17})', line)
+ if mm:
+ macs.add(_norm(mm.group(1)))
+ return sorted(macs)
+
+ return []
+
+ # ---------------------------
+ # Linux / macOS: ip link
+ # ---------------------------
+ rc, out = self._helper.run([shutil.which("ip") or "ip", "link"])
+ if rc == 0:
+ # Lines with "link/ether 00:11:22:33:44:55"
+ for line in out.splitlines():
+ mm = re.search(r'link/ether\s+([0-9A-Fa-f:]{17})', line)
+ if mm:
+ macs.add(_norm(mm.group(1)))
+ if macs:
+ return sorted(macs)
+
+ # Fallback: ifconfig
+ rc, out = self._helper.run([shutil.which("ifconfig") or "ifconfig", "-a"])
+ if rc == 0:
+ for line in out.splitlines():
+ # Look for MAC in common formats
+ for m in re.findall(r'([0-9A-Fa-f]{2}([-:])[0-9A-Fa-f]{2}(?:\2[0-9A-Fa-f]{2}){4})', line):
+ macs.add(_norm(m[0]))
+
+ return sorted(macs)
+
+ def wan(self):
+ # IPv4 regex
+ ipv4_re = re.compile(r'\b([0-9]{1,3}(?:\.[0-9]{1,3}){3})\b')
+
+ # ---------------------------
+ # Non-Windows: use dig
+ # ---------------------------
+ if self._os in ("linux", "macos"):
+ dig_path = shutil.which("dig") or "dig"
+ rc, out = self._helper.run(
+ [dig_path, "+short", "myip.opendns.com", "@resolver1.opendns.com"]
+ )
+ if rc == 0:
+ for line in out.splitlines():
+ m = ipv4_re.search(line)
+ if m:
+ return m.group(1)
+ # If dig is missing or fails, fall through to nslookup below.
+
+ # ---------------------------
+ # Windows or fallback: nslookup
+ # ---------------------------
+ rc, out = self._helper.run([
+ shutil.which("nslookup") or "nslookup",
+ "myip.opendns.com",
+ "resolver1.opendns.com",
+ ])
+ if rc == 0:
+ # Typical output has several "Address:" lines; the last IPv4 is usually the WAN IP.
+ candidates = []
+ for line in out.splitlines():
+ m = ipv4_re.search(line)
+ if m:
+ ip = m.group(1)
+ # Skip the resolver's own IP (208.67.x.x) if you want only your WAN IP
+ if not ip.startswith("208.67."):
+ candidates.append(ip)
+ if candidates:
+ return candidates[-1]
+
+ return None
+
+ def apipa(self, ip):
+ if not isinstance(ip, str):
+ return False
+
+ parts = ip.split(".")
+ if len(parts) != 4:
+ return False
+
+ try:
+ octets = [int(p) for p in parts]
+ except ValueError:
+ return False
+
+ # 169.254.x.x
+ return (octets[0] == 169 and octets[1] == 254)
+
+ def nmap(self, host, ports):
+ # Normalize `ports` into a list of ints
+ port_list = []
+
+ if isinstance(ports, int):
+ port_list = [ports]
+
+ elif isinstance(ports, (list, tuple)):
+ for p in ports:
+ try:
+ port_list.append(int(p))
+ except Exception:
+ pass
+
+ elif isinstance(ports, str) and "-" in ports:
+ # range like "20-30"
+ try:
+ start, end = ports.split("-", 1)
+ start = int(start)
+ end = int(end)
+ port_list = list(range(start, end + 1))
+ except Exception:
+ return {}
+
+ else:
+ # last fallback
+ try:
+ port_list = [int(ports)]
+ except Exception:
+ return {}
+
+ # No valid ports
+ if not port_list:
+ return {}
+
+ results = {}
+
+ # -------------------------------------------------------
+ # Try using nmap (fast, reliable, bulk scan)
+ # -------------------------------------------------------
+ nmap_bin = shutil.which("nmap")
+ if nmap_bin:
+ # Build port string: "22,80,443"
+ port_str = ",".join(str(p) for p in port_list)
+
+ args = [
+ nmap_bin,
+ "-p", port_str,
+ "-T4",
+ "-Pn",
+ host
+ ]
+
+ rc, out = self._helper.run(args)
+ if rc == 0:
+ # Parse output for each port
+ for port in port_list:
+ pattern = rf"^{port}/\w+\s+open\b"
+ is_open = False
+ for line in out.splitlines():
+ if re.search(pattern, line.strip(), re.IGNORECASE):
+ is_open = True
+ break
+ results[str(port)] = is_open
+
+ return results
+
+ # -------------------------------------------------------
+ # Fallback: try TCP connect for each port
+ # -------------------------------------------------------
+ import socket
+ for port in port_list:
+ try:
+ with socket.create_connection((host, port), timeout=1.0):
+ results[str(port)] = True
+ except Exception:
+ results[str(port)] = False
+
+ return results
+
+ def scan_ap(self) -> List[str]:
+ ssids: List[str] = []
+
+ # --- Prefer nmcli if available ---
+ if shutil.which("nmcli"):
+ try:
+ out = subprocess.check_output(
+ ["nmcli", "-t", "-f", "SSID", "dev", "wifi"],
+ stderr=subprocess.DEVNULL,
+ text=True,
+ )
+ for line in out.splitlines():
+ s = line.strip()
+ if s and s not in ssids:
+ ssids.append(s)
+ except Exception as e:
+ print(f"[Tools] nmcli wifi scan failed: {e}")
+
+ # --- Fallback to iwlist if no SSIDs yet ---
+ if not ssids and shutil.which("iwlist"):
+ try:
+ out = subprocess.check_output(
+ ["iwlist", "scan"],
+ stderr=subprocess.DEVNULL,
+ text=True,
+ )
+ for m in re.finditer(r'ESSID:"([^"]*)"', out):
+ s = m.group(1).strip()
+ if s and s not in ssids:
+ ssids.append(s)
+ except Exception as e:
+ print(f"[Tools] iwlist wifi scan failed: {e}")
+
+ return ssids
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/service.py b/dist/macos/Replicator.app/Contents/Resources/core/service.py
new file mode 100644
index 00000000..ab1677a9
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/service.py
@@ -0,0 +1,1674 @@
+#!/usr/bin/env python3
+# src/core/service.py
+
+from __future__ import annotations
+
+import os
+import sys
+import time
+import signal
+import shutil
+import subprocess
+import platform
+import ctypes
+import json
+from pathlib import Path
+from dataclasses import dataclass
+from typing import Any, Callable, Optional
+from PyQt5.QtCore import QTimer
+# PyQt5 widgets
+from PyQt5.QtWidgets import (
+ QApplication,
+ QDialog,
+ QTabWidget,
+ QGroupBox,
+ QVBoxLayout,
+ QHBoxLayout,
+ QFormLayout,
+ QLabel,
+ QPushButton,
+ QPlainTextEdit,
+ QMessageBox,
+ QWidget,
+)
+
+# Optional UI helpers (icons/buttons)
+try:
+ from .ui import Form # type: ignore
+except Exception: # pragma: no cover
+ Form = None # type: ignore
+
+
+@dataclass
+class _Task:
+ description: str
+ callable: Callable[..., Any]
+ interval: float = 60.0
+ last_run: float = 0.0
+
+
+class Service:
+ """Service module for corePY.
+
+ This class is **not** an application runner like CommandLine / Application.
+ It is a module that:
+ - Adds service-management commands to the CLI (start/stop/restart/install/uninstall)
+ - Allows apps to register tasks to be executed by the service loop
+
+ Usage pattern (from an app/handler):
+
+ def cli(self, cli):
+ cli.add("run", "Run jobs", self.run)
+ cli.service.add("run", "Run jobs", self.run, interval=60)
+
+ Then:
+ python main.py --start
+
+ runs all registered service tasks in a loop.
+ """
+
+ def __init__(self, logger=None, configuration=None):
+ self._tasks: dict[str, _Task] = {}
+ self._stop_requested = False
+ self._loop_sleep = 1.0
+ self._pidfile_path: Optional[str] = None
+
+ # Optional integrations (passed by CommandLine)
+ self._logger = logger
+ self._configuration = configuration
+
+ # Service configuration defaults
+ if self._configuration is not None:
+ # How often the main loop wakes up to check due tasks (seconds)
+ self._configuration.add("service.loopSleep", 1, "number", label="Service loop sleep (s)", min=1, max=60)
+ # Default interval used when registering tasks (seconds)
+ self._configuration.add("service.defaultInterval", 3600, "number", label="Service default task interval (s)", min=1, max=86400)
+ # Reverse-DNS prefix used to name/register the service (e.g. com.laswitchtech)
+ # The final label becomes: . (lowercased and sanitized)
+ self._configuration.add("service.domain", "com.corepy", "text", label="Service domain (reverse DNS)")
+ # Persist any new defaults
+ self._configuration.save()
+
+ # Initial visibility update
+ self._refresh_action_visibility()
+
+ # Keep visibility in sync when configuration is saved/changed
+ try:
+ self._configuration.configChanged.connect(lambda _cfg: self._refresh_action_visibility())
+ except Exception:
+ pass
+ def _service_domain(self) -> str:
+ """Return reverse-DNS domain used for service labels.
+
+ Prefer configuration key `service.domain` when available; fall back to
+ an environment override COREPY_SERVICE_DOMAIN; then to 'com.corepy'.
+ """
+ # 1) Configuration
+ try:
+ if self._configuration is not None:
+ v = (self._configuration.get("service.domain", "") or "").strip()
+ if v:
+ return v
+ except Exception:
+ pass
+
+ # 2) Environment override
+ v = (os.environ.get("COREPY_SERVICE_DOMAIN") or "").strip()
+ if v:
+ return v
+
+ return "com.corepy"
+ def _refresh_action_visibility(self) -> None:
+ """Show/hide configuration action buttons based on install/running state."""
+ if self._configuration is None:
+ return
+
+ installed = self._is_installed()
+ running = self._is_service_active() if installed else self._is_running()
+
+ # Start/Stop are mutually exclusive when installed; when not installed, start runs in-process.
+ self._configuration.visibility("service.actions.start", not running)
+ self._configuration.visibility("service.actions.stop", running)
+
+ # Restart only makes sense when running
+ self._configuration.visibility("service.actions.restart", running)
+
+ # Install/Uninstall are mutually exclusive
+ self._configuration.visibility("service.actions.install", not installed)
+ self._configuration.visibility("service.actions.uninstall", installed)
+
+ # ------------------------------------------------------------------
+ # CLI integration
+ # ------------------------------------------------------------------
+
+ def cli(self, cli=None) -> None:
+ """Register service management commands into CommandLine."""
+ if cli is None:
+ return
+
+ cli.add("service.status", "Show service status.", self.status)
+ cli.add("service.start", "Start the service loop.", self.start)
+ cli.add("service.stop", "Stop the running service.", self.stop)
+ cli.add("service.restart", "Restart the running service.", self.restart)
+ cli.add("service.install", "Install as a python service.", self.install)
+ cli.add("service.uninstall", "Uninstall the python service.", self.uninstall)
+
+ # ------------------------------------------------------------------
+ # Task registry
+ # ------------------------------------------------------------------
+
+ def add(
+ self,
+ command: str,
+ description: str = "",
+ callable: Optional[Callable[..., Any]] = None,
+ *,
+ interval: Optional[float] = None,
+ ) -> None:
+ """Register a command to run as part of the service loop.
+
+ Notes:
+ - The command name is informational here (used for logs / debugging).
+ - The callable is invoked with **no arguments** by the service loop.
+ - Use closures/lambdas if you need to bind context.
+ """
+ if callable is None:
+ raise ValueError("Service.add() requires a callable")
+
+ # Keep the name consistent with CLI commands (no leading -- expected here)
+ normalized = command[2:] if command.startswith("--") else command
+
+ # Resolve effective interval from configuration (if present)
+ # - interval is None: use configured service.defaultInterval (default 3600)
+ # - interval == 0: run every tick
+ # - interval > 0: use that value (and allow override via per-task config)
+ if interval is None:
+ effective_interval = 0.0
+ else:
+ effective_interval = float(interval)
+
+ if self._configuration is not None:
+ key = f"service.intervals.{normalized}"
+
+ # Schema default for this task's interval
+ if interval is None:
+ schema_default = int(self._configuration.get("service.defaultInterval", 3600) or 3600)
+ else:
+ schema_default = int(effective_interval)
+
+ self._configuration.add(
+ key,
+ schema_default,
+ "spin",
+ label=f"Service interval: {normalized} (s)",
+ min=0,
+ max=86400,
+ )
+
+ try:
+ configured = self._configuration.get(key, schema_default)
+ effective_interval = float(configured) if configured is not None else float(schema_default)
+ except Exception:
+ effective_interval = float(schema_default)
+
+ self._configuration.save()
+ else:
+ # No configuration system; if interval is None, fall back to 3600
+ if interval is None:
+ effective_interval = 3600.0
+
+ self._tasks[normalized] = _Task(
+ description=description,
+ callable=callable,
+ interval=effective_interval,
+ last_run=0.0,
+ )
+
+ def remove(self, command: str) -> None:
+ normalized = command[2:] if command.startswith("--") else command
+ if normalized in self._tasks:
+ del self._tasks[normalized]
+
+ def clear(self) -> None:
+ self._tasks.clear()
+
+ def set_loop_sleep(self, seconds: float) -> None:
+ """How often to wake up to check due tasks."""
+ self._loop_sleep = max(0.1, float(seconds))
+
+ def _log(self, message: str, level: str = "info") -> None:
+ """Log to core Log module if available, else print."""
+ try:
+ if self._logger is not None and hasattr(self._logger, "append"):
+ self._logger.append(message, channel="service", level=level)
+ return
+ except Exception:
+ pass
+
+ # Fallback to console
+ if level == "error":
+ print(message, file=sys.stderr)
+ else:
+ print(message)
+
+ # ------------------------------------------------------------------
+ # Service control
+ # ------------------------------------------------------------------
+
+ def start(self) -> None:
+ """Enter the service loop in the current process."""
+ self._stop_requested = False
+
+ # If installed, either:
+ # - We are a *manager* request (user invoked --start): delegate to OS manager, OR
+ # - We are the *service process* (OS invoked --start): run the loop in-process.
+ if self._is_installed() and not self._running_under_service_manager():
+ self._service_manager_start()
+ self._refresh_action_visibility()
+ return
+
+ # Apply configured loop sleep if available
+ if self._configuration is not None:
+ try:
+ self._loop_sleep = float(self._configuration.get("service.loopSleep", 1) or 1)
+ except Exception:
+ self._loop_sleep = 1.0
+
+ self._install_signal_handlers()
+
+ pidfile = self._pidfile()
+ if self._is_running():
+ self._log(f"Service already running (pidfile: {pidfile}).", level="info")
+ return
+
+ os.makedirs(os.path.dirname(pidfile), exist_ok=True)
+ with open(pidfile, "w", encoding="utf-8") as f:
+ f.write(str(os.getpid()))
+
+ self._log(f"Service started (PID {os.getpid()}).", level="info")
+
+ try:
+ self._service_loop()
+ finally:
+ self._cleanup_pidfile()
+ self._log("Service stopped.", level="info")
+
+ def stop(self) -> None:
+ """Stop a running service by signaling the PID from the pidfile."""
+ # If installed, delegate to platform service manager.
+ if self._is_installed():
+ self._service_manager_stop()
+ self._refresh_action_visibility()
+ return
+
+ pid = self._read_pidfile()
+ if not pid:
+ self._log("Service is not running (no pidfile).", level="info")
+ return
+
+ if not self._pid_exists(pid):
+ self._log("Service pidfile found but process is not running. Cleaning up.", level="warning")
+ self._cleanup_pidfile()
+ return
+
+ try:
+ os.kill(pid, signal.SIGTERM)
+ self._log(f"Stop signal sent to PID {pid}.", level="info")
+ except PermissionError:
+ self._log(f"Permission denied sending SIGTERM to PID {pid}.", level="error")
+ except ProcessLookupError:
+ self._log(f"Process {pid} not found. Cleaning up pidfile.", level="error")
+ self._cleanup_pidfile()
+
+ def restart(self) -> None:
+ # If installed, delegate to platform service manager.
+ if self._is_installed():
+ self._service_manager_restart()
+ self._refresh_action_visibility()
+ return
+
+ # Otherwise run in-process restart
+ self.stop()
+ time.sleep(0.5)
+ self.start()
+
+ def status(self) -> None:
+ """Show current service status."""
+ pidfile = self._pidfile()
+ pid = self._read_pidfile()
+
+ installed = self._is_installed()
+ running = self._is_service_active() if installed else bool(pid and self._pid_exists(pid))
+
+ # Build a readable status
+ lines = []
+ lines.append(f"Service: {self._app_name()}")
+ lines.append(f"PID file: {pidfile}")
+ lines.append(f"Installed: {'yes' if installed else 'no'}")
+ lines.append(f"Running: {'yes' if running else 'no'}")
+ if pid:
+ lines.append(f"PID: {pid}")
+ lines.append(f"Tasks registered: {len(self._tasks)}")
+
+ # Show intervals (effective)
+ if self._configuration is not None and self._tasks:
+ lines.append("Task intervals (s):")
+ for name in sorted(self._tasks.keys()):
+ key = f"service.intervals.{name}"
+ interval = self._configuration.get(key, self._tasks[name].interval)
+ lines.append(f" - {name}: {interval}")
+
+ msg = "\n".join(lines)
+ print(msg)
+ self._log(msg, level="info")
+
+
+ # ------------------------------------------------------------------
+ # UI helper
+ # ------------------------------------------------------------------
+
+ def open_manager_dialog(self, parent=None) -> None:
+ """Open a small GUI dialog to manage the service (install/start/stop + logs).
+
+ This is intentionally optional: apps can call it when running with a GUI.
+ """
+ try:
+ dlg = ServiceManagerDialog(self, parent=parent)
+ dlg.exec_()
+ except Exception as e:
+ # If called from a non-GUI context, fail gracefully.
+ self._log(f"Failed to open service manager dialog: {e}", level="error")
+
+ def request_stop(self) -> None:
+ """Request the current service loop to stop (used by signal handlers)."""
+ self._stop_requested = True
+
+ # ------------------------------------------------------------------
+ # Scheduler loop
+ # ------------------------------------------------------------------
+
+ def _service_loop(self) -> None:
+ if not self._tasks:
+ self._log("Warning: no tasks registered for service mode.", level="warning")
+
+ while not self._stop_requested:
+ now = time.time()
+
+ for name, task in list(self._tasks.items()):
+ # interval == 0 means "run every tick"
+ due = (task.interval <= 0) or (now - task.last_run >= task.interval)
+ if not due:
+ continue
+
+ try:
+ task.callable()
+ task.last_run = now
+ except Exception as e:
+ # Keep the service alive; print error.
+ self._log(f"Task {name} failed: {e}", level="error")
+
+ time.sleep(self._loop_sleep)
+
+ # ------------------------------------------------------------------
+ # systemd integration (Linux only)
+ # ------------------------------------------------------------------
+
+ def install(self) -> None:
+
+ if sys.platform.startswith("win"):
+ self._install_windows_service()
+ elif sys.platform == "darwin":
+ self._install_macos_launchd()
+ else:
+ self._install_systemd()
+
+ self._refresh_action_visibility()
+
+ def uninstall(self) -> None:
+
+ if sys.platform.startswith("win"):
+ self._uninstall_windows_service()
+ elif sys.platform == "darwin":
+ self._uninstall_macos_launchd()
+ else:
+ self._uninstall_systemd()
+
+ self._refresh_action_visibility()
+
+ def _install_systemd(self) -> None:
+ """Install a systemd unit that runs ` --start`."""
+ if shutil.which("systemctl") is None:
+ raise RuntimeError("systemctl not found; systemd does not appear available")
+
+ service_name = f"{self._app_name()}.service"
+ unit_path = os.path.join("/etc/systemd/system", service_name)
+
+ program_start, argv_start, workdir = self._service_command("start")
+ program_stop, argv_stop, _ = self._service_command("stop")
+
+ unit = self._systemd_unit(
+ service_name=service_name,
+ python_exe=program_start,
+ entrypoint=" ".join(argv_start),
+ working_dir=workdir,
+ pidfile=self._pidfile(),
+ execstop_program=program_stop,
+ execstop_args=" ".join(argv_stop),
+ )
+
+ with open(unit_path, "w", encoding="utf-8") as f:
+ f.write(unit)
+
+ subprocess.run(["systemctl", "daemon-reload"], check=False)
+ subprocess.run(["systemctl", "enable", "--now", service_name], check=False)
+
+ print(f"Installed and started: {unit_path}")
+
+ def _uninstall_systemd(self) -> None:
+ if shutil.which("systemctl") is None:
+ raise RuntimeError("systemctl not found; systemd does not appear available")
+
+ service_name = f"{self._app_name()}.service"
+ unit_path = os.path.join("/etc/systemd/system", service_name)
+
+ subprocess.run(["systemctl", "disable", "--now", service_name], check=False)
+ if os.path.exists(unit_path):
+ os.remove(unit_path)
+ subprocess.run(["systemctl", "daemon-reload"], check=False)
+
+ print(f"Uninstalled: {unit_path}")
+
+ def _systemd_unit(
+ self,
+ *,
+ service_name: str,
+ python_exe: str,
+ entrypoint: str,
+ working_dir: str,
+ pidfile: str,
+ execstop_program: str,
+ execstop_args: str,
+ ) -> str:
+ app_name = self._app_name()
+ return (
+ "[Unit]\n"
+ f"Description={app_name}\n"
+ "After=network.target\n\n"
+ "[Service]\n"
+ "Type=simple\n"
+ "Environment=COREPY_RUN_AS_SERVICE=1\n"
+ f"Environment=COREPY_SERVICE_LABEL={self._service_label()}\n"
+ f"WorkingDirectory={working_dir}\n"
+ f"ExecStart={python_exe} {entrypoint}\n"
+ f"ExecStop={execstop_program} {execstop_args}\n"
+ "Restart=on-failure\n"
+ "RestartSec=2\n"
+ f"PIDFile={pidfile}\n"
+ "\n"
+ "[Install]\n"
+ "WantedBy=multi-user.target\n"
+ )
+ def _running_under_service_manager(self) -> bool:
+ """Return True if this process should run the service loop.
+
+ When installed, `start()` is used both to *request* the service manager to
+ start the job and as the entrypoint that the service manager executes.
+
+ We differentiate the two cases using an explicit env flag set by the
+ service definitions (launchd/systemd/Windows), plus a couple of platform
+ hints as fallback.
+ """
+ # Explicit flag (preferred)
+ flag = (os.environ.get("COREPY_RUN_AS_SERVICE") or "").strip().lower()
+ if flag in ("1", "true", "yes", "on"):
+ return True
+
+ # Fallback hints
+ if sys.platform == "darwin":
+ # launchd commonly sets this for jobs
+ xpc = (os.environ.get("XPC_SERVICE_NAME") or "").strip()
+ if xpc and xpc == self._service_label():
+ return True
+
+ if not sys.platform.startswith("win"):
+ # systemd sets INVOCATION_ID for services
+ if os.environ.get("INVOCATION_ID"):
+ return True
+
+ return False
+
+ # ------------------------------------------------------------------
+ # Internals
+ # ------------------------------------------------------------------
+
+ def _app_name(self) -> str:
+ # Try to read from Qt application if present, else fallback.
+ try:
+ inst = QApplication.instance()
+ if inst and inst.applicationName():
+ return inst.applicationName()
+ except Exception:
+ pass
+ return "corepy"
+
+ def _pidfile(self) -> str:
+ if self._pidfile_path:
+ return self._pidfile_path
+ self._pidfile_path = self._default_pidfile_path()
+ return self._pidfile_path
+
+ def _default_pidfile_path(self) -> str:
+ name = self._app_name() or "corepy"
+ candidate = os.path.join("/run", f"{name}.pid")
+ try:
+ os.makedirs(os.path.dirname(candidate), exist_ok=True)
+ if os.access(os.path.dirname(candidate), os.W_OK):
+ return candidate
+ except Exception:
+ pass
+ return os.path.join("/tmp", f"{name}.pid")
+
+ def _read_pidfile(self) -> Optional[int]:
+ try:
+ with open(self._pidfile(), "r", encoding="utf-8") as f:
+ raw = f.read().strip()
+ return int(raw) if raw else None
+ except Exception:
+ return None
+
+ def _cleanup_pidfile(self) -> None:
+ try:
+ path = self._pidfile()
+ if os.path.exists(path):
+ os.remove(path)
+ except Exception:
+ pass
+
+ def _pid_exists(self, pid: int) -> bool:
+ if pid <= 0:
+ return False
+ try:
+ os.kill(pid, 0)
+ return True
+ except Exception:
+ return False
+
+ def _is_running(self) -> bool:
+ pid = self._read_pidfile()
+ return bool(pid and self._pid_exists(pid))
+
+ def _install_signal_handlers(self) -> None:
+ def _handler(_sig, _frame):
+ self.request_stop()
+
+ try:
+ signal.signal(signal.SIGTERM, _handler)
+ signal.signal(signal.SIGINT, _handler)
+ except Exception:
+ pass
+
+
+ # ------------------------------------------------------------------
+ # Windows elevation helpers
+ # ------------------------------------------------------------------
+
+ def _windows_is_admin(self) -> bool:
+ """Return True if the current process has admin rights on Windows."""
+ if not sys.platform.startswith("win"):
+ return False
+ try:
+ return bool(ctypes.windll.shell32.IsUserAnAdmin())
+ except Exception:
+ return False
+
+ def _windows_relaunch_elevated(self, argv: list[str]) -> bool:
+ """Relaunch the current executable with UAC elevation.
+
+ Returns True if the elevation prompt was successfully triggered.
+ """
+ if not sys.platform.startswith("win"):
+ return False
+
+ # Avoid infinite loops if something goes wrong.
+ if (os.environ.get("COREPY_ELEVATED_RELAUNCH") or "").strip() == "1":
+ return False
+
+ try:
+ # Build argument string for ShellExecuteW.
+ # Use subprocess.list2cmdline to quote correctly for Windows.
+ args = list(argv[1:]) + ["--elevated-relaunch"]
+ params = subprocess.list2cmdline(args)
+
+ # Pass a marker to the elevated process.
+ # We cannot reliably set environment variables via ShellExecute,
+ # so we append an internal flag and also set an env var for same-process codepaths.
+ os.environ["COREPY_ELEVATED_RELAUNCH"] = "1"
+
+ # 1 = SW_SHOWNORMAL
+ rc = ctypes.windll.shell32.ShellExecuteW(None, "runas", argv[0], params, None, 1)
+ return int(rc) > 32
+ except Exception:
+ return False
+
+ # ------------------------------------------------------------------
+ # Cross-platform service manager helpers
+ # ------------------------------------------------------------------
+
+ def _run_subprocess(self, args: list[str], **kwargs) -> subprocess.CompletedProcess:
+ if sys.platform.startswith("win"):
+ try:
+ kwargs.setdefault("creationflags", subprocess.CREATE_NO_WINDOW)
+ except Exception:
+ pass
+ return subprocess.run(args, **kwargs)
+
+ def _service_label(self) -> str:
+ """Stable label used for OS service registration.
+
+ - macOS launchd prefers reverse-DNS labels.
+ - Windows 'sc' service names and systemd unit names also benefit from stability.
+
+ Result: . (lowercased and sanitized)
+ """
+ domain = (self._service_domain() or "com.corepy").strip().strip(".")
+ app = (self._app_name() or "corepy").strip()
+
+ base = f"{domain}.{app}" if domain else app
+ base = base.lower()
+
+ # Keep only characters typically safe across platforms; replace others with '-'
+ safe = "".join(c if c.isalnum() or c in ("-", "_", ".") else "-" for c in base)
+ # Avoid accidental leading/trailing dots
+ safe = safe.strip(".")
+ return safe or "com.corepy.corepy"
+
+ def _entry_command(self) -> tuple[str, str, str]:
+ """Return (python_exe, entrypoint, working_dir) for service registration.
+
+ Notes:
+ - When `sys.argv[0]` is a relative path and the current working directory
+ is already inside `src/`, it's easy to end up with paths like `src/src/main.py`.
+ - launchd will happily try to execute that path, but the process will fail.
+
+ This helper resolves the path and applies a small normalization pass.
+ """
+ python_exe = sys.executable
+
+ # Resolve argv[0] robustly (handles relative paths)
+ try:
+ p = Path(sys.argv[0]).expanduser()
+ entry_path = (p if p.is_absolute() else (Path.cwd() / p)).resolve()
+ except Exception:
+ entry_path = Path(os.path.abspath(sys.argv[0]))
+
+ def _dedupe_consecutive(parts: list[str]) -> list[str]:
+ out: list[str] = []
+ for seg in parts:
+ if out and out[-1] == seg:
+ continue
+ out.append(seg)
+ return out
+
+ # If we ended up with duplicated segments like .../src/src/..., collapse them.
+ parts = list(entry_path.parts)
+ deduped = Path(*_dedupe_consecutive(parts))
+
+ # Prefer the deduped version if it exists.
+ if deduped.exists():
+ entry_path = deduped
+ else:
+ # Common case: /.../src/src/main.py -> /.../src/main.py
+ # If the path contains two consecutive 'src' segments, drop one.
+ if "src" in parts:
+ try:
+ new_parts: list[str] = []
+ i = 0
+ while i < len(parts):
+ if i + 1 < len(parts) and parts[i] == parts[i + 1]:
+ # Drop the duplicate segment
+ new_parts.append(parts[i])
+ i += 2
+ continue
+ new_parts.append(parts[i])
+ i += 1
+ candidate = Path(*new_parts)
+ if candidate.exists():
+ entry_path = candidate
+ except Exception:
+ pass
+
+ entry = str(entry_path)
+ workdir = str(entry_path.parent)
+ return python_exe, entry, workdir
+
+ def _is_installed(self) -> bool:
+ """Best-effort check whether this app is installed as a service."""
+ try:
+ if sys.platform.startswith("win"):
+ name = self._service_label()
+ # sc query returns non-zero if missing
+ r = self._run_subprocess(["sc", "query", name], capture_output=True, text=True)
+ return r.returncode == 0
+ if sys.platform == "darwin":
+ return self._macos_plist_path().exists()
+ # linux
+ if shutil.which("systemctl") is None:
+ return False
+ service_name = f"{self._service_label()}.service"
+ r = subprocess.run(["systemctl", "is-enabled", service_name], capture_output=True, text=True)
+ return r.returncode == 0
+ except Exception:
+ return False
+
+ def _is_service_active(self) -> bool:
+ """Check if the installed service is running (best-effort)."""
+ try:
+ if sys.platform.startswith("win"):
+ name = self._service_label()
+ r = self._run_subprocess(["sc", "query", name], capture_output=True, text=True)
+ if r.returncode != 0:
+ return False
+ out = (r.stdout or "") + (r.stderr or "")
+ return "RUNNING" in out.upper()
+ if sys.platform == "darwin":
+ label = self._service_label()
+ r = subprocess.run(["launchctl", "print", f"gui/{os.getuid()}/{label}"], capture_output=True, text=True)
+ if r.returncode != 0:
+ return False
+ out = (r.stdout or "") + (r.stderr or "")
+ o = out.lower()
+ # Typical indicators in `launchctl print` output
+ if "job state = running" in o:
+ return True
+ if "state = running" in o:
+ return True
+ # If explicitly exited, consider not running
+ if "job state = exited" in o:
+ return False
+ # Fallback: loaded but ambiguous => treat as active
+ return True
+ # linux
+ if shutil.which("systemctl") is None:
+ return False
+ service_name = f"{self._service_label()}.service"
+ r = subprocess.run(["systemctl", "is-active", service_name], capture_output=True, text=True)
+ return r.returncode == 0
+ except Exception:
+ return False
+
+ def _service_manager_start(self) -> None:
+ label = self._service_label()
+ try:
+ if sys.platform.startswith("win"):
+ # Prefer NSSM if bundled
+ if self._windows_has_nssm():
+ self._windows_nssm_run(["start", label])
+ else:
+ self._run_subprocess(["sc", "start", label], check=False)
+ self._log(f"Requested start for Windows service: {label}")
+ return
+ if sys.platform == "darwin":
+ plist = self._macos_plist_path()
+ domain = f"gui/{os.getuid()}"
+ target = f"{domain}/{label}"
+
+ # If it's already loaded, bootstrap may fail with error 5.
+ already_loaded = False
+ try:
+ r = subprocess.run(["launchctl", "print", target], capture_output=True, text=True)
+ already_loaded = (r.returncode == 0)
+ except Exception:
+ already_loaded = False
+
+ if plist.exists() and not already_loaded:
+ subprocess.run(["launchctl", "bootstrap", domain, str(plist)], check=False)
+
+ # Enable + kickstart regardless (these are safe even if already loaded)
+ subprocess.run(["launchctl", "enable", target], check=False)
+ subprocess.run(["launchctl", "kickstart", "-k", target], check=False)
+
+ self._log(f"Requested start for launchd agent: {label}")
+ return
+ # linux
+ service_name = f"{label}.service"
+ subprocess.run(["systemctl", "start", service_name], check=False)
+ self._log(f"Requested start for systemd service: {service_name}")
+ except Exception as e:
+ self._log(f"Failed to start service via manager: {e}", level="error")
+
+ def _service_manager_stop(self) -> None:
+ label = self._service_label()
+ try:
+ if sys.platform.startswith("win"):
+ # Prefer NSSM if bundled
+ if self._windows_has_nssm():
+ self._windows_nssm_run(["stop", label])
+ else:
+ self._run_subprocess(["sc", "stop", label], check=False)
+ self._log(f"Requested stop for Windows service: {label}")
+ return
+ if sys.platform == "darwin":
+ subprocess.run(["launchctl", "bootout", f"gui/{os.getuid()}/{label}"], check=False)
+ self._log(f"Requested stop for launchd agent: {label}")
+ return
+ service_name = f"{label}.service"
+ subprocess.run(["systemctl", "stop", service_name], check=False)
+ self._log(f"Requested stop for systemd service: {service_name}")
+ except Exception as e:
+ self._log(f"Failed to stop service via manager: {e}", level="error")
+
+ def _service_manager_restart(self) -> None:
+ label = self._service_label()
+ try:
+ if sys.platform.startswith("win"):
+ if self._windows_has_nssm():
+ self._windows_nssm_run(["restart", label])
+ else:
+ self._run_subprocess(["sc", "stop", label], check=False)
+ time.sleep(0.5)
+ self._run_subprocess(["sc", "start", label], check=False)
+ self._log(f"Requested restart for Windows service: {label}")
+ return
+ if sys.platform == "darwin":
+ subprocess.run(["launchctl", "kickstart", "-k", f"gui/{os.getuid()}/{label}"], check=False)
+ self._log(f"Requested restart for launchd agent: {label}")
+ return
+ service_name = f"{label}.service"
+ subprocess.run(["systemctl", "restart", service_name], check=False)
+ self._log(f"Requested restart for systemd service: {service_name}")
+ except Exception as e:
+ self._log(f"Failed to restart service via manager: {e}", level="error")
+
+ # ------------------------------------------------------------------
+ # Windows service install/uninstall (sc.exe)
+ # ------------------------------------------------------------------
+
+ def _install_windows_service(self) -> None:
+ """Install Windows service.
+
+ Prefer NSSM when available (bundled in corePY).
+
+ Why:
+ - `sc create` + a long `cmd.exe /c ...` binPath is fragile.
+ - NSSM hosts a normal console program and reports service state properly.
+ """
+ name = self._service_label()
+
+ # Installing a Windows service requires elevation.
+ if not self._windows_is_admin():
+ self._log("Admin privileges required to install the service. Requesting elevation...", level="info")
+ if self._windows_relaunch_elevated(sys.argv):
+ return
+ raise RuntimeError("Admin privileges required to install the service.")
+
+ # Use NSSM if present.
+ if not self._windows_has_nssm():
+ raise RuntimeError(
+ "NSSM was not found. Expected one of: "
+ "src/bin/nssm/windows/x86/nssm.exe or src/bin/nssm/windows/x86_64/nssm.exe (or the same under core/src/bin)."
+ )
+
+ # Resolve the command the service should run.
+ program, argv, workdir = self._service_command("start")
+
+ # Install directory: stable place for logs and (optionally) copied binaries.
+ install_dir = self._windows_install_dir()
+ install_dir.mkdir(parents=True, exist_ok=True)
+
+ logs_dir = install_dir.parent / "log"
+ logs_dir.mkdir(parents=True, exist_ok=True)
+
+ stdout_log = logs_dir / f"{name}.out.log"
+ stderr_log = logs_dir / f"{name}.err.log"
+
+ # In frozen mode, it's safer to point NSSM at a stable path.
+ # If we're already running from Program Files/ProgramData, we can use it directly.
+ # Otherwise (common during dev / unpackaged runs), we still allow it, but you may
+ # want to set COREPY_SERVICE_EXECUTABLE to an installed *-cli.exe.
+ app_program = program
+ app_dir = workdir
+
+ # Install (create) service
+ self._windows_nssm_run(["install", name, app_program] + argv)
+
+ # Configure working directory
+ self._windows_nssm_run(["set", name, "AppDirectory", app_dir])
+
+ # Environment variables so `start()` knows this is the service process
+ env_extra = "COREPY_RUN_AS_SERVICE=1\r\n" + f"COREPY_SERVICE_LABEL={name}" + "\r\n"
+ self._windows_nssm_run(["set", name, "AppEnvironmentExtra", env_extra])
+
+ # Logging
+ self._windows_nssm_run(["set", name, "AppStdout", str(stdout_log)])
+ self._windows_nssm_run(["set", name, "AppStderr", str(stderr_log)])
+ self._windows_nssm_run(["set", name, "AppRotateFiles", "1"])
+ self._windows_nssm_run(["set", name, "AppRotateOnline", "1"])
+ self._windows_nssm_run(["set", name, "AppRotateSeconds", "86400"])
+ self._windows_nssm_run(["set", name, "AppRotateBytes", str(10 * 1024 * 1024)])
+
+ # Startup
+ self._windows_nssm_run(["set", name, "Start", "SERVICE_AUTO_START"])
+
+ # Restart policy
+ self._windows_nssm_run(["set", name, "AppExit", "Default", "Restart"])
+
+ self._log(f"Installed Windows service (NSSM): {name}")
+
+ def _uninstall_windows_service(self) -> None:
+ name = self._service_label()
+
+ # Uninstalling a Windows service requires elevation.
+ if not self._windows_is_admin():
+ self._log("Admin privileges required to uninstall the service. Requesting elevation...", level="info")
+ if self._windows_relaunch_elevated(sys.argv):
+ return
+ raise RuntimeError("Admin privileges required to uninstall the service.")
+
+ # Prefer NSSM removal if available
+ if self._windows_has_nssm():
+ # Stop (best-effort)
+ self._windows_nssm_run(["stop", name], check=False)
+ # remove confirm
+ self._windows_nssm_run(["remove", name, "confirm"], check=False)
+ self._log(f"Uninstalled Windows service (NSSM): {name}")
+ return
+
+ # Fallback to sc
+ subprocess.run(["sc", "stop", name], check=False)
+ r = subprocess.run(["sc", "delete", name], capture_output=True, text=True)
+ if r.returncode != 0:
+ raise RuntimeError(f"sc delete failed: {(r.stdout or '') + (r.stderr or '')}")
+ self._log(f"Uninstalled Windows service: {name}")
+
+ # ------------------------------------------------------------------
+ # macOS launchd install/uninstall (per-user LaunchAgent)
+ # ------------------------------------------------------------------
+
+ def _macos_plist_path(self) -> Path:
+ # Per-user agent (no root required)
+ return Path.home() / "Library" / "LaunchAgents" / f"{self._service_label()}.plist"
+
+ def _install_macos_launchd(self) -> None:
+ label = self._service_label()
+ program, argv, workdir = self._service_command("start")
+ plist_path = self._macos_plist_path()
+ plist_path.parent.mkdir(parents=True, exist_ok=True)
+ # Ensure logs directory exists
+ logs_dir = Path.home() / "Library" / "Logs" / self._app_name()
+ logs_dir.mkdir(parents=True, exist_ok=True)
+
+ # Simple LaunchAgent; keep it alive, run at load.
+ plist = (
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ f" Label{label}\n"
+ " ProgramArguments\n"
+ " \n"
+ f" {program}\n"
+ + "".join(f" {a}\n" for a in argv)
+ + " \n"
+ f" WorkingDirectory{workdir}\n"
+ " EnvironmentVariables\n"
+ " \n"
+ " COREPY_RUN_AS_SERVICE1\n"
+ f" COREPY_SERVICE_LABEL{label}\n"
+ " \n"
+ " RunAtLoad\n"
+ " KeepAlive\n"
+ f" StandardOutPath{Path.home() / 'Library' / 'Logs' / self._app_name() / 'service.out.log'}\n"
+ f" StandardErrorPath{Path.home() / 'Library' / 'Logs' / self._app_name() / 'service.err.log'}\n"
+ "\n"
+ "\n"
+ )
+
+ plist_path.write_text(plist, encoding="utf-8")
+
+ # Load into the current user's launchd
+ subprocess.run(["launchctl", "bootstrap", f"gui/{os.getuid()}", str(plist_path)], check=False)
+ subprocess.run(["launchctl", "enable", f"gui/{os.getuid()}/{label}"], check=False)
+ subprocess.run(["launchctl", "kickstart", "-k", f"gui/{os.getuid()}/{label}"], check=False)
+
+ self._log(f"Installed launchd LaunchAgent: {plist_path}")
+
+ def _uninstall_macos_launchd(self) -> None:
+ label = self._service_label()
+ plist_path = self._macos_plist_path()
+
+ subprocess.run(["launchctl", "bootout", f"gui/{os.getuid()}/{label}"], check=False)
+ if plist_path.exists():
+ try:
+ plist_path.unlink()
+ except Exception:
+ pass
+
+ self._log(f"Uninstalled launchd LaunchAgent: {label}")
+
+ def _service_command(self, action: str) -> tuple[str, list[str], str]:
+ """Return (program, argv, working_dir) for service manager registration.
+
+ Supports both source runs (python + entry script) and frozen bundles (.exe/.app).
+
+ Environment overrides:
+ - COREPY_SERVICE_EXECUTABLE: absolute/relative path to the executable to register
+ (useful to force a sibling *-cli.exe on Windows).
+ """
+ action = (action or "").strip().lstrip("-")
+ if action not in ("start", "stop"):
+ raise ValueError(f"Invalid service action: {action}")
+
+ # Frozen bundle: run the executable directly.
+ if getattr(sys, "frozen", False):
+ exe_path = Path(sys.executable).expanduser()
+
+ # Optional override (preferred for Windows to target a console *-cli.exe)
+ override = (os.environ.get("COREPY_SERVICE_EXECUTABLE") or "").strip().strip('"')
+ if override:
+ ov = Path(override).expanduser()
+ if not ov.is_absolute():
+ ov = (Path.cwd() / ov).resolve()
+ if ov.exists():
+ exe_path = ov
+
+ # Best-effort Windows: if running GUI exe, prefer sibling "-cli.exe".
+ if sys.platform.startswith("win"):
+ try:
+ s = str(exe_path)
+ if s.lower().endswith(".exe") and not s.lower().endswith("-cli.exe"):
+ cand = Path(s[:-4] + "-cli.exe")
+ if cand.exists():
+ exe_path = cand
+ except Exception:
+ pass
+
+ program = str(exe_path)
+ workdir = str(exe_path.parent)
+ argv = [f"--{action}"]
+ return program, argv, workdir
+
+ # Source run: python + entrypoint
+ python_exe, entry, workdir = self._entry_command()
+ program = python_exe
+ argv = [entry, f"--{action}"]
+ return program, argv, workdir
+
+ def _windows_is_64bit(self) -> bool:
+ try:
+ return platform.machine().endswith("64") or ("PROGRAMFILES(X86)" in os.environ)
+ except Exception:
+ return True
+
+ def _windows_install_dir(self) -> Path:
+ """Stable per-machine install dir for service-related assets/logs."""
+ base = Path(os.environ.get("PROGRAMDATA", r"C:\\ProgramData"))
+ return base / (self._app_name() or "corepy") / "bin"
+
+ def _windows_bin_dir(self) -> Path:
+ """Best-effort directory where vendored tools live on Windows.
+
+ Priority:
+ 1) Stable install dir under ProgramData (created by this module)
+ 2) If frozen, beside the executable (typical for unpacked builds)
+ 3) Source-tree locations (dev)
+
+ Returns a directory that *may* not exist; callers should validate.
+ """
+ # 1) Preferred stable location
+ try:
+ d = self._windows_install_dir()
+ if d:
+ return d
+ except Exception:
+ pass
+
+ # 2) Frozen bundle: beside the executable
+ if getattr(sys, "frozen", False):
+ try:
+ return Path(sys.executable).resolve().parent
+ except Exception:
+ pass
+
+ # 3) Fallback: current working directory
+ return Path.cwd()
+
+ def _windows_copy_tool_to_install_dir(self, tool_path: Path) -> Path:
+ """Copy a bundled tool (like nssm.exe) into the stable install dir.
+
+ This avoids services pointing into PyInstaller's temporary _MEI folder.
+ """
+ install_dir = self._windows_install_dir()
+ install_dir.mkdir(parents=True, exist_ok=True)
+
+ dest = install_dir / tool_path.name
+ try:
+ # Only copy if missing or different size/mtime; keep it simple.
+ if not dest.exists():
+ shutil.copy2(str(tool_path), str(dest))
+ else:
+ try:
+ if tool_path.stat().st_size != dest.stat().st_size:
+ shutil.copy2(str(tool_path), str(dest))
+ except Exception:
+ # If we can't stat, just overwrite.
+ shutil.copy2(str(tool_path), str(dest))
+ except Exception:
+ # Last resort: attempt overwrite
+ try:
+ shutil.copy2(str(tool_path), str(dest))
+ except Exception:
+ pass
+
+ return dest
+
+ def _windows_nssm_candidates(self) -> list[Path]:
+ """Return candidate NSSM locations (supports both corePY and vendored corePY inside apps)."""
+ # Running inside corePY source tree
+ here = Path(__file__).resolve()
+ core_dir = here.parent # .../src/core
+
+ rels = [
+ # Common repo layouts
+ Path("src") / "bin" / "nssm" / "windows" / "x86" / "nssm.exe",
+ Path("src") / "bin" / "nssm" / "windows" / "x86_64" / "nssm.exe",
+ Path("bin") / "nssm" / "windows" / "x86" / "nssm.exe",
+ Path("bin") / "nssm" / "windows" / "x86_64" / "nssm.exe",
+
+ # Vendored corePY inside another repo (e.g. Replicator: src/core/src/bin/...)
+ Path("src") / "core" / "src" / "bin" / "nssm" / "windows" / "x86" / "nssm.exe",
+ Path("src") / "core" / "src" / "bin" / "nssm" / "windows" / "x86_64" / "nssm.exe",
+
+ # Flat copies in a bin dir (after we copy tools into ProgramData)
+ Path("nssm.exe"),
+ Path("bin") / "nssm.exe",
+ Path("tools") / "nssm.exe",
+ ]
+
+ roots = [
+ Path.cwd(),
+ core_dir.parent, # .../src
+ core_dir.parent.parent, # project root
+ here.parent.parent, # .../src (safe-ish)
+ ]
+
+ # Preferred stable install dir (ProgramData) for services
+ try:
+ roots.insert(0, self._windows_install_dir())
+ except Exception:
+ pass
+
+ # If frozen, also look beside the executable
+ if getattr(sys, "frozen", False):
+ try:
+ roots.append(Path(sys.executable).resolve().parent)
+ except Exception:
+ pass
+
+ out: list[Path] = []
+ for root in roots:
+ for rel in rels:
+ p = (root / rel).resolve()
+ out.append(p)
+ return out
+
+ def _windows_find_nssm(self) -> Optional[Path]:
+ if not sys.platform.startswith("win"):
+ return None
+
+ # Allow explicit override
+ override = (os.environ.get("COREPY_NSSM") or "").strip().strip('"')
+ if override:
+ p = Path(override).expanduser()
+ if not p.is_absolute():
+ p = (Path.cwd() / p).resolve()
+ if p.exists():
+ return p
+
+ want_64 = self._windows_is_64bit()
+ # Prefer matching arch first
+ preferred = ["x86_64"] if want_64 else ["x86"]
+ preferred += ["x86"] if want_64 else ["x86_64"]
+
+ candidates = self._windows_nssm_candidates()
+ candidate: Optional[Path] = None
+ # Prefer matching arch
+ for arch in preferred:
+ for p in candidates:
+ if f"\\{arch}\\" in str(p).lower() and p.exists():
+ candidate = p
+ break
+ if candidate is not None:
+ break
+ # Any existing
+ if candidate is None:
+ for p in candidates:
+ if p.exists():
+ candidate = p
+ break
+ if candidate is not None:
+ # If it's in a temp unpack dir, copy to stable dir.
+ try:
+ s = str(candidate)
+ if "_mei" in s.lower() or "\\appdata\\local\\temp\\" in s.lower():
+ stable = self._windows_copy_tool_to_install_dir(candidate)
+ if stable.exists():
+ return stable
+ except Exception:
+ pass
+ return candidate
+
+ # If we found NSSM inside a PyInstaller temp folder (_MEI...), copy it to stable ProgramData
+ # and return the stable path so services never reference a temporary location.
+ try:
+ found = None
+ for arch in preferred:
+ for p in candidates:
+ if f"\\{arch}\\" in str(p).lower() and p.exists():
+ found = p
+ break
+ if found:
+ break
+ if found is None:
+ for p in candidates:
+ if p.exists():
+ found = p
+ break
+
+ if found is not None:
+ s = str(found)
+ if "_mei" in s.lower() or "\\appdata\\local\\temp\\" in s.lower():
+ stable = self._windows_copy_tool_to_install_dir(found)
+ if stable.exists():
+ return stable
+ except Exception:
+ pass
+
+ return None
+
+ def _windows_has_nssm(self) -> bool:
+ return bool(self._windows_find_nssm())
+
+
+ def _windows_nssm_run(self, args: list[str], *, check: bool = True) -> subprocess.CompletedProcess:
+ """Run NSSM with the given args.
+
+ Uses the bundled NSSM, and captures output for better error messages.
+ """
+ nssm = self._windows_find_nssm()
+ # Prefer a stable copy under ProgramData if available
+ try:
+ stable = self._windows_install_dir() / "nssm.exe"
+ if stable.exists():
+ nssm = stable
+ except Exception:
+ pass
+
+ if not nssm:
+ raise RuntimeError("NSSM not found (COREPY_NSSM override not set and no bundled binary found).")
+
+ cmd = [str(nssm)] + list(args)
+ r = subprocess.run(cmd, capture_output=True, text=True)
+ if check and r.returncode != 0:
+ raise RuntimeError(f"nssm failed: {cmd}\n{(r.stdout or '') + (r.stderr or '')}")
+ return r
+
+
+# ------------------------------------------------------------------
+# Service Manager Dialog UI (PyQt5)
+# ------------------------------------------------------------------
+
+class _TailReader:
+ """Tiny file tailer used by the ServiceManagerDialog.
+
+ Keeps an in-memory cursor per file and reads new bytes on each poll.
+ """
+
+ def __init__(self) -> None:
+ self._pos: dict[str, int] = {}
+
+ def read_new_text(self, path: str, *, max_bytes: int = 256 * 1024) -> str:
+ if not path:
+ return ""
+ try:
+ if not os.path.exists(path):
+ return ""
+
+ last = self._pos.get(path, 0)
+ size = os.path.getsize(path)
+
+ # If file was truncated/rotated, restart.
+ if size < last:
+ last = 0
+
+ # Avoid reading gigantic chunks at once.
+ start = max(0, size - max_bytes) if last == 0 and size > max_bytes else last
+
+ with open(path, "rb") as f:
+ f.seek(start)
+ data = f.read()
+
+ self._pos[path] = start + len(data)
+
+ # Best-effort decode
+ try:
+ return data.decode("utf-8", errors="replace")
+ except Exception:
+ return data.decode(errors="replace")
+ except Exception:
+ return ""
+
+
+class ServiceManagerDialog(QDialog):
+ """Cross-platform service manager UI.
+
+ Focus:
+ - Install / Uninstall
+ - Start / Stop
+ - Status (installed/running)
+ - Live-ish log view (stdout/stderr files when available)
+
+ Note: On Linux, services often log to journald; this dialog will show file logs
+ when present, otherwise it shows a hint.
+ """
+
+ def __init__(self, service: Service, parent=None) -> None:
+ super().__init__(parent)
+ self._svc = service
+ self._tail = _TailReader()
+ self._poll_ms = 1000
+
+ self.setWindowTitle("Service Manager")
+ self.setModal(True)
+ self.resize(980, 560)
+
+ # -----------------------------
+ # Logs (right column, under controls)
+ # -----------------------------
+ self._tabs = QTabWidget()
+
+ self._txt_out = QPlainTextEdit()
+ self._txt_out.setReadOnly(True)
+ self._txt_err = QPlainTextEdit()
+ self._txt_err.setReadOnly(True)
+
+ self._tabs.addTab(self._txt_out, "Stdout")
+ self._tabs.addTab(self._txt_err, "Stderr")
+
+ logs_box = QGroupBox()
+ logs_layout = QVBoxLayout(logs_box)
+ logs_layout.addWidget(self._tabs)
+
+ # -----------------------------
+ # Controls (right column)
+ # -----------------------------
+ controls = QGroupBox()
+ self._controls_layout = QHBoxLayout(controls)
+ self._controls_layout.setSpacing(10)
+ self._controls_layout.setContentsMargins(10, 10, 10, 10)
+
+ # Use Form.button when available (for icons); fallback to QPushButton.
+ def _mk_btn(label: str, fn: Callable[[], None], icon: str = ""):
+ if Form is not None and hasattr(Form, "button"):
+ return Form.button(label="", icon=icon, action=fn)
+ b = QPushButton(label)
+ b.clicked.connect(fn)
+ return b
+
+ self._btn_install = _mk_btn("Install", self._on_install, icon="download")
+ self._btn_uninstall = _mk_btn("Uninstall", self._on_uninstall, icon="trash")
+ self._btn_start = _mk_btn("Start", self._on_start, icon="play")
+ self._btn_stop = _mk_btn("Stop", self._on_stop, icon="stop")
+ self._btn_refresh = _mk_btn("Refresh", self.refresh, icon="arrow-clockwise")
+
+ self._controls_layout.addWidget(self._btn_install)
+ self._controls_layout.addWidget(self._btn_uninstall)
+ self._controls_layout.addWidget(self._btn_start)
+ self._controls_layout.addWidget(self._btn_stop)
+ self._controls_layout.addWidget(self._btn_refresh)
+ self._controls_layout.addStretch(1)
+
+ # -----------------------------
+ # Status (left column)
+ # -----------------------------
+ self._lbl_name = QLabel("")
+ self._lbl_label = QLabel("")
+ self._lbl_installed = QLabel("")
+ self._lbl_running = QLabel("")
+ self._lbl_pid = QLabel("")
+ self._lbl_mgr = QLabel("")
+ self._lbl_nssm = QLabel("")
+ self._lbl_stdout = QLabel("")
+ self._lbl_stderr = QLabel("")
+
+ status_box = QGroupBox()
+ status_form = QFormLayout(status_box)
+ status_form.addRow("Application:", self._lbl_name)
+ status_form.addRow("Service label:", self._lbl_label)
+ status_form.addRow("Manager:", self._lbl_mgr)
+ status_form.addRow("Installed:", self._lbl_installed)
+ status_form.addRow("Running:", self._lbl_running)
+ status_form.addRow("PID:", self._lbl_pid)
+ status_form.addRow("NSSM:", self._lbl_nssm)
+
+ # -----------------------------
+ # Root layout: 2 columns
+ # Left (1/4): Status
+ # Right (3/4): Controls + Logs
+ # -----------------------------
+ root = QHBoxLayout(self)
+ root.setContentsMargins(8, 8, 8, 8)
+ root.setSpacing(10)
+
+ # Left column (Status)
+ root.addWidget(status_box, 1)
+
+ # Right column (Controls + Logs)
+ right = QWidget(self)
+ right_layout = QVBoxLayout(right)
+ right_layout.setContentsMargins(0, 0, 0, 0)
+ right_layout.setSpacing(10)
+ right_layout.addWidget(controls, 0)
+ right_layout.addWidget(logs_box, 1)
+
+ root.addWidget(right, 3)
+
+ # Avoid UI flashing on Windows by only updating widgets when values actually change.
+ self._last_snapshot: dict[str, object] = {}
+ self._refresh_tick = 0
+ # Poll timer (status + logs)
+ self._timer = QTimer(self)
+ self._timer.setInterval(self._poll_ms)
+ self._timer.timeout.connect(self._on_timer)
+
+ # Initial refresh
+ self.refresh()
+ self._timer.start()
+
+ # -----------------------------
+ # Helpers
+ # -----------------------------
+
+ def _manager_name(self) -> str:
+ if sys.platform.startswith("win"):
+ return "Windows Service Control Manager (NSSM)" if self._svc._windows_has_nssm() else "Windows Service Control Manager"
+ if sys.platform == "darwin":
+ return "launchd"
+ return "systemd" if shutil.which("systemctl") else "(unknown)"
+
+ def _windows_log_paths(self) -> tuple[str, str]:
+ label = self._svc._service_label()
+ try:
+ logs_dir = self._svc._windows_install_dir().parent / "log"
+ return str(logs_dir / f"{label}.out.log"), str(logs_dir / f"{label}.err.log")
+ except Exception:
+ return "", ""
+
+ def _macos_log_paths(self) -> tuple[str, str]:
+ try:
+ logs_dir = Path.home() / "Library" / "Logs" / self._svc._app_name()
+ return str(logs_dir / "service.out.log"), str(logs_dir / "service.err.log")
+ except Exception:
+ return "", ""
+
+ def _log_paths(self) -> tuple[str, str]:
+ if sys.platform.startswith("win"):
+ return self._windows_log_paths()
+ if sys.platform == "darwin":
+ return self._macos_log_paths()
+ # Linux: systemd typically logs to journal; still try common file paths
+ return "", ""
+
+ def _set_bool_label(self, lbl: QLabel, value: bool) -> None:
+ lbl.setText("yes" if value else "no")
+
+ def refresh(self) -> None:
+ try:
+ name = self._svc._app_name()
+ label = self._svc._service_label()
+ installed = self._svc._is_installed()
+ running = self._svc._is_service_active() if installed else self._svc._is_running()
+ pid = self._svc._read_pidfile() if not installed else None
+
+ # NSSM path (Windows only)
+ if sys.platform.startswith("win"):
+ try:
+ nssm_path = self._svc._windows_find_nssm()
+ nssm_text = str(nssm_path) if nssm_path else "(not found)"
+ except Exception:
+ nssm_text = "(unknown)"
+ else:
+ nssm_text = "-"
+
+ out_path, err_path = self._log_paths()
+ stdout_text = out_path if out_path else "(no file log configured)"
+ stderr_text = err_path if err_path else "(no file log configured)"
+
+ # Snapshot current state to avoid redundant UI updates (prevents flashing on Windows)
+ snap: dict[str, object] = {
+ "name": name,
+ "label": label,
+ "manager": self._manager_name(),
+ "installed": installed,
+ "running": running,
+ "pid": str(pid) if pid else "-",
+ "nssm": nssm_text,
+ "stdout": stdout_text,
+ "stderr": stderr_text,
+ "btn_install": (not installed),
+ "btn_uninstall": installed,
+ "btn_start": (installed and not running),
+ "btn_stop": (installed and running),
+ }
+
+ if snap != self._last_snapshot:
+ self._lbl_name.setText(str(snap["name"]))
+ self._lbl_label.setText(str(snap["label"]))
+ self._lbl_mgr.setText(str(snap["manager"]))
+ self._set_bool_label(self._lbl_installed, bool(snap["installed"]))
+ self._set_bool_label(self._lbl_running, bool(snap["running"]))
+ self._lbl_pid.setText(str(snap["pid"]))
+ self._lbl_nssm.setText(str(snap["nssm"]))
+ self._lbl_stdout.setText(str(snap["stdout"]))
+ self._lbl_stderr.setText(str(snap["stderr"]))
+
+ # Button visibility: only show actions that make sense.
+ self._btn_install.setVisible(bool(snap["btn_install"]))
+ self._btn_uninstall.setVisible(bool(snap["btn_uninstall"]))
+ self._btn_start.setVisible(bool(snap["btn_start"]))
+ self._btn_stop.setVisible(bool(snap["btn_stop"]))
+
+ self._last_snapshot = snap
+
+ # Show a helpful note in logs pane when file logs aren't available.
+ if not out_path and not err_path:
+ note = (
+ "Logs are not available as files on this platform/configuration.\n"
+ "- Windows: logs are written to ProgramData via NSSM settings.\n"
+ "- macOS: logs are written to ~/Library/Logs//.\n"
+ "- Linux: consider using journald (journalctl -u ).\n"
+ )
+ # Only set the note once to avoid flicker
+ if self._txt_out.toPlainText().strip() == "":
+ self._txt_out.setPlainText(note)
+ if self._txt_err.toPlainText().strip() == "":
+ self._txt_err.setPlainText(note)
+
+ except Exception as e:
+ QMessageBox.warning(self, "Service", f"Failed to refresh status: {e}")
+
+ # -----------------------------
+ # Actions
+ # -----------------------------
+
+ def _on_install(self) -> None:
+ try:
+ self._svc.install()
+ except Exception as e:
+ QMessageBox.critical(self, "Service", f"Install failed:\n\n{e}")
+ finally:
+ self.refresh()
+
+ def _on_uninstall(self) -> None:
+ try:
+ self._svc.uninstall()
+ except Exception as e:
+ QMessageBox.critical(self, "Service", f"Uninstall failed:\n\n{e}")
+ finally:
+ self.refresh()
+
+ def _on_start(self) -> None:
+ try:
+ self._svc._service_manager_start()
+ except Exception as e:
+ QMessageBox.critical(self, "Service", f"Start failed:\n\n{e}")
+ finally:
+ self.refresh()
+
+ def _on_stop(self) -> None:
+ try:
+ self._svc._service_manager_stop()
+ except Exception as e:
+ QMessageBox.critical(self, "Service", f"Stop failed:\n\n{e}")
+ finally:
+ self.refresh()
+
+ # -----------------------------
+ # Log polling
+ # -----------------------------
+
+ def _on_timer(self) -> None:
+ # Only tail logs on a timer. Avoid polling status (may spawn sc/systemctl windows).
+ self._poll_logs()
+
+ # -----------------------------
+ # Log polling
+ # -----------------------------
+
+ def _append_text(self, widget: QPlainTextEdit, text: str) -> None:
+ if not text:
+ return
+ try:
+ widget.moveCursor(widget.textCursor().End)
+ widget.insertPlainText(text)
+ widget.moveCursor(widget.textCursor().End)
+ except Exception:
+ # Safe fallback
+ try:
+ widget.setPlainText(widget.toPlainText() + text)
+ except Exception:
+ pass
+
+ def _poll_logs(self) -> None:
+ out_path, err_path = self._log_paths()
+ if out_path:
+ self._append_text(self._txt_out, self._tail.read_new_text(out_path))
+ if err_path:
+ self._append_text(self._txt_err, self._tail.read_new_text(err_path))
+
+ def closeEvent(self, event) -> None: # type: ignore[override]
+ try:
+ if self._timer.isActive():
+ self._timer.stop()
+ except Exception:
+ pass
+ super().closeEvent(event)
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/src/bin/nssm/windows/x86/nssm.exe b/dist/macos/Replicator.app/Contents/Resources/core/src/bin/nssm/windows/x86/nssm.exe
new file mode 100644
index 00000000..8faee45b
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Resources/core/src/bin/nssm/windows/x86/nssm.exe differ
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/src/bin/nssm/windows/x86_64/nssm.exe b/dist/macos/Replicator.app/Contents/Resources/core/src/bin/nssm/windows/x86_64/nssm.exe
new file mode 100644
index 00000000..6ccfe3cf
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Resources/core/src/bin/nssm/windows/x86_64/nssm.exe differ
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/styles/style.css b/dist/macos/Replicator.app/Contents/Resources/core/styles/style.css
new file mode 100644
index 00000000..8a40cedd
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/styles/style.css
@@ -0,0 +1,320 @@
+QDialog, QMessageBox, QProgressDialog, QTabWidget, QTabWidget::pane, QGroupBox, QGroupBox::title {
+ background-color: #212121;
+ color: #FFFFFF;
+ font-family: Arial, sans-serif;
+ font-size: 14px;
+}
+
+QGroupBox {
+ border: 1px solid #76797C;
+ border-radius: 5px;
+}
+
+QGroupBox::title {
+ subcontrol-origin: margin;
+ subcontrol-position: top left;
+ left: 10px;
+ padding: 0 6px;
+}
+
+QTabWidget::pane {
+ background-color: #414141;
+ border: 1px solid #76797C;
+ border-radius: 5px;
+ position: absolute;
+ top: -1px;
+ padding: 10px;
+}
+
+QLabel, QLineEdit, QTimeEdit, QCheckBox, QTextEdit, QComboBox, QSpinBox, QPushButton, QTabBar::tab, QPlainTextEdit {
+ font-size: 14px;
+ border: 1px solid #76797C;
+ border-radius: 5px;
+ padding: 10px;
+ margin: 5px 0px;
+}
+
+QLineEdit, QTimeEdit, QTextEdit, QComboBox, QSpinBox, QPlainTextEdit {
+ background-color: #212121;
+ color: #FFFFFF;
+ width: 300px;
+}
+
+QTimeEdit {
+ width: 75px;
+}
+
+QLineEdit:focus, QTimeEdit:focus, QTextEdit:focus, QComboBox:focus, QSpinBox:focus, QPlainTextEdit:focus {
+ border: 1px solid #0d6efd;
+}
+
+QSpinBox {
+ padding-right: 39px;
+}
+QSpinBox::up-button,
+QSpinBox::down-button {
+ border: none;
+ border-left: 1px solid #76797C;
+ background-color: #414344;
+ width: 39px;
+}
+QSpinBox::up-button {
+ border-top-right-radius: 5px;
+ border-bottom-right-radius: 0px;
+}
+QSpinBox::down-button {
+ border-top-right-radius: 0px;
+ border-bottom-right-radius: 5px;
+}
+QSpinBox::up-button:hover,
+QSpinBox::down-button:hover {
+ background-color: #5f6263;
+}
+QSpinBox::up-button:pressed,
+QSpinBox::down-button:pressed,
+QSpinBox::up-button:focus,
+QSpinBox::down-button:focus {
+ background-color: #0d6efd;
+}
+QSpinBox::up-arrow,
+QSpinBox::down-arrow {
+ width: 20px;
+ height: 20px;
+}
+
+QComboBox::drop-down {
+ border: none;
+ border-left: 1px solid #76797C;
+ border-radius: 5px;
+ border-top-left-radius: 0px;
+ border-bottom-left-radius: 0px;
+ margin: 0px;
+ background-color: #414344;
+ color: #FFFFFF;
+ width: 39px;
+ height: 39px;
+}
+QComboBox::down-arrow:pressed,
+QComboBox::drop-down:hover {
+ background-color: #5f6263;
+}
+QComboBox::down-arrow:pressed,
+QComboBox::down-arrow:focus,
+QComboBox::drop-down:pressed,
+QComboBox::drop-down:focus {
+ background-color: #0d6efd;
+}
+QComboBox::down-arrow {
+ width: 20px;
+ height: 20px;
+}
+QListView,
+QAbstractItemView {
+ border: 1px solid #76797C;
+ border-radius: 5px;
+ background-color: #212121;
+ color: #FFFFFF;
+ outline: 0;
+ selection-background-color: #0d6efd;
+ selection-color: #FFFFFF;
+}
+QListView::item,
+QAbstractItemView::item {
+ min-height: 28px;
+ padding: 6px 10px;
+ border: none;
+}
+QListView::item:selected,
+QAbstractItemView::item:selected {
+ background-color: #0d6efd;
+ color: #FFFFFF;
+}
+QListView::item:hover,
+QAbstractItemView::item:hover {
+ background-color: #5f6263;
+ border: none;
+}
+
+/* Added styling for QTableView and QTableWidget to match dark theme */
+QTableView, QTableWidget {
+ border: 1px solid #76797C;
+ border-radius: 5px;
+ background-color: #212121;
+ color: #FFFFFF;
+ gridline-color: #414344;
+ selection-background-color: #0d6efd;
+ selection-color: #FFFFFF;
+ outline: 0;
+ padding: 0px;
+ margin: 5px 0px;
+}
+
+QHeaderView::section {
+ background-color: #414344;
+ color: #FFFFFF;
+ border: 1px solid #414344;
+ border-bottom-left-radius: 0px;
+ border-bottom-right-radius: 0px;
+ padding: 8px 10px;
+}
+
+QTableCornerButton::section {
+ background-color: #414344;
+ color: #FFFFFF;
+ border: 1px solid #414344;
+ padding: 8px 10px;
+}
+
+QTableView::item, QTableWidget::item {
+ padding: 6px 10px;
+ border: none;
+}
+
+QTableView::item:hover, QTableWidget::item:hover {
+ background-color: #5f6263;
+}
+
+QTableView::item:selected, QTableWidget::item:selected {
+ background-color: #0d6efd;
+ color: #FFFFFF;
+}
+
+QTableView:focus, QTableWidget:focus {
+ outline: 0;
+}
+
+QComboBox QFrame,
+QFrame#qt_qcombobox_popup {
+ background-color: #212121;
+ border: 1px solid #76797C;
+ border-radius: 5px;
+ padding: 0px;
+}
+QFrame#qt_qcombobox_popup QListView,
+QFrame#qt_qcombobox_popup QAbstractItemView {
+ background-color: #212121;
+ border: none;
+}
+
+QLabel, QCheckBox {
+ color: #FFFFFF;
+ border: none;
+}
+
+QCheckBox {
+ spacing: 8px;
+}
+
+QCheckBox::indicator {
+ width: 18px;
+ height: 18px;
+ border: 1px solid #76797C;
+ border-radius: 5px;
+ background: #212121;
+ margin-right: 6px;
+}
+QCheckBox::indicator:hover {
+ border-color: #5f6263;
+}
+QCheckBox::indicator:pressed {
+ background: #5f6263;
+ border-color: #5f6263;
+}
+QCheckBox::indicator:checked {
+ background: #0d6efd;
+ border-color: #0d6efd;
+}
+QCheckBox:disabled {
+ color: #8c8c8c;
+}
+QCheckBox::indicator:disabled {
+ background: #2b2b2b;
+ border-color: #555555;
+}
+QCheckBox:focus {
+ outline: none;
+}
+QCheckBox::indicator:focus {
+ border-color: #0d6efd;
+}
+
+QPushButton,
+QTabBar::tab {
+ background-color: #414344;
+ color: #FFFFFF;
+
+ selection-background-color: #414344;
+ selection-color: #FFFFFF;
+}
+QPushButton:focus,
+QTabBar::tab:focus {
+ border-color: #0d6efd;
+ background-color: #414344;
+}
+QPushButton:hover,
+QTabBar::tab:hover {
+ background-color: #5f6263;
+ border-color: #5f6263;
+}
+QPushButton:pressed,
+QTabBar::tab:pressed,
+QTabBar::tab:selected {
+ background-color: #0d6efd;
+ border-color: #0d6efd;
+}
+QPushButton:default,
+QPushButton:default:focus,
+QTabBar::tab:default,
+QTabBar::tab:default:focus {
+ background-color: #414344;
+ border-color: #0d6efd;
+}
+
+QTabWidget::tab-bar, QTabBar {
+ alignment: center;
+}
+QTabBar::tab {
+ border-radius: 0px;
+}
+QTabBar::tab:first {
+ border-top-left-radius: 5px;
+ border-bottom-left-radius: 5px;
+}
+QTabBar::tab:last {
+ border-top-right-radius: 5px;
+ border-bottom-right-radius: 5px;
+}
+QPushButton:hover, QTabBar::tab:hover {
+ background-color: #5f6263;
+ border-color: #5f6263;
+}
+QPushButton:pressed, QTabBar::tab:pressed, QTabBar::tab:selected {
+ background-color: #0d6efd;
+ border-color: #0d6efd;
+}
+
+#MsgBox {
+ min-width: 400px;
+ background-color: #212121;
+ color: #FFFFFF;
+ padding: 10px;
+ font-size: 14px;
+}
+
+#Client {
+ min-width: 400px;
+ background-color: #212121;
+ color: #FFFFFF;
+ padding: 10px;
+ font-size: 14px;
+}
+
+#DiagnosticDialog QLabel#statusDot {
+ padding: 0;
+ margin: 0;
+ min-width: 16px;
+ min-height: 16px;
+ max-width: 16px;
+ max-height: 16px;
+ border-radius: 8px;
+}
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/ui.py b/dist/macos/Replicator.app/Contents/Resources/core/ui.py
new file mode 100644
index 00000000..20ddb653
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/ui.py
@@ -0,0 +1,775 @@
+#!/usr/bin/env python3
+# src/core/ui.py
+
+from __future__ import annotations
+
+import os
+import base64
+
+from typing import Iterable, Optional, Callable, List
+
+from PyQt5.QtWidgets import (
+ QDialog, QVBoxLayout, QLabel, QHBoxLayout, QPushButton,
+ QLineEdit, QSpinBox, QComboBox, QCheckBox, QColorDialog,
+ QSizePolicy, QStyle, QStyleOptionButton, QWidget, QFileDialog,
+ QListView, QFrame, QApplication, QListWidget
+)
+from PyQt5.QtGui import (
+ QIcon, QPixmap, QPainter, QColor, QPen, QTransform
+)
+from PyQt5.QtCore import QRect, Qt, QTimer
+from PyQt5.QtSvg import QSvgWidget, QSvgRenderer
+
+# Allow this module to be used both as part of the 'app' package and as a standalone script
+try:
+ from .helper import Helper
+ from .network.tools import Tools
+except ImportError: # likely running as a top-level script
+ from helper import Helper
+ from network.tools import Tools
+
+class MsgBox(QDialog):
+
+ ICONS = {
+ "info": "core/icons/info.svg",
+ "error": "core/icons/error.svg",
+ "warning": "core/icons/warning.svg",
+ "question":"core/icons/question.svg",
+ }
+
+ def __init__(
+ self,
+ parent=None,
+ title: str = "",
+ message: str = "",
+ icon: Optional[str] = None,
+ buttons: Iterable[str] = ("OK",),
+ default: Optional[str] = None,
+ icon_size: int = 64,
+ icon_lookup_fn: Optional[Callable[[str], Optional[str]]] = None,
+ ):
+ super().__init__(parent)
+ self.setWindowTitle(title)
+ self.setObjectName("MsgBox")
+ self.setModal(True)
+
+ self._selected: Optional[str] = None
+ self._icon_lookup = icon_lookup_fn
+ self._helper = None
+
+ if isinstance(buttons, str):
+ buttons = (buttons,)
+ else:
+ buttons = tuple(buttons)
+
+ layout = QVBoxLayout(self)
+
+ # --- Top area: icon + message ---
+ top = QHBoxLayout()
+ layout.addLayout(top)
+
+ if icon:
+ icon_path = self._resolve_icon_path(icon)
+ if icon_path:
+ svg = QSvgWidget(icon_path)
+ svg.setFixedSize(icon_size, icon_size)
+ svg.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
+ top.addWidget(svg, alignment=Qt.AlignVCenter)
+
+ # message text
+ msg_label = QLabel(message)
+ msg_label.setWordWrap(True)
+ msg_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
+ top.addWidget(msg_label)
+
+ # --- Buttons ---
+ btn_row = QHBoxLayout()
+ btn_row.addStretch(1)
+ self._buttons: dict[str, QPushButton] = {}
+
+ for text in buttons:
+ btn = QPushButton(text)
+ self._buttons[text] = btn
+ btn.clicked.connect(lambda _, t=text: self._on_click(t))
+ btn_row.addWidget(btn)
+
+ layout.addLayout(btn_row)
+
+ # default selection
+ if default and default in self._buttons:
+ self._buttons[default].setDefault(True)
+ self._buttons[default].setFocus()
+
+ # ------------------------------------------------------------------
+ # Internals
+ # ------------------------------------------------------------------
+
+ def _ensure_helper(self):
+ """
+ Lazily import and instantiate helper.
+ """
+ if self._helper is not None:
+ return self._helper
+
+ try:
+ self._helper = Helper()
+ except Exception as e:
+ print(f"[MsgBox] Failed to initialize helpers: {e}")
+ self._helper = None
+
+ return self._helper
+
+ def _resolve_icon_path(self, icon: str) -> Optional[str]:
+ """
+ Resolve an icon name or path using ICONS + optional lookup function.
+ """
+ # Named icon → relative path from ICONS
+ candidate = self.ICONS.get(icon, icon)
+
+ if self._icon_lookup:
+ resolved = self._icon_lookup(candidate)
+ if resolved:
+ return resolved
+ else:
+ if self._ensure_helper() is not None:
+ resolved = self._helper.get_path(candidate)
+ if resolved and self._helper.file_exists(resolved):
+ return resolved
+
+ return candidate
+
+ def _on_click(self, text: str):
+ self._selected = text
+ self.accept()
+
+ # -------------------------------------------------
+ # Convenience static helper
+ # -------------------------------------------------
+
+ @staticmethod
+ def show(
+ parent=None,
+ title: str = "",
+ message: str = "",
+ icon: Optional[str] = None,
+ buttons: Iterable[str] = ("OK",),
+ default: Optional[str] = None,
+ icon_lookup_fn: Optional[Callable[[str], Optional[str]]] = None,
+ ) -> str:
+ """
+ Show the dialog modally and return the label of the chosen button.
+ """
+ dlg = MsgBox(
+ parent=parent,
+ title=title,
+ message=message,
+ icon=icon,
+ buttons=buttons,
+ default=default,
+ icon_lookup_fn=icon_lookup_fn,
+ )
+ dlg.exec_()
+ return dlg._selected or default or ""
+
+class SpinningIconLabel(QLabel):
+
+ def __init__(self, svg_path: str, size: int = 32, interval_ms: int = 50, parent=None):
+ super().__init__(parent)
+ self._size = size
+ self._svg_path = svg_path
+ self._angle = 0
+
+ self._base_pixmap = QIcon(svg_path).pixmap(size, size)
+
+ self.setFixedSize(size, size)
+ self.setAlignment(Qt.AlignCenter)
+ self.setScaledContents(True)
+ self.setStyleSheet("padding: 0; margin: 0; border: none;")
+
+ # Timer
+ self._timer = QTimer(self)
+ self._timer.timeout.connect(self._tick)
+ self._timer.start(interval_ms)
+
+ def _tick(self):
+ """Rotate icon by a fixed number of degrees."""
+ self._angle = (self._angle + 10) % 360
+
+ rotated = self._rotate_pixmap(self._base_pixmap, self._angle)
+ self.setPixmap(rotated)
+
+ def _rotate_pixmap(self, pixmap: QPixmap, angle: float) -> QPixmap:
+ """Return a rotated copy of the pixmap."""
+ transform = QTransform()
+ transform.rotate(angle)
+ rotated = pixmap.transformed(transform, Qt.SmoothTransformation)
+
+ # Scale back to exact size
+ return rotated.scaled(self._size, self._size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
+
+ def stop(self):
+ self._timer.stop()
+
+ def start(self):
+ self._timer.start()
+
+class ColorButton(QPushButton):
+ def __init__(self, initial: str = "#265162", parent=None):
+ super().__init__(parent)
+ self._color = QColor(initial)
+ self.clicked.connect(self._pick)
+
+ def _pick(self):
+ chosen = QColorDialog.getColor(self._color, self, "Choose Color")
+ if chosen.isValid():
+ self._color = chosen
+ self.update() # repaint
+
+ def color(self) -> QColor:
+ return self._color
+
+ def hex(self) -> str:
+ return self._color.name()
+
+ def setHex(self, value: str):
+ if not value:
+ return
+ c = QColor(value)
+ if c.isValid():
+ self._color = c
+ self.update() # repaint
+
+ def _styleOption(self):
+ option = QStyleOptionButton()
+ option.initFrom(self)
+ option.text = self.text()
+ option.icon = self.icon()
+ return option
+
+ def paintEvent(self, event):
+ super().paintEvent(event)
+
+ painter = QPainter(self)
+ painter.setRenderHint(QPainter.Antialiasing, True)
+
+ # Get content rect WITHIN QPushButton's styled padding
+ content = self.style().subElementRect(
+ QStyle.SE_PushButtonContents,
+ self._styleOption(),
+ self
+ )
+
+ pen = QPen(QColor(118, 121, 124))
+ painter.setPen(pen)
+ painter.setBrush(self._color)
+
+ painter.drawRoundedRect(content, 4, 4)
+ painter.end()
+
+class PictureButton(QPushButton):
+ def __init__(self, initial: Optional[str] = None, parent=None, max_size: int = 72):
+ super().__init__(parent)
+ self._b64: str = ""
+ self._max_size = max_size
+
+ # Visual setup
+ self.setFixedSize(max_size + 16, max_size + 24)
+ self.setStyleSheet("padding: 4px;")
+ self.setText("Select Logo")
+
+ # Initialize from existing config value
+ if initial:
+ self._init_from_value(initial)
+
+ self.clicked.connect(self._pick)
+
+ # ----- public API for Configuration -----
+
+ def value(self) -> str:
+ """
+ Return the stored base64 string (or "" if none).
+ """
+ return self._b64
+
+ def setValue(self, raw: str):
+ # Reset visuals first
+ self._b64 = ""
+ self.setIcon(QIcon())
+ self.setText("Select Logo")
+
+ if raw:
+ self._init_from_value(raw)
+
+ # ----- internals -----
+
+ def _init_from_value(self, raw: str):
+ raw = raw.strip()
+ if not raw:
+ return
+
+ # 1) Try as base64
+ data: Optional[bytes] = None
+ try:
+ data = base64.b64decode(raw, validate=True)
+ except Exception:
+ data = None
+
+ # 2) If not valid base64, treat as path
+ if data is None:
+ if os.path.isfile(raw):
+ try:
+ with open(raw, "rb") as f:
+ data = f.read()
+ except Exception as e:
+ print(f"[PictureButton] Failed to read logo file '{raw}': {e}")
+ return
+ else:
+ # Unknown format; give up silently
+ return
+
+ # At this point we have `data`
+ pm = QPixmap()
+ if not pm.loadFromData(data, "PNG"):
+ return
+
+ self._b64 = base64.b64encode(data).decode("ascii")
+ self._set_pixmap(pm)
+
+ def _pick(self):
+ path, _ = QFileDialog.getOpenFileName(
+ self,
+ "Select Logo",
+ "",
+ "PNG Files (*.png)"
+ )
+ if not path:
+ return
+
+ try:
+ with open(path, "rb") as f:
+ data = f.read()
+
+ pm = QPixmap()
+ if not pm.loadFromData(data, "PNG"):
+ print(f"[PictureButton] Not a valid PNG: {path}")
+ return
+
+ self._b64 = base64.b64encode(data).decode("ascii")
+ self._set_pixmap(pm)
+ except Exception as e:
+ print(f"[PictureButton] Failed to load logo '{path}': {e}")
+
+ def _set_pixmap(self, pm: QPixmap):
+ scaled = pm.scaled(
+ self._max_size,
+ self._max_size,
+ Qt.KeepAspectRatio,
+ Qt.SmoothTransformation,
+ )
+ self.setIcon(QIcon(scaled))
+ self.setIconSize(scaled.size())
+ self.setText("")
+ self.update()
+
+class FileInput(QWidget):
+ def __init__(
+ self,
+ initial: str = "",
+ caption: str = "Select File",
+ directory: str = "",
+ filter: str = "All Files (*)",
+ as_base64: bool = False,
+ on_changed: Optional[Callable[[str], None]] = None,
+ parent=None,
+ ):
+ super().__init__(parent)
+ self._caption = caption
+ self._directory = directory
+ self._filter = filter
+ self._as_base64 = as_base64
+ self._on_changed = on_changed
+ self._stored_value: str = initial or ""
+
+ layout = QHBoxLayout(self)
+ layout.setContentsMargins(0, 0, 0, 0)
+ layout.setSpacing(4)
+
+ self._edit = QLineEdit(self)
+
+ if not self._as_base64:
+ if initial:
+ self._edit.setText(initial)
+ else:
+ display = ""
+ if initial:
+ if "::" in initial:
+ fname, _ = initial.split("::", 1)
+ display = fname
+ else:
+ display = "(embedded)"
+ self._edit.setText(display)
+
+ self._btn = QPushButton("...", self)
+ self._btn.clicked.connect(self._browse)
+
+ layout.addWidget(self._edit)
+ layout.addWidget(self._btn)
+
+ def _browse(self):
+ # Start from configured directory, or from current value's folder
+ start_dir = self._directory or os.path.dirname(self._edit.text() or "") or ""
+ path, _ = QFileDialog.getOpenFileName(
+ self,
+ self._caption,
+ start_dir,
+ self._filter,
+ )
+ if path:
+ self._edit.setText(path)
+ if self._on_changed:
+ self._on_changed(path)
+
+ def value(self) -> str:
+ """
+ Return either the raw path (default) or a 'filename::base64' value
+ when as_base64=True.
+ """
+ path_or_name = self._edit.text().strip()
+
+ if not self._as_base64:
+ # Normal mode – return the path from the line edit
+ return path_or_name
+
+ # Base64 mode
+ # If the text points to an actual file, re-read and encode it
+ if path_or_name and os.path.isfile(path_or_name):
+ try:
+ with open(path_or_name, "rb") as f:
+ data = f.read()
+ b64 = base64.b64encode(data).decode("ascii")
+ fname = os.path.basename(path_or_name)
+ self._stored_value = f"{fname}::{b64}"
+ except Exception as e:
+ print(f"[FileInput] Failed to read '{path_or_name}' for base64: {e}")
+
+ # If it's not a real file path, just return the last stored value
+ return self._stored_value or ""
+
+ def setValue(self, value: str):
+ """
+ Update both the stored value and the visible text.
+ value is expected to be either:
+ - a path (normal mode), or
+ - 'filename::base64' / raw base64 (as_base64=True).
+ """
+ if not self._as_base64:
+ self._edit.setText(value or "")
+ if self._on_changed:
+ self._on_changed(value)
+ return
+
+ self._stored_value = value or ""
+
+ display = ""
+ if value:
+ if "::" in value:
+ fname, _ = value.split("::", 1)
+ display = fname
+ else:
+ display = "(embedded)"
+
+ self._edit.setText(display)
+
+ # Only call on_changed if we really want config-file reactions etc.
+ if self._on_changed:
+ self._on_changed(value)
+
+class WiFiButton(QPushButton):
+
+ def __init__(self, parent=None, label: str = "Connect"):
+ super().__init__(label, parent)
+ self._ssid: str = ""
+ self._tools = None # lazy-loaded network.Tools instance
+ self.clicked.connect(self._on_click)
+
+ # ------------------------------------------------------------------
+ # Public API
+ # ------------------------------------------------------------------
+
+ def value(self) -> str:
+ """
+ Return the currently selected SSID (or "" if none).
+ """
+ return self._ssid
+
+ def setValue(self, ssid: str):
+ """
+ Set the currently selected SSID and update the button label.
+ """
+ self._ssid = ssid or ""
+ self.setText(self._ssid or "Connect")
+
+ # ------------------------------------------------------------------
+ # Internals
+ # ------------------------------------------------------------------
+
+ def _ensure_tools(self):
+ """
+ Lazily import and instantiate network.tools.Tools.
+ """
+ if self._tools is not None:
+ return self._tools
+
+ try:
+ self._tools = Tools()
+ except Exception as e:
+ print(f"[WiFiButton] Failed to initialize network Tools: {e}")
+ self._tools = None
+
+ return self._tools
+
+ def _on_click(self):
+ tools = self._ensure_tools()
+ if tools is None:
+ MsgBox.show(
+ parent=self,
+ title="Wi-Fi",
+ message="Network tools are not available.",
+ icon="error",
+ )
+ return
+
+ # --- 1) Show a small dialog that immediately displays the scanning message ---
+ dlg = QDialog(self)
+ dlg.setModal(True)
+ dlg.setWindowTitle("Wi-Fi Networks")
+
+ layout = QVBoxLayout(dlg)
+ scanning_label = QLabel("Scanning Access Points...")
+ layout.addWidget(scanning_label)
+
+ dlg.show()
+ QApplication.processEvents()
+
+ # Perform the scan (synchronously)
+ try:
+ networks = tools.scan_ap()
+ except Exception as e:
+ print(f"[WiFiButton] Failed to scan Wi-Fi networks: {e}")
+ networks = []
+
+ # Clear the "Scanning..." UI
+ while layout.count():
+ item = layout.takeAt(0)
+ w = item.widget()
+ if w is not None:
+ w.deleteLater()
+
+ # --- 2) If no networks found, show a simple message + Close button ---
+ if not networks:
+ info = QLabel("No Wi-Fi networks found.")
+ layout.addWidget(info)
+
+ btn_row = QHBoxLayout()
+ btn_row.addStretch(1)
+ close_btn = QPushButton("Close", dlg)
+ close_btn.clicked.connect(dlg.reject)
+ btn_row.addWidget(close_btn)
+ layout.addLayout(btn_row)
+
+ dlg.exec_()
+ dlg.deleteLater()
+ return
+
+ # --- 3) Show list of access points + Connect / Cancel buttons ---
+ list_widget = QListWidget(dlg)
+ list_widget.addItems(networks)
+ layout.addWidget(list_widget)
+
+ btn_row = QHBoxLayout()
+ btn_row.addStretch(1)
+ cancel_btn = QPushButton("Cancel", dlg)
+ ok_btn = QPushButton("Connect", dlg)
+
+ def on_ok():
+ # Require a selection to accept
+ if list_widget.currentItem() is not None:
+ dlg.accept()
+
+ cancel_btn.clicked.connect(dlg.reject)
+ ok_btn.clicked.connect(on_ok)
+
+ btn_row.addWidget(cancel_btn)
+ btn_row.addWidget(ok_btn)
+ layout.addLayout(btn_row)
+
+ def on_item_double_clicked(_item):
+ dlg.accept()
+
+ list_widget.itemDoubleClicked.connect(on_item_double_clicked)
+
+ result = dlg.exec_()
+ selected_ssid = ""
+ if result == QDialog.Accepted and list_widget.currentItem() is not None:
+ selected_ssid = list_widget.currentItem().text().strip()
+
+ dlg.deleteLater()
+ self.setValue(selected_ssid)
+
+class StepIndicator(QWidget):
+ def __init__(self, text: str, parent=None):
+ super().__init__(parent)
+ self._state = 'idle'
+ self.dot = QLabel()
+ self.dot.setObjectName("statusDot")
+ self.dot.setFixedSize(16, 16)
+ self.label = QLabel(text)
+
+ lay = QVBoxLayout(self)
+ lay.setContentsMargins(4, 4, 4, 4)
+ lay.setSpacing(6)
+ dot_wrap = QWidget()
+ dot_wrap.setFixedHeight(20)
+ dot_lay = QHBoxLayout(dot_wrap)
+ dot_lay.setContentsMargins(0,0,0,0)
+ dot_lay.addStretch(1)
+ dot_lay.addWidget(self.dot, 0, Qt.AlignCenter)
+ dot_lay.addStretch(1)
+
+ lay.addWidget(dot_wrap)
+ self.label.setAlignment(Qt.AlignCenter)
+ lay.addWidget(self.label)
+ self._apply_style()
+
+ def set_state(self, state: str):
+ self._state = state
+ self._apply_style()
+
+ def _apply_style(self):
+ colors = {
+ 'idle': '#A0A4A8', # grey
+ 'running': '#F5C542', # amber
+ 'ok': '#2FB344', # green
+ 'fail': '#E03131', # red
+ }
+ c = colors.get(self._state, '#A0A4A8')
+ self.dot.setStyleSheet(f"background:{c}; border-radius:7px; border:1px solid rgba(0,0,0,.25);")
+
+class Form:
+
+ @staticmethod
+ def text(text: str = "", placeholder: str = "") -> QLineEdit:
+ w = QLineEdit()
+ if text:
+ w.setText(text)
+ if placeholder:
+ w.setPlaceholderText(placeholder)
+ return w
+
+ @staticmethod
+ def password(text: str = "", placeholder: str = "") -> QLineEdit:
+ w = QLineEdit()
+ w.setEchoMode(QLineEdit.Password)
+ if text:
+ w.setText(text)
+ if placeholder:
+ w.setPlaceholderText(placeholder)
+ return w
+
+ @staticmethod
+ def checkbox(checked: bool = False) -> QCheckBox:
+ w = QCheckBox()
+ w.setChecked(checked)
+ return w
+
+ @staticmethod
+ def spin(value: int = 0, minimum: int = 0, maximum: int = 65535) -> QSpinBox:
+ w = QSpinBox()
+ w.setRange(minimum, maximum)
+ w.setValue(value)
+ return w
+
+ @staticmethod
+ def number(value: int = 0, minimum: int = 0, maximum: int = 65535) -> QSpinBox:
+ w = QSpinBox()
+ w.setRange(minimum, maximum)
+ w.setValue(value)
+ return w
+
+ @staticmethod
+ def integer(value: int = 0, minimum: int = 0, maximum: int = 65535) -> QSpinBox:
+ w = QSpinBox()
+ w.setRange(minimum, maximum)
+ w.setValue(value)
+ return w
+
+ @staticmethod
+ def select(items: Iterable[str], current: Optional[str] = None) -> QComboBox:
+ cb = QComboBox()
+ popup = QListView()
+ popup.setFrameShape(QFrame.NoFrame)
+ popup.setFrameShadow(QFrame.Plain)
+ cb.setView(popup)
+ vals: List[str] = list(items)
+ cb.addItems(vals)
+ if current is not None:
+ idx = cb.findText(str(current))
+ if idx >= 0:
+ cb.setCurrentIndex(idx)
+ return cb
+
+ @staticmethod
+ def color(initial: str = "#265162") -> ColorButton:
+ return ColorButton(initial)
+
+ @staticmethod
+ def button(label: str, action: Callable | None, icon: str = "") -> QPushButton:
+ helper = Helper()
+ btn = QPushButton(label)
+ if icon:
+ icon_path = helper.get_path(f"core/icons/{icon}.svg")
+ if icon_path:
+ if helper.file_exists(icon_path):
+ renderer = QSvgRenderer(icon_path)
+ pixmap = QPixmap(18, 18)
+ pixmap.fill(Qt.transparent)
+ painter = QPainter(pixmap)
+ renderer.render(painter)
+ painter.end()
+ icon = QIcon(pixmap)
+ btn.setIcon(icon)
+ btn.setIconSize(pixmap.size())
+ if label:
+ btn.setText("\u2002" + label)
+ if action:
+ btn.clicked.connect(action)
+ return btn
+
+ @staticmethod
+ def picture(initial: Optional[str] = None) -> PictureButton:
+ return PictureButton(initial=initial)
+
+ @staticmethod
+ def file(
+ initial: str = "",
+ caption: str = "Select File",
+ directory: str = "",
+ filter: str = "All Files (*)",
+ as_base64: bool = False,
+ on_changed: Optional[Callable[[str], None]] = None,
+ ) -> FileInput:
+ return FileInput(
+ initial=initial,
+ caption=caption,
+ directory=directory,
+ filter=filter,
+ as_base64=as_base64,
+ on_changed=on_changed,
+ )
+
+ @staticmethod
+ def wifi(current: str = "") -> WiFiButton:
+ btn = WiFiButton()
+ if current:
+ btn.setValue(current)
+ return btn
diff --git a/dist/macos/Replicator.app/Contents/Resources/core/update.sh b/dist/macos/Replicator.app/Contents/Resources/core/update.sh
new file mode 100755
index 00000000..86308c99
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/core/update.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+BRANCH=$(git config -f .gitmodules submodule.src/core.branch)
+cd src/core && git checkout $BRANCH && git pull && cd ../..
+git add src/core
+git commit -m "General: Update submodule corePY" || echo "No changes to commit"
+git push
diff --git a/dist/macos/Replicator.app/Contents/Resources/icon.icns b/dist/macos/Replicator.app/Contents/Resources/icon.icns
new file mode 100644
index 00000000..e1a2c0f7
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Resources/icon.icns differ
diff --git a/dist/macos/Replicator.app/Contents/Resources/icons/icon.icns b/dist/macos/Replicator.app/Contents/Resources/icons/icon.icns
new file mode 100644
index 00000000..e1a2c0f7
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Resources/icons/icon.icns differ
diff --git a/dist/macos/Replicator.app/Contents/Resources/icons/icon.png b/dist/macos/Replicator.app/Contents/Resources/icons/icon.png
new file mode 100644
index 00000000..e683fe20
Binary files /dev/null and b/dist/macos/Replicator.app/Contents/Resources/icons/icon.png differ
diff --git a/dist/macos/Replicator.app/Contents/Resources/icons/icon.svg b/dist/macos/Replicator.app/Contents/Resources/icons/icon.svg
new file mode 100644
index 00000000..d29f6262
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/icons/icon.svg
@@ -0,0 +1,51 @@
+
diff --git a/dist/macos/Replicator.app/Contents/Resources/libcrypto.3.dylib b/dist/macos/Replicator.app/Contents/Resources/libcrypto.3.dylib
new file mode 120000
index 00000000..cc14f152
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/libcrypto.3.dylib
@@ -0,0 +1 @@
+../Frameworks/libcrypto.3.dylib
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/liblzma.5.dylib b/dist/macos/Replicator.app/Contents/Resources/liblzma.5.dylib
new file mode 120000
index 00000000..fbed50fa
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/liblzma.5.dylib
@@ -0,0 +1 @@
+../Frameworks/liblzma.5.dylib
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/libmpdec.4.dylib b/dist/macos/Replicator.app/Contents/Resources/libmpdec.4.dylib
new file mode 120000
index 00000000..21d144b1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/libmpdec.4.dylib
@@ -0,0 +1 @@
+../Frameworks/libmpdec.4.dylib
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/libsqlite3.dylib b/dist/macos/Replicator.app/Contents/Resources/libsqlite3.dylib
new file mode 120000
index 00000000..d1e11ce8
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/libsqlite3.dylib
@@ -0,0 +1 @@
+../Frameworks/libsqlite3.dylib
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/libssl.3.dylib b/dist/macos/Replicator.app/Contents/Resources/libssl.3.dylib
new file mode 120000
index 00000000..4651492c
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/libssl.3.dylib
@@ -0,0 +1 @@
+../Frameworks/libssl.3.dylib
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/python3.11 b/dist/macos/Replicator.app/Contents/Resources/python3.11
new file mode 120000
index 00000000..68fdb5d2
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/python3.11
@@ -0,0 +1 @@
+../Frameworks/python3.11
\ No newline at end of file
diff --git a/dist/macos/Replicator.app/Contents/Resources/replicator/__init__.py b/dist/macos/Replicator.app/Contents/Resources/replicator/__init__.py
new file mode 100644
index 00000000..7c3a9253
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/replicator/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python3
+# src/core/__init__.py
+
+from .replicator import Replicator
+from .ui import JobDialog, ScheduleDialog
+from .job import Schedule, Endpoint, Job, JobRunResult, JobStore
+from .migration import Migration
+from .mount import RemoteMountError, MountedEndpoint, mount_endpoint_if_remote
+
+__version__ = "1.0.0"
+
+__all__ = [
+ "Replicator",
+ "JobDialog",
+ "ScheduleDialog",
+ "Schedule",
+ "Endpoint",
+ "Job",
+ "JobRunResult",
+ "JobStore",
+ "Migration",
+ "RemoteMountError",
+ "MountedEndpoint",
+ "mount_endpoint_if_remote",
+]
diff --git a/dist/macos/Replicator.app/Contents/Resources/replicator/job.py b/dist/macos/Replicator.app/Contents/Resources/replicator/job.py
new file mode 100644
index 00000000..56f102e6
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/replicator/job.py
@@ -0,0 +1,766 @@
+#!/usr/bin/env python3
+# src/replicator/job.py
+
+from __future__ import annotations
+import json
+from dataclasses import dataclass, field
+from datetime import datetime, timedelta, timezone, time
+from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Tuple, Union
+from urllib.parse import urlparse
+from .mount import RemoteMountError, MountedEndpoint, mount_endpoint_if_remote
+
+try:
+ # corePY SQLite wrapper (preferred)
+ from core.database.sqlite import SQLite # type: ignore
+except Exception: # pragma: no cover
+ SQLite = None # type: ignore
+
+JsonDict = Dict[str, Any]
+
+# ---------------------------------------------------------------------------
+# Data models
+# ---------------------------------------------------------------------------
+
+@dataclass(frozen=True)
+class Endpoint:
+ type: str = "local"
+ location: str = ""
+ auth: JsonDict = field(default_factory=dict)
+
+ def validate(self, role: str) -> List[str]:
+ errs: List[str] = []
+ if not self.type:
+ errs.append(f"{role} endpoint type is required.")
+ if not self.location:
+ errs.append(f"{role} endpoint location is required.")
+ t = (self.type or "").lower()
+ # SMB port validation removed
+ return errs
+
+ def to_db_fields(self, role: str) -> JsonDict:
+ t = (self.type or "local").lower()
+ auth = dict(self.auth or {})
+
+ guest: int = 1
+ username: Optional[str] = None
+ password: Optional[str] = None
+ options: JsonDict = {}
+
+ if t == "local":
+ pass
+ elif t == "smb":
+ guest = 1 if bool(auth.get("guest", True)) else 0
+ username = auth.get("username") or None
+ password = auth.get("password") or None
+ # Optional SMB domain
+ options["domain"] = auth.get("domain")
+ # Optional rclone args (applies to all remote types)
+ if isinstance(auth.get("rcloneArgs"), list):
+ options["rcloneArgs"] = auth.get("rcloneArgs")
+
+ known_keys = {"guest", "username", "password", "domain"}
+ for k, v in auth.items():
+ if k not in known_keys:
+ options[k] = v
+
+ return {
+ "role": role,
+ "type": t,
+ "location": self.location,
+ "port": None,
+ "guest": guest,
+ "username": username,
+ "password": password,
+ "options": json.dumps(options) if options else None,
+ }
+
+ @staticmethod
+ def from_db_row(row: Mapping[str, Any]) -> "Endpoint":
+ t = (row.get("type") or "local").lower()
+ auth: JsonDict = {}
+
+ if t == "local":
+ auth = {}
+ elif t == "smb":
+ auth = {
+ "guest": bool(row.get("guest", 1)),
+ "username": row.get("username") or "",
+ "password": row.get("password") or "",
+ }
+ # domain is stored in options JSON when present
+
+ opt = row.get("options")
+ if opt:
+ try:
+ extra = json.loads(opt)
+ if isinstance(extra, dict):
+ auth.update(extra)
+ except Exception:
+ pass
+
+ return Endpoint(type=t, location=row.get("location") or "", auth=auth)
+
+@dataclass
+class Schedule:
+ enabled: bool = False
+ intervalSeconds: int = 3600
+ windows: Dict[str, Any] = field(default_factory=dict)
+
+ def to_dict(self) -> Dict[str, Any]:
+ return {
+ "enabled": bool(self.enabled),
+ "intervalSeconds": int(self.intervalSeconds or 0),
+ "windows": self.windows if isinstance(self.windows, dict) else {},
+ }
+
+ @staticmethod
+ def from_dict(d: Optional[Dict[str, Any]]) -> "Schedule":
+ d = d or {}
+ enabled = bool(d.get("enabled", False))
+ # intervalSeconds can be on the schedule itself, or in per-day window objects
+ try:
+ interval_s = int(d.get("intervalSeconds") or 0)
+ except Exception:
+ interval_s = 0
+ windows = d.get("windows") if isinstance(d.get("windows"), dict) else {}
+ if interval_s <= 0:
+ # best-effort: derive from first window on any day
+ try:
+ for _k, _v in windows.items():
+ if isinstance(_v, list) and _v and isinstance(_v[0], dict):
+ v = _v[0].get("intervalSeconds")
+ if v is not None:
+ interval_s = int(v or 0)
+ break
+ except Exception:
+ pass
+ if interval_s <= 0:
+ interval_s = 3600
+ return Schedule(enabled=enabled, intervalSeconds=interval_s, windows=windows)
+
+ def validate(self) -> List[str]:
+ errs: List[str] = []
+ if self.enabled:
+ try:
+ iv = int(self.intervalSeconds or 0)
+ except Exception:
+ iv = 0
+ if iv <= 0:
+ errs.append("Schedule intervalSeconds must be > 0 when schedule is enabled.")
+ # windows is optional; if provided, validate structure best-effort
+ if self.windows and not isinstance(self.windows, dict):
+ errs.append("Schedule windows must be an object/dict.")
+ return errs
+
+ def _parse_hhmm(val: Any) -> Optional[Tuple[int, int]]:
+ try:
+ parts = str(val).strip().split(":")
+ if len(parts) != 2:
+ return None
+ hh = int(parts[0]); mm = int(parts[1])
+ if hh < 0 or hh > 23 or mm < 0 or mm > 59:
+ return None
+ return (hh, mm)
+ except Exception:
+ return None
+
+ try:
+ for _day, arr in (self.windows or {}).items():
+ if not isinstance(arr, list):
+ errs.append("Schedule windows entries must be lists.")
+ continue
+ for w in arr:
+ if not isinstance(w, dict):
+ errs.append("Schedule window must be an object.")
+ continue
+ if _parse_hhmm(w.get("start")) is None:
+ errs.append("Schedule window start must be HH:MM.")
+ if _parse_hhmm(w.get("end")) is None:
+ errs.append("Schedule window end must be HH:MM.")
+ if "intervalSeconds" in w:
+ try:
+ if int(w.get("intervalSeconds") or 0) <= 0:
+ errs.append("Schedule window intervalSeconds must be > 0 when provided.")
+ except Exception:
+ errs.append("Schedule window intervalSeconds must be an integer.")
+ except Exception:
+ # best-effort validation only
+ pass
+
+ return errs
+
+ def _window_allows_now_local(self, now_local: datetime) -> bool:
+ # If windows is missing/not-a-dict, treat as unrestricted (legacy).
+ if self.windows is None or not isinstance(self.windows, dict):
+ return True
+ # If windows is an empty dict, treat as "no service window configured" => not allowed.
+ if not self.windows:
+ return False
+
+ wd = now_local.weekday() # 0..6 (Mon..Sun)
+ day_windows = self.windows.get(str(wd)) or self.windows.get(wd)
+ if not isinstance(day_windows, list) or not day_windows:
+ return False
+
+ tnow = now_local.time().replace(tzinfo=None)
+
+ def _parse_hhmm(val: Any) -> Optional[Tuple[int, int]]:
+ try:
+ parts = str(val).strip().split(":")
+ if len(parts) != 2:
+ return None
+ hh = int(parts[0]); mm = int(parts[1])
+ if hh < 0 or hh > 23 or mm < 0 or mm > 59:
+ return None
+ return (hh, mm)
+ except Exception:
+ return None
+
+ for w in day_windows:
+ if not isinstance(w, dict):
+ continue
+ ps = _parse_hhmm(w.get("start"))
+ pe = _parse_hhmm(w.get("end"))
+ if ps is None or pe is None:
+ continue
+ sh, sm = ps; eh, em = pe
+ ts = datetime(2000, 1, 1, sh, sm).time()
+ te = datetime(2000, 1, 1, eh, em).time()
+
+ if ts <= te:
+ if ts <= tnow <= te:
+ return True
+ else:
+ # overnight window (e.g. 22:00-06:00)
+ if tnow >= ts or tnow <= te:
+ return True
+
+ return False
+
+ def interval_seconds_for_now(self, now: Optional[datetime] = None) -> int:
+ now_local = (now or datetime.now(timezone.utc)).astimezone()
+ # Priority:
+ # 1) per-day window intervalSeconds (first window entry)
+ # 2) schedule.intervalSeconds
+ try:
+ wd = now_local.weekday()
+ day = None
+ if isinstance(self.windows, dict):
+ day = self.windows.get(str(wd)) or self.windows.get(wd)
+ if isinstance(day, list) and day and isinstance(day[0], dict):
+ v = day[0].get("intervalSeconds")
+ if v is not None:
+ iv = int(v or 0)
+ if iv > 0:
+ return iv
+ except Exception:
+ pass
+
+ try:
+ iv = int(self.intervalSeconds or 0)
+ return iv if iv > 0 else 3600
+ except Exception:
+ return 3600
+
+ def should_run_now(self, now: Optional[datetime] = None) -> bool:
+ if not self.enabled:
+ return False
+ now_local = (now or datetime.now(timezone.utc)).astimezone()
+ return self._window_allows_now_local(now_local)
+
+ def next_run_at(self, now: Optional[datetime] = None) -> Optional[datetime]:
+ # This domain object doesn't store lastScheduledRunAt; caller should decide cadence.
+ # We return "now + interval" if windows allow now; otherwise the next allowed window start.
+ if not self.enabled:
+ return None
+
+ now_utc = now or datetime.now(timezone.utc)
+ now_local = now_utc.astimezone()
+
+ if self._window_allows_now_local(now_local):
+ return now_utc + timedelta(seconds=int(self.interval_seconds_for_now(now_utc)))
+
+ # Find next allowed window start within the next 7 days (best-effort).
+ if not self.windows or not isinstance(self.windows, dict):
+ return now_utc + timedelta(seconds=int(self.interval_seconds_for_now(now_utc)))
+
+ def _parse_hhmm(val: Any) -> Optional[Tuple[int, int]]:
+ try:
+ parts = str(val).strip().split(":")
+ if len(parts) != 2:
+ return None
+ hh = int(parts[0]); mm = int(parts[1])
+ if hh < 0 or hh > 23 or mm < 0 or mm > 59:
+ return None
+ return (hh, mm)
+ except Exception:
+ return None
+
+ base_local = now_local.replace(second=0, microsecond=0)
+ for add_days in range(0, 8):
+ day_local = base_local + timedelta(days=add_days)
+ wd = day_local.weekday()
+ day_windows = self.windows.get(str(wd)) or self.windows.get(wd)
+ if not isinstance(day_windows, list) or not day_windows:
+ continue
+ # use the first window as the start candidate (UI currently writes a single window per day)
+ w0 = day_windows[0] if isinstance(day_windows[0], dict) else None
+ if not w0:
+ continue
+ ps = _parse_hhmm(w0.get("start"))
+ if ps is None:
+ continue
+ sh, sm = ps
+ candidate_local = day_local.replace(hour=sh, minute=sm)
+ candidate_utc = candidate_local.astimezone(timezone.utc)
+ if candidate_utc > now_utc:
+ return candidate_utc
+
+ return None
+
+@dataclass
+class JobRunResult:
+ ok: bool
+ started_at: str
+ ended_at: str
+ result: str
+ message: Optional[str] = None
+ stats: JsonDict = field(default_factory=dict)
+
+@dataclass
+class Job:
+ id: Optional[int] = None
+ name: str = ""
+ enabled: bool = True
+ mode: str = "mirror"
+ direction: str = "unidirectional"
+ preserveMetadata: bool = True
+ conflictPolicy: str = "newest"
+ pairId: Optional[str] = None
+
+ sourceEndpoint: Endpoint = field(default_factory=Endpoint)
+ targetEndpoint: Endpoint = field(default_factory=Endpoint)
+
+ schedule: Schedule = field(default_factory=Schedule)
+
+ lastRun: Optional[str] = None
+ lastResult: Optional[str] = None
+ lastError: Optional[str] = None
+
+ def validate(self) -> List[str]:
+ errs: List[str] = []
+ if not self.name.strip():
+ errs.append("Job name is required.")
+ if not self.mode:
+ errs.append("Job mode is required.")
+ if not self.direction:
+ errs.append("Job direction is required.")
+
+ errs.extend(self.sourceEndpoint.validate("source"))
+ errs.extend(self.targetEndpoint.validate("target"))
+ errs.extend(self.schedule.validate())
+
+ d = (self.direction or "").lower()
+ if d not in ("unidirectional", "bidirectional"):
+ errs.append("Job direction must be 'unidirectional' or 'bidirectional'.")
+
+ cp = (self.conflictPolicy or "newest").lower()
+ if cp not in ("newest", "keepa", "a", "keepb", "b"):
+ errs.append("conflictPolicy must be one of: newest, keepA/a, keepB/b.")
+
+ return errs
+
+ def should_run_now(self, now: Optional[datetime] = None) -> bool:
+ if not self.enabled:
+ return False
+ return self.schedule.should_run_now(now)
+
+ def next_run_at(self, now: Optional[datetime] = None) -> Optional[datetime]:
+ if not self.enabled:
+ return None
+ return self.schedule.next_run_at(now)
+
+ def run(
+ self,
+ *,
+ now: Optional[datetime] = None,
+ copy_func: Optional[Callable[..., bool]] = None,
+ bidirectional_func: Optional[Callable[..., Tuple[bool, JsonDict]]] = None,
+ logger: Optional[Callable[[str, str], None]] = None,
+ ) -> JobRunResult:
+ now_dt = now or datetime.now(timezone.utc)
+ started_at = now_dt.isoformat()
+
+ def _log(msg: str, level: str = "info") -> None:
+ if logger:
+ try:
+ logger(msg, level)
+ except Exception:
+ pass
+
+ errs = self.validate()
+ if errs:
+ msg = "; ".join(errs)
+ ended = datetime.now(timezone.utc).isoformat()
+ self.lastRun = ended
+ self.lastResult = "fail"
+ self.lastError = msg
+ return JobRunResult(
+ ok=False,
+ started_at=started_at,
+ ended_at=ended,
+ result="fail",
+ message=msg,
+ stats={},
+ )
+
+ if not self.enabled:
+ ended = datetime.now(timezone.utc).isoformat()
+ return JobRunResult(
+ ok=True,
+ started_at=started_at,
+ ended_at=ended,
+ result="ok",
+ message="Job disabled; skipped.",
+ stats={},
+ )
+
+ preserve = bool(self.preserveMetadata)
+ allow_del = (str(self.mode or "").lower() == "mirror")
+
+ # Resolve endpoints (mount SMB endpoints to local paths for the duration of the run)
+ mounted: List[MountedEndpoint] = []
+ ok: bool = False
+ stats: JsonDict = {}
+
+ # Define upfront so logging/error handling can't reference undefined vars.
+ src = ""
+ dst = ""
+
+ try:
+ # Mount endpoints inside the try so a failure mounting target still cleans up source.
+ src_m = mount_endpoint_if_remote(self.sourceEndpoint, self.id, "source", logger=logger)
+ mounted.append(src_m)
+ dst_m = mount_endpoint_if_remote(self.targetEndpoint, self.id, "target", logger=logger)
+ mounted.append(dst_m)
+
+ src = src_m.local_path
+ dst = dst_m.local_path
+
+ if (self.direction or "").lower() == "bidirectional":
+ if not bidirectional_func:
+ raise NotImplementedError("Bidirectional engine not provided.")
+
+ # IMPORTANT:
+ # The bidirectional engine historically enforced local endpoints only by checking
+ # endpoint types. Since remote endpoints are mounted to local paths for the duration
+ # of the run, we provide a local-view of this job to the engine.
+ job_local_view = Job(
+ id=self.id,
+ name=self.name,
+ enabled=self.enabled,
+ mode=self.mode,
+ direction=self.direction,
+ preserveMetadata=self.preserveMetadata,
+ conflictPolicy=self.conflictPolicy,
+ pairId=self.pairId,
+ sourceEndpoint=Endpoint(type="local", location=src, auth={}),
+ targetEndpoint=Endpoint(type="local", location=dst, auth={}),
+ schedule=self.schedule,
+ lastRun=self.lastRun,
+ lastResult=self.lastResult,
+ lastError=self.lastError,
+ )
+
+ _log(f"[Job] Running bidirectional job '{self.name}': {src} <-> {dst}", "info")
+ ok, stats = bidirectional_func(job_local_view, None)
+ else:
+ if not copy_func:
+ raise NotImplementedError("Copy function not provided.")
+ _log(f"[Job] Running unidirectional job '{self.name}': {src} -> {dst}", "info")
+ ok = bool(copy_func(src, dst, preserve_metadata=preserve, allow_deletion=allow_del))
+ except NotImplementedError as e:
+ ok = False
+ self.lastError = str(e)
+ _log(f"[Job] Not supported: {e}", "error")
+ except Exception as e:
+ ok = False
+ self.lastError = str(e)
+ _log(f"[Job] Execution failed: {e}", "error")
+ finally:
+ # Always unmount remote endpoints
+ for m in reversed(mounted):
+ try:
+ m.cleanup()
+ except Exception:
+ pass
+
+ ended_at = datetime.now(timezone.utc).isoformat()
+ self.lastRun = ended_at
+ self.lastResult = "ok" if ok else "fail"
+ if ok:
+ self.lastError = None
+ else:
+ self.lastError = self.lastError or "Failed"
+
+ return JobRunResult(
+ ok=ok,
+ started_at=started_at,
+ ended_at=ended_at,
+ result="ok" if ok else "fail",
+ message=None if ok else self.lastError,
+ stats=stats or {},
+ )
+
+ def to_row_dicts(self) -> Dict[str, Any]:
+ job_row = {
+ "id": self.id,
+ "name": self.name,
+ "enabled": 1 if self.enabled else 0,
+ "mode": self.mode or "mirror",
+ "direction": self.direction or "unidirectional",
+ # "allowDeletion" entry removed; will be derived at write time
+ "preserveMetadata": 1 if self.preserveMetadata else 0,
+ "pairId": self.pairId,
+ "conflictPolicy": self.conflictPolicy or "newest",
+ "lastRun": self.lastRun,
+ "lastResult": self.lastResult,
+ "lastError": self.lastError,
+ }
+
+ endpoint_rows = [
+ self.sourceEndpoint.to_db_fields("source"),
+ self.targetEndpoint.to_db_fields("target"),
+ ]
+
+ sched_row = {
+ "enabled": 1 if self.schedule and self.schedule.enabled else 0,
+ "intervalSeconds": int(self.schedule.intervalSeconds if self.schedule else 3600),
+ "windows": json.dumps(self.schedule.windows if self.schedule and isinstance(self.schedule.windows, dict) else {}),
+ }
+
+ return {
+ "job_row": job_row,
+ "endpoint_rows": endpoint_rows,
+ "schedule_row": sched_row,
+ }
+
+ @staticmethod
+ def from_db_rows(
+ job_row: Mapping[str, Any],
+ endpoint_rows: Iterable[Mapping[str, Any]],
+ schedule_row: Optional[Mapping[str, Any]] = None,
+ ) -> "Job":
+ j = Job(
+ id=int(job_row.get("id")) if job_row.get("id") is not None else None,
+ name=str(job_row.get("name") or ""),
+ enabled=bool(job_row.get("enabled", 1)),
+ mode=str(job_row.get("mode") or "mirror"),
+ direction=str(job_row.get("direction") or "unidirectional"),
+ preserveMetadata=bool(job_row.get("preserveMetadata", 1)),
+ conflictPolicy=str(job_row.get("conflictPolicy") or "newest"),
+ pairId=job_row.get("pairId"),
+ lastRun=job_row.get("lastRun"),
+ lastResult=job_row.get("lastResult"),
+ lastError=job_row.get("lastError"),
+ )
+
+ src = None
+ tgt = None
+ for r in endpoint_rows:
+ role = (r.get("role") or "").lower()
+ ep = Endpoint.from_db_row(r)
+ if role == "source":
+ src = ep
+ elif role == "target":
+ tgt = ep
+
+ j.sourceEndpoint = src or Endpoint()
+ j.targetEndpoint = tgt or Endpoint()
+
+ if schedule_row:
+ # Read intervalSeconds and windows as new model, fallback as needed
+ try:
+ interval_s = int(schedule_row.get("intervalSeconds") or 0)
+ except Exception:
+ interval_s = 0
+ # Remove legacy everyMinutes fallback
+ if interval_s <= 0:
+ interval_s = 3600
+ windows = {}
+ try:
+ raw = schedule_row.get("windows")
+ if raw:
+ windows = json.loads(raw) if isinstance(raw, str) else (raw if isinstance(raw, dict) else {})
+ except Exception:
+ windows = {}
+ j.schedule = Schedule(
+ enabled=bool(schedule_row.get("enabled", 1)),
+ intervalSeconds=int(interval_s),
+ windows=windows,
+ )
+ else:
+ j.schedule = Schedule()
+
+ return j
+
+
+
+# ---------------------------------------------------------------------------
+# JobStore (DB persistence)
+# ---------------------------------------------------------------------------
+
+class JobStore:
+ """SQLite persistence for Job domain objects (UI-agnostic)."""
+
+ def __init__(self, db: Any):
+ self._db = db
+ if SQLite is None or not isinstance(self._db, SQLite):
+ raise TypeError("JobStore requires core.database.sqlite.SQLite")
+
+ def _is_core_sqlite(self) -> bool:
+ return True
+
+ def _select(self, table: str, where: Optional[str] = None, params: Any = None, *, order_by: Optional[str] = None) -> List[Dict[str, Any]]:
+ return self._db.select(table, where=where, params=params, order_by=order_by) # type: ignore[union-attr]
+
+ def _one(self, table: str, where: str, params: Any) -> Optional[Dict[str, Any]]:
+ return self._db.one(f'SELECT * FROM "{table}" WHERE {where} LIMIT 1;', params) # type: ignore[union-attr]
+
+ def _insert(self, table: str, data: Dict[str, Any]) -> int:
+ return int(self._db.insert(table, data)) # type: ignore[union-attr]
+
+ def _update(self, table: str, data: Dict[str, Any], where: str, params: Any) -> None:
+ if not isinstance(params, dict):
+ raise ValueError("JobStore._update requires dict params")
+ self._db.update(table, data, where, params) # type: ignore[union-attr]
+
+ def _upsert(self, table: str, data: Dict[str, Any], conflict_columns: List[str], update_columns: Optional[List[str]] = None) -> None:
+ self._db.upsert(table, data, conflict_columns, update_columns) # type: ignore[union-attr]
+
+ def _delete(self, table: str, where: str, params: Any) -> None:
+ self._db.delete(table, where, params) # type: ignore[union-attr]
+
+ def _transaction(self):
+ return self._db.transaction() # type: ignore[union-attr]
+
+ # -------------------------------
+ # Reads
+ # -------------------------------
+
+ def fetch_all(self) -> List[Job]:
+ job_rows = self._select("jobs", order_by="id ASC")
+ jobs: List[Job] = []
+ for jr in job_rows:
+ jid = int(jr.get("id") or 0)
+ ep_rows = self.fetch_endpoints_rows(jid)
+ sched_row = self.fetch_schedule_row(jid)
+ jobs.append(Job.from_db_rows(jr, ep_rows, sched_row))
+ return jobs
+
+ def fetch_by_id(self, job_id: int) -> Optional[Job]:
+ jr = self._one("jobs", "id = ?", (int(job_id),))
+ if not jr:
+ return None
+ ep_rows = self.fetch_endpoints_rows(int(job_id))
+ sched_row = self.fetch_schedule_row(int(job_id))
+ return Job.from_db_rows(jr, ep_rows, sched_row)
+
+ def fetch_endpoints_rows(self, job_id: int) -> List[Dict[str, Any]]:
+ return self._select("endpoints", "jobId = ?", (int(job_id),))
+
+ def fetch_schedule_row(self, job_id: int) -> Optional[Dict[str, Any]]:
+ return self._one("schedule", "jobId = ?", (int(job_id),))
+
+ # -------------------------------
+ # Writes
+ # -------------------------------
+
+ def upsert(self, job: Job) -> int:
+ row_dicts = job.to_row_dicts()
+ job_row: Dict[str, Any] = row_dicts["job_row"]
+ endpoint_rows: List[Dict[str, Any]] = row_dicts["endpoint_rows"]
+ schedule_row: Dict[str, Any] = row_dicts["schedule_row"]
+
+ with self._transaction():
+ # --- jobs ---
+ data = {
+ "name": job_row.get("name"),
+ "enabled": int(job_row.get("enabled") or 0),
+ "mode": job_row.get("mode") or "mirror",
+ "direction": job_row.get("direction") or "unidirectional",
+ # mode is the source of truth for deletion behavior
+ "allowDeletion": 1 if (str(job_row.get("mode") or "mirror").lower() == "mirror") else 0,
+ "preserveMetadata": int(job_row.get("preserveMetadata") or 0),
+ "pairId": job_row.get("pairId"),
+ "conflictPolicy": job_row.get("conflictPolicy") or "newest",
+ "lastRun": job_row.get("lastRun"),
+ "lastResult": job_row.get("lastResult"),
+ "lastError": job_row.get("lastError"),
+ }
+
+ if job.id:
+ self._update("jobs", data, "id = :id", {"id": int(job.id)})
+ job_id = int(job.id)
+ else:
+ job_id = self._insert("jobs", data)
+ job.id = job_id
+
+ # --- endpoints (unique: jobId+role) ---
+ for ep in endpoint_rows:
+ role = ep.get("role")
+ if role not in ("source", "target"):
+ continue
+ ep_data = {
+ "jobId": job_id,
+ "role": role,
+ "type": ep.get("type"),
+ "location": ep.get("location"),
+ "port": ep.get("port"),
+ "guest": ep.get("guest"),
+ "username": ep.get("username"),
+ "password": ep.get("password"),
+ "options": ep.get("options"),
+ }
+ self._upsert(
+ "endpoints",
+ ep_data,
+ ["jobId", "role"],
+ update_columns=["type", "location", "port", "guest", "username", "password", "options"],
+ )
+
+ # --- schedule (unique: jobId) ---
+ s_data = {
+ "jobId": job_id,
+ "enabled": int(schedule_row.get("enabled") or 0),
+ "intervalSeconds": int(schedule_row.get("intervalSeconds") or 0),
+ "windows": schedule_row.get("windows"),
+ }
+ self._upsert(
+ "schedule",
+ s_data,
+ ["jobId"],
+ update_columns=["enabled", "intervalSeconds", "windows"],
+ )
+
+ return int(job.id or 0)
+
+ def delete(self, job_id: int) -> None:
+ jid = int(job_id)
+ with self._transaction():
+ # Delete dependent rows first to avoid orphan data (and to work even without FK cascades)
+ try:
+ self._delete("endpoints", "jobId = ?", (jid,))
+ except Exception:
+ pass
+ try:
+ self._delete("schedule", "jobId = ?", (jid,))
+ except Exception:
+ pass
+ # These tables may exist depending on enabled features; delete best-effort.
+ for tbl in ("runs", "conflicts", "file_state"):
+ try:
+ self._delete(tbl, "jobId = ?", (jid,))
+ except Exception:
+ pass
+ self._delete("jobs", "id = ?", (jid,))
diff --git a/dist/macos/Replicator.app/Contents/Resources/replicator/migration.py b/dist/macos/Replicator.app/Contents/Resources/replicator/migration.py
new file mode 100644
index 00000000..e4cddada
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/replicator/migration.py
@@ -0,0 +1,280 @@
+#!/usr/bin/env python3
+# src/replicator/migration.py
+
+from __future__ import annotations
+
+from typing import List, Optional, Sequence, Tuple
+
+try:
+ from core.database.sqlite import SQLite
+ from core.log import Log
+except ImportError:
+ from database.sqlite import SQLite
+ from log import Log
+
+
+class Migration:
+ """Database schema creation + migrations for Replicator.
+
+ This class intentionally *does not* re-implement a DB wrapper.
+ It relies on corePY's `SQLite` for connections/transactions and only
+ owns Replicator's schema + migration history + small meta KV helpers.
+ """
+
+ def __init__(self, db: SQLite, logger: Optional[Log] = None):
+ self._db = db
+ self._logger = logger
+
+ # ------------------------------------------------------------------
+ # Public API
+ # ------------------------------------------------------------------
+
+ def ensure(self) -> None:
+ """Ensure base tables exist and apply all pending migrations."""
+ self._ensure_schema_migrations_table()
+ self._apply_migrations(self._migrations())
+
+ def get_meta(self, key: str, default: Optional[str] = None) -> Optional[str]:
+ """Read a value from the `meta` table. Returns default if missing."""
+ try:
+ row = self._db.one("SELECT value FROM meta WHERE key = ?", (key,))
+ if not row:
+ return default
+ val = row.get("value")
+ return default if val is None else str(val)
+ except Exception:
+ return default
+
+ def set_meta(self, key: str, value: Optional[str]) -> None:
+ """Upsert a value into the `meta` table."""
+ try:
+ with self._db.transaction():
+ exists = self._db.scalar("SELECT 1 FROM meta WHERE key = ? LIMIT 1", (key,))
+ if exists:
+ self._db.execute(
+ "UPDATE meta SET value = ?, modified = CURRENT_TIMESTAMP WHERE key = ?",
+ (value, key),
+ )
+ else:
+ self._db.execute(
+ "INSERT INTO meta (key, value) VALUES (?, ?)",
+ (key, value),
+ )
+ except Exception:
+ # best effort
+ pass
+
+ # ------------------------------------------------------------------
+ # Internals
+ # ------------------------------------------------------------------
+
+ def _log(self, msg: str, *, level: str = "info", channel: str = "migration") -> None:
+ if self._logger is not None and hasattr(self._logger, "append"):
+ self._logger.append(msg, level=level, channel=channel) # type: ignore[call-arg]
+ else:
+ print(msg)
+
+ def _ensure_schema_migrations_table(self) -> None:
+ self._db.execute(
+ """
+ CREATE TABLE IF NOT EXISTS schema_migrations (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ name TEXT NOT NULL UNIQUE
+ );
+ """
+ )
+
+ def _is_applied(self, name: str) -> bool:
+ r = self._db.scalar(
+ "SELECT 1 FROM schema_migrations WHERE name = ? LIMIT 1;",
+ (name,),
+ )
+ return bool(r)
+
+ def _apply_migrations(self, migrations: Sequence[Tuple[str, Sequence[str]]]) -> None:
+ for name, stmts in migrations:
+ if self._is_applied(name):
+ continue
+
+ try:
+ with self._db.transaction():
+ for stmt in stmts:
+ self._db.execute(stmt)
+ self._db.execute("INSERT INTO schema_migrations (name) VALUES (?)", (name,))
+
+ self._log(f"[Migration] applied {name}", level="debug")
+ except Exception as e:
+ raise RuntimeError(f"Failed to apply migration {name}: {e}")
+
+ def _migrations(self) -> List[Tuple[str, List[str]]]:
+ return [
+ (
+ "0001_init",
+ [
+ # jobs
+ """
+ CREATE TABLE IF NOT EXISTS jobs (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ name TEXT NOT NULL,
+ enabled INTEGER NOT NULL DEFAULT 1,
+ mode TEXT NOT NULL DEFAULT 'mirror',
+ direction TEXT NOT NULL DEFAULT 'unidirectional',
+ allowDeletion INTEGER NOT NULL DEFAULT 0,
+ preserveMetadata INTEGER NOT NULL DEFAULT 1,
+ pairId TEXT NULL,
+ conflictPolicy TEXT NOT NULL DEFAULT 'newest',
+ lastRun TEXT NULL,
+ lastResult TEXT NULL,
+ lastError TEXT NULL
+ );
+ """,
+ "CREATE INDEX IF NOT EXISTS idx_jobs_enabled ON jobs(enabled);",
+
+ # endpoints
+ """
+ CREATE TABLE IF NOT EXISTS endpoints (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ jobId INTEGER NOT NULL,
+ role TEXT NOT NULL,
+ type TEXT NOT NULL,
+ location TEXT NOT NULL,
+ port INTEGER NULL,
+ guest INTEGER NOT NULL DEFAULT 1,
+ username TEXT NULL,
+ password TEXT NULL,
+ options TEXT NULL,
+ FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE
+ );
+ """,
+ "CREATE UNIQUE INDEX IF NOT EXISTS uq_endpoints_job_role ON endpoints(jobId, role);",
+
+ # schedule
+ """
+ CREATE TABLE IF NOT EXISTS schedule (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ jobId INTEGER NOT NULL UNIQUE,
+ -- Schedule is controlled via per-day windows and intervalSeconds.
+ enabled INTEGER NOT NULL DEFAULT 1,
+ intervalSeconds INTEGER NOT NULL DEFAULT 3600,
+ nextRunAt TEXT NULL,
+ lastScheduledRunAt TEXT NULL,
+ windows TEXT NULL,
+ FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE
+ );
+ """,
+
+ # runs
+ """
+ CREATE TABLE IF NOT EXISTS runs (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ jobId INTEGER NOT NULL,
+ startedAt TEXT NOT NULL,
+ endedAt TEXT NULL,
+ result TEXT NOT NULL,
+ message TEXT NULL,
+ stats TEXT NULL,
+ FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE
+ );
+ """,
+ "CREATE INDEX IF NOT EXISTS idx_runs_job_started ON runs(jobId, startedAt);",
+
+ # file_state
+ """
+ CREATE TABLE IF NOT EXISTS file_state (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ jobId INTEGER NOT NULL,
+ side TEXT NOT NULL,
+ relPath TEXT NOT NULL,
+ size INTEGER NOT NULL DEFAULT 0,
+ mtime INTEGER NOT NULL DEFAULT 0,
+ hash TEXT NULL,
+ isDir INTEGER NOT NULL DEFAULT 0,
+ deleted INTEGER NOT NULL DEFAULT 0,
+ deletedAt TEXT NULL,
+ meta TEXT NULL,
+ lastSeenAt TEXT NULL,
+ lastSeenRunId INTEGER NULL,
+ FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE
+ );
+ """,
+ "CREATE UNIQUE INDEX IF NOT EXISTS uq_file_state ON file_state(jobId, side, relPath);",
+
+ # conflicts
+ """
+ CREATE TABLE IF NOT EXISTS conflicts (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ jobId INTEGER NOT NULL,
+ runId INTEGER NULL,
+ relPath TEXT NOT NULL,
+ a_size INTEGER NULL,
+ a_mtime INTEGER NULL,
+ a_hash TEXT NULL,
+ b_size INTEGER NULL,
+ b_mtime INTEGER NULL,
+ b_hash TEXT NULL,
+ status TEXT NOT NULL DEFAULT 'open',
+ resolution TEXT NULL,
+ note TEXT NULL,
+ FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE,
+ FOREIGN KEY(runId) REFERENCES runs(id) ON DELETE SET NULL
+ );
+ """,
+ "CREATE INDEX IF NOT EXISTS idx_conflicts_job_status ON conflicts(jobId, status);",
+
+ # meta (simple KV store)
+ """
+ CREATE TABLE IF NOT EXISTS meta (
+ key TEXT PRIMARY KEY,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ value TEXT NULL
+ );
+ """,
+ ],
+ ),
+ (
+ "0002_schedule_enabled_default_off",
+ [
+ # SQLite cannot ALTER COLUMN defaults directly; rebuild the schedule table so
+ # new rows default to enabled=0 while preserving existing data.
+ "PRAGMA foreign_keys=OFF;",
+ """
+ CREATE TABLE IF NOT EXISTS schedule_new (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ jobId INTEGER NOT NULL UNIQUE,
+ -- Schedule is controlled via per-day windows and intervalSeconds.
+ enabled INTEGER NOT NULL DEFAULT 0,
+ intervalSeconds INTEGER NOT NULL DEFAULT 3600,
+ nextRunAt TEXT NULL,
+ lastScheduledRunAt TEXT NULL,
+ windows TEXT NULL,
+ FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE
+ );
+ """.strip(),
+ """
+ INSERT INTO schedule_new (id, created, modified, jobId, enabled, intervalSeconds, nextRunAt, lastScheduledRunAt, windows)
+ SELECT id, created, modified, jobId, enabled, intervalSeconds, nextRunAt, lastScheduledRunAt, windows
+ FROM schedule;
+ """.strip(),
+ "DROP TABLE schedule;",
+ "ALTER TABLE schedule_new RENAME TO schedule;",
+ "PRAGMA foreign_keys=ON;",
+ ],
+ ),
+ ]
diff --git a/dist/macos/Replicator.app/Contents/Resources/replicator/mount.py b/dist/macos/Replicator.app/Contents/Resources/replicator/mount.py
new file mode 100644
index 00000000..30f3276a
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/replicator/mount.py
@@ -0,0 +1,317 @@
+
+#!/usr/bin/env python3
+# src/replicator/mount.py
+
+"""Remote mount helpers for Replicator.
+
+Purpose
+-------
+Centralize SMB mounting logic so it can be reused by both:
+ - UI runner (replicator.py) which often works with legacy dict endpoints
+ - Domain runner (job.py) which uses Endpoint dataclass objects
+
+Supported
+---------
+- SMB (CIFS / Windows shares)
+
+This module is intentionally thin and delegates to corePY's Share helper:
+ from core.filesystem.share import Share, ShareAuth
+
+Notes
+-----
+- Remote "location" strings are accepted in common forms:
+ * \\host\\Share\\dir
+ * //host/Share/dir
+ * host/Share/dir
+- On macOS, `mount_smbfs` expects URL-style paths; the remote part is
+ percent-encoded (spaces, etc.) while keeping '/' separators intact.
+- Secrets are never logged; obvious fields are redacted.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Any, Callable, Dict, Optional, Tuple
+
+import os
+import re
+import tempfile
+from urllib.parse import quote
+
+# corePY Share helper (import once at module import time)
+try:
+ from core.filesystem.share import Share, ShareAuth # type: ignore
+except Exception as _e: # pragma: no cover
+ Share = None # type: ignore
+ ShareAuth = None # type: ignore
+ _COREPY_SHARE_IMPORT_ERROR = _e
+else:
+ _COREPY_SHARE_IMPORT_ERROR = None
+
+# ---------------------------------------------------------------------------
+# Exceptions
+# ---------------------------------------------------------------------------
+
+class RemoteMountError(RuntimeError):
+ """Raised when a remote endpoint cannot be mounted or parsed."""
+
+# ---------------------------------------------------------------------------
+# Logging helpers
+# ---------------------------------------------------------------------------
+
+def redact_secrets(s: str) -> str:
+ """Best-effort secret redaction for log lines."""
+ if not s:
+ return s
+ # key=value patterns
+ s = re.sub(r"(pass=)([^,\s]+)", r"\1***", s, flags=re.IGNORECASE)
+ s = re.sub(r"(password=)([^,\s]+)", r"\1***", s, flags=re.IGNORECASE)
+ # CLI flags
+ s = re.sub(r"(--password\s+)(\S+)", r"\1***", s, flags=re.IGNORECASE)
+ s = re.sub(r"(--pass\s+)(\S+)", r"\1***", s, flags=re.IGNORECASE)
+ return s
+
+class ShareLoggerAdapter:
+ """Adapter to feed Share's logging into Replicator/corePY style append()."""
+
+ def __init__(self, append_fn: Callable[[str, str], None] | Callable[..., None]):
+ self._append = append_fn
+
+ def debug(self, msg: str) -> None:
+ self._append(redact_secrets(msg), level="debug") # type: ignore[misc]
+
+ def info(self, msg: str) -> None:
+ self._append(redact_secrets(msg), level="info") # type: ignore[misc]
+
+ def warning(self, msg: str) -> None:
+ self._append(redact_secrets(msg), level="warning") # type: ignore[misc]
+
+ def error(self, msg: str) -> None:
+ self._append(redact_secrets(msg), level="error") # type: ignore[misc]
+
+# ---------------------------------------------------------------------------
+# Models
+# ---------------------------------------------------------------------------
+
+@dataclass
+class MountedEndpoint:
+ """Represents a mounted (or local) endpoint."""
+
+ local_path: str
+ mount_point: Optional[str] = None
+ share: Any = None # Share instance (only present when mounted)
+
+ def cleanup(self, *, elevate: bool = False) -> None:
+ """Unmount the share if mounted; best-effort.
+
+ Notes
+ -----
+ Some platforms/targets may require elevation to unmount. We keep the
+ default as `False` to match current behavior; callers can opt-in.
+ """
+ if self.share is not None and self.mount_point:
+ try:
+ self.share.umount(self.mount_point, elevate=bool(elevate))
+ except Exception:
+ pass
+
+# ---------------------------------------------------------------------------
+# Parsing
+# ---------------------------------------------------------------------------
+
+def parse_smb_location(location: str) -> Tuple[str, str]:
+ """Parse SMB location into (host, remote).
+
+ Accepts forms:
+ - \\host\\Share
+ - \\host\\Share\\dir\\sub
+ - //host/Share/dir
+ - host/Share/dir
+
+ Returns:
+ host, remote where remote is "Share" or "Share/dir/sub".
+ """
+ s = (location or "").strip()
+ if not s:
+ raise RemoteMountError("SMB location is empty")
+
+ # Normalize to backslashes then split.
+ s = s.replace("/", "\\")
+ while s.startswith("\\"):
+ s = s[1:]
+
+ parts = [p for p in s.split("\\") if p]
+ if len(parts) < 2:
+ raise RemoteMountError(
+ f"Invalid SMB location '{location}'. Expected //host/Share[/path] or \\\\host\\Share[/path]."
+ )
+
+ host = parts[0]
+ remote = "/".join(parts[1:])
+ return host, remote
+
+# ---------------------------------------------------------------------------
+# Mounting
+# ---------------------------------------------------------------------------
+
+def _get_mount_point(job_id: Optional[int], role: str, *, unique: bool = False) -> str:
+ """Compute the local mount point for a job/role."""
+ base = Path(tempfile.gettempdir()) / "replicator" / "mounts" / (str(job_id or "new")) / role
+ if unique:
+ # Avoid collisions if caller mounts multiple times quickly.
+ base = base / str(os.getpid())
+ base.mkdir(parents=True, exist_ok=True)
+ return str(base)
+
+def mount_endpoint_if_remote(
+ endpoint: Any,
+ job_id: Optional[int],
+ role: str,
+ *,
+ share: Any = None,
+ log_append: Optional[Callable[..., None]] = None,
+ logger: Any = None,
+ timeout: int = 60,
+) -> MountedEndpoint:
+ """Mount an endpoint if it is remote; otherwise return the local path.
+
+ Parameters
+ ----------
+ endpoint:
+ Can be either:
+ - dict-like: {type, location, auth?} or legacy endpoint row fields
+ - an object with attributes: .type, .location, .auth
+ job_id:
+ Job id (used for mount dir structure).
+ role:
+ "source" or "target" (used for mount dir structure).
+ share:
+ Optional Share instance to reuse.
+ log_append:
+ Function compatible with core Log.append(msg, level=..., channel=...).
+ If provided, used for safe debug messages.
+ logger:
+ Backward-compatible alias used by some callers (e.g. job.py).
+ Can be either:
+ - an object exposing `.append(msg, level=...)` (core Log-like)
+ - a callable compatible with `log_append`
+ If provided and `log_append` is None, it will be used.
+ """
+
+ # Share support is optional at import time; raise a meaningful error at runtime.
+ if Share is None or ShareAuth is None:
+ raise RemoteMountError(
+ "Share support not available (corePY missing or failed to import). "
+ f"Import error: {_COREPY_SHARE_IMPORT_ERROR}"
+ )
+
+ # Backward-compatible: allow callers to pass `logger=` instead of `log_append=`.
+ if log_append is None and logger is not None:
+ if callable(logger):
+ log_append = logger # type: ignore[assignment]
+ else:
+ append_fn = getattr(logger, "append", None)
+ if callable(append_fn):
+ def _append(msg: str, *, level: str = "info") -> None:
+ # core Log.append may accept (msg, level=..., channel=...) or (msg, level)
+ try:
+ append_fn(msg, level=level) # type: ignore[misc]
+ except TypeError:
+ append_fn(msg, level) # type: ignore[misc]
+
+ log_append = _append
+
+ def _get(d: Any, k: str, default: Any = None) -> Any:
+ if isinstance(d, dict):
+ return d.get(k, default)
+ return getattr(d, k, default)
+
+ t = str(_get(endpoint, "type", "local") or "local").lower()
+ loc = str(_get(endpoint, "location", "") or "").strip()
+
+ if t == "local":
+ return MountedEndpoint(local_path=loc)
+
+ if t != "smb":
+ raise RemoteMountError(f"Unsupported endpoint type: {t}")
+
+ mount_point = _get_mount_point(job_id, role, unique=False)
+
+ # Build auth dict from endpoint.auth + legacy flat fields
+ auth: Dict[str, Any] = {}
+ raw_auth = _get(endpoint, "auth", None)
+ if isinstance(raw_auth, dict):
+ auth.update(raw_auth)
+
+ # Backward compatible: top-level fields (as stored in endpoints table)
+ for k in ("port", "guest", "username", "password", "options", "domain", "workgroup", "read_only", "ro"):
+ v = _get(endpoint, k, None)
+ if v is not None and k not in auth:
+ auth[k] = v
+
+ # Parse host/remote
+ host, remote = parse_smb_location(loc)
+
+ # Percent-encode remote path (keep '/' separators)
+ remote_enc = quote(remote, safe="/")
+
+ # Port
+ port = 445
+ try:
+ if auth.get("port") is not None:
+ port = int(auth.get("port") or 445)
+ except Exception:
+ port = 445
+
+ # Options
+ opts: Dict[str, Any] = {}
+ if isinstance(auth.get("options"), dict):
+ opts.update(auth.get("options") or {})
+
+ # ShareAuth (best effort)
+ share_auth = ShareAuth(
+ username=auth.get("username"),
+ password=auth.get("password"),
+ domain=auth.get("domain") or auth.get("workgroup"),
+ )
+
+ # Share instance (reuse if provided). If we create one here, wire it with our adapter.
+ if share is None:
+ share_logger = ShareLoggerAdapter(log_append) if log_append is not None else None
+ share = Share(logger=share_logger)
+
+ # Emit safe debug line
+ if log_append is not None:
+ safe_auth = dict(auth)
+ for k in ("password", "pass"):
+ if k in safe_auth and safe_auth[k]:
+ safe_auth[k] = "***"
+ msg = (
+ f"[Replicator][Mount] protocol={t} host={host} remote={remote_enc} "
+ f"mount_point={mount_point} auth={safe_auth}"
+ )
+ try:
+ log_append(msg, level="debug") # type: ignore[misc]
+ except TypeError:
+ # Some callables accept only positional args.
+ log_append(msg) # type: ignore[misc]
+
+ # Mount
+ try:
+ share.mount(
+ t,
+ host,
+ remote_enc,
+ mount_point,
+ auth=share_auth,
+ port=port,
+ options=opts,
+ read_only=bool(auth.get("read_only") or auth.get("ro") or False),
+ elevate=False,
+ timeout=int(timeout or 60),
+ )
+ except Exception as e:
+ raise RemoteMountError(f"Failed to mount SMB endpoint '{loc}': {e}")
+
+ return MountedEndpoint(local_path=mount_point, mount_point=mount_point, share=share)
diff --git a/dist/macos/Replicator.app/Contents/Resources/replicator/replicator.py b/dist/macos/Replicator.app/Contents/Resources/replicator/replicator.py
new file mode 100644
index 00000000..208ea9a7
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/replicator/replicator.py
@@ -0,0 +1,1424 @@
+#!/usr/bin/env python3
+# src/replicator/replicator.py
+
+from __future__ import annotations
+import os
+import json
+import shutil
+import time
+from typing import Optional, Any, Dict, List
+from datetime import datetime, timezone, timedelta
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QPixmap
+from PyQt5.QtWidgets import (
+ QApplication,
+ QMainWindow,
+ QWidget,
+ QVBoxLayout,
+ QHBoxLayout,
+ QLabel,
+ QTableWidget,
+ QTableWidgetItem,
+ QHeaderView,
+ QDialog,
+)
+from .ui import JobDialog, ScheduleDialog
+from .migration import Migration
+from .job import Job, Endpoint, Schedule, JobStore
+from .mount import RemoteMountError, MountedEndpoint, mount_endpoint_if_remote
+
+try:
+ from core.helper import Helper
+ from core.configuration import Configuration
+ from core.log import Log
+ from core.ui import MsgBox, Form
+ from core.filesystem.filesystem import FileSystem
+ from core.filesystem.share import Share, ShareAuth
+ from core.database.sqlite import SQLite
+except ImportError:
+ from helper import Helper
+ from configuration import Configuration
+ from log import Log
+ from ui import MsgBox, Form
+ from filesystem.filesystem import FileSystem
+ from filesystem.share import Share, ShareAuth
+ from database.sqlite import SQLite
+
+# ---------------------------------------------------------------------------
+# Remote endpoints (Share mount)
+# ---------------------------------------------------------------------------
+
+class _ShareLogger:
+ """Adapter that forwards Share logs into Replicator's logger."""
+
+ def __init__(self, append_fn):
+ self._append = append_fn
+
+ def debug(self, msg: str) -> None:
+ self._append(f"[Share] {msg}", level="debug")
+
+ def info(self, msg: str) -> None:
+ self._append(f"[Share] {msg}", level="info")
+
+ def warning(self, msg: str) -> None:
+ self._append(f"[Share] {msg}", level="warning")
+
+ def error(self, msg: str) -> None:
+ self._append(f"[Share] {msg}", level="error")
+
+class Replicator(QMainWindow):
+
+ # ------------------------------------------------------------------
+ # Initialization
+ # ------------------------------------------------------------------
+
+ def __init__(
+ self,
+ helper: Optional[Helper] = None,
+ configuration: Optional[Configuration] = None,
+ logger: Optional[Log] = None,
+ ):
+ super().__init__()
+
+ self._app = QApplication.instance()
+ if self._app is None:
+ raise RuntimeError("Replicator must be created after QApplication/Application.")
+
+ helper = helper or getattr(self._app, "helper", None)
+ configuration = configuration or getattr(self._app, "configuration", None)
+ logger = logger or getattr(self._app, "logger", None)
+
+ if helper is None or configuration is None:
+ raise RuntimeError("Replicator requires corePY Helper + Configuration.")
+
+ self._helper: Helper = helper
+ self._configuration: Configuration = configuration
+ self._logger: Optional[Log] = logger
+
+ self._fs = FileSystem(helper=self._helper, logger=self._logger)
+
+ # Shared Share instance (for SMB mounts) so we don't recreate it on every job run.
+ self._share_logger = _ShareLogger(self._log)
+ self._share = Share(logger=self._share_logger)
+
+ # --- Database path setup ---
+ # Use Helper.get_data_path to locate data/replicator.db
+ data_dir = self._helper.get_data_path("data", scope="system")
+ if data_dir is None:
+ raise RuntimeError("Could not locate data/ directory via Helper.get_data_path().")
+ os.makedirs(data_dir, exist_ok=True)
+ db_path = os.path.join(data_dir, "replicator.db")
+
+ # --- Database (corePY SQLite wrapper) + migrations ---
+ self._db = SQLite(db_path=db_path)
+ self._migration = Migration(self._db, logger=self._logger)
+ self._migration.ensure()
+
+ self._store = JobStore(self._db)
+
+ self._log(f"[Replicator] Database: {db_path}", level="debug")
+ self._log("[Replicator] Database migrations applied.", level="info")
+
+ # Domain jobs (Job objects)
+ self._jobs: List[Job] = []
+ self._table: Optional[QTableWidget] = None
+
+ # After DB is ready, log a snapshot of DB layout/counts (non-verbose)
+ self._db_debug_snapshot(verbose=False)
+
+ # ------------------------------------------------------------------
+ # Scheduler (service mode)
+ # ------------------------------------------------------------------
+
+ def _parse_iso_dt(self, s: Any) -> Optional[datetime]:
+ if not s:
+ return None
+ try:
+ dt = datetime.fromisoformat(str(s))
+ if dt.tzinfo is None:
+ dt = dt.replace(tzinfo=timezone.utc)
+ return dt
+ except Exception:
+ return None
+
+ def _should_run_scheduled(self, job: Job, *, now_utc: datetime) -> bool:
+ """Decide if a job should run in SERVICE/SCHEDULED mode.
+
+ Manual 'Run now' intentionally bypasses this.
+ """
+ if not job.id:
+ return False
+
+ sched = self._db.one(
+ "SELECT enabled, nextRunAt, lastScheduledRunAt FROM schedule WHERE jobId=?",
+ (int(job.id),),
+ )
+ if not sched:
+ # No schedule row => do not auto-run (manual run still works)
+ return False
+
+ # Schedule must be explicitly enabled in DB
+ if not bool(sched.get("enabled", 0)):
+ return False
+
+ # Schedule windows/interval are defined on the Job domain object
+ if not job.schedule.should_run_now(now_utc):
+ return False
+
+ # Respect nextRunAt if present
+ next_dt = self._parse_iso_dt(sched.get("nextRunAt"))
+ if next_dt and now_utc < next_dt:
+ return False
+
+ # Respect lastScheduledRunAt + interval
+ last_dt = self._parse_iso_dt(sched.get("lastScheduledRunAt"))
+ if last_dt is None:
+ return True
+
+ interval_s = int(job.schedule.interval_seconds_for_now(now_utc) or 0)
+ if interval_s <= 0:
+ interval_s = self._default_interval_seconds()
+
+ elapsed = (now_utc - last_dt).total_seconds()
+ return elapsed >= float(interval_s)
+ # Bidirectional sync helpers (local-only for now)
+ # ------------------------------------------------------------------
+
+ def _endpoint_local_root(self, ep: Dict[str, Any]) -> str:
+ """Return the local root path for an endpoint or raise.
+ This is a simple extractor for the already-mounted path."""
+ if not isinstance(ep, dict):
+ raise ValueError("Endpoint is missing")
+ loc = (ep.get("location") or "").strip()
+ if not loc:
+ raise ValueError("Endpoint location is required")
+ return loc
+
+ def _scan_local_tree(self, root: str) -> Dict[str, Dict[str, Any]]:
+ """Return a map relPath -> {isDir,size,mtime} for a local filesystem root."""
+ root = os.path.abspath(root)
+ out: Dict[str, Dict[str, Any]] = {}
+ if not os.path.exists(root):
+ return out
+
+ # Walk directories; include dirs as entries so deletions can be propagated.
+ for dirpath, dirnames, filenames in os.walk(root):
+ # Normalize and skip hidden special entries if needed (keep simple for now)
+ rel_dir = os.path.relpath(dirpath, root)
+ if rel_dir == ".":
+ rel_dir = ""
+
+ # Record directory itself (except root)
+ if rel_dir:
+ try:
+ st = os.stat(dirpath)
+ out[rel_dir] = {
+ "isDir": True,
+ "size": 0,
+ "mtime": int(st.st_mtime),
+ }
+ except Exception:
+ # If stat fails, still record directory
+ out[rel_dir] = {"isDir": True, "size": 0, "mtime": 0}
+
+ for fn in filenames:
+ full = os.path.join(dirpath, fn)
+ rel = os.path.relpath(full, root)
+ try:
+ st = os.stat(full)
+ out[rel] = {
+ "isDir": False,
+ "size": int(st.st_size),
+ "mtime": int(st.st_mtime),
+ }
+ except Exception:
+ out[rel] = {"isDir": False, "size": 0, "mtime": 0}
+
+ return out
+
+ def _load_prev_file_state(self, job_id: int, side: str) -> Dict[str, Dict[str, Any]]:
+ rows = self._db.query(
+ "SELECT relPath, size, mtime, isDir, deleted, deletedAt, lastSeenAt, lastSeenRunId FROM file_state WHERE jobId=? AND side=?",
+ (job_id, side),
+ )
+ prev: Dict[str, Dict[str, Any]] = {}
+ for r in rows or []:
+ prev[str(r.get("relPath"))] = {
+ "size": int(r.get("size") or 0),
+ "mtime": int(r.get("mtime") or 0),
+ "isDir": bool(r.get("isDir")),
+ "deleted": bool(r.get("deleted")),
+ "deletedAt": r.get("deletedAt"),
+ "lastSeenAt": r.get("lastSeenAt"),
+ "lastSeenRunId": r.get("lastSeenRunId"),
+ }
+ return prev
+
+ def _persist_file_state(self, job_id: int, side: str, cur: Dict[str, Dict[str, Any]], run_id: Optional[int]) -> None:
+ # Use wrapper transaction; no raw connection needed.
+ now_iso = datetime.now(timezone.utc).isoformat()
+ with self._db.transaction():
+ # Upsert all current entries
+ for rel, meta in cur.items():
+ is_dir = 1 if meta.get("isDir") else 0
+ size = int(meta.get("size", 0) or 0)
+ mtime = int(meta.get("mtime", 0) or 0)
+
+ exists = self._db.scalar(
+ "SELECT id FROM file_state WHERE jobId=? AND side=? AND relPath=?",
+ (job_id, side, rel),
+ )
+ if exists:
+ self._db.execute(
+ "UPDATE file_state SET size=?, mtime=?, isDir=?, deleted=0, deletedAt=NULL, lastSeenAt=?, lastSeenRunId=? WHERE jobId=? AND side=? AND relPath=?",
+ (size, mtime, is_dir, now_iso, run_id, job_id, side, rel),
+ )
+ else:
+ self._db.execute(
+ "INSERT INTO file_state (jobId, side, relPath, size, mtime, isDir, deleted, deletedAt, lastSeenAt, lastSeenRunId) VALUES (?,?,?,?,?,?,0,NULL,?,?)",
+ (job_id, side, rel, size, mtime, is_dir, now_iso, run_id),
+ )
+
+ # Mark missing as deleted (single UPDATE)
+ rels = list(cur.keys())
+ if rels:
+ placeholders = ",".join(["?"] * len(rels))
+ self._db.execute(
+ f"UPDATE file_state SET deleted=1, deletedAt=COALESCE(deletedAt, ?) WHERE jobId=? AND side=? AND deleted=0 AND relPath NOT IN ({placeholders})",
+ (now_iso, job_id, side, *rels),
+ )
+ else:
+ # cur is empty: mark all as deleted for this job/side
+ self._db.execute(
+ "UPDATE file_state SET deleted=1, deletedAt=COALESCE(deletedAt, ?) WHERE jobId=? AND side=? AND deleted=0",
+ (now_iso, job_id, side),
+ )
+
+ def _ensure_parent_dir(self, path: str) -> None:
+ parent = os.path.dirname(path)
+ if parent and not os.path.exists(parent):
+ os.makedirs(parent, exist_ok=True)
+
+ def _copy_local_path(self, src_root: str, dst_root: str, rel: str, is_dir: bool, preserve_metadata: bool) -> None:
+ src_full = os.path.join(src_root, rel)
+ dst_full = os.path.join(dst_root, rel)
+
+ if is_dir:
+ os.makedirs(dst_full, exist_ok=True)
+ return
+
+ self._ensure_parent_dir(dst_full)
+ if preserve_metadata:
+ shutil.copy2(src_full, dst_full)
+ else:
+ shutil.copy(src_full, dst_full)
+
+ def _delete_local_path(self, root: str, rel: str, is_dir: bool) -> None:
+ full = os.path.join(root, rel)
+ if not os.path.exists(full):
+ return
+ if is_dir:
+ # Only remove if empty; never rmtree blindly in sync engine.
+ try:
+ os.rmdir(full)
+ except OSError:
+ pass
+ else:
+ try:
+ os.remove(full)
+ except Exception:
+ pass
+
+ def _record_conflict(
+ self,
+ job_id: int,
+ run_id: Optional[int],
+ rel: str,
+ a: Dict[str, Any],
+ b: Dict[str, Any],
+ note: str = "",
+ ) -> None:
+ try:
+ # Avoid inserting duplicate open conflicts for the same path/note.
+ existing = self._db.one(
+ "SELECT id FROM conflicts WHERE jobId=? AND relPath=? AND status='open' AND COALESCE(note,'')=COALESCE(?, '') ORDER BY id DESC LIMIT 1",
+ (job_id, rel, note or ""),
+ )
+ if existing:
+ return
+ self._db.execute(
+ """
+ INSERT INTO conflicts (jobId, runId, relPath, a_size, a_mtime, a_hash, b_size, b_mtime, b_hash, status, note)
+ VALUES (?,?,?,?,?,?,?,?,?,'open',?)
+ """,
+ (
+ job_id,
+ run_id,
+ rel,
+ int(a.get("size", 0) or 0),
+ int(a.get("mtime", 0) or 0),
+ a.get("hash"),
+ int(b.get("size", 0) or 0),
+ int(b.get("mtime", 0) or 0),
+ b.get("hash"),
+ note or None,
+ ),
+ )
+ except Exception as e:
+ self._log(f"[Replicator][DB] Failed to record conflict for '{rel}': {e}", level="warning")
+
+ def _run_job_bidirectional(self, job: Dict[str, Any], run_id: Optional[int]) -> tuple[bool, Dict[str, Any]]:
+ """Bidirectional sync using `file_state` as the persistent baseline.
+
+ Currently implemented for local<->local only.
+ """
+ job_id = int(job.get("id") or 0)
+ if job_id <= 0:
+ raise ValueError("Job must have an id to run bidirectional sync")
+
+ src_ep = job.get("sourceEndpoint")
+ dst_ep = job.get("targetEndpoint")
+ a_root = self._endpoint_local_root(src_ep)
+ b_root = self._endpoint_local_root(dst_ep)
+
+ # Deletion behavior is derived from mode; mirror allows deletions.
+ allow_deletion = str(job.get("mode") or "mirror").lower() == "mirror"
+ preserve_metadata = bool(job.get("preserveMetadata", True))
+ conflict_policy = (job.get("conflictPolicy") or "newest").lower()
+
+ # Load previous baseline before scanning/persisting.
+ prev_a = self._load_prev_file_state(job_id, "A")
+ prev_b = self._load_prev_file_state(job_id, "B")
+
+ # Scan current trees
+ cur_a = self._scan_local_tree(a_root)
+ cur_b = self._scan_local_tree(b_root)
+
+ def _meta_changed(cur: Dict[str, Any], prev: Dict[str, Any]) -> bool:
+ return (
+ bool(cur.get("isDir")) != bool(prev.get("isDir"))
+ or int(cur.get("size", 0) or 0) != int(prev.get("size", 0) or 0)
+ or int(cur.get("mtime", 0) or 0) != int(prev.get("mtime", 0) or 0)
+ )
+
+ def _changed_set(cur: Dict[str, Dict[str, Any]], prev: Dict[str, Dict[str, Any]]) -> set[str]:
+ out = set()
+ for rel, meta in cur.items():
+ p = prev.get(rel)
+ if p is None or p.get("deleted", False):
+ out.add(rel)
+ else:
+ if _meta_changed(meta, p):
+ out.add(rel)
+ return out
+
+ def _deleted_set(cur: Dict[str, Dict[str, Any]], prev: Dict[str, Dict[str, Any]]) -> set[str]:
+ # deleted since last baseline means it existed (not deleted) and now missing
+ out = set()
+ for rel, p in prev.items():
+ if p.get("deleted", False):
+ continue
+ if rel not in cur:
+ out.add(rel)
+ return out
+
+ changed_a = _changed_set(cur_a, prev_a)
+ changed_b = _changed_set(cur_b, prev_b)
+ deleted_a = _deleted_set(cur_a, prev_a)
+ deleted_b = _deleted_set(cur_b, prev_b)
+
+ self._log(
+ f"[Replicator][BiDi] Snapshot A={len(cur_a)} entries, B={len(cur_b)} entries; changedA={len(changed_a)} changedB={len(changed_b)} deletedA={len(deleted_a)} deletedB={len(deleted_b)}",
+ level="debug",
+ )
+
+ # Build unified rel set
+ all_paths = set(cur_a.keys()) | set(cur_b.keys()) | set(prev_a.keys()) | set(prev_b.keys())
+
+ actions_copy_a_to_b: list[tuple[str, bool]] = []
+ actions_copy_b_to_a: list[tuple[str, bool]] = []
+ actions_del_a: list[tuple[str, bool]] = []
+ actions_del_b: list[tuple[str, bool]] = []
+
+ # Helper for newest
+ def _winner_newest(a: Dict[str, Any], b: Dict[str, Any]) -> str:
+ am = int(a.get("mtime", 0) or 0)
+ bm = int(b.get("mtime", 0) or 0)
+ if am == bm:
+ # tie-breaker: larger size wins
+ return "A" if int(a.get("size", 0) or 0) >= int(b.get("size", 0) or 0) else "B"
+ return "A" if am > bm else "B"
+
+ for rel in sorted(all_paths):
+ a_cur = cur_a.get(rel)
+ b_cur = cur_b.get(rel)
+
+ a_exists = a_cur is not None
+ b_exists = b_cur is not None
+
+ a_changed = rel in changed_a
+ b_changed = rel in changed_b
+
+ a_deleted = rel in deleted_a
+ b_deleted = rel in deleted_b
+
+ # If exists only on one side.
+ # When deletions are enabled and the missing side deleted it since the last baseline,
+ # deletion should win unless the existing side also changed (conflict).
+ if a_exists and not b_exists:
+ if b_deleted and allow_deletion:
+ if a_changed:
+ # conflict: B deleted while A changed
+ self._record_conflict(job_id, run_id, rel, a_cur, {"deleted": True}, note="B deleted, A changed")
+ winner = "A" if conflict_policy == "newest" else "A"
+ if winner == "A":
+ actions_copy_a_to_b.append((rel, bool(a_cur.get("isDir"))))
+ else:
+ actions_del_a.append((rel, bool(a_cur.get("isDir"))))
+ else:
+ # propagate deletion (keep B deleted, delete A)
+ actions_del_a.append((rel, bool(a_cur.get("isDir"))))
+ elif b_deleted and not allow_deletion:
+ # If B was deleted and deletions are not allowed, do NOT copy A->B (do nothing)
+ pass
+ else:
+ # no deletion involved: treat as create on A, copy to B
+ actions_copy_a_to_b.append((rel, bool(a_cur.get("isDir"))))
+ continue
+
+ if b_exists and not a_exists:
+ if a_deleted and allow_deletion:
+ if b_changed:
+ # conflict: A deleted while B changed
+ self._record_conflict(job_id, run_id, rel, {"deleted": True}, b_cur, note="A deleted, B changed")
+ winner = "B" if conflict_policy == "newest" else "B"
+ if winner == "B":
+ actions_copy_b_to_a.append((rel, bool(b_cur.get("isDir"))))
+ else:
+ actions_del_b.append((rel, bool(b_cur.get("isDir"))))
+ else:
+ # propagate deletion (keep A deleted, delete B)
+ actions_del_b.append((rel, bool(b_cur.get("isDir"))))
+ elif a_deleted and not allow_deletion:
+ # If A was deleted and deletions are not allowed, do NOT copy B->A (do nothing)
+ pass
+ else:
+ actions_copy_b_to_a.append((rel, bool(b_cur.get("isDir"))))
+ continue
+
+ # Missing on both sides: maybe deletion propagation; nothing to do.
+ if not a_exists and not b_exists:
+ continue
+
+ # Exists on both: check if identical
+ if a_exists and b_exists:
+ same = (
+ bool(a_cur.get("isDir")) == bool(b_cur.get("isDir"))
+ and int(a_cur.get("size", 0) or 0) == int(b_cur.get("size", 0) or 0)
+ and int(a_cur.get("mtime", 0) or 0) == int(b_cur.get("mtime", 0) or 0)
+ )
+
+ if same:
+ # Maybe propagate deletions if one side deleted previously (should not happen if same exists)
+ continue
+
+ # Not same: detect changes since baseline
+ if a_changed and b_changed:
+ # true conflict
+ self._record_conflict(job_id, run_id, rel, a_cur, b_cur, note="Changed on both sides")
+ if conflict_policy == "newest":
+ winner = _winner_newest(a_cur, b_cur)
+ elif conflict_policy in ("keepa", "a"):
+ winner = "A"
+ elif conflict_policy in ("keepb", "b"):
+ winner = "B"
+ else:
+ winner = _winner_newest(a_cur, b_cur)
+
+ if winner == "A":
+ actions_copy_a_to_b.append((rel, bool(a_cur.get("isDir"))))
+ else:
+ actions_copy_b_to_a.append((rel, bool(b_cur.get("isDir"))))
+ elif a_changed and not b_changed:
+ actions_copy_a_to_b.append((rel, bool(a_cur.get("isDir"))))
+ elif b_changed and not a_changed:
+ actions_copy_b_to_a.append((rel, bool(b_cur.get("isDir"))))
+ else:
+ # differs but we can't prove which changed vs baseline (e.g., first run with baseline empty)
+ # Choose newest as default.
+ winner = _winner_newest(a_cur, b_cur)
+ if winner == "A":
+ actions_copy_a_to_b.append((rel, bool(a_cur.get("isDir"))))
+ else:
+ actions_copy_b_to_a.append((rel, bool(b_cur.get("isDir"))))
+
+ # Deletions propagation (safe rules)
+ if allow_deletion:
+ # If A deleted and B did not change since baseline, delete on B
+ for rel in sorted(deleted_a):
+ if rel in changed_b:
+ # conflict: deleted on A but changed on B
+ self._record_conflict(job_id, run_id, rel, {"deleted": True}, cur_b.get(rel, {}), note="A deleted, B changed")
+ continue
+ pb = prev_b.get(rel)
+ if pb and not pb.get("deleted", False) and rel in cur_b:
+ actions_del_b.append((rel, bool(cur_b[rel].get("isDir"))))
+
+ # If B deleted and A did not change since baseline, delete on A
+ for rel in sorted(deleted_b):
+ if rel in changed_a:
+ self._record_conflict(job_id, run_id, rel, cur_a.get(rel, {}), {"deleted": True}, note="B deleted, A changed")
+ continue
+ pa = prev_a.get(rel)
+ if pa and not pa.get("deleted", False) and rel in cur_a:
+ actions_del_a.append((rel, bool(cur_a[rel].get("isDir"))))
+
+ # Execute actions
+ try:
+ # Copy directories first to ensure parents exist
+ for rel, is_dir in actions_copy_a_to_b:
+ self._copy_local_path(a_root, b_root, rel, is_dir, preserve_metadata)
+ for rel, is_dir in actions_copy_b_to_a:
+ self._copy_local_path(b_root, a_root, rel, is_dir, preserve_metadata)
+
+ # Then copy files (copy method handles both, but ordering helps for deep paths)
+ # (Already handled in _copy_local_path)
+
+ # Deletions last
+ for rel, is_dir in actions_del_a:
+ self._delete_local_path(a_root, rel, is_dir)
+ for rel, is_dir in actions_del_b:
+ self._delete_local_path(b_root, rel, is_dir)
+
+ except Exception as e:
+ self._log(f"[Replicator][BiDi] Execution failed: {e}", level="error")
+ return False, {}
+
+ # After actions, scan again and persist final file state as baseline
+ final_a = self._scan_local_tree(a_root)
+ final_b = self._scan_local_tree(b_root)
+ self._persist_file_state(job_id, "A", final_a, run_id)
+ self._persist_file_state(job_id, "B", final_b, run_id)
+
+ stats = {
+ "copyAtoB": len(actions_copy_a_to_b),
+ "copyBtoA": len(actions_copy_b_to_a),
+ "delA": len(actions_del_a),
+ "delB": len(actions_del_b),
+ "changedA": len(changed_a),
+ "changedB": len(changed_b),
+ "deletedA": len(deleted_a),
+ "deletedB": len(deleted_b),
+ }
+ self._log(f"[Replicator][BiDi] Actions: {stats}", level="debug")
+
+ # Update run stats if possible
+ if run_id:
+ try:
+ self._db.execute(
+ "UPDATE runs SET stats=? WHERE id=?",
+ (json.dumps(stats), run_id),
+ )
+ except Exception:
+ pass
+
+ return True, stats
+
+ # ------------------------------------------------------------------
+ # CLI integration
+ # ------------------------------------------------------------------
+
+ def cli(self, cli: QApplication = None) -> None:
+ """
+ Called by corePY CommandLine when running in CLI mode.
+ """
+ if cli is None:
+ return
+ cli.add("run", "Run all replication jobs.", self.run)
+ cli.service.add("run", "Run all replication jobs", self.run_scheduled, interval=1)
+
+ # ------------------------------------------------------------------
+ # UI
+ # ------------------------------------------------------------------
+
+ def show(self):
+ self.init()
+ super().show()
+
+ def init(self):
+ self.setWindowTitle(getattr(self._app, "name", "Replicator"))
+ self.setObjectName("Replicator")
+ # Ensure minimum window width for table visibility
+ self.setMinimumWidth(800)
+
+ central = QWidget(self)
+ self.setCentralWidget(central)
+ central.setStyleSheet("background-color: #212121;")
+ root = QVBoxLayout(central)
+ root.setContentsMargins(16, 16, 16, 16)
+ root.setSpacing(12)
+
+ # Logo (top, centered)
+ logo = QLabel()
+ logo.setAlignment(Qt.AlignCenter)
+
+ # Use app icon/logo if you have one; otherwise harmless
+ # Adjust path to wherever you store icons in Replicator
+ candidate = self._helper.get_path("icons/icon.png") or self._helper.get_path("core/icons/info.svg")
+ name = getattr(self._app, "name", None) or self._app.applicationName()
+ if candidate and self._helper.file_exists(candidate) and candidate.lower().endswith(".png"):
+ pm = QPixmap(candidate)
+ if not pm.isNull():
+ logo.setPixmap(pm.scaled(256, 256, Qt.KeepAspectRatio, Qt.SmoothTransformation))
+ else:
+ logo.setText(name)
+ logo.setStyleSheet("font-size: 32px; font-weight: 200; opacity: 0.8; padding: 0px; margin: 0px;")
+
+ root.addWidget(logo)
+
+ # Application name (under logo)
+ app_name = QLabel()
+ app_name.setText(str(name) if name else "")
+ app_name.setAlignment(Qt.AlignCenter)
+ app_name.setStyleSheet("font-size: 32px; font-weight: 200; opacity: 0.8; padding: 0px; margin: 0px;")
+ root.addWidget(app_name)
+
+ # --- Actions row (top, after logo) ---
+ actions_row = QHBoxLayout()
+ # No stretch at start; left-aligned by default
+ self._add_btn = Form.button(label="", icon="plus-circle", action=self._add_job)
+ self._add_btn.setToolTip("Add job")
+ self._edit_btn = Form.button(label="", icon="pencil-square", action=self._edit_job)
+ self._edit_btn.setToolTip("Edit job")
+ self._edit_btn.setVisible(False)
+ self._dup_btn = Form.button(label="", icon="files", action=self._duplicate_job)
+ self._dup_btn.setToolTip("Duplicate job")
+ self._dup_btn.setVisible(False)
+ self._del_btn = Form.button(label="", icon="trash", action=self._delete_job)
+ self._del_btn.setToolTip("Delete job")
+ self._del_btn.setVisible(False)
+ self._schedule_btn = Form.button(label="", icon="calendar-event", action=self._edit_schedule)
+ self._schedule_btn.setToolTip("Schedule")
+ self._schedule_btn.setVisible(False)
+ actions_row.addWidget(self._add_btn)
+ actions_row.addWidget(self._edit_btn)
+ actions_row.addWidget(self._dup_btn)
+ actions_row.addWidget(self._del_btn)
+ actions_row.addWidget(self._schedule_btn)
+ actions_row.addStretch(1)
+ root.addLayout(actions_row)
+
+ # Table
+ self._table = QTableWidget(0, 6, self)
+ self._table.setHorizontalHeaderLabels([
+ "Name", "Enabled", "Source", "Target", "Last run", "Result"
+ ])
+ self._table.setSelectionBehavior(QTableWidget.SelectRows)
+ self._table.setEditTriggers(QTableWidget.NoEditTriggers)
+
+ # Hide row numbers
+ self._table.verticalHeader().setVisible(False)
+
+ # Connect selection change to update visibility of action buttons
+ self._table.selectionModel().selectionChanged.connect(self._on_selection_changed)
+
+ # Ensure columns never shrink below their content (use scrollbar for overflow)
+ header = self._table.horizontalHeader()
+ # Performance: ResizeToContents on every column can get painfully slow.
+ header.setSectionResizeMode(0, QHeaderView.Stretch) # Name
+ for i in range(1, self._table.columnCount()):
+ header.setSectionResizeMode(i, QHeaderView.ResizeToContents)
+
+ # Keep horizontal scrollbar available for overflow
+ header.setStretchLastSection(False)
+
+ root.addWidget(self._table)
+
+ # --- Bottom row: Service Manager, Configurator and Run now (right aligned) ---
+ bottom_row = QHBoxLayout()
+ bottom_row.addStretch(1)
+
+ service_btn = Form.button(label="", icon="hdd-network", action=self._open_service_manager)
+ service_btn.setToolTip("Service Manager")
+
+ config_btn = Form.button(label="", icon="gear-fill", action=self._configuration.show)
+ config_btn.setToolTip("Configurator")
+
+ run_btn = Form.button(label="", icon="play-fill", action=lambda: self._run_with_ui_feedback())
+ run_btn.setToolTip("Run now")
+
+ bottom_row.addWidget(service_btn)
+ bottom_row.addWidget(config_btn)
+ bottom_row.addWidget(run_btn)
+ root.addLayout(bottom_row)
+
+ self._reload_jobs()
+ self._on_selection_changed()
+
+ def _on_selection_changed(self, *_args):
+ has = self._selected_index() >= 0
+ if hasattr(self, "_edit_btn"):
+ self._edit_btn.setVisible(has)
+ if hasattr(self, "_dup_btn"):
+ self._dup_btn.setVisible(has)
+ if hasattr(self, "_del_btn"):
+ self._del_btn.setVisible(has)
+ if hasattr(self, "_schedule_btn"):
+ self._schedule_btn.setVisible(has)
+
+ def _selected_index(self) -> int:
+ if not self._table:
+ return -1
+ rows = self._table.selectionModel().selectedRows()
+ if not rows:
+ return -1
+ return rows[0].row()
+
+ def _reload_jobs(self):
+ self._jobs = self._store.fetch_all()
+ self._refresh_table()
+
+ def _save_jobs(self):
+ return
+
+ def _refresh_table(self):
+ if not self._table:
+ return
+ self._table.setRowCount(0)
+
+ for job in self._jobs:
+ r = self._table.rowCount()
+ self._table.insertRow(r)
+
+ def _it(v: Any) -> QTableWidgetItem:
+ item = QTableWidgetItem(str(v) if v is not None else "")
+ item.setFlags(item.flags() ^ Qt.ItemIsEditable)
+ return item
+
+ self._table.setItem(r, 0, _it(job.name))
+ self._table.setItem(r, 1, _it("On" if job.enabled else "Off"))
+
+ src_str = f"{job.sourceEndpoint.type}:{job.sourceEndpoint.location}"
+ tgt_str = f"{job.targetEndpoint.type}:{job.targetEndpoint.location}"
+ self._table.setItem(r, 2, _it(src_str))
+ self._table.setItem(r, 3, _it(tgt_str))
+
+ last_run = job.lastRun
+ self._table.setItem(r, 4, _it(last_run if last_run else "Never"))
+
+ self._table.setItem(r, 5, _it(job.lastResult or ""))
+
+ def _default_interval_seconds(self) -> int:
+ """Return the configured default interval in seconds (service.defaultInterval), fallback to 3600."""
+ try:
+ v = self._configuration.get("service.defaultInterval", 3600)
+ iv = int(v)
+ return iv if iv > 0 else 3600
+ except Exception:
+ return 3600
+
+ def _default_schedule_dict(self) -> Dict[str, Any]:
+ interval = self._default_interval_seconds()
+ windows: Dict[str, Any] = {}
+ for wd in range(7):
+ windows[str(wd)] = [{"start": "00:00", "end": "23:59", "intervalSeconds": interval}]
+ return {
+ "enabled": False,
+ "intervalSeconds": interval,
+ "windows": windows,
+ }
+
+ def _schedule_interval_seconds_for_now(self, sched: Dict[str, Any]) -> int:
+ # Kept for backward compatibility with existing log formatting.
+ # Prefer the Job/Schedule domain helpers where possible.
+ try:
+ j = self._job_from_legacy_dict({"schedule": sched, "name": "_tmp", "enabled": True, "sourceEndpoint": {"type": "local", "location": "/"}, "targetEndpoint": {"type": "local", "location": "/"}})
+ return int(j.schedule.interval_seconds_for_now(datetime.now(timezone.utc)) or 0) or self._default_interval_seconds()
+ except Exception:
+ return self._default_interval_seconds()
+
+ def _job_from_legacy_dict(self, d: Dict[str, Any], *, existing_id: Optional[int] = None) -> Job:
+ src = d.get("sourceEndpoint") or {"type": "local", "location": d.get("source", ""), "auth": {}}
+ tgt = d.get("targetEndpoint") or {"type": "local", "location": d.get("target", ""), "auth": {}}
+
+ # Schedule defaults:
+ # - enabled
+ # - every day, all day
+ # - interval 3600 seconds (1h)
+ sched = d.get("schedule")
+ if not isinstance(sched, dict):
+ sched = self._default_schedule_dict()
+
+ default_interval = self._default_interval_seconds()
+
+ # Normalize intervalSeconds (no everyMinutes support)
+ try:
+ iv = int(sched.get("intervalSeconds") or 0)
+ except Exception:
+ iv = 0
+ if iv <= 0:
+ iv = default_interval
+ sched["intervalSeconds"] = iv
+
+ # Ensure windows exist; if missing, make it "every day, all day" with per-day interval
+ windows = sched.get("windows")
+ if not isinstance(windows, dict) or not windows:
+ windows = {}
+ for wd in range(7):
+ windows[str(wd)] = [{"start": "00:00", "end": "23:59", "intervalSeconds": iv}]
+ sched["windows"] = windows
+ else:
+ # Ensure each enabled day window includes intervalSeconds (per-day) for the upcoming scheduler logic
+ for k, v in list(windows.items()):
+ if isinstance(v, list) and v and isinstance(v[0], dict):
+ if "intervalSeconds" not in v[0]:
+ v[0]["intervalSeconds"] = iv
+
+ j = Job(
+ id=existing_id,
+ name=str(d.get("name") or ""),
+ enabled=bool(d.get("enabled", True)),
+ mode=str(d.get("mode") or "mirror"),
+ direction=str(d.get("direction") or "unidirectional"),
+ # Deletion behavior is derived from mode; mirror allows deletions.
+ allowDeletion=(str(d.get("mode") or "mirror").lower() == "mirror"),
+ preserveMetadata=bool(d.get("preserveMetadata", True)),
+ conflictPolicy=str(d.get("conflictPolicy") or "newest"),
+ pairId=d.get("pairId"),
+ lastRun=d.get("lastRun"),
+ lastResult=d.get("lastResult"),
+ lastError=d.get("lastError"),
+ )
+
+ j.sourceEndpoint = Endpoint(
+ type=str(src.get("type") or "local"),
+ location=str(src.get("location") or ""),
+ auth=dict(src.get("auth") or {}),
+ )
+ j.targetEndpoint = Endpoint(
+ type=str(tgt.get("type") or "local"),
+ location=str(tgt.get("location") or ""),
+ auth=dict(tgt.get("auth") or {}),
+ )
+
+ windows = sched.get("windows") if isinstance(sched.get("windows"), dict) else {}
+
+ j.schedule = Schedule(
+ enabled=bool(sched.get("enabled", False)),
+ intervalSeconds=int(iv),
+ windows=windows,
+ )
+
+ return j
+
+ def _add_job(self):
+ # Pass configured service.defaultInterval into the dialog so Schedule defaults match user config
+ dlg = JobDialog(self, default_interval_seconds=self._default_interval_seconds())
+ if dlg.exec_() == QDialog.Accepted:
+ new_job_dict = dlg.value()
+ if "schedule" not in new_job_dict or not isinstance(new_job_dict.get("schedule"), dict):
+ new_job_dict["schedule"] = self._default_schedule_dict()
+ job_obj = self._job_from_legacy_dict(new_job_dict)
+ job_id = self._store.upsert(job_obj)
+ job_obj.id = job_id
+ self._reload_jobs()
+
+ def _edit_job(self):
+ idx = self._selected_index()
+ if idx < 0:
+ MsgBox.show(self, "Jobs", "Select a job to edit.", icon="info")
+ return
+
+ current = self._jobs[idx]
+ # Pass configured service.defaultInterval into the dialog so Schedule defaults remain consistent
+ dlg = JobDialog(self, current.to_legacy_dict(), default_interval_seconds=self._default_interval_seconds())
+ if dlg.exec_() == QDialog.Accepted:
+ updated_dict = dlg.value()
+ job_obj = self._job_from_legacy_dict(updated_dict, existing_id=current.id)
+ self._store.upsert(job_obj)
+ self._reload_jobs()
+
+ def _duplicate_job(self):
+ idx = self._selected_index()
+ if idx < 0:
+ MsgBox.show(self, "Jobs", "Select a job to duplicate.", icon="info")
+ return
+
+ orig = self._jobs[idx]
+ d = orig.to_legacy_dict()
+ d.pop("id", None)
+ d["name"] = f"{orig.name} (copy)" if orig.name else "Copy"
+
+ job_obj = self._job_from_legacy_dict(d)
+ self._store.upsert(job_obj)
+ self._reload_jobs()
+
+ if self._table:
+ # Select the newest row (best-effort)
+ self._table.selectRow(self._table.rowCount() - 1)
+
+ def _delete_job(self):
+ idx = self._selected_index()
+ if idx < 0:
+ MsgBox.show(self, "Jobs", "Select a job to delete.", icon="info")
+ return
+
+ job = self._jobs[idx]
+ choice = MsgBox.show(
+ self,
+ title="Delete",
+ message=f"Delete job '{job.name}'?",
+ icon="question",
+ buttons=("Cancel", "Delete"),
+ default="Cancel",
+ )
+ if choice == "Delete":
+ if job.id:
+ jid = int(job.id)
+ try:
+ with self._db.transaction():
+ # Explicitly delete dependent rows to avoid orphans.
+ self._db.execute("DELETE FROM endpoints WHERE jobId = ?", (jid,))
+ self._db.execute("DELETE FROM schedule WHERE jobId = ?", (jid,))
+ self._db.execute("DELETE FROM runs WHERE jobId = ?", (jid,))
+ self._db.execute("DELETE FROM conflicts WHERE jobId = ?", (jid,))
+ self._db.execute("DELETE FROM file_state WHERE jobId = ?", (jid,))
+ self._db.execute("DELETE FROM jobs WHERE id = ?", (jid,))
+ except Exception as e:
+ self._log(f"[Replicator][DB] Failed to delete job {jid} cascade: {e}", level="warning")
+ # Fallback to store deletion if cascade failed.
+ try:
+ self._store.delete(jid)
+ except Exception:
+ pass
+ self._reload_jobs()
+
+ def _edit_schedule(self):
+ idx = self._selected_index()
+ if idx < 0:
+ MsgBox.show(self, "Jobs", "Select a job to edit schedule.", icon="info")
+ return
+
+ current = self._jobs[idx]
+ # Pass configured service.defaultInterval into the schedule editor so defaults match user config
+ dlg = ScheduleDialog(self, job=current.to_legacy_dict(), default_interval_seconds=self._default_interval_seconds())
+ if dlg.exec_() == QDialog.Accepted:
+ d = current.to_legacy_dict()
+ d["schedule"] = dlg.value()
+ job_obj = self._job_from_legacy_dict(d, existing_id=current.id)
+ self._store.upsert(job_obj)
+ self._reload_jobs()
+
+ def _run_with_ui_feedback(self):
+ ok = self.run()
+ MsgBox.show(
+ self,
+ title="Replicator",
+ message="Replication completed successfully." if ok else "Replication finished with errors.",
+ icon="info" if ok else "warning",
+ )
+
+ # ------------------------------------------------------------------
+ # Execution
+ # ------------------------------------------------------------------
+
+ def run_scheduled(self) -> bool:
+ """
+ Scheduled/service execution entrypoint.
+ Respects per-job schedule (enabled/windows/interval).
+ Returns True if all eligible jobs succeeded.
+ """
+ self._reload_jobs()
+
+ verbose_db = False
+ try:
+ verbose_db = bool(self._configuration.get("log.verbose", False))
+ except Exception:
+ verbose_db = False
+ self._db_debug_snapshot(verbose=verbose_db)
+
+ if not self._jobs:
+ self._log("[Replicator] No jobs configured.", level="warning")
+ return False
+
+ now_utc = datetime.now(timezone.utc)
+
+ any_ran = False
+ all_ok = True
+
+ for job in self._jobs:
+ if not bool(job.enabled):
+ continue
+
+ if not self._should_run_scheduled(job, now_utc=now_utc):
+ continue
+
+ any_ran = True
+ ok = self._run_job(job)
+ all_ok = all_ok and ok
+
+ # Update schedule bookkeeping for this job
+ try:
+ interval_s = int(job.schedule.interval_seconds_for_now(datetime.now(timezone.utc)) or 0) or self._default_interval_seconds()
+ next_run = (datetime.now(timezone.utc) + timedelta(seconds=int(interval_s))).isoformat()
+ self._db.update(
+ "schedule",
+ {
+ "lastScheduledRunAt": datetime.now(timezone.utc).isoformat(),
+ "nextRunAt": next_run,
+ },
+ "jobId = :jobId",
+ {"jobId": int(job.id)},
+ )
+ except Exception:
+ pass
+
+ if not any_ran:
+ self._log("[Replicator] No jobs due per schedule.", level="debug")
+
+ return all_ok
+
+ def run(self) -> bool:
+ """
+ Run all configured jobs (from DB).
+ Returns True if all jobs succeeded.
+ """
+ self._reload_jobs()
+ # After reloading jobs, log a DB snapshot for debugging.
+ # Only log verbose DB details when log.verbose is enabled.
+ verbose_db = False
+ try:
+ verbose_db = bool(self._configuration.get("log.verbose", False))
+ except Exception:
+ verbose_db = False
+
+ self._db_debug_snapshot(verbose=verbose_db)
+ jobs: List[Job] = self._jobs
+ if not jobs:
+ self._log("[Replicator] No jobs configured.", level="warning")
+ return False
+
+ all_ok = True
+ for job in jobs:
+ if not bool(job.enabled):
+ self._log(f"[Replicator] Skipping disabled job '{job.name or 'Unnamed'}'.", level="info")
+ continue
+ ok = self._run_job(job)
+ all_ok = all_ok and ok
+
+ # Run DB maintenance at most once per day.
+ try:
+ last = self._migration.get_meta("maintenance.lastRunAt")
+ should = True
+ if last:
+ try:
+ last_dt = datetime.fromisoformat(str(last))
+ if last_dt.tzinfo is None:
+ last_dt = last_dt.replace(tzinfo=timezone.utc)
+ should = (datetime.now(timezone.utc) - last_dt).total_seconds() >= 86400
+ except Exception:
+ should = True
+ if should:
+ self._db_maintenance()
+ except Exception:
+ pass
+
+ return all_ok
+
+ def _run_job(self, job: Job) -> bool:
+ name = job.name or "Unnamed"
+ if not bool(job.enabled):
+ self._log(f"[Replicator] Job '{name}' is disabled; skipping.", level="info")
+ return True
+ job_id = job.id
+ src = job.sourceEndpoint.location
+ src_type = job.sourceEndpoint.type
+ dst = job.targetEndpoint.location
+ dst_type = job.targetEndpoint.type
+ mode = job.mode
+ # Deletion behavior is derived from mode; mirror allows deletions.
+ allow_deletion = str(mode or "mirror").lower() == "mirror"
+ preserve_metadata = bool(job.preserveMetadata)
+ direction = job.direction
+
+ if not src or not dst:
+ self._log(f"[Replicator] Job '{name}' invalid: missing source or target path (src='{src}', dst='{dst}').", level="error")
+ return False
+
+ sched_str = ""
+ try:
+ if job_id:
+ sched = self._db.one(
+ "SELECT enabled FROM schedule WHERE jobId=?",
+ (int(job_id),),
+ )
+ if sched and bool(sched.get("enabled", 0)):
+ interval_s = int(job.schedule.interval_seconds_for_now(datetime.now(timezone.utc)) or 0) or self._default_interval_seconds()
+ sched_str = f", schedule=Interval {interval_s}s"
+ except Exception:
+ pass
+ logline = f"[Replicator] Running job '{name}': {src} -> {dst} (mode={mode}, direction={direction}, delete={allow_deletion}, meta={preserve_metadata}{sched_str}"
+ logline += f", srcType={src_type}, dstType={dst_type})"
+ self._log(logline)
+
+ # Insert a run record at start
+ started_at = datetime.now(timezone.utc).isoformat()
+ run_id = None
+ bidi_stats = None
+ try:
+ run_id = None
+ if job_id:
+ try:
+ run_id = int(self._db.insert("runs", {"jobId": int(job_id), "startedAt": started_at, "result": "running"}))
+ except Exception as e:
+ self._log(f"[Replicator] Failed to insert run record: {e}", level="warning")
+ except Exception as e:
+ self._log(f"[Replicator] Failed to insert run record: {e}", level="warning")
+
+ record_noop_runs = bool(self._configuration.get("db.recordNoopRuns", False))
+
+ ok = False
+ try:
+ # Always build a legacy dict so we can reuse the endpoint records + auth.
+ job_dict = job.to_legacy_dict()
+ src_ep = job_dict.get("sourceEndpoint") or {"type": "local", "location": src, "auth": {}}
+ dst_ep = job_dict.get("targetEndpoint") or {"type": "local", "location": dst, "auth": {}}
+
+ # Mount remote endpoints (SMB only) via Share so BOTH uni + bidi operate on
+ # local filesystem paths, and we get connectivity debug logs.
+ mounted: list[MountedEndpoint] = []
+
+ try:
+ src_m = mount_endpoint_if_remote(src_ep, job_id, "source", share=self._share, log_append=self._log)
+ mounted.append(src_m)
+ dst_m = mount_endpoint_if_remote(dst_ep, job_id, "target", share=self._share, log_append=self._log)
+ mounted.append(dst_m)
+
+ # Use the mounted/local view for execution
+ local_src = src_m.local_path
+ local_dst = dst_m.local_path
+
+ if str(direction).lower() == "bidirectional":
+ # Local view for the bidirectional sync engine
+ job_dict["sourceEndpoint"] = {"type": "local", "location": local_src, "auth": {}}
+ job_dict["targetEndpoint"] = {"type": "local", "location": local_dst, "auth": {}}
+ ok, bidi_stats = self._run_job_bidirectional(job_dict, run_id)
+ else:
+ # Unidirectional copy (mirror) also uses mounted/local paths
+ ok = self._fs.copy(
+ local_src,
+ local_dst,
+ preserve_metadata=preserve_metadata,
+ allow_deletion=allow_deletion,
+ )
+ finally:
+ for m in reversed(mounted):
+ try:
+ m.cleanup()
+ except Exception:
+ pass
+ except NotImplementedError as e:
+ self._log(f"[Replicator] Job '{name}' not supported: {e}", level="error")
+ ok = False
+ except RemoteMountError as e:
+ self._log(f"[Replicator] Job '{name}' failed to mount remote endpoint: {e}", level="error")
+ ok = False
+ except Exception as e:
+ self._log(f"[Replicator] Job '{name}' failed: {e}", level="error")
+ ok = False
+
+ # If this was a bidirectional run with no actions/changes, optionally drop the run row.
+ if run_id and bidi_stats is not None and not record_noop_runs:
+ try:
+ noop = (
+ int(bidi_stats.get("copyAtoB", 0) or 0) == 0
+ and int(bidi_stats.get("copyBtoA", 0) or 0) == 0
+ and int(bidi_stats.get("delA", 0) or 0) == 0
+ and int(bidi_stats.get("delB", 0) or 0) == 0
+ and int(bidi_stats.get("changedA", 0) or 0) == 0
+ and int(bidi_stats.get("changedB", 0) or 0) == 0
+ and int(bidi_stats.get("deletedA", 0) or 0) == 0
+ and int(bidi_stats.get("deletedB", 0) or 0) == 0
+ )
+ if noop:
+ self._db.execute("DELETE FROM runs WHERE id = ?", (run_id,))
+ run_id = None
+ except Exception:
+ pass
+
+ # Persist lastRun and lastResult, refresh UI, save to DB
+ now_str = datetime.now(timezone.utc).isoformat()
+ job.lastRun = now_str
+ job.lastResult = "ok" if ok else "fail"
+ job.lastError = None if ok else (job.lastError or "Failed")
+ self._store.upsert(job)
+ if run_id:
+ try:
+ self._db.update(
+ "runs",
+ {"endedAt": now_str, "result": ("ok" if ok else "fail"), "message": (None if ok else (job.lastError or "Failed"))},
+ "id = :id",
+ {"id": int(run_id)},
+ )
+ except Exception as e:
+ self._log(f"[Replicator] Failed to update run record: {e}", level="warning")
+ if self._table:
+ self._reload_jobs()
+
+ self._log(f"[Replicator] Job '{job.name}' result: {'OK' if ok else 'FAIL'} (lastResult={job.lastResult})", level="info" if ok else "warning")
+ return ok
+
+ def _log(self, msg: str, level: str = "info", channel: str = "replicator") -> None:
+ if self._logger is not None and hasattr(self._logger, "append"):
+ self._logger.append(msg, level=level, channel=channel) # type: ignore[call-arg]
+ else:
+ print(msg)
+
+ def _db_debug_snapshot(self, verbose: bool = False) -> None:
+ """
+ Log a compact snapshot of the current DB layout + row counts.
+ If verbose=True, also logs the most recent rows for key tables.
+ """
+ try:
+ # List tables
+ tables = [r["name"] for r in self._db.query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name")]
+
+ self._log(f"[Replicator][DB] Tables: {', '.join(tables) if tables else '(none)'}", level="debug")
+
+ # Layout + counts
+ for t in tables:
+ try:
+ cols = self._db.query(f"PRAGMA table_info({t})")
+ col_names = [c.get("name") for c in cols]
+ count = int(self._db.scalar(f"SELECT COUNT(*) FROM {t}") or 0)
+ self._log(f"[Replicator][DB] {t}: columns={len(col_names)} rows={count}", level="debug")
+ if verbose:
+ self._log(f"[Replicator][DB] {t}: {', '.join(col_names)}", level="debug")
+ except Exception as e:
+ self._log(f"[Replicator][DB] Failed introspecting {t}: {e}", level="warning")
+
+ if not verbose:
+ return
+
+ for t, order_col in (("jobs", "id"), ("runs", "id"), ("schedule", "id"), ("conflicts", "id")):
+ if t in tables:
+ try:
+ rows = self._db.query(f"SELECT * FROM {t} ORDER BY {order_col} DESC LIMIT 3")
+ self._log(f"[Replicator][DB] {t}: latest {len(rows)} row(s)", level="debug")
+ for r in rows:
+ dr = dict(r)
+ for k in ("password", "pass"):
+ if k in dr and dr[k]:
+ dr[k] = "***"
+ self._log(f"[Replicator][DB] {t}: {dr}", level="debug")
+ except Exception as e:
+ self._log(f"[Replicator][DB] Failed reading latest rows from {t}: {e}", level="warning")
+
+ except Exception as e:
+ self._log(f"[Replicator][DB] Snapshot failed: {e}", level="warning")
+
+ def _db_maintenance(self) -> None:
+ """Prune old history and run lightweight SQLite maintenance.
+
+ Safe defaults:
+ - Keep last N runs per job (db.retention.runs.keepLast)
+ - Also prune runs older than X days (db.retention.runs.keepDays)
+ - Keep open conflicts; prune resolved conflicts older than X days
+ - Optionally VACUUM (off by default)
+
+ This is designed to be called periodically (e.g. daily) by the service loop.
+ """
+ try:
+ keep_last = int(self._configuration.get("db.retention.runs.keepLast", 500))
+ keep_days = int(self._configuration.get("db.retention.runs.keepDays", 30))
+ prune_conflict_days = int(self._configuration.get("db.retention.conflicts.keepDays", 90))
+ prune_deleted_state_days = int(self._configuration.get("db.retention.fileState.deletedKeepDays", 30))
+ do_vacuum = bool(self._configuration.get("db.maintenance.vacuum", False))
+ except Exception:
+ keep_last, keep_days = 500, 30
+ prune_conflict_days, prune_deleted_state_days = 90, 30
+ do_vacuum = False
+
+ now = datetime.now(timezone.utc)
+ now_iso = now.isoformat()
+
+ # Runs: keep last N per job + prune anything older than keep_days
+ try:
+ job_ids = [int(r.get("id")) for r in (self._db.query("SELECT id FROM jobs") or [])]
+ with self._db.transaction():
+ for jid in job_ids:
+ if keep_last > 0:
+ self._db.execute(
+ """
+ DELETE FROM runs
+ WHERE jobId = ?
+ AND id NOT IN (
+ SELECT id FROM runs WHERE jobId = ? ORDER BY id DESC LIMIT ?
+ )
+ """,
+ (jid, jid, keep_last),
+ )
+
+ if keep_days > 0:
+ self._db.execute(
+ "DELETE FROM runs WHERE julianday(created) < julianday('now', ?) ",
+ (f'-{keep_days} days',),
+ )
+ except Exception as e:
+ self._log(f"[Replicator][DB] Maintenance: runs prune failed: {e}", level="warning")
+
+ # Conflicts: keep all open; prune non-open older than prune_conflict_days
+ try:
+ if prune_conflict_days > 0:
+ with self._db.transaction():
+ self._db.execute(
+ "DELETE FROM conflicts WHERE status <> 'open' AND julianday(created) < julianday('now', ?) ",
+ (f'-{prune_conflict_days} days',),
+ )
+ except Exception as e:
+ self._log(f"[Replicator][DB] Maintenance: conflicts prune failed: {e}", level="warning")
+
+ # file_state: prune deleted entries that have been deleted for a long time
+ try:
+ if prune_deleted_state_days > 0:
+ with self._db.transaction():
+ self._db.execute(
+ "DELETE FROM file_state WHERE deleted = 1 AND deletedAt IS NOT NULL AND julianday(deletedAt) < julianday('now', ?) ",
+ (f'-{prune_deleted_state_days} days',),
+ )
+ except Exception as e:
+ self._log(f"[Replicator][DB] Maintenance: file_state prune failed: {e}", level="warning")
+
+ # SQLite maintenance: optimize; optionally vacuum
+ try:
+ self._db.execute("PRAGMA optimize;")
+ if do_vacuum:
+ self._db.execute("VACUUM;")
+ self._migration.set_meta("maintenance.lastRunAt", now_iso)
+ self._log("[Replicator][DB] Maintenance completed.", level="debug")
+ except Exception as e:
+ self._log(f"[Replicator][DB] Maintenance failed: {e}", level="warning")
+
+ def _open_service_manager(self) -> None:
+ """Open the corePY Service Manager dialog (if available)."""
+ try:
+ svc = getattr(self._app, "service", None)
+ if svc is None:
+ raise RuntimeError("Service is not available on the application instance.")
+
+ # Preferred helper added in corePY Service
+ if hasattr(svc, "open_manager_dialog"):
+ svc.open_manager_dialog(parent=self)
+ return
+
+ # Fallback: if the dialog class exists, try to instantiate it
+ dlg_cls = getattr(svc, "ServiceManagerDialog", None)
+ if dlg_cls is not None:
+ dlg = dlg_cls(parent=self, service=svc)
+ dlg.exec_()
+ return
+
+ raise RuntimeError("Service manager UI is not available in this build.")
+
+ except Exception as e:
+ MsgBox.show(self, "Service", f"Unable to open Service Manager: {e}", icon="warning")
diff --git a/dist/macos/Replicator.app/Contents/Resources/replicator/ui.py b/dist/macos/Replicator.app/Contents/Resources/replicator/ui.py
new file mode 100644
index 00000000..e0c96b10
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/Resources/replicator/ui.py
@@ -0,0 +1,816 @@
+#!/usr/bin/env python3
+# src/replicator/ui.py
+
+from __future__ import annotations
+
+from typing import Optional, Any, Dict, List, Tuple
+
+from PyQt5.QtCore import Qt, QTime, pyqtSignal
+from PyQt5.QtWidgets import (
+ QWidget,
+ QVBoxLayout,
+ QHBoxLayout,
+ QLabel,
+ QPushButton,
+ QDialog,
+ QFormLayout,
+ QLineEdit,
+ QCheckBox,
+ QComboBox,
+ QSpinBox,
+ QTextEdit,
+ QFrame,
+ QSizePolicy,
+ QLayout,
+ QTimeEdit,
+ QFileDialog,
+)
+
+try:
+ from core.ui import MsgBox
+except ImportError:
+ from ui import MsgBox
+
+# ------------------------------------------------------------------
+# Helpers
+# ------------------------------------------------------------------
+class BrowseLineEdit(QLineEdit):
+ """QLineEdit that emits a signal when clicked (used to open browse dialogs)."""
+ clicked = pyqtSignal()
+
+ def mousePressEvent(self, event):
+ try:
+ self.clicked.emit()
+ except Exception:
+ pass
+ super().mousePressEvent(event)
+
+# ------------------------------------------------------------------
+# Job Dialog
+# ------------------------------------------------------------------
+class JobDialog(QDialog):
+ def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None, *, default_interval_seconds: Optional[int] = None):
+ super().__init__(parent)
+ self.setWindowTitle("Replication Job")
+ self.setModal(True)
+ self.setFixedHeight(400)
+ self.setMinimumHeight(400)
+ self.setMinimumWidth(1200)
+
+ job = job or {}
+ self._original_job = job
+
+ # Default interval for new schedules (seconds). Provided by the app (e.g. Configuration service.defaultInterval).
+ try:
+ di = int(default_interval_seconds) if default_interval_seconds is not None else 3600
+ except Exception:
+ di = 3600
+ if di <= 0:
+ di = 3600
+ self._default_interval_seconds = di
+
+ layout = QVBoxLayout(self)
+ layout.setAlignment(Qt.AlignTop)
+ layout.setSizeConstraint(QLayout.SetMinimumSize)
+
+ # ------------------------------
+ # Name row
+ # ------------------------------
+ name_row = QHBoxLayout()
+ name_row.addWidget(QLabel("Name"))
+ self.name = QLineEdit(job.get("name", ""))
+ name_row.addWidget(self.name, 1)
+ layout.addLayout(name_row)
+
+ # Helper to get endpoint dict from job or fallback
+ def _get_endpoint(key_endpoint: str, key_str: str, default_type: str = "local") -> Dict[str, Any]:
+ ep = job.get(key_endpoint)
+ if isinstance(ep, dict):
+ return dict(ep)
+ return {"type": default_type, "location": job.get(key_str, ""), "auth": {}}
+
+ self._source_ep = _get_endpoint("sourceEndpoint", "source", "local")
+ self._target_ep = _get_endpoint("targetEndpoint", "target", "local")
+
+ # ------------------------------
+ # Two-column Source / Destination
+ # ------------------------------
+ cols = QHBoxLayout()
+ cols.setSpacing(20)
+ cols.setAlignment(Qt.AlignTop)
+
+ src_frame = QFrame()
+ src_frame.setObjectName("EndpointFrame")
+ src_frame.setFrameShape(QFrame.NoFrame)
+ src_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
+ src_frame.setMinimumHeight(0)
+ src_frame.setStyleSheet(
+ "#EndpointFrame { border: 1px solid rgba(255,255,255,0.12); border-radius: 8px; padding: 10px; }"
+ )
+ src_col = QVBoxLayout(src_frame)
+ src_col.setContentsMargins(10, 10, 10, 10)
+ src_col.setSpacing(8)
+ src_col.setAlignment(Qt.AlignTop)
+
+ dst_frame = QFrame()
+ dst_frame.setObjectName("EndpointFrame")
+ dst_frame.setFrameShape(QFrame.NoFrame)
+ dst_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
+ dst_frame.setMinimumHeight(0)
+ dst_frame.setStyleSheet(
+ "#EndpointFrame { border: 1px solid rgba(255,255,255,0.12); border-radius: 8px; padding: 10px; }"
+ )
+ dst_col = QVBoxLayout(dst_frame)
+ dst_col.setContentsMargins(10, 10, 10, 10)
+ dst_col.setSpacing(8)
+ dst_col.setAlignment(Qt.AlignTop)
+
+ src_title = QLabel("Source")
+ src_title.setStyleSheet("font-weight: 600;")
+ dst_title = QLabel("Destination")
+ dst_title.setStyleSheet("font-weight: 600;")
+
+ src_col.addWidget(src_title)
+ dst_col.addWidget(dst_title)
+
+ (
+ self._source_type_combo,
+ self._source_location_edit,
+ self._source_port_spin,
+ self._source_endpoint_row,
+ self._source_auth_widget,
+ self._source_auth_widgets,
+ ) = self._build_endpoint(existing=self._source_ep)
+
+ (
+ self._target_type_combo,
+ self._target_location_edit,
+ self._target_port_spin,
+ self._target_endpoint_row,
+ self._target_auth_widget,
+ self._target_auth_widgets,
+ ) = self._build_endpoint(existing=self._target_ep)
+
+ src_col.addWidget(self._source_endpoint_row)
+ src_col.addWidget(self._source_auth_widget)
+
+ dst_col.addWidget(self._target_endpoint_row)
+ dst_col.addWidget(self._target_auth_widget)
+
+ cols.addWidget(src_frame, 1)
+ cols.addWidget(dst_frame, 1)
+ layout.addLayout(cols)
+
+ # ------------------------------
+ # Mode + Direction on same line
+ # ------------------------------
+ self.mode = QComboBox()
+ # Mode is now a first-class control.
+ # mirror => allowDeletion=True
+ # incremental => allowDeletion=False
+ self.mode.addItems(["mirror", "incremental"])
+
+ # Backward compatibility:
+ # - Older jobs may only have allowDeletion without a mode.
+ # - Some jobs may still have mode="mirror".
+ mode_val = str(job.get("mode") or "").strip().lower()
+ if mode_val not in ("mirror", "incremental"):
+ # Infer from allowDeletion when mode is missing/legacy.
+ mode_val = "mirror" if bool(job.get("allowDeletion", False)) else "incremental"
+
+ idx = self.mode.findText(mode_val)
+ if idx >= 0:
+ self.mode.setCurrentIndex(idx)
+
+ self.direction = QComboBox()
+ self.direction.addItems(["unidirectional", "bidirectional"])
+ direction_val = job.get("direction", "unidirectional")
+ idx = self.direction.findText(direction_val)
+ if idx >= 0:
+ self.direction.setCurrentIndex(idx)
+
+ mode_dir_row = QHBoxLayout()
+
+ mode_wrap = QWidget()
+ mode_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
+ mode_lay = QHBoxLayout(mode_wrap)
+ mode_lay.setContentsMargins(0, 0, 0, 0)
+ mode_lay.setSpacing(8)
+ mode_lay.addWidget(QLabel("Mode"))
+ mode_lay.addWidget(self.mode, 1)
+
+ dir_wrap = QWidget()
+ dir_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
+ dir_lay = QHBoxLayout(dir_wrap)
+ dir_lay.setContentsMargins(0, 0, 0, 0)
+ dir_lay.setSpacing(8)
+ dir_lay.addWidget(QLabel("Direction"))
+ dir_lay.addWidget(self.direction, 1)
+
+ mode_dir_row.addWidget(mode_wrap, 1)
+ mode_dir_row.addWidget(dir_wrap, 1)
+ layout.addLayout(mode_dir_row)
+
+ # ------------------------------
+ # Other options
+ # ------------------------------
+ opts_row = QHBoxLayout()
+
+ # Deletion behavior is controlled by mode now.
+ # Keep the UI simple: show metadata + enabled toggles here.
+ self.preserve_metadata = QCheckBox("Preserve metadata")
+ self.preserve_metadata.setChecked(bool(job.get("preserveMetadata", True)))
+ self.enabled = QCheckBox("Enabled")
+ self.enabled.setChecked(bool(job.get("enabled", True)))
+
+ opts_row.addWidget(self.preserve_metadata)
+ opts_row.addSpacing(16)
+ opts_row.addWidget(self.enabled)
+ opts_row.addStretch(1)
+
+ # Schedule button (UI stays here)
+ self._schedule_btn = QPushButton("Schedule…")
+ self._schedule_btn.clicked.connect(self._open_schedule)
+ opts_row.addWidget(self._schedule_btn)
+
+ layout.addLayout(opts_row)
+
+ # ------------------------------
+ # Buttons
+ # ------------------------------
+ btn_row = QHBoxLayout()
+ btn_row.addStretch(1)
+ cancel_btn = QPushButton("Cancel")
+ ok_btn = QPushButton("Save")
+ cancel_btn.clicked.connect(self.reject)
+ ok_btn.clicked.connect(self._on_ok)
+ btn_row.addWidget(cancel_btn)
+ btn_row.addWidget(ok_btn)
+ layout.addLayout(btn_row)
+
+ # Wire type changes
+ self._source_type_combo.currentIndexChanged.connect(lambda _i: self._on_type_changed(
+ self._source_type_combo,
+ self._source_location_edit,
+ self._source_port_spin,
+ self._source_auth_widget,
+ self._source_auth_widgets,
+ ))
+ self._target_type_combo.currentIndexChanged.connect(lambda _i: self._on_type_changed(
+ self._target_type_combo,
+ self._target_location_edit,
+ self._target_port_spin,
+ self._target_auth_widget,
+ self._target_auth_widgets,
+ ))
+
+ # Trigger initial state
+ self._on_type_changed(
+ self._source_type_combo,
+ self._source_location_edit,
+ self._source_port_spin,
+ self._source_auth_widget,
+ self._source_auth_widgets,
+ initial=True,
+ )
+ self._on_type_changed(
+ self._target_type_combo,
+ self._target_location_edit,
+ self._target_port_spin,
+ self._target_auth_widget,
+ self._target_auth_widgets,
+ initial=True,
+ )
+
+ def _open_schedule(self) -> None:
+ job = self._original_job if isinstance(self._original_job, dict) else {}
+ dlg = ScheduleDialog(self, job=job, default_interval_seconds=self._default_interval_seconds)
+ if dlg.exec_() == QDialog.Accepted:
+ # store schedule back into original job dict so value() preserves it
+ if not isinstance(self._original_job, dict):
+ self._original_job = {}
+ self._original_job["schedule"] = dlg.value()
+
+ # ------------------------------------------------------------------
+ # Endpoint UI
+ # ------------------------------------------------------------------
+
+ def _build_endpoint(self, existing: Dict[str, Any]):
+ type_combo = QComboBox()
+ type_combo.addItems(["local", "smb"])
+ idx = type_combo.findText(existing.get("type", "local"))
+ if idx >= 0:
+ type_combo.setCurrentIndex(idx)
+ if idx < 0:
+ type_combo.setCurrentIndex(0)
+ type_combo.setFixedWidth(110)
+ type_combo.setProperty("existing_type", existing.get("type", "local"))
+ type_combo.setProperty("existing_auth", existing.get("auth", {}) or {})
+
+ location_edit = BrowseLineEdit(existing.get("location", ""))
+
+ port_spin = QSpinBox()
+ port_spin.setRange(1, 65535)
+ port_spin.setFixedWidth(110)
+ # Ports are not used for local/SMB endpoints.
+ port_spin.setValue(1)
+ port_spin.setVisible(False)
+
+ endpoint_fields = QWidget()
+ fields_lay = QHBoxLayout(endpoint_fields)
+ fields_lay.setContentsMargins(0, 0, 0, 0)
+ fields_lay.setSpacing(8)
+ fields_lay.addWidget(type_combo)
+ fields_lay.addWidget(location_edit, 1)
+ fields_lay.addWidget(port_spin)
+
+ def _browse_location():
+ try:
+ typ = (type_combo.currentText() or "").lower()
+ if typ != "local":
+ return
+ start_dir = location_edit.text().strip() or ""
+ # If the user typed a file path, prefer its directory.
+ if start_dir and not start_dir.endswith("/") and not start_dir.endswith("\\"):
+ try:
+ import os
+ if os.path.isfile(start_dir):
+ start_dir = os.path.dirname(start_dir)
+ except Exception:
+ pass
+ selected = QFileDialog.getExistingDirectory(self, "Select folder", start_dir)
+ if selected:
+ location_edit.setText(selected)
+ except Exception:
+ return
+
+ # Click-to-browse for local paths (no extra button needed).
+ location_edit.clicked.connect(_browse_location)
+
+ endpoint_row = QWidget()
+ endpoint_row.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
+ endpoint_form = QFormLayout(endpoint_row)
+ endpoint_form.setContentsMargins(0, 0, 0, 0)
+ endpoint_form.setSpacing(6)
+ endpoint_form.addRow(endpoint_fields)
+
+ auth_widget = QFrame()
+ auth_widget.setFrameShape(QFrame.NoFrame)
+ auth_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
+ auth_widget.setMinimumHeight(0)
+
+ auth_lay = QVBoxLayout(auth_widget)
+ auth_lay.setContentsMargins(0, 0, 0, 0)
+ auth_lay.setSpacing(6)
+ auth_lay.setAlignment(Qt.AlignTop)
+
+ auth_title = QLabel("Authentication")
+ auth_title.setStyleSheet("font-weight: 600;")
+
+ widgets: Dict[str, Any] = {}
+
+ # SMB auth
+ smb_wrap = QWidget()
+ smb_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
+ smb_form = QFormLayout(smb_wrap)
+ smb_form.setContentsMargins(0, 0, 0, 0)
+ smb_guest = QCheckBox("Login as Guest")
+ smb_user_lbl = QLabel("Username (user or user@domain)")
+ smb_username = QLineEdit()
+ smb_pass_lbl = QLabel("Password")
+ smb_password = QLineEdit()
+ smb_password.setEchoMode(QLineEdit.Password)
+
+ smb_auth = existing.get("auth", {}) if existing.get("type") == "smb" else {}
+ smb_guest.setChecked(bool(smb_auth.get("guest", True)))
+ smb_username.setText(smb_auth.get("username", ""))
+ smb_password.setText(smb_auth.get("password", ""))
+
+ smb_form.addRow(smb_guest)
+ smb_form.addRow(smb_user_lbl, smb_username)
+ smb_form.addRow(smb_pass_lbl, smb_password)
+
+ def _smb_guest_update():
+ guest = smb_guest.isChecked()
+ smb_user_lbl.setVisible(not guest)
+ smb_username.setVisible(not guest)
+ smb_pass_lbl.setVisible(not guest)
+ smb_password.setVisible(not guest)
+
+ smb_guest.stateChanged.connect(_smb_guest_update)
+ _smb_guest_update()
+
+ auth_lay.addWidget(auth_title)
+ auth_lay.addWidget(smb_wrap)
+
+ widgets["smb"] = {
+ "wrap": smb_wrap,
+ "guest": smb_guest,
+ "user_lbl": smb_user_lbl,
+ "username": smb_username,
+ "pass_lbl": smb_pass_lbl,
+ "password": smb_password,
+ }
+
+ return type_combo, location_edit, port_spin, endpoint_row, auth_widget, widgets
+
+ def _on_type_changed(
+ self,
+ type_combo: QComboBox,
+ location_edit: QLineEdit,
+ port_spin: QSpinBox,
+ auth_widget: QWidget,
+ widgets: Dict[str, Any],
+ initial: bool = False,
+ ):
+ typ = type_combo.currentText()
+
+ placeholder = {
+ "local": "Local path (e.g. /data or C:\\Data)",
+ "smb": "SMB path (e.g. \\\\SERVER\\Share\\Folder)",
+ }.get(typ, "")
+ location_edit.setPlaceholderText(placeholder)
+ # For local endpoints, clicking the location field opens a folder chooser.
+ # For non-local endpoints, keep normal typing behavior.
+ if (typ or "").lower() == "local":
+ location_edit.setCursorPosition(len(location_edit.text()))
+ # Ports are not used for local/SMB endpoints.
+ port_spin.setVisible(False)
+
+ if typ == "local":
+ auth_widget.setVisible(False)
+ auth_widget.setMaximumHeight(0)
+ else:
+ auth_widget.setVisible(True)
+ auth_widget.setMaximumHeight(16777215)
+
+ # Hide all auth sections
+ for key in ("smb",):
+ if key in widgets and "wrap" in widgets[key]:
+ widgets[key]["wrap"].setVisible(False)
+
+ # Show only the relevant auth section
+ if typ == "smb":
+ widgets["smb"]["wrap"].setVisible(True)
+
+ auth_widget.adjustSize()
+ if auth_widget.parentWidget() is not None:
+ auth_widget.parentWidget().adjustSize()
+ self.adjustSize()
+
+ # ------------------------------------------------------------------
+ # Validation
+ # ------------------------------------------------------------------
+
+ def _on_ok(self):
+ if not self.name.text().strip():
+ MsgBox.show(self, "Job", "Name is required.", icon="warning")
+ return
+
+ src_type = self._source_type_combo.currentText()
+ tgt_type = self._target_type_combo.currentText()
+
+ src_loc = self._source_location_edit.text().strip()
+ tgt_loc = self._target_location_edit.text().strip()
+
+ if not src_loc:
+ MsgBox.show(self, "Job", "Source location is required.", icon="warning")
+ return
+ if not tgt_loc:
+ MsgBox.show(self, "Job", "Target location is required.", icon="warning")
+ return
+
+ if src_type == "smb":
+ w = self._source_auth_widgets["smb"]
+ if not w["guest"].isChecked():
+ # Either Guest is enabled OR both username+password are provided.
+ # If either field is missing, force Guest and block save.
+ if not w["username"].text().strip() or not w["password"].text().strip():
+ w["guest"].setChecked(True)
+ MsgBox.show(
+ self,
+ "Job",
+ "For the Source SMB endpoint, either enable Guest or provide BOTH a username and a password.",
+ icon="warning",
+ )
+ return
+
+ if tgt_type == "smb":
+ w = self._target_auth_widgets["smb"]
+ if not w["guest"].isChecked():
+ # Either Guest is enabled OR both username+password are provided.
+ # If either field is missing, force Guest and block save.
+ if not w["username"].text().strip() or not w["password"].text().strip():
+ w["guest"].setChecked(True)
+ MsgBox.show(
+ self,
+ "Job",
+ "For the Destination SMB endpoint, either enable Guest or provide BOTH a username and a password.",
+ icon="warning",
+ )
+ return
+
+ self.accept()
+
+ # ------------------------------------------------------------------
+ # Value extraction
+ # ------------------------------------------------------------------
+
+ def value(self) -> Dict[str, Any]:
+ def _extract(type_combo: QComboBox, location_edit: QLineEdit, port_spin: QSpinBox, widgets: Dict[str, Any]) -> Dict[str, Any]:
+ typ = type_combo.currentText()
+ location = location_edit.text().strip()
+ auth: Dict[str, Any] = {}
+
+ if typ == "local":
+ auth = {}
+ elif typ == "smb":
+ w = widgets["smb"]
+ guest = bool(w["guest"].isChecked())
+ # If Guest is enabled, credentials are irrelevant; clear them on save.
+ auth = {
+ "guest": guest,
+ "username": "" if guest else w["username"].text().strip(),
+ "password": "" if guest else w["password"].text(),
+ }
+ else:
+ # Unsupported/legacy types fall back to local
+ typ = "local"
+ auth = {}
+
+ return {"type": typ, "location": location, "auth": auth}
+
+ source_ep = _extract(self._source_type_combo, self._source_location_edit, self._source_port_spin, self._source_auth_widgets)
+ target_ep = _extract(self._target_type_combo, self._target_location_edit, self._target_port_spin, self._target_auth_widgets)
+
+ val: Dict[str, Any] = {
+ "name": self.name.text().strip(),
+ "sourceEndpoint": source_ep,
+ "targetEndpoint": target_ep,
+ "source": source_ep["location"], # backward compatibility
+ "target": target_ep["location"], # backward compatibility
+ # Backward compatible field: allowDeletion derives from mode.
+ "allowDeletion": bool((self.mode.currentText() or "").lower() == "mirror"),
+ "preserveMetadata": bool(self.preserve_metadata.isChecked()),
+ "mode": self.mode.currentText(),
+ "direction": self.direction.currentText(),
+ "enabled": bool(self.enabled.isChecked()),
+ }
+
+ # Preserve schedule (and update if ScheduleDialog was used)
+ if self._original_job and isinstance(self._original_job, dict):
+ sched = self._original_job.get("schedule")
+ if sched is not None:
+ val["schedule"] = sched
+
+ return val
+ # Remove any remaining references to self.allow_deletion in this class.
+
+# ------------------------------------------------------------------
+# Schedule Dialog
+# ------------------------------------------------------------------
+class ScheduleDialog(QDialog):
+ """Schedule editor UI.
+
+ Persists as:
+ schedule = {
+ "enabled": bool,
+ "intervalSeconds": int, # computed fallback (minimum per-day interval if enabled, else default)
+ "windows": {
+ "0":[{"start":"22:00","end":"06:00","intervalSeconds":3600}],
+ ...
+ }
+ }
+ """
+
+ _DAYS: List[Tuple[int, str]] = [
+ (0, "Monday"),
+ (1, "Tuesday"),
+ (2, "Wednesday"),
+ (3, "Thursday"),
+ (4, "Friday"),
+ (5, "Saturday"),
+ (6, "Sunday"),
+ ]
+
+ def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None, *, default_interval_seconds: Optional[int] = None):
+ super().__init__(parent)
+ self.setWindowTitle("Schedule")
+ self.setModal(True)
+
+ # Default interval for new schedules (seconds). Provided by the app (e.g. Configuration service.defaultInterval).
+ try:
+ di = int(default_interval_seconds) if default_interval_seconds is not None else 3600
+ except Exception:
+ di = 3600
+ if di <= 0:
+ di = 3600
+ self._default_interval_seconds = di
+
+ job = job or {}
+ schedule = job.get("schedule") if isinstance(job.get("schedule"), dict) else {}
+
+ # IMPORTANT semantics:
+ # - schedule.enabled == False => job can be run manually but will NOT be executed by the service.
+ # - schedule.enabled == True => job may run in the service, but ONLY within selected day windows.
+ # - enabled with no days selected means "run never" (no service runs).
+
+ if not schedule:
+ # New schedule default: enabled, every day, all day, default interval
+ schedule = {"enabled": True, "intervalSeconds": int(self._default_interval_seconds), "windows": {}}
+ for wd in range(7):
+ schedule["windows"][str(wd)] = [
+ {"start": "00:00", "end": "23:59", "intervalSeconds": int(self._default_interval_seconds)}
+ ]
+ else:
+ # Existing schedule: preserve intent.
+ # If windows is missing/empty, DO NOT auto-populate days (that would change meaning).
+ if not isinstance(schedule.get("windows"), dict):
+ schedule["windows"] = {}
+
+ # Ensure each existing day window has intervalSeconds
+ try:
+ for _k, _v in list(schedule["windows"].items()):
+ if isinstance(_v, list) and _v and isinstance(_v[0], dict) and "intervalSeconds" not in _v[0]:
+ _v[0]["intervalSeconds"] = int(schedule.get("intervalSeconds") or self._default_interval_seconds)
+ except Exception:
+ pass
+
+ self._windows: Dict[str, Any] = schedule.get("windows", {}) if isinstance(schedule.get("windows", {}), dict) else {}
+
+ layout = QVBoxLayout(self)
+ layout.setAlignment(Qt.AlignTop)
+ layout.setSizeConstraint(QLayout.SetMinimumSize)
+
+ form = QFormLayout()
+ layout.addLayout(form)
+
+ self.enabled = QCheckBox()
+ self.enabled.setChecked(bool(schedule.get("enabled", False)))
+
+ form.addRow("Enabled", self.enabled)
+
+ hint = QLabel("Tip: Enable the schedule to allow service runs. If enabled and no days are selected, the job will not run in the service.")
+ hint.setWordWrap(True)
+ hint.setStyleSheet("opacity: 0.75;")
+ layout.addWidget(hint)
+
+ # Windows editor
+ win_frame = QFrame()
+ win_frame.setFrameShape(QFrame.NoFrame)
+ win_lay = QVBoxLayout(win_frame)
+ win_lay.setContentsMargins(0, 0, 0, 0)
+ win_lay.setSpacing(6)
+
+ title = QLabel("Allowed run windows")
+ title.setStyleSheet("font-weight: 600;")
+ win_lay.addWidget(title)
+
+ self._day_controls: Dict[int, Dict[str, Any]] = {}
+
+ for wd, label in self._DAYS:
+ row = QHBoxLayout()
+ row.setSpacing(10)
+
+ day_enabled = QCheckBox(label)
+ start = QTimeEdit()
+ end = QTimeEdit()
+ start.setDisplayFormat("HH:mm")
+ end.setDisplayFormat("HH:mm")
+ start.setTime(QTime(0, 0))
+ end.setTime(QTime(23, 59))
+
+ interval = QSpinBox()
+ interval.setRange(1, 604800) # 1s .. 7 days
+ interval.setFixedWidth(130)
+ interval.setValue(int(schedule.get("intervalSeconds", self._default_interval_seconds) or self._default_interval_seconds))
+
+ # Load from existing windows if present
+ key = str(wd)
+ day_windows = self._windows.get(key)
+ if isinstance(day_windows, list) and len(day_windows) > 0 and isinstance(day_windows[0], dict):
+ w0 = day_windows[0]
+ s = str(w0.get("start", "00:00"))
+ e = str(w0.get("end", "23:59"))
+ day_enabled.setChecked(True)
+ start.setTime(self._parse_qtime(s, QTime(0, 0)))
+ end.setTime(self._parse_qtime(e, QTime(23, 59)))
+ try:
+ interval.setValue(int(w0.get("intervalSeconds", schedule.get("intervalSeconds", self._default_interval_seconds)) or self._default_interval_seconds))
+ except Exception:
+ interval.setValue(int(schedule.get("intervalSeconds", self._default_interval_seconds) or self._default_interval_seconds))
+ else:
+ day_enabled.setChecked(False)
+ try:
+ interval.setValue(int(schedule.get("intervalSeconds", self._default_interval_seconds) or self._default_interval_seconds))
+ except Exception:
+ interval.setValue(int(self._default_interval_seconds))
+
+ # Disable edits when not enabled
+ def _apply_enabled_state(_=None, *, _day_enabled=day_enabled, _start=start, _end=end, _interval=interval):
+ en = _day_enabled.isChecked()
+ _start.setEnabled(en)
+ _end.setEnabled(en)
+ _interval.setEnabled(en)
+
+ day_enabled.stateChanged.connect(_apply_enabled_state)
+ _apply_enabled_state()
+
+ row.addWidget(day_enabled, 1)
+ row.addWidget(QLabel("Start"))
+ row.addWidget(start)
+ row.addWidget(QLabel("End"))
+ row.addWidget(end)
+ row.addWidget(QLabel("Interval (s)"))
+ row.addWidget(interval)
+
+ win_lay.addLayout(row)
+
+ self._day_controls[wd] = {"enabled": day_enabled, "start": start, "end": end, "interval": interval}
+
+ layout.addWidget(win_frame)
+
+ btn_row = QHBoxLayout()
+ btn_row.addStretch(1)
+ cancel_btn = QPushButton("Cancel")
+ ok_btn = QPushButton("Save")
+ cancel_btn.clicked.connect(self.reject)
+ ok_btn.clicked.connect(self._on_ok)
+ btn_row.addWidget(cancel_btn)
+ btn_row.addWidget(ok_btn)
+ layout.addLayout(btn_row)
+
+ self._sync_enabled_state()
+
+ self.enabled.stateChanged.connect(lambda _i: self._sync_enabled_state())
+
+ def _sync_enabled_state(self):
+ en = self.enabled.isChecked()
+ for wd, ctrls in self._day_controls.items():
+ ctrls["enabled"].setEnabled(en)
+ # if schedule disabled, visually disable time edits too
+ if not en:
+ ctrls["start"].setEnabled(False)
+ ctrls["end"].setEnabled(False)
+ ctrls["interval"].setEnabled(False)
+ else:
+ # restore per-day checkbox logic
+ day_en = ctrls["enabled"].isChecked()
+ ctrls["start"].setEnabled(day_en)
+ ctrls["end"].setEnabled(day_en)
+ ctrls["interval"].setEnabled(day_en)
+
+ def _parse_qtime(self, s: str, default: QTime) -> QTime:
+ try:
+ parts = s.strip().split(":")
+ if len(parts) != 2:
+ return default
+ hh = int(parts[0])
+ mm = int(parts[1])
+ if hh < 0 or hh > 23 or mm < 0 or mm > 59:
+ return default
+ return QTime(hh, mm)
+ except Exception:
+ return default
+
+ def _on_ok(self):
+ if self.enabled.isChecked():
+ any_day = any(bool(ctrls["enabled"].isChecked()) for _wd, ctrls in self._day_controls.items())
+ if not any_day:
+ MsgBox.show(self, "Schedule", "Select at least one day when the schedule is enabled (otherwise the job will never run in the service).", icon="warning")
+ return
+ for _wd, ctrls in self._day_controls.items():
+ if not ctrls["enabled"].isChecked():
+ continue
+ if int(ctrls["interval"].value() or 0) < 1:
+ MsgBox.show(self, "Schedule", "Per-day interval must be at least 1 second for enabled days.", icon="warning")
+ return
+
+ # Basic validation: ensure enabled days have valid times (QTimeEdit ensures format)
+ # Allow overnight windows naturally (start > end) — that's intended.
+ self.accept()
+
+ def value(self) -> Dict[str, Any]:
+ windows: Dict[str, Any] = {}
+ enabled_intervals = []
+ if self.enabled.isChecked():
+ for wd, ctrls in self._day_controls.items():
+ if not ctrls["enabled"].isChecked():
+ continue
+ s = ctrls["start"].time().toString("HH:mm")
+ e = ctrls["end"].time().toString("HH:mm")
+ itv = int(ctrls["interval"].value())
+ windows[str(wd)] = [{"start": s, "end": e, "intervalSeconds": itv}]
+ enabled_intervals.append(itv)
+
+ # Compute fallback intervalSeconds: min per-day interval if enabled, else default
+ if self.enabled.isChecked() and enabled_intervals:
+ computed_interval_seconds = min(enabled_intervals)
+ else:
+ computed_interval_seconds = int(self._default_interval_seconds)
+
+ return {
+ "enabled": bool(self.enabled.isChecked()),
+ "intervalSeconds": int(computed_interval_seconds),
+ "windows": windows,
+ }
diff --git a/dist/macos/Replicator.app/Contents/_CodeSignature/CodeResources b/dist/macos/Replicator.app/Contents/_CodeSignature/CodeResources
new file mode 100644
index 00000000..7ae972c1
--- /dev/null
+++ b/dist/macos/Replicator.app/Contents/_CodeSignature/CodeResources
@@ -0,0 +1,26051 @@
+
+
+
+
+ files
+
+ Resources/PyQt5/Qt5/translations/qt_ar.qm
+
+ 1a6wmuVXzu+3WJcuxKxiTN3J5qc=
+
+ Resources/PyQt5/Qt5/translations/qt_bg.qm
+
+ hZmrm3Mb/IT27segEp85b7j+xOo=
+
+ Resources/PyQt5/Qt5/translations/qt_ca.qm
+
+ 6+9u2EYCGM6N9zVlmoy81pNgCsY=
+
+ Resources/PyQt5/Qt5/translations/qt_cs.qm
+
+ tOlfgngSHiVJ+LtrXa8UlvFzin0=
+
+ Resources/PyQt5/Qt5/translations/qt_da.qm
+
+ /yRA27/gSthsbyhUJqr9SaiVsSg=
+
+ Resources/PyQt5/Qt5/translations/qt_de.qm
+
+ 3q8bU8P+5ssohAQY0AYK+k1Z0/w=
+
+ Resources/PyQt5/Qt5/translations/qt_en.qm
+
+ SIT9mvaJBke3rxrvpX84zKSa2Jk=
+
+ Resources/PyQt5/Qt5/translations/qt_es.qm
+
+ 3mUOlqnBMNNfikmCAnc+9/yHXSc=
+
+ Resources/PyQt5/Qt5/translations/qt_fa.qm
+
+ 7bb1N88KN4Clc7iC/eXiDPuXnpw=
+
+ Resources/PyQt5/Qt5/translations/qt_fi.qm
+
+ vyO0wTa4Y7EOdwAZot9i/JiIWd8=
+
+ Resources/PyQt5/Qt5/translations/qt_fr.qm
+
+ YjEt/zxM04uuhFbJgWAdDYlgD2M=
+
+ Resources/PyQt5/Qt5/translations/qt_gd.qm
+
+ JRI2FUgpR3chduBV5KdAQ7L7yqA=
+
+ Resources/PyQt5/Qt5/translations/qt_gl.qm
+
+ 615yBTVc/GvLTfJ+IkB5hCyXspY=
+
+ Resources/PyQt5/Qt5/translations/qt_he.qm
+
+ g2jzIDnMB3ahuVyd7V/myeoNk/0=
+
+ Resources/PyQt5/Qt5/translations/qt_help_ar.qm
+
+ V1G+loF7tq58nanx+6f0LzHPzF0=
+
+ Resources/PyQt5/Qt5/translations/qt_help_bg.qm
+
+ cla+OQuIoFPAJSSIxEO+Qvby2So=
+
+ Resources/PyQt5/Qt5/translations/qt_help_ca.qm
+
+ gJ3QwiHjNugS/5rCwRTO1zRsmcI=
+
+ Resources/PyQt5/Qt5/translations/qt_help_cs.qm
+
+ fwYc8MaZ0SWlUx40gMIZZEUvReo=
+
+ Resources/PyQt5/Qt5/translations/qt_help_da.qm
+
+ x38KFIJiqRZ58ZaJ5HkLdU1F1dU=
+
+ Resources/PyQt5/Qt5/translations/qt_help_de.qm
+
+ cIhut+9WayltCBS9TCRArBdmmdY=
+
+ Resources/PyQt5/Qt5/translations/qt_help_en.qm
+
+ SIT9mvaJBke3rxrvpX84zKSa2Jk=
+
+ Resources/PyQt5/Qt5/translations/qt_help_es.qm
+
+ qRiuFaDfGHx3ib6FmagOJ58DmWQ=
+
+ Resources/PyQt5/Qt5/translations/qt_help_fr.qm
+
+ RyktBoFy+8ncDZvi9HnokKN84Tg=
+
+ Resources/PyQt5/Qt5/translations/qt_help_gl.qm
+
+ WJzgmfLB2SWBtc8OF75Jor8AFNQ=
+
+ Resources/PyQt5/Qt5/translations/qt_help_hr.qm
+
+ 9KxH3kxpESyJlULlvLBWxN6qS1g=
+
+ Resources/PyQt5/Qt5/translations/qt_help_hu.qm
+
+ 1vB59A+7gTsCk8HSIQuucIQJL+w=
+
+ Resources/PyQt5/Qt5/translations/qt_help_it.qm
+
+ ZZNaZ8c/Gfz2Aj+5UDClrK+dohw=
+
+ Resources/PyQt5/Qt5/translations/qt_help_ja.qm
+
+ m3N+0F+A/l082PWIzOwWuxHdNWA=
+
+ Resources/PyQt5/Qt5/translations/qt_help_ko.qm
+
+ rqIuSg0hOWczgCx6tzjd0Dc3t9Y=
+
+ Resources/PyQt5/Qt5/translations/qt_help_nl.qm
+
+ XBilR68q3A4KAhAnMG4X5XekBDY=
+
+ Resources/PyQt5/Qt5/translations/qt_help_nn.qm
+
+ NQMMNLoLNSxhsFkszNhWkRXX7rw=
+
+ Resources/PyQt5/Qt5/translations/qt_help_pl.qm
+
+ igm9OJmFQ7dOJnNHjt1U+0u90Gg=
+
+ Resources/PyQt5/Qt5/translations/qt_help_pt_BR.qm
+
+ G1yOhdQK6XUsQosgN/Z+XsDbrL0=
+
+ Resources/PyQt5/Qt5/translations/qt_help_ru.qm
+
+ Ogw2mHKxEqFXKqF+64FLFosiXZg=
+
+ Resources/PyQt5/Qt5/translations/qt_help_sk.qm
+
+ 4+X0WCzF+v5t9DZE0RSEhhAjwIQ=
+
+ Resources/PyQt5/Qt5/translations/qt_help_sl.qm
+
+ yU6DJZXsdlNwVTRo+HwC2359E4o=
+
+ Resources/PyQt5/Qt5/translations/qt_help_tr.qm
+
+ WC7gedvUIADDeOBwHSZAV1BSTbo=
+
+ Resources/PyQt5/Qt5/translations/qt_help_uk.qm
+
+ XfOyE9mFo7vbR2s3t3gNfX3xfkE=
+
+ Resources/PyQt5/Qt5/translations/qt_help_zh_CN.qm
+
+ TtQ/AmgRy0aMnHkOQJjK2FQ1/t0=
+
+ Resources/PyQt5/Qt5/translations/qt_help_zh_TW.qm
+
+ ZMyrJ0P8wW5D8Le2g7ws2XbKbtg=
+
+ Resources/PyQt5/Qt5/translations/qt_hr.qm
+
+ d3T7h/yC2sXRmpD6QEv5Gwcu5/s=
+
+ Resources/PyQt5/Qt5/translations/qt_hu.qm
+
+ TFaDax+xDZkDspnLy5JZR9UVtMg=
+
+ Resources/PyQt5/Qt5/translations/qt_it.qm
+
+ P7p3PVjm02mcIKtBruaAHnHi3a4=
+
+ Resources/PyQt5/Qt5/translations/qt_ja.qm
+
+ KzR2WINQxThOD5pR/y42WeibSEY=
+
+ Resources/PyQt5/Qt5/translations/qt_ko.qm
+
+ VycQkhiyIuO2VKjMmTPpcOt8IRg=
+
+ Resources/PyQt5/Qt5/translations/qt_lt.qm
+
+ 4idmpJYS95FWxVDYPGwjA0XdpDM=
+
+ Resources/PyQt5/Qt5/translations/qt_lv.qm
+
+ Rugmg86ijYd8c6XOAvlluxEw/GI=
+
+ Resources/PyQt5/Qt5/translations/qt_nl.qm
+
+ 5o41fN93Ke0kKniRUrVv5YDXBmQ=
+
+ Resources/PyQt5/Qt5/translations/qt_nn.qm
+
+ VueyLTXZ3/UAHuBFRqhYxtv+4DQ=
+
+ Resources/PyQt5/Qt5/translations/qt_pl.qm
+
+ YQJv5gL9G4tEKg00HGvXWe7HVIg=
+
+ Resources/PyQt5/Qt5/translations/qt_pt_BR.qm
+
+ XZQ/HWLmXTKdACn9bBHpqy4xlzU=
+
+ Resources/PyQt5/Qt5/translations/qt_pt_PT.qm
+
+ zBErnJUTvPdJfzQXFotMip92QKk=
+
+ Resources/PyQt5/Qt5/translations/qt_ru.qm
+
+ Bl6YcTnF+4Cab5zfOEW815cH/bs=
+
+ Resources/PyQt5/Qt5/translations/qt_sk.qm
+
+ Cmfx7YykpRgvUEgG+NR9SZeJ8tI=
+
+ Resources/PyQt5/Qt5/translations/qt_sl.qm
+
+ nzyFwRWig+UjDR7q2EyMtzpx+gM=
+
+ Resources/PyQt5/Qt5/translations/qt_sv.qm
+
+ rlk0jgwuT4b5nabPXas7fpJQS3w=
+
+ Resources/PyQt5/Qt5/translations/qt_tr.qm
+
+ bkA8kJazBRlz4raB3+u8jdAkgw0=
+
+ Resources/PyQt5/Qt5/translations/qt_uk.qm
+
+ 8N8s/5E+WIt8rf2rv2n09jKy+Wo=
+
+ Resources/PyQt5/Qt5/translations/qt_zh_CN.qm
+
+ YyHpkogeDVql25FNKMxCxmcCjL0=
+
+ Resources/PyQt5/Qt5/translations/qt_zh_TW.qm
+
+ MI4rj3S4Y6Ya0LaPShjtBpZevqo=
+
+ Resources/PyQt5/Qt5/translations/qtbase_ar.qm
+
+ If0TGyO90bunu7hvPtXIOHb0Vjg=
+
+ Resources/PyQt5/Qt5/translations/qtbase_bg.qm
+
+ ZUQJzfP1UVVZV9Pbz41qDY8DpsU=
+
+ Resources/PyQt5/Qt5/translations/qtbase_ca.qm
+
+ ik1Qjx+tOPfYKIR28Xq8fBHwdec=
+
+ Resources/PyQt5/Qt5/translations/qtbase_cs.qm
+
+ Og53dTnFG7Ze52uOHY3OQ4bLyIY=
+
+ Resources/PyQt5/Qt5/translations/qtbase_da.qm
+
+ cLGbKmkU2n1in1d/iYdVNxPNXT8=
+
+ Resources/PyQt5/Qt5/translations/qtbase_de.qm
+
+ skmqHL+MJjbOV+tJMtU0kuTONqw=
+
+ Resources/PyQt5/Qt5/translations/qtbase_en.qm
+
+ SIT9mvaJBke3rxrvpX84zKSa2Jk=
+
+ Resources/PyQt5/Qt5/translations/qtbase_es.qm
+
+ gJ5YDNvy/9oQx3+L6brAgZeMECs=
+
+ Resources/PyQt5/Qt5/translations/qtbase_fa.qm
+
+ sK2+lQeQkkJCgG9nFxLFe1hLWPs=
+
+ Resources/PyQt5/Qt5/translations/qtbase_fi.qm
+
+ e1MTzaEmu3hjABSZ+2b7G1bCVfw=
+
+ Resources/PyQt5/Qt5/translations/qtbase_fr.qm
+
+ 7POzFW/+FFaezfgFzzvhLyloEmE=
+
+ Resources/PyQt5/Qt5/translations/qtbase_gd.qm
+
+ JPf/gJ4vEcV5zTiP6lpMVS/41NA=
+
+ Resources/PyQt5/Qt5/translations/qtbase_he.qm
+
+ 2znGuqRDqpuyCAQ+9/t+NAPBLZA=
+
+ Resources/PyQt5/Qt5/translations/qtbase_hr.qm
+
+ yVpBY4hSHvW9s+5dEenc1M4i670=
+
+ Resources/PyQt5/Qt5/translations/qtbase_hu.qm
+
+ u/NcBBd88pC0P30lM75EoV2SnQI=
+
+ Resources/PyQt5/Qt5/translations/qtbase_it.qm
+
+ mK16iJ2yBZ0webAzX5vxjegfA14=
+
+ Resources/PyQt5/Qt5/translations/qtbase_ja.qm
+
+ f0QDmauiMSD0D29Pyulm1iGhzGc=
+
+ Resources/PyQt5/Qt5/translations/qtbase_ko.qm
+
+ 8Q6IJ2Lc0uYAQb3WzFdZj8PfQ0M=
+
+ Resources/PyQt5/Qt5/translations/qtbase_lv.qm
+
+ lTjE2LualcDZ3FfHcIqZ3VOjLR8=
+
+ Resources/PyQt5/Qt5/translations/qtbase_nl.qm
+
+ XM6JLNftkkOwtXkj7pLHmtYmFTs=
+
+ Resources/PyQt5/Qt5/translations/qtbase_nn.qm
+
+ afxH/zozrcI0FfT4cnIFOZjWFJo=
+
+ Resources/PyQt5/Qt5/translations/qtbase_pl.qm
+
+ drlyJaEd0fd8rG7xRIEvkb2HNL0=
+
+ Resources/PyQt5/Qt5/translations/qtbase_pt_BR.qm
+
+ /RNzQXx+k9Y8l7O+eVbNfdgM6+k=
+
+ Resources/PyQt5/Qt5/translations/qtbase_ru.qm
+
+ QF9FNhpTfHkjwkDVGw/xxGYhwgM=
+
+ Resources/PyQt5/Qt5/translations/qtbase_sk.qm
+
+ AhG0kRtbdMwaRsD8qH079WMqpEo=
+
+ Resources/PyQt5/Qt5/translations/qtbase_tr.qm
+
+ Th5ro3ckKCJ8sDN0cAa0iH5dmtE=
+
+ Resources/PyQt5/Qt5/translations/qtbase_uk.qm
+
+ Y6FDJ9DPCUHW1rWL+n6LEDN/VXs=
+
+ Resources/PyQt5/Qt5/translations/qtbase_zh_CN.qm
+
+ NWH5X2RrIXLS6HCXO22B6YPtAKY=
+
+ Resources/PyQt5/Qt5/translations/qtbase_zh_TW.qm
+
+ YveJ6PoME8X9T+JQV2v0WwfIV4E=
+
+ Resources/base_library.zip
+
+ r+U0M7cyxfWAVEcfuVigk9EPV6o=
+
+ Resources/bin/nssm/windows/x86/nssm.exe
+
+ 4ZCMqrb5OEBK+Fp98PgPh3pNnuY=
+
+ Resources/bin/nssm/windows/x86_64/nssm.exe
+
+ R8ESwjx73yrySiC9US+R/2r3a8Y=
+
+ Resources/core/.editorconfig
+
+ DxfYYhYdYzC6/1Zfy84vpFXuPBU=
+
+ Resources/core/.git
+
+ ekJ8cBXsqunlZDE+6BzdIdl6aqA=
+
+ Resources/core/.github/FUNDING.yml
+
+ hxaQ/pPyo9rNEBbbUV9u8qQ+Fi8=
+
+ Resources/core/.github/ISSUE_TEMPLATE/bug_report.md
+
+ ZIRaVQsCKmW01emYVsJWQvMoEGA=
+
+ Resources/core/.github/ISSUE_TEMPLATE/feature_request.md
+
+ z1mvYgsfLJ3sqt01M32pmL+BXjU=
+
+ Resources/core/.github/no-response.yml
+
+ CkNspNn2JgYU6NTvNDsw5Eb0UGg=
+
+ Resources/core/.github/workflows/release.yml
+
+ KnRpAO3Ghl1vtyJ9bza5AHiPJ6w=
+
+ Resources/core/.gitignore
+
+ 2IwuMoV34WFggKrk/O5s3eBJabU=
+
+ Resources/core/LICENSE
+
+ MaPUYLs8fZiEUYfHFqMNuBxEthU=
+
+ Resources/core/README.md
+
+ UFIJhGIW6ujsqzNAsnblih0nViU=
+
+ Resources/core/SECURITY.md
+
+ VxzcDNEhmbLySdv04QzfsvF4kGI=
+
+ Resources/core/VERSION
+
+ ICwgfLjh5FknNDqp5863tzEoVfM=
+
+ Resources/core/__init__.py
+
+ uDfQJ3gsXr0khboXA4LcUN38OuQ=
+
+ Resources/core/__pycache__/__init__.cpython-311.pyc
+
+ SJUDeUmXPYs9LO5GYHuXGRjzLQI=
+
+ Resources/core/__pycache__/__init__.cpython-312.pyc
+
+ q4nbOsaZqOeXPnzz/U2FuewMi3E=
+
+ Resources/core/__pycache__/__init__.cpython-314.pyc
+
+ nQ4E9RijlW1J4lluAGlSAuW0sgo=
+
+ Resources/core/__pycache__/application.cpython-311.pyc
+
+ VKGYpga9T19n7NwuikhJKKLoIHA=
+
+ Resources/core/__pycache__/application.cpython-312.pyc
+
+ WJNnVhIHq9qXvVWjdjIBjjXYmWI=
+
+ Resources/core/__pycache__/application.cpython-314.pyc
+
+ vo1rpU38lxUYP9mFz1+nEmFW07k=
+
+ Resources/core/__pycache__/cli.cpython-311.pyc
+
+ 8lTNBZhC4pnGDKnxTZcc6UkVC4Q=
+
+ Resources/core/__pycache__/cli.cpython-312.pyc
+
+ N9alwMWQqdEC/ZVaVWLzs22yEag=
+
+ Resources/core/__pycache__/configuration.cpython-311.pyc
+
+ K6SabsH7w7fxj63RoyM0WAfrA1k=
+
+ Resources/core/__pycache__/configuration.cpython-312.pyc
+
+ vNYka2mQhLo21v1sUjUzY9xlZdo=
+
+ Resources/core/__pycache__/helper.cpython-311.pyc
+
+ kuNxNPuLEiGqdoUxNaZ5Fcfvpb4=
+
+ Resources/core/__pycache__/helper.cpython-312.pyc
+
+ WJfD706rPG7mYNmvBPFnYaepNWk=
+
+ Resources/core/__pycache__/log.cpython-311.pyc
+
+ eAofXzXoAu/tdWVlQG5flABVgJU=
+
+ Resources/core/__pycache__/log.cpython-312.pyc
+
+ SLp7dJlVZYyyCuhmHJbUwPPNRTk=
+
+ Resources/core/__pycache__/service.cpython-311.pyc
+
+ HHnf6SmUew153jm5wgr5Sj6bKw4=
+
+ Resources/core/__pycache__/service.cpython-312.pyc
+
+ tXS07Lh9/jJnU2jL8cvnLqkezIs=
+
+ Resources/core/__pycache__/ui.cpython-311.pyc
+
+ UCXy2m3sCmKASnB+tGgv5niZ1yQ=
+
+ Resources/core/__pycache__/ui.cpython-312.pyc
+
+ SCZOkPSvShBlRhStGhLlYcxpJ7E=
+
+ Resources/core/application.py
+
+ 1PdL7ghNbE3+41tK0QQNwt5NO6o=
+
+ Resources/core/build.sh
+
+ ACFtW7ca5miLZlF2W3Cp1QYPJ6A=
+
+ Resources/core/bundle.sh
+
+ iCHgxqGQIn4JqXef2DOYnDRoFsM=
+
+ Resources/core/cli.py
+
+ 7hYlxa2dkt86P37pTLHXU8G6+7k=
+
+ Resources/core/configuration.py
+
+ UG2HYg8SsdcMgFtEZTY/5rU/Fzw=
+
+ Resources/core/database/__init__.py
+
+ 4Nt+5Rf4xGnt4SGGLr5W8YLzYXg=
+
+ Resources/core/database/__pycache__/__init__.cpython-311.pyc
+
+ vQQzd5Q06gky0hLwvyc2lK4h8to=
+
+ Resources/core/database/__pycache__/__init__.cpython-312.pyc
+
+ GRlWNQ6tbchSIKa7985dyylw+dU=
+
+ Resources/core/database/__pycache__/sqlite.cpython-311.pyc
+
+ 9XjZCvdMmXCNp9QL3+Il9zxR69g=
+
+ Resources/core/database/__pycache__/sqlite.cpython-312.pyc
+
+ mASuSzrNeGkgl0HRhZ8vM0W+STI=
+
+ Resources/core/database/sqlite.py
+
+ NELDiL9+bNDDu2SXhDChL3yPziI=
+
+ Resources/core/filesystem/__init__.py
+
+ Fr/sO+PQ9b3LP2wjg8Kon5HHLmA=
+
+ Resources/core/filesystem/__pycache__/__init__.cpython-311.pyc
+
+ dA0dyN8I7t5FvtLGIWXlCRKQsDc=
+
+ Resources/core/filesystem/__pycache__/__init__.cpython-312.pyc
+
+ XMUq78147JY5JyneySjKNwtkac4=
+
+ Resources/core/filesystem/__pycache__/filesystem.cpython-311.pyc
+
+ oF3JDCPI9DSWOdjigMM+eqPDDrM=
+
+ Resources/core/filesystem/__pycache__/filesystem.cpython-312.pyc
+
+ PFCjLfRYguq87Nqxep+EFb9AZMk=
+
+ Resources/core/filesystem/__pycache__/share.cpython-311.pyc
+
+ zlnDuOVqgVDKWgSh8pkcYO0e3EU=
+
+ Resources/core/filesystem/filesystem.py
+
+ XwiaKaqKulVxRC4PxYxhxcCbxrE=
+
+ Resources/core/filesystem/share.py
+
+ eqx4b4CfBdOuLKuQrVOsEStEdfU=
+
+ Resources/core/helper.py
+
+ p1D4iZS3LtjbaNp0Gbl3TtQHkBI=
+
+ Resources/core/icons/0-circle-fill.svg
+
+ 6hIp9W0BuObMZTr9f2Pl1jTTYuw=
+
+ Resources/core/icons/0-circle.svg
+
+ k7NpvnKq47UH6sA5STl4Wvi8HJg=
+
+ Resources/core/icons/0-square-fill.svg
+
+ Fnen0wcBZ02vl8wi/5te/aO4kas=
+
+ Resources/core/icons/0-square.svg
+
+ mYQ/fAy+JINSyphw7SyrNzbcpvo=
+
+ Resources/core/icons/1-circle-fill.svg
+
+ a5mvyZ3vg+WIsJhSgnGsmerTpoE=
+
+ Resources/core/icons/1-circle.svg
+
+ tFX7XA+V84Oy9ncMt179q2lB74w=
+
+ Resources/core/icons/1-square-fill.svg
+
+ +ZwigAzd7hNj3aV1kNndlYC+/i8=
+
+ Resources/core/icons/1-square.svg
+
+ D0ucP/0NMCEX3YxaJWJR4hydjOc=
+
+ Resources/core/icons/123.svg
+
+ kVH/EagA1E/eZ04RCEhCB3651To=
+
+ Resources/core/icons/2-circle-fill.svg
+
+ +qd4/AjNzro/2QAaaRTWN7f62cI=
+
+ Resources/core/icons/2-circle.svg
+
+ CB2Rs1KpCGxRHaSqHYYZLXg8ItI=
+
+ Resources/core/icons/2-square-fill.svg
+
+ Xt6M8GZuUoqhiCxUsHpJ2lpBX9Y=
+
+ Resources/core/icons/2-square.svg
+
+ vvEKfEGyt40dxtbN1qiy/C9Un4Y=
+
+ Resources/core/icons/3-circle-fill.svg
+
+ rzvZEaKzzzVTSHgDtp5RDJSqtq0=
+
+ Resources/core/icons/3-circle.svg
+
+ 9PNNXXU+8fuDL8oZlDqwUdWXFis=
+
+ Resources/core/icons/3-square-fill.svg
+
+ qpBBOJ03euc5zbA+v4DP+Pid2II=
+
+ Resources/core/icons/3-square.svg
+
+ kzyXuV0F0Cq90Rw8LR6vcK0Qi30=
+
+ Resources/core/icons/4-circle-fill.svg
+
+ ujYcJMF89r+iLu1NbtuUTRwe4d8=
+
+ Resources/core/icons/4-circle.svg
+
+ /sdxHvSpxcR9le3xZUKtGb4Ppcg=
+
+ Resources/core/icons/4-square-fill.svg
+
+ JWgyKWz/6UciHRbDcvlRuPCThy8=
+
+ Resources/core/icons/4-square.svg
+
+ PaeagIcQLLvkY9e+ExWYuCwLN+8=
+
+ Resources/core/icons/5-circle-fill.svg
+
+ QnJOy3INUSiK+ecFPrNxtV29150=
+
+ Resources/core/icons/5-circle.svg
+
+ YNg/OMSCpbit5/xl09r7nmlp7is=
+
+ Resources/core/icons/5-square-fill.svg
+
+ Aq/vpCDWqKUgnmZ+gXzFV61ipOE=
+
+ Resources/core/icons/5-square.svg
+
+ UAoe5XZ3AFDQX+SaGHdkaRkNQcA=
+
+ Resources/core/icons/6-circle-fill.svg
+
+ DqGwuuObm6/+bZTvIGkjpHgAQtM=
+
+ Resources/core/icons/6-circle.svg
+
+ NExewBK/a721meJGC+nO6Gf0r7Y=
+
+ Resources/core/icons/6-square-fill.svg
+
+ XT1k00PH++p8LAd1l4Qisw7/NUU=
+
+ Resources/core/icons/6-square.svg
+
+ bEOtvRAXmRFZyuX+ZJUV3kafXwY=
+
+ Resources/core/icons/7-circle-fill.svg
+
+ k5s/vITCzsbI96BAgodpYQI9w5c=
+
+ Resources/core/icons/7-circle.svg
+
+ ptYMC8gEAappm24QUDS4v0V6Hws=
+
+ Resources/core/icons/7-square-fill.svg
+
+ taSFJyIXvWk6dYqH2XFVrOhJ8QA=
+
+ Resources/core/icons/7-square.svg
+
+ DJ+xibmNp3sZM03wKcKaMamVQhE=
+
+ Resources/core/icons/8-circle-fill.svg
+
+ CIrLVaNAgrwSXlQ2gG1ehY5Ig1U=
+
+ Resources/core/icons/8-circle.svg
+
+ 60913AagqEZ9iIesgzEw6pXDVCA=
+
+ Resources/core/icons/8-square-fill.svg
+
+ GV5SWKLdSH80H/bGe0vUHjPyq5E=
+
+ Resources/core/icons/8-square.svg
+
+ rT73aLsdOqZXpY6DuLXVi1uVHM0=
+
+ Resources/core/icons/9-circle-fill.svg
+
+ BA+dF6L5IDXkH+nWZQo88VqagiQ=
+
+ Resources/core/icons/9-circle.svg
+
+ pkgIk5HYKjcyWzYdealuI6B4q2o=
+
+ Resources/core/icons/9-square-fill.svg
+
+ ad2VGh4WI3M1jLWsy1Ra8b7RXpM=
+
+ Resources/core/icons/9-square.svg
+
+ 9o5YFQ8jzbuLqbfsWzUIeWYKQqI=
+
+ Resources/core/icons/activity.svg
+
+ Uu38Ooiaysodz6TjdMEUYK9xh6g=
+
+ Resources/core/icons/airplane-engines-fill.svg
+
+ 9HnO1CAAuumrTtwW3qIohZghbxo=
+
+ Resources/core/icons/airplane-engines.svg
+
+ 95HD5faKwHaYPzHyjpwcDoPOxl4=
+
+ Resources/core/icons/airplane-fill.svg
+
+ IYWRa7ukLa9/qE4636me39w7NDE=
+
+ Resources/core/icons/airplane.svg
+
+ gWDlhoXhkxqvjrkbTOPny7TNFQ8=
+
+ Resources/core/icons/alarm-fill.svg
+
+ 5KVK+aE3PRv0Gui081YYNYcg5xo=
+
+ Resources/core/icons/alarm.svg
+
+ PUOR3UTNnY07L/qGlSidWnJdRas=
+
+ Resources/core/icons/alexa.svg
+
+ 5nrMCVs7Zu230Q0CW/ciE3M38uU=
+
+ Resources/core/icons/align-bottom.svg
+
+ Bz3y+cfNSnZalEKui1QA5m09moE=
+
+ Resources/core/icons/align-center.svg
+
+ gHk4sedsBoO9mLcJsrKC1tGxSFE=
+
+ Resources/core/icons/align-end.svg
+
+ ChD5V9gnhCs5VCg6zS2CpReWo6Q=
+
+ Resources/core/icons/align-middle.svg
+
+ CjpAAsODHhG1/ZV7RT5PgjHXnZY=
+
+ Resources/core/icons/align-start.svg
+
+ DKfYOhcSjTZkpWOxl00DMKg9DQk=
+
+ Resources/core/icons/align-top.svg
+
+ 8Eak4uLd5w/jkAmFnVZnrrWcMAw=
+
+ Resources/core/icons/alipay.svg
+
+ kkCY32gAg41zSfvyeSmUwxJcIJE=
+
+ Resources/core/icons/alphabet-uppercase.svg
+
+ +eXVLINqMo9emD4NpC1nyIOMkzE=
+
+ Resources/core/icons/alphabet.svg
+
+ QYkIam045TF1is+K6q9y6w3gdDI=
+
+ Resources/core/icons/alt.svg
+
+ gHnt5tVMNZdMAIZnTMvWzLB9s/U=
+
+ Resources/core/icons/amazon.svg
+
+ /UoFyRwzRDjz2FsozWXKXvfu7Yo=
+
+ Resources/core/icons/amd.svg
+
+ xs3cBlyzaZYuQPNSXA6GO943CzU=
+
+ Resources/core/icons/android.svg
+
+ 0fYFX7ZVpsh86gaPYEk8qqhPZPE=
+
+ Resources/core/icons/android2.svg
+
+ LSqDIkLmQLzbQVknA+yfFQNESpk=
+
+ Resources/core/icons/anthropic.svg
+
+ W2/rPUjyt3f3hg+Y4+nQik2OPz0=
+
+ Resources/core/icons/app-indicator.svg
+
+ 56RBkiwT8+o9aPEfgFUwkMROV8A=
+
+ Resources/core/icons/app.svg
+
+ vt6IO9x/0M8QQGsk3BN94INW8ZY=
+
+ Resources/core/icons/apple-music.svg
+
+ ZkNBJIyhn2FgRJMb4Vciir71VtQ=
+
+ Resources/core/icons/apple.svg
+
+ cHHgjkaO5CQQA0zZq4xCyurAVpI=
+
+ Resources/core/icons/archive-fill.svg
+
+ 5LFl6t2fcKqt49Aio1zIOfpU+oc=
+
+ Resources/core/icons/archive.svg
+
+ BuUo0ewDEkf3560zyrecbr4rIrc=
+
+ Resources/core/icons/arrow-90deg-down.svg
+
+ jcu9cha3SWVlaPTGAMdbLCxSpEg=
+
+ Resources/core/icons/arrow-90deg-left.svg
+
+ FFzTP3tcQWHbC/8DdGOxVsi949A=
+
+ Resources/core/icons/arrow-90deg-right.svg
+
+ Dizp+58gdrpEE1vksxL01lxY+k8=
+
+ Resources/core/icons/arrow-90deg-up.svg
+
+ F2c7PAx5wOOogJEVHp+c87X8rbc=
+
+ Resources/core/icons/arrow-bar-down.svg
+
+ X3n5cJTk+16MexoKZzluriURRLM=
+
+ Resources/core/icons/arrow-bar-left.svg
+
+ d7MssJ19I6E6Pq88uGL2ckqaPbc=
+
+ Resources/core/icons/arrow-bar-right.svg
+
+ 1qUaCgvEobw5pctJitXY0VgFLNE=
+
+ Resources/core/icons/arrow-bar-up.svg
+
+ ftrBbDCcM/cCO1n1jayFNEdzPqI=
+
+ Resources/core/icons/arrow-clockwise.svg
+
+ qfoFhPigxqFY2sxdHHP1M90lflY=
+
+ Resources/core/icons/arrow-counterclockwise.svg
+
+ GYpgwQFDRUIWJJcA8xFWwYoFQOA=
+
+ Resources/core/icons/arrow-down-circle-fill.svg
+
+ OzJ1JMpbrEDTTe4kWMyIbY6Hgbc=
+
+ Resources/core/icons/arrow-down-circle.svg
+
+ Wno4m2feqL9Y/yRxPGX3ABIGyg0=
+
+ Resources/core/icons/arrow-down-left-circle-fill.svg
+
+ E42Pl4MaUdARwqoPyAWnn1z1nCc=
+
+ Resources/core/icons/arrow-down-left-circle.svg
+
+ mRSGyboYOOPr1WaGRnfE95eTa/g=
+
+ Resources/core/icons/arrow-down-left-square-fill.svg
+
+ vZOYYBXTnoyQL9CpaKgXq0SdF9A=
+
+ Resources/core/icons/arrow-down-left-square.svg
+
+ bzB6QttN1lIzIiywzs+T8pjg/Go=
+
+ Resources/core/icons/arrow-down-left.svg
+
+ WTZ67XkQAytOZQ4TetZNwXrg7mQ=
+
+ Resources/core/icons/arrow-down-right-circle-fill.svg
+
+ rGzDepLVwA7+IU6qn3woV4BN5KM=
+
+ Resources/core/icons/arrow-down-right-circle.svg
+
+ W6sVAM0iTTu3jm7vF+RXkc3mGGU=
+
+ Resources/core/icons/arrow-down-right-square-fill.svg
+
+ iHlL5nfoneF9S7/hVbdYX82xEUM=
+
+ Resources/core/icons/arrow-down-right-square.svg
+
+ R5COSCdxPxHlIkm0/AfVlrTRYw0=
+
+ Resources/core/icons/arrow-down-right.svg
+
+ SdfH1e0dKmZFmBGTDsUmYOZ+V6Q=
+
+ Resources/core/icons/arrow-down-short.svg
+
+ XIp2obdm6OS7rS2qxpmq8F25q78=
+
+ Resources/core/icons/arrow-down-square-fill.svg
+
+ d+OOKmIFVatZ87aCbaTnhBzSAAs=
+
+ Resources/core/icons/arrow-down-square.svg
+
+ /KDTeBOtDAEPYZ4Hq/98A4t6V5w=
+
+ Resources/core/icons/arrow-down-up.svg
+
+ XFA2Q8yMzqHdQWWe+4xPjXJNMjI=
+
+ Resources/core/icons/arrow-down.svg
+
+ UZLflwLfaHtqODgSUCI1x8K/vo4=
+
+ Resources/core/icons/arrow-left-circle-fill.svg
+
+ B0QodXy6eor1pVxboyCVTJE9dOI=
+
+ Resources/core/icons/arrow-left-circle.svg
+
+ gv5YI0e7cSS3pHss7biLYWn0Wk4=
+
+ Resources/core/icons/arrow-left-right.svg
+
+ +qvBdw/ZjzQ6FqDcItRnPkI2q5E=
+
+ Resources/core/icons/arrow-left-short.svg
+
+ 5wXOvCxhunJeIv6upRNbwPgRK24=
+
+ Resources/core/icons/arrow-left-square-fill.svg
+
+ YMT5GA0tNur7kHZfaH+AW2sKWDQ=
+
+ Resources/core/icons/arrow-left-square.svg
+
+ ru+XjCLXYukJPwB0iClvBbC97E0=
+
+ Resources/core/icons/arrow-left.svg
+
+ rqhQacSOsI3Q1e73e912tDhaOeI=
+
+ Resources/core/icons/arrow-repeat.svg
+
+ aNnlfcfDTiTw80mpRWeaHeJCdP8=
+
+ Resources/core/icons/arrow-return-left.svg
+
+ WuzgLIRL9+yxu2idpqBsGrfpvTg=
+
+ Resources/core/icons/arrow-return-right.svg
+
+ rBkudLQHL+zoSdHPYbLGWPR7zro=
+
+ Resources/core/icons/arrow-right-circle-fill.svg
+
+ cLNq3R0l28AzKWQz718msGyXf6g=
+
+ Resources/core/icons/arrow-right-circle.svg
+
+ vZU03vA4cRtET0+AE4R3AKJNEpw=
+
+ Resources/core/icons/arrow-right-short.svg
+
+ 39gFKPLRHVOsoXZ5hMhluk7BvIo=
+
+ Resources/core/icons/arrow-right-square-fill.svg
+
+ bkGNXWCdFsdTs+HyU2Zc4XVoPek=
+
+ Resources/core/icons/arrow-right-square.svg
+
+ ULLoHpKnoYIPMkQerP2yFMOya9I=
+
+ Resources/core/icons/arrow-right.svg
+
+ VDtx6wSkdDKRYRGa5aTAqlUyU4k=
+
+ Resources/core/icons/arrow-through-heart-fill.svg
+
+ m8AE89za3Bh/EOwci/UcZ91nUys=
+
+ Resources/core/icons/arrow-through-heart.svg
+
+ rTWh9LeIWuaVuxuDmaSTSDyLtDg=
+
+ Resources/core/icons/arrow-up-circle-fill.svg
+
+ L6G1UPcv/h+fuWV9pjP2V95DLlY=
+
+ Resources/core/icons/arrow-up-circle.svg
+
+ f7DvZ1RvbXaBt8rPEn8gZLxJcCY=
+
+ Resources/core/icons/arrow-up-left-circle-fill.svg
+
+ OkbhpglQO2R6nhnN6JhNgmal2To=
+
+ Resources/core/icons/arrow-up-left-circle.svg
+
+ AAN5fCSgwJYJd+XAqxa9vxjqYOY=
+
+ Resources/core/icons/arrow-up-left-square-fill.svg
+
+ WH3Do8JPA78UJev9cBVKaJ5zW/w=
+
+ Resources/core/icons/arrow-up-left-square.svg
+
+ BE0kfk2TpWO9/n1mykeAR3Pwsx4=
+
+ Resources/core/icons/arrow-up-left.svg
+
+ coxlUsHhH86+VU7v8Hv5nreVeGs=
+
+ Resources/core/icons/arrow-up-right-circle-fill.svg
+
+ 8Sp0sebCTeq3ejEV3STZ/jpn6wE=
+
+ Resources/core/icons/arrow-up-right-circle.svg
+
+ E83b7YtGkSvp4brnRhrXyaBAiM8=
+
+ Resources/core/icons/arrow-up-right-square-fill.svg
+
+ KJ7HQGM62XRTWP8g+nT62A01OYA=
+
+ Resources/core/icons/arrow-up-right-square.svg
+
+ eksEnfl1yUnSQj/Yq5SfeQE9GIc=
+
+ Resources/core/icons/arrow-up-right.svg
+
+ nafulhQ1tk328C3ss58fyv/1XcQ=
+
+ Resources/core/icons/arrow-up-short.svg
+
+ sDXYQoJ3HT2RckjaoI9guZx7auI=
+
+ Resources/core/icons/arrow-up-square-fill.svg
+
+ yqTfmLapRpmoYZPD7NgCtP2yt0k=
+
+ Resources/core/icons/arrow-up-square.svg
+
+ y+wAgzvR04sqm1EFymcxvZACEOM=
+
+ Resources/core/icons/arrow-up.svg
+
+ W7fSoW+t05F42Z45rJaxsSs38sA=
+
+ Resources/core/icons/arrows-angle-contract.svg
+
+ aNsivpRrVNE9buVo768koa7hyNc=
+
+ Resources/core/icons/arrows-angle-expand.svg
+
+ i3LQ3YQ/U+OsT+xLXtreVhV+n4A=
+
+ Resources/core/icons/arrows-collapse-vertical.svg
+
+ +tKFjoInyMH9d7/vyGEfiZfg4qE=
+
+ Resources/core/icons/arrows-collapse.svg
+
+ gPdnLskcR2L857F2JC99q+PB6ys=
+
+ Resources/core/icons/arrows-expand-vertical.svg
+
+ Lxrd06KZORG3lf2BY84SMYP4DSY=
+
+ Resources/core/icons/arrows-expand.svg
+
+ 8UIQsOS2okdYRJQcDNsr8TJBFdU=
+
+ Resources/core/icons/arrows-fullscreen.svg
+
+ 8dfI+0PJw7L3Dnn2xWqqnQIARC4=
+
+ Resources/core/icons/arrows-move.svg
+
+ XMTc7pMDT+skcw8oQ5yoPmUu2lY=
+
+ Resources/core/icons/arrows-vertical.svg
+
+ b/GBE9VtkUWIfzysjSPTszYD92g=
+
+ Resources/core/icons/arrows.svg
+
+ P5OUKn3cQWxlZVrl/8FChbcmV1o=
+
+ Resources/core/icons/aspect-ratio-fill.svg
+
+ q5cYx7KX/ldK8KXZGyW3Id+PjIA=
+
+ Resources/core/icons/aspect-ratio.svg
+
+ 9ajlPFTKKrDFVryEbgADajONdIk=
+
+ Resources/core/icons/asterisk.svg
+
+ IV4K6rl4X6Pde49mpw+gzpL9nhI=
+
+ Resources/core/icons/at.svg
+
+ T6GPRoVIzzu44fcE05Vvzy82oQQ=
+
+ Resources/core/icons/award-fill.svg
+
+ e9ocvYCi1nF373CmR0DcBVcP73Y=
+
+ Resources/core/icons/award.svg
+
+ 4acRHmHok87Vt79ChSsAKVEZDhE=
+
+ Resources/core/icons/back.svg
+
+ W+9/OqXIJQqQxlgNXECrapX+854=
+
+ Resources/core/icons/backpack-fill.svg
+
+ lroMtETgMXSU3fqhz2a/tN7UA8o=
+
+ Resources/core/icons/backpack.svg
+
+ g4qMhkyYDwKBxr2lt9YM9ZFcEb8=
+
+ Resources/core/icons/backpack2-fill.svg
+
+ wu18DZZg3wNBgP8gfF9H0YGE5ls=
+
+ Resources/core/icons/backpack2.svg
+
+ oVPThHPhGzqeQfsZIMS+F6RfgI8=
+
+ Resources/core/icons/backpack3-fill.svg
+
+ x72TlvtuBZ03rCdQ1ta/PjnuyC4=
+
+ Resources/core/icons/backpack3.svg
+
+ 1BH8pKxn/DQ+8b56fCcIFE5F/R0=
+
+ Resources/core/icons/backpack4-fill.svg
+
+ sIQS28HKgIOYVNn1ZKAf4iblofA=
+
+ Resources/core/icons/backpack4.svg
+
+ iLKe3INfrxq4BwWdw+6tZceN67c=
+
+ Resources/core/icons/backspace-fill.svg
+
+ l3k4ir07pu6BDjzd7rg+c020u/I=
+
+ Resources/core/icons/backspace-reverse-fill.svg
+
+ NvsEpVRmMbU/iIJdPpK7E7Ct/3Q=
+
+ Resources/core/icons/backspace-reverse.svg
+
+ qbjiymbq7XF14UcLyUjABdoFqew=
+
+ Resources/core/icons/backspace.svg
+
+ c0J5dAzSsLBAhC7Flj7BP/nuUes=
+
+ Resources/core/icons/badge-3d-fill.svg
+
+ zeNOhrjZVLOFNog1dFoKR4j0PXk=
+
+ Resources/core/icons/badge-3d.svg
+
+ pavh7K8paPLTbBfWct1Tyxg24FA=
+
+ Resources/core/icons/badge-4k-fill.svg
+
+ U11zNS/gsPGUcncwxblusyS4jX0=
+
+ Resources/core/icons/badge-4k.svg
+
+ iTX3kY58okGd5drsYFFW40Z7TZI=
+
+ Resources/core/icons/badge-8k-fill.svg
+
+ JRWbgHYfXbg2FeaVRNUcJzcNnns=
+
+ Resources/core/icons/badge-8k.svg
+
+ 6xYIiXwtrtCFjI8A9Ausq+hVXqY=
+
+ Resources/core/icons/badge-ad-fill.svg
+
+ FyF+oojkgSuqnLizF3WuqB8PwPA=
+
+ Resources/core/icons/badge-ad.svg
+
+ KUDq9RZVdBDA+DKIbS3MEZWuujk=
+
+ Resources/core/icons/badge-ar-fill.svg
+
+ RRP8R5B3d+ueoonX2bjqZ4JKJDc=
+
+ Resources/core/icons/badge-ar.svg
+
+ a8LMevppviHQshWKJWZdXLPWqLk=
+
+ Resources/core/icons/badge-cc-fill.svg
+
+ eyDilTCym7eVKCyhp+7H0YJMJsI=
+
+ Resources/core/icons/badge-cc.svg
+
+ Iia1AyoICxcISJy/OvjsYVSxjTE=
+
+ Resources/core/icons/badge-hd-fill.svg
+
+ qRikTbM+sy5W0IOLXJEv+1Dl6mM=
+
+ Resources/core/icons/badge-hd.svg
+
+ 3x2Us/P2khaBP9yVqguc0PP4nBI=
+
+ Resources/core/icons/badge-sd-fill.svg
+
+ goOrtZl7V54eZ8wW4Agw+HJbPPA=
+
+ Resources/core/icons/badge-sd.svg
+
+ e0SAxulyhdjrg7fs3sI3lCRFpEM=
+
+ Resources/core/icons/badge-tm-fill.svg
+
+ 07S5BDq2V/arxyq0k+d8+8ywH/o=
+
+ Resources/core/icons/badge-tm.svg
+
+ B6NwcZgRJaOrCFu4ZGJVYOnkg/0=
+
+ Resources/core/icons/badge-vo-fill.svg
+
+ W9I2odMDL4zLuwlaI9phQuEHBuE=
+
+ Resources/core/icons/badge-vo.svg
+
+ 0Qrns8Z84fnL6EVFfd/pYmWZKE0=
+
+ Resources/core/icons/badge-vr-fill.svg
+
+ cUJhrrfgc8fuNBJWuieV7o4zNaE=
+
+ Resources/core/icons/badge-vr.svg
+
+ 0/avkrVo+Cd37EMkb84+xm0OoQM=
+
+ Resources/core/icons/badge-wc-fill.svg
+
+ y3oLRLBJdYsq/TqBkcRkvv/8Cwo=
+
+ Resources/core/icons/badge-wc.svg
+
+ 7M6F2jXCMfVZvNbipc3DHCcLXlg=
+
+ Resources/core/icons/bag-check-fill.svg
+
+ 4ox1O8JFPSmwjU2HXnWbGgaMe4A=
+
+ Resources/core/icons/bag-check.svg
+
+ Bl9aqVhcCKU/iZ9zJZZP5qIMryE=
+
+ Resources/core/icons/bag-dash-fill.svg
+
+ aJUVTTmPRlo9YWWzAJIBW9i20xQ=
+
+ Resources/core/icons/bag-dash.svg
+
+ MjDsXvFd7kx5jJGBxVndkRnX7ZQ=
+
+ Resources/core/icons/bag-fill.svg
+
+ Lp24d8+WqXWVLLv03EsNXX1dUdE=
+
+ Resources/core/icons/bag-heart-fill.svg
+
+ 0gJEkHxHLvDR6x5x6Z14SohNIuw=
+
+ Resources/core/icons/bag-heart.svg
+
+ 3Z/iDx8X/hmKjDz7fBrB3JWUa+A=
+
+ Resources/core/icons/bag-plus-fill.svg
+
+ 0cy91IuAXk5dwTb7XN8PyOHJ8MA=
+
+ Resources/core/icons/bag-plus.svg
+
+ diezBP3TLoqoXNuxY0uRfpf3ypg=
+
+ Resources/core/icons/bag-x-fill.svg
+
+ AoG3CsZtAcy7zoz+D7RDwU5zSfs=
+
+ Resources/core/icons/bag-x.svg
+
+ 0/0m8e2gZJL1wywKxv3xGFF4sQk=
+
+ Resources/core/icons/bag.svg
+
+ FOWRIuSZrOx/qEvkycODoOwD1Es=
+
+ Resources/core/icons/balloon-fill.svg
+
+ juO5/mcjVeDZsdiR0fIb6YftGHU=
+
+ Resources/core/icons/balloon-heart-fill.svg
+
+ T4te8ks6Mbb/u2jy04ODgAYKl5k=
+
+ Resources/core/icons/balloon-heart.svg
+
+ YMr6EAlWjliGM3IRpLbRDEQ/6/w=
+
+ Resources/core/icons/balloon.svg
+
+ 5fZEdwzeDv1Wj0w+e6rsCMcMJFs=
+
+ Resources/core/icons/ban-fill.svg
+
+ NstSm6H54Qhyjw489u79qiw6gEY=
+
+ Resources/core/icons/ban.svg
+
+ 8GUR7EZxG/kdQi5NJkrpgH4EIqU=
+
+ Resources/core/icons/bandaid-fill.svg
+
+ m3SfGOL1MISj3dBf7L7rcrVFwgA=
+
+ Resources/core/icons/bandaid.svg
+
+ Alh0RF07oG27KC9goWo+gr1AmyI=
+
+ Resources/core/icons/bank.svg
+
+ y18EuySzuUkozK9be33l7HpY1NA=
+
+ Resources/core/icons/bank2.svg
+
+ r964M1wme8juTYVoidKwjqqkfHc=
+
+ Resources/core/icons/bar-chart-fill.svg
+
+ Svv1WQ6Q9gGg4Pq/+Jz0bNTQJX0=
+
+ Resources/core/icons/bar-chart-line-fill.svg
+
+ O1eQpQwb68hJn0AKfAsV/whQ8vk=
+
+ Resources/core/icons/bar-chart-line.svg
+
+ UomNJ2B57Pp9FHPNx7XXUg5FeXE=
+
+ Resources/core/icons/bar-chart-steps.svg
+
+ GuKvMGLTp4Fa8wRbVR9hGgTo24o=
+
+ Resources/core/icons/bar-chart.svg
+
+ FL7kj9g1ENwMny4/0Gb8aJr/XMY=
+
+ Resources/core/icons/basket-fill.svg
+
+ gi4e4pKDQseB/w2vU8QULNYwtOg=
+
+ Resources/core/icons/basket.svg
+
+ rxhJVcBUhZn3GWNPTTF+jgaWlZs=
+
+ Resources/core/icons/basket2-fill.svg
+
+ 0hPkiq5c1VAoG5woftJLwYGplEc=
+
+ Resources/core/icons/basket2.svg
+
+ 3WalySaGoSrpGbwms1Mme96yvUo=
+
+ Resources/core/icons/basket3-fill.svg
+
+ 7qmdLg22s52I62EbTrngM4wbIgo=
+
+ Resources/core/icons/basket3.svg
+
+ M5cEh/AOaqd4t/0H9cxRu8of+ks=
+
+ Resources/core/icons/battery-charging.svg
+
+ SGzOEUc0aH01kM8+/0HOI+2zRCQ=
+
+ Resources/core/icons/battery-full.svg
+
+ ibYp5P7UZ4CYcRBm1r85O9kjA/4=
+
+ Resources/core/icons/battery-half.svg
+
+ fn9VOUTfgg5Ps/u1VwvvcSThNqQ=
+
+ Resources/core/icons/battery-low.svg
+
+ gDo8rMN6Uau2TZ/euko0H+OMG2k=
+
+ Resources/core/icons/battery.svg
+
+ tisCWhk3aMBMI9slkm5GWinIdPc=
+
+ Resources/core/icons/beaker-fill.svg
+
+ kJ0qPXhv4xZH5yBn0keErs9ZvFo=
+
+ Resources/core/icons/beaker.svg
+
+ ++L4UjalUGan6ScN1N+qyPc1TMg=
+
+ Resources/core/icons/behance.svg
+
+ zJt5DsA/ZZ0M5P6ASLQN6QvYNHo=
+
+ Resources/core/icons/bell-fill.svg
+
+ IiMSYOtsrbNPirf6wZJkFVhy5VY=
+
+ Resources/core/icons/bell-slash-fill.svg
+
+ Fu46nX2vJuQOZlxLYctoup+wGk8=
+
+ Resources/core/icons/bell-slash.svg
+
+ 4S5K+ArgTnRrMaKF7Aws8bGzlko=
+
+ Resources/core/icons/bell.svg
+
+ ZR3Y+StVSuS1i9vgVeDypxl/CGg=
+
+ Resources/core/icons/bezier.svg
+
+ HdPQEPlwr6xtIivY6g9QJxaZaTc=
+
+ Resources/core/icons/bezier2.svg
+
+ h2fepRChT3XwMj8sgHoGtq/P7Nw=
+
+ Resources/core/icons/bicycle.svg
+
+ CBtQqz8SOVzTGquSxotEKnzWz0M=
+
+ Resources/core/icons/bing.svg
+
+ 4OzR6UIfYQrrhmyIXxJMwNXF22Q=
+
+ Resources/core/icons/binoculars-fill.svg
+
+ 05s38CsVOZkvN2r3jEsTpdqmQC0=
+
+ Resources/core/icons/binoculars.svg
+
+ y3AcOP4KBA7FRj/dTaSUQI+C3PI=
+
+ Resources/core/icons/blockquote-left.svg
+
+ RrrgI68ZiqH6c2VRAPOaLO+lryY=
+
+ Resources/core/icons/blockquote-right.svg
+
+ SXkuP0PAPNfntZu7RIxdbg3Kks8=
+
+ Resources/core/icons/bluesky.svg
+
+ WaCcXx5Vv0NXRwdGvFCFw9lyxzE=
+
+ Resources/core/icons/bluetooth.svg
+
+ v2Zz5JTK7UC1aku4LTrrk1wBPkQ=
+
+ Resources/core/icons/body-text.svg
+
+ hJK26SYDy40cDB4lL8fg3xy/+oc=
+
+ Resources/core/icons/book-fill.svg
+
+ eBaDtUXdXzV0EqntKyyd8dRz0d4=
+
+ Resources/core/icons/book-half.svg
+
+ RKX1Ie5k/zVwMC1y/DV9QnbOd9Q=
+
+ Resources/core/icons/book.svg
+
+ q84YGk3QiJbzcDYIMlBaeLCptCc=
+
+ Resources/core/icons/bookmark-check-fill.svg
+
+ Lzulx2XPOlRkVNtdAO1Fwa/33ow=
+
+ Resources/core/icons/bookmark-check.svg
+
+ Hre9RvPx5B1ib9p8M6e9MJm3Ivo=
+
+ Resources/core/icons/bookmark-dash-fill.svg
+
+ tfAW+beOEmbc3xOkmQ77xA56U5c=
+
+ Resources/core/icons/bookmark-dash.svg
+
+ 3EfJs3pcroLXI1SRekEwFCwn2/s=
+
+ Resources/core/icons/bookmark-fill.svg
+
+ lSc/Z8eCNOb/807WBHjUsrRlEjo=
+
+ Resources/core/icons/bookmark-heart-fill.svg
+
+ c+vqfjiwhCoMl2Ut04rsefHd7sU=
+
+ Resources/core/icons/bookmark-heart.svg
+
+ Kn9H7exXSG8qSPwR48StJtRabOc=
+
+ Resources/core/icons/bookmark-plus-fill.svg
+
+ 7NMkr8jOCtAmrHJ7P1Ya9JHa3jM=
+
+ Resources/core/icons/bookmark-plus.svg
+
+ mtgUvhmaHdRhiLJn7W1qwX/tEyA=
+
+ Resources/core/icons/bookmark-star-fill.svg
+
+ KlwJUkFVCQPfMO+UK31nRLjM2rU=
+
+ Resources/core/icons/bookmark-star.svg
+
+ dax74lNGRueVTxe2N+l3VdxZ1wM=
+
+ Resources/core/icons/bookmark-x-fill.svg
+
+ g0IID4jPF3xpoRSAW9LlPMPj/MY=
+
+ Resources/core/icons/bookmark-x.svg
+
+ 0i11P+xbqL0IgcPNZzE1ZWO6WmI=
+
+ Resources/core/icons/bookmark.svg
+
+ qrVK97GfmkP943zFxc5pbsPKOxg=
+
+ Resources/core/icons/bookmarks-fill.svg
+
+ 1zozMqmps1LEqhDZSFsd91b4iJ8=
+
+ Resources/core/icons/bookmarks.svg
+
+ fiCAELD9EfYzJ7Ei+s0jRqXekAg=
+
+ Resources/core/icons/bookshelf.svg
+
+ y65UJPEL9v5Z2WuOuFKE0/W3fvM=
+
+ Resources/core/icons/boombox-fill.svg
+
+ pmL6+EGthm+me18mPsXZBI+fJhw=
+
+ Resources/core/icons/boombox.svg
+
+ DXPDmOH0lBt/dBjCC0/zVtMZVOE=
+
+ Resources/core/icons/bootstrap-fill.svg
+
+ 3lsrERV6FV4pu6qns5jTJjQvhzQ=
+
+ Resources/core/icons/bootstrap-icons.css
+
+ ptHboTbR5gcHjbMAMHDqs0tR7FA=
+
+ Resources/core/icons/bootstrap-icons.json
+
+ dRchq4AL7fq6aRvn9+2eAti3wVM=
+
+ Resources/core/icons/bootstrap-icons.min.css
+
+ rp/p6DPa9owjeUXpre5bfzVHjRk=
+
+ Resources/core/icons/bootstrap-icons.scss
+
+ dB2E5pHnxHA/MshiQsxjpA9Hk8w=
+
+ Resources/core/icons/bootstrap-icons.svg
+
+ Tl+nM23FD6yJWaYY9GoPb5hPXYk=
+
+ Resources/core/icons/bootstrap-reboot.svg
+
+ T5uBknt5YknmexG3oRwEKAb6XvA=
+
+ Resources/core/icons/bootstrap.svg
+
+ Bxx8nl0lqwruiTpDUbjy6kPahLo=
+
+ Resources/core/icons/border-all.svg
+
+ jJa+CBcPE4AtSfTtGnGyDsIRRFk=
+
+ Resources/core/icons/border-bottom.svg
+
+ KB2hNrgrkDOmDV8maUUUbYwp/jU=
+
+ Resources/core/icons/border-center.svg
+
+ kopUFrvF3Ybl74tdsvFXVr9HX7E=
+
+ Resources/core/icons/border-inner.svg
+
+ AFo7wlMtTgkMPF5AEF7NitQDO5g=
+
+ Resources/core/icons/border-left.svg
+
+ +NJss7si751uRJZLJ5gc9wOmMZ8=
+
+ Resources/core/icons/border-middle.svg
+
+ cQ5esce9RNwEZDE6eNEy8e3mU0U=
+
+ Resources/core/icons/border-outer.svg
+
+ Jadms6MtqvAVSpA+nf/4bNdZgWE=
+
+ Resources/core/icons/border-right.svg
+
+ Qsr8Uw5qJWl3OrVAK7QyylkRwSg=
+
+ Resources/core/icons/border-style.svg
+
+ X65dG5i44LMSoTpB3MvDBPHbj20=
+
+ Resources/core/icons/border-top.svg
+
+ ebGpGv3pP9wulG4QkulQnnJDFEI=
+
+ Resources/core/icons/border-width.svg
+
+ vFo3j0jY4lqKzzpPmOnNEZsCo0M=
+
+ Resources/core/icons/border.svg
+
+ IEA8Yd9khRu46izShuzRvMyR05g=
+
+ Resources/core/icons/bounding-box-circles.svg
+
+ NfQ72IGoyibhLcpr6cI2lTA1nsc=
+
+ Resources/core/icons/bounding-box.svg
+
+ C5zs02hUIoMNJWIWH5kKCSK/I1k=
+
+ Resources/core/icons/box-arrow-down-left.svg
+
+ 8KHTCKY89RUoa5SBpjfFuR817Bw=
+
+ Resources/core/icons/box-arrow-down-right.svg
+
+ f4DChasNpWLGhvFEITmFWrTcwQ8=
+
+ Resources/core/icons/box-arrow-down.svg
+
+ HfOkfolaTT6WaKnhxL1wvXxszQw=
+
+ Resources/core/icons/box-arrow-in-down-left.svg
+
+ dClFJWwfN1D55kh4js2WtPsn7V4=
+
+ Resources/core/icons/box-arrow-in-down-right.svg
+
+ 0YVMCkpCTPmRn5i9YydOdm8TaI8=
+
+ Resources/core/icons/box-arrow-in-down.svg
+
+ +IhCN+liB5iz+UWbYG83/EhvQK0=
+
+ Resources/core/icons/box-arrow-in-left.svg
+
+ xWdKUJcvPsmcDyLdwqRJtdE1c74=
+
+ Resources/core/icons/box-arrow-in-right.svg
+
+ 4G8fN3Rr7kxe6aZWQ99GWevOk3s=
+
+ Resources/core/icons/box-arrow-in-up-left.svg
+
+ muKz2ux213i0SRRpS7ESQlvFQBA=
+
+ Resources/core/icons/box-arrow-in-up-right.svg
+
+ KjoUCSWoe5SkQ8myiXf/oUMebIs=
+
+ Resources/core/icons/box-arrow-in-up.svg
+
+ AUU36xwXAihr3KPrU2Sj2xC7R/Y=
+
+ Resources/core/icons/box-arrow-left.svg
+
+ Z5R68s1PAVgPlGjGIxhyIAJcX28=
+
+ Resources/core/icons/box-arrow-right.svg
+
+ 2JdU5/tXw1Lg9jfDXNzbDeN+ZkM=
+
+ Resources/core/icons/box-arrow-up-left.svg
+
+ kZTu341qhrf3VaOpJxY7YMdsg+M=
+
+ Resources/core/icons/box-arrow-up-right.svg
+
+ kVmVKWntDlYbQUmmOpZC8e8SqAQ=
+
+ Resources/core/icons/box-arrow-up.svg
+
+ ezc+giyvFCfRe0fZPJowKr+Ge70=
+
+ Resources/core/icons/box-fill.svg
+
+ YFdzFjvP7JQis4m+NGNlzpkjXlc=
+
+ Resources/core/icons/box-seam-fill.svg
+
+ 9yGwnBDcmRKI7QYi4lrPwaF2DMY=
+
+ Resources/core/icons/box-seam.svg
+
+ uH+xOHUeEiXEaJNvoWLil4kHhYQ=
+
+ Resources/core/icons/box.svg
+
+ d+IvBwN9l9V/LenNMfdmcnDkBVY=
+
+ Resources/core/icons/box2-fill.svg
+
+ objwKsUoT7XUnUrj+tvovwqNj2U=
+
+ Resources/core/icons/box2-heart-fill.svg
+
+ 4hXph5uYCdYIdE0l+79hxiEBdDw=
+
+ Resources/core/icons/box2-heart.svg
+
+ ZsvQeBgcgONcTo/rtHtzzQ8kdcc=
+
+ Resources/core/icons/box2.svg
+
+ NnLfFY+jxnKS/dDnVF2i3FWHPGY=
+
+ Resources/core/icons/boxes.svg
+
+ 1PYGWzUTYJPZ/pYayaSIefm2gqg=
+
+ Resources/core/icons/braces-asterisk.svg
+
+ xYW0Lfldn/+cMR9NrgOVJKsYMQ4=
+
+ Resources/core/icons/braces.svg
+
+ ix1zTPbDGD6vmKCG0PAblN7+BB0=
+
+ Resources/core/icons/bricks.svg
+
+ xddh/q0Xj9+hYgmrjTKsOSw/zVM=
+
+ Resources/core/icons/briefcase-fill.svg
+
+ KuZJpXQj4JqeZCbxYM+SXE7qzR0=
+
+ Resources/core/icons/briefcase.svg
+
+ Zukg8LQr62Vf/NDjWRamv5fNQsw=
+
+ Resources/core/icons/brightness-alt-high-fill.svg
+
+ kGccgfLa0WwJElXU1B7u4xirNU8=
+
+ Resources/core/icons/brightness-alt-high.svg
+
+ Emk4wEpLBkRHG6HcuC7BJAIib5A=
+
+ Resources/core/icons/brightness-alt-low-fill.svg
+
+ 4CKDyfx161eIq02SwzfETpwbCks=
+
+ Resources/core/icons/brightness-alt-low.svg
+
+ 7k5bh/KoEt2g2f3qEwoMedvXzvc=
+
+ Resources/core/icons/brightness-high-fill.svg
+
+ clIsIcbH71Yi8/184zmpHNI/Lk4=
+
+ Resources/core/icons/brightness-high.svg
+
+ EbMf5njoEsg+Qooe6t7pwoJ0zDE=
+
+ Resources/core/icons/brightness-low-fill.svg
+
+ 1S23XHpfNLoqZyhjZef/M5/2W90=
+
+ Resources/core/icons/brightness-low.svg
+
+ KIEkwgfSZeRv2n5FBY3jFKXS2Ik=
+
+ Resources/core/icons/brilliance.svg
+
+ yNr/S5X70rXwBbUwTBAWs2jBbTo=
+
+ Resources/core/icons/broadcast-pin.svg
+
+ +gahNMbRw4PUmzZscOYvUOOn55Q=
+
+ Resources/core/icons/broadcast.svg
+
+ BFejGxbG7/a2jh3NiGT0PivCdG8=
+
+ Resources/core/icons/browser-chrome.svg
+
+ P1ndctyKdwhSXDZUyx8dCMW0i5k=
+
+ Resources/core/icons/browser-edge.svg
+
+ XDJLit8Y/5QpREBG9tgxeBRGTsE=
+
+ Resources/core/icons/browser-firefox.svg
+
+ K6cgG6f9rJbtjzCBJqYbKFIXaKs=
+
+ Resources/core/icons/browser-safari.svg
+
+ u9LginBdCJjL+vmtLCKWCBkr4jg=
+
+ Resources/core/icons/brush-fill.svg
+
+ hW3Xjub3AspoO6tsAnOk8QdeSRU=
+
+ Resources/core/icons/brush.svg
+
+ uClZSer+2++soFlnfnmj64ffvC4=
+
+ Resources/core/icons/bucket-fill.svg
+
+ AtiU8PCVAHzsfD0kKp3H3KVnX7Y=
+
+ Resources/core/icons/bucket.svg
+
+ r64+ONb5Ryk8YPztSPw56vwM9LA=
+
+ Resources/core/icons/bug-fill.svg
+
+ JZFikN6sYNYyo0duWfp+TPeJSLg=
+
+ Resources/core/icons/bug.svg
+
+ 2EQ48J5rC61xPYwDS1bV+df58vA=
+
+ Resources/core/icons/building-add.svg
+
+ c2o9I5eo/mQG3n3SrJlze8D0pEk=
+
+ Resources/core/icons/building-check.svg
+
+ cklVfgA3GUUDVSkBZDDMuE9SYNE=
+
+ Resources/core/icons/building-dash.svg
+
+ REg2+muh2n4k7XrgU/+j0es08/E=
+
+ Resources/core/icons/building-down.svg
+
+ 7K97mHhFLZ84IYdINHOxtkXHyDA=
+
+ Resources/core/icons/building-exclamation.svg
+
+ ItwjMmh+RyiG7KSOrzq/IPT3Q3Y=
+
+ Resources/core/icons/building-fill-add.svg
+
+ cW0dwmhKDf+CyLTkWSc7PxdHgUs=
+
+ Resources/core/icons/building-fill-check.svg
+
+ MU4k9HpRvnDCn67q0apTmy24hcE=
+
+ Resources/core/icons/building-fill-dash.svg
+
+ /Id/kz9R5M8i6biqkkGnXGKyjQw=
+
+ Resources/core/icons/building-fill-down.svg
+
+ 8jo2+C2S/qXd/9ejpVPDgbJMLAA=
+
+ Resources/core/icons/building-fill-exclamation.svg
+
+ 5KwogTmH1JSuAcqu053s0UnLS6w=
+
+ Resources/core/icons/building-fill-gear.svg
+
+ 0xlRsaSdJ+DnNVSAtdwQLk2BWxo=
+
+ Resources/core/icons/building-fill-lock.svg
+
+ HlihsKAYAXLYQoB/fUlGpegbgO8=
+
+ Resources/core/icons/building-fill-slash.svg
+
+ K8T2m9FKtbovUfB83A8spFWe7Zg=
+
+ Resources/core/icons/building-fill-up.svg
+
+ AJRnHl5+0daM1O/OoFYScf2j0+w=
+
+ Resources/core/icons/building-fill-x.svg
+
+ H+pcv6lrz/O4wsWLf1aW1VY5EDM=
+
+ Resources/core/icons/building-fill.svg
+
+ zY637UQHgCzGjNjLor8J0eLoe4k=
+
+ Resources/core/icons/building-gear.svg
+
+ 6qMobaX/LWxPB1DBEmhs6fu427M=
+
+ Resources/core/icons/building-lock.svg
+
+ /xT6myqCM29sLVQObpEgrKG8Gw4=
+
+ Resources/core/icons/building-slash.svg
+
+ ywdMY6N939NOZaRLvPCWv7BAtzs=
+
+ Resources/core/icons/building-up.svg
+
+ Ncq9cRO4CEok+I4FR3gOAWB2vHY=
+
+ Resources/core/icons/building-x.svg
+
+ JQwTXB3otsT61RoWhvf93Uvyr24=
+
+ Resources/core/icons/building.svg
+
+ bH02u+zIIvD60glT0P5B0cA+ku8=
+
+ Resources/core/icons/buildings-fill.svg
+
+ vg2awp3IrFQy/ihjeuL62hYPpQ4=
+
+ Resources/core/icons/buildings.svg
+
+ stJ/q2U4BEcZ6wSu/MXegWgCjaA=
+
+ Resources/core/icons/bullseye.svg
+
+ RC1EfOcXzBIw81pfv27r5Cy5rvw=
+
+ Resources/core/icons/bus-front-fill.svg
+
+ FqMc3R8owzy7I/blPi0lh7CeQFw=
+
+ Resources/core/icons/bus-front.svg
+
+ m0riUafNKx6cm+vr24MfHoO6HJk=
+
+ Resources/core/icons/c-circle-fill.svg
+
+ xwiH6xnOGtZR2jO0G0Q8WdV0Bn4=
+
+ Resources/core/icons/c-circle.svg
+
+ fMP6WOjAgXi6srcn28VUPEeTFYw=
+
+ Resources/core/icons/c-square-fill.svg
+
+ MDzweBrV1+jfdzg1ZwuJNZoZZlo=
+
+ Resources/core/icons/c-square.svg
+
+ bgm5SH/hMrF6amCzE3/o0UO9PeU=
+
+ Resources/core/icons/cake-fill.svg
+
+ SdEtN8Hrjchgdrx+r8+NQ7znnXQ=
+
+ Resources/core/icons/cake.svg
+
+ e4cGHNvCZgossL33WAAZB0gmrjw=
+
+ Resources/core/icons/cake2-fill.svg
+
+ JuNadrBwwaDOYt7d2EPsQ5M8w/Y=
+
+ Resources/core/icons/cake2.svg
+
+ tlZCcYKbfvl1pLyf4feeDKD1TyI=
+
+ Resources/core/icons/calculator-fill.svg
+
+ DVxFnTpBJbg6Yvs5Osh0U6oKPnQ=
+
+ Resources/core/icons/calculator.svg
+
+ bIO2FGJnHCssXSGMahT1q4m/ySg=
+
+ Resources/core/icons/calendar-check-fill.svg
+
+ +jjFc+FOsqkfzilFfIt4+V3wj3I=
+
+ Resources/core/icons/calendar-check.svg
+
+ p12joWKufxKJb4II2dIq/gIlRWs=
+
+ Resources/core/icons/calendar-date-fill.svg
+
+ 4HIkOpQt3eIMUkYKdKJ+5YS9+28=
+
+ Resources/core/icons/calendar-date.svg
+
+ JH7YZVF32M2QCuImSsMt47hrB2U=
+
+ Resources/core/icons/calendar-day-fill.svg
+
+ +aHBhR9E4gGFK8mAqxbz/JOkIOU=
+
+ Resources/core/icons/calendar-day.svg
+
+ dFLw1FztsUrmPec1HvgDkaOZ9nU=
+
+ Resources/core/icons/calendar-event-fill.svg
+
+ wjcH1LFSH/Tayq0HM1Y+As7sztU=
+
+ Resources/core/icons/calendar-event.svg
+
+ QcyVwivXQXvOhhyKyqPrJqpHAm4=
+
+ Resources/core/icons/calendar-fill.svg
+
+ cZzld5/a7SnHyGuKek/bhG+/jYE=
+
+ Resources/core/icons/calendar-heart-fill.svg
+
+ eQEX4Ew7Z+d9Ti2t9kI+/2fXnAk=
+
+ Resources/core/icons/calendar-heart.svg
+
+ OEp5w4ZQ/QmAAZnKzUU3Qc08Xq8=
+
+ Resources/core/icons/calendar-minus-fill.svg
+
+ 5BGidqwkAIOy3LxrCGuBNrKx4Ho=
+
+ Resources/core/icons/calendar-minus.svg
+
+ WSQDXPOD/TEzwoHDwk0mrNWeZrk=
+
+ Resources/core/icons/calendar-month-fill.svg
+
+ EKT7fa5dU911zi3Qte7UG8ySuNk=
+
+ Resources/core/icons/calendar-month.svg
+
+ CpWGT3Bh5T2hpToTO/iE+YJOKQ0=
+
+ Resources/core/icons/calendar-plus-fill.svg
+
+ /9+zdfd0CuOSVqplOqR/4j571q4=
+
+ Resources/core/icons/calendar-plus.svg
+
+ dbumD1tkj6N79F7oulZqvQFEL6M=
+
+ Resources/core/icons/calendar-range-fill.svg
+
+ 3FYY8Hn/lfYID0RAPq0XdJDXj3A=
+
+ Resources/core/icons/calendar-range.svg
+
+ rPS6wjTqFxksUSp/sWcnUsZB/m8=
+
+ Resources/core/icons/calendar-week-fill.svg
+
+ W7pDvVFbwZipq47cjxL447OPAC8=
+
+ Resources/core/icons/calendar-week.svg
+
+ 67xSJRdqnBR7EwjvH8KhTym0yJc=
+
+ Resources/core/icons/calendar-x-fill.svg
+
+ JFHJsXg4rOqpNWpAIXhgQkABHYg=
+
+ Resources/core/icons/calendar-x.svg
+
+ snFBHP58NQPAE7L0H5PxqpTtgZ8=
+
+ Resources/core/icons/calendar.svg
+
+ 0bZV3Tn/b0oCQsloJtNBb03WAbs=
+
+ Resources/core/icons/calendar2-check-fill.svg
+
+ 2Br77bjn/MIW45KK7n0FcPPFkSo=
+
+ Resources/core/icons/calendar2-check.svg
+
+ Le9Tp9E8LWtYtVPjxmcf6faeQ9o=
+
+ Resources/core/icons/calendar2-date-fill.svg
+
+ iXh0cpYajPVXcm6LAPiGCyMa9KE=
+
+ Resources/core/icons/calendar2-date.svg
+
+ mitQvRQspZ3DnkZHxX/TzTvdx3k=
+
+ Resources/core/icons/calendar2-day-fill.svg
+
+ 2WxGOWJLFyfd0s78rfwei2do2Gk=
+
+ Resources/core/icons/calendar2-day.svg
+
+ Uzr7gsF+jX41vWNX6tJ8zGJJPG8=
+
+ Resources/core/icons/calendar2-event-fill.svg
+
+ AOHAHySEcJg3BZTuUkZgWR3v4xE=
+
+ Resources/core/icons/calendar2-event.svg
+
+ 9Pa3+m5Q4UVUikCn1ZiiJbFDZ20=
+
+ Resources/core/icons/calendar2-fill.svg
+
+ /5Iz/U4SCY8CASzhSIJVfGLHw3E=
+
+ Resources/core/icons/calendar2-heart-fill.svg
+
+ 59WfGsUCoUiNm4umL+cgQJBDulY=
+
+ Resources/core/icons/calendar2-heart.svg
+
+ zlhtmN3fK0HPm4QEf1HYlbFLP1g=
+
+ Resources/core/icons/calendar2-minus-fill.svg
+
+ 2USv/GC+YngUkY3ZEl52OYv9Exc=
+
+ Resources/core/icons/calendar2-minus.svg
+
+ w3YAmDnF5sv2FA4WoRxdl1YhpAw=
+
+ Resources/core/icons/calendar2-month-fill.svg
+
+ RrlszhaGEMdSQSn4uHNaQfImg9o=
+
+ Resources/core/icons/calendar2-month.svg
+
+ Sz3NJrT8fYzSiqTRuVKe2GIxRI0=
+
+ Resources/core/icons/calendar2-plus-fill.svg
+
+ F9t3qz6R2oHBGwIPl/nJaBPZP3A=
+
+ Resources/core/icons/calendar2-plus.svg
+
+ Fh/+efOBGJMD/v4VL4S9y2LQWoA=
+
+ Resources/core/icons/calendar2-range-fill.svg
+
+ IAgsFUaau8D8fTe3IXcDT4jP6w4=
+
+ Resources/core/icons/calendar2-range.svg
+
+ ATcqnkHlKtCeJhMdJ0jYAXRuA0o=
+
+ Resources/core/icons/calendar2-week-fill.svg
+
+ zaE95m7UuU9nM4u0+oyz5HPSosM=
+
+ Resources/core/icons/calendar2-week.svg
+
+ 2GWRyAz0anI+KrVLRwT1mjTJkwM=
+
+ Resources/core/icons/calendar2-x-fill.svg
+
+ /G8084TOS4HawA/Q8C+3f50Vh50=
+
+ Resources/core/icons/calendar2-x.svg
+
+ BGiUZinrBznc7wH9H4d3HXsdz5k=
+
+ Resources/core/icons/calendar2.svg
+
+ b3Wzw3uaE4+HkpXdu+eI709nvsg=
+
+ Resources/core/icons/calendar3-event-fill.svg
+
+ vEhvx7+4dxamDXdC63O36fGRPaQ=
+
+ Resources/core/icons/calendar3-event.svg
+
+ hbT9PcwdRFOKH0KSgy6IkmbK6sc=
+
+ Resources/core/icons/calendar3-fill.svg
+
+ BdqUvs+LFUXCses/iCI/QBEmfNE=
+
+ Resources/core/icons/calendar3-range-fill.svg
+
+ 0Gyi5TexGZ1WSq0yFB10TJwrFqA=
+
+ Resources/core/icons/calendar3-range.svg
+
+ 8VBNc5s2s9UGAnFJE/c1w4N3Uuk=
+
+ Resources/core/icons/calendar3-week-fill.svg
+
+ y2bbEzMjbFOQgBlBXC3h5vHifMw=
+
+ Resources/core/icons/calendar3-week.svg
+
+ Z/vradBwJBVJVgSPGNs9bfSetOE=
+
+ Resources/core/icons/calendar3.svg
+
+ ZvljPDVe6t3quUem99We24HhMa4=
+
+ Resources/core/icons/calendar4-event.svg
+
+ kqC673EcrFV+7dFbPaFitW836oc=
+
+ Resources/core/icons/calendar4-range.svg
+
+ J7P5YST+g9V8D1wVGLbQvLrMlRc=
+
+ Resources/core/icons/calendar4-week.svg
+
+ ZeoPUhrbJvfba2U4Gc7t1YWnSUE=
+
+ Resources/core/icons/calendar4.svg
+
+ yvYBuT8FlRjL0ZODcyqL7qnCB64=
+
+ Resources/core/icons/camera-fill.svg
+
+ xDESahTLHpqpEdxPMXac30Ei5ow=
+
+ Resources/core/icons/camera-reels-fill.svg
+
+ E/Lb39axwTF2NJCVjqLi6D86ehs=
+
+ Resources/core/icons/camera-reels.svg
+
+ Oe73swodtbP0v76y5uftMztlQys=
+
+ Resources/core/icons/camera-video-fill.svg
+
+ rCk9gOAVlbJr4cxeUwZRL9Nb4oU=
+
+ Resources/core/icons/camera-video-off-fill.svg
+
+ JgSw4LHetT2bomw4N+ERySyEbkk=
+
+ Resources/core/icons/camera-video-off.svg
+
+ 3YdBDiQDK0HNITgTpr5TvFbhSCY=
+
+ Resources/core/icons/camera-video.svg
+
+ 5gS0iiuedZx5D2M355hPRa5x08c=
+
+ Resources/core/icons/camera.svg
+
+ FQRGddbMGUtuA3g7YZqpaAowTbU=
+
+ Resources/core/icons/camera2.svg
+
+ R7doHNPdFIkty1bSRtqnT6VMc8A=
+
+ Resources/core/icons/capslock-fill.svg
+
+ 3IvHVmtKdqxENgMFF/wcfVosB8A=
+
+ Resources/core/icons/capslock.svg
+
+ QXmcnwdh+EKCPulYsTXYcX59Nmo=
+
+ Resources/core/icons/capsule-pill.svg
+
+ k4doywFpOHMRV/5TBxyspU08GUk=
+
+ Resources/core/icons/capsule.svg
+
+ i7uFmqo2dwkdVORAYOxgEdgDRtU=
+
+ Resources/core/icons/car-front-fill.svg
+
+ fHc4m598/lRt1BbHv6FBGJV8y+g=
+
+ Resources/core/icons/car-front.svg
+
+ pp7GpU8XADtWSlBH4e/UWVPtIXU=
+
+ Resources/core/icons/card-checklist.svg
+
+ mbuy3a+N0vdpExNcVk7P0JQV+uc=
+
+ Resources/core/icons/card-heading.svg
+
+ RHflLVgCRnKL5k2v6yVNomYAxQk=
+
+ Resources/core/icons/card-image.svg
+
+ GepqqvDVCKI7RLluL/zOe8Y4WbM=
+
+ Resources/core/icons/card-list.svg
+
+ Q8QRuxVHp3j5X15KGPyaFSiBOHs=
+
+ Resources/core/icons/card-text.svg
+
+ 8x7m+DDFqzpk+JXxqTkRF2UXngA=
+
+ Resources/core/icons/caret-down-fill.svg
+
+ 1E5FAnF/MxQefMTNyEc1cQ8MF4s=
+
+ Resources/core/icons/caret-down-square-fill.svg
+
+ 8leL+37VlNTT9sH2pg41zq7acnc=
+
+ Resources/core/icons/caret-down-square.svg
+
+ BzhW5z7bpHswhuPynaJTkcUzA98=
+
+ Resources/core/icons/caret-down.svg
+
+ 8kLeK3QUCrWX7OoT8dwf3ldsm9E=
+
+ Resources/core/icons/caret-left-fill.svg
+
+ LWuUYwxzAMP52/lVOWbYVdRcsQg=
+
+ Resources/core/icons/caret-left-square-fill.svg
+
+ rRVfRVsBliCRCJPAZsQrm9sY6GQ=
+
+ Resources/core/icons/caret-left-square.svg
+
+ 7Ov6LIOpR9oBOzNFCgjV7IXi7es=
+
+ Resources/core/icons/caret-left.svg
+
+ AyJ0fpORJAxcEGXDFCdDBxb5BcY=
+
+ Resources/core/icons/caret-right-fill.svg
+
+ ix5DHYOQtq6P4rEMBKpS/MxuI5E=
+
+ Resources/core/icons/caret-right-square-fill.svg
+
+ VoH/vsTa+z3IQPGKIC0NH++Ge/k=
+
+ Resources/core/icons/caret-right-square.svg
+
+ d8bE8nLrl4iMSkmaPsyxxXYwaVY=
+
+ Resources/core/icons/caret-right.svg
+
+ YKIiHFCBCv7D4FHIc8wiJmDglUs=
+
+ Resources/core/icons/caret-up-fill.svg
+
+ f7fkNz4uDwnLlIv0nvHHm/u8LJg=
+
+ Resources/core/icons/caret-up-square-fill.svg
+
+ o+959QtHBCh2d47v3Upxygo2VwM=
+
+ Resources/core/icons/caret-up-square.svg
+
+ wygXgU7aa4wtHX/Xy9/l97IyCKE=
+
+ Resources/core/icons/caret-up.svg
+
+ CtSPqc1C/in1kPkit6slByfT0zc=
+
+ Resources/core/icons/cart-check-fill.svg
+
+ WC6rd0CLE3joQOa9lOud0Cn4Rto=
+
+ Resources/core/icons/cart-check.svg
+
+ kpQHtW+zVWabrsOG9Wd4piOwk0w=
+
+ Resources/core/icons/cart-dash-fill.svg
+
+ 6px8XzHs7mran1LdNnPeLQrzUnI=
+
+ Resources/core/icons/cart-dash.svg
+
+ i/4PYK9AhqjNYyggKCuMEa9eUcw=
+
+ Resources/core/icons/cart-fill.svg
+
+ 60dtv0OXonYCRuR4x36Hmm/Zlx0=
+
+ Resources/core/icons/cart-plus-fill.svg
+
+ VmeJdTvzGI93zITUxpliT6VE9OI=
+
+ Resources/core/icons/cart-plus.svg
+
+ bCGYksYjteg9scrodRBOXXqOPuk=
+
+ Resources/core/icons/cart-x-fill.svg
+
+ YdpQhPHlevC8eKyWDg/xQvJwW7E=
+
+ Resources/core/icons/cart-x.svg
+
+ XZ7BrjKXTGBGEmuQ9/UxMhE0EaU=
+
+ Resources/core/icons/cart.svg
+
+ QMXb5229/TyOIZqGH3IHZoNJ4ts=
+
+ Resources/core/icons/cart2.svg
+
+ cbimonMvEt+sizLOTHmYZc9E8dg=
+
+ Resources/core/icons/cart3.svg
+
+ BDeWElMEpl+G+n7qHZHyzHAWcMY=
+
+ Resources/core/icons/cart4.svg
+
+ xItxWYAc24S4BuqxBWiwnJH69K8=
+
+ Resources/core/icons/cash-coin.svg
+
+ 3/ZoAnV4o3Tw8CjzAPk2oP4g5g4=
+
+ Resources/core/icons/cash-stack.svg
+
+ oZggYWoayKc+YP3DPoaa4CLnUrw=
+
+ Resources/core/icons/cash.svg
+
+ Ph6QdW+WBCOGWzYyxYZQEYS7vZU=
+
+ Resources/core/icons/cassette-fill.svg
+
+ F4qM2YcrLWHLYnwbFF87O3KjKvc=
+
+ Resources/core/icons/cassette.svg
+
+ NqwJRccLNfIndzWAS0MiRLNBwh4=
+
+ Resources/core/icons/cast.svg
+
+ 7AP5/IVbHO44nNZzePzGghHysiw=
+
+ Resources/core/icons/cc-circle-fill.svg
+
+ Vm6Qv/hIQwRVuhFP/0Ygg4cOabs=
+
+ Resources/core/icons/cc-circle.svg
+
+ qPdLlG8+7620USOSrlYdHFA2m9I=
+
+ Resources/core/icons/cc-square-fill.svg
+
+ fiQB/lMuk+roe/Pirmei9/Ezvm4=
+
+ Resources/core/icons/cc-square.svg
+
+ di+6h62gQ7sDgC+RrS47FQkQVNM=
+
+ Resources/core/icons/chat-dots-fill.svg
+
+ ZazrPqRYroHjPtanbSYOhLiQc2U=
+
+ Resources/core/icons/chat-dots.svg
+
+ LzYvBS5N3VstJ/Dg9Q6nAxvQ1sM=
+
+ Resources/core/icons/chat-fill.svg
+
+ wR8TW7GkqnvoOa1FP2qaNrQl82g=
+
+ Resources/core/icons/chat-heart-fill.svg
+
+ KuF3cqJgtyLHKi/TDKz0WB+HAPs=
+
+ Resources/core/icons/chat-heart.svg
+
+ eV9zB+45adjvc27F9k2DOQHxeKk=
+
+ Resources/core/icons/chat-left-dots-fill.svg
+
+ rXF8fhNe5gZ+U3aFHVtud1uQDwo=
+
+ Resources/core/icons/chat-left-dots.svg
+
+ UjloQ220aQaPlJYKYJPW5yevrxM=
+
+ Resources/core/icons/chat-left-fill.svg
+
+ lNHbGbO0QHI7NspaoysFOU7xQro=
+
+ Resources/core/icons/chat-left-heart-fill.svg
+
+ Z4UC7eVRDWqULp62Do4Zb3rcZQ4=
+
+ Resources/core/icons/chat-left-heart.svg
+
+ CbokhP0F0b+6YeavFtNRrglPsnY=
+
+ Resources/core/icons/chat-left-quote-fill.svg
+
+ RyqB/9eN40+3ZkGABg3AyacHll0=
+
+ Resources/core/icons/chat-left-quote.svg
+
+ vXfL3FvlL2+wHl6qrl35UofOQb4=
+
+ Resources/core/icons/chat-left-text-fill.svg
+
+ d7+c2a56RvQN7INFMilK6YWW9xQ=
+
+ Resources/core/icons/chat-left-text.svg
+
+ lIR/3z0vtUASRgQpPIuafzCrHfM=
+
+ Resources/core/icons/chat-left.svg
+
+ gKY7ZG5LqavSLJ/FJKgP1bhvCnI=
+
+ Resources/core/icons/chat-quote-fill.svg
+
+ 6A+F8UAUsb9ibCRVSBNUc7gtOMw=
+
+ Resources/core/icons/chat-quote.svg
+
+ uZ8QPMXSmb49K2ftHazAPyVPtRc=
+
+ Resources/core/icons/chat-right-dots-fill.svg
+
+ ZTq7t85jN7Y2rdV2jnvfClWa9LQ=
+
+ Resources/core/icons/chat-right-dots.svg
+
+ JHb/cG3lZzFhxElA8FM6j7II4KQ=
+
+ Resources/core/icons/chat-right-fill.svg
+
+ 91HiE1qN2kFFuos3evuRh2l6V/g=
+
+ Resources/core/icons/chat-right-heart-fill.svg
+
+ jD+EUf//qBLG9htD98LdOP2CixI=
+
+ Resources/core/icons/chat-right-heart.svg
+
+ PFHIn8QRhw0Sn81NZ5OQrnmAiAA=
+
+ Resources/core/icons/chat-right-quote-fill.svg
+
+ DUgw1lPD/hATJ0TVh+XR3L+yy5E=
+
+ Resources/core/icons/chat-right-quote.svg
+
+ 9ylj8I3c/iqxD0G07O7MGxeuWpQ=
+
+ Resources/core/icons/chat-right-text-fill.svg
+
+ LM/wwyRYaxzL8HODMMdmuVMrjpc=
+
+ Resources/core/icons/chat-right-text.svg
+
+ rd69wfG+3xxnL1fePMBwLBZC6t0=
+
+ Resources/core/icons/chat-right.svg
+
+ a9PWPzR3LhqqyqxNNHXtzPS1bak=
+
+ Resources/core/icons/chat-square-dots-fill.svg
+
+ nBYuU6HMj5sghdkN73cNsHKLd5Y=
+
+ Resources/core/icons/chat-square-dots.svg
+
+ RUFZMbj+dRceZv/abB6Xn5reZhY=
+
+ Resources/core/icons/chat-square-fill.svg
+
+ dv7ApUmHD0+6BfBMQz51ho6AyIg=
+
+ Resources/core/icons/chat-square-heart-fill.svg
+
+ GbLfklLfxxow3C68AuO3Vmmhwtg=
+
+ Resources/core/icons/chat-square-heart.svg
+
+ c3egEUA/tok3zQhv+ydWkC/iWEE=
+
+ Resources/core/icons/chat-square-quote-fill.svg
+
+ T8Z8BQma1aJhSsTXkm0rVcqdvvs=
+
+ Resources/core/icons/chat-square-quote.svg
+
+ 1JMHuBBFi8gBivXQk1SxjOGDo0k=
+
+ Resources/core/icons/chat-square-text-fill.svg
+
+ MoT5ORCPFJ7iZGQeMJQJ84zKFVE=
+
+ Resources/core/icons/chat-square-text.svg
+
+ WMZ5KRMyYIuekfUTT/f8xKYyTLU=
+
+ Resources/core/icons/chat-square.svg
+
+ dlTIIYCX7/hVBWfWEF5xAY1yd0I=
+
+ Resources/core/icons/chat-text-fill.svg
+
+ 6lmz3z4QDqRrtxfO4MNit8xD9GY=
+
+ Resources/core/icons/chat-text.svg
+
+ /rTgIilI2bc+vYHrYsPmXFdeOB0=
+
+ Resources/core/icons/chat.svg
+
+ T7ltF9FORBcPpHQ188y03eTw1og=
+
+ Resources/core/icons/check-all.svg
+
+ Wq/FE76cJLESo+csEWzeq2R7XL4=
+
+ Resources/core/icons/check-circle-fill.svg
+
+ Tcd4TC3ZVlC3OQ7gr5xXQC0pJCQ=
+
+ Resources/core/icons/check-circle.svg
+
+ DQsie8aUD+NYodI1dqlJbaTh3oQ=
+
+ Resources/core/icons/check-lg.svg
+
+ XgBRgekEw16lGD2XQOU2kbx3Fow=
+
+ Resources/core/icons/check-square-fill.svg
+
+ +dB0+7SB4i/dzCI/tfS5IUReRIs=
+
+ Resources/core/icons/check-square.svg
+
+ vxTNy9CukDiFOesXZLsb9pSPlMw=
+
+ Resources/core/icons/check.svg
+
+ rHIq7QF+he86SEaMwj2Z6rvNL9Y=
+
+ Resources/core/icons/check2-all.svg
+
+ nsBAzt+l8slqfzNcbae8+fGNKM4=
+
+ Resources/core/icons/check2-circle.svg
+
+ e6saOhf6fJpalgKdhElrHVRT2ek=
+
+ Resources/core/icons/check2-square.svg
+
+ 0xS2bLmc1Gxw9XUXGnhj9vvepYo=
+
+ Resources/core/icons/check2.svg
+
+ ajHgpB4V9bloBbivSedt1Urmiu4=
+
+ Resources/core/icons/chevron-bar-contract.svg
+
+ gpMv1sVN8WurkURH1y+3rMaAf7c=
+
+ Resources/core/icons/chevron-bar-down.svg
+
+ CtNfo/n/UIYNWAJ7k6h/h/7hE20=
+
+ Resources/core/icons/chevron-bar-expand.svg
+
+ eLjlonJpx6A0HdG/mNUQBTxUY78=
+
+ Resources/core/icons/chevron-bar-left.svg
+
+ pqxWsPitpX6aArFZLhLseqBd+a8=
+
+ Resources/core/icons/chevron-bar-right.svg
+
+ 0DpRQDEPEd8NaR4BMzAjDQbfEh8=
+
+ Resources/core/icons/chevron-bar-up.svg
+
+ biODH7kgn7qFUII7Bn2YF04VjG8=
+
+ Resources/core/icons/chevron-compact-down.svg
+
+ KJ+49sX98orlqduCUVaeln76Ffs=
+
+ Resources/core/icons/chevron-compact-left.svg
+
+ 9YGZrgxpsmMROLf5/VFUSpvb9lM=
+
+ Resources/core/icons/chevron-compact-right.svg
+
+ axgWC2VX73WPnHyGk42iQXlIjWs=
+
+ Resources/core/icons/chevron-compact-up.svg
+
+ rdFKhq/7o6y6Uo4RHOrbadZ5zYE=
+
+ Resources/core/icons/chevron-contract.svg
+
+ OWXgt06w2X1TGWT4+bvUQQMODbw=
+
+ Resources/core/icons/chevron-double-down.svg
+
+ 03Oa9chy5uGhqMJl304m4BXsWIQ=
+
+ Resources/core/icons/chevron-double-left.svg
+
+ lhDpTU9RMFh0FPoTv0QoIH7JbEg=
+
+ Resources/core/icons/chevron-double-right.svg
+
+ NXTbBhDVeoiYvbrdZ+n3o54KlpQ=
+
+ Resources/core/icons/chevron-double-up.svg
+
+ sohyDHGvZeBzxTlQ3wDJhfInjdI=
+
+ Resources/core/icons/chevron-down.svg
+
+ Povfvc95W9fX7SriRMq71gy+OpE=
+
+ Resources/core/icons/chevron-expand.svg
+
+ TwVS48nh+nQz1wEu003s7tppLmE=
+
+ Resources/core/icons/chevron-left.svg
+
+ sVReYFG/Cx7F0SL2E05cNk3qDhM=
+
+ Resources/core/icons/chevron-right.svg
+
+ Y1oB8mIipBTgSHeK5GH02VBWhrw=
+
+ Resources/core/icons/chevron-up.svg
+
+ Gg+qn7Td5Z8DvppOs6LBW6RQxes=
+
+ Resources/core/icons/circle-fill.svg
+
+ kZ5btNNPDb9DtrldfXd+JfuNGfY=
+
+ Resources/core/icons/circle-half.svg
+
+ ekQHJ+MXh9Z91qzOGCofQjYFZ04=
+
+ Resources/core/icons/circle-square.svg
+
+ kWOwJCk8XEyoLNj2TSSH0lxj6aY=
+
+ Resources/core/icons/circle.svg
+
+ t1lvpCSGGxhCPtYVCuN4pd8a/Ls=
+
+ Resources/core/icons/claude.svg
+
+ J6lOcKnccxkCyzD4QhJVuYeVZ3c=
+
+ Resources/core/icons/clipboard-check-fill.svg
+
+ d4sfdr8jz+bI1QNkZiZRvOuxb4Q=
+
+ Resources/core/icons/clipboard-check.svg
+
+ yaJECW5Eb6W9VLOp43oj61cR66g=
+
+ Resources/core/icons/clipboard-data-fill.svg
+
+ iVhOFSN5PvfNlKlcHOZC3+vRO6M=
+
+ Resources/core/icons/clipboard-data.svg
+
+ PAQpDwk9RO/1W6NREynYphJ8x2Q=
+
+ Resources/core/icons/clipboard-fill.svg
+
+ zHEz9OS7O/yYZ3ZBnUlSI4u3P9Q=
+
+ Resources/core/icons/clipboard-heart-fill.svg
+
+ RFh/t0ILSvjWfrcShCSsQUZqrDI=
+
+ Resources/core/icons/clipboard-heart.svg
+
+ 0MgE6MM0KgyH6C8dqdNEoHWPOr8=
+
+ Resources/core/icons/clipboard-minus-fill.svg
+
+ NtS/S9qKp5L3bwaQdYnUpmNwIQw=
+
+ Resources/core/icons/clipboard-minus.svg
+
+ n+4aKUCQYfI5I7OKRiJ9vL4w0cc=
+
+ Resources/core/icons/clipboard-plus-fill.svg
+
+ b7mGMmIHquAm73ht/2qBVvSkyOk=
+
+ Resources/core/icons/clipboard-plus.svg
+
+ LBsIhL183tL5uZFNKugdqr6rxT0=
+
+ Resources/core/icons/clipboard-pulse.svg
+
+ EDEpiItg/OtVgTLm9BQz2iwg6l0=
+
+ Resources/core/icons/clipboard-x-fill.svg
+
+ Ap026Boi+lvbPwuwkV4mBo3gAKs=
+
+ Resources/core/icons/clipboard-x.svg
+
+ TvZu1b1FOyQsfY/EjqT9f639zGA=
+
+ Resources/core/icons/clipboard.svg
+
+ lAMsVYKslpeL+NhyxbNwK62MsjU=
+
+ Resources/core/icons/clipboard2-check-fill.svg
+
+ enq3lNn4fvTPJa8sZZdhsdD2t34=
+
+ Resources/core/icons/clipboard2-check.svg
+
+ vpr9ytO+n2PRh+pBCzayfmpRbeY=
+
+ Resources/core/icons/clipboard2-data-fill.svg
+
+ bwYtVhwHGsHPlfPQGBOnZK0i/yo=
+
+ Resources/core/icons/clipboard2-data.svg
+
+ 4YHKIszWEu+krlNzUUhCiZnqFIg=
+
+ Resources/core/icons/clipboard2-fill.svg
+
+ jj9aJJUgPx3XNvZe7ip8t/t1CKs=
+
+ Resources/core/icons/clipboard2-heart-fill.svg
+
+ kGHi4a8TQ57sSYjWEXITbNTeJ94=
+
+ Resources/core/icons/clipboard2-heart.svg
+
+ JUPzYLct9yjXGE2u5IikA+plOjg=
+
+ Resources/core/icons/clipboard2-minus-fill.svg
+
+ 2nbG+MFb/B29H2I1IKYsCnEg3i0=
+
+ Resources/core/icons/clipboard2-minus.svg
+
+ TZUgEMi3wVgOC2fMllls1ueqEy4=
+
+ Resources/core/icons/clipboard2-plus-fill.svg
+
+ QFCaVJSekyMcdjwY1oMXnF+DP/w=
+
+ Resources/core/icons/clipboard2-plus.svg
+
+ vuhC+NnviQRL1L+jCGsvdi2R2iU=
+
+ Resources/core/icons/clipboard2-pulse-fill.svg
+
+ S30Zjd/pyADCtIPpTPqi3PgM8ls=
+
+ Resources/core/icons/clipboard2-pulse.svg
+
+ 7eXuiAJIxa0Wkgps7YmcrskF2Eo=
+
+ Resources/core/icons/clipboard2-x-fill.svg
+
+ X4tDSe9trDVeWPLRtwVUGvdmp/A=
+
+ Resources/core/icons/clipboard2-x.svg
+
+ 3TH4Lg8gLoUlD8/3Pre45Rec6Ik=
+
+ Resources/core/icons/clipboard2.svg
+
+ 95XFKHOZJVtI+2Z2XRIo6xCWpQk=
+
+ Resources/core/icons/clock-fill.svg
+
+ 78AYMk8JTNMbSbpykcj2I0O9gdY=
+
+ Resources/core/icons/clock-history.svg
+
+ lNK21I5iL1RmeHrniiWauiLbl+c=
+
+ Resources/core/icons/clock.svg
+
+ HjFI2JAODSo6FJrRPHKvIGQ6mOE=
+
+ Resources/core/icons/cloud-arrow-down-fill.svg
+
+ EQtLLRJgBHNrih0tgVrn40w3obc=
+
+ Resources/core/icons/cloud-arrow-down.svg
+
+ 2Rsq7aINsR0+oclppZC3Mc7mLoo=
+
+ Resources/core/icons/cloud-arrow-up-fill.svg
+
+ N42b2HBwXpdrDer+vymloQNXSyM=
+
+ Resources/core/icons/cloud-arrow-up.svg
+
+ SmNNyZklvXYnp7RYVUNjlE7d+/s=
+
+ Resources/core/icons/cloud-check-fill.svg
+
+ DrCVA59PU+/dw4hsOIOSVn5bN+g=
+
+ Resources/core/icons/cloud-check.svg
+
+ cTFHnJxborW14rhh/pWTUeHmgps=
+
+ Resources/core/icons/cloud-download-fill.svg
+
+ bjZV7HMS4CAEUlozIflp0caFW5E=
+
+ Resources/core/icons/cloud-download.svg
+
+ Tawxci6U56VsCXxlyzUpOlHCvHc=
+
+ Resources/core/icons/cloud-drizzle-fill.svg
+
+ Xp3gumzTMSlUTHqMQ21q9X848gE=
+
+ Resources/core/icons/cloud-drizzle.svg
+
+ ysnjaroKEqZKkSIvpoHOrCtiCUw=
+
+ Resources/core/icons/cloud-fill.svg
+
+ q4nBpA1UpM3yi0RRMiefopdqb3w=
+
+ Resources/core/icons/cloud-fog-fill.svg
+
+ z1BNsUkXl4OQJALE1crLTXGAaBI=
+
+ Resources/core/icons/cloud-fog.svg
+
+ OnUs1Z/hZxorVCO5fn64YGT+QXQ=
+
+ Resources/core/icons/cloud-fog2-fill.svg
+
+ WIyH6/Xf8D+c5xsVAeWgoEEcCIY=
+
+ Resources/core/icons/cloud-fog2.svg
+
+ OPdA25c1N2ZiwJ52aTEebsR4kqU=
+
+ Resources/core/icons/cloud-hail-fill.svg
+
+ 2mcHAchTXziAIahoiBdLLsJJG5U=
+
+ Resources/core/icons/cloud-hail.svg
+
+ bml0gK912BxzpsedrSiwD/pjRLk=
+
+ Resources/core/icons/cloud-haze-fill.svg
+
+ Q8LRhpoCy2w5XjiN/q4mKH5VcEs=
+
+ Resources/core/icons/cloud-haze.svg
+
+ UlsaKhKYUEViKVhKto/7f0/IE1o=
+
+ Resources/core/icons/cloud-haze2-fill.svg
+
+ cpLZ2+r8oNTkrdHPc89lb/HLFoE=
+
+ Resources/core/icons/cloud-haze2.svg
+
+ DRJZNfVfzMQQZNPhtez+Mu9zFsw=
+
+ Resources/core/icons/cloud-lightning-fill.svg
+
+ UT8ANPQQ7USrJKoXdo5ioBq4gao=
+
+ Resources/core/icons/cloud-lightning-rain-fill.svg
+
+ JXs+w1mu76I2YM5oubmJBXXTkT8=
+
+ Resources/core/icons/cloud-lightning-rain.svg
+
+ E+0iJ8VqMEENxQbHGpFgeikvse0=
+
+ Resources/core/icons/cloud-lightning.svg
+
+ 7pK10Pn6zG/BrZOTrVu6GeFnIj8=
+
+ Resources/core/icons/cloud-minus-fill.svg
+
+ ikisJWbozm5UgTSOfSk4pLbKPzQ=
+
+ Resources/core/icons/cloud-minus.svg
+
+ v4w782F/Y3UOjGHKccnX6Bm4khw=
+
+ Resources/core/icons/cloud-moon-fill.svg
+
+ Bt4ySSzme1PnI8UEpEorLWg2c6Q=
+
+ Resources/core/icons/cloud-moon.svg
+
+ rQfBvZ+vu48axxv2INeAG/lyVGM=
+
+ Resources/core/icons/cloud-plus-fill.svg
+
+ kU+rJnQaniH7oLLyZtNcm/UI/hI=
+
+ Resources/core/icons/cloud-plus.svg
+
+ tGRl//Ty+UbFdY4vuvFTEv0KXJY=
+
+ Resources/core/icons/cloud-rain-fill.svg
+
+ mNnMHCjgS1ZmgeJezZCIDcAvHg8=
+
+ Resources/core/icons/cloud-rain-heavy-fill.svg
+
+ U0h5oqYFRUM2AHXoi7XQaWEzeRE=
+
+ Resources/core/icons/cloud-rain-heavy.svg
+
+ BVTFdTDD872WhD2NxEWQbIjBgfU=
+
+ Resources/core/icons/cloud-rain.svg
+
+ VS+1X6nvzqXffbNXH2Ojip3UBdY=
+
+ Resources/core/icons/cloud-slash-fill.svg
+
+ DQlYO6bNY73d2Np8glozC/peZ+Y=
+
+ Resources/core/icons/cloud-slash.svg
+
+ mZ/oId+H4X1gorDZe29CRvv67lw=
+
+ Resources/core/icons/cloud-sleet-fill.svg
+
+ GhCKa0nw9zWCf2uL27r8ne+KwLk=
+
+ Resources/core/icons/cloud-sleet.svg
+
+ B5XRpI+s13O5mfP4fP6DY1ibW44=
+
+ Resources/core/icons/cloud-snow-fill.svg
+
+ Ix86v+Q1jWnaNJJ8OI0Jp8XcDbE=
+
+ Resources/core/icons/cloud-snow.svg
+
+ lib43qY15RQsfBurN0oByqoq4Wk=
+
+ Resources/core/icons/cloud-sun-fill.svg
+
+ T7pPZfZRPtIMtcAkurn74DEYwVk=
+
+ Resources/core/icons/cloud-sun.svg
+
+ 7qNedgusFsPxgh5h5ewYoUpuB+g=
+
+ Resources/core/icons/cloud-upload-fill.svg
+
+ 9MoTqy9Mm7m9To7Vqy8Qi1No3e8=
+
+ Resources/core/icons/cloud-upload.svg
+
+ ET8mBi678Sgxq4WCjtmoAVVLgJI=
+
+ Resources/core/icons/cloud.svg
+
+ 0GStnPZ96YAx7X8jyDIPmaqnQ1U=
+
+ Resources/core/icons/clouds-fill.svg
+
+ PUt7dO0ZPrZpBje5sjJ97X5y/uQ=
+
+ Resources/core/icons/clouds.svg
+
+ KQJZL5seVnSPiusL7DrittSVkVs=
+
+ Resources/core/icons/cloudy-fill.svg
+
+ YinvusyFZCZOiiyuJGbbDjzUPYA=
+
+ Resources/core/icons/cloudy.svg
+
+ xIQq2FQ4G+gTw9RzuAqqeKR/grk=
+
+ Resources/core/icons/code-slash.svg
+
+ aIexDo+pBQNDI4mymDOYENXp/P8=
+
+ Resources/core/icons/code-square.svg
+
+ NYBQY8o3j5u4L/mBAMDJkNzW49s=
+
+ Resources/core/icons/code.svg
+
+ R3RzFMTTVYRtwaTrC2pj1UwgtF4=
+
+ Resources/core/icons/coin.svg
+
+ tHmBagjnSUl1BzzQm5x0UQfDmI4=
+
+ Resources/core/icons/collection-fill.svg
+
+ cTPmEBH5V3SesWREFKdZCJSRj/M=
+
+ Resources/core/icons/collection-play-fill.svg
+
+ tudb0+ByU6spFf7Bpdrs2xxmvc8=
+
+ Resources/core/icons/collection-play.svg
+
+ 0ksMwXs1hnC8R8l4/XMekVzi4oI=
+
+ Resources/core/icons/collection.svg
+
+ n+UR99UwkRKXov14z4qY5JaOldk=
+
+ Resources/core/icons/columns-gap.svg
+
+ 7wRHAc689IcQuik2g7EmVWMdBnQ=
+
+ Resources/core/icons/columns.svg
+
+ q0Vr5YuD1IwNGMsUp11KrBztdJM=
+
+ Resources/core/icons/command.svg
+
+ Vt+x11b7SpIo2YKiiEOsTQwpnYU=
+
+ Resources/core/icons/compass-fill.svg
+
+ LGnC+7Sp6uSRve8q8b2m5Bfg50c=
+
+ Resources/core/icons/compass.svg
+
+ NlHFFy4n6EiwVMHxwi4tbQa6S8c=
+
+ Resources/core/icons/cone-striped.svg
+
+ sHmejiqxn4odRywLmBbdLJs5Csk=
+
+ Resources/core/icons/cone.svg
+
+ hsMSYhnPCrfuzk0GUHfCuyHO2Fo=
+
+ Resources/core/icons/controller.svg
+
+ ri7noOLHBBm3SL0mxOJLaZoQf0E=
+
+ Resources/core/icons/cookie.svg
+
+ q2BCVOf+ttnGv5P0FcrubGTeM2s=
+
+ Resources/core/icons/copy.svg
+
+ MwbMtg2EG2HxgUtT7rqVtBtEzO4=
+
+ Resources/core/icons/cpu-fill.svg
+
+ YMIn5wVkzxompZ64JDeWUWd0k24=
+
+ Resources/core/icons/cpu.svg
+
+ iWXRURqNUMYv6Y0V0UOsvkpKXFM=
+
+ Resources/core/icons/credit-card-2-back-fill.svg
+
+ G6F3wBS+51okVU2zYlnn+zpX2M0=
+
+ Resources/core/icons/credit-card-2-back.svg
+
+ q62eScSacpq5XJ2qcDY95nWelt4=
+
+ Resources/core/icons/credit-card-2-front-fill.svg
+
+ RlSrTmU8O7oSw4lhyKtN52eV3xk=
+
+ Resources/core/icons/credit-card-2-front.svg
+
+ shJC5d8pxmI84+jJpLn9H0+0Dkg=
+
+ Resources/core/icons/credit-card-fill.svg
+
+ Qcr6SY/CmX6gxI7ESpTT29Cyztk=
+
+ Resources/core/icons/credit-card.svg
+
+ zuI0waG2XwRT8+n7zeJ2LL4QBSE=
+
+ Resources/core/icons/crop.svg
+
+ E9q5rU808x0MG+1C4cvCBWboInc=
+
+ Resources/core/icons/crosshair.svg
+
+ P5CAyfvg5RsnrXSMuRAqbb98K6w=
+
+ Resources/core/icons/crosshair2.svg
+
+ 5I/9KXjyiVe5vE3+fuSNwOiCADk=
+
+ Resources/core/icons/css.svg
+
+ AsjPXt0FeFxpFtQ3WlIKw+yd1L0=
+
+ Resources/core/icons/cup-fill.svg
+
+ c5rnzMUvYxLSgYv74Fr6TieDMDc=
+
+ Resources/core/icons/cup-hot-fill.svg
+
+ yvW7OIFKb1s/o4cn22SjhVc5naU=
+
+ Resources/core/icons/cup-hot.svg
+
+ n6Xd/PnCDiNgO1OStoBPWKFHkHA=
+
+ Resources/core/icons/cup-straw.svg
+
+ Ol/L2LgrujtEg88rbMvIKrEY1Yw=
+
+ Resources/core/icons/cup.svg
+
+ aooc0AJ6j6+o1dBHuBsLDOJDPu4=
+
+ Resources/core/icons/currency-bitcoin.svg
+
+ HEHY5BKjGHq1GYlynQm1De28xMs=
+
+ Resources/core/icons/currency-dollar.svg
+
+ SB6Z2WZRQeLpaNCoGS6GOKkzrQY=
+
+ Resources/core/icons/currency-euro.svg
+
+ x2rW3A+3CeD2UJ/9ZADdaarPUVM=
+
+ Resources/core/icons/currency-exchange.svg
+
+ yfjyLwGYEz0P1Wgbpwna8IFdJJc=
+
+ Resources/core/icons/currency-pound.svg
+
+ w6QThQOSgbm+hIr4fswX46o/g80=
+
+ Resources/core/icons/currency-rupee.svg
+
+ LgJfg3SgnNOLacBut6rp9TI4lYQ=
+
+ Resources/core/icons/currency-yen.svg
+
+ y4RewQGNFGZUsVMg2SftRgd1w3c=
+
+ Resources/core/icons/cursor-fill.svg
+
+ UyoUF9rtuQL8PBr4+4vDlknYHsw=
+
+ Resources/core/icons/cursor-text.svg
+
+ NMGaqKICt1+SQSzuXsWVCGGvqio=
+
+ Resources/core/icons/cursor.svg
+
+ Y+PlzaztxKmd2BctKg6pE723Yns=
+
+ Resources/core/icons/dash-circle-dotted.svg
+
+ WO3w1xK5sB+ZPgXfoa8e/cj9Dpk=
+
+ Resources/core/icons/dash-circle-fill.svg
+
+ IhgbExFMo3sNbSk3dOdgkR3OqQA=
+
+ Resources/core/icons/dash-circle.svg
+
+ Ss29CDZHp18uHQFkLkvyIl+Q0v4=
+
+ Resources/core/icons/dash-lg.svg
+
+ szm+GgGyLRu6s81A7ZSo2mGYeg8=
+
+ Resources/core/icons/dash-square-dotted.svg
+
+ Cc1uNRDA85NylBg5+GrE0QJuyVE=
+
+ Resources/core/icons/dash-square-fill.svg
+
+ zjWphIn6Z9tOGfxoq6wS0lqobv0=
+
+ Resources/core/icons/dash-square.svg
+
+ 066gTT34liI+XMu5TooWVUqdQlw=
+
+ Resources/core/icons/dash.svg
+
+ 0fyRf3RZ6In5eoBGgrNY+5IxjRM=
+
+ Resources/core/icons/database-add.svg
+
+ rJ5QzvdXt+aYv8dGkzLQ9DLLqmg=
+
+ Resources/core/icons/database-check.svg
+
+ 745+INBKmjWQfHojKAkLW1ydfHA=
+
+ Resources/core/icons/database-dash.svg
+
+ 2qVdfPgjpG0zMAGWPDQFMlOkuGs=
+
+ Resources/core/icons/database-down.svg
+
+ gdz3AtGUbrbVXsSQIHVemIUV/PA=
+
+ Resources/core/icons/database-exclamation.svg
+
+ kPNTitlv/aWQxoXJsZy1U+gfHes=
+
+ Resources/core/icons/database-fill-add.svg
+
+ IA+ZWnyks2HZhugnUsq4M5qIiZE=
+
+ Resources/core/icons/database-fill-check.svg
+
+ WDpHqjiRKwelsXquyTD7juzPlXY=
+
+ Resources/core/icons/database-fill-dash.svg
+
+ +4QRTBERrzQ4y+QVTdgx0kk4N7k=
+
+ Resources/core/icons/database-fill-down.svg
+
+ EF8TZpRGw58JLP89+YsYvv0sPRg=
+
+ Resources/core/icons/database-fill-exclamation.svg
+
+ 2JNtUF3oY8mZ092LBBmeKqzEcoU=
+
+ Resources/core/icons/database-fill-gear.svg
+
+ OqpY50qiZ4R2YxVRkxzQZ54Z/xU=
+
+ Resources/core/icons/database-fill-lock.svg
+
+ yG2s+2Ncwwu7fn+wubNKy7bqv84=
+
+ Resources/core/icons/database-fill-slash.svg
+
+ 7ob2rQIXxNZVMFG9l21ed0moHlQ=
+
+ Resources/core/icons/database-fill-up.svg
+
+ slqYnGvpnxXww8MwY1NQSzUvQFc=
+
+ Resources/core/icons/database-fill-x.svg
+
+ 4bivwp2xkO40Qvxqo9oNj1oB5lw=
+
+ Resources/core/icons/database-fill.svg
+
+ 62GJVluIfQKc/ypLSInY/cA93MQ=
+
+ Resources/core/icons/database-gear.svg
+
+ ZmG4/Ul4BdXe1vDgCPlY8xSfNow=
+
+ Resources/core/icons/database-lock.svg
+
+ kzoY1ix9Ku/b7sfqzmlsmO1SwkY=
+
+ Resources/core/icons/database-slash.svg
+
+ 8RjxBqKZlxOBqCLTrQECQf2XCcw=
+
+ Resources/core/icons/database-up.svg
+
+ n0S974FNPad7SCrRLaOQgol+G80=
+
+ Resources/core/icons/database-x.svg
+
+ jgMvgIfJxhojX+xvqNfzNNBojD0=
+
+ Resources/core/icons/database.svg
+
+ 7URyv64TnvfiQbAeGaOg3imQbbg=
+
+ Resources/core/icons/device-hdd-fill.svg
+
+ ykr1OGqzAomBEKgNq908JBaIfRc=
+
+ Resources/core/icons/device-hdd.svg
+
+ 038016D1nWxusBEPr0vWAzKchCI=
+
+ Resources/core/icons/device-ssd-fill.svg
+
+ APKlOGKJMMEI+vIRDeEc7iKr/1w=
+
+ Resources/core/icons/device-ssd.svg
+
+ gR3tCeZVh5YKd4oj+b3JZeHJkVI=
+
+ Resources/core/icons/diagram-2-fill.svg
+
+ RBiE/OKYfavZ+LvNTFOc9i9+e9o=
+
+ Resources/core/icons/diagram-2.svg
+
+ ThXOSZ9EI8yRMBPxhYzLu7DXyPc=
+
+ Resources/core/icons/diagram-3-fill.svg
+
+ egQ54ZdPWqnUeIc2qwAD+V6tajw=
+
+ Resources/core/icons/diagram-3.svg
+
+ ZigRWPXqGDPMjVLMKA/EaMr7ZCk=
+
+ Resources/core/icons/diamond-fill.svg
+
+ OKMmVm9Hvkig5lpzkNVnLHFEEdU=
+
+ Resources/core/icons/diamond-half.svg
+
+ kczNBsXR7xitKhcWxROIYQta4Cs=
+
+ Resources/core/icons/diamond.svg
+
+ FMPxfG2UAA0m1N640unKjIJFMPc=
+
+ Resources/core/icons/dice-1-fill.svg
+
+ weMKrhhyPETfqFKhsTnAd+j59/o=
+
+ Resources/core/icons/dice-1.svg
+
+ 6ewuUsQPI1afk2vqFsY+JJOiIEM=
+
+ Resources/core/icons/dice-2-fill.svg
+
+ HkPrlh0iEHonwfRyjHP0B8NO548=
+
+ Resources/core/icons/dice-2.svg
+
+ tkJid90bGsuAIywf7h1evQch3zY=
+
+ Resources/core/icons/dice-3-fill.svg
+
+ Cf+vKnclrme2hlcMPI1WUwA4A9k=
+
+ Resources/core/icons/dice-3.svg
+
+ H2c46fTYLzUszfolb4sGYgscgEY=
+
+ Resources/core/icons/dice-4-fill.svg
+
+ 34LeZr4nWSAIK/Ln3n0JpngEFU0=
+
+ Resources/core/icons/dice-4.svg
+
+ rRInT0feVRqDeZXlw6Ir/GAR44k=
+
+ Resources/core/icons/dice-5-fill.svg
+
+ dL1Oj72akg5nBopxGCUe3kgRBCs=
+
+ Resources/core/icons/dice-5.svg
+
+ yCQ1GZPnLq649Cwl9HmFlTK3VbQ=
+
+ Resources/core/icons/dice-6-fill.svg
+
+ SjDYIvubgDSktl5ZB0zv3NMhrlc=
+
+ Resources/core/icons/dice-6.svg
+
+ WzSHsNVXnwqnK77+ZCYpHTLncFk=
+
+ Resources/core/icons/disc-fill.svg
+
+ 2wkDuGBL/S/Uxy6oArJsXoicniY=
+
+ Resources/core/icons/disc.svg
+
+ viATwDw5es2yGW9FT/5wdOXPjYg=
+
+ Resources/core/icons/discord.svg
+
+ rR3vVUfR9D5EkRIwLkCJUF2T2xg=
+
+ Resources/core/icons/display-fill.svg
+
+ qljK9NM8U80xUna/qvCwd1iXYzg=
+
+ Resources/core/icons/display.svg
+
+ tlLzgEs7FJ5vAt4QqdLL+Z8d+lo=
+
+ Resources/core/icons/displayport-fill.svg
+
+ 0oKa4Ise6NO1SWljnqz9uvyHz8I=
+
+ Resources/core/icons/displayport.svg
+
+ SeavJ6yTIVY9zgBQNdKvblx846w=
+
+ Resources/core/icons/distribute-horizontal.svg
+
+ R6+0NJJmS03Vl9pCC5XazppJ3fE=
+
+ Resources/core/icons/distribute-vertical.svg
+
+ T9TxV5V+MeZTd9seyx/29XMj1vE=
+
+ Resources/core/icons/door-closed-fill.svg
+
+ CcxDmVIPsW5nJKV3tgjtvwVQbKk=
+
+ Resources/core/icons/door-closed.svg
+
+ CNImdpABOCfVecbP9PHEJQe/sZM=
+
+ Resources/core/icons/door-open-fill.svg
+
+ 5ANgDwxnFHrKknJ6FarUkZeF3Ys=
+
+ Resources/core/icons/door-open.svg
+
+ 9yqXmrKS+2EImIazN2CWaG/gFOs=
+
+ Resources/core/icons/dot.svg
+
+ sUQRPYQBFxrfHFyd7bCyT7kJk6g=
+
+ Resources/core/icons/download.svg
+
+ Kaai+b1Adqy9FSC4I2ZpBMOA6lo=
+
+ Resources/core/icons/dpad-fill.svg
+
+ Ywq6AjUpDyxoMtoF5ke1QiQTYLU=
+
+ Resources/core/icons/dpad.svg
+
+ +B8/3qIreRuiNx3EhgvzYOGp/kM=
+
+ Resources/core/icons/dribbble.svg
+
+ YZbA70LFB00mKT7yg8cXJpbd9eQ=
+
+ Resources/core/icons/dropbox.svg
+
+ fy5s7SVY014Sg9C6qH+f8ctH6jo=
+
+ Resources/core/icons/droplet-fill.svg
+
+ uklyUPxY5s/GSpHqDC1zCsQY/cg=
+
+ Resources/core/icons/droplet-half.svg
+
+ qzg3HnkYaqXzCaqchw8jJPOadBU=
+
+ Resources/core/icons/droplet.svg
+
+ DxMPM8Q+e2LiRoR6LmVMu82mJZw=
+
+ Resources/core/icons/duffle-fill.svg
+
+ X2I2fSg0k1gd6qdKoW4srkvO/J8=
+
+ Resources/core/icons/duffle.svg
+
+ x2qVl70+LptsP+qDfPvC1r4FHM0=
+
+ Resources/core/icons/ear-fill.svg
+
+ /DYs6xUCJoLcjMcQai1uHN0RUuc=
+
+ Resources/core/icons/ear.svg
+
+ YvCRnOMknUushFy4wU8y5I0kF5o=
+
+ Resources/core/icons/earbuds.svg
+
+ sbBWEIjirVodoVYOGf/7U0ej2TE=
+
+ Resources/core/icons/easel-fill.svg
+
+ 7yJlxqZVB5f3emytBeDzEhPKfto=
+
+ Resources/core/icons/easel.svg
+
+ 9OTYuo0uY+qfBDCI01tVLHbaMlE=
+
+ Resources/core/icons/easel2-fill.svg
+
+ FE4J9+BHckNpLjWUJ4eHQFm6/ns=
+
+ Resources/core/icons/easel2.svg
+
+ Lp3gW3TUh0aiwrfI1TZOCrozhbI=
+
+ Resources/core/icons/easel3-fill.svg
+
+ qdlWes3l0SAQ1e78ofLRDNAWYSg=
+
+ Resources/core/icons/easel3.svg
+
+ uIx9t/uqs8lP/w733/fzrP7QBd8=
+
+ Resources/core/icons/egg-fill.svg
+
+ 3ufsmi+QqJbjpmcx4Lo4JrWZQ5s=
+
+ Resources/core/icons/egg-fried.svg
+
+ d2D+3e7LTYP1Oh5qyFOIxI9MzQI=
+
+ Resources/core/icons/egg.svg
+
+ u+Fqu5GxHvNIeMwOpemeEfK4JL4=
+
+ Resources/core/icons/eject-fill.svg
+
+ p9zZN4OF835Fwo7qt5OU6foFA+E=
+
+ Resources/core/icons/eject.svg
+
+ t5Zl5kXQCcbbuiqcGKIX/xUC4WY=
+
+ Resources/core/icons/emoji-angry-fill.svg
+
+ EcQBUsOUd5237gRIscqFW//F2GQ=
+
+ Resources/core/icons/emoji-angry.svg
+
+ jZI7qUrVsydYoJPNGTGgZjkG9gg=
+
+ Resources/core/icons/emoji-astonished-fill.svg
+
+ 4k5LF+110SkHzKEvgLQ6QyqBH1I=
+
+ Resources/core/icons/emoji-astonished.svg
+
+ 5SxEKOAZ/cYU6EHF+wmtyQM2++4=
+
+ Resources/core/icons/emoji-dizzy-fill.svg
+
+ cxeRB4FQCyd1rfSh4hrJ0vZejN4=
+
+ Resources/core/icons/emoji-dizzy.svg
+
+ +Q6WQ6XIBq0oyQ4t/8UeUrT5nN4=
+
+ Resources/core/icons/emoji-expressionless-fill.svg
+
+ H91qKNn+/fvkKDy7IViGxl5Oofw=
+
+ Resources/core/icons/emoji-expressionless.svg
+
+ iN3fkh3o7PuxosSFBhQs2nGWcCA=
+
+ Resources/core/icons/emoji-frown-fill.svg
+
+ 2j2QtLiYpscCEQ07r8I7eu3Ny74=
+
+ Resources/core/icons/emoji-frown.svg
+
+ kvIw65HrDRnGF1NSzCmhmjJ6abM=
+
+ Resources/core/icons/emoji-grimace-fill.svg
+
+ dID6LqiasIgGKdn0ABDBmGOMO28=
+
+ Resources/core/icons/emoji-grimace.svg
+
+ 6A2LJMyWEDLINlhMTwDGJqigeq8=
+
+ Resources/core/icons/emoji-grin-fill.svg
+
+ LwKX/GfmzGIz8kMxycBaklOu6Lo=
+
+ Resources/core/icons/emoji-grin.svg
+
+ DLXlIkQyqFgNMeBH5Xk0x6PPr00=
+
+ Resources/core/icons/emoji-heart-eyes-fill.svg
+
+ crPp8yiYDQFlPd9/vKXcp5iQfEE=
+
+ Resources/core/icons/emoji-heart-eyes.svg
+
+ t5dSaN7Zs+jP4txtsbAAj5LHruw=
+
+ Resources/core/icons/emoji-kiss-fill.svg
+
+ evaUa0qRuo5IHDha3ej46ATlw2w=
+
+ Resources/core/icons/emoji-kiss.svg
+
+ bgaDAVUSCzjI8k/Uj7yTK1pyR7M=
+
+ Resources/core/icons/emoji-laughing-fill.svg
+
+ w1t7Pxt2H+ijRX33ESPO+V8hUNw=
+
+ Resources/core/icons/emoji-laughing.svg
+
+ c/C/kYuTyTyM/lhF3fTKNpRrKdQ=
+
+ Resources/core/icons/emoji-neutral-fill.svg
+
+ KcgI/gJd6H0PRIhA41vtCBm7nLc=
+
+ Resources/core/icons/emoji-neutral.svg
+
+ tfKWTTd6pLa9zQ2sbo1EVlxDXmo=
+
+ Resources/core/icons/emoji-smile-fill.svg
+
+ KmvFY5n8+T5I7Er9/D5bmClC/AA=
+
+ Resources/core/icons/emoji-smile-upside-down-fill.svg
+
+ p2aFWpVqLC5Br4dFwW0X7y+OUbg=
+
+ Resources/core/icons/emoji-smile-upside-down.svg
+
+ UNpy3+tfQ/DUwCIKswYECxWd79U=
+
+ Resources/core/icons/emoji-smile.svg
+
+ WW/0owNi+1cx4hdsFxUDPqv7thY=
+
+ Resources/core/icons/emoji-sunglasses-fill.svg
+
+ a04denLA6THtoR1STZcdrrgnv28=
+
+ Resources/core/icons/emoji-sunglasses.svg
+
+ HlYrVQ9+B4kJ/Zx4kKGW/ATDbgg=
+
+ Resources/core/icons/emoji-surprise-fill.svg
+
+ 39hZAryjXtpHLKaYUEScOI5Jr2s=
+
+ Resources/core/icons/emoji-surprise.svg
+
+ EbGpz7h1RXgekckRY8J62gKOYLs=
+
+ Resources/core/icons/emoji-tear-fill.svg
+
+ H5J4L0Hq1x87nFZGdZXwr9iv+Bc=
+
+ Resources/core/icons/emoji-tear.svg
+
+ KLSuo5ZeOCXo5pVABBbUGxMKe84=
+
+ Resources/core/icons/emoji-wink-fill.svg
+
+ 8fNda7MREnBpSbnypiMrrRVDmtY=
+
+ Resources/core/icons/emoji-wink.svg
+
+ JvjAHtXw0spJc4SJgeDWBABL6tM=
+
+ Resources/core/icons/envelope-arrow-down-fill.svg
+
+ Yj/xVhIzyaJYOyRCLrclhfronrA=
+
+ Resources/core/icons/envelope-arrow-down.svg
+
+ hyJCVtQMi+07c5LAL7fFsVsyKAw=
+
+ Resources/core/icons/envelope-arrow-up-fill.svg
+
+ MbaOxteAwfejwUbJJ7RjE6lwJGE=
+
+ Resources/core/icons/envelope-arrow-up.svg
+
+ OI+ILflBM6lu6BxIG5IcK3KcmUU=
+
+ Resources/core/icons/envelope-at-fill.svg
+
+ +Qb+21yskYJNT28JwuXDvPGx8s0=
+
+ Resources/core/icons/envelope-at.svg
+
+ jRB7QbUUOVHAawOUzLAPtBio8GI=
+
+ Resources/core/icons/envelope-check-fill.svg
+
+ lb9R+3ROGOrsK4cLqM6k9Q7IJh8=
+
+ Resources/core/icons/envelope-check.svg
+
+ 1b8kKN2RaUvFpf3gaEarm6PGDk8=
+
+ Resources/core/icons/envelope-dash-fill.svg
+
+ +w0b65+z2BE5PQmx+UDgXWB22ek=
+
+ Resources/core/icons/envelope-dash.svg
+
+ oxyRfJ/a+0K0dRj8jXdFsMZCen0=
+
+ Resources/core/icons/envelope-exclamation-fill.svg
+
+ /gENKjRmcEpQuPoOVlq73Te2DKM=
+
+ Resources/core/icons/envelope-exclamation.svg
+
+ wSohAPnzhBmJIrtylnIVP73zieQ=
+
+ Resources/core/icons/envelope-fill.svg
+
+ Dwn/w1LsNIH6q8sSb2Sth1aFoWI=
+
+ Resources/core/icons/envelope-heart-fill.svg
+
+ kxGlMmI9RnYLsUT3OEMDiOjbwks=
+
+ Resources/core/icons/envelope-heart.svg
+
+ A69zD7q9I2MuFDqxYe0dKRpsiw4=
+
+ Resources/core/icons/envelope-open-fill.svg
+
+ HP9QA7AhdAjIwBC7z1VM+uQvS1I=
+
+ Resources/core/icons/envelope-open-heart-fill.svg
+
+ qUGcm5jrqXV/ds5xe0PXUgoHU9k=
+
+ Resources/core/icons/envelope-open-heart.svg
+
+ J2aJuhNO7I1vToJJkUAXjWJR08A=
+
+ Resources/core/icons/envelope-open.svg
+
+ +rxaD3g6KfgUO5Ni1meelOVwa60=
+
+ Resources/core/icons/envelope-paper-fill.svg
+
+ iSusQFiUQETyVaoBE7tLZZSRV8Y=
+
+ Resources/core/icons/envelope-paper-heart-fill.svg
+
+ 3kA0gz790T3ZBmQIN/SgiMDUyx4=
+
+ Resources/core/icons/envelope-paper-heart.svg
+
+ uloJckqDyVyQRHe/zw4kOxFISSg=
+
+ Resources/core/icons/envelope-paper.svg
+
+ zRIumPDwfM1iRXfi+WSLM1f29pE=
+
+ Resources/core/icons/envelope-plus-fill.svg
+
+ U4yV5w37qvRKziboV1hOgAx+IQI=
+
+ Resources/core/icons/envelope-plus.svg
+
+ aXSzaPnj09wpzSn87Mb+uxKmlKM=
+
+ Resources/core/icons/envelope-slash-fill.svg
+
+ 0md7Zpoig3+4oydC+oNMqS5fWbI=
+
+ Resources/core/icons/envelope-slash.svg
+
+ 5jmAZwCiPcPAAYxKOAO6XfMqgxw=
+
+ Resources/core/icons/envelope-x-fill.svg
+
+ OkftbYoVtEFxmut8wf8qR7pTTNw=
+
+ Resources/core/icons/envelope-x.svg
+
+ mzbxETFodxAk0oSmAf+O+bOWFyg=
+
+ Resources/core/icons/envelope.svg
+
+ cJJZp00FMd/tH6hKU9occkPFrsU=
+
+ Resources/core/icons/eraser-fill.svg
+
+ L7jTk/3FYn51E8LiYhUMuAduj9A=
+
+ Resources/core/icons/eraser.svg
+
+ Ypt7uX+Gr1HdzeRYfYdMNy4R3iQ=
+
+ Resources/core/icons/error.svg
+
+ voMrPTl2xlW6ap53cehqkv0h+4c=
+
+ Resources/core/icons/escape.svg
+
+ 8pwT+MUnaP5KalXBnHnr+f93D28=
+
+ Resources/core/icons/ethernet.svg
+
+ 6PDYQqY0AJ4C/zIt64Rb6mBHnfc=
+
+ Resources/core/icons/ev-front-fill.svg
+
+ 3P7QhploeFuG/vr2V+FjupM3s2g=
+
+ Resources/core/icons/ev-front.svg
+
+ VogaVC4ToH/V1Lqi58QiFjfp6PI=
+
+ Resources/core/icons/ev-station-fill.svg
+
+ mUqJyg1hEHmgrtDXJWIZd12tKBk=
+
+ Resources/core/icons/ev-station.svg
+
+ 0wkrJl8UOfh01a13Kgj1TczklyM=
+
+ Resources/core/icons/exclamation-circle-fill.svg
+
+ FB0aFPpoFYbOXWhrOuzhKdOM3rw=
+
+ Resources/core/icons/exclamation-circle.svg
+
+ SBPPvzioSV53Q7n7wBwPcWaEEeU=
+
+ Resources/core/icons/exclamation-diamond-fill.svg
+
+ U2AILFzOrbp2cmM/K0SJ4pyYnZY=
+
+ Resources/core/icons/exclamation-diamond.svg
+
+ Sp8NJp0JjwgM3y5s9dnFq8HNvCA=
+
+ Resources/core/icons/exclamation-lg.svg
+
+ /z2Jw3AixJPbNFSKQVlEPbdTlLE=
+
+ Resources/core/icons/exclamation-octagon-fill.svg
+
+ UWS3QPL/POGCkNyPKTuoOiiJmes=
+
+ Resources/core/icons/exclamation-octagon.svg
+
+ luNiNDV6tR9KIBks5xiz4QhB5Wc=
+
+ Resources/core/icons/exclamation-square-fill.svg
+
+ enY51igAgVoroPvg77/BFwbJXqM=
+
+ Resources/core/icons/exclamation-square.svg
+
+ VxD8GdaWGl9AWaCMF+B0Pd7zbEU=
+
+ Resources/core/icons/exclamation-triangle-fill.svg
+
+ 4abBK//KyEzK6IHEE/1Ryy7Pv7U=
+
+ Resources/core/icons/exclamation-triangle.svg
+
+ 6HQQdShpxA1vUa8X/vg/Fwuu+lw=
+
+ Resources/core/icons/exclamation.svg
+
+ F7ja9XjglUf7dpW4+P8XMVL2fhU=
+
+ Resources/core/icons/exclude.svg
+
+ Fk+zmcPnYxCYTc8rYCIgpE4YlEI=
+
+ Resources/core/icons/explicit-fill.svg
+
+ lyVrBHnPWIOJXPQaLIGU5lRqGDw=
+
+ Resources/core/icons/explicit.svg
+
+ TdykmcW1Z1/JfcfUL1m4XSZj070=
+
+ Resources/core/icons/exposure.svg
+
+ FSesU6h1bO4HA0qB6z4lmOzpOTI=
+
+ Resources/core/icons/eye-fill.svg
+
+ 6yfSh/G1NXGzJ4qtb0P2NwrG91I=
+
+ Resources/core/icons/eye-slash-fill.svg
+
+ WAodlBWZDboxaYxrI2cpRJE0YJ4=
+
+ Resources/core/icons/eye-slash.svg
+
+ bPo+Pd+LnhMXm/HdksTVmsklJuI=
+
+ Resources/core/icons/eye.svg
+
+ dyBm96/2O+0sa00kqH0H25m6K60=
+
+ Resources/core/icons/eyedropper.svg
+
+ fp7s6tP0IKktLtb+p2sYoBvfOw0=
+
+ Resources/core/icons/eyeglasses.svg
+
+ AjYBtq4MLjdMwYPOmLxAi8f/bLg=
+
+ Resources/core/icons/facebook.svg
+
+ 9Mhey2eyjsKupsPa+OQCnNFhwwc=
+
+ Resources/core/icons/fan.svg
+
+ +kVsNGfleZ0nQsG0i1bswfGJT8c=
+
+ Resources/core/icons/fast-forward-btn-fill.svg
+
+ 3SN/2Lm6+tfNOxEl8F8i/tFj7qs=
+
+ Resources/core/icons/fast-forward-btn.svg
+
+ Rjr7WGLENonsdLfDzBIsj5s7xHU=
+
+ Resources/core/icons/fast-forward-circle-fill.svg
+
+ aOHx6TsPzhFWrwR0savEuFugFfI=
+
+ Resources/core/icons/fast-forward-circle.svg
+
+ m+FUsk5Gk48FrCB0JLoI305Xs1s=
+
+ Resources/core/icons/fast-forward-fill.svg
+
+ kt8A/EJpJs6YXu6/fXWGeiE7H3g=
+
+ Resources/core/icons/fast-forward.svg
+
+ e0mcV7GgZifrEkIvB43OGNEon2Q=
+
+ Resources/core/icons/feather.svg
+
+ a6+HQC3MQ88qNpvGoIrSjrbiXeY=
+
+ Resources/core/icons/feather2.svg
+
+ sP1AElYoiwHz1DqQ3+eRpbP31+s=
+
+ Resources/core/icons/file-arrow-down-fill.svg
+
+ ydj3ihr5G4sgEiGjtHWCSk8jkGk=
+
+ Resources/core/icons/file-arrow-down.svg
+
+ /7rARB+HEOTjTLweIGtdawlWcX0=
+
+ Resources/core/icons/file-arrow-up-fill.svg
+
+ dIbg+n4SljUlqZl0QHnMERb5DNU=
+
+ Resources/core/icons/file-arrow-up.svg
+
+ pByYmGYWWSaPZxyOnf3mimy4LSw=
+
+ Resources/core/icons/file-bar-graph-fill.svg
+
+ A+VEQRLG/Pc2Z+d//02Z0Rfn2a4=
+
+ Resources/core/icons/file-bar-graph.svg
+
+ mGTho8JzB/u0SoWPfQbwblRCAL0=
+
+ Resources/core/icons/file-binary-fill.svg
+
+ b/ZiMIfOyR7Ys0q62Vt11xlQSrY=
+
+ Resources/core/icons/file-binary.svg
+
+ IR9jeEZZouTBQDqUZgV9S0usiHY=
+
+ Resources/core/icons/file-break-fill.svg
+
+ MksX/KMCVGcOHnxz2te3r4nRn4Q=
+
+ Resources/core/icons/file-break.svg
+
+ xxBRSskUEGajTB6rkow2u/Y+Og4=
+
+ Resources/core/icons/file-check-fill.svg
+
+ zDVU/gR6y+scdDk4ujzjDTtuv50=
+
+ Resources/core/icons/file-check.svg
+
+ e9IK0oTeWtuDDCN536oGAOWuOUA=
+
+ Resources/core/icons/file-code-fill.svg
+
+ QKKnvZAbsiH8gEg1GC+hsucReCA=
+
+ Resources/core/icons/file-code.svg
+
+ aVmV9zCI7IpN/JgWnGe+JlS2dxE=
+
+ Resources/core/icons/file-diff-fill.svg
+
+ JOgtPsoB8SVfx3kzf3rj4D+4VCc=
+
+ Resources/core/icons/file-diff.svg
+
+ NKxvcUZB4NyrHn9vkMEcmT7VRn4=
+
+ Resources/core/icons/file-earmark-arrow-down-fill.svg
+
+ kSgLasVoVnMaO5vP5QjEkTX8dd8=
+
+ Resources/core/icons/file-earmark-arrow-down.svg
+
+ lU0AaBBm0kmA7lmqIFk7Iews3G4=
+
+ Resources/core/icons/file-earmark-arrow-up-fill.svg
+
+ 3LFcIJ369LAmD6hyuORQYnLCOhQ=
+
+ Resources/core/icons/file-earmark-arrow-up.svg
+
+ fO/xFfvGaaQ0/xWRUMWomP+fHQk=
+
+ Resources/core/icons/file-earmark-bar-graph-fill.svg
+
+ RxUU7ReNo6plXl6iIjaeFVSK/bM=
+
+ Resources/core/icons/file-earmark-bar-graph.svg
+
+ hgrcG/robrA5wb57KRlGOvQ5bZQ=
+
+ Resources/core/icons/file-earmark-binary-fill.svg
+
+ iKtHrUktGVVdOtxDv7xwB4GHfZk=
+
+ Resources/core/icons/file-earmark-binary.svg
+
+ BKnCd+hR9lRaEEeEruCw+1xhFcA=
+
+ Resources/core/icons/file-earmark-break-fill.svg
+
+ g5klBMBrjBFSYco64uZPgrnBBIs=
+
+ Resources/core/icons/file-earmark-break.svg
+
+ MlZG4aAUIlokHE59a2qi7wbVGuI=
+
+ Resources/core/icons/file-earmark-check-fill.svg
+
+ o9fhbmv0oJUv8/T/shvon/gQYTY=
+
+ Resources/core/icons/file-earmark-check.svg
+
+ VaeWGUbDh2MsiiOSdPHtSIAktFo=
+
+ Resources/core/icons/file-earmark-code-fill.svg
+
+ Naox1vC8GGMgJwI2GeHHp3f0Sqg=
+
+ Resources/core/icons/file-earmark-code.svg
+
+ jI8XTgCRRMsfzhqryKp1GB+DzaQ=
+
+ Resources/core/icons/file-earmark-diff-fill.svg
+
+ JCdDYooMobgpUALP5BuHhY6+nH4=
+
+ Resources/core/icons/file-earmark-diff.svg
+
+ t95pvVDKXsSMXO3p2rAuvfxZ+JE=
+
+ Resources/core/icons/file-earmark-easel-fill.svg
+
+ BBjN4ZBPvxJUY9BaiEP8yemnreE=
+
+ Resources/core/icons/file-earmark-easel.svg
+
+ n6w/iWapdlJ8QBXV8d6yiQHAyIk=
+
+ Resources/core/icons/file-earmark-excel-fill.svg
+
+ qHhLbMYPbOaisAt04npdu6JrTkY=
+
+ Resources/core/icons/file-earmark-excel.svg
+
+ I02IdaUWhPABuOy4r1X+qQBSm9Y=
+
+ Resources/core/icons/file-earmark-fill.svg
+
+ 0qCl7Y8p9A5tEH6Y7FFunI06ow0=
+
+ Resources/core/icons/file-earmark-font-fill.svg
+
+ 5akW2XLEPyjMGZL5/PbjsdCNEOw=
+
+ Resources/core/icons/file-earmark-font.svg
+
+ coxk9t28plxuClQUQy532F7Gx40=
+
+ Resources/core/icons/file-earmark-image-fill.svg
+
+ oRYYnsySVltnTWqGaYb8wHtk7zM=
+
+ Resources/core/icons/file-earmark-image.svg
+
+ im1NjOct/GJdilYFVogQGCw1jcc=
+
+ Resources/core/icons/file-earmark-lock-fill.svg
+
+ fh8N7cZK08CdcrTmP+MUDQbNVbE=
+
+ Resources/core/icons/file-earmark-lock.svg
+
+ dtLF3mBgp1xc0dM1bBdE36NyJw4=
+
+ Resources/core/icons/file-earmark-lock2-fill.svg
+
+ YTA3CHj3xxKGmPHVnlUBaZx4gDA=
+
+ Resources/core/icons/file-earmark-lock2.svg
+
+ aZj3Pko/geKqesbH3itEm1uCMjI=
+
+ Resources/core/icons/file-earmark-medical-fill.svg
+
+ ZDmjYAgZ6W59GheQumYnoUoHDhY=
+
+ Resources/core/icons/file-earmark-medical.svg
+
+ iiU2BgkoLPePZ7nnAaNqOx0h4k8=
+
+ Resources/core/icons/file-earmark-minus-fill.svg
+
+ LU1zS1EhCYVfIqDXQ6aZ7i82Io0=
+
+ Resources/core/icons/file-earmark-minus.svg
+
+ PQsc4v34F1Cwl1/iz54Nirna27s=
+
+ Resources/core/icons/file-earmark-music-fill.svg
+
+ Xvn+3lWRaEPWlWHZGe8CAcMAVJs=
+
+ Resources/core/icons/file-earmark-music.svg
+
+ stGB8VmTBmqQeJx+Enukydp8IkI=
+
+ Resources/core/icons/file-earmark-pdf-fill.svg
+
+ QZNm50RRC5vk0qcuk0lJFI+m7Hs=
+
+ Resources/core/icons/file-earmark-pdf.svg
+
+ pvI9lV+boK42uYcy5TEr72JFPGs=
+
+ Resources/core/icons/file-earmark-person-fill.svg
+
+ gpj1TtrPkwU6kRm1mhJLUYn7dtA=
+
+ Resources/core/icons/file-earmark-person.svg
+
+ GPYAeEWkH6B5D7Y+qwdSJqeHJGE=
+
+ Resources/core/icons/file-earmark-play-fill.svg
+
+ vk7QeWmqxsUyW/2al8FCx6K4hbs=
+
+ Resources/core/icons/file-earmark-play.svg
+
+ uqjrFfV05NyzW24zTWzpNbOLuRE=
+
+ Resources/core/icons/file-earmark-plus-fill.svg
+
+ tTZKj55Oine7jYV49YU3gGmzZXE=
+
+ Resources/core/icons/file-earmark-plus.svg
+
+ k4TwJ8r1M55PJvboBGBOfbdjIb8=
+
+ Resources/core/icons/file-earmark-post-fill.svg
+
+ 5CAk5Qn+OFnQzydy/eJ8+f4i5x0=
+
+ Resources/core/icons/file-earmark-post.svg
+
+ 01k59IftFkHhu33ZjIheXgrm0PE=
+
+ Resources/core/icons/file-earmark-ppt-fill.svg
+
+ bgtEk0K4KnJd/RbsRxFEmdbvXEo=
+
+ Resources/core/icons/file-earmark-ppt.svg
+
+ 7LP1DaENcpx+QI/+wNOGPh2DmLo=
+
+ Resources/core/icons/file-earmark-richtext-fill.svg
+
+ 8BJ4PNxRpM5ARJjP5niCFQBo0O8=
+
+ Resources/core/icons/file-earmark-richtext.svg
+
+ Mco4QlDoDtUKrZIraq5jSy/xIJA=
+
+ Resources/core/icons/file-earmark-ruled-fill.svg
+
+ 8gjd+T4ZkwhJcpZmYmdMsAoW4pg=
+
+ Resources/core/icons/file-earmark-ruled.svg
+
+ WKcaNHkev4fwysisYPU1u7zatAU=
+
+ Resources/core/icons/file-earmark-slides-fill.svg
+
+ 4Q6QEQAC9WkormZJ9bY43fft+cA=
+
+ Resources/core/icons/file-earmark-slides.svg
+
+ EoZJgke5iDwD4PufOLxA9iC6Xh4=
+
+ Resources/core/icons/file-earmark-spreadsheet-fill.svg
+
+ i3e7k5mvTwCfhxrteyZQa9tm03M=
+
+ Resources/core/icons/file-earmark-spreadsheet.svg
+
+ Kx+x3bLZ7u/ff6bjBoCKUM8ctaU=
+
+ Resources/core/icons/file-earmark-text-fill.svg
+
+ oI0/kvyzMGuy8Pdaf8eevL6RXq0=
+
+ Resources/core/icons/file-earmark-text.svg
+
+ 2ZWEJHOi962ISePCQm3sPDP/LMI=
+
+ Resources/core/icons/file-earmark-word-fill.svg
+
+ gFx5KjD2ZKv4uz988C4U08DS2MM=
+
+ Resources/core/icons/file-earmark-word.svg
+
+ pyfdZDJSWLhCbQOS804ZRNaRw0k=
+
+ Resources/core/icons/file-earmark-x-fill.svg
+
+ WCoyI2LIa7/kxno51kVU7Fp3//0=
+
+ Resources/core/icons/file-earmark-x.svg
+
+ qYfsigtL6wRuDI87jITJbN9/E88=
+
+ Resources/core/icons/file-earmark-zip-fill.svg
+
+ YGfD4IVTUFvkpokkp8daq2yb2nk=
+
+ Resources/core/icons/file-earmark-zip.svg
+
+ xvCh0KdfboVapVZyKZ51LA58aPw=
+
+ Resources/core/icons/file-earmark.svg
+
+ V8N4+gvZMZX9XhqLmEuJwnFEA60=
+
+ Resources/core/icons/file-easel-fill.svg
+
+ YfW6LTmlnb7dLd9FdXMUc/FSlwA=
+
+ Resources/core/icons/file-easel.svg
+
+ gGQbb/aHuxt0Cuo7TEqHp3MmxKc=
+
+ Resources/core/icons/file-excel-fill.svg
+
+ BT31yQRb4eUfapOMoM742WbD9Uk=
+
+ Resources/core/icons/file-excel.svg
+
+ xetugRhlZyr5C9wwct8YpQT+xqE=
+
+ Resources/core/icons/file-fill.svg
+
+ YYhQeR4NuJkcGeFi317jcSG15tQ=
+
+ Resources/core/icons/file-font-fill.svg
+
+ hg3BDEPfRMXFgjAXVf/reeEincs=
+
+ Resources/core/icons/file-font.svg
+
+ WWf9YJkkujMPq7+QNI7C+1oPzVI=
+
+ Resources/core/icons/file-image-fill.svg
+
+ kSAE02lmYmqpjb54Ju6dVYcUD68=
+
+ Resources/core/icons/file-image.svg
+
+ ICUoKsyFl53Oth3jpRXGh90NwCI=
+
+ Resources/core/icons/file-lock-fill.svg
+
+ t3Xoe8ZoPnvtAoHOWFznWQacP6M=
+
+ Resources/core/icons/file-lock.svg
+
+ 64UpSzJwM7ufWxKjfXGvj6HmglA=
+
+ Resources/core/icons/file-lock2-fill.svg
+
+ rkm6sqon/RsPSdeQ3zxXzwgPcmk=
+
+ Resources/core/icons/file-lock2.svg
+
+ 8kejkAq6LXwJDPZynnX9V02ls44=
+
+ Resources/core/icons/file-medical-fill.svg
+
+ mh+tzcPrLP3jYjzNZ4LZcZdjzV8=
+
+ Resources/core/icons/file-medical.svg
+
+ 0TmES/w7MlrXhNrEn5uAUSYhQe4=
+
+ Resources/core/icons/file-minus-fill.svg
+
+ 5g9/B+2HB5xoYPbo6XNLoADCXos=
+
+ Resources/core/icons/file-minus.svg
+
+ TykECxQrIzdgrE458CXQavR3Ge8=
+
+ Resources/core/icons/file-music-fill.svg
+
+ u9SjPfg6cG0LE5+SOkoTHy31odA=
+
+ Resources/core/icons/file-music.svg
+
+ u7zawvnYKnlBljxI4sKy+IxPtFo=
+
+ Resources/core/icons/file-pdf-fill.svg
+
+ fhYpBUS/yb2+7tDuMFVyhckBuhI=
+
+ Resources/core/icons/file-pdf.svg
+
+ 1ApJiBTG9hvp/laEiMr6qk/T8kg=
+
+ Resources/core/icons/file-person-fill.svg
+
+ x7Pdhm0GGflwH53D8ueMPAnrVZc=
+
+ Resources/core/icons/file-person.svg
+
+ cAjRYS07VKH7DhL0xJPdGgLn7IE=
+
+ Resources/core/icons/file-play-fill.svg
+
+ LCCdJe0KC40VO4PBx9F/z3YvnL8=
+
+ Resources/core/icons/file-play.svg
+
+ eikpFuELTr6lWHaiUAY3ZJ4XCUg=
+
+ Resources/core/icons/file-plus-fill.svg
+
+ K4F7y4tlyt0BayJh/m63mI03+ko=
+
+ Resources/core/icons/file-plus.svg
+
+ 9fOKoJnCr+RMQS8YtY9Uc4+MJ/0=
+
+ Resources/core/icons/file-post-fill.svg
+
+ 5TBbNrXEap7X5JI2I5+r+HIp6wQ=
+
+ Resources/core/icons/file-post.svg
+
+ BIDpdSn56eEecQSaoxgPsGWUlSI=
+
+ Resources/core/icons/file-ppt-fill.svg
+
+ AhAg4fkLV7LM4uhbjp6gbe5JmaU=
+
+ Resources/core/icons/file-ppt.svg
+
+ jHIy8kr2Kbrh9ktdFkRsniyZQhU=
+
+ Resources/core/icons/file-richtext-fill.svg
+
+ /6DI42VLIQB2E3ywzrzLFR4uB+Y=
+
+ Resources/core/icons/file-richtext.svg
+
+ B/Ket8sPyqxl62hs7BkFVi82QRE=
+
+ Resources/core/icons/file-ruled-fill.svg
+
+ 1VQ0W6YiTNU+GECdBuK2P0cA7R0=
+
+ Resources/core/icons/file-ruled.svg
+
+ IYwBp3g8iaQ3GkvAs7QcoRTYQBw=
+
+ Resources/core/icons/file-slides-fill.svg
+
+ TGSX/PRxhQZk5BhgufkbBjmHw0o=
+
+ Resources/core/icons/file-slides.svg
+
+ HdGrT/MR3um1mhXOIbesEPWF4ew=
+
+ Resources/core/icons/file-spreadsheet-fill.svg
+
+ MlwECx3xWDGf2svGP3phOrZCccc=
+
+ Resources/core/icons/file-spreadsheet.svg
+
+ GgvFHIqrX34RCsfNWC3IS1arvkk=
+
+ Resources/core/icons/file-text-fill.svg
+
+ 302/AEauRp1Qf/cD65kpaOAl7O0=
+
+ Resources/core/icons/file-text.svg
+
+ KRxXMEbzIFi89L8JJcrx4ocj7dw=
+
+ Resources/core/icons/file-word-fill.svg
+
+ q0vm5/ilpvHo6ZHJPCic7IP+iZ4=
+
+ Resources/core/icons/file-word.svg
+
+ KRWiIJHellC6Gc+2+gUV4GwJFWA=
+
+ Resources/core/icons/file-x-fill.svg
+
+ 9b6SO3xRBbLQTA1R9Y6NxeKwG/Q=
+
+ Resources/core/icons/file-x.svg
+
+ Ah9lQ6X925wEgimls/GZpg9etC4=
+
+ Resources/core/icons/file-zip-fill.svg
+
+ EtcLFYc+5bneLsSAPucfXI+Gs00=
+
+ Resources/core/icons/file-zip.svg
+
+ RAPwERDtB6eO86M8Fp0mrjmErJ4=
+
+ Resources/core/icons/file.svg
+
+ 9x1mZ6znu56ElNLxGWS1gHgNMQc=
+
+ Resources/core/icons/files-alt.svg
+
+ EtOOV7jmLwUtEmf35PONrcuT8Kg=
+
+ Resources/core/icons/files.svg
+
+ 0aBXDz96RJkRRtcoU9L8m19OEZ4=
+
+ Resources/core/icons/filetype-aac.svg
+
+ AYIuxQ+JssAujG3R/XAHViYO9qo=
+
+ Resources/core/icons/filetype-ai.svg
+
+ DjRcqSHjZtvMvgqj0RpcsD6HL8g=
+
+ Resources/core/icons/filetype-bmp.svg
+
+ XgGCJxcHuH3D/xc1aGCIEzltGTU=
+
+ Resources/core/icons/filetype-cs.svg
+
+ Jmen/YdH77SDmoTCIYb/IH5v+CA=
+
+ Resources/core/icons/filetype-css.svg
+
+ dMkCrS5d/KOv0aapW+SXVqPmqfs=
+
+ Resources/core/icons/filetype-csv.svg
+
+ k9CK/oupW+VPgsbpxoJh8CuFmc8=
+
+ Resources/core/icons/filetype-doc.svg
+
+ mMFVvylRgBRfxs2wbYzf0vuQMj0=
+
+ Resources/core/icons/filetype-docx.svg
+
+ DNQCt4HOgmtrrJHddL27Y6KDw20=
+
+ Resources/core/icons/filetype-exe.svg
+
+ B4QuwKd2whIALZyDVlvie7oBH0w=
+
+ Resources/core/icons/filetype-gif.svg
+
+ bpQtV3iF0rVvAe/y+lKXmzkXIdU=
+
+ Resources/core/icons/filetype-heic.svg
+
+ TJcl416ep8N2g2OeQ2UGs3XphLo=
+
+ Resources/core/icons/filetype-html.svg
+
+ HtnyKjaAOAO7Ncf+FjejaUA9DlI=
+
+ Resources/core/icons/filetype-java.svg
+
+ fPcZ+sPs6D2EM+k/UxdN+Xrkvf4=
+
+ Resources/core/icons/filetype-jpg.svg
+
+ wr3Q7ec/f8h8tJ5QpoL0kY8oGRs=
+
+ Resources/core/icons/filetype-js.svg
+
+ 0UWhUSZlGYciNDfbY756RLqRfxg=
+
+ Resources/core/icons/filetype-json.svg
+
+ zgij4uKQRm/hbJCj7fpyiA8s5jo=
+
+ Resources/core/icons/filetype-jsx.svg
+
+ zRuJeRVejCxanTc8LBQ+jGHSslQ=
+
+ Resources/core/icons/filetype-key.svg
+
+ 3oVclA8uwEniGHfnyCePgx5DbXM=
+
+ Resources/core/icons/filetype-m4p.svg
+
+ JclbxXjW2evKUEECHOEuYbPgN4A=
+
+ Resources/core/icons/filetype-md.svg
+
+ XZzTJnXrEkORwpKDvs7qS87IS8I=
+
+ Resources/core/icons/filetype-mdx.svg
+
+ vPY+V1Vw3fmAhto3voUSNg2VU6Q=
+
+ Resources/core/icons/filetype-mov.svg
+
+ +3Bsdhb9ICD3Jy193X0OGsYTVVg=
+
+ Resources/core/icons/filetype-mp3.svg
+
+ 4AmFtPYatShfnE49zIE5pQHMgOQ=
+
+ Resources/core/icons/filetype-mp4.svg
+
+ WqJ4UzHxFkji6itxVT7FclB1ecE=
+
+ Resources/core/icons/filetype-otf.svg
+
+ yaKRWCS9YI7X02Hy6Sf51t4Syks=
+
+ Resources/core/icons/filetype-pdf.svg
+
+ TMuUIXqiPnTnpIqMbnWGtUqkqLw=
+
+ Resources/core/icons/filetype-php.svg
+
+ Z5LdZQh4b9Wa5rKA4DCWgNuKphc=
+
+ Resources/core/icons/filetype-png.svg
+
+ NRFx4rmk5ZYN3bTXZmebW94QynQ=
+
+ Resources/core/icons/filetype-ppt.svg
+
+ KzJl26k0mdViH56rijcyZpNx/7U=
+
+ Resources/core/icons/filetype-pptx.svg
+
+ FwSH4D7ZRBM0LNcALMsDsgKR6kI=
+
+ Resources/core/icons/filetype-psd.svg
+
+ 3ghhT86ksTHB8fUY+oAgpymwQz0=
+
+ Resources/core/icons/filetype-py.svg
+
+ A/YYUcAXdIJcG+eHBvEsp9sAAHA=
+
+ Resources/core/icons/filetype-raw.svg
+
+ iGtO2+cS5eOwiuFqKGFV/LHAFL0=
+
+ Resources/core/icons/filetype-rb.svg
+
+ LFay7PcP7ZNgYmhHwf9SSXQ8JT0=
+
+ Resources/core/icons/filetype-sass.svg
+
+ 3FVojLT1ae+i0BV0g2LZu+t115w=
+
+ Resources/core/icons/filetype-scss.svg
+
+ UYWbVmobGR5qSvs1mmvF1kLJhSk=
+
+ Resources/core/icons/filetype-sh.svg
+
+ COsKnsIa9S1FwTTlE7dBQh0Kbvs=
+
+ Resources/core/icons/filetype-sql.svg
+
+ JEHq2vvRVf5PtMGjSssz4r9Uivw=
+
+ Resources/core/icons/filetype-svg.svg
+
+ 3GaT5nHyEkDmqw8U3pda90P0CwI=
+
+ Resources/core/icons/filetype-tiff.svg
+
+ ZvYqJxzZyqstjvxKovaLxncHQlw=
+
+ Resources/core/icons/filetype-tsx.svg
+
+ JI9K+t7dnKp9iEUUiPsxjD2x+24=
+
+ Resources/core/icons/filetype-ttf.svg
+
+ 4O09VegioVsiKeBOz/XN4EzydTY=
+
+ Resources/core/icons/filetype-txt.svg
+
+ p0yX+3E3bDX2dqiTdvnuuvHYF4w=
+
+ Resources/core/icons/filetype-wav.svg
+
+ 98Lnad2i7qg6AOeeclzg+vKmdmw=
+
+ Resources/core/icons/filetype-woff.svg
+
+ UPJihLrJ1lgGVVOieG0AyK4Dc0c=
+
+ Resources/core/icons/filetype-xls.svg
+
+ U8hR7X7aKwW27ELr7cAw14bdkws=
+
+ Resources/core/icons/filetype-xlsx.svg
+
+ 7lwaR65CW26IMD7j1nPCnfvf9UQ=
+
+ Resources/core/icons/filetype-xml.svg
+
+ ciXtsmn+IUbogwZozHBJpce1hbs=
+
+ Resources/core/icons/filetype-yml.svg
+
+ NL8+q+gwk+i5RUa56LzLeLqt+jA=
+
+ Resources/core/icons/film.svg
+
+ PDu/JDoYGRc4xSld+P2F+OIKqhs=
+
+ Resources/core/icons/filter-circle-fill.svg
+
+ 9Oi2+uv2MKUB2wIy+23bQ8Ox/Gg=
+
+ Resources/core/icons/filter-circle.svg
+
+ s+1lhYNba79Qp+seFnh/DwWcuIM=
+
+ Resources/core/icons/filter-left.svg
+
+ P0Z6hwJ6X9IOnZBGJ7j+zmq0mUE=
+
+ Resources/core/icons/filter-right.svg
+
+ krtYnZKhaVJPH8EUyta2EpEP654=
+
+ Resources/core/icons/filter-square-fill.svg
+
+ 8Xo2T+MpCUhWwLarup6+3myPhgk=
+
+ Resources/core/icons/filter-square.svg
+
+ cWs275EaCL1Y8XS7OoPI7ehu2Yg=
+
+ Resources/core/icons/filter.svg
+
+ OiFK0YYvtN2AG840zzyorj0j6/U=
+
+ Resources/core/icons/fingerprint.svg
+
+ SxN5EEYglQmvnSv5ZlyaJp2njhk=
+
+ Resources/core/icons/fire.svg
+
+ 7xdLVnjfnC6PTS2ScnFCpusICr8=
+
+ Resources/core/icons/flag-fill.svg
+
+ OSl9RwOtUckl+08k7DBtUqBX0+c=
+
+ Resources/core/icons/flag.svg
+
+ 6iPPq0XO5ZYw9tnhpWKltpFoyLI=
+
+ Resources/core/icons/flask-fill.svg
+
+ 7hwnZK1zAMn6KAJqBZnoZwdPONY=
+
+ Resources/core/icons/flask-florence-fill.svg
+
+ 8f5jrhn2EzFvceNn8WN8QzpXyLk=
+
+ Resources/core/icons/flask-florence.svg
+
+ d4ScDKjVaWNBTtgYAjH9WYp0/r0=
+
+ Resources/core/icons/flask.svg
+
+ HFa53V2MQIfukkukLPVm/mDngDM=
+
+ Resources/core/icons/floppy-fill.svg
+
+ SKl3I+Fj50w+TfHmxIEMFPUcSss=
+
+ Resources/core/icons/floppy.svg
+
+ qOp2cyczAG+ZYdeDcRBM+lPl0Hk=
+
+ Resources/core/icons/floppy2-fill.svg
+
+ P6gIdowoBOUYwz2leUDZQCBiSWA=
+
+ Resources/core/icons/floppy2.svg
+
+ WyRjB0tLQj+0kpAGDMDruvJMlDY=
+
+ Resources/core/icons/flower1.svg
+
+ 4j1yoUtz9KZ/Vsx1N+OaJP70/3k=
+
+ Resources/core/icons/flower2.svg
+
+ dQpNgrjm3lfQv54oJgae5lXsgDc=
+
+ Resources/core/icons/flower3.svg
+
+ ig4QCvO1otZF365LgyYx8rfSoSU=
+
+ Resources/core/icons/folder-check.svg
+
+ IzLG5lWPw8SmdmmHp0bBhT1F9Kk=
+
+ Resources/core/icons/folder-fill.svg
+
+ yVI+wqtB4xESGI47DfVz0KJxEZo=
+
+ Resources/core/icons/folder-minus.svg
+
+ 2aBnppazaszHgkrgTgvBIC4IsJg=
+
+ Resources/core/icons/folder-plus.svg
+
+ 00GP7sFykG3icby3MCQPH+8P/ac=
+
+ Resources/core/icons/folder-symlink-fill.svg
+
+ JZ8O2NKK8appWNiC2qIqV/L+vL4=
+
+ Resources/core/icons/folder-symlink.svg
+
+ F4tykA5fbD1zJ/3T/Fsq93n6Kk4=
+
+ Resources/core/icons/folder-x.svg
+
+ t1Sw3POPL0rfVabI/Ao9PLs2FDk=
+
+ Resources/core/icons/folder.svg
+
+ qWWEnRvoMJ8pQPsnQZZPCttkSuU=
+
+ Resources/core/icons/folder2-open.svg
+
+ 2fIbLalG286JALMRLL3UIfMRFQI=
+
+ Resources/core/icons/folder2.svg
+
+ ksQ6R+9jD2Qyk0Z9FctVNhKY6MI=
+
+ Resources/core/icons/fonts.svg
+
+ M/3G+J+AVmwa6r8BXkh8oDVhXK0=
+
+ Resources/core/icons/fonts/bootstrap-icons.woff
+
+ diAQx5tKBeETH8j7Oxrv8yTYAz8=
+
+ Resources/core/icons/fonts/bootstrap-icons.woff2
+
+ tEH8/5OeJPoJBqN7d4h4i5/du2g=
+
+ Resources/core/icons/fork-knife.svg
+
+ w8Oo5QddcwpOsiADaqLcfP9yusY=
+
+ Resources/core/icons/forward-fill.svg
+
+ Qi6ITVB6U6XRpLnWzVRXXlM9TxA=
+
+ Resources/core/icons/forward.svg
+
+ 2RLaJ1pGhv/ygPrhqC0nCVPFem0=
+
+ Resources/core/icons/front.svg
+
+ 9XiWl8Jz4W2oq+eP8yRpVbWkMlw=
+
+ Resources/core/icons/fuel-pump-diesel-fill.svg
+
+ 7EDmQPHDGtnyJTBLjkgJ6+goQuQ=
+
+ Resources/core/icons/fuel-pump-diesel.svg
+
+ EHFJ/DXCPhfZ+TEJmlxhqBT0ncM=
+
+ Resources/core/icons/fuel-pump-fill.svg
+
+ lMpkYiCt6sM8MrGczN3PrUoylfw=
+
+ Resources/core/icons/fuel-pump.svg
+
+ LcvSwXc/rgX2OHa9YSsjlgx9svo=
+
+ Resources/core/icons/fullscreen-exit.svg
+
+ 12H2P+3P57/BX23RUFLPKqKtoSA=
+
+ Resources/core/icons/fullscreen.svg
+
+ GUb+qjck7NN7cO1tjRwkovfDl6k=
+
+ Resources/core/icons/funnel-fill.svg
+
+ 3ulztOmJjN2JqaxT5q5Xhu0XuH4=
+
+ Resources/core/icons/funnel.svg
+
+ XtHzrELgB/4rqj6ca2yf5qp0180=
+
+ Resources/core/icons/gear-fill.svg
+
+ cfQN74ac4X/TtXyQQ0ZRymONjLs=
+
+ Resources/core/icons/gear-wide-connected.svg
+
+ Of20HusDw4BaYJLePSKnLv+ZVSw=
+
+ Resources/core/icons/gear-wide.svg
+
+ OzFv0Ibb2MgJfcG6UBhTuc/gAkA=
+
+ Resources/core/icons/gear.svg
+
+ oZ8vZOBA8E39oI6I2nSGKwZQhtM=
+
+ Resources/core/icons/gem.svg
+
+ 1oM7MnVa1rdbkXJ2vFcRDQJtwco=
+
+ Resources/core/icons/gender-ambiguous.svg
+
+ eTaA7o1RPbARKQbTjc7JEZmdPXA=
+
+ Resources/core/icons/gender-female.svg
+
+ juTv1Wf2Ef18mHU/aEPKpvRIJpM=
+
+ Resources/core/icons/gender-male.svg
+
+ DWZFLP6Lzd4aqTW899Dbnmis8dI=
+
+ Resources/core/icons/gender-neuter.svg
+
+ bXhMXwdSMYdRczAbvwqGmK1sAEA=
+
+ Resources/core/icons/gender-trans.svg
+
+ HeJNQx01Sg3KVw8uvQ183GvGm9M=
+
+ Resources/core/icons/geo-alt-fill.svg
+
+ hOCEuFdYmdFXrguM+Hc8F4IaT3I=
+
+ Resources/core/icons/geo-alt.svg
+
+ xQRdJWOb4qPoSgVWdIP5ONHgvWk=
+
+ Resources/core/icons/geo-fill.svg
+
+ XkdnLivH0jPcMJM5D6JAMHfwcW4=
+
+ Resources/core/icons/geo.svg
+
+ ThDKeZIplyLzFaM0UYpjecKQZ74=
+
+ Resources/core/icons/gift-fill.svg
+
+ J4AggNfQnOwBdnjoor5Pf7qZpos=
+
+ Resources/core/icons/gift.svg
+
+ uteVJmSomWJXDNHf1y5FwS6DuL0=
+
+ Resources/core/icons/git.svg
+
+ We4hqUn+/JSh5LjBw2cuNZ+ghcQ=
+
+ Resources/core/icons/github.svg
+
+ urUkkucd2vV7s/KfGPckU6azWtY=
+
+ Resources/core/icons/gitlab.svg
+
+ rT2GUnwlW7WQ7UJa/2Y/hQuA0SI=
+
+ Resources/core/icons/globe-americas-fill.svg
+
+ GLcqLZ81BvOSUPBPqUL9GH2Hbp4=
+
+ Resources/core/icons/globe-americas.svg
+
+ IfFS7PBM10wkBpTvKg75Jx4ohM8=
+
+ Resources/core/icons/globe-asia-australia-fill.svg
+
+ GS+UqedtFj1Q0TGPREz9ysiwKZ0=
+
+ Resources/core/icons/globe-asia-australia.svg
+
+ yyzV/4BT0KFwqJzFD4xJLaVZvMc=
+
+ Resources/core/icons/globe-central-south-asia-fill.svg
+
+ gEEYx0Bb/Zzgm6agh0LEIkncmfI=
+
+ Resources/core/icons/globe-central-south-asia.svg
+
+ XbCq6Z3VYMBZTNku9ci2E3uUgrE=
+
+ Resources/core/icons/globe-europe-africa-fill.svg
+
+ ysqHNBpAm516++UB+dJhT2WWeno=
+
+ Resources/core/icons/globe-europe-africa.svg
+
+ GjH/qUMtS7SqalRSPOB1UtrAlSU=
+
+ Resources/core/icons/globe.svg
+
+ 0tzTRmNMmf4p7swpLnOM7sTRjeo=
+
+ Resources/core/icons/globe2.svg
+
+ SguxVyqhdndXmZs8GooelHwcKW4=
+
+ Resources/core/icons/google-play.svg
+
+ ykPMTmgAeJksnepbjqC8Obp9oyM=
+
+ Resources/core/icons/google.svg
+
+ 3zn8ptQp0WKdqXvT+S25kBhKHsY=
+
+ Resources/core/icons/gpu-card.svg
+
+ CraVoVr4GyMkXTHOjyGhPraBcD4=
+
+ Resources/core/icons/graph-down-arrow.svg
+
+ CChCiQoe3poo2g/6KENvhURHCwM=
+
+ Resources/core/icons/graph-down.svg
+
+ g5I9bPgRUMVJBgUgbCF+rjk23Ig=
+
+ Resources/core/icons/graph-up-arrow.svg
+
+ O8q17I8xoll5HQ2zJd6SfcKyscQ=
+
+ Resources/core/icons/graph-up.svg
+
+ QueLGA/mQsMz0LqWKQV2hvXKUGg=
+
+ Resources/core/icons/grid-1x2-fill.svg
+
+ D5tWfA7Yjvs+9lUMtz1yD0oud5w=
+
+ Resources/core/icons/grid-1x2.svg
+
+ DhYSTYDOMvfYVKeW0yojOQ1c+zk=
+
+ Resources/core/icons/grid-3x2-gap-fill.svg
+
+ 62dqm3NXEzVebVAMxp+S8bzE3DM=
+
+ Resources/core/icons/grid-3x2-gap.svg
+
+ XwHHHe3q9Ur+Z14t6p3pwSgeDes=
+
+ Resources/core/icons/grid-3x2.svg
+
+ EV5CWavTAr2XFJMA379T2DPT+Hw=
+
+ Resources/core/icons/grid-3x3-gap-fill.svg
+
+ NxkhNcq0Eh3l64uyyVMtlg0fkB4=
+
+ Resources/core/icons/grid-3x3-gap.svg
+
+ I+/6YVWBJJNfY/lWKmTKkMQ0bAw=
+
+ Resources/core/icons/grid-3x3.svg
+
+ PhUyP+DuYrYUbZZERAouw/vSxq4=
+
+ Resources/core/icons/grid-fill.svg
+
+ jJ+4KHGAZupVwpPce35RAmq3nEQ=
+
+ Resources/core/icons/grid.svg
+
+ 5wKez8UEdNNEXwtZLG8NuHD1skI=
+
+ Resources/core/icons/grip-horizontal.svg
+
+ bSROEaTCwjXelPL0nVcyOR9Pt2E=
+
+ Resources/core/icons/grip-vertical.svg
+
+ Sgy8ImzeOAeRBzYs1QSPwpYdDSs=
+
+ Resources/core/icons/h-circle-fill.svg
+
+ IzG5dQuZe5GSDHVzQFGTi177EMw=
+
+ Resources/core/icons/h-circle.svg
+
+ K1dz1Hv3fmJjTASTSg0aUiuUPrg=
+
+ Resources/core/icons/h-square-fill.svg
+
+ E3BvcyeB/jys4E5m2yRxE2RVT0Q=
+
+ Resources/core/icons/h-square.svg
+
+ zkAmjoLj60tkrzUjAWsnPAV4TgU=
+
+ Resources/core/icons/hammer.svg
+
+ Ikf0MjfZg1IdzqrH5cCSJZ+wHOg=
+
+ Resources/core/icons/hand-index-fill.svg
+
+ 2EwhblSqWCFtoyUr24MzLV8DkGk=
+
+ Resources/core/icons/hand-index-thumb-fill.svg
+
+ glzi33qSDxnJYwxwL+hAq/0ALP8=
+
+ Resources/core/icons/hand-index-thumb.svg
+
+ rlWfzTPqfPjAVG9zyCNcqMC56ks=
+
+ Resources/core/icons/hand-index.svg
+
+ DytWgT4svOTvHnM5L8QUj9x+6PI=
+
+ Resources/core/icons/hand-thumbs-down-fill.svg
+
+ C9f3TU8eybIF0qOG7eFGVjgXPqw=
+
+ Resources/core/icons/hand-thumbs-down.svg
+
+ H7c2KbCSolQSjvhdJ+A6O1x9VAY=
+
+ Resources/core/icons/hand-thumbs-up-fill.svg
+
+ iGjEz7zzlwOQXPEXilOBYhWlVUw=
+
+ Resources/core/icons/hand-thumbs-up.svg
+
+ lV45DRgSIo0yvG2XITNfYefRbb0=
+
+ Resources/core/icons/handbag-fill.svg
+
+ DxpAhC+TfSgjk1SWvqMYYF3r69A=
+
+ Resources/core/icons/handbag.svg
+
+ tqxnFdyu7YLxsuHhTWNVr/HfnTs=
+
+ Resources/core/icons/hash.svg
+
+ cV2BX8FvxAFf0FJkye1Ji87opAk=
+
+ Resources/core/icons/hdd-fill.svg
+
+ aynp2eP6IF/bLSqxmOix2s4wLzI=
+
+ Resources/core/icons/hdd-network-fill.svg
+
+ cgW9hbq5C6D0YLdiBWC2Uh8KK8c=
+
+ Resources/core/icons/hdd-network.svg
+
+ ev4oPy19AR5eVkks+fFioSou4eA=
+
+ Resources/core/icons/hdd-rack-fill.svg
+
+ 2JNkj+y+fRf5QAWnK/uDZgXoKgY=
+
+ Resources/core/icons/hdd-rack.svg
+
+ lj0C5GE3AaCIe9m0g5yNc40dDoo=
+
+ Resources/core/icons/hdd-stack-fill.svg
+
+ skDBy//RRD7hmZoKB/Piw3IxAwI=
+
+ Resources/core/icons/hdd-stack.svg
+
+ oSAlfZDNx6vhwuZLOktheOU8zXc=
+
+ Resources/core/icons/hdd.svg
+
+ y3kK4EmZ9lJH/vbXmJFmIONLhiI=
+
+ Resources/core/icons/hdmi-fill.svg
+
+ zChhM3q67B3DXXRDU/M1vSi6ub0=
+
+ Resources/core/icons/hdmi.svg
+
+ A+rh2LY5GBy/LkGGeoNp4ila4fA=
+
+ Resources/core/icons/headphones.svg
+
+ lG7uC0HtZQr7SPHlJun6HQvJhr8=
+
+ Resources/core/icons/headset-vr.svg
+
+ 8r6/Qu2As8z3N+QqVXxDE7bW6Gw=
+
+ Resources/core/icons/headset.svg
+
+ ORCJrrsP3TdyMGdtEeiOZoh7VLs=
+
+ Resources/core/icons/heart-arrow.svg
+
+ 0NZ4m/qQy2uXWug4dPm556j2kL8=
+
+ Resources/core/icons/heart-fill.svg
+
+ bzaP6ZaG6+GnuYTsChHa9YiMk04=
+
+ Resources/core/icons/heart-half.svg
+
+ yuUtr+1+rpmCq0KArkZdxdaYvZ8=
+
+ Resources/core/icons/heart-pulse-fill.svg
+
+ TD6FruXNjBXKxmcXPHcrMlbm6Uc=
+
+ Resources/core/icons/heart-pulse.svg
+
+ kDI2q/eAZWjoEtJkwjtRifwaYbw=
+
+ Resources/core/icons/heart.svg
+
+ mNdlzYmq5B4/Xu3KcSM2+u7koSs=
+
+ Resources/core/icons/heartbreak-fill.svg
+
+ 6eXz9nlmibh9HD2AHPYyoOrubmo=
+
+ Resources/core/icons/heartbreak.svg
+
+ t1XNkBoVco5ET3t7Z7LK6vsmdYk=
+
+ Resources/core/icons/hearts.svg
+
+ Pycn7OJlNNegGpUWuAR/LtWyDDY=
+
+ Resources/core/icons/heptagon-fill.svg
+
+ Ihtbage/OEmDOwq2r8bqjkOH6gg=
+
+ Resources/core/icons/heptagon-half.svg
+
+ 69xjAmMO8O5o/ttMHNdHn/l/zNs=
+
+ Resources/core/icons/heptagon.svg
+
+ 4eh9sCQKmqMjCIeE+25ROmZORSM=
+
+ Resources/core/icons/hexagon-fill.svg
+
+ 9RVyF86I8Wd8Vr7GACU+8kn0ZjE=
+
+ Resources/core/icons/hexagon-half.svg
+
+ zR6DnIqn4BaF2lxAKChg8zUK8iM=
+
+ Resources/core/icons/hexagon.svg
+
+ VrUQ9+Nbe10J6sJAw+iuG8dYbNA=
+
+ Resources/core/icons/highlighter.svg
+
+ B2O2wZoxpMn1GrqnXnMzu/3hkOw=
+
+ Resources/core/icons/highlights.svg
+
+ Uz+CQ23w1eyaqY5StK7ySpLgYUU=
+
+ Resources/core/icons/hospital-fill.svg
+
+ mQudwXn5rXCjn7G5kANrPix98xY=
+
+ Resources/core/icons/hospital.svg
+
+ urQK+sIRN1PIhBnLlbmAm7exdQQ=
+
+ Resources/core/icons/hourglass-bottom.svg
+
+ 73Y1Cw6+pgcj9Sem+E3ieEg4b4I=
+
+ Resources/core/icons/hourglass-split.svg
+
+ TJkTQ1jV/FRYgp8tQYK0hIcOq/E=
+
+ Resources/core/icons/hourglass-top.svg
+
+ EjFVWFMb8u7ZFGlQgZe+TvvDamk=
+
+ Resources/core/icons/hourglass.svg
+
+ 1DAf59e/cdKZ09cSeEFJlJytWdU=
+
+ Resources/core/icons/house-add-fill.svg
+
+ 9iTf5qKyp+VAu1J4OP5OeNUvhUI=
+
+ Resources/core/icons/house-add.svg
+
+ P8lMA7NPO7cZbuaKzwROYGCTIAw=
+
+ Resources/core/icons/house-check-fill.svg
+
+ 2PQJOt2PudDCfIkKR8vyZyH1jxo=
+
+ Resources/core/icons/house-check.svg
+
+ c+UqIg8TUnlBzuCaS8Ixxu+0Uc0=
+
+ Resources/core/icons/house-dash-fill.svg
+
+ +t/K8ROt1/8lPtyxQmU9qtjo+iE=
+
+ Resources/core/icons/house-dash.svg
+
+ JtGkCTOEqcaw5yVXJRkV2tBWl20=
+
+ Resources/core/icons/house-door-fill.svg
+
+ N3tzi6Cx4m0renHOKaed5A61tic=
+
+ Resources/core/icons/house-door.svg
+
+ R+1+L/rhCJpM7I7sH84TwLbZDcs=
+
+ Resources/core/icons/house-down-fill.svg
+
+ 2nqgUxHsv8I7sgarOcUiZwlKcGk=
+
+ Resources/core/icons/house-down.svg
+
+ 5uCihXo5AiZ0FckHJihvgoEU9jY=
+
+ Resources/core/icons/house-exclamation-fill.svg
+
+ olyyUm1HseUdG9ZJqJcrpcg9CZI=
+
+ Resources/core/icons/house-exclamation.svg
+
+ DceSuo2SZl3L92GQl8R4wKXQqKs=
+
+ Resources/core/icons/house-fill.svg
+
+ B2Yf8TgQ/9vWQoDZTv0E2dhi/xo=
+
+ Resources/core/icons/house-gear-fill.svg
+
+ de+GmB3dkGQDhHxhXwbD0FpHJMA=
+
+ Resources/core/icons/house-gear.svg
+
+ 3fZ7q28uWGaAslAQqj/ZlELeSwI=
+
+ Resources/core/icons/house-heart-fill.svg
+
+ aRjM4JkrkKdGckmtQZobpPuJ61s=
+
+ Resources/core/icons/house-heart.svg
+
+ Va6CKbeJNUNRgzjqyP+funFi1wc=
+
+ Resources/core/icons/house-lock-fill.svg
+
+ NTunOLb8B+fFFrkNZjo+fJRtNdU=
+
+ Resources/core/icons/house-lock.svg
+
+ aEOkQwVj5Jn8N132GLRGPN+LQP8=
+
+ Resources/core/icons/house-slash-fill.svg
+
+ lsGc9Yd/Qf17yzO1TY52+rlIQNA=
+
+ Resources/core/icons/house-slash.svg
+
+ VYRYfYNYIXY/HvRGbVamOvIS408=
+
+ Resources/core/icons/house-up-fill.svg
+
+ MhyjNJfD7E9NAONtqi2diSq2C0w=
+
+ Resources/core/icons/house-up.svg
+
+ 2b1m0HDEcpq76lXy5xEl6BsMg+E=
+
+ Resources/core/icons/house-x-fill.svg
+
+ 4FY8V2fDmnWdWVsqiBEr39+P/MA=
+
+ Resources/core/icons/house-x.svg
+
+ C3TUTaEbZjGZ0nfkTUzQOszHcnA=
+
+ Resources/core/icons/house.svg
+
+ sxt883g5inFm23UgEz8DnoBDxhg=
+
+ Resources/core/icons/houses-fill.svg
+
+ vRTqK24WEw0gdyOOC/nQ7bnRgtA=
+
+ Resources/core/icons/houses.svg
+
+ XrovmyZA4vYWVGvqL/gCg7WtX1s=
+
+ Resources/core/icons/hr.svg
+
+ OWuXvQnreN292NU2xJlxu5ZBucI=
+
+ Resources/core/icons/hurricane.svg
+
+ gNEiXaC1BztaO7ZzigYGNgn0m5w=
+
+ Resources/core/icons/hypnotize.svg
+
+ XMJPFdDNYy11QOSjxDtcUrlSnS4=
+
+ Resources/core/icons/image-alt.svg
+
+ mcupESjPpIoBsU1KVULdpixHyGw=
+
+ Resources/core/icons/image-fill.svg
+
+ 09YtakNlTi+Zb0/n2VsIW4FRYgU=
+
+ Resources/core/icons/image.svg
+
+ ujFmN78PoPl2rzL/xZkd61oKlws=
+
+ Resources/core/icons/images.svg
+
+ Q+GpwMxK6LWOmWjS6xtIoNsdjzk=
+
+ Resources/core/icons/inbox-fill.svg
+
+ MPT9SFoUjCKd/ZLqIFUDpfEXirc=
+
+ Resources/core/icons/inbox.svg
+
+ QrOpDOGxJuULlLQ4FB9951nDuxU=
+
+ Resources/core/icons/inboxes-fill.svg
+
+ 1YDhe7RH2txSzoiAbJMjJl4bqZU=
+
+ Resources/core/icons/inboxes.svg
+
+ +TviLEcmbKL4B+qEF93AedA6aHU=
+
+ Resources/core/icons/incognito.svg
+
+ Q5ufDybeZGMbNNvypUYnmfyvU6w=
+
+ Resources/core/icons/indent.svg
+
+ 8RhAKEi5m/4qFE8cMMnsqBMfaRc=
+
+ Resources/core/icons/infinity.svg
+
+ 0xtdI+HCglCk5KXdQdbH7jrpmlc=
+
+ Resources/core/icons/info-circle-fill.svg
+
+ yeeWIRn+8TZPsGYDYR1IME00UGY=
+
+ Resources/core/icons/info-circle.svg
+
+ sS5xi9yDPZ0uGPXVoiaj0QODTIg=
+
+ Resources/core/icons/info-lg.svg
+
+ THLxTGXs1zIxkCQPGFmd98wrvUI=
+
+ Resources/core/icons/info-square-fill.svg
+
+ ZDMyw1Fstb/gkl+6jqpCQtpvNh4=
+
+ Resources/core/icons/info-square.svg
+
+ sAioHFlyXGsDmAxu0wgkore0kwQ=
+
+ Resources/core/icons/info.svg
+
+ fxfc+5ylCsbikEfHlT6F5FE6tPo=
+
+ Resources/core/icons/input-cursor-text.svg
+
+ Pra3BVKKhwsqSopyaDbfZsB/ThM=
+
+ Resources/core/icons/input-cursor.svg
+
+ d8ma+CMBe7slncULuIlD1eSaukk=
+
+ Resources/core/icons/instagram.svg
+
+ 9AVBmkrr5gpnVCJ2fetp4Wy7YL4=
+
+ Resources/core/icons/intersect.svg
+
+ 4gXKnZFfxODwGuqIrN7LMC6oo4w=
+
+ Resources/core/icons/javascript.svg
+
+ w2s8VN1Qswaap5mZmDlDe+WAZqI=
+
+ Resources/core/icons/journal-album.svg
+
+ 3A5ctHoCu11AvbAbrnHgZkPe1CE=
+
+ Resources/core/icons/journal-arrow-down.svg
+
+ 6hxx4fg6EJtdy0W9RqGphcHMa80=
+
+ Resources/core/icons/journal-arrow-up.svg
+
+ C30b1iCH8Hhhn/FNzkv+ODutiDI=
+
+ Resources/core/icons/journal-bookmark-fill.svg
+
+ xD7lDVCe98L6i0ilUQk1RRW0VcA=
+
+ Resources/core/icons/journal-bookmark.svg
+
+ i6ADsX658h3DMvQz1LM7aTT6Ly4=
+
+ Resources/core/icons/journal-check.svg
+
+ lDbsx0SxTWEI51IcCAH2XqRfRVQ=
+
+ Resources/core/icons/journal-code.svg
+
+ PAjt/ADtaPC7fIw4nBq7bBiMUd8=
+
+ Resources/core/icons/journal-medical.svg
+
+ vXod38AZWxdLXDXKSo6EmoclI34=
+
+ Resources/core/icons/journal-minus.svg
+
+ RmFh0KftA5kVwIK5yQ8NCl7qdFM=
+
+ Resources/core/icons/journal-plus.svg
+
+ Y/G7lXXT4pEGDfRNyKYs1wR4tyI=
+
+ Resources/core/icons/journal-richtext.svg
+
+ CAztD9yyDSrHvRdblgL8WHg/r7g=
+
+ Resources/core/icons/journal-text.svg
+
+ KpS3MXy14wDmid7PMxE7McjqlUI=
+
+ Resources/core/icons/journal-x.svg
+
+ HuTQ/SYchHfSLVMV3HdJ0Moj7tA=
+
+ Resources/core/icons/journal.svg
+
+ 9vb6seRZbzUpagVH7hTy7OlniO0=
+
+ Resources/core/icons/journals.svg
+
+ BFFDvJk2M1Ry0RudjDZMIb5fdi4=
+
+ Resources/core/icons/joystick.svg
+
+ ReW4Xvg0GOoqiCeyNnQv+ADd5vk=
+
+ Resources/core/icons/justify-left.svg
+
+ juAU4545lfGY0sXpUvFIpl4egks=
+
+ Resources/core/icons/justify-right.svg
+
+ pjEQwdaTosXc2xfHHs7/N/vpmMw=
+
+ Resources/core/icons/justify.svg
+
+ TwaRIBkpGoHrx1lL6jg+JjuCM1k=
+
+ Resources/core/icons/kanban-fill.svg
+
+ ZT6Ah6NZLCSnu8TqG4Tz4q+C3lA=
+
+ Resources/core/icons/kanban.svg
+
+ vJJBlS2x4X/vmc77f1Md34IYA3g=
+
+ Resources/core/icons/key-fill.svg
+
+ z6qua/vn/zNC+UAs9ata/g0BGtA=
+
+ Resources/core/icons/key.svg
+
+ sYJyWnAVVcFQgqbEEzntMmCa7+k=
+
+ Resources/core/icons/keyboard-fill.svg
+
+ agLwGmDS8Bg+FtZawwOlDXCs5kY=
+
+ Resources/core/icons/keyboard.svg
+
+ g8oGSC5IJe0paD69AkvYK0phaiU=
+
+ Resources/core/icons/ladder.svg
+
+ gMbDvCFiKcjxHjFv42z70xND/7c=
+
+ Resources/core/icons/lamp-fill.svg
+
+ f6+i5YETZxKNbxVNAaPu3b+dkE8=
+
+ Resources/core/icons/lamp.svg
+
+ 1pdj5qkQJAxQNyP3L5SgYp6d1Qk=
+
+ Resources/core/icons/laptop-fill.svg
+
+ 6JjKdhrC1ZB/GiOAVfk7YTTZh60=
+
+ Resources/core/icons/laptop.svg
+
+ o+UVFQplTsLzUNG9vDuUoABmYdM=
+
+ Resources/core/icons/layer-backward.svg
+
+ XFj1SilbKoLb3+vv8lbyYwRtcII=
+
+ Resources/core/icons/layer-forward.svg
+
+ C2CJEtBvEsGf76dNrXxISh3p2Qg=
+
+ Resources/core/icons/layers-fill.svg
+
+ onM8Gz3CKoRetWQjP+xL19MeoXs=
+
+ Resources/core/icons/layers-half.svg
+
+ xetjUKTrltF/jngLx662XK1Lm84=
+
+ Resources/core/icons/layers.svg
+
+ bQEcDqbqVaPy/1goKdqXFW4/66A=
+
+ Resources/core/icons/layout-sidebar-inset-reverse.svg
+
+ 4dZ94J3nuQW7Tip+W7DOUKxbK0I=
+
+ Resources/core/icons/layout-sidebar-inset.svg
+
+ GLcMkY1qfdADuUb3i3h35xjtHOs=
+
+ Resources/core/icons/layout-sidebar-reverse.svg
+
+ w+LRbM8oJz7GfkSw6GZF8VqkEZA=
+
+ Resources/core/icons/layout-sidebar.svg
+
+ SD4hUZP/95nDKtQ8AQCc938H4/E=
+
+ Resources/core/icons/layout-split.svg
+
+ Fsb5dsLtzByV/FK0BrBMzM6zUrw=
+
+ Resources/core/icons/layout-text-sidebar-reverse.svg
+
+ K6Pl8kEE1J607Dnd3qoWOu1WrCQ=
+
+ Resources/core/icons/layout-text-sidebar.svg
+
+ vz/279sn4zIl5UH3nL8n+E8mCSo=
+
+ Resources/core/icons/layout-text-window-reverse.svg
+
+ GbuJhjVhLfWqtr1LuZxGJaRE+0k=
+
+ Resources/core/icons/layout-text-window.svg
+
+ acA0cYsdlluV4JOcAESuCp6aDdo=
+
+ Resources/core/icons/layout-three-columns.svg
+
+ s0gqyOskIujs+iyF/rokmZ+F6ik=
+
+ Resources/core/icons/layout-wtf.svg
+
+ NDs97nmOI2ol7NUi+NvSMCwDeUQ=
+
+ Resources/core/icons/leaf-fill.svg
+
+ TAIfz9La59YkOuM58bLbiNijiyU=
+
+ Resources/core/icons/leaf.svg
+
+ soVSqJtoWmQdZFZIlzPSoCyymcQ=
+
+ Resources/core/icons/life-preserver.svg
+
+ kWwUXRH7YS45u6pLtVzTFmX/7Dc=
+
+ Resources/core/icons/lightbulb-fill.svg
+
+ vHem/QFFaC+hBbFZZvjAYv37YIs=
+
+ Resources/core/icons/lightbulb-off-fill.svg
+
+ uG+7ABTMg/ruDYE6mvQcSZkHSRE=
+
+ Resources/core/icons/lightbulb-off.svg
+
+ +4vXOfsKlqiNsVwHm32skaBoEyM=
+
+ Resources/core/icons/lightbulb.svg
+
+ 2pmrZBI+ST8UkQwT3ULSelNN7bk=
+
+ Resources/core/icons/lightning-charge-fill.svg
+
+ 3/S4xzWjc7uN0lDF/VqFz0NaD6k=
+
+ Resources/core/icons/lightning-charge.svg
+
+ miy2Cg/QC9QQ0fHuqqlpIyg+82o=
+
+ Resources/core/icons/lightning-fill.svg
+
+ /0I/MNbggAkGbfp/NMYUETaRGYo=
+
+ Resources/core/icons/lightning.svg
+
+ v/Rr4kzMl8OYEEmrwPe6s+K+jUA=
+
+ Resources/core/icons/line.svg
+
+ DQc8n7+3MfngxvJH0Qq4YYpYhgE=
+
+ Resources/core/icons/link-45deg.svg
+
+ iqTM3WLITIRpJKRowBLtF+FAQA8=
+
+ Resources/core/icons/link.svg
+
+ cWy6zj3PaH7R8oWhCGRbst1E9/o=
+
+ Resources/core/icons/linkedin.svg
+
+ OCRL7Z6pswA0f0R6dYNhSpbgDRg=
+
+ Resources/core/icons/list-check.svg
+
+ uctng+1kTGffqWmjRL3td8CjzBw=
+
+ Resources/core/icons/list-columns-reverse.svg
+
+ r2rajAUVuHwDIL1VI/+tM84dVps=
+
+ Resources/core/icons/list-columns.svg
+
+ /D7feut2gaA4vye7Sa5j9+LDpJA=
+
+ Resources/core/icons/list-nested.svg
+
+ qpAF+UBj/9R7EfjJTZscqBeEpRw=
+
+ Resources/core/icons/list-ol.svg
+
+ ayvknLkwjt/pb1kmLZYibBN6kkY=
+
+ Resources/core/icons/list-stars.svg
+
+ P8Oq5+fYA32ESDDxEX/XX62384A=
+
+ Resources/core/icons/list-task.svg
+
+ sG5dJ7IBPaX6Dc5UUkE9GDyaJFc=
+
+ Resources/core/icons/list-ul.svg
+
+ 7o30vMCqgutBTklyg/08pyS5sTo=
+
+ Resources/core/icons/list.svg
+
+ BQBE+LGvsUlRyeQqIeLR1mx2xek=
+
+ Resources/core/icons/lock-fill.svg
+
+ 1XypV7zKfK25dtO7xrNcitAM6NM=
+
+ Resources/core/icons/lock.svg
+
+ q+PA/WNbwsH+Ex9p+9+LfasB4QU=
+
+ Resources/core/icons/luggage-fill.svg
+
+ uEj4WiQAs/UaoWlvYdRlig0XKmw=
+
+ Resources/core/icons/luggage.svg
+
+ syBdFVN9lh89gfzj6bhts3Psmt0=
+
+ Resources/core/icons/lungs-fill.svg
+
+ H0JkUp/JjPWyFN5I1XeE4ENUgDc=
+
+ Resources/core/icons/lungs.svg
+
+ jz4qAyBGOQpnjCCbNnCEh6Wf980=
+
+ Resources/core/icons/magic.svg
+
+ HfHf/KQG5Yj8mHeDz/ULNTg0SXI=
+
+ Resources/core/icons/magnet-fill.svg
+
+ v6nCh/IOBdm0hdYzp31I2UzbAt0=
+
+ Resources/core/icons/magnet.svg
+
+ DRVf9wmxqzxyhTBQFrpl8gwpnw4=
+
+ Resources/core/icons/mailbox-flag.svg
+
+ dfXvOyJM+3GVPW4IOvpmLhJX0oU=
+
+ Resources/core/icons/mailbox.svg
+
+ qI63lD5sUFVLBO2e5lKHPWBzN8M=
+
+ Resources/core/icons/mailbox2-flag.svg
+
+ d8VtmtdkCD6JJrykwRgIruo/+yE=
+
+ Resources/core/icons/mailbox2.svg
+
+ p9VLPijZ7gIq02HM9UVbaHEsl04=
+
+ Resources/core/icons/map-fill.svg
+
+ Rs3hbuoJ0dBrlZwCVQGQ7CIk3yA=
+
+ Resources/core/icons/map.svg
+
+ F5V5E1wy+Lz4Fb2x3gO7yibUO+s=
+
+ Resources/core/icons/markdown-fill.svg
+
+ cprslnpYAdP0mQwczIwRtXlRQt0=
+
+ Resources/core/icons/markdown.svg
+
+ ofAlSPQ91qqDSV8uAY/8fUyIaP0=
+
+ Resources/core/icons/marker-tip.svg
+
+ Fhy7d9p37YwRRcqaaq6Gfzlc9aQ=
+
+ Resources/core/icons/mask.svg
+
+ StHWMF9uTdZYEVXVW3fK6pwFhPo=
+
+ Resources/core/icons/mastodon.svg
+
+ rtMaZDV7qmZcqZY8RBy3LX5SfRo=
+
+ Resources/core/icons/measuring-cup-fill.svg
+
+ b1mt18po/Byo4DihpXgEi9DYx08=
+
+ Resources/core/icons/measuring-cup.svg
+
+ kJn+subsgNaOtJSy6EfF4d3BFKw=
+
+ Resources/core/icons/medium.svg
+
+ wKLoULJ14V/crRau19zcnVVBzrw=
+
+ Resources/core/icons/megaphone-fill.svg
+
+ Ou93i8VaLgKiY7EedwPLAeNTFhY=
+
+ Resources/core/icons/megaphone.svg
+
+ j764Y8v9Jk63Y0Pw9OHbDmFLu/Q=
+
+ Resources/core/icons/memory.svg
+
+ Hh0NaUmktw4d8LT7aX1ihFszpZs=
+
+ Resources/core/icons/menu-app-fill.svg
+
+ WJlYtOTSdfRI/eJQEBc9SYPWGjA=
+
+ Resources/core/icons/menu-app.svg
+
+ dVIqeNIFOSeSFNoaMZqpndft2g4=
+
+ Resources/core/icons/menu-button-fill.svg
+
+ 0a80emJw2o/Ws/8FiFixtugX6Zo=
+
+ Resources/core/icons/menu-button-wide-fill.svg
+
+ 2pSFtZ9K8wLz/CX3wHQbH0ldF9s=
+
+ Resources/core/icons/menu-button-wide.svg
+
+ PdbH+bIxC0KgMd8YS3uC4rYecRo=
+
+ Resources/core/icons/menu-button.svg
+
+ ct6WZ+4LVh6X08Hq6I6d+UPY3zY=
+
+ Resources/core/icons/menu-down.svg
+
+ LMy/CAKmxftCwS4bNPt+vVvxjZE=
+
+ Resources/core/icons/menu-up.svg
+
+ wdKc2pHlM6m8GH31v8ZsMeX07hY=
+
+ Resources/core/icons/messenger.svg
+
+ VjJRS6kJRVh82sYIiT+Koji0/SQ=
+
+ Resources/core/icons/meta.svg
+
+ 230LDSJDdPnROE7frAJrFUeo8Mc=
+
+ Resources/core/icons/mic-fill.svg
+
+ 7YiuaDJl+irc2Vr62tpABsGXwyU=
+
+ Resources/core/icons/mic-mute-fill.svg
+
+ WrH5OU878YX/wqVwwwcBceNKWjc=
+
+ Resources/core/icons/mic-mute.svg
+
+ S7C0s4fJiKqFyhD5Qr4ySyIV7Ks=
+
+ Resources/core/icons/mic.svg
+
+ EbGAKYRa0ByDRiPi46Dz7FWyqKU=
+
+ Resources/core/icons/microsoft-teams.svg
+
+ iB4pDpeza0R2+xaeb7IogxIbP7o=
+
+ Resources/core/icons/microsoft.svg
+
+ mLXHv0rCwhBm6ZTgWH0PzYOFrM0=
+
+ Resources/core/icons/minecart-loaded.svg
+
+ XfortWDAQhQKirSg0139nkTU9vE=
+
+ Resources/core/icons/minecart.svg
+
+ 6N5pSVyWdCC0ZOI5kMQ5c691XtQ=
+
+ Resources/core/icons/modem-fill.svg
+
+ h7sVYRihl5fYM27DgZF1uP6s1RE=
+
+ Resources/core/icons/modem.svg
+
+ g0cUxkexF6G5/23WfIh0fO+U8D8=
+
+ Resources/core/icons/moisture.svg
+
+ hnM39sAu51WVFH8ser5yyNetV+s=
+
+ Resources/core/icons/moon-fill.svg
+
+ CBRwG0XnvuKT0qF272Trnw+p4k4=
+
+ Resources/core/icons/moon-stars-fill.svg
+
+ 04hg0NzB65bxB4XfgFBDZykKWEs=
+
+ Resources/core/icons/moon-stars.svg
+
+ 7pYlScg9fyKAjrzdrDELtRz5rDk=
+
+ Resources/core/icons/moon.svg
+
+ nkCKvOYuFof4AdAs1d5KV+bITZo=
+
+ Resources/core/icons/mortarboard-fill.svg
+
+ JAUxT+MdPsJNJGY5uY8dfx5Mm68=
+
+ Resources/core/icons/mortarboard.svg
+
+ pf9b++trHZOaF+HPfLNBCoDiasg=
+
+ Resources/core/icons/motherboard-fill.svg
+
+ azSAyH4a3QE7vkIqstZI0DR9QTA=
+
+ Resources/core/icons/motherboard.svg
+
+ C60KMKhdDDjDld34dJXDFt8Tt2g=
+
+ Resources/core/icons/mouse-fill.svg
+
+ 8GSi8O4QQlHC5weL6KX5gMMRQNI=
+
+ Resources/core/icons/mouse.svg
+
+ o/RcxLRl3QnGkm0Qmjso8bhSUCs=
+
+ Resources/core/icons/mouse2-fill.svg
+
+ FhrGGrw97qzL5bg81yZ6r1Pt9v8=
+
+ Resources/core/icons/mouse2.svg
+
+ 92nWSnd21NQwBoQB4vhRcekBo+o=
+
+ Resources/core/icons/mouse3-fill.svg
+
+ 77Qk/S4HJbelbFS2f8C8JEMcS2s=
+
+ Resources/core/icons/mouse3.svg
+
+ TCdkzXgZo6mlXX3wVAzbO0QNATU=
+
+ Resources/core/icons/music-note-beamed.svg
+
+ C6Z1peJkDhP5ewm7ohkh+/l3btI=
+
+ Resources/core/icons/music-note-list.svg
+
+ 5PzYUNPeb17sAasA6umNaHWnXNc=
+
+ Resources/core/icons/music-note.svg
+
+ 2mx/Qm25AFWmdqNAiYM9fsyzCTk=
+
+ Resources/core/icons/music-player-fill.svg
+
+ BuiW5XMTjlcJg827GmCaZqIfDdw=
+
+ Resources/core/icons/music-player.svg
+
+ b9IXkbaeCkf05GJp8iEsEemFYN0=
+
+ Resources/core/icons/newspaper.svg
+
+ acX36T/j1+k723vfShDm/k/uvY4=
+
+ Resources/core/icons/nintendo-switch.svg
+
+ aBPg+Yysf8PjA3RtaWHqNYDRQR8=
+
+ Resources/core/icons/node-minus-fill.svg
+
+ Z/RXYovwfmto2n+tT7E283v/mno=
+
+ Resources/core/icons/node-minus.svg
+
+ 3Cuui6BaDSho1Gjh3iyz7WnyouU=
+
+ Resources/core/icons/node-plus-fill.svg
+
+ Bz0omgIYidcvdUoa5GFTtnLkLe8=
+
+ Resources/core/icons/node-plus.svg
+
+ pJghsEy0owLBmZIgWkBP1K4YQhU=
+
+ Resources/core/icons/noise-reduction.svg
+
+ y9XQB1PXw/b20HIJ5VBX2f5otTs=
+
+ Resources/core/icons/nut-fill.svg
+
+ FW+v0XvN8aB7iyywk3fIMZd73MY=
+
+ Resources/core/icons/nut.svg
+
+ 2/B7AfxgxMsBxJAjU2/VvxN/YVk=
+
+ Resources/core/icons/nvidia.svg
+
+ XeyondYfrkuThlV4pTtbzLQU76Q=
+
+ Resources/core/icons/nvme-fill.svg
+
+ vXVMCKjDuVcDnHb/5cOHlmgUdPQ=
+
+ Resources/core/icons/nvme.svg
+
+ Q7YZqVCA1db7Dykp5EVgrwG5pLQ=
+
+ Resources/core/icons/octagon-fill.svg
+
+ Oj7vNlaUYWXYecySV8GpxDIPjko=
+
+ Resources/core/icons/octagon-half.svg
+
+ S3fzYXRycAKD6VALoJRu0VfavaU=
+
+ Resources/core/icons/octagon.svg
+
+ kVAL9cpHKYFVMNRj3k3EAaarNOA=
+
+ Resources/core/icons/openai.svg
+
+ 9W9eLbHRwy8YlXqV7jdaxHLZtcw=
+
+ Resources/core/icons/opencollective.svg
+
+ JGJvWl1wOkr8kzeLHkd3kHM/OOU=
+
+ Resources/core/icons/optical-audio-fill.svg
+
+ +ZH3elrv+it2xOGDibv1D6gJ/uY=
+
+ Resources/core/icons/optical-audio.svg
+
+ jxXWGO9U2PqHS7o0GPBa1hz0BIk=
+
+ Resources/core/icons/option.svg
+
+ j8E7GA5oldgaqToMO6VzjMcWGEQ=
+
+ Resources/core/icons/outlet.svg
+
+ z6ZA/qwCsJIiNw5jsZ63Yi7Ghhc=
+
+ Resources/core/icons/p-circle-fill.svg
+
+ WpHPEApSjjRQ3yEQsQXVeJqAaOA=
+
+ Resources/core/icons/p-circle.svg
+
+ AkAdQd3VyxTmSaXgiBxQ+XkGVww=
+
+ Resources/core/icons/p-square-fill.svg
+
+ UOlkYo9AtoAJSqQg878qUUl5keM=
+
+ Resources/core/icons/p-square.svg
+
+ gd/vo+6/o1466W2j0C+XJU2Z+7Y=
+
+ Resources/core/icons/paint-bucket.svg
+
+ yFUnPJF9KHfgpSyIO4J/vg0XGnA=
+
+ Resources/core/icons/palette-fill.svg
+
+ dj3wd2zKS2uAEwbL8XBuNVhY+0g=
+
+ Resources/core/icons/palette.svg
+
+ DlUie1GGUMOvi63yxoQPs1bo4eQ=
+
+ Resources/core/icons/palette2.svg
+
+ VzzxavF7tx2frcNfA1xl7fK92Bg=
+
+ Resources/core/icons/paperclip.svg
+
+ W5DCcWUz0HgWP93sZ3nYKLSE+nY=
+
+ Resources/core/icons/paragraph.svg
+
+ li5Nazi+xTcvhP6ZY4HsVkiBlyA=
+
+ Resources/core/icons/pass-fill.svg
+
+ 49ditOuma8Q/ArrWhGJiHHUW1/U=
+
+ Resources/core/icons/pass.svg
+
+ 5U2IRLlwRKvTskHqlNVo1LWscfI=
+
+ Resources/core/icons/passport-fill.svg
+
+ StkAlJ4KImxVM0mN53+HAmeL3p8=
+
+ Resources/core/icons/passport.svg
+
+ SmAJfgmmKtTPUy7HhuIzBJiSeZ4=
+
+ Resources/core/icons/patch-check-fill.svg
+
+ EaG/pK9TKuHiqOAYcOwfFtCyL8A=
+
+ Resources/core/icons/patch-check.svg
+
+ ApuAYOo2A3UY8PiQRbrnvx9Eoxc=
+
+ Resources/core/icons/patch-exclamation-fill.svg
+
+ crPhTWbx+oPn3JcEbhEhgdcZQrE=
+
+ Resources/core/icons/patch-exclamation.svg
+
+ CXvtIzgznmvSE+H1w9SPojseG18=
+
+ Resources/core/icons/patch-minus-fill.svg
+
+ MliqUKajQnorM11cyPO60rMbyRY=
+
+ Resources/core/icons/patch-minus.svg
+
+ UNFDw2+GOdHWSh4zjqzePx8K5cw=
+
+ Resources/core/icons/patch-plus-fill.svg
+
+ Fn0A9V5rbGkLW9vTD9I5lgYwJzw=
+
+ Resources/core/icons/patch-plus.svg
+
+ T3JmmS4Ig1di5sYr2yG9554b7fk=
+
+ Resources/core/icons/patch-question-fill.svg
+
+ mSZTbn5q+2yhLzGzac6aRtOWma8=
+
+ Resources/core/icons/patch-question.svg
+
+ OWxTb+8jxXcqgWDnPdecOyxnjvM=
+
+ Resources/core/icons/pause-btn-fill.svg
+
+ Ic2ZuXFlFYsG7a+eOZxH1wBnZcw=
+
+ Resources/core/icons/pause-btn.svg
+
+ 4nzJibsqkbwYwTiQ3v2OLHD6rjA=
+
+ Resources/core/icons/pause-circle-fill.svg
+
+ YqR0CbQ7ZfIOwtN88zEsJp7u6AU=
+
+ Resources/core/icons/pause-circle.svg
+
+ wHgqYZrDuooZBHVXbtGXZqMvxcY=
+
+ Resources/core/icons/pause-fill.svg
+
+ BOgthmkGjwyxhdaG1LftsQZMXKg=
+
+ Resources/core/icons/pause.svg
+
+ 0ndH+fDfdQ4OlkxRT8WoVGa6Pno=
+
+ Resources/core/icons/paypal.svg
+
+ nmrcLFY+tGgXXZlnVUJ4v68iZ3M=
+
+ Resources/core/icons/pc-display-horizontal.svg
+
+ emnxr3RdUr0E2u4wAQr+34/4oh0=
+
+ Resources/core/icons/pc-display.svg
+
+ 3kmEmmp61ZYQLE29w0tKbNzPTQc=
+
+ Resources/core/icons/pc-horizontal.svg
+
+ RTC37RrgAcDfJisxPtzVb8VYlS4=
+
+ Resources/core/icons/pc.svg
+
+ wnkOohMxCKZmFtpRUAtkSIfV9fw=
+
+ Resources/core/icons/pci-card-network.svg
+
+ 5+IkcAY+8vr1x8MpC7cZVVfYrF0=
+
+ Resources/core/icons/pci-card-sound.svg
+
+ NT9Jeh+aJFM55pr04fgY3kjYbt0=
+
+ Resources/core/icons/pci-card.svg
+
+ BFMW4qlW5pS2dBAekwKae//xII4=
+
+ Resources/core/icons/peace-fill.svg
+
+ DWw8cysWHhAjhaOcS6AB/MTuOHc=
+
+ Resources/core/icons/peace.svg
+
+ 0EeatEkgJsp67kdrAMfT60krzW8=
+
+ Resources/core/icons/pen-fill.svg
+
+ 3qV7opj0hSJMpyb32c4kNpc35WI=
+
+ Resources/core/icons/pen.svg
+
+ ZzD20LfCTBi+WG8wsEJGMok1v3Q=
+
+ Resources/core/icons/pencil-fill.svg
+
+ 5nKk3mDuDsOsOLktrDWUW4qxHmE=
+
+ Resources/core/icons/pencil-square.svg
+
+ 59UUIYImDVexT5cN8UWzaUPzuhI=
+
+ Resources/core/icons/pencil.svg
+
+ VP0EWicrxV/Rc9vokcATAtcfACs=
+
+ Resources/core/icons/pentagon-fill.svg
+
+ jjLCWVXKayQFodWuePTZNsjPI6c=
+
+ Resources/core/icons/pentagon-half.svg
+
+ uofPdYrPNy0uNAyYWfOTUIqnx+Q=
+
+ Resources/core/icons/pentagon.svg
+
+ IbV4cIU4n+yh30KJnYXkWrWp9xc=
+
+ Resources/core/icons/people-fill.svg
+
+ hzY1Wqx7vwz9De9m3wStl067Y1g=
+
+ Resources/core/icons/people.svg
+
+ 1ZZAiHAwb3Epqdcuswbg8+Bb/w8=
+
+ Resources/core/icons/percent.svg
+
+ JE8TQflxPo4KHzn7pMSq9gcY6JY=
+
+ Resources/core/icons/perplexity.svg
+
+ Mr0mDoUQkWwmpSTB0F6pLt6cS6c=
+
+ Resources/core/icons/person-add.svg
+
+ U68x+g/qqdGsuYFRU85FqXDd0W4=
+
+ Resources/core/icons/person-arms-up.svg
+
+ OBGvhLCv62aezZwVTWzc+GO1wDw=
+
+ Resources/core/icons/person-badge-fill.svg
+
+ AEh900m4JlENB13VgxXAykwXxsw=
+
+ Resources/core/icons/person-badge.svg
+
+ 9WEZonUZgLnm8/WOYc9cS/xNFXI=
+
+ Resources/core/icons/person-bounding-box.svg
+
+ MZOH33uG5kupPYq1yCxWvKM1H3w=
+
+ Resources/core/icons/person-check-fill.svg
+
+ YbvFH0KSRWKpVFcsBlWi8Z+tOzE=
+
+ Resources/core/icons/person-check.svg
+
+ as0iLLV1tiG1Krj5YZyhYqOFdQA=
+
+ Resources/core/icons/person-circle.svg
+
+ a7ZOfDWeLNxT0v6fQRHdRjAp6Ho=
+
+ Resources/core/icons/person-dash-fill.svg
+
+ avZd97Tm3pfO1aopTjOUdqaMnpg=
+
+ Resources/core/icons/person-dash.svg
+
+ drLHMEUMyV4PWxdq+NQrWjY+W3w=
+
+ Resources/core/icons/person-down.svg
+
+ wjoM3AldXk9mZGnsm1n+UF5ZNNA=
+
+ Resources/core/icons/person-exclamation.svg
+
+ OG2lHsFGQBLr6ZWjLYlubNM3mNM=
+
+ Resources/core/icons/person-fill-add.svg
+
+ zDbe5x/LesSTNX/96y1JAl8Sfy8=
+
+ Resources/core/icons/person-fill-check.svg
+
+ ChirKswP5kJnV1Z6Llj3WwpFMTE=
+
+ Resources/core/icons/person-fill-dash.svg
+
+ oi1BJcPPmwhkRSd4BXtHIbLFNg0=
+
+ Resources/core/icons/person-fill-down.svg
+
+ KI7UbiWgDIX96lPhibJuinjGfeI=
+
+ Resources/core/icons/person-fill-exclamation.svg
+
+ iYd/+Uo502DAjbl+aN3q6XHO/5M=
+
+ Resources/core/icons/person-fill-gear.svg
+
+ ySrxe9ZCROsVaEqWhmGnKLz3jEY=
+
+ Resources/core/icons/person-fill-lock.svg
+
+ RhPCP+2WbOjfV4A+uGDMitdtaEo=
+
+ Resources/core/icons/person-fill-slash.svg
+
+ cFyK5tdxo19jIvG9vFgri5v86eQ=
+
+ Resources/core/icons/person-fill-up.svg
+
+ q9K8sZf4wwBg2cPijGeYqTGOlYE=
+
+ Resources/core/icons/person-fill-x.svg
+
+ e1I9g4+bRDMEzxi5OPNAMau2VLg=
+
+ Resources/core/icons/person-fill.svg
+
+ iC2gEitmVRdznttA6Ly5GVyoiNk=
+
+ Resources/core/icons/person-gear.svg
+
+ FP1XtydjpTA2R34g3t+X7Z0ZHoo=
+
+ Resources/core/icons/person-heart.svg
+
+ V1ga1ilip5ODF8IYkF7lpM8j1fA=
+
+ Resources/core/icons/person-hearts.svg
+
+ ZNro1uAxb/YRAsi9TkclBkCps70=
+
+ Resources/core/icons/person-lines-fill.svg
+
+ 25Y2q4b6YXo91k41XHu4p5hyNs0=
+
+ Resources/core/icons/person-lock.svg
+
+ Vzw+/HMnHupuCAQF4HJ5e432iqs=
+
+ Resources/core/icons/person-plus-fill.svg
+
+ 53ipquA2CwglcD3heiJfia9hEqA=
+
+ Resources/core/icons/person-plus.svg
+
+ yew1u4csQTE2Btn0aW/HIk9PDcU=
+
+ Resources/core/icons/person-raised-hand.svg
+
+ mAYa0wTZJVNMa6KptoJKR06j2hQ=
+
+ Resources/core/icons/person-rolodex.svg
+
+ 0yRhQB63lkbFopiqFrNm9Ij1hsc=
+
+ Resources/core/icons/person-slash.svg
+
+ N0UDUJqQfwLh8g/MSjCXez5txes=
+
+ Resources/core/icons/person-square.svg
+
+ ArgXTMI+jA+Ekfmv/QsC6ZwITi4=
+
+ Resources/core/icons/person-standing-dress.svg
+
+ F0FIFnvCn67xZR8ZDZ3yzpmAn6c=
+
+ Resources/core/icons/person-standing.svg
+
+ vhN9EduuPh91SW3JFmqtLZkxLS8=
+
+ Resources/core/icons/person-up.svg
+
+ ylNM2so04/jSqmyqhDynndFJWsk=
+
+ Resources/core/icons/person-vcard-fill.svg
+
+ TgBldjIRdXOeMjIFXqJQsaZiNig=
+
+ Resources/core/icons/person-vcard.svg
+
+ dNux3o7or9n9FODixg1stCaTVfQ=
+
+ Resources/core/icons/person-video.svg
+
+ bZMId/d3pb5VupfXN0VT0o0IA3c=
+
+ Resources/core/icons/person-video2.svg
+
+ MIdC/Y7861/xXatvT4UQWh+8MVo=
+
+ Resources/core/icons/person-video3.svg
+
+ sBjvrcIgRP1XL6GQi9CNKjASY5A=
+
+ Resources/core/icons/person-walking.svg
+
+ LGRD7v5q+6XRcezgTArIVNhacBE=
+
+ Resources/core/icons/person-wheelchair.svg
+
+ nrj3ydbwwC9gpP8M2cypaJDgN4Y=
+
+ Resources/core/icons/person-workspace.svg
+
+ 5CS0XRhEuCQqgdrhdLaBvIH8AvE=
+
+ Resources/core/icons/person-x-fill.svg
+
+ jPvspVQDpiefKoXGpxpXmp9T740=
+
+ Resources/core/icons/person-x.svg
+
+ cvd14P62xC7KbdCjGpL3RP5wi+o=
+
+ Resources/core/icons/person.svg
+
+ sQeEgrNNXyoR0rnMtsUJsyoazE0=
+
+ Resources/core/icons/phone-fill.svg
+
+ Ze8Y/JptRLKAOcUNkZmsfwvxgM0=
+
+ Resources/core/icons/phone-flip.svg
+
+ ZYDeVPx1oWPVBfB0lhSwF6ZPNe8=
+
+ Resources/core/icons/phone-landscape-fill.svg
+
+ W6H+E23WOTStcY7RmKW+8ayX1Ro=
+
+ Resources/core/icons/phone-landscape.svg
+
+ I+XTGEgXo7L83Sm0AQsXzbB4P44=
+
+ Resources/core/icons/phone-vibrate-fill.svg
+
+ Vf63ydsnJ3TFWtprITtsCdqIRLM=
+
+ Resources/core/icons/phone-vibrate.svg
+
+ xX8b2EFY4IZzSy876mfFkwFEx6U=
+
+ Resources/core/icons/phone.svg
+
+ bOjm6zCYPdE14D4/oIPs9xsWKs0=
+
+ Resources/core/icons/pie-chart-fill.svg
+
+ q1eGclEMApmqOk66jefwt+B/hdM=
+
+ Resources/core/icons/pie-chart.svg
+
+ VQPj49C+wgFQthPD2bzeNKN2HKw=
+
+ Resources/core/icons/piggy-bank-fill.svg
+
+ 9o45fJyHQNU259D8bWbPccf3qG0=
+
+ Resources/core/icons/piggy-bank.svg
+
+ r1lbFLEfymWsNOFV04oufTKNGJ0=
+
+ Resources/core/icons/pin-angle-fill.svg
+
+ WjXaJ07if1SlOHHH3jJiImY+g9c=
+
+ Resources/core/icons/pin-angle.svg
+
+ RdRhIwxb8fdNoeBLT31trfOJpIU=
+
+ Resources/core/icons/pin-fill.svg
+
+ gHjFjjJfqChKGCLqnywbf+IYmYw=
+
+ Resources/core/icons/pin-map-fill.svg
+
+ g6FVGdWAkH9hzUM57uzafRIQS0E=
+
+ Resources/core/icons/pin-map.svg
+
+ wqO2XZGWGPPOg7Hwllex221HnsI=
+
+ Resources/core/icons/pin.svg
+
+ xMAFdKpLwNSsTK6WprE8Wg1LMpc=
+
+ Resources/core/icons/pinterest.svg
+
+ aA6jYBACdf3TO5zomx9q0McQps0=
+
+ Resources/core/icons/pip-fill.svg
+
+ PKcLu9+up1cpD5wi0l6BlBt1QKI=
+
+ Resources/core/icons/pip.svg
+
+ P5mIrHH6kO45urCkfmIRbQS0iQg=
+
+ Resources/core/icons/play-btn-fill.svg
+
+ zs0bpDDBMJadb8a2skOBLBzjNlw=
+
+ Resources/core/icons/play-btn.svg
+
+ 3f+WqI0n0FhtnUbxsPyEeNdTOH4=
+
+ Resources/core/icons/play-circle-fill.svg
+
+ AqkS9pOlKkayUC0W+ywSjSe19Jo=
+
+ Resources/core/icons/play-circle.svg
+
+ ozS1Dngn43MyaRsewKfNfFEQ2qs=
+
+ Resources/core/icons/play-fill.svg
+
+ 35dDc+spcCiE3Vlm9gfYFE1dkhs=
+
+ Resources/core/icons/play.svg
+
+ 2vjwNApAYcsafvsoJ8ybaKLZYKw=
+
+ Resources/core/icons/playstation.svg
+
+ pxqx7K2Mcd82o0bUWLqASzoOxtk=
+
+ Resources/core/icons/plug-fill.svg
+
+ vWKWzK034ADpQydIb3uNLjKBgJM=
+
+ Resources/core/icons/plug.svg
+
+ HRsxnbdU4pSSwRHJoq7yjT1fB4Q=
+
+ Resources/core/icons/plugin.svg
+
+ zxNpBMrgmLIzJDCiWVuctpWC24Q=
+
+ Resources/core/icons/plus-circle-dotted.svg
+
+ eo6U6ZD3PXB99P7E3QFQ8HMpK+k=
+
+ Resources/core/icons/plus-circle-fill.svg
+
+ cEQUMz2TvtwD54OB2BfdY7H+14E=
+
+ Resources/core/icons/plus-circle.svg
+
+ ozr9UsbAgGZeKVYCMVA2Y4RbtlE=
+
+ Resources/core/icons/plus-lg.svg
+
+ Goc+G7y4iIrrQw5MoV9jtfXWQ1M=
+
+ Resources/core/icons/plus-slash-minus.svg
+
+ Rto+EWP1/QdXZsM7+/ELeD/ISgA=
+
+ Resources/core/icons/plus-square-dotted.svg
+
+ 0RTBi++UZz4QjJ4c+YITziUPtEg=
+
+ Resources/core/icons/plus-square-fill.svg
+
+ coeyDvlHiPK2DLOjoaopqtoIfic=
+
+ Resources/core/icons/plus-square.svg
+
+ faQbAUMz35qT3+STmbwzz/sJS5k=
+
+ Resources/core/icons/plus.svg
+
+ ax2OPewx6nBhG175LAsQhbREBJM=
+
+ Resources/core/icons/postage-fill.svg
+
+ Ul7yc63RwKJKogvb4Osq/gpDktg=
+
+ Resources/core/icons/postage-heart-fill.svg
+
+ 6Gm8x30NJJQiooQd0CZpzQpzqd4=
+
+ Resources/core/icons/postage-heart.svg
+
+ 96wjeigswsfrVBSSNmdZEm6uoFY=
+
+ Resources/core/icons/postage.svg
+
+ z5TIeBEPDJ02OVBNStzpqBDDl+w=
+
+ Resources/core/icons/postcard-fill.svg
+
+ xIw5CtLK4IKXqL4873gTo7XV5bY=
+
+ Resources/core/icons/postcard-heart-fill.svg
+
+ NvwoP1u3gP7L5jiCNrPKEJWuGVg=
+
+ Resources/core/icons/postcard-heart.svg
+
+ 9B8JDfYML0gzqopMbAtwak9iqQA=
+
+ Resources/core/icons/postcard.svg
+
+ 4BYJjv49qAPRCzcLCEVCqp5e8Zo=
+
+ Resources/core/icons/power.svg
+
+ 9rZH7ayehdPlDbFkNgQDqBAi8jo=
+
+ Resources/core/icons/prescription.svg
+
+ XVAG7MMqwjpCV848XHcwCyZ/lLc=
+
+ Resources/core/icons/prescription2.svg
+
+ fBex4tO7l+f1Qk2bORqC2+s3Z6M=
+
+ Resources/core/icons/printer-fill.svg
+
+ mKcwoKZusMV/oAk2pPUwLYEbegQ=
+
+ Resources/core/icons/printer.svg
+
+ qCg74HUTwG0nH62/IgoJyN86F5Q=
+
+ Resources/core/icons/projector-fill.svg
+
+ 71Eczd0mhzKTANxrbtRMnx9xYUY=
+
+ Resources/core/icons/projector.svg
+
+ ehdJea3VtLpXTr4WRykngN0nXSA=
+
+ Resources/core/icons/puzzle-fill.svg
+
+ iXegpIOS3+DeOwM61oFJ0HGvts0=
+
+ Resources/core/icons/puzzle.svg
+
+ opaRtwGoZTQ9ykUzV0eQStmitMY=
+
+ Resources/core/icons/qr-code-scan.svg
+
+ DueXOSZbtvFTC/Y+UJvRrBRFm4M=
+
+ Resources/core/icons/qr-code.svg
+
+ Z+1w+DPJhneLb1qmAhRw8k4h1X8=
+
+ Resources/core/icons/question-circle-fill.svg
+
+ 69cmvCXK/blgll8hA6rcpvdLkEM=
+
+ Resources/core/icons/question-circle.svg
+
+ Kpl5rzYr7NQLAQUC6FdX7met66Y=
+
+ Resources/core/icons/question-diamond-fill.svg
+
+ rBWI1gdjMku3XNA6SDWNkGvxOlw=
+
+ Resources/core/icons/question-diamond.svg
+
+ 5810I2PhlAY9oPMP95EY/0tB8Qw=
+
+ Resources/core/icons/question-lg.svg
+
+ 84hctui7+YGlDhNQuocrPTCHFyU=
+
+ Resources/core/icons/question-octagon-fill.svg
+
+ huZu7OOK/BOR9q0wLtAXacVKADI=
+
+ Resources/core/icons/question-octagon.svg
+
+ 5jwUHU3jzMcGBCc8mfkaRh97S7Y=
+
+ Resources/core/icons/question-square-fill.svg
+
+ EYxoCZCTEY7R3csKXN7uJNjq1g4=
+
+ Resources/core/icons/question-square.svg
+
+ RhJv0LWqPu741gHLaI32iszCLBM=
+
+ Resources/core/icons/question.svg
+
+ EiNKiPqTg2zmMxV+S7/NE72lUyc=
+
+ Resources/core/icons/quora.svg
+
+ 5K5ZBbtl5/r2N0JkcjwpAb1lfF0=
+
+ Resources/core/icons/quote.svg
+
+ xhsVJcO4Fu8qyw47V4A1Nkh5QR0=
+
+ Resources/core/icons/r-circle-fill.svg
+
+ GEbmskA7SO9k0iGiZAUoSOZd6q0=
+
+ Resources/core/icons/r-circle.svg
+
+ p+LNlGQ8NubIalaZ2UjE/J58vPU=
+
+ Resources/core/icons/r-square-fill.svg
+
+ JGDIkMQ0PwAck2txny0ojjYjHvg=
+
+ Resources/core/icons/r-square.svg
+
+ +W6YrM2N2JH+hi/L5fm3YXfKNww=
+
+ Resources/core/icons/radar.svg
+
+ ExOTMz49KW/BarXqYO5ZICkCDC0=
+
+ Resources/core/icons/radioactive.svg
+
+ GxQk6R5qsoiw04Qy1hinKbpj/c4=
+
+ Resources/core/icons/rainbow.svg
+
+ VieMwhrN+0dhgxSP/s4bp0jE7QQ=
+
+ Resources/core/icons/receipt-cutoff.svg
+
+ 0QP5uYuT8DuwMLY3zU9G9gDzLzw=
+
+ Resources/core/icons/receipt.svg
+
+ d61JValUQ4MsdXn+cStHdlSfIzI=
+
+ Resources/core/icons/reception-0.svg
+
+ 6RftUSx7rKr0C6ZlExnr4Ic2R20=
+
+ Resources/core/icons/reception-1.svg
+
+ Oyqbv03IORrHuN62NBaNokITLU0=
+
+ Resources/core/icons/reception-2.svg
+
+ C5pCfGl05GATIqAqEJkVd1Ve6dE=
+
+ Resources/core/icons/reception-3.svg
+
+ KnDxd70WxR7v3xK/tveFpkQzb4U=
+
+ Resources/core/icons/reception-4.svg
+
+ bHY1qkMYf3pzZedXlt3JgLsaKbI=
+
+ Resources/core/icons/record-btn-fill.svg
+
+ 1e2S1uLTuX/OLoGdh1bYgi8qkgM=
+
+ Resources/core/icons/record-btn.svg
+
+ Zedw+KEHCo9R0RO+bwfdsK1WIPk=
+
+ Resources/core/icons/record-circle-fill.svg
+
+ 3z5bnWuijxmDYBJG5IZ6mABK5Gc=
+
+ Resources/core/icons/record-circle.svg
+
+ YnceC42XFwsccn4vRza6WqkZYWk=
+
+ Resources/core/icons/record-fill.svg
+
+ HwNGz8kz+COX3CGDS98wkx3O8M0=
+
+ Resources/core/icons/record.svg
+
+ aevRopdTW9Lm+k/SorQLSjsHbgM=
+
+ Resources/core/icons/record2-fill.svg
+
+ 4Z7wIBY4iM8Bx71IHoBzSLz7UuY=
+
+ Resources/core/icons/record2.svg
+
+ UzfS1MMoo5ayyXN7k8pqW8iz0fw=
+
+ Resources/core/icons/recycle.svg
+
+ LVS/EcH6ZxS6qU8cSYy3161ahy4=
+
+ Resources/core/icons/reddit.svg
+
+ 39PNaCvgUtw39ziimhmACO5yd4g=
+
+ Resources/core/icons/regex.svg
+
+ 22c9/3HljLJYZOeI4TXGyU1JOik=
+
+ Resources/core/icons/repeat-1.svg
+
+ iGJ3qapIv1WpnlklfW6SfpOvz30=
+
+ Resources/core/icons/repeat.svg
+
+ zaQe5tR169CEn8N/gBHykMZLQ7k=
+
+ Resources/core/icons/reply-all-fill.svg
+
+ Rv4gBboHHOtAHbEQ4CrWHjUns8I=
+
+ Resources/core/icons/reply-all.svg
+
+ fXszfBfr89PzAQl2C8X1cKA4nKU=
+
+ Resources/core/icons/reply-fill.svg
+
+ 3j6PAs86Y9tKpsryjM+rxab/bYY=
+
+ Resources/core/icons/reply.svg
+
+ SJ1RnEK+hwxTZTtMhXAGiNbsCtg=
+
+ Resources/core/icons/rewind-btn-fill.svg
+
+ NTAxrF8KsMiIdLQrJme5ovL9siU=
+
+ Resources/core/icons/rewind-btn.svg
+
+ 775m4wxlkpLufoy7OgpeY6r0df4=
+
+ Resources/core/icons/rewind-circle-fill.svg
+
+ fG+caFnzartJzeWWJF9FDShuRC8=
+
+ Resources/core/icons/rewind-circle.svg
+
+ s1NidBhTlzuOPQAP9X1FEr9VvYg=
+
+ Resources/core/icons/rewind-fill.svg
+
+ ouEtHGj5s+Ze3jXp8ogxUoNahuY=
+
+ Resources/core/icons/rewind.svg
+
+ BZsW3d104hRmFpdNhgm202gffsQ=
+
+ Resources/core/icons/robot.svg
+
+ YEhOVSSxoLTNY/yMRyWMW4LUZnE=
+
+ Resources/core/icons/rocket-fill.svg
+
+ nmA0MXq7SxZixu/+DVRWybBgfFs=
+
+ Resources/core/icons/rocket-takeoff-fill.svg
+
+ HeFoosYwCtYD2EkPuzU/5ylIE2A=
+
+ Resources/core/icons/rocket-takeoff.svg
+
+ NfuUQKtteBpOo32EJl7+JDbmeD0=
+
+ Resources/core/icons/rocket.svg
+
+ 1X7n/xoPvElN/ldZNy0bmNn/J3o=
+
+ Resources/core/icons/router-fill.svg
+
+ QOfIBdqohX7gj6U5diMuXCylYeU=
+
+ Resources/core/icons/router.svg
+
+ A9dMwYn6iiHdkww6ZJ6p+vsniEA=
+
+ Resources/core/icons/rss-fill.svg
+
+ jyp5IOSWB3Ha1HgTb5HFvJNbDUk=
+
+ Resources/core/icons/rss.svg
+
+ hmnt8G5vFdLAHRKBb8fmDTFXdpA=
+
+ Resources/core/icons/rulers.svg
+
+ kVQ3MKYNmbIRMbqfrsJxPInlgiU=
+
+ Resources/core/icons/safe-fill.svg
+
+ ivm4gBW03RQakj1BLtTylqOh2eU=
+
+ Resources/core/icons/safe.svg
+
+ 64YpMYOZsJFWU1riSoHjxP9vXws=
+
+ Resources/core/icons/safe2-fill.svg
+
+ Vjh9eYeqquX+k6gMuKZ9bTYIHAM=
+
+ Resources/core/icons/safe2.svg
+
+ qajHOsFbz9ckDH/izseNGwcWa30=
+
+ Resources/core/icons/save-fill.svg
+
+ RJDdBPCbph/dSk0GPXlN8MCzFjQ=
+
+ Resources/core/icons/save.svg
+
+ g67Zlcvz8l5KaIH8mR5iYfWsChc=
+
+ Resources/core/icons/save2-fill.svg
+
+ oH3ApOcYgBx6ZK2kzVX/LqI/SpY=
+
+ Resources/core/icons/save2.svg
+
+ P1uPbLZLPTZ0lW3OlSkDq0xNLig=
+
+ Resources/core/icons/scissors.svg
+
+ ZrlPSh1QLokmoaQdoNkkc8qh8Cs=
+
+ Resources/core/icons/scooter.svg
+
+ 2HdtfJZljwcO9/1t8a8rZ4rxfrs=
+
+ Resources/core/icons/screwdriver.svg
+
+ d4ATVdIodso86sHbJIuz2MPXIYY=
+
+ Resources/core/icons/sd-card-fill.svg
+
+ I8XnVKHQnRYIZECcQnToF9O2g8A=
+
+ Resources/core/icons/sd-card.svg
+
+ z9EDNTkpp78cSQv7H5FbLRvKG54=
+
+ Resources/core/icons/search-heart-fill.svg
+
+ K1u/YMMkcAxyvoBD/0aqZinLx1M=
+
+ Resources/core/icons/search-heart.svg
+
+ 4zzZ6Er8ukPUwb7vUWpRoBcjiD4=
+
+ Resources/core/icons/search.svg
+
+ kJrak3LDerHM1PvTxF54cqcjt7s=
+
+ Resources/core/icons/segmented-nav.svg
+
+ uTjoFwBt+um/da0H/IiIL+ZEOl8=
+
+ Resources/core/icons/send-arrow-down-fill.svg
+
+ JfD8kNuxnlAD8U2NxkT07fREqME=
+
+ Resources/core/icons/send-arrow-down.svg
+
+ mN363d+Zx8OWN9DzV5yQD09qPPg=
+
+ Resources/core/icons/send-arrow-up-fill.svg
+
+ 29sWbncLhMaMK0g6dG0etPS3E5c=
+
+ Resources/core/icons/send-arrow-up.svg
+
+ 1qSyQA1r3mGha+Pj/+XGkCHqRhw=
+
+ Resources/core/icons/send-check-fill.svg
+
+ P7qr9DA10ZsTa7+oz7jGtY+j7RY=
+
+ Resources/core/icons/send-check.svg
+
+ MeXOdrMy1/m1mWwgxfcfqDWcMhw=
+
+ Resources/core/icons/send-dash-fill.svg
+
+ 8vlLpjOiMER9mSz81twQdMxNFYM=
+
+ Resources/core/icons/send-dash.svg
+
+ Mnvnm3MQj7fx9BXJH0okNrbVhgg=
+
+ Resources/core/icons/send-exclamation-fill.svg
+
+ Of2i5FSCYjKULg7njD2KBETUSDw=
+
+ Resources/core/icons/send-exclamation.svg
+
+ MGRhQuqXN0MNOqcMMBrKYEunu6M=
+
+ Resources/core/icons/send-fill.svg
+
+ cXngIY6OdHW+upPO3m4Xg9c3XsU=
+
+ Resources/core/icons/send-plus-fill.svg
+
+ xQjXHIHjUm45hegR5RzXqppkXVQ=
+
+ Resources/core/icons/send-plus.svg
+
+ POM/iAALA+LndXfZmTilFt6h1UY=
+
+ Resources/core/icons/send-slash-fill.svg
+
+ 1OLBtZl1qMKk96fAbnw1a18/tFs=
+
+ Resources/core/icons/send-slash.svg
+
+ 7FYNcaV7YqGGUg7t3LZiDh36Glw=
+
+ Resources/core/icons/send-x-fill.svg
+
+ djtb4NNfKiC/azgnxUDbfJ3S1r0=
+
+ Resources/core/icons/send-x.svg
+
+ xinP/nHG2q9BLLysAGS8XB+0X1s=
+
+ Resources/core/icons/send.svg
+
+ oniiJtemznUKwc4ifdcbK84jIXk=
+
+ Resources/core/icons/server.svg
+
+ hTpc+VMSxkVnJ2dc+4K2qzTZTs8=
+
+ Resources/core/icons/shadows.svg
+
+ xsvK53tq/PqtR3nVKu/6b41E694=
+
+ Resources/core/icons/share-fill.svg
+
+ OFqWJ/13iVQnUWoEsbFqYUCAJ9A=
+
+ Resources/core/icons/share.svg
+
+ TZOkbkVrwNBb6lqh2rGlrw3n0+8=
+
+ Resources/core/icons/shield-check.svg
+
+ fXSd7OGgiJnlTU6DRzVTj4GflNM=
+
+ Resources/core/icons/shield-exclamation.svg
+
+ AbdbhjJlwxxeE/ZSJ+NnzQInX0c=
+
+ Resources/core/icons/shield-fill-check.svg
+
+ JF6grEzY9L+8PwPyRlRnHUt6UVg=
+
+ Resources/core/icons/shield-fill-exclamation.svg
+
+ ajPJhm2nWLP13btrAceol6C1u/s=
+
+ Resources/core/icons/shield-fill-minus.svg
+
+ XqrVK7ee3B7EUwEUEmo7yD6K1X8=
+
+ Resources/core/icons/shield-fill-plus.svg
+
+ 1BsBNO76mYh/AAm+GAP9pshbZwQ=
+
+ Resources/core/icons/shield-fill-x.svg
+
+ GLNTliTio3YIq7QsVD/SHjCoLpI=
+
+ Resources/core/icons/shield-fill.svg
+
+ ap28yn9hQYg0lbd9lYVrVcG/0pQ=
+
+ Resources/core/icons/shield-lock-fill.svg
+
+ 9jn9VMeNMPzpu5L/AGzP8oqW2Cw=
+
+ Resources/core/icons/shield-lock.svg
+
+ m5ShI0/M3Lqj8Ux+uIF6C1lty+Y=
+
+ Resources/core/icons/shield-minus.svg
+
+ ve3RClcW3uSF89yF6hWOBFi6Fr0=
+
+ Resources/core/icons/shield-plus.svg
+
+ cIhTDAV9DRCS+cWtUADVHGB4sZ0=
+
+ Resources/core/icons/shield-shaded.svg
+
+ rR1UcYwA5YUYjAnAb1ROX3rAGiI=
+
+ Resources/core/icons/shield-slash-fill.svg
+
+ zcw0bNYyz2DN23sbsJNynr2Thac=
+
+ Resources/core/icons/shield-slash.svg
+
+ nObv9NZQeG5HzB19LU/McFQjsh4=
+
+ Resources/core/icons/shield-x.svg
+
+ GKT81jgP6ILIAW7SqagrBx8yO3U=
+
+ Resources/core/icons/shield.svg
+
+ Y1IrlfwJxzrlv29RwY+3zQWxRzw=
+
+ Resources/core/icons/shift-fill.svg
+
+ 6pVRX3bskSv0jfQtliGLUUaw+BA=
+
+ Resources/core/icons/shift.svg
+
+ YLx60s3w7OwjbN3gHwt8QEXJCOE=
+
+ Resources/core/icons/shop-window.svg
+
+ ehuvSzamb/UyPSmougGWs/EdqhI=
+
+ Resources/core/icons/shop.svg
+
+ XjsO38UnhWAMHkxTtr6h6y9Ahns=
+
+ Resources/core/icons/shuffle.svg
+
+ iwzyxJp5H+etTxUimJBN3A6RL3A=
+
+ Resources/core/icons/sign-dead-end-fill.svg
+
+ uuKRojFWJC+Xco7U0IuhaKSTP0w=
+
+ Resources/core/icons/sign-dead-end.svg
+
+ v0OhRgCxpkONyJhOHzS8hkfaU1c=
+
+ Resources/core/icons/sign-do-not-enter-fill.svg
+
+ O2KOzavQ/ZshEvhS2dXewb2OdCE=
+
+ Resources/core/icons/sign-do-not-enter.svg
+
+ 1Vzd2G0m2VcIx1kw3dTIWV4Tsdg=
+
+ Resources/core/icons/sign-intersection-fill.svg
+
+ pAfxqIH+vHD7vEdIvC44fv0SGeY=
+
+ Resources/core/icons/sign-intersection-side-fill.svg
+
+ 4H//pNXJNiAjZ6KJNJfT7D5gTk0=
+
+ Resources/core/icons/sign-intersection-side.svg
+
+ Oq+iVYS0C2V9GvZsBeKUw4dfCKs=
+
+ Resources/core/icons/sign-intersection-t-fill.svg
+
+ mbKjqLTazW/SavCEYCWUmhtU7CI=
+
+ Resources/core/icons/sign-intersection-t.svg
+
+ QkIwvwkfXTD1gjiAsyZUOG/zBP8=
+
+ Resources/core/icons/sign-intersection-y-fill.svg
+
+ F3kbM48JaY8EHRNTbAWlPvMPhZM=
+
+ Resources/core/icons/sign-intersection-y.svg
+
+ wOPka2taqvtd9MGm+chZumDuDPo=
+
+ Resources/core/icons/sign-intersection.svg
+
+ L9ceN6Wk2eCZor5B5mYnEzT0rfw=
+
+ Resources/core/icons/sign-merge-left-fill.svg
+
+ 1DvYZK0l4ZTkK2qxuLRsjghiHXY=
+
+ Resources/core/icons/sign-merge-left.svg
+
+ nd/ScRzUEdmZXttnuz1pIz4/q+o=
+
+ Resources/core/icons/sign-merge-right-fill.svg
+
+ pSKJyONDMyq52sIs6K/OC+98GYs=
+
+ Resources/core/icons/sign-merge-right.svg
+
+ aZyMQ0Qw+rXufxfqHu/FpyXyeuM=
+
+ Resources/core/icons/sign-no-left-turn-fill.svg
+
+ +p08OcyXjkuBtUA+T15GCsdDpSY=
+
+ Resources/core/icons/sign-no-left-turn.svg
+
+ 1SwyV6uf6jHCitOOCYQ2eo6tlXs=
+
+ Resources/core/icons/sign-no-parking-fill.svg
+
+ mrl1y9VU7hOLXw+mmL4VV4xYjFE=
+
+ Resources/core/icons/sign-no-parking.svg
+
+ qiQ1GxiLgSvB0O7ACnDGXHQP2Ys=
+
+ Resources/core/icons/sign-no-right-turn-fill.svg
+
+ TVwhzHuI1iwfiLUzD7kMOGj35Co=
+
+ Resources/core/icons/sign-no-right-turn.svg
+
+ kx3OZ+mSq/08S295+MrX5KlKwcs=
+
+ Resources/core/icons/sign-railroad-fill.svg
+
+ e1GJhdE/Xrx2RGmKFzzZ6HjZqYA=
+
+ Resources/core/icons/sign-railroad.svg
+
+ 6Fd/EwEImwaBcPFOtp38V7d8Y4A=
+
+ Resources/core/icons/sign-stop-fill.svg
+
+ IzVGLkWlGZ9nhLXE+96GeO1+uhI=
+
+ Resources/core/icons/sign-stop-lights-fill.svg
+
+ LyV+sWWjDdllhZMrCrcwrr/+rfI=
+
+ Resources/core/icons/sign-stop-lights.svg
+
+ vxee9geoKePrhIEJsqTCWHyhi/g=
+
+ Resources/core/icons/sign-stop.svg
+
+ b8E1hOH4t822wSslzz/gZc8Ksrk=
+
+ Resources/core/icons/sign-turn-left-fill.svg
+
+ TmggbtNvVvYMvGh0pC3QaAaWAUY=
+
+ Resources/core/icons/sign-turn-left.svg
+
+ dlop0QivCz6QqfuIhKTYZ2XDqL4=
+
+ Resources/core/icons/sign-turn-right-fill.svg
+
+ 0jmXaq2/7iG87xySekz0Nyg+gEo=
+
+ Resources/core/icons/sign-turn-right.svg
+
+ d+ZxtNLa0sc4WhVS24hPe+p6jcU=
+
+ Resources/core/icons/sign-turn-slight-left-fill.svg
+
+ AFGnn6QJ9RL39Xe/x+bfZU7U/Rw=
+
+ Resources/core/icons/sign-turn-slight-left.svg
+
+ E1P6w8EJ5C9ibym4jfwKvBimZEI=
+
+ Resources/core/icons/sign-turn-slight-right-fill.svg
+
+ MHV+T9irR6fpTqf9Ec+tGZ0u9RA=
+
+ Resources/core/icons/sign-turn-slight-right.svg
+
+ wLkVBX/2XDOkKiHxL1vPLzkzRFY=
+
+ Resources/core/icons/sign-yield-fill.svg
+
+ UHLe0VQo8mR5ymBJPB7an+NKWKM=
+
+ Resources/core/icons/sign-yield.svg
+
+ rpfyKhNSSy6/12GBi4msh/5elhA=
+
+ Resources/core/icons/signal.svg
+
+ 8YIS2FL02JD6GqxPHnruMeHwqAw=
+
+ Resources/core/icons/signpost-2-fill.svg
+
+ uxj3lin2NsU9qedcVAeYmFhGE2k=
+
+ Resources/core/icons/signpost-2.svg
+
+ Z3nkRHjx8nxJ+9JYXLtM8qlHIQ8=
+
+ Resources/core/icons/signpost-fill.svg
+
+ F7xSpnRKA4q+PLwrZQ3e7LbFJ+c=
+
+ Resources/core/icons/signpost-split-fill.svg
+
+ gObz/x8AgGXLLUZ+q0UdG1VSE2c=
+
+ Resources/core/icons/signpost-split.svg
+
+ MG1OWMb41b3c0nOUWQta8mSvBtI=
+
+ Resources/core/icons/signpost.svg
+
+ 8MswXY4XcGQ1QPKU8xgTPIKpTxA=
+
+ Resources/core/icons/sim-fill.svg
+
+ 2RmumAwgEXeCFs7Rt/79cZWjg1k=
+
+ Resources/core/icons/sim-slash-fill.svg
+
+ 2AYpMANUyhVoPNhV4efbXFuHDsA=
+
+ Resources/core/icons/sim-slash.svg
+
+ 3FHMF+I3NUSB3yRbRw8n4MVIzbo=
+
+ Resources/core/icons/sim.svg
+
+ QgdQ54Nw5/mKnmXH7nMq3Jiq2aY=
+
+ Resources/core/icons/sina-weibo.svg
+
+ gwSSGyfI0m4Eo/HwXEaREFARBCM=
+
+ Resources/core/icons/skip-backward-btn-fill.svg
+
+ IlXcXP0o/ZtpayHwO1Ymu6mfS78=
+
+ Resources/core/icons/skip-backward-btn.svg
+
+ YleY430SntvmisegJ3CbL08iZFA=
+
+ Resources/core/icons/skip-backward-circle-fill.svg
+
+ IgNHwJGH3hnC7idrTYMJsH63xzQ=
+
+ Resources/core/icons/skip-backward-circle.svg
+
+ 2ZIbvosPMp4NrOtDJaZRl/W50GU=
+
+ Resources/core/icons/skip-backward-fill.svg
+
+ IhHuSj8PY+wLkn0zFaeglfpCovk=
+
+ Resources/core/icons/skip-backward.svg
+
+ 8Z4znWeq43+rV3fFu+0N8bfRCkE=
+
+ Resources/core/icons/skip-end-btn-fill.svg
+
+ bMa0x7E18/Um9wVWfiSlYYI2eMA=
+
+ Resources/core/icons/skip-end-btn.svg
+
+ 8N8iRa3TMiJG5oQoEHZMz0kYai8=
+
+ Resources/core/icons/skip-end-circle-fill.svg
+
+ XMrFnrDjuFYcZNUD4BmLzZcLRVQ=
+
+ Resources/core/icons/skip-end-circle.svg
+
+ An+K0D1g59w9pQy0qqaCNCxL0es=
+
+ Resources/core/icons/skip-end-fill.svg
+
+ RRNoKzy4KGwwQT/y1ihdCw4YBvo=
+
+ Resources/core/icons/skip-end.svg
+
+ uvEmyDLvWs3EuoPrXjC+mNtqvD8=
+
+ Resources/core/icons/skip-forward-btn-fill.svg
+
+ KHVI9gYyTNomFV4Qm6/J1Ad/jpg=
+
+ Resources/core/icons/skip-forward-btn.svg
+
+ HOc4yKLPah7kstyIlQa+zOVfb4o=
+
+ Resources/core/icons/skip-forward-circle-fill.svg
+
+ 8pb+tfpkG4KuzOExYNJhBzQ2xlo=
+
+ Resources/core/icons/skip-forward-circle.svg
+
+ iGJSyX4bx1DRRXlmioEQ1dpiBRE=
+
+ Resources/core/icons/skip-forward-fill.svg
+
+ pnhWQmhILluHFYUr2JD+pFd8UZ4=
+
+ Resources/core/icons/skip-forward.svg
+
+ ND7OX9L7ZStmrMYHLhs3+n4CuMk=
+
+ Resources/core/icons/skip-start-btn-fill.svg
+
+ 888cNZ7PFAse/4tdZhUQujcUKxQ=
+
+ Resources/core/icons/skip-start-btn.svg
+
+ s/8/upzyWkt/O09BLXP9TvF+Ivo=
+
+ Resources/core/icons/skip-start-circle-fill.svg
+
+ M3TlCGKwWyAoLbq4sxjye8bCo6c=
+
+ Resources/core/icons/skip-start-circle.svg
+
+ ZLFk41NlqbSDi+cMzU2OMbAcitU=
+
+ Resources/core/icons/skip-start-fill.svg
+
+ MVvR8WlqYHH4+kbnuOXvhi53RvM=
+
+ Resources/core/icons/skip-start.svg
+
+ +O5mQJcn7AQu8891Sv2+aVODJl0=
+
+ Resources/core/icons/skype.svg
+
+ 4YKeIT5AeJZpeW6Hv7y5MEmDE04=
+
+ Resources/core/icons/slack.svg
+
+ Tfkw3fR0NXInqUzW+Eij4nVT+ik=
+
+ Resources/core/icons/slash-circle-fill.svg
+
+ RslYbZWf98TwTWTt/FSmxkoC9hw=
+
+ Resources/core/icons/slash-circle.svg
+
+ o9dQj8n8cdRrqEzC09DX85rPFps=
+
+ Resources/core/icons/slash-lg.svg
+
+ uCjfAUwSU4NDJCy6zofj1foZhzc=
+
+ Resources/core/icons/slash-square-fill.svg
+
+ kLRJqXp1HIIiLaO/pUa29YsLa2Y=
+
+ Resources/core/icons/slash-square.svg
+
+ MPpi5Zf40ggpZ6DsgL9duHEkJoQ=
+
+ Resources/core/icons/slash.svg
+
+ h0tABcoZIV+CI3z+3QQvqCqc/VA=
+
+ Resources/core/icons/sliders.svg
+
+ 9XAXsbwZi76qdJbY07+PzUHrfYY=
+
+ Resources/core/icons/sliders2-vertical.svg
+
+ HzK/yyD+JG0L1R+9y8LG1avX4JE=
+
+ Resources/core/icons/sliders2.svg
+
+ WvCbH69tqEhdzpr4LwhzMZGUNyk=
+
+ Resources/core/icons/smartwatch.svg
+
+ oG6H2CudydD598WzIC2EgnEhZro=
+
+ Resources/core/icons/snapchat.svg
+
+ Yv0M3j5qZki8zDugzEY5VcTwy3Y=
+
+ Resources/core/icons/snow.svg
+
+ 0f5LdOnWF9GvDcie8IwY3jjycoE=
+
+ Resources/core/icons/snow2.svg
+
+ Xe7uUkTvXnXLXfbPX4a+WjqFFjQ=
+
+ Resources/core/icons/snow3.svg
+
+ IglZ8AMk8CBQlBw5hYiN/VWVYFQ=
+
+ Resources/core/icons/sort-alpha-down-alt.svg
+
+ YqDepZqgB/SXKOasvY+XCq554jM=
+
+ Resources/core/icons/sort-alpha-down.svg
+
+ 17edOzajo9ybhNg6w7w+3JD9SjE=
+
+ Resources/core/icons/sort-alpha-up-alt.svg
+
+ OFRyP8RdO94XX2EeUj2tUpb7La0=
+
+ Resources/core/icons/sort-alpha-up.svg
+
+ Dv+HLFFWDcAwX1cfzD7Zyt8M69w=
+
+ Resources/core/icons/sort-down-alt.svg
+
+ 1jRxnOfBPQubl/a22ThWGsCBTCE=
+
+ Resources/core/icons/sort-down.svg
+
+ 6ekJHsLffreFdSYggRnzMDKNOIs=
+
+ Resources/core/icons/sort-numeric-down-alt.svg
+
+ yC50JjDwJQ8ThxD+UDAY1f/GQYQ=
+
+ Resources/core/icons/sort-numeric-down.svg
+
+ lO+pVORaSB7ipyAEfUxdecuV9h0=
+
+ Resources/core/icons/sort-numeric-up-alt.svg
+
+ szzZbW6si4y4YwL+ypQITpSAqiU=
+
+ Resources/core/icons/sort-numeric-up.svg
+
+ 8RMYw69cJ10F/ITWj3dGYELJn/I=
+
+ Resources/core/icons/sort-up-alt.svg
+
+ 8m4hMV/ByZOeT6GiolFtMI2uXoI=
+
+ Resources/core/icons/sort-up.svg
+
+ yT+rCamgrAGX356KCAh34gwV3m4=
+
+ Resources/core/icons/soundwave.svg
+
+ sW8fu/UfbiCATHnw6ezINSZQ0oY=
+
+ Resources/core/icons/sourceforge.svg
+
+ EIVqu+yLWdMPWoOj7y9Ia+usxX8=
+
+ Resources/core/icons/speaker-fill.svg
+
+ kQlze/anJToSv2j3I6WauzKsu9I=
+
+ Resources/core/icons/speaker.svg
+
+ 4XIB1MLW4nn8lFEHT4F7NtgQ1Hc=
+
+ Resources/core/icons/speedometer.svg
+
+ r+NO/CJeJVLM/FPMElHZvTLWnOQ=
+
+ Resources/core/icons/speedometer2.svg
+
+ 51alQhimKgdv4uKN3ok0b0Lz+9w=
+
+ Resources/core/icons/spellcheck.svg
+
+ k6ODJYiaXPPUf3oaVDHBcYK3nCc=
+
+ Resources/core/icons/spinner.svg
+
+ RV9O255pHr8J3PWX3rcpu/GimN0=
+
+ Resources/core/icons/spotify.svg
+
+ ZEIqSCESWDJeXSkTHeAVy2UDSYs=
+
+ Resources/core/icons/square-fill.svg
+
+ 3nIWeR8kfLyDWrtW6eZmYES/MSI=
+
+ Resources/core/icons/square-half.svg
+
+ nbrt41WLGZDyrqSTTNP7i0hahCg=
+
+ Resources/core/icons/square.svg
+
+ l7gmVkd1nBuvDxbUAyRLojTnIH8=
+
+ Resources/core/icons/stack-overflow.svg
+
+ XhN+B0PmZqChryf8UsEwWo/WWpY=
+
+ Resources/core/icons/stack.svg
+
+ nW8EMUJymdwn7iEF8bNgAp/GJCA=
+
+ Resources/core/icons/star-fill.svg
+
+ OeVARN1mqBKXDeok5CYeuBAB5H0=
+
+ Resources/core/icons/star-half.svg
+
+ 3wniLAE2wkix7Vlsj06I7Bp/vOg=
+
+ Resources/core/icons/star.svg
+
+ rOM+X5Va7m25QIaBjXLUem/QB3Y=
+
+ Resources/core/icons/stars.svg
+
+ bg6a6nUNyE14iVzdY7WRtTIIpbI=
+
+ Resources/core/icons/steam.svg
+
+ atvTqZJ0gE0B69AJAvZdEnKL+w4=
+
+ Resources/core/icons/stickies-fill.svg
+
+ 2rfu8/GRYX3cB1yRHdcgjDiuvj0=
+
+ Resources/core/icons/stickies.svg
+
+ vOiOou5QIHUWIJC5UxugZB59EwI=
+
+ Resources/core/icons/sticky-fill.svg
+
+ sdMbN7P0qL1WQHZVeENWcKLucKg=
+
+ Resources/core/icons/sticky.svg
+
+ Ka9VcIT95z+02gGGvYACeDSUDow=
+
+ Resources/core/icons/stop-btn-fill.svg
+
+ +rbKj3CQde14rt6iW4vfRORWGcY=
+
+ Resources/core/icons/stop-btn.svg
+
+ 8mCY5pqFweD72h8DYelYs2pDW2I=
+
+ Resources/core/icons/stop-circle-fill.svg
+
+ 9rK9XjDsF86IGC4PsKzAFE29PHk=
+
+ Resources/core/icons/stop-circle.svg
+
+ g9AS9S6qimcPO9GQE2b4bqWMjiE=
+
+ Resources/core/icons/stop-fill.svg
+
+ KIB6i5+NuYje+Ki9RsGQFUS5z9E=
+
+ Resources/core/icons/stop.svg
+
+ XqzRbYHRHhHJuyVroBsXTyGThCA=
+
+ Resources/core/icons/stoplights-fill.svg
+
+ VY1Hnw71RCrD2zWKpLQqVnr7ER0=
+
+ Resources/core/icons/stoplights.svg
+
+ KWjHinOSXDTXO7dZK1fViD3THcA=
+
+ Resources/core/icons/stopwatch-fill.svg
+
+ iAjR5Pn5GVba9sm3mqBiQ0z+L+w=
+
+ Resources/core/icons/stopwatch.svg
+
+ ZpyBGbpyX3bTu67hhR+rXXE2ftE=
+
+ Resources/core/icons/strava.svg
+
+ f402zNXbgGV/ntZv6ATQpywfldc=
+
+ Resources/core/icons/stripe.svg
+
+ LoSaODyoKzwHt6J2P3V1jaT6M18=
+
+ Resources/core/icons/subscript.svg
+
+ hDEhuY+8KudGlMqe4laoY8jUXUs=
+
+ Resources/core/icons/substack.svg
+
+ oGn/trdlZ/7tb7MLokjaKsOmDqI=
+
+ Resources/core/icons/subtract.svg
+
+ JgAI4t/ctw8I84lb7B6RfWW/Cvc=
+
+ Resources/core/icons/success.svg
+
+ ijGT01WFNyBl/RG3YIxetJHDQ0Y=
+
+ Resources/core/icons/suit-club-fill.svg
+
+ Wjbm4tHkq45yvYdlZRQOKEkyr1c=
+
+ Resources/core/icons/suit-club.svg
+
+ 0WfN1UNUHG3zMLFHfR6vOc3w5i4=
+
+ Resources/core/icons/suit-diamond-fill.svg
+
+ j4lJfanZpH9X1WhZK7OYJHM5UYE=
+
+ Resources/core/icons/suit-diamond.svg
+
+ hPg0MBPd8KQq0KT5V6jHB6ycZkc=
+
+ Resources/core/icons/suit-heart-fill.svg
+
+ QrRo+/GYLD5Zg2DqshythxenKQs=
+
+ Resources/core/icons/suit-heart.svg
+
+ 89K2TaH3/aI2/izduFemYKhE4zA=
+
+ Resources/core/icons/suit-spade-fill.svg
+
+ SPLv6BRMmdPiVhBKKUV901XD6e8=
+
+ Resources/core/icons/suit-spade.svg
+
+ oBUc4Us2Mj29o2S+2MpXtUa7vIE=
+
+ Resources/core/icons/suitcase-fill.svg
+
+ siA/0nob2mBbv9r8GtlRMxcnJtg=
+
+ Resources/core/icons/suitcase-lg-fill.svg
+
+ eLq0+0rTcLmfmSB7i3owIbTN+sU=
+
+ Resources/core/icons/suitcase-lg.svg
+
+ gT0GPYi/VUjxMBa4uZYWCnhbAhw=
+
+ Resources/core/icons/suitcase.svg
+
+ DbweZmkE9Z0Fsv4TtQhlmx4aKwo=
+
+ Resources/core/icons/suitcase2-fill.svg
+
+ MjG2PLyhVgwyW7AW4Frb21Kij68=
+
+ Resources/core/icons/suitcase2.svg
+
+ 4XmBDM/dcy0Th76nYRzwjafNfYA=
+
+ Resources/core/icons/sun-fill.svg
+
+ U3mvcJeWGpGAQToCJ7Zs8ecr5cw=
+
+ Resources/core/icons/sun.svg
+
+ oRMwan0wejUxmksCrLmkPsKvBI4=
+
+ Resources/core/icons/sunglasses.svg
+
+ nZ/s9nHmyv6mbxMjqvpGbbemIl8=
+
+ Resources/core/icons/sunrise-fill.svg
+
+ hdhMG1GzvP7J3JhHrXDFtCiXK3U=
+
+ Resources/core/icons/sunrise.svg
+
+ w3CwVzVE/46Ux0TifHFxZ6ZyOtk=
+
+ Resources/core/icons/sunset-fill.svg
+
+ wpQDuKYkM1hUrmrEMcKC8/4jojA=
+
+ Resources/core/icons/sunset.svg
+
+ U+S8+Ni+NxiZENOQWy9lrzgjG7o=
+
+ Resources/core/icons/superscript.svg
+
+ ejjplD+5D0m/oeM304ZyfFoL3zo=
+
+ Resources/core/icons/symmetry-horizontal.svg
+
+ VbrmyTxVx4m+taL1g9FB2ZMcbps=
+
+ Resources/core/icons/symmetry-vertical.svg
+
+ i/356RKJ7ZTVhJGOm5VUye4YA9g=
+
+ Resources/core/icons/table.svg
+
+ 2cK5B+MdNnrt+ywTwdsxp3k06Ss=
+
+ Resources/core/icons/tablet-fill.svg
+
+ cObYk4O7U87LpRaiLIctwHMWugw=
+
+ Resources/core/icons/tablet-landscape-fill.svg
+
+ Qc0iGHcVK2GpVX/OMnI5q4oakcc=
+
+ Resources/core/icons/tablet-landscape.svg
+
+ +CvnT8Pr2IGeJPRpaiExdSCZMR0=
+
+ Resources/core/icons/tablet.svg
+
+ HkFwozJBMnCCFbVwts7o5QcWWnw=
+
+ Resources/core/icons/tag-fill.svg
+
+ zsDNDayyP372w72MPxqnRPRLSmo=
+
+ Resources/core/icons/tag.svg
+
+ zmmvpWui1QLgcy2tXscLbZqMMCo=
+
+ Resources/core/icons/tags-fill.svg
+
+ Uh1LTzHZkU9j6hRGt1K1B2G0MYA=
+
+ Resources/core/icons/tags.svg
+
+ DZFmZyT0ZS8KDDhSTPtNvU77Vo4=
+
+ Resources/core/icons/taxi-front-fill.svg
+
+ IgxNOxvBiKJGShA6IcyASLddATw=
+
+ Resources/core/icons/taxi-front.svg
+
+ socuElKWoZenU3QPdkudn8Ijq/s=
+
+ Resources/core/icons/telegram.svg
+
+ DZ5gj6X0Ij0IzTWMXgD99gUM2UM=
+
+ Resources/core/icons/telephone-fill.svg
+
+ 8MZz9VcFF1ALICCEu40jmlvsExM=
+
+ Resources/core/icons/telephone-forward-fill.svg
+
+ 40a9i9KN5gG0br33/kHS/+oRmIw=
+
+ Resources/core/icons/telephone-forward.svg
+
+ peMVqv7l5+ZMrTpdCLv8t9E1s8A=
+
+ Resources/core/icons/telephone-inbound-fill.svg
+
+ XDhMpiSXGYPQBwEp4lxtsROp1qQ=
+
+ Resources/core/icons/telephone-inbound.svg
+
+ QK40X61MjknMh6hbQdx7OLbVtR0=
+
+ Resources/core/icons/telephone-minus-fill.svg
+
+ Vqlj8LuLTi+F16DT477TyqZ6vz0=
+
+ Resources/core/icons/telephone-minus.svg
+
+ qesTQHpvMLBXq1eSEfIg70hp/zE=
+
+ Resources/core/icons/telephone-outbound-fill.svg
+
+ EqAVA0gvd3ErUPGGTuPcDdIstDY=
+
+ Resources/core/icons/telephone-outbound.svg
+
+ lFAXDRyW624iF9fMdzhhRVR/KhA=
+
+ Resources/core/icons/telephone-plus-fill.svg
+
+ PCK0HbGclGVhfnuTU/o5lPEK4Xk=
+
+ Resources/core/icons/telephone-plus.svg
+
+ XdLmSEzpnRB7Y6cGac/t7sfeoco=
+
+ Resources/core/icons/telephone-x-fill.svg
+
+ 3Akd7FmIHe8XVyfmkGtn7utkK+8=
+
+ Resources/core/icons/telephone-x.svg
+
+ iTlCM6skJFsdK+d2+8VgAZmRSc8=
+
+ Resources/core/icons/telephone.svg
+
+ 932aQYxC5MP7l4RJ4bn24SPDtbI=
+
+ Resources/core/icons/tencent-qq.svg
+
+ i42tnyNxKvNkqytxCyjnBZC4/sk=
+
+ Resources/core/icons/terminal-dash.svg
+
+ agS5FwNjrC3kMwE42GL07C0UlF4=
+
+ Resources/core/icons/terminal-fill.svg
+
+ 1QSQtnWJL5Sin0VMZgH0wBtZxf4=
+
+ Resources/core/icons/terminal-plus.svg
+
+ vqC0Xj8J4oUHr4tXYMLM/ql67a8=
+
+ Resources/core/icons/terminal-split.svg
+
+ YhFY4wtHHMs9kEkdIwvHdi/RWYg=
+
+ Resources/core/icons/terminal-x.svg
+
+ IMxc2zX1C2LFAf5tr0EpJoDdOhY=
+
+ Resources/core/icons/terminal.svg
+
+ bHe3ZFsJSB2mISiu3on1tczxcUc=
+
+ Resources/core/icons/text-center.svg
+
+ 41XgpG4xHEz9btV1T+Dxtpn9YkA=
+
+ Resources/core/icons/text-indent-left.svg
+
+ qGBN4SP7CONU351BAlUjKUNDCgs=
+
+ Resources/core/icons/text-indent-right.svg
+
+ a6KuQ5VjAXchC1Y0kRn6bGCusfY=
+
+ Resources/core/icons/text-left.svg
+
+ /vlvRV6B+eXMYbQkRNhkaD3P+Wk=
+
+ Resources/core/icons/text-paragraph.svg
+
+ N+d+EHWWMdIsxuhKNA92rNRuTDk=
+
+ Resources/core/icons/text-right.svg
+
+ 9PfiGKdwUeOFkZP58Qr+ExoXmp4=
+
+ Resources/core/icons/text-wrap.svg
+
+ gnleaXPlvirQDzvO1FbHLkxKy58=
+
+ Resources/core/icons/textarea-resize.svg
+
+ OmbPI8jU6RS2S5Io2OTlpwIeqyc=
+
+ Resources/core/icons/textarea-t.svg
+
+ CDZkcmlnXw1nM6wFMORBPrkkiA4=
+
+ Resources/core/icons/textarea.svg
+
+ ShaWoletdI89NMCHu+LnXREy9ms=
+
+ Resources/core/icons/thermometer-half.svg
+
+ /4HdDwK76PYWFvvAD4XyC9zEutw=
+
+ Resources/core/icons/thermometer-high.svg
+
+ OwFko2vBnkMmka5FO6KEn68jnq0=
+
+ Resources/core/icons/thermometer-low.svg
+
+ QRG44fdwEK1zJfye9Up7t6kSHrw=
+
+ Resources/core/icons/thermometer-snow.svg
+
+ Ub5r02nC2X6qLjyrsu6ogmzfzcI=
+
+ Resources/core/icons/thermometer-sun.svg
+
+ 4qnpCJQ52QLEWYShGGZd4CdRoCg=
+
+ Resources/core/icons/thermometer.svg
+
+ uUEgWNhWva9DLWLCKOzYkh7ygk0=
+
+ Resources/core/icons/threads-fill.svg
+
+ Np0Sy+MDnFRX8/YWh1O/PLCFSqM=
+
+ Resources/core/icons/threads.svg
+
+ /z/2h6HIFDPZgWL7mT+SG5QpsiI=
+
+ Resources/core/icons/three-dots-vertical.svg
+
+ ZUfKZ8j9ifaMIVBpwYTmVKjRwp8=
+
+ Resources/core/icons/three-dots.svg
+
+ eePk57EQTv6SPob9HmHk3FEFu+g=
+
+ Resources/core/icons/thunderbolt-fill.svg
+
+ UW/5of2c1Cb2opxIzGkfNQB6Zjk=
+
+ Resources/core/icons/thunderbolt.svg
+
+ bswl6qmbCr2J6sPyyDqVVZZUqjQ=
+
+ Resources/core/icons/ticket-detailed-fill.svg
+
+ MlZ72iBYFcrXcx4Jyoq60BUHoRU=
+
+ Resources/core/icons/ticket-detailed.svg
+
+ SvwFAjnw3WfUm12ld5k+SQXeqsU=
+
+ Resources/core/icons/ticket-fill.svg
+
+ NThKvnGZeMT89kjx1mXdsH1sDiA=
+
+ Resources/core/icons/ticket-perforated-fill.svg
+
+ IAk9LDk/axICHQy0Au2b49nJijc=
+
+ Resources/core/icons/ticket-perforated.svg
+
+ BenkbYHcQR3jheEFzDFS+YGf5Ds=
+
+ Resources/core/icons/ticket.svg
+
+ 3RJF+Ba3wNUfTuE16Vbgxt89oac=
+
+ Resources/core/icons/tiktok.svg
+
+ yDJ7ASCh8yKZeQPKJ1q16hpy4Cg=
+
+ Resources/core/icons/toggle-off.svg
+
+ W/V7W06Mnl/8DfFpVpwWDt8gPHM=
+
+ Resources/core/icons/toggle-on.svg
+
+ PDSx3ffgXMwrey2i7L/vehjw01c=
+
+ Resources/core/icons/toggle2-off.svg
+
+ DvIN/Hp98cP4pEKXlzeg1NFfgzQ=
+
+ Resources/core/icons/toggle2-on.svg
+
+ PvvAihrlc0cve3M2O5AcEvnBXQQ=
+
+ Resources/core/icons/toggles.svg
+
+ 88ZCjTiYJy01/pCm8alMHpd0bsU=
+
+ Resources/core/icons/toggles2.svg
+
+ 0SzyYHNKXqy2gonQ0w3lYtPLfC0=
+
+ Resources/core/icons/tools.svg
+
+ 7WxX5Er9Xf4JEUFBIMe8WZNt4G8=
+
+ Resources/core/icons/tornado.svg
+
+ /gJ1+MDpBJzMPUeSmgEXazJGNnA=
+
+ Resources/core/icons/train-freight-front-fill.svg
+
+ iWt0PiIHPe0QebsMQZ7iu/Lh+EA=
+
+ Resources/core/icons/train-freight-front.svg
+
+ yZxS97dYPTnBZFZZ/l/1Yu4QI+I=
+
+ Resources/core/icons/train-front-fill.svg
+
+ pN+dXK+MH7rpRPAxF21OgFBahek=
+
+ Resources/core/icons/train-front.svg
+
+ mX04rUCvkm4Gi1rNfIhEPdiD4sc=
+
+ Resources/core/icons/train-lightrail-front-fill.svg
+
+ G2V+EhR4iFDeWw4El3fmVGNmv3w=
+
+ Resources/core/icons/train-lightrail-front.svg
+
+ WURvmHZzaDMy3fdIp8gvCx/cSc0=
+
+ Resources/core/icons/translate.svg
+
+ +8gA++0Uy8GUrQC3Taiguc6s8Do=
+
+ Resources/core/icons/transparency.svg
+
+ r1nq+elDv7+yO1MqxgYycghY/jQ=
+
+ Resources/core/icons/trash-fill.svg
+
+ kPHm9/65FPe6Invz7sNyle5BPuM=
+
+ Resources/core/icons/trash.svg
+
+ Oif4b0u4IBtnyR09PCPVRIgUuNw=
+
+ Resources/core/icons/trash2-fill.svg
+
+ MJ5a2YYzMnKApYx/Y9OkPeCn9Cs=
+
+ Resources/core/icons/trash2.svg
+
+ 7ySZP06gMsPVI0SdcbFX7Jr0+3c=
+
+ Resources/core/icons/trash3-fill.svg
+
+ nowK7VxoyTXOpSZE0cl4ZPMFErE=
+
+ Resources/core/icons/trash3.svg
+
+ YxZxtEDLfY3myma2vQ2fq7AZiJo=
+
+ Resources/core/icons/tree-fill.svg
+
+ k/SSRhuQQyikCfI+KT4SVBjaw4g=
+
+ Resources/core/icons/tree.svg
+
+ QyF9wH5fOxchuZfiIZ+n9k4/dE0=
+
+ Resources/core/icons/trello.svg
+
+ WZfOcyb8hbrrkGJfV1xaxENtBHE=
+
+ Resources/core/icons/triangle-fill.svg
+
+ uV7Cwx0rvFmVjSxlTDLhU5lMMZg=
+
+ Resources/core/icons/triangle-half.svg
+
+ OhY1kaBGewrFiIrMt2KKEq2iSa8=
+
+ Resources/core/icons/triangle.svg
+
+ RDIpUbFHyGUufkG7Fm8/5s7qNe8=
+
+ Resources/core/icons/trophy-fill.svg
+
+ XHhJ7d3udN3PrWEF2VL+VZoAa6A=
+
+ Resources/core/icons/trophy.svg
+
+ XqgmQSvu0bQ7GqAPIgOT07Picfc=
+
+ Resources/core/icons/tropical-storm.svg
+
+ 7CX9uE7CvEMarHvv6jdoUsS9JMA=
+
+ Resources/core/icons/truck-flatbed.svg
+
+ JuPyM1Xlkjd8HKznVSBut0uJkp8=
+
+ Resources/core/icons/truck-front-fill.svg
+
+ vd91EeEVfwYg2IusnqCtj2g+l+M=
+
+ Resources/core/icons/truck-front.svg
+
+ rVyTbdu4XlQjEZ2/pWZiKW/BWeQ=
+
+ Resources/core/icons/truck.svg
+
+ dF7sc1s0B/b4QYLi5rfTiUxeciE=
+
+ Resources/core/icons/tsunami.svg
+
+ j2TsZWv9uvOsm5JXVOWGzYJQqDw=
+
+ Resources/core/icons/tux.svg
+
+ /o0AILbvS2nMTOe6xIDvBCuAFto=
+
+ Resources/core/icons/tv-fill.svg
+
+ WPM0T6TBRHiT9lbAmCpcvlg+97Y=
+
+ Resources/core/icons/tv.svg
+
+ xLNoONwu2JYMD1lxVhFgJTsJ0u8=
+
+ Resources/core/icons/twitch.svg
+
+ fvd0knUU/AT9Xr4beTa0R2ArcGs=
+
+ Resources/core/icons/twitter-x.svg
+
+ FIrSjQeE6iwf12cCMWNOQ/b9t6o=
+
+ Resources/core/icons/twitter.svg
+
+ RlbP0wgB1wb4lym5DzexE0Yfh90=
+
+ Resources/core/icons/type-bold.svg
+
+ Xr+JJ1up9XyNTN/TSjFZBFMavbo=
+
+ Resources/core/icons/type-h1.svg
+
+ MkS1rR8GCctdJrY9mQKbKAgpHs8=
+
+ Resources/core/icons/type-h2.svg
+
+ wcDquUrIZxB+55nlw6882iCBvRI=
+
+ Resources/core/icons/type-h3.svg
+
+ e762zWH1ml662xjnH3ielq1Mjas=
+
+ Resources/core/icons/type-h4.svg
+
+ oVduvSoGhYARc75Gv9jaKVdOMwA=
+
+ Resources/core/icons/type-h5.svg
+
+ txoAnOz52+z8dCsRUIZY7su4HSg=
+
+ Resources/core/icons/type-h6.svg
+
+ 4bRi/iOnxj45VXLJtSAfLhOXz9k=
+
+ Resources/core/icons/type-italic.svg
+
+ hpKKRLn8wSqmJmLFSPpeL8MO5a4=
+
+ Resources/core/icons/type-strikethrough.svg
+
+ hE1py5z6hoi296M71/SJv7nZhgM=
+
+ Resources/core/icons/type-underline.svg
+
+ omPHABehycoCRFTEkOEQ413DYd8=
+
+ Resources/core/icons/type.svg
+
+ lvUbua86ZGsK/Vi2K8vKfMUs8hk=
+
+ Resources/core/icons/typescript.svg
+
+ LjcXvPp+YdyHpgGbY/CxZcJKink=
+
+ Resources/core/icons/ubuntu.svg
+
+ wohNojyrFX02o6Tx5hkNJMZVbqQ=
+
+ Resources/core/icons/ui-checks-grid.svg
+
+ eZn+DeYP+J695YijUtgNwXEg7rI=
+
+ Resources/core/icons/ui-checks.svg
+
+ GfNLNLEo+XAuf/eQPz4TIFGlG5I=
+
+ Resources/core/icons/ui-radios-grid.svg
+
+ /hCHm1uyJywTs6AdJ4/8NQmmRP4=
+
+ Resources/core/icons/ui-radios.svg
+
+ Zb4oqUUc0aCdGaem3ivqjmM6/8M=
+
+ Resources/core/icons/umbrella-fill.svg
+
+ llLCw5WzPEgHRqubN+lWgE2Qpvc=
+
+ Resources/core/icons/umbrella.svg
+
+ CG9gfi0s7gwajqYzWmSztavlAhc=
+
+ Resources/core/icons/unindent.svg
+
+ 1q6kuW9sQ8cYhRBaxXx+IMq5nfQ=
+
+ Resources/core/icons/union.svg
+
+ 7DlWJT+ebxNwen0kPPmvvr/LGvc=
+
+ Resources/core/icons/unity.svg
+
+ +TJXLz9F9JtFxXsWMHoGpo68PaI=
+
+ Resources/core/icons/universal-access-circle.svg
+
+ 7ONB5dyME30LabAs1lyxqsdId8A=
+
+ Resources/core/icons/universal-access.svg
+
+ 8jmul/eu36Khj9RzYNy0QNB6r+Q=
+
+ Resources/core/icons/unlock-fill.svg
+
+ /0E8rTvNTRO/4+1FAtYeZ0Vfw24=
+
+ Resources/core/icons/unlock.svg
+
+ BcXyTkfq202K2nOquvTO+ADMWqs=
+
+ Resources/core/icons/unlock2-fill.svg
+
+ oy/EUD6n6BszTNw8XwxMqVCwNsU=
+
+ Resources/core/icons/unlock2.svg
+
+ 9fGP6VosOnSTxbHMl+52ZtKq2Ww=
+
+ Resources/core/icons/upc-scan.svg
+
+ o30h9sVL8RXKkP5iP+YpdqGyjsw=
+
+ Resources/core/icons/upc.svg
+
+ eZEDQds/DlO3sIKYrGcvCGMeYNI=
+
+ Resources/core/icons/upload.svg
+
+ MNgcmVdBicn4ntHDNuKTbTyXbj0=
+
+ Resources/core/icons/usb-c-fill.svg
+
+ oCRkB+XvgkiNjAavVHbCbCkMhok=
+
+ Resources/core/icons/usb-c.svg
+
+ mBIAyx/F6horI3BuP7GxPqCH8ps=
+
+ Resources/core/icons/usb-drive-fill.svg
+
+ jlnWDhdGdAEARf9ptX/ULzpXuhY=
+
+ Resources/core/icons/usb-drive.svg
+
+ N2jQoExbDclS0Ihslecd9i6Ezcg=
+
+ Resources/core/icons/usb-fill.svg
+
+ HP4sljQueLt9JAprNZ5a6q/Rqy0=
+
+ Resources/core/icons/usb-micro-fill.svg
+
+ u5cSjlG9TQiBdARISlZXbHNZvPs=
+
+ Resources/core/icons/usb-micro.svg
+
+ hkWA8Fk0eDrwUrVSOcOW5MRDD7k=
+
+ Resources/core/icons/usb-mini-fill.svg
+
+ mZv+4tZyPoS6PiQrTsUBEhVUt90=
+
+ Resources/core/icons/usb-mini.svg
+
+ 5jUfqEfiu7Q/7Q+7k7WzPOL7IGc=
+
+ Resources/core/icons/usb-plug-fill.svg
+
+ Jz+ZmpULrhl1b4cSQkozld3bcBE=
+
+ Resources/core/icons/usb-plug.svg
+
+ mAk6djEDMqBDbx8oD1OtJ4JL0zk=
+
+ Resources/core/icons/usb-symbol.svg
+
+ p3wDzmEpC9q399Cq9P4LYF4fN0A=
+
+ Resources/core/icons/usb.svg
+
+ Z4oNYd/y8Pm68lFM9stKNCdR7C8=
+
+ Resources/core/icons/valentine.svg
+
+ AHcrSDi7jxmogEBqwALTfPL7Ne0=
+
+ Resources/core/icons/valentine2.svg
+
+ KWm7tUPRRss7bnQLfBrk+UNNDVw=
+
+ Resources/core/icons/vector-pen.svg
+
+ kcKrIVAMU+gaCnPA7niAFzG6Cyo=
+
+ Resources/core/icons/view-list.svg
+
+ ahSBJ8uwEWqZUxy38hxH2zxXxK0=
+
+ Resources/core/icons/view-stacked.svg
+
+ DVkzae5llHtsMvJsGJj+dqGnWhU=
+
+ Resources/core/icons/vignette.svg
+
+ IYx7+iBRL/eoSn0o+lsZyWjlRM0=
+
+ Resources/core/icons/vimeo.svg
+
+ 5NTTZnesjdSdvyKwNLSbPa+QJKs=
+
+ Resources/core/icons/vinyl-fill.svg
+
+ aach744DkOF/dRyfAwF/ziFLWCg=
+
+ Resources/core/icons/vinyl.svg
+
+ PEwy+2QByrdL6H4+ZGlLF48xNYQ=
+
+ Resources/core/icons/virus.svg
+
+ svzJhRHV/ql4JHABLoR55XhFzJ0=
+
+ Resources/core/icons/virus2.svg
+
+ lxFXHMAmxupxlATj9H8DYk6DMD8=
+
+ Resources/core/icons/voicemail.svg
+
+ AvuuUEjNqkp8IXDT4TScEz2H0nY=
+
+ Resources/core/icons/volume-down-fill.svg
+
+ Agwoex4c0tuKPDvVjkWVBibGhAo=
+
+ Resources/core/icons/volume-down.svg
+
+ mhD041DTzPEJFZpRtMI14cE9E4U=
+
+ Resources/core/icons/volume-mute-fill.svg
+
+ n6Uy6p2269UQgnka4Ziddsb8vKY=
+
+ Resources/core/icons/volume-mute.svg
+
+ 8x/UEDahkgf0fZZI66aFaavT+Tc=
+
+ Resources/core/icons/volume-off-fill.svg
+
+ vtRYYYt0D3T1gruU3rTGCWuSTGQ=
+
+ Resources/core/icons/volume-off.svg
+
+ zsGDyVe6bgukLKQW784jEaD0ziY=
+
+ Resources/core/icons/volume-up-fill.svg
+
+ 5QS55k6rhRanGJ8bZr3J8cfPnco=
+
+ Resources/core/icons/volume-up.svg
+
+ KIdWiWUBH8VmoCh3loCjQAulCac=
+
+ Resources/core/icons/vr.svg
+
+ h+ux1rNjjp06GRAPfKKpXanIXxU=
+
+ Resources/core/icons/wallet-fill.svg
+
+ BZTus0610STisAw10CHsn7gO03U=
+
+ Resources/core/icons/wallet.svg
+
+ Cb+fbZi/BbUGityX3JiSrUQ/D7Q=
+
+ Resources/core/icons/wallet2.svg
+
+ xnBmJaqAAPSxrSNCMUXO6ALtkbM=
+
+ Resources/core/icons/warning.svg
+
+ H8fFp9OH57BGObxiJoDWo0gRYGo=
+
+ Resources/core/icons/watch.svg
+
+ RjxDimHBJSqAtwL+3vkyugThNec=
+
+ Resources/core/icons/water.svg
+
+ SRZ0osWFQLOYATwgyhXiGBErG/U=
+
+ Resources/core/icons/webcam-fill.svg
+
+ zu+OV3oqJlyf/RKjaB4FCGSqG+w=
+
+ Resources/core/icons/webcam.svg
+
+ vu+gcQTiPsSnPw2Nmh8tuXz1biw=
+
+ Resources/core/icons/wechat.svg
+
+ 0oIV5D95yMr4hclWOUrNdqEnn+M=
+
+ Resources/core/icons/whatsapp.svg
+
+ 5We2BDbdqqMx+tvgs/o8XHe7bgM=
+
+ Resources/core/icons/wifi-1.svg
+
+ oPuUK+44d0Zv+KnwNAg75k8VrAM=
+
+ Resources/core/icons/wifi-2.svg
+
+ anmimVALk7SlTfZ6rATqUsDs+pc=
+
+ Resources/core/icons/wifi-off.svg
+
+ WqOcgb+pzKCiwu5COeoFQjx5Xec=
+
+ Resources/core/icons/wifi.svg
+
+ kHDZGJ/Hjd/QnkGgomBWq9k5lTU=
+
+ Resources/core/icons/wikipedia.svg
+
+ fnDe9tEptoVByrOLpgTE/h4GXQE=
+
+ Resources/core/icons/wind.svg
+
+ F1TyctB3zUerhGqkWZC06wJl56U=
+
+ Resources/core/icons/window-dash.svg
+
+ LPzNZ5opKtbvtj6sMAxk5qxhXpU=
+
+ Resources/core/icons/window-desktop.svg
+
+ NQH5IYnDIIn7ByPX45FXsepJQMM=
+
+ Resources/core/icons/window-dock.svg
+
+ VI5O3BKSVKh6j4pg3Z8HeypP9iY=
+
+ Resources/core/icons/window-fullscreen.svg
+
+ E5gSOkE4df7CVHtcAdyOZ2PvfJ4=
+
+ Resources/core/icons/window-plus.svg
+
+ KKT9ZlMrIw1dHWt4EHf3pXUN5FA=
+
+ Resources/core/icons/window-sidebar.svg
+
+ Ai/gxJ5Ucc45yuCMoAbXekn/3JY=
+
+ Resources/core/icons/window-split.svg
+
+ RV6cZIH6zP6OjPnf+jf6Oi6lJlc=
+
+ Resources/core/icons/window-stack.svg
+
+ OlipyP9nyRRIQ2ZaJA437l0+fq0=
+
+ Resources/core/icons/window-x.svg
+
+ e0lUwf3BJUVeDoY6/mQw0v4dgrU=
+
+ Resources/core/icons/window.svg
+
+ Aj0NJv2/gThNhqLJG+XfK5Z/NZg=
+
+ Resources/core/icons/windows.svg
+
+ ZtncWh5yzzp215IALHjliLY5V3A=
+
+ Resources/core/icons/wordpress.svg
+
+ gMyPxUMErM2wglyXE1EDj5pzYnk=
+
+ Resources/core/icons/wrench-adjustable-circle-fill.svg
+
+ T5xwej+TmbqUnq3tVF1LNcHtpaQ=
+
+ Resources/core/icons/wrench-adjustable-circle.svg
+
+ LVJzz1bIgFsREVQLXImTeMxPigo=
+
+ Resources/core/icons/wrench-adjustable.svg
+
+ Wn4ColfRx4VbcEHoDZQk4KWFSec=
+
+ Resources/core/icons/wrench.svg
+
+ NoQq3Utd1nS848z1dnxlBmC8v9M=
+
+ Resources/core/icons/x-circle-fill.svg
+
+ xsvaA0iTxSC7IR1Senx30knLHug=
+
+ Resources/core/icons/x-circle.svg
+
+ znaok3J4JSzM6bMbkNuetZ24aM8=
+
+ Resources/core/icons/x-diamond-fill.svg
+
+ rUAQ/4+4+fo/EByDhlDrEz6unxw=
+
+ Resources/core/icons/x-diamond.svg
+
+ XtqLIdzemCzSYxuFBe0cnMDvoZY=
+
+ Resources/core/icons/x-lg.svg
+
+ /iLDqFWUjR5PkdvuwWe5gEbiLgc=
+
+ Resources/core/icons/x-octagon-fill.svg
+
+ qOmqXlOfiTzEgu524gvxoDE69n0=
+
+ Resources/core/icons/x-octagon.svg
+
+ Tu8HfH2HHhXbq58akNnwYbTwDLo=
+
+ Resources/core/icons/x-square-fill.svg
+
+ b/uPNx89wHmAOWrISXzMFwUNPFE=
+
+ Resources/core/icons/x-square.svg
+
+ zFizkSi6DNZHjg6klw8BKosAxnU=
+
+ Resources/core/icons/x.svg
+
+ eAKaaXn0UT7BRHHtazXazEfZN2E=
+
+ Resources/core/icons/xbox.svg
+
+ 8xzF9B7P4jkkDC9jS/XjGbB1kJs=
+
+ Resources/core/icons/yelp.svg
+
+ vLHPR/HUvAAJjxRofhOssIlVjfg=
+
+ Resources/core/icons/yin-yang.svg
+
+ QfRGuyXUA8uuxniJhO3TvhjNIRQ=
+
+ Resources/core/icons/youtube.svg
+
+ D6BzC0zqQp7nsrXjZjgAbC2Wv3I=
+
+ Resources/core/icons/zoom-in.svg
+
+ v7Y6J66/ltkXgXXRDS9qg8uQFfo=
+
+ Resources/core/icons/zoom-out.svg
+
+ 6T8wOdEwqXyon1wGP3nxMJzJb4E=
+
+ Resources/core/log.py
+
+ C8YrIBMgMjfRAZD1afE591UBOzY=
+
+ Resources/core/network/__pycache__/diagnostic.cpython-311.pyc
+
+ fhVXchMis8MxtBml+sguF43MAOw=
+
+ Resources/core/network/__pycache__/diagnostic.cpython-312.pyc
+
+ eEV/DX4mwLib8eauDrGLjLHBqS4=
+
+ Resources/core/network/__pycache__/tools.cpython-311.pyc
+
+ 2mUO9UKhQYOzRzPcfkfmCCn7VKU=
+
+ Resources/core/network/__pycache__/tools.cpython-312.pyc
+
+ eWt2RaFIHFDldApjbp2ga5FLOPY=
+
+ Resources/core/network/diagnostic.py
+
+ jcLiDzKmqK3jaR7LXBxy5d/KsRs=
+
+ Resources/core/network/tools.py
+
+ bjootV4sYY9Pta+3+GqetZH+TOs=
+
+ Resources/core/service.py
+
+ TfQRkS2pzEqi+Eyhe7R4g10mvbY=
+
+ Resources/core/src/bin/nssm/windows/x86/nssm.exe
+
+ 4ZCMqrb5OEBK+Fp98PgPh3pNnuY=
+
+ Resources/core/src/bin/nssm/windows/x86_64/nssm.exe
+
+ R8ESwjx73yrySiC9US+R/2r3a8Y=
+
+ Resources/core/styles/style.css
+
+ IRkhZG9q4R4qgXwIW+BmQoNqC50=
+
+ Resources/core/ui.py
+
+ nWXYVuUIybrpeYkSY0Pz26BFgIU=
+
+ Resources/core/update.sh
+
+ /FK0L2hD8TgmZm/H0dWwdK1+JaE=
+
+ Resources/icon.icns
+
+ BzKKBxC+CzqBFuCZKK8Uo7IQeaE=
+
+ Resources/icons/icon.icns
+
+ BzKKBxC+CzqBFuCZKK8Uo7IQeaE=
+
+ Resources/icons/icon.png
+
+ poFazYnMTEgJBkAWjX8odyt0TaA=
+
+ Resources/icons/icon.svg
+
+ hsAVtbFJ4qB/ItbW2abydhPN+k0=
+
+ Resources/replicator/__init__.py
+
+ JzqmZlW5rxhyunqsitH5Q1GZCik=
+
+ Resources/replicator/__pycache__/__init__.cpython-311.pyc
+
+ b8QLu3agshhtsFHcljqWCO5ree4=
+
+ Resources/replicator/__pycache__/__init__.cpython-312.pyc
+
+ bUdlYyuAhMBfeuydL3vxtP1lLuw=
+
+ Resources/replicator/__pycache__/job.cpython-311.pyc
+
+ Nh/jsQTyuAHwGbEikeETBoNX+4Y=
+
+ Resources/replicator/__pycache__/job.cpython-312.pyc
+
+ UkQqyeeQc1tBMPNCYwh+zPjLzlk=
+
+ Resources/replicator/__pycache__/migration.cpython-311.pyc
+
+ Ku9d+KJVAH5XScB5wpS0Y2MiCHA=
+
+ Resources/replicator/__pycache__/migration.cpython-312.pyc
+
+ +FuveDiUH7Ey1Pgk/fTVqS5CIPY=
+
+ Resources/replicator/__pycache__/mount.cpython-311.pyc
+
+ XQSwt/VJOu+mUa/E+ZTD9WcH9DE=
+
+ Resources/replicator/__pycache__/replicator.cpython-311.pyc
+
+ Uvr4Q7B5Iy3e+uZoJSD7Ej5nY/U=
+
+ Resources/replicator/__pycache__/replicator.cpython-312.pyc
+
+ WIoYYlfOMRST5tq1j6g0V+PNGOA=
+
+ Resources/replicator/__pycache__/ui.cpython-311.pyc
+
+ 4RkS9ItXSdgYWXPqRzXdWegh348=
+
+ Resources/replicator/__pycache__/ui.cpython-312.pyc
+
+ wswzQpgQk5MxUQR9OHw/ksuxNbQ=
+
+ Resources/replicator/job.py
+
+ zNx1kBktxANmubeLd2eY026TliU=
+
+ Resources/replicator/migration.py
+
+ E9zz2FzkYuxbIELfbjjDEdy5lVo=
+
+ Resources/replicator/mount.py
+
+ dPKq+1iWr8lrxivCaND7BI0FcQE=
+
+ Resources/replicator/replicator.py
+
+ 9nY6T1KzMgUvjAIzBxfeaK5X1Vs=
+
+ Resources/replicator/ui.py
+
+ zV3CIrON2KKUc58GCta2DmSFhlA=
+
+
+ files2
+
+ Frameworks/PyQt5/Qt5/lib/QtCore.framework
+
+ cdhash
+
+ 6eaCwyZXpQpkftm6en8H8kjoeDU=
+
+ requirement
+ cdhash H"e9e682c32657a50a647ed9ba7a7f07f248e87835"
+
+ Frameworks/PyQt5/Qt5/lib/QtDBus.framework
+
+ cdhash
+
+ 4PmbAraz6Z74Y1w0oxa2ZUByabM=
+
+ requirement
+ cdhash H"e0f99b02b6b3e99ef8635c34a316b665407269b3"
+
+ Frameworks/PyQt5/Qt5/lib/QtGui.framework
+
+ cdhash
+
+ dpyF3eiSDuwVZyw8n/mqqUuSrSM=
+
+ requirement
+ cdhash H"769c85dde8920eec15672c3c9ff9aaa94b92ad23"
+
+ Frameworks/PyQt5/Qt5/lib/QtNetwork.framework
+
+ cdhash
+
+ Nz2RYTKj/FhsWOYKER5kaXXkHUs=
+
+ requirement
+ cdhash H"373d916132a3fc586c58e60a111e646975e41d4b"
+
+ Frameworks/PyQt5/Qt5/lib/QtPrintSupport.framework
+
+ cdhash
+
+ BWu2O2z1lAvIjg4Hd/PgGUki8R4=
+
+ requirement
+ cdhash H"056bb63b6cf5940bc88e0e0777f3e0194922f11e"
+
+ Frameworks/PyQt5/Qt5/lib/QtQml.framework
+
+ cdhash
+
+ ifgZcYuvyH62XzD8jWt00V33mvw=
+
+ requirement
+ cdhash H"89f819718bafc87eb65f30fc8d6b74d15df79afc"
+
+ Frameworks/PyQt5/Qt5/lib/QtQmlModels.framework
+
+ cdhash
+
+ 9niWvyMAlxDpOiyXJx1Xs0qyoso=
+
+ requirement
+ cdhash H"f67896bf23009710e93a2c97271d57b34ab2a2ca"
+
+ Frameworks/PyQt5/Qt5/lib/QtQuick.framework
+
+ cdhash
+
+ fvAj5AXoLtL3fjUNZkyFlAvjfms=
+
+ requirement
+ cdhash H"7ef023e405e82ed2f77e350d664c85940be37e6b"
+
+ Frameworks/PyQt5/Qt5/lib/QtSvg.framework
+
+ cdhash
+
+ Oh5Fvh6j+CBI50K8L0FMSWg683s=
+
+ requirement
+ cdhash H"3a1e45be1ea3f82048e742bc2f414c49683af37b"
+
+ Frameworks/PyQt5/Qt5/lib/QtWebSockets.framework
+
+ cdhash
+
+ +2uE+74GTYcSxLnGCbusA2P+FDk=
+
+ requirement
+ cdhash H"fb6b84fbbe064d8712c4b9c609bbac0363fe1439"
+
+ Frameworks/PyQt5/Qt5/lib/QtWidgets.framework
+
+ cdhash
+
+ fmaqAq5NJO64+/juY4XiPcN9cOo=
+
+ requirement
+ cdhash H"7e66aa02ae4d24eeb8fbf8ee6385e23dc37d70ea"
+
+ Frameworks/PyQt5/Qt5/plugins/generic/libqtuiotouchplugin.dylib
+
+ cdhash
+
+ 2RhBY5o3lbD6gbEBqAATG8Th/Rw=
+
+ requirement
+ cdhash H"d91841639a3795b0fa81b101a800131bc4e1fd1c"
+
+ Frameworks/PyQt5/Qt5/plugins/iconengines/libqsvgicon.dylib
+
+ cdhash
+
+ D++3TC0Wbz13A+RRNXwRumFlJGc=
+
+ requirement
+ cdhash H"0fefb74c2d166f3d7703e451357c11ba61652467"
+
+ Frameworks/PyQt5/Qt5/plugins/imageformats/libqgif.dylib
+
+ cdhash
+
+ cUH5D4tQYpGRrOxR9JtLW0OtcWY=
+
+ requirement
+ cdhash H"7141f90f8b50629191acec51f49b4b5b43ad7166"
+
+ Frameworks/PyQt5/Qt5/plugins/imageformats/libqicns.dylib
+
+ cdhash
+
+ qzBrbR2PYWIFIDaN/6cKIe7iT3A=
+
+ requirement
+ cdhash H"ab306b6d1d8f61620520368dffa70a21eee24f70"
+
+ Frameworks/PyQt5/Qt5/plugins/imageformats/libqico.dylib
+
+ cdhash
+
+ ZwjpgqjUyl+5gZt+aVWj8PUKXtA=
+
+ requirement
+ cdhash H"6708e982a8d4ca5fb9819b7e6955a3f0f50a5ed0"
+
+ Frameworks/PyQt5/Qt5/plugins/imageformats/libqjpeg.dylib
+
+ cdhash
+
+ uTxeAbYF92+tJUpYuwwEcRclgUo=
+
+ requirement
+ cdhash H"b93c5e01b605f76fad254a58bb0c04711725814a"
+
+ Frameworks/PyQt5/Qt5/plugins/imageformats/libqmacheif.dylib
+
+ cdhash
+
+ F+hEJObhZc5/avixzTMMkbPp3E4=
+
+ requirement
+ cdhash H"17e84424e6e165ce7f6af8b1cd330c91b3e9dc4e"
+
+ Frameworks/PyQt5/Qt5/plugins/imageformats/libqmacjp2.dylib
+
+ cdhash
+
+ 1e+rBmvrugYYmTVWX8eRlxDzwq0=
+
+ requirement
+ cdhash H"d5efab066bebba06189935565fc7919710f3c2ad"
+
+ Frameworks/PyQt5/Qt5/plugins/imageformats/libqsvg.dylib
+
+ cdhash
+
+ ActOB6CJmGC39Jb+AHjVyuLVsok=
+
+ requirement
+ cdhash H"01cb4e07a0899860b7f496fe0078d5cae2d5b289"
+
+ Frameworks/PyQt5/Qt5/plugins/imageformats/libqtga.dylib
+
+ cdhash
+
+ Sxlif4wfNbsb9yTePH4jMtlgEVo=
+
+ requirement
+ cdhash H"4b19627f8c1f35bb1bf724de3c7e2332d960115a"
+
+ Frameworks/PyQt5/Qt5/plugins/imageformats/libqtiff.dylib
+
+ cdhash
+
+ xWPZbYN8Et7P18nJqCZxwvSC8v8=
+
+ requirement
+ cdhash H"c563d96d837c12decfd7c9c9a82671c2f482f2ff"
+
+ Frameworks/PyQt5/Qt5/plugins/imageformats/libqwbmp.dylib
+
+ cdhash
+
+ wVid0U4jO5xfTDS44x++mMRCRiA=
+
+ requirement
+ cdhash H"c1589dd14e233b9c5f4c34b8e31fbe98c4424620"
+
+ Frameworks/PyQt5/Qt5/plugins/imageformats/libqwebp.dylib
+
+ cdhash
+
+ +7X617OdoWJBufcjvT+Qcylo6kU=
+
+ requirement
+ cdhash H"fbb5fad7b39da16241b9f723bd3f90732968ea45"
+
+ Frameworks/PyQt5/Qt5/plugins/platforms/libqcocoa.dylib
+
+ cdhash
+
+ P1s3oGV5lCdpZK+wLLo/I/F2t9U=
+
+ requirement
+ cdhash H"3f5b37a0657994276964afb02cba3f23f176b7d5"
+
+ Frameworks/PyQt5/Qt5/plugins/platforms/libqminimal.dylib
+
+ cdhash
+
+ C6k4M0F++ubal5f4uSeb8dwCpyU=
+
+ requirement
+ cdhash H"0ba93833417efae6da9797f8b9279bf1dc02a725"
+
+ Frameworks/PyQt5/Qt5/plugins/platforms/libqoffscreen.dylib
+
+ cdhash
+
+ GqNA4GyCjXph9Up2mpCOYX/2UeU=
+
+ requirement
+ cdhash H"1aa340e06c828d7a61f54a769a908e617ff651e5"
+
+ Frameworks/PyQt5/Qt5/plugins/platforms/libqwebgl.dylib
+
+ cdhash
+
+ WVKjFn3kUgQtG5YaNynMY/QHSMQ=
+
+ requirement
+ cdhash H"5952a3167de452042d1b961a3729cc63f40748c4"
+
+ Frameworks/PyQt5/Qt5/plugins/platformthemes/libqxdgdesktopportal.dylib
+
+ cdhash
+
+ nMUlHtvejCYITSGTmAnvBQqExFA=
+
+ requirement
+ cdhash H"9cc5251edbde8c26084d21939809ef050a84c450"
+
+ Frameworks/PyQt5/Qt5/plugins/styles/libqmacstyle.dylib
+
+ cdhash
+
+ fwORC0ajua0Tzl76rIth/u51uHA=
+
+ requirement
+ cdhash H"7f03910b46a3b9ad13ce5efaac8b61feee75b870"
+
+ Frameworks/PyQt5/Qt5/translations
+
+ symlink
+ ../../../Resources/PyQt5/Qt5/translations
+
+ Frameworks/PyQt5/QtCore.abi3.so
+
+ cdhash
+
+ vgF/x3xh3Yt/oy4PzgLIZtkO/mc=
+
+ requirement
+ cdhash H"be017fc77c61dd8b7fa32e0fce02c866d90efe67"
+
+ Frameworks/PyQt5/QtGui.abi3.so
+
+ cdhash
+
+ wDy4mgkOoYMUodyWpRxLbredGYQ=
+
+ requirement
+ cdhash H"c03cb89a090ea18314a1dc96a51c4b6eb79d1984"
+
+ Frameworks/PyQt5/QtSvg.abi3.so
+
+ cdhash
+
+ D6Rz4VO+iuDm8phTFx3/6Wwz5JE=
+
+ requirement
+ cdhash H"0fa473e153be8ae0e6f29853171dffe96c33e491"
+
+ Frameworks/PyQt5/QtWidgets.abi3.so
+
+ cdhash
+
+ rYWPQX/Yg75c126RBzFeBZ2iXgY=
+
+ requirement
+ cdhash H"ad858f417fd883be5cd76e9107315e059da25e06"
+
+ Frameworks/PyQt5/sip.cpython-311-darwin.so
+
+ cdhash
+
+ Xg7oTNlf1ydWgtFYovTtYUixfd4=
+
+ requirement
+ cdhash H"5e0ee84cd95fd7275682d158a2f4ed6148b17dde"
+
+ Frameworks/Python
+
+ symlink
+ Python.framework/Versions/3.11/Python
+
+ Frameworks/Python.framework
+
+ cdhash
+
+ GxCPFcP4vkxxIc/mjH6J2ZPJfRo=
+
+ requirement
+ cdhash H"1b108f15c3f8be4c7121cfe68c7e89d993c97d1a"
+
+ Frameworks/QtCore
+
+ symlink
+ PyQt5/Qt5/lib/QtCore.framework/Versions/5/QtCore
+
+ Frameworks/QtDBus
+
+ symlink
+ PyQt5/Qt5/lib/QtDBus.framework/Versions/5/QtDBus
+
+ Frameworks/QtGui
+
+ symlink
+ PyQt5/Qt5/lib/QtGui.framework/Versions/5/QtGui
+
+ Frameworks/QtNetwork
+
+ symlink
+ PyQt5/Qt5/lib/QtNetwork.framework/Versions/5/QtNetwork
+
+ Frameworks/QtPrintSupport
+
+ symlink
+ PyQt5/Qt5/lib/QtPrintSupport.framework/Versions/5/QtPrintSupport
+
+ Frameworks/QtQml
+
+ symlink
+ PyQt5/Qt5/lib/QtQml.framework/Versions/5/QtQml
+
+ Frameworks/QtQmlModels
+
+ symlink
+ PyQt5/Qt5/lib/QtQmlModels.framework/Versions/5/QtQmlModels
+
+ Frameworks/QtQuick
+
+ symlink
+ PyQt5/Qt5/lib/QtQuick.framework/Versions/5/QtQuick
+
+ Frameworks/QtSvg
+
+ symlink
+ PyQt5/Qt5/lib/QtSvg.framework/Versions/5/QtSvg
+
+ Frameworks/QtWebSockets
+
+ symlink
+ PyQt5/Qt5/lib/QtWebSockets.framework/Versions/5/QtWebSockets
+
+ Frameworks/QtWidgets
+
+ symlink
+ PyQt5/Qt5/lib/QtWidgets.framework/Versions/5/QtWidgets
+
+ Frameworks/base_library.zip
+
+ symlink
+ ../Resources/base_library.zip
+
+ Frameworks/bin
+
+ symlink
+ ../Resources/bin
+
+ Frameworks/core
+
+ symlink
+ ../Resources/core
+
+ Frameworks/icons
+
+ symlink
+ ../Resources/icons
+
+ Frameworks/libcrypto.3.dylib
+
+ cdhash
+
+ 499KuqQqoojFqZLiAKZIJHfWA4g=
+
+ requirement
+ cdhash H"e3df4abaa42aa288c5a992e200a6482477d60388"
+
+ Frameworks/liblzma.5.dylib
+
+ cdhash
+
+ idC7TO0NtChAHeq+77tLuA1pnNM=
+
+ requirement
+ cdhash H"89d0bb4ced0db428401deabeefbb4bb80d699cd3"
+
+ Frameworks/libmpdec.4.dylib
+
+ cdhash
+
+ Mk0vDwHinPNBhHA4tGEREhMWeHs=
+
+ requirement
+ cdhash H"324d2f0f01e29cf341847038b46111121316787b"
+
+ Frameworks/libsqlite3.dylib
+
+ cdhash
+
+ 0Hsq2CrcvPan30Rh+UJp13tns3s=
+
+ requirement
+ cdhash H"d07b2ad82adcbcf6a7df4461f94269d77b67b37b"
+
+ Frameworks/libssl.3.dylib
+
+ cdhash
+
+ btIgKtyB1/Wpa8Hs2nIbGK7uOtA=
+
+ requirement
+ cdhash H"6ed2202adc81d7f5a96bc1ecda721b18aeee3ad0"
+
+ Frameworks/python3.11
+
+ symlink
+ python3__dot__11
+
+ Frameworks/python3__dot__11/lib-dynload/_bisect.cpython-311-darwin.so
+
+ cdhash
+
+ Bx/UA6pTuYXCFhf8XfP2QuFSc9g=
+
+ requirement
+ cdhash H"071fd403aa53b985c21617fc5df3f642e15273d8"
+
+ Frameworks/python3__dot__11/lib-dynload/_blake2.cpython-311-darwin.so
+
+ cdhash
+
+ 4RncA5422fqG/O5KkFwX6Ha/dRY=
+
+ requirement
+ cdhash H"e119dc039e36d9fa86fcee4a905c17e876bf7516"
+
+ Frameworks/python3__dot__11/lib-dynload/_bz2.cpython-311-darwin.so
+
+ cdhash
+
+ 1NtZPm0vRQqcGicdTB4ntkxtpQQ=
+
+ requirement
+ cdhash H"d4db593e6d2f450a9c1a271d4c1e27b64c6da504"
+
+ Frameworks/python3__dot__11/lib-dynload/_codecs_cn.cpython-311-darwin.so
+
+ cdhash
+
+ WibonEillh7xUyl0lsy/Pp+XOf0=
+
+ requirement
+ cdhash H"5a26e89c48a5961ef153297496ccbf3e9f9739fd"
+
+ Frameworks/python3__dot__11/lib-dynload/_codecs_hk.cpython-311-darwin.so
+
+ cdhash
+
+ 2KDZxUiAaWQ+p6zdWvbkBzw4W/I=
+
+ requirement
+ cdhash H"d8a0d9c5488069643ea7acdd5af6e4073c385bf2"
+
+ Frameworks/python3__dot__11/lib-dynload/_codecs_iso2022.cpython-311-darwin.so
+
+ cdhash
+
+ FysbWVdJNIfsG9IXunsQM15vIOI=
+
+ requirement
+ cdhash H"172b1b5957493487ec1bd217ba7b10335e6f20e2"
+
+ Frameworks/python3__dot__11/lib-dynload/_codecs_jp.cpython-311-darwin.so
+
+ cdhash
+
+ WX9YkY8tdiwSZVG08vJylBjc1ow=
+
+ requirement
+ cdhash H"597f58918f2d762c126551b4f2f2729418dcd68c"
+
+ Frameworks/python3__dot__11/lib-dynload/_codecs_kr.cpython-311-darwin.so
+
+ cdhash
+
+ 9fwYU5UDexSSWqDSaBNvXTP9FjI=
+
+ requirement
+ cdhash H"f5fc185395037b14925aa0d268136f5d33fd1632"
+
+ Frameworks/python3__dot__11/lib-dynload/_codecs_tw.cpython-311-darwin.so
+
+ cdhash
+
+ q1vGidA7UatqW7r89iNvxEpo9Gs=
+
+ requirement
+ cdhash H"ab5bc689d03b51ab6a5bbafcf6236fc44a68f46b"
+
+ Frameworks/python3__dot__11/lib-dynload/_contextvars.cpython-311-darwin.so
+
+ cdhash
+
+ cg1wwf7pEi4MFJrjk/2s2g307zk=
+
+ requirement
+ cdhash H"720d70c1fee9122e0c149ae393fdacda0df4ef39"
+
+ Frameworks/python3__dot__11/lib-dynload/_csv.cpython-311-darwin.so
+
+ cdhash
+
+ 3ZeKOIDcvjrOIf0VzOMh/SlfS6Y=
+
+ requirement
+ cdhash H"dd978a3880dcbe3ace21fd15cce321fd295f4ba6"
+
+ Frameworks/python3__dot__11/lib-dynload/_ctypes.cpython-311-darwin.so
+
+ cdhash
+
+ /Z9MI/tiQtBfSBmEUJtCAusWCHc=
+
+ requirement
+ cdhash H"fd9f4c23fb6242d05f481984509b4202eb160877"
+
+ Frameworks/python3__dot__11/lib-dynload/_datetime.cpython-311-darwin.so
+
+ cdhash
+
+ +CZ+Y7/26qZ6c9gzqrNAZ0trtCE=
+
+ requirement
+ cdhash H"f8267e63bff6eaa67a73d833aab340674b6bb421"
+
+ Frameworks/python3__dot__11/lib-dynload/_decimal.cpython-311-darwin.so
+
+ cdhash
+
+ itxdbhIknBISj5AajxfRAjU/NFg=
+
+ requirement
+ cdhash H"8adc5d6e12249c12128f901a8f17d102353f3458"
+
+ Frameworks/python3__dot__11/lib-dynload/_hashlib.cpython-311-darwin.so
+
+ cdhash
+
+ WdZNKYqsD2YRkw41ieH12xbBQvw=
+
+ requirement
+ cdhash H"59d64d298aac0f6611930e3589e1f5db16c142fc"
+
+ Frameworks/python3__dot__11/lib-dynload/_heapq.cpython-311-darwin.so
+
+ cdhash
+
+ ft4nh3R7Bcwy3Gf+Kzj2l1g+3/Q=
+
+ requirement
+ cdhash H"7ede2787747b05cc32dc67fe2b38f697583edff4"
+
+ Frameworks/python3__dot__11/lib-dynload/_json.cpython-311-darwin.so
+
+ cdhash
+
+ l0wzV/+x2iDWpvD8Ytu9fP8DpR0=
+
+ requirement
+ cdhash H"974c3357ffb1da20d6a6f0fc62dbbd7cff03a51d"
+
+ Frameworks/python3__dot__11/lib-dynload/_lzma.cpython-311-darwin.so
+
+ cdhash
+
+ pKrGpwX23ZCTLvpOYnkogL6l4rU=
+
+ requirement
+ cdhash H"a4aac6a705f6dd90932efa4e62792880bea5e2b5"
+
+ Frameworks/python3__dot__11/lib-dynload/_md5.cpython-311-darwin.so
+
+ cdhash
+
+ IybsYVubywtFmHvrnzNeSHDmW1g=
+
+ requirement
+ cdhash H"2326ec615b9bcb0b45987beb9f335e4870e65b58"
+
+ Frameworks/python3__dot__11/lib-dynload/_multibytecodec.cpython-311-darwin.so
+
+ cdhash
+
+ zytxk6aJI3ZPTKwZiUau0eDjyoM=
+
+ requirement
+ cdhash H"cf2b7193a68923764f4cac198946aed1e0e3ca83"
+
+ Frameworks/python3__dot__11/lib-dynload/_opcode.cpython-311-darwin.so
+
+ cdhash
+
+ wQEIRhJskn8hwd9ZPr+KmrwVExY=
+
+ requirement
+ cdhash H"c1010846126c927f21c1df593ebf8a9abc151316"
+
+ Frameworks/python3__dot__11/lib-dynload/_pickle.cpython-311-darwin.so
+
+ cdhash
+
+ 9eB/x3hkKbUi0p8UyDJ/sUsMbNw=
+
+ requirement
+ cdhash H"f5e07fc7786429b522d29f14c8327fb14b0c6cdc"
+
+ Frameworks/python3__dot__11/lib-dynload/_posixsubprocess.cpython-311-darwin.so
+
+ cdhash
+
+ 1otc3TyWxSAo12Z3zj52f6bR3M0=
+
+ requirement
+ cdhash H"d68b5cdd3c96c52028d76677ce3e767fa6d1dccd"
+
+ Frameworks/python3__dot__11/lib-dynload/_random.cpython-311-darwin.so
+
+ cdhash
+
+ LJzHv0qm4yRanQkD/VAXhg9DNtg=
+
+ requirement
+ cdhash H"2c9cc7bf4aa6e3245a9d0903fd5017860f4336d8"
+
+ Frameworks/python3__dot__11/lib-dynload/_scproxy.cpython-311-darwin.so
+
+ cdhash
+
+ 4q8RvcoNYlgeNi108OvB3StZZUQ=
+
+ requirement
+ cdhash H"e2af11bdca0d62581e362d74f0ebc1dd2b596544"
+
+ Frameworks/python3__dot__11/lib-dynload/_sha1.cpython-311-darwin.so
+
+ cdhash
+
+ 00YsQ+tGL2utl1YxKvvhfOwmvs0=
+
+ requirement
+ cdhash H"d3462c43eb462f6bad9756312afbe17cec26becd"
+
+ Frameworks/python3__dot__11/lib-dynload/_sha256.cpython-311-darwin.so
+
+ cdhash
+
+ upoWE7fasaZ7jXhtjMG/Thme11k=
+
+ requirement
+ cdhash H"ba9a1613b7dab1a67b8d786d8cc1bf4e199ed759"
+
+ Frameworks/python3__dot__11/lib-dynload/_sha3.cpython-311-darwin.so
+
+ cdhash
+
+ t/kR1KvlfpyvZm4rJ1NLytu604c=
+
+ requirement
+ cdhash H"b7f911d4abe57e9caf666e2b27534bcadbbad387"
+
+ Frameworks/python3__dot__11/lib-dynload/_sha512.cpython-311-darwin.so
+
+ cdhash
+
+ 12j2MhIu43eNIzd0Wip/GhEFb48=
+
+ requirement
+ cdhash H"d768f632122ee3778d2337745a2a7f1a11056f8f"
+
+ Frameworks/python3__dot__11/lib-dynload/_socket.cpython-311-darwin.so
+
+ cdhash
+
+ I5Av94svblj/+olgWhCkhp9Xdes=
+
+ requirement
+ cdhash H"23902ff78b2f6e58fffa89605a10a4869f5775eb"
+
+ Frameworks/python3__dot__11/lib-dynload/_sqlite3.cpython-311-darwin.so
+
+ cdhash
+
+ Yq0XdmYDGoikKkEoLPhlXBzyxe0=
+
+ requirement
+ cdhash H"62ad177666031a88a42a41282cf8655c1cf2c5ed"
+
+ Frameworks/python3__dot__11/lib-dynload/_ssl.cpython-311-darwin.so
+
+ cdhash
+
+ BEtXmmuCeJVkorWAkxkFfo5cG6I=
+
+ requirement
+ cdhash H"044b579a6b82789564a2b5809319057e8e5c1ba2"
+
+ Frameworks/python3__dot__11/lib-dynload/_statistics.cpython-311-darwin.so
+
+ cdhash
+
+ Q5oaSz6P3MmhmzNztIT4zaocSrk=
+
+ requirement
+ cdhash H"439a1a4b3e8fdcc9a19b3373b484f8cdaa1c4ab9"
+
+ Frameworks/python3__dot__11/lib-dynload/_struct.cpython-311-darwin.so
+
+ cdhash
+
+ xyNo8zDTzVm3+6la2U5DWkdd1Rg=
+
+ requirement
+ cdhash H"c72368f330d3cd59b7fba95ad94e435a475dd518"
+
+ Frameworks/python3__dot__11/lib-dynload/_typing.cpython-311-darwin.so
+
+ cdhash
+
+ L+kSKzycD0PX1ZQEPx8qgrpPcvI=
+
+ requirement
+ cdhash H"2fe9122b3c9c0f43d7d594043f1f2a82ba4f72f2"
+
+ Frameworks/python3__dot__11/lib-dynload/array.cpython-311-darwin.so
+
+ cdhash
+
+ E1Av6WzVg1JLZoWNzViEDMywhA0=
+
+ requirement
+ cdhash H"13502fe96cd583524b66858dcd58840cccb0840d"
+
+ Frameworks/python3__dot__11/lib-dynload/binascii.cpython-311-darwin.so
+
+ cdhash
+
+ pj0uo0nGV7UIIIWOD2E8Gh2fj9I=
+
+ requirement
+ cdhash H"a63d2ea349c657b50820858e0f613c1a1d9f8fd2"
+
+ Frameworks/python3__dot__11/lib-dynload/fcntl.cpython-311-darwin.so
+
+ cdhash
+
+ J04Chpr4qgnYqufXybZlCRc+kIU=
+
+ requirement
+ cdhash H"274e02869af8aa09d8aae7d7c9b66509173e9085"
+
+ Frameworks/python3__dot__11/lib-dynload/grp.cpython-311-darwin.so
+
+ cdhash
+
+ MYC6Y9Z1rZ64wJoERVP5T/J5nQc=
+
+ requirement
+ cdhash H"3180ba63d675ad9eb8c09a044553f94ff2799d07"
+
+ Frameworks/python3__dot__11/lib-dynload/math.cpython-311-darwin.so
+
+ cdhash
+
+ ap9+ozd2trtGq/NdlF3E8KU2AoI=
+
+ requirement
+ cdhash H"6a9f7ea33776b6bb46abf35d945dc4f0a5360282"
+
+ Frameworks/python3__dot__11/lib-dynload/pyexpat.cpython-311-darwin.so
+
+ cdhash
+
+ LbqJ8FJv04vp5UBciCC4IlgqXRU=
+
+ requirement
+ cdhash H"2dba89f0526fd38be9e5405c8820b822582a5d15"
+
+ Frameworks/python3__dot__11/lib-dynload/resource.cpython-311-darwin.so
+
+ cdhash
+
+ SJ4mmtuIWhr35sAq3lfY+ZtHPhA=
+
+ requirement
+ cdhash H"489e269adb885a1af7e6c02ade57d8f99b473e10"
+
+ Frameworks/python3__dot__11/lib-dynload/select.cpython-311-darwin.so
+
+ cdhash
+
+ vSw8n8BC3j64rnOKA7geLMNdkQA=
+
+ requirement
+ cdhash H"bd2c3c9fc042de3eb8ae738a03b81e2cc35d9100"
+
+ Frameworks/python3__dot__11/lib-dynload/termios.cpython-311-darwin.so
+
+ cdhash
+
+ 4SvnSWHEHAmZBCOYM8hdnIILcxo=
+
+ requirement
+ cdhash H"e12be74961c41c099904239833c85d9c820b731a"
+
+ Frameworks/python3__dot__11/lib-dynload/unicodedata.cpython-311-darwin.so
+
+ cdhash
+
+ iKKD4q00uMvUuQ2+47DJGh8YCQc=
+
+ requirement
+ cdhash H"88a283e2ad34b8cbd4b90dbee3b0c91a1f180907"
+
+ Frameworks/python3__dot__11/lib-dynload/zlib.cpython-311-darwin.so
+
+ cdhash
+
+ yWqGVV5aJFiugSJjYxCOz2v82kY=
+
+ requirement
+ cdhash H"c96a86555e5a2458ae81226363108ecf6bfcda46"
+
+ Frameworks/replicator
+
+ symlink
+ ../Resources/replicator
+
+ Resources/PyQt5/Qt5/lib
+
+ symlink
+ ../../../Frameworks/PyQt5/Qt5/lib
+
+ Resources/PyQt5/Qt5/plugins
+
+ symlink
+ ../../../Frameworks/PyQt5/Qt5/plugins
+
+ Resources/PyQt5/Qt5/translations/qt_ar.qm
+
+ hash2
+
+ qKGw1vlY5zZtHIVr5hAAEG0+f8mT+5MWdTaYkrkALQs=
+
+
+ Resources/PyQt5/Qt5/translations/qt_bg.qm
+
+ hash2
+
+ nhzk2RhSNSBD2RkfGpkoOPkZy6fi8tm7EWHklOi/X14=
+
+
+ Resources/PyQt5/Qt5/translations/qt_ca.qm
+
+ hash2
+
+ Fxx0JLJNhQKrU8s3hP802Pz64mVXz4r0393sZIWswv4=
+
+
+ Resources/PyQt5/Qt5/translations/qt_cs.qm
+
+ hash2
+
+ PAy/0ZSQ1n0bO56UTDpNmp5/h9euNeiNXVoAdzSbWyE=
+
+
+ Resources/PyQt5/Qt5/translations/qt_da.qm
+
+ hash2
+
+ tQNhYc6AjHKOX9qYX3kttWWDH9Ac8AsoJUd5DANzU6I=
+
+
+ Resources/PyQt5/Qt5/translations/qt_de.qm
+
+ hash2
+
+ WAW4/zdHhJeU4tcGYdc3xpwV8a52PDjhcISx5agekVM=
+
+
+ Resources/PyQt5/Qt5/translations/qt_en.qm
+
+ hash2
+
+ mVm1ELFdGJN4SK0TAH4wRZ0umTxn5WS62/wY+TVpXIU=
+
+
+ Resources/PyQt5/Qt5/translations/qt_es.qm
+
+ hash2
+
+ 4l5D8Eb2ECL/6HGi9zxqEu38XD79lYwOAZpyGGCgU7A=
+
+
+ Resources/PyQt5/Qt5/translations/qt_fa.qm
+
+ hash2
+
+ X3wXFT3SzlwZ5OIZgF5CbsKIZ7l7lCxLLJJuYbeSq0s=
+
+
+ Resources/PyQt5/Qt5/translations/qt_fi.qm
+
+ hash2
+
+ BIykLc5Pr1/CHYQ1duPG/ZYxRuzHhVTn5fNNB/ZPshM=
+
+
+ Resources/PyQt5/Qt5/translations/qt_fr.qm
+
+ hash2
+
+ hiQDPYSeZwsSyVMjN/y/Jg8ghI4ET+53h8/irJK+KNs=
+
+
+ Resources/PyQt5/Qt5/translations/qt_gd.qm
+
+ hash2
+
+ Sd+FWgBKF5UDOK8xRkZvbfTVhSQQvQtY6oDg0CA6nSQ=
+
+
+ Resources/PyQt5/Qt5/translations/qt_gl.qm
+
+ hash2
+
+ IEoBrH3ra1uuGTr+y9HlDRjHO/fZS63rK7/fYSPE7ZM=
+
+
+ Resources/PyQt5/Qt5/translations/qt_he.qm
+
+ hash2
+
+ wRPRTiGNVAK2Ftq+onlpxvg4UmdkaMXvBR3d77PuAjU=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_ar.qm
+
+ hash2
+
+ IiCIyXUtHMO6uYXvLcd+WueFeNzhimHsFbOfAuWIFj0=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_bg.qm
+
+ hash2
+
+ y83R4LuuMy2A3bCihgVvF8gk+ijTU9f98S/JfZ9v4FQ=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_ca.qm
+
+ hash2
+
+ x/At4KZE/27jKeZS1HwXX+7ATuJ0QAzWg2tpF93vyBM=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_cs.qm
+
+ hash2
+
+ SsVvxj5ACUO6sT8dTEGFAhOJCOHUiMJK7mEx09F1Uqo=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_da.qm
+
+ hash2
+
+ a7CSVSo5hocRn21SFF8Ev4Nzl3RG2PAMDcvVa5aCnw8=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_de.qm
+
+ hash2
+
+ mWJSP7rp8eTDtcPBaGDQWSkcsw3F6+Wl7aTINqA/7R4=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_en.qm
+
+ hash2
+
+ mVm1ELFdGJN4SK0TAH4wRZ0umTxn5WS62/wY+TVpXIU=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_es.qm
+
+ hash2
+
+ /RayefjPaQd/delNkMnAeir/85SKV543ifX/teX0IC0=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_fr.qm
+
+ hash2
+
+ aH41HAYvaIqv9s8FIY1gF7gLGhtCONHTAlClXuQcX+0=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_gl.qm
+
+ hash2
+
+ c5miSGCZdHc/YIZsh7eOp9+8T3UDE9aS94hs12OIPJ8=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_hr.qm
+
+ hash2
+
+ jIzDxbGrKwdPgpNwAbiWYDQg73VZKzt4FyF3Ng3X6mE=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_hu.qm
+
+ hash2
+
+ cPM7VpwpQvQcbWNOpqYcuNgOsscBG61I72266Wd5YNU=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_it.qm
+
+ hash2
+
+ MW/o0IFeK0s5aJW+s47xpAQxkVteBU34D0wM1Vbybks=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_ja.qm
+
+ hash2
+
+ rmA7LA1DTUDN5DP/y6ZfnuJ5eKnhkxYAe+f+eCpbi0c=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_ko.qm
+
+ hash2
+
+ 8RxkaU6ONOHSxGwaHRXWup8tt7Yd5P31TspauXfD4FI=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_nl.qm
+
+ hash2
+
+ RKig2vd10UNSGABS0T9iOBTXvfmOLFyAFEt790IU47A=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_nn.qm
+
+ hash2
+
+ TIpyE5QgeOT3L1ur0mxBt3rOcBiXFKWLVQSn1a0Bk9w=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_pl.qm
+
+ hash2
+
+ zGy02MVAhiJGcvLknmI8jLfAwc1luNXs1C/JujpgZb0=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_pt_BR.qm
+
+ hash2
+
+ F6FkBFwPxT0FB8/JDDlw/hr9+hxKjlyIHTT6H4aoIb8=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_ru.qm
+
+ hash2
+
+ VXtkTm2l8exyDvk5ZWFwh+TR9AsklMxapSTPN5YQjec=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_sk.qm
+
+ hash2
+
+ 9B4z4deQvQ0+sYDx+HW8GR/nR3NijyXCytleFALmaGc=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_sl.qm
+
+ hash2
+
+ LslV5mJAfrzY3NrlqqIeQQjgtbCu4OnbcSwnBylDU18=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_tr.qm
+
+ hash2
+
+ 697KDP7nqUQd64ALq/2XxjvE5CHaiFxVs71Jcl66zSU=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_uk.qm
+
+ hash2
+
+ SXz8RzaEaS7kTXo3lej7InDFcGn9nrmKYV3Smrm+inw=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_zh_CN.qm
+
+ hash2
+
+ 4lbR9gNKWlUhxi8Sn5FzEhbkdPceHZyuo9PcsRtnDes=
+
+
+ Resources/PyQt5/Qt5/translations/qt_help_zh_TW.qm
+
+ hash2
+
+ Z+bfCmxl4PzzN5jftBf5u/DN5FErVunjFJql0Y97AZg=
+
+
+ Resources/PyQt5/Qt5/translations/qt_hr.qm
+
+ hash2
+
+ q4sscqwH3SK7IBlmVaC+DCxvockki86wTW5nBxhg2eg=
+
+
+ Resources/PyQt5/Qt5/translations/qt_hu.qm
+
+ hash2
+
+ uyRqq9UB4Uztix/8E2nj1dJlZ6rmKz6tTZTCL7d8NHE=
+
+
+ Resources/PyQt5/Qt5/translations/qt_it.qm
+
+ hash2
+
+ kReqwtB7yG36VaKbiCXtJ8cJMwD8yQ4UPhNeAOhfCdc=
+
+
+ Resources/PyQt5/Qt5/translations/qt_ja.qm
+
+ hash2
+
+ I0V86OROIzxvhdVqTuaizs2Hyce93m2LipJZAu7RzZw=
+
+
+ Resources/PyQt5/Qt5/translations/qt_ko.qm
+
+ hash2
+
+ TMGvN+dx8KQ4mISc/yzUKoIEUbjSsuiJMQMWKdeB2wU=
+
+
+ Resources/PyQt5/Qt5/translations/qt_lt.qm
+
+ hash2
+
+ R+tfl0Z992kmFCHVSlvqETHJ+5tjiHkdOLtldDNbZL8=
+
+
+ Resources/PyQt5/Qt5/translations/qt_lv.qm
+
+ hash2
+
+ A0Z3OAQqFWduUEugLLMm3Nt3OxcfraPNYreg4FZDFKA=
+
+
+ Resources/PyQt5/Qt5/translations/qt_nl.qm
+
+ hash2
+
+ uSjkOApLXUc44+x6rMeleiJZJMyW2vLTDhhS8Ug24Cg=
+
+
+ Resources/PyQt5/Qt5/translations/qt_nn.qm
+
+ hash2
+
+ LBi3+va8KYFU0pEbiK/YCvj32AgNCyyJ2974aBYs9Ss=
+
+
+ Resources/PyQt5/Qt5/translations/qt_pl.qm
+
+ hash2
+
+ vX3QwsqxGalz3BDDv/dJnZcouSi1QfhgVpIbMMjbeOY=
+
+
+ Resources/PyQt5/Qt5/translations/qt_pt_BR.qm
+
+ hash2
+
+ 7Pq4gLC/9Yqo7/LlvR+rUwp7SVxzohx94fUor8Mqf4U=
+
+
+ Resources/PyQt5/Qt5/translations/qt_pt_PT.qm
+
+ hash2
+
+ LB57v1FopktDdS3UxUdgHAvebWEPhnH6PjrzhZfoR4M=
+
+
+ Resources/PyQt5/Qt5/translations/qt_ru.qm
+
+ hash2
+
+ ZJKyZ2CMb7dpB72Pz8jx71fp9Ou8LoGsqBcVqIOI+Uo=
+
+
+ Resources/PyQt5/Qt5/translations/qt_sk.qm
+
+ hash2
+
+ tq3/2In/lr8ZXLmXMn59cAWoFcrWeCP6aRWhnC2btmg=
+
+
+ Resources/PyQt5/Qt5/translations/qt_sl.qm
+
+ hash2
+
+ xE4DE6lBTMDkkLZbDANvoRvKlZNTsiiIZUe8LISSA08=
+
+
+ Resources/PyQt5/Qt5/translations/qt_sv.qm
+
+ hash2
+
+ S0tv9/0jfJ2gMBtJRhMuaGU9Fetfrzjkxfv+uxLdl/c=
+
+
+ Resources/PyQt5/Qt5/translations/qt_tr.qm
+
+ hash2
+
+ 9XSiz9RxWIXD299a5gmVJSZzvZT9qpWG9+BYb2wawO4=
+
+
+ Resources/PyQt5/Qt5/translations/qt_uk.qm
+
+ hash2
+
+ 8WIeaA4WQvlGPksH5+eLUPmnvbfDIdcwIDnLNAXL3qQ=
+
+
+ Resources/PyQt5/Qt5/translations/qt_zh_CN.qm
+
+ hash2
+
+ ao+JzNJQ/A8AKiWB37e/ukNmp661qdmB6kL9yY38mV4=
+
+
+ Resources/PyQt5/Qt5/translations/qt_zh_TW.qm
+
+ hash2
+
+ dR7NoMM+Bh2RJBJoNX+9L2t/cKERbnFPKNIu/WHseho=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_ar.qm
+
+ hash2
+
+ 4D/mjYMgFUNpj9f+Jn3V38W/0ZUUfnT/LxmsNJFAEmM=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_bg.qm
+
+ hash2
+
+ 5Eisnj8Wwp6yevMBLv4hBS2qePq/s0zW3/L2nuO9PNs=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_ca.qm
+
+ hash2
+
+ 5OKmeENU8W+EC0kKASVhKe1WeJX+SzAnaO1FNNJEv9s=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_cs.qm
+
+ hash2
+
+ AwKLQt9UeScDceTDvcffL1bLvm3alWooZKxvZBWGH+g=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_da.qm
+
+ hash2
+
+ fR5cozELVNEEwZvyq9QCs45YTocDmnDhU8Spr3SyXCI=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_de.qm
+
+ hash2
+
+ VTwEaDXbmt7xWVT6mldmJTZrqL/RZjcDjEvNKOXrrOE=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_en.qm
+
+ hash2
+
+ mVm1ELFdGJN4SK0TAH4wRZ0umTxn5WS62/wY+TVpXIU=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_es.qm
+
+ hash2
+
+ T/2la6O7VBSrBILR3eZKbyJuNIj2t/PxGhUOAfU/pMg=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_fa.qm
+
+ hash2
+
+ 945Ztb3VhhgamZA0ukGIaO0X/pwFcH+2XlI/cOkiU9I=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_fi.qm
+
+ hash2
+
+ 5H/hNxPhhNB/pEld3gxYmw6PVi6RV0o1WKk2NEOk+nI=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_fr.qm
+
+ hash2
+
+ 7bMqkzzvN2omNmNOFOKXfO1ihOSqmkrH4ikvnKVMOEo=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_gd.qm
+
+ hash2
+
+ Y7Q53UQTmqOu1UwuvgP6m8d/IsFO2Puo7/JghEW7Iz0=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_he.qm
+
+ hash2
+
+ 4evKFq/omUNW+BygB/vbnd+GWEIBD+kIkj2HO2h8rT8=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_hr.qm
+
+ hash2
+
+ u57smpplLBNA3HXrLnSb5Q3wD4hbPWkA38dnmcRbJE0=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_hu.qm
+
+ hash2
+
+ xhtnu50ehPCrB5K2UY/gVUFKaORNDHvHyGJ3OAD6gpk=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_it.qm
+
+ hash2
+
+ eAjtVCp4M8S9lOyL37vKALtPL+FZebkSHOMkkyNocwA=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_ja.qm
+
+ hash2
+
+ y6OCrMRNNoDUAPLGJd6T0MS9cqkBAnae39H+kcubYXs=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_ko.qm
+
+ hash2
+
+ AXntGxNuHLP1gzUeqixUW6PYOm7j+CwyUFkmoaX18YM=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_lv.qm
+
+ hash2
+
+ hG4EdXOuQMg2ccO6f3PifvwkuYyCcB2g35lz5XQXi7I=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_nl.qm
+
+ hash2
+
+ 9hUTvNu2rYAFhflDepXQGKqxf6Eu5BSqMUYv8nnYJA4=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_nn.qm
+
+ hash2
+
+ mDc0tAqk0lBCG6DRYUQWy4tCTW8UD0p9v4sR5l7vY9w=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_pl.qm
+
+ hash2
+
+ zpkDKjsL+KutdYiVzCKDcIjq2Z/S0lFOLRgGkwgc/lc=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_pt_BR.qm
+
+ hash2
+
+ g0T1vDM8RdN8/UJaCRJOsR8SQ58zc37AuGg9BKQWdG4=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_ru.qm
+
+ hash2
+
+ PaZgVmj5F40RqDjEUVR4CE3PtPnPIvmdepK0ktucIks=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_sk.qm
+
+ hash2
+
+ 1YauLDFAdM85hBf97LQHCdVHjf6wpnwv5g1Qnum1ntc=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_tr.qm
+
+ hash2
+
+ agv25w55IMKxk+dukvePMVk2lV07BqwDnZF/LgbEMoE=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_uk.qm
+
+ hash2
+
+ Ubj/VbN9xZB9Y3qN3aEvvoFoUrAkTHTrTw+4SGenhuA=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_zh_CN.qm
+
+ hash2
+
+ mP1Ll9vsivXN0V+FoWwz78dzAbGusEadqvwEuasvNXA=
+
+
+ Resources/PyQt5/Qt5/translations/qtbase_zh_TW.qm
+
+ hash2
+
+ IQErK0J/jQnTHOnK5YLKJ5VbR4yj3C7BijBo2AhjjGo=
+
+
+ Resources/PyQt5/QtCore.abi3.so
+
+ symlink
+ ../../Frameworks/PyQt5/QtCore.abi3.so
+
+ Resources/PyQt5/QtGui.abi3.so
+
+ symlink
+ ../../Frameworks/PyQt5/QtGui.abi3.so
+
+ Resources/PyQt5/QtSvg.abi3.so
+
+ symlink
+ ../../Frameworks/PyQt5/QtSvg.abi3.so
+
+ Resources/PyQt5/QtWidgets.abi3.so
+
+ symlink
+ ../../Frameworks/PyQt5/QtWidgets.abi3.so
+
+ Resources/PyQt5/sip.cpython-311-darwin.so
+
+ symlink
+ ../../Frameworks/PyQt5/sip.cpython-311-darwin.so
+
+ Resources/Python
+
+ symlink
+ Python.framework/Versions/3.11/Python
+
+ Resources/Python.framework
+
+ symlink
+ ../Frameworks/Python.framework
+
+ Resources/QtCore
+
+ symlink
+ PyQt5/Qt5/lib/QtCore.framework/Versions/5/QtCore
+
+ Resources/QtDBus
+
+ symlink
+ PyQt5/Qt5/lib/QtDBus.framework/Versions/5/QtDBus
+
+ Resources/QtGui
+
+ symlink
+ PyQt5/Qt5/lib/QtGui.framework/Versions/5/QtGui
+
+ Resources/QtNetwork
+
+ symlink
+ PyQt5/Qt5/lib/QtNetwork.framework/Versions/5/QtNetwork
+
+ Resources/QtPrintSupport
+
+ symlink
+ PyQt5/Qt5/lib/QtPrintSupport.framework/Versions/5/QtPrintSupport
+
+ Resources/QtQml
+
+ symlink
+ PyQt5/Qt5/lib/QtQml.framework/Versions/5/QtQml
+
+ Resources/QtQmlModels
+
+ symlink
+ PyQt5/Qt5/lib/QtQmlModels.framework/Versions/5/QtQmlModels
+
+ Resources/QtQuick
+
+ symlink
+ PyQt5/Qt5/lib/QtQuick.framework/Versions/5/QtQuick
+
+ Resources/QtSvg
+
+ symlink
+ PyQt5/Qt5/lib/QtSvg.framework/Versions/5/QtSvg
+
+ Resources/QtWebSockets
+
+ symlink
+ PyQt5/Qt5/lib/QtWebSockets.framework/Versions/5/QtWebSockets
+
+ Resources/QtWidgets
+
+ symlink
+ PyQt5/Qt5/lib/QtWidgets.framework/Versions/5/QtWidgets
+
+ Resources/base_library.zip
+
+ hash2
+
+ 3KeLPTdCSyQ579wV+NNlryctvaHsvse0kCXKVnT7N5I=
+
+
+ Resources/bin/nssm/windows/x86/nssm.exe
+
+ hash2
+
+ RyIyyoIbXC71YqsH9TY4vCzILq6EzqE/vmdNYCK2SBw=
+
+
+ Resources/bin/nssm/windows/x86_64/nssm.exe
+
+ hash2
+
+ 9onumvlLAOnj8LsHKzTKryB/Mty09Xgvyco1HfmgbJc=
+
+
+ Resources/core/.editorconfig
+
+ hash2
+
+ tNDGpaKw/Q8OOGEnxKpy+g/2CSuSCDXjBTiihuxb/GE=
+
+
+ Resources/core/.git
+
+ hash2
+
+ xj1mDyPITuk9lhgsZwi3sv20wAqz0EuRwceGsMAzcPI=
+
+
+ Resources/core/.github/FUNDING.yml
+
+ hash2
+
+ ZMUx3UQ0ivlcI+qPMIivEbXzJjALGa0On/ajweoJ2SE=
+
+
+ Resources/core/.github/ISSUE_TEMPLATE/bug_report.md
+
+ hash2
+
+ YP2Ftg6yXgwXAZft9M4SX6qwKNPIf464uKsoE6pRysM=
+
+
+ Resources/core/.github/ISSUE_TEMPLATE/feature_request.md
+
+ hash2
+
+ OaV+amDHIOBF4gZHorVC5D/GFr/wHehaKDcoprAY/eA=
+
+
+ Resources/core/.github/no-response.yml
+
+ hash2
+
+ 3CvFSMBT1OnXiFtrUe/YF3R2i7xaKshQUrwShfZaIQw=
+
+
+ Resources/core/.github/workflows/release.yml
+
+ hash2
+
+ /ZBa79W7C/qz1Hz887wjNIILe3LqtykEsRr2HPo3MQY=
+
+
+ Resources/core/.gitignore
+
+ hash2
+
+ bd9immDtsDrbVhktTdaOQM79FrNPJMcuVr2ASnd8bL0=
+
+
+ Resources/core/LICENSE
+
+ hash2
+
+ OXLcl0T2SZ8Pmy2/dmlvKuetivmyPd5m1q+Gyd+zaYY=
+
+
+ Resources/core/README.md
+
+ hash2
+
+ IuUAyRieDr+Imb8Q1FvfuE+1yH7eT7grnZVT3ncIa2w=
+
+
+ Resources/core/SECURITY.md
+
+ hash2
+
+ kAB0rp/FR18JvnDluda2Hf6a2C+/7dC33dmjU/KQgQg=
+
+
+ Resources/core/VERSION
+
+ hash2
+
+ R66OVItaS1LbFQIiGypKiJUodzsHvSNzccxHXDa15Ks=
+
+
+ Resources/core/__init__.py
+
+ hash2
+
+ u/Tck6/CSMvuKphQO+GLmen5M9ZN0WUFutKHCJskQRE=
+
+
+ Resources/core/__pycache__/__init__.cpython-311.pyc
+
+ hash2
+
+ iX/jHrkwXsG132X2TTmStFIa+urij6t/G7DKgylVU64=
+
+
+ Resources/core/__pycache__/__init__.cpython-312.pyc
+
+ hash2
+
+ JxjyhVdPu9G+pKwnbTl8nJxX038C99TzyItGMg4Piww=
+
+
+ Resources/core/__pycache__/__init__.cpython-314.pyc
+
+ hash2
+
+ FqsvlDDaArJy71DKdir79VvzCHE34awQ6d6kb43GgS4=
+
+
+ Resources/core/__pycache__/application.cpython-311.pyc
+
+ hash2
+
+ 948ySdJv3u1CCtUKYlr/YC2AzCUkixZim5yLuI68kbA=
+
+
+ Resources/core/__pycache__/application.cpython-312.pyc
+
+ hash2
+
+ ieXkCb8sA+Wbvo59NeXKQALiCFtilSjeKMML/d3pYyY=
+
+
+ Resources/core/__pycache__/application.cpython-314.pyc
+
+ hash2
+
+ pGp9ORrc8Mk/68joknmJfMtVEcodyS+G5+ixkaAWW4A=
+
+
+ Resources/core/__pycache__/cli.cpython-311.pyc
+
+ hash2
+
+ RabnrQ8LshapZkgDycCKNdKiJ1isQ274mtNULqAEOTM=
+
+
+ Resources/core/__pycache__/cli.cpython-312.pyc
+
+ hash2
+
+ UxXLgieqeMZjAs2AwXv11hAd6ksxamQhLuwedwqkizw=
+
+
+ Resources/core/__pycache__/configuration.cpython-311.pyc
+
+ hash2
+
+ 4OAzlZIvmIGgLqgqYPJEoSFjKpo/OXMYorKRoKqUQWA=
+
+
+ Resources/core/__pycache__/configuration.cpython-312.pyc
+
+ hash2
+
+ nnqE4LRAZaFSAQTrZzZPr6iUCL7RZ30UnFa2vPzfX8s=
+
+
+ Resources/core/__pycache__/helper.cpython-311.pyc
+
+ hash2
+
+ ibfi7J9FXPefaEtbISlmUkz1BCx+CNzkqoxFU+C1zN8=
+
+
+ Resources/core/__pycache__/helper.cpython-312.pyc
+
+ hash2
+
+ DP5TX+7HDAmtKW+KGEMT0aQ3LPFTG7xESTEYxZUI7yo=
+
+
+ Resources/core/__pycache__/log.cpython-311.pyc
+
+ hash2
+
+ zhhkshQxjHS8kz0z7bKnMviHAF/hwx4MckmVjpZFFR4=
+
+
+ Resources/core/__pycache__/log.cpython-312.pyc
+
+ hash2
+
+ SNaVbA+KkYQKR3nStSQLGQpzu+CFmj/t6H2Yibg6HzI=
+
+
+ Resources/core/__pycache__/service.cpython-311.pyc
+
+ hash2
+
+ 4+mcEQE/m0qgD40iCxTfUdQ0cdcfD9Ot/sltF6YM7uw=
+
+
+ Resources/core/__pycache__/service.cpython-312.pyc
+
+ hash2
+
+ OOZjYUUNjgtuYdo+wpoYBcEOrv6BK0hJuL8zFi4mVxE=
+
+
+ Resources/core/__pycache__/ui.cpython-311.pyc
+
+ hash2
+
+ DumSS97ITm1Rn8kUK/PmcnYCABzIPEUe68F4KFCEQFY=
+
+
+ Resources/core/__pycache__/ui.cpython-312.pyc
+
+ hash2
+
+ cXwrrikpd7MtCcCaSdwVxC5+QITVaKsiS7GH2/FMGB4=
+
+
+ Resources/core/application.py
+
+ hash2
+
+ 9diZqiQMmVPVpVOgIAVRT54BhIGqt0dVkH4U9QQz6U0=
+
+
+ Resources/core/build.sh
+
+ hash2
+
+ QQcvQIAicRkAHVzo8c7uWYNsirwM5LyayOodY4PoxwY=
+
+
+ Resources/core/bundle.sh
+
+ hash2
+
+ Q2a6hf4PtneSf+lbv7bjwOjdjlFciUixl47pTBql10A=
+
+
+ Resources/core/cli.py
+
+ hash2
+
+ P8ZtwzaSw2atDhngNpcHODdsrfPDA2S6/K0Lm1hcubM=
+
+
+ Resources/core/configuration.py
+
+ hash2
+
+ WwdazvFucuthJ5eTfXtoCWK+xntpZ0UpiN+sM3UkSAM=
+
+
+ Resources/core/database/__init__.py
+
+ hash2
+
+ lnIR6Wy31LsnLPfgX3/QDRyfR6bskkTDLxGbcyZFAE4=
+
+
+ Resources/core/database/__pycache__/__init__.cpython-311.pyc
+
+ hash2
+
+ NPNhkpY1WedGBQ5kt9+anw+/SUWKuE5GVG77eQYTynI=
+
+
+ Resources/core/database/__pycache__/__init__.cpython-312.pyc
+
+ hash2
+
+ e7+6USsnq2CNndE2VRbrvUHGhonSG0wrgvqOy2XOACk=
+
+
+ Resources/core/database/__pycache__/sqlite.cpython-311.pyc
+
+ hash2
+
+ Dz/vsEfjyMf4LQQP35gS+dKslsVtLq2oYkOblGZofWY=
+
+
+ Resources/core/database/__pycache__/sqlite.cpython-312.pyc
+
+ hash2
+
+ OwwOIFU9KTdcATLL4QCaWWtZxtIWpMMk3dPdF9aBCK0=
+
+
+ Resources/core/database/sqlite.py
+
+ hash2
+
+ 4JskES8eD8FFp8RImnr0iJ41m95HIvJWOfVrzqwrep4=
+
+
+ Resources/core/filesystem/__init__.py
+
+ hash2
+
+ sdWCKEeB7ful9NZltmqhz4/L+OVVdxj3UEhMhghoT54=
+
+
+ Resources/core/filesystem/__pycache__/__init__.cpython-311.pyc
+
+ hash2
+
+ eIeyiqBPiRvv0btDuPY8PqfI4m2ljMcddW+s/MoyUTk=
+
+
+ Resources/core/filesystem/__pycache__/__init__.cpython-312.pyc
+
+ hash2
+
+ 8nJP1ZekxS9jAEE7x9uUykVlVcHjxTSoV7wAYsqvw+Q=
+
+
+ Resources/core/filesystem/__pycache__/filesystem.cpython-311.pyc
+
+ hash2
+
+ G1QcxSHVfDiMBiZ0NMGJKHUazEYyZ5IYYBFxlNDIdfQ=
+
+
+ Resources/core/filesystem/__pycache__/filesystem.cpython-312.pyc
+
+ hash2
+
+ e4aJq+sXQf2ENxfWu5EWi0epnENNSF7l07Bl9zAODwo=
+
+
+ Resources/core/filesystem/__pycache__/share.cpython-311.pyc
+
+ hash2
+
+ Fxl/8kPu+EDmEMXPMwC81dg1c9xsRZcDlmNQ0DvOucQ=
+
+
+ Resources/core/filesystem/filesystem.py
+
+ hash2
+
+ QyNxzgcOTuiPmR32W+5uONAJMdc4A7hnbsfvLJKIw+I=
+
+
+ Resources/core/filesystem/share.py
+
+ hash2
+
+ 5Ev6QACs2vDancwefrfR82MCVrlzM43xDyKS3BH33Ms=
+
+
+ Resources/core/helper.py
+
+ hash2
+
+ fSO75POLf4qGOnVM/TSyvbVU/XzhD9Ne6CNi4fRi/3E=
+
+
+ Resources/core/icons/0-circle-fill.svg
+
+ hash2
+
+ xGuS/VDrkvC/Bpv8FolBPpgMf/uWw7DhE7aMfmMkSpQ=
+
+
+ Resources/core/icons/0-circle.svg
+
+ hash2
+
+ YfYgygYBI9Ypi/OT395dugeJ5UZ9aCBY2DaHzXkhzgE=
+
+
+ Resources/core/icons/0-square-fill.svg
+
+ hash2
+
+ NPDSKNyN7LzbrakbxutwP5YEdsBCSbMGFR++wdiX7Zs=
+
+
+ Resources/core/icons/0-square.svg
+
+ hash2
+
+ knqWQrVdC/QxKpuHZAQ4c13A6K0NjEpCW3oNXkbATtk=
+
+
+ Resources/core/icons/1-circle-fill.svg
+
+ hash2
+
+ i4j8vPkpY0UhFMYHXQ5oETQKzGanqgng4C1t+uh/VvQ=
+
+
+ Resources/core/icons/1-circle.svg
+
+ hash2
+
+ 70mahrpzej5yrdwVxenvWXbK+TPadc1C1/KHuFjlAjM=
+
+
+ Resources/core/icons/1-square-fill.svg
+
+ hash2
+
+ J3GGghKLYQz853DNNpmXLbG4UAcflwwAbIfIPefNYvU=
+
+
+ Resources/core/icons/1-square.svg
+
+ hash2
+
+ mh8/FqADxc1pZGmL9urqaijlhUENouNtTJZ0HjZ/bTg=
+
+
+ Resources/core/icons/123.svg
+
+ hash2
+
+ 8u2Dd7Wiv7OB2UByLXg6dLA6Se1hyeDVcsRpVMoLV9o=
+
+
+ Resources/core/icons/2-circle-fill.svg
+
+ hash2
+
+ apTkeZukbHEpnaVVWHQwaO+P/NUJA3brCnfFp0uyODc=
+
+
+ Resources/core/icons/2-circle.svg
+
+ hash2
+
+ jYvgGnlGAB4+lGqRjWfT5kLAOkWTvB1k9cgqzrvwHZk=
+
+
+ Resources/core/icons/2-square-fill.svg
+
+ hash2
+
+ IJSsDyplu9IZ1QmHKwhYdTCMZhREJpB9Os/RVkgWM7I=
+
+
+ Resources/core/icons/2-square.svg
+
+ hash2
+
+ MSmMTcesI6MoQKss9fgQeQFS5VzmkwZlA3akRM1MayA=
+
+
+ Resources/core/icons/3-circle-fill.svg
+
+ hash2
+
+ AxVkyTxtQZYVRnpFf+X58xsDVvlYCTUCMA/AVb2e7zg=
+
+
+ Resources/core/icons/3-circle.svg
+
+ hash2
+
+ tIT9qoR/KG1FG2VurMQKB7RJknL54uNmKirz6HyHop4=
+
+
+ Resources/core/icons/3-square-fill.svg
+
+ hash2
+
+ 4n+EtHzWo+yzFQcSmNe5jeaArgJ+Mo2Xlbpl+rxnTGc=
+
+
+ Resources/core/icons/3-square.svg
+
+ hash2
+
+ p2yXLgeWsPAKlchCkrKXieX0RXwsbuy4Li3e6EZweyM=
+
+
+ Resources/core/icons/4-circle-fill.svg
+
+ hash2
+
+ GBSrePcoOPCF3q1T2AyFZXR6Oq6WiXbP1+Zip9DBj6g=
+
+
+ Resources/core/icons/4-circle.svg
+
+ hash2
+
+ pjIBJPVtH5X7EgyvyV9F3Dg3WXoVOIq4f8Otb0YANa0=
+
+
+ Resources/core/icons/4-square-fill.svg
+
+ hash2
+
+ 0cWf1ld7q8rtYnxSlA/jSXHr0da90M5iKYX/L5mgNzQ=
+
+
+ Resources/core/icons/4-square.svg
+
+ hash2
+
+ yv2xc0y9W6XQAIhEZX8fFZfpe+kC6qR4mwxYQXsoNqo=
+
+
+ Resources/core/icons/5-circle-fill.svg
+
+ hash2
+
+ bsgaPM8zZS+v7nalXjpklNoqVJtAomye9Re+ORx/dN4=
+
+
+ Resources/core/icons/5-circle.svg
+
+ hash2
+
+ AwhyanhgjjfgEhNvUBY1VkUwUenm+kU/Au3UIcIm3+o=
+
+
+ Resources/core/icons/5-square-fill.svg
+
+ hash2
+
+ 3DYnyIy0nCH7FQGzYDnWrfpcXAZ5J8GTjulx438hV2Y=
+
+
+ Resources/core/icons/5-square.svg
+
+ hash2
+
+ nbiiA2EdHDrJCc9sJ3lCDQUTr9Zw3bNFfrQikzN5aLg=
+
+
+ Resources/core/icons/6-circle-fill.svg
+
+ hash2
+
+ BlrvzB/Eufz5grpjDJw45enZwuBZNm3+tCZQrZgKbNY=
+
+
+ Resources/core/icons/6-circle.svg
+
+ hash2
+
+ fHeyK2r+PdnjQHEtE0lXQeBoDxBYZlpJVYEPI6D7Q04=
+
+
+ Resources/core/icons/6-square-fill.svg
+
+ hash2
+
+ mGDIA4j2MnmOwa3MpJuSv/bRC/w50zRSdej3D/lGG0k=
+
+
+ Resources/core/icons/6-square.svg
+
+ hash2
+
+ dJeOt/i7f2w8rASDDnCuCSiKvfw+Cdv+jG6I2Yxe9TM=
+
+
+ Resources/core/icons/7-circle-fill.svg
+
+ hash2
+
+ 4LkghVL1CiYRxaIbnm+3/qh1/kShkJegyWFNAK3+tP8=
+
+
+ Resources/core/icons/7-circle.svg
+
+ hash2
+
+ JZRo1h61IQzglM9eGcspHvFwnV7TFvWR/bNDfSfeZU4=
+
+
+ Resources/core/icons/7-square-fill.svg
+
+ hash2
+
+ fAxPP0akhkABmo4gjgEfyFnj5n0PlOSzwqiRFhHhtio=
+
+
+ Resources/core/icons/7-square.svg
+
+ hash2
+
+ us6i39+pKuRM8o9Npe8zn8DGyi4CfrcqGWX253nvQOw=
+
+
+ Resources/core/icons/8-circle-fill.svg
+
+ hash2
+
+ QsmSCgzH0UGEpxKwKxIhDiaVL+Z4aDiuFG1+dkP82SY=
+
+
+ Resources/core/icons/8-circle.svg
+
+ hash2
+
+ te2RZNxXFdWZFrCidE/vs/2AaVM8tmZGgiCjRXVKGdk=
+
+
+ Resources/core/icons/8-square-fill.svg
+
+ hash2
+
+ WjQi4/5QwhG3Pnyb8f986JihuOHXs1PT4AocV5295hc=
+
+
+ Resources/core/icons/8-square.svg
+
+ hash2
+
+ IYY3qQklfthXFub8wvscuKrLiJ42+Op3blgPWr/xti4=
+
+
+ Resources/core/icons/9-circle-fill.svg
+
+ hash2
+
+ 0zYg+A958V/D2KDGZPdshVy8Q7DcMpGPXuWZA4eXSTw=
+
+
+ Resources/core/icons/9-circle.svg
+
+ hash2
+
+ gBtBiMwPJE3smQjbdhbRcsj4RB24hVTTXm62gswFB48=
+
+
+ Resources/core/icons/9-square-fill.svg
+
+ hash2
+
+ heTsZI4U89RrDvNiJZW0DThy3vj2jMtrSerf4/GC8C8=
+
+
+ Resources/core/icons/9-square.svg
+
+ hash2
+
+ FF/pw1wz0lkZoipye3UexEfQOz2fcaPz/4wS2+ApTDc=
+
+
+ Resources/core/icons/activity.svg
+
+ hash2
+
+ J/wsj6j9+gRP91mdyZReIH0CobhCx49ChAi/mlBl0Lg=
+
+
+ Resources/core/icons/airplane-engines-fill.svg
+
+ hash2
+
+ p2E12d8LmCJ8b7CHve60trxG7yxzY6G5a2+DpyYO/yg=
+
+
+ Resources/core/icons/airplane-engines.svg
+
+ hash2
+
+ yOePuoxYux14pgYqaOxUBr54wAULSegF1rohZl9AmaI=
+
+
+ Resources/core/icons/airplane-fill.svg
+
+ hash2
+
+ yj64m7wVI/ujT5AZMbZFHlkKXK8pt7sbi0ENMVmP6XU=
+
+
+ Resources/core/icons/airplane.svg
+
+ hash2
+
+ i3IA0zdajGMDWeBa7jBG/Bm7O8q8cexBlVMU1e1y1tM=
+
+
+ Resources/core/icons/alarm-fill.svg
+
+ hash2
+
+ 6h1qks2x7oTYWVMpjQb00fbC3vnjvp5GHkx8XxVxvAM=
+
+
+ Resources/core/icons/alarm.svg
+
+ hash2
+
+ /2MDYYMCChXR6fNJux25VJii/H3cGda4zIA5azdBo78=
+
+
+ Resources/core/icons/alexa.svg
+
+ hash2
+
+ O0r0PRHWAg0WT0jagdDvxMtGC052jad8zozLBXHk3I0=
+
+
+ Resources/core/icons/align-bottom.svg
+
+ hash2
+
+ JCZOBWxp+q5qoLUrlTGYdIV6Pr9ao5OMDQMJZiyOpsk=
+
+
+ Resources/core/icons/align-center.svg
+
+ hash2
+
+ rUAhNdkM5gDs2STmSFzFT51pDIKE0IFbbjuG22D15Jw=
+
+
+ Resources/core/icons/align-end.svg
+
+ hash2
+
+ oV1wDFxrkvWfMUNXwV3kqLWhx4BTeuEoy74OD6FLPjQ=
+
+
+ Resources/core/icons/align-middle.svg
+
+ hash2
+
+ Tm7K7OdoubJoIvOBOeDC11fjJRrlHAFEGzekyP8xFPc=
+
+
+ Resources/core/icons/align-start.svg
+
+ hash2
+
+ ey3ss8Gtzg7QaQRy/D0AxOA40XIpqRpqJIz8exsZDQM=
+
+
+ Resources/core/icons/align-top.svg
+
+ hash2
+
+ jE2XhhbEZ9y3PbtTNRJLhweSv9tNIAr0cwaqK8Vuypk=
+
+
+ Resources/core/icons/alipay.svg
+
+ hash2
+
+ YbQoQelni3ivZUSzA1EtAIHxBldSzoKbssbL2lXFINA=
+
+
+ Resources/core/icons/alphabet-uppercase.svg
+
+ hash2
+
+ jpsCwdxW1kfPMxTLirCrnkLDjS1UaJgeJnCIYfFDipE=
+
+
+ Resources/core/icons/alphabet.svg
+
+ hash2
+
+ R5+AvDUeFMaavEl30FHa3F51+7MrsuKtrvY3wGSzMms=
+
+
+ Resources/core/icons/alt.svg
+
+ hash2
+
+ 6gxglSZovn2sV2dfC7GhX4NKjvc6VJ/zxz0K8FbbRjQ=
+
+
+ Resources/core/icons/amazon.svg
+
+ hash2
+
+ PmfB6O9tu3vJGlDz5hq0NSpVNKlg14vAR/IlPhqxpHQ=
+
+
+ Resources/core/icons/amd.svg
+
+ hash2
+
+ 8LvPNBKgfJZ1H83bXzykl/Bls8VTycfTOixsX+mcE1g=
+
+
+ Resources/core/icons/android.svg
+
+ hash2
+
+ cBE4bgUnCd2gPEuv4aR9cBGDyTaZKW/Bpy+3zTdo6z8=
+
+
+ Resources/core/icons/android2.svg
+
+ hash2
+
+ CU5zR2Mds1QUXRf2sPU0Q4Fz6aFrGiQenU6jb95Qt5Y=
+
+
+ Resources/core/icons/anthropic.svg
+
+ hash2
+
+ 5KUQ4b16UQrtfNNqUn4mBatPG1H6NebGPXv5ji5AuX4=
+
+
+ Resources/core/icons/app-indicator.svg
+
+ hash2
+
+ NIMvog5WGxLstgnQ15FHMy3+pS3D5nLIgSUECJMK9RA=
+
+
+ Resources/core/icons/app.svg
+
+ hash2
+
+ rSItDdkTDXXi7a4mVquSR0J7MpcJwsHneAuwEn0io24=
+
+
+ Resources/core/icons/apple-music.svg
+
+ hash2
+
+ q4dC6jdb9bexUUaQuwVlEhZL8F9ZUcF7ucrjIfBZ+sI=
+
+
+ Resources/core/icons/apple.svg
+
+ hash2
+
+ VuR5Wz5CakXYx/POKQOEObg82pZn+a07vLE6SCjEBCA=
+
+
+ Resources/core/icons/archive-fill.svg
+
+ hash2
+
+ RCX629+t/ZeuFZiW6J/jw+hxHufuLtTazJ/AfPdtCW8=
+
+
+ Resources/core/icons/archive.svg
+
+ hash2
+
+ l0S00maKFKwXuA0Wm+iCP3gODmvC9g9iD9Z8YSUwc70=
+
+
+ Resources/core/icons/arrow-90deg-down.svg
+
+ hash2
+
+ EwLSs+NokO6NN+y2kdX4UcB/qRrPQJChl/kTHbR8sCo=
+
+
+ Resources/core/icons/arrow-90deg-left.svg
+
+ hash2
+
+ XBy3PgEOXsNXR6FM1v1ZOOreLU28eGSPuLkKsJLj1IY=
+
+
+ Resources/core/icons/arrow-90deg-right.svg
+
+ hash2
+
+ BLZg8fg7vsx07sRBr/G2XbTp6TUCZL4Le0m/GNeXqOo=
+
+
+ Resources/core/icons/arrow-90deg-up.svg
+
+ hash2
+
+ J7HP7bgysS91BW1OvwbUtVudnFYJ5LTVSOXPEh++WVQ=
+
+
+ Resources/core/icons/arrow-bar-down.svg
+
+ hash2
+
+ K0UZ9n/VQhpfP6S2t7I0XK6mE8QaDPIsh1aK3bI7h8o=
+
+
+ Resources/core/icons/arrow-bar-left.svg
+
+ hash2
+
+ vJ9c21UKb2OSM8/26xHmvZ+daCTQyiSpQmZAnjyPIVY=
+
+
+ Resources/core/icons/arrow-bar-right.svg
+
+ hash2
+
+ bGDbb6DUCpTzMRnQRizebxyjLhLzxbSYdmdYor7goeA=
+
+
+ Resources/core/icons/arrow-bar-up.svg
+
+ hash2
+
+ qERgbPK28ufnOehYyi/090rnDMyd3aGeA6tI7TIpybA=
+
+
+ Resources/core/icons/arrow-clockwise.svg
+
+ hash2
+
+ V/v4mbkfhynlnRUpnzFye5Gl4sRqL2125ed1sjXFioo=
+
+
+ Resources/core/icons/arrow-counterclockwise.svg
+
+ hash2
+
+ YTQ9nw0ILmwrP+EcnZjO/oWqS/SKYqk3vyCzQlFMUOU=
+
+
+ Resources/core/icons/arrow-down-circle-fill.svg
+
+ hash2
+
+ 5i/2NrCHDGMyX3vhIW14FkPjZBwk7JbuledHtjbbaU0=
+
+
+ Resources/core/icons/arrow-down-circle.svg
+
+ hash2
+
+ j491AEdPYLLH1tJR/1OlW9XGy5qdKGdIwi9q5lPIDYo=
+
+
+ Resources/core/icons/arrow-down-left-circle-fill.svg
+
+ hash2
+
+ m/zsetaQCYFH8m5tWW+pSn2I6TWBBuCD/0O7X2IDto0=
+
+
+ Resources/core/icons/arrow-down-left-circle.svg
+
+ hash2
+
+ Ga2JzHfUDNKK8hYqUeH4b5Gaf3KAxrq1mMWPB9Unofk=
+
+
+ Resources/core/icons/arrow-down-left-square-fill.svg
+
+ hash2
+
+ wgxKgqRYQIL7CLwn3mYF/Ci9rEXkhOBrUZd/FbcRqcw=
+
+
+ Resources/core/icons/arrow-down-left-square.svg
+
+ hash2
+
+ PqTedJa/jtS2aJ2pzJnxs1OyOp6WtBkyRV1EuYPtDkQ=
+
+
+ Resources/core/icons/arrow-down-left.svg
+
+ hash2
+
+ tsNRZgC57aGbW0tiLy6zfBSD8tellxzdfq3gB2bUe3Q=
+
+
+ Resources/core/icons/arrow-down-right-circle-fill.svg
+
+ hash2
+
+ EaPcL77Tt2fXuelo7QlnTO1mivbL0cunPLbzpxJQ+Z4=
+
+
+ Resources/core/icons/arrow-down-right-circle.svg
+
+ hash2
+
+ 7VinWjfuceFcmN6axOHHRnZ7y1ANS5lv0P344WQXzok=
+
+
+ Resources/core/icons/arrow-down-right-square-fill.svg
+
+ hash2
+
+ RRQ5gUZVpHgHlpCavaRoyM4vqimQSX+zEcHB4hPQA0s=
+
+
+ Resources/core/icons/arrow-down-right-square.svg
+
+ hash2
+
+ 9UciChR7eKNC/AJnjveQGVepkJWfJcwsKdFWfCOJcvw=
+
+
+ Resources/core/icons/arrow-down-right.svg
+
+ hash2
+
+ I0ulft6khce604vF3kyjRsVWbJlNpBz+qBah6AVzNCI=
+
+
+ Resources/core/icons/arrow-down-short.svg
+
+ hash2
+
+ JSHQFEQqopeug7F2G6kGUmg5sVgcUrln0C7lsvM+bUw=
+
+
+ Resources/core/icons/arrow-down-square-fill.svg
+
+ hash2
+
+ +Z3ckkLfiLyN1BJ/3es/csEcSQCR8M5ExZKr7NOCeF0=
+
+
+ Resources/core/icons/arrow-down-square.svg
+
+ hash2
+
+ KaTmtjBaqIjjVlFoxYnWNY6k/4Zmw86NgiReS++i2fg=
+
+
+ Resources/core/icons/arrow-down-up.svg
+
+ hash2
+
+ bT/v9PEp1a7K8vYP4cuyOGa/C7Nmvwdsmpo2XE4Z0XA=
+
+
+ Resources/core/icons/arrow-down.svg
+
+ hash2
+
+ r7Y+nffSGuaW4GioEiZ1rnwC5yMq2J2QEGMJNvJ7tBI=
+
+
+ Resources/core/icons/arrow-left-circle-fill.svg
+
+ hash2
+
+ cijx80dQxU3EDvI6sGCn7xNCXT33E4jhJ3barJafyBI=
+
+
+ Resources/core/icons/arrow-left-circle.svg
+
+ hash2
+
+ 7APW6YVllfcrMEkK7rAnjOLq+ynIv98FL57UTvcikCw=
+
+
+ Resources/core/icons/arrow-left-right.svg
+
+ hash2
+
+ 45isqVGd5AaMn8QE1AYvl4xgBms74GKn1u5zrA6SXO8=
+
+
+ Resources/core/icons/arrow-left-short.svg
+
+ hash2
+
+ l60rUIq892S+DyxUZUYU+9aWmkuvSZttWF7xQ8ui/fc=
+
+
+ Resources/core/icons/arrow-left-square-fill.svg
+
+ hash2
+
+ DlKjlSJTFrUiq8mV5XfZYrlhmsf99Xw916AuxHa7TFg=
+
+
+ Resources/core/icons/arrow-left-square.svg
+
+ hash2
+
+ p9+7g+LeOQ9ih/0QCQs17FoQQR4P/LCDugdI+GdARXo=
+
+
+ Resources/core/icons/arrow-left.svg
+
+ hash2
+
+ qh0mfjUV+JeYdWUbSLFChfA0lp4vXmo6vve3MpJVq38=
+
+
+ Resources/core/icons/arrow-repeat.svg
+
+ hash2
+
+ Cs6+B9x6WDZlKDTx3YvINg9XRrWn1nbri0CT/iBPoL8=
+
+
+ Resources/core/icons/arrow-return-left.svg
+
+ hash2
+
+ y44c0wnC8+RhE57jiJIFcu8zpJTbKAIEWb2HJ74N9sk=
+
+
+ Resources/core/icons/arrow-return-right.svg
+
+ hash2
+
+ 6FYTD9rWycMqVTw5ZVCifpfm4M9qJUpeAcWwEuldIfk=
+
+
+ Resources/core/icons/arrow-right-circle-fill.svg
+
+ hash2
+
+ YIjreP/CJ3OHHhwbiS/pixG7uyeZr5rqvTA8wDvQnvk=
+
+
+ Resources/core/icons/arrow-right-circle.svg
+
+ hash2
+
+ XkTHI2dbPcBU0lwIdTJDFCxIf59E/bO7gMOY3V70uYg=
+
+
+ Resources/core/icons/arrow-right-short.svg
+
+ hash2
+
+ FUxWSGsQOrqvpMOPlbeo8/GkwzO4Ch/1DqPuftxfjZU=
+
+
+ Resources/core/icons/arrow-right-square-fill.svg
+
+ hash2
+
+ krfZcxugR8hfnwlkEP7QiUBUvmamYF9CQ0dSk0dfynY=
+
+
+ Resources/core/icons/arrow-right-square.svg
+
+ hash2
+
+ 5diJYUiIwyA9InOzEI/8b624/OHEhSAiFrDFKhZz/EI=
+
+
+ Resources/core/icons/arrow-right.svg
+
+ hash2
+
+ mvEFEQXa/ovEZwsGMwrZx+A2dlxj+deLMeXOB+Y7nQQ=
+
+
+ Resources/core/icons/arrow-through-heart-fill.svg
+
+ hash2
+
+ KSbGUXAZ9gRsPL1rIZ5ueBuUZjOl+lYd+4h6sNmVNuA=
+
+
+ Resources/core/icons/arrow-through-heart.svg
+
+ hash2
+
+ ZDjt6lyto5P6gn50EIAdPhAXSvKx8xQOjvLWBGx9ahk=
+
+
+ Resources/core/icons/arrow-up-circle-fill.svg
+
+ hash2
+
+ aPaUyVWaPuASxo3ZaPje3hCvj7EBsedm65AO2R8HtIE=
+
+
+ Resources/core/icons/arrow-up-circle.svg
+
+ hash2
+
+ ZWv0x2dHJsta0Q9Q3l4KAJafD4PrX/29uRuthZobRL8=
+
+
+ Resources/core/icons/arrow-up-left-circle-fill.svg
+
+ hash2
+
+ wj/NibvO+UwjhGJzjzjY0ZD8rHq4W3MQPh/RH2QQSqA=
+
+
+ Resources/core/icons/arrow-up-left-circle.svg
+
+ hash2
+
+ fs9d8byEV0ghOwzr4QMr6ckJXCOXZWLjpQwA/lwKl88=
+
+
+ Resources/core/icons/arrow-up-left-square-fill.svg
+
+ hash2
+
+ 7MJZIZ9LafPScRXxCcxi1kZf6q/0MiZFd+aFk9vrUxE=
+
+
+ Resources/core/icons/arrow-up-left-square.svg
+
+ hash2
+
+ 52sTdoGCLVWxux/ontwl0Rpc7/V+3mUHV1fSmINvjFA=
+
+
+ Resources/core/icons/arrow-up-left.svg
+
+ hash2
+
+ Y7hDWYc9+RXCQaFDtfmWqo4a+jl/lEq167c/ppqKDsg=
+
+
+ Resources/core/icons/arrow-up-right-circle-fill.svg
+
+ hash2
+
+ 7d3Kg8sQz5rMuiZRnK9SwJAZix6uhBWYM9uvdUkrNnM=
+
+
+ Resources/core/icons/arrow-up-right-circle.svg
+
+ hash2
+
+ TKUZjUCCoVqW9vIvUo8H5bIfJK/ROqwvb4SLS1Pb4SY=
+
+
+ Resources/core/icons/arrow-up-right-square-fill.svg
+
+ hash2
+
+ DKIKSgNteZ/BSHuDwQr3X8zAPhGBo+Zov0yQ61VfCg0=
+
+
+ Resources/core/icons/arrow-up-right-square.svg
+
+ hash2
+
+ GxKTom4n6lH/2uXmrrBtkuepWLgZ8z4hqqOCGT+twro=
+
+
+ Resources/core/icons/arrow-up-right.svg
+
+ hash2
+
+ 7vbQ3vB0ZN5PCUDURHlWftpp9z7Wc3SLw6KvUjJvq6M=
+
+
+ Resources/core/icons/arrow-up-short.svg
+
+ hash2
+
+ 7Kqf3Bg3nCRXhy+oMtTVjpulwJm+U8XeZ9KbWNvAqDY=
+
+
+ Resources/core/icons/arrow-up-square-fill.svg
+
+ hash2
+
+ dJcKYHjxJCiRKTbBw6Dyp2D2QssHVWWsmuF5mgwuWOI=
+
+
+ Resources/core/icons/arrow-up-square.svg
+
+ hash2
+
+ KYoYolrR+0Iw6idvC8geFAZblQAPrOJFOuCSPfNcfQo=
+
+
+ Resources/core/icons/arrow-up.svg
+
+ hash2
+
+ ALINMZDXtJfjWQBehOeDeJA8mrNriWm6DFFw61aDTKs=
+
+
+ Resources/core/icons/arrows-angle-contract.svg
+
+ hash2
+
+ aUAzJ/p3rcrP1R5x+Lcee/iE7yNMfN3/7q9I/9CwUJI=
+
+
+ Resources/core/icons/arrows-angle-expand.svg
+
+ hash2
+
+ BR2dB879Gk1qt4vZc8MeMt3taZ6IaOxezQRkyQxBZDU=
+
+
+ Resources/core/icons/arrows-collapse-vertical.svg
+
+ hash2
+
+ C4TQSdVGxTGxgsRkCArRhND9o7EYLK7G+UJiTuJs/c0=
+
+
+ Resources/core/icons/arrows-collapse.svg
+
+ hash2
+
+ E9en5TNzeXoBgqmzkkWHuezl1yB57hlns/qFBnlrjdI=
+
+
+ Resources/core/icons/arrows-expand-vertical.svg
+
+ hash2
+
+ ZuW5TQncYsebtlaLVFigPmsZPbeyinCXOuM37ldTaJY=
+
+
+ Resources/core/icons/arrows-expand.svg
+
+ hash2
+
+ 5Q15k7XqXWxBtDL/D3sUFmLrMxjYENcJCeEkK9EvOvg=
+
+
+ Resources/core/icons/arrows-fullscreen.svg
+
+ hash2
+
+ EudkR73KNw6XLrjjK0aVdTFc7CwBxdbr1i6THwmQioE=
+
+
+ Resources/core/icons/arrows-move.svg
+
+ hash2
+
+ hkznOC/Lco7XVgfgemPD/W7fm40MfSzy61se79pVcLs=
+
+
+ Resources/core/icons/arrows-vertical.svg
+
+ hash2
+
+ Qce5p7oiH7KCQDMhvBcOxxJ1tP5O9awCt7hKnxAdY1Y=
+
+
+ Resources/core/icons/arrows.svg
+
+ hash2
+
+ c23pdV9rGLTaew7b6LjCIlmLPxBph8X4d07DFX36f/I=
+
+
+ Resources/core/icons/aspect-ratio-fill.svg
+
+ hash2
+
+ HWDJzxGYSExUQycSwFRrHXKQDrMFIevgiwYHORnCIwA=
+
+
+ Resources/core/icons/aspect-ratio.svg
+
+ hash2
+
+ E1qd14c7+ChXci9FXUKzhm2Bz6U3DprgSGZyHgBMKOE=
+
+
+ Resources/core/icons/asterisk.svg
+
+ hash2
+
+ 0zPEckq23tImSoK1OjG4JpuannxpfUq4Tw/WJX7WLfo=
+
+
+ Resources/core/icons/at.svg
+
+ hash2
+
+ E27XjL3QBR60wBeyLIQnaklnNexChkVsqLBCH2abFus=
+
+
+ Resources/core/icons/award-fill.svg
+
+ hash2
+
+ EJAMkNMFN/WvqX+kFC2SvHysAZcEEHhrWFthWMeKd84=
+
+
+ Resources/core/icons/award.svg
+
+ hash2
+
+ Wvq4c5lKF4CDZfll2hYOeIHMhFUGDbT6QxaIvPdLoyw=
+
+
+ Resources/core/icons/back.svg
+
+ hash2
+
+ avpG5/fpStZEIx5SKpdBLQZKFt7Kwxdm6Cz+AsR8AQQ=
+
+
+ Resources/core/icons/backpack-fill.svg
+
+ hash2
+
+ gUyKiI9QnR/bdaH1ANoTeepr0Ep0mErao1QzfgfGGwM=
+
+
+ Resources/core/icons/backpack.svg
+
+ hash2
+
+ 23dv1CE3Qcl2CqfPkM7G1heLoF2/mtFtDWbDa0Nkqvw=
+
+
+ Resources/core/icons/backpack2-fill.svg
+
+ hash2
+
+ DQwxZObAeKgPx62FzhCRTtOIFsJhm/ofdicGFuQRMT8=
+
+
+ Resources/core/icons/backpack2.svg
+
+ hash2
+
+ ZY1FaWjXuDx33zP1cTJbDl3pWonk7zzyHZ+xsnBkLjU=
+
+
+ Resources/core/icons/backpack3-fill.svg
+
+ hash2
+
+ duNfb6uNX2JBJRbEYUlBo/RgyePrl3cX+s2TPPxD8hQ=
+
+
+ Resources/core/icons/backpack3.svg
+
+ hash2
+
+ 9pQSvzf2RVTtf5/ryrBd+pu4FahW7EeHTmAGkPZYIV8=
+
+
+ Resources/core/icons/backpack4-fill.svg
+
+ hash2
+
+ k16Ya55DYSiziv7TyvTBpE1ERnEtaB4hBQCPumMnSi8=
+
+
+ Resources/core/icons/backpack4.svg
+
+ hash2
+
+ 49EnC6NFSxVkyH2hmQg7VVa7X/Fn1HhjiNfO6lsaUlo=
+
+
+ Resources/core/icons/backspace-fill.svg
+
+ hash2
+
+ Ge95kZ6gXJGW+xe/ght0aJlH9c6SJSq2y9RcbkECWz8=
+
+
+ Resources/core/icons/backspace-reverse-fill.svg
+
+ hash2
+
+ 4gn7zg4KkDoE84oU0i675Ro9Uj13KYbTK9Yen4CeFCY=
+
+
+ Resources/core/icons/backspace-reverse.svg
+
+ hash2
+
+ 8kIDyvbVdVpSon45pyTSb10gKQ2CYZHxEufD73pTiKA=
+
+
+ Resources/core/icons/backspace.svg
+
+ hash2
+
+ y/ixa1pNTCDE5WDVoNn57dip1oCxHqZzevK6YsiQ/+w=
+
+
+ Resources/core/icons/badge-3d-fill.svg
+
+ hash2
+
+ r3n1k4fWmJOneaDzrLHNDEWWbHk0u8Pudjkc87y4FEw=
+
+
+ Resources/core/icons/badge-3d.svg
+
+ hash2
+
+ JTCkwp9BOWX5Bohm6eSdIbDuKzwtiLeGTJ/LDXEMxgg=
+
+
+ Resources/core/icons/badge-4k-fill.svg
+
+ hash2
+
+ Q7HGe6BvFggkiqFM6ZWcUa8Lk8KWGymgTVkiaZ3utvo=
+
+
+ Resources/core/icons/badge-4k.svg
+
+ hash2
+
+ VhSFKytjdUJwpXKYZ3xZai/riRFYbU/tRUWqSn5TFIY=
+
+
+ Resources/core/icons/badge-8k-fill.svg
+
+ hash2
+
+ Yeg7A6RIzqyRC7883WllaHRcx+NpOlT2uPYC2N28xUE=
+
+
+ Resources/core/icons/badge-8k.svg
+
+ hash2
+
+ 77XyLONJJ2XkHjCU70sPWOvOBsSnt44BU2deO8qYruw=
+
+
+ Resources/core/icons/badge-ad-fill.svg
+
+ hash2
+
+ 6pflI3cnHe2xr0LQnFoOvXKd+JQaSWrRhr2k/aSGKNo=
+
+
+ Resources/core/icons/badge-ad.svg
+
+ hash2
+
+ 8Iu2X0iDInw0C7LMOYNUGU/H8s/MPO3aGSvzKTgq+ho=
+
+
+ Resources/core/icons/badge-ar-fill.svg
+
+ hash2
+
+ nCj7LsSwcgCKGqrukGXiJKsiQeRvDRw4MPRMg2LXXqk=
+
+
+ Resources/core/icons/badge-ar.svg
+
+ hash2
+
+ kWVPyShHcEr6kabKZEW5J0OOpnihbOr6Y9qL0JYsM78=
+
+
+ Resources/core/icons/badge-cc-fill.svg
+
+ hash2
+
+ VpkGhjAtcItsAAgUiHxZang/fBKLSW6DT0WhewBN1sw=
+
+
+ Resources/core/icons/badge-cc.svg
+
+ hash2
+
+ UmVR2O/ybaUBVwRrTyW0UFUmpr4yoaDwJStJsj54wAU=
+
+
+ Resources/core/icons/badge-hd-fill.svg
+
+ hash2
+
+ S5pzZuG5Xx78Rq4D8mCAchG2l8ZjJXQMjiotGxeU0lY=
+
+
+ Resources/core/icons/badge-hd.svg
+
+ hash2
+
+ 4UgU+ykbe2Uz/qJ5GjweUSJDEWJSa08Bak+lRQEq3kk=
+
+
+ Resources/core/icons/badge-sd-fill.svg
+
+ hash2
+
+ zePuLouPUDvtVyJPkR+s1V6TczOllF1lCWkpq90Ach4=
+
+
+ Resources/core/icons/badge-sd.svg
+
+ hash2
+
+ 6vqmLMsX4McE16BgQrRjho7aQWHLK4qXIzFJYzaw4YA=
+
+
+ Resources/core/icons/badge-tm-fill.svg
+
+ hash2
+
+ 0zhvCJraKZ0fX568mfBDoJB3vfOOg11CCyfr24enzaY=
+
+
+ Resources/core/icons/badge-tm.svg
+
+ hash2
+
+ trGeUQnblmPRb9g/pWJyOCo9kS0cxI/GpFTEyhgeo4E=
+
+
+ Resources/core/icons/badge-vo-fill.svg
+
+ hash2
+
+ 4SQax5vWFidvvnKMtxSryPexv0xB1IvqulOXcybZhm8=
+
+
+ Resources/core/icons/badge-vo.svg
+
+ hash2
+
+ xhMQ5GYc3eij9sj4WBbihhTM6U6fFeE0TkpIkOq7YR4=
+
+
+ Resources/core/icons/badge-vr-fill.svg
+
+ hash2
+
+ NMjjsYmIm5fAV1Mfu4wEhixj/HGwE1pFB6vZkVTngpg=
+
+
+ Resources/core/icons/badge-vr.svg
+
+ hash2
+
+ ojBJGnK21m9GPuCBHa71ns+5r0P/vVSH7N1nfcviA6I=
+
+
+ Resources/core/icons/badge-wc-fill.svg
+
+ hash2
+
+ JYGJUDabIBCRoXNubZeezjC6J6Hx4EnfUg8FQwzK09M=
+
+
+ Resources/core/icons/badge-wc.svg
+
+ hash2
+
+ eAQShfJVjL8vFgwjTDMgWFcOyDApjEvdlg7abcCPpww=
+
+
+ Resources/core/icons/bag-check-fill.svg
+
+ hash2
+
+ REXvyag5DniJvKKm0xgBSOiIBpeHdj3GkReN3uoK/G0=
+
+
+ Resources/core/icons/bag-check.svg
+
+ hash2
+
+ DlVQtCb/YyUDdl4ZZRF0Aj0kPd7VIbxLY31xF4UVR6I=
+
+
+ Resources/core/icons/bag-dash-fill.svg
+
+ hash2
+
+ mwkFs0ehFC1GJ9ngPgMo3ydIENDzCGVus+jxzld3Mcs=
+
+
+ Resources/core/icons/bag-dash.svg
+
+ hash2
+
+ VzliNDCAk5hC1pCATIFKMb1DotGk8NjPoTpt8LtO88U=
+
+
+ Resources/core/icons/bag-fill.svg
+
+ hash2
+
+ e3tU2Oc/AGVwzLs5eyw4Zq1jr3YH7voCQAsl3YX1LSg=
+
+
+ Resources/core/icons/bag-heart-fill.svg
+
+ hash2
+
+ yYQkI6MadMmCDi5rU4Mph8egRjeSjQVF2j4+2cFhXnw=
+
+
+ Resources/core/icons/bag-heart.svg
+
+ hash2
+
+ c5abo2YgBE1O7a6jiXkpB5yBYvdAiGGvSqvuuOLKMfU=
+
+
+ Resources/core/icons/bag-plus-fill.svg
+
+ hash2
+
+ bN3SSYV9Fn07tAl2Jaz75YQJB9t05hrQ+MMaGbe2Ryk=
+
+
+ Resources/core/icons/bag-plus.svg
+
+ hash2
+
+ Gi8VPRqiSbaZMySuul2vH7EI0rnR6m2k1Rc5xRSceV4=
+
+
+ Resources/core/icons/bag-x-fill.svg
+
+ hash2
+
+ iEADPL1+Z4LEFa/W3WbizckQW5cd1yVVgdW5V1M5OwA=
+
+
+ Resources/core/icons/bag-x.svg
+
+ hash2
+
+ gxsabsqOpIQLzFFtfep7JOn/t3KdGSQvZcK0q1h7p0o=
+
+
+ Resources/core/icons/bag.svg
+
+ hash2
+
+ 098f3Tx9NKGBG5R1Em9KGy9HjPKn8V0RoIiHMn1hwos=
+
+
+ Resources/core/icons/balloon-fill.svg
+
+ hash2
+
+ X3MA0hY+scu51KOkncbxdZ7VEsU0eEmwlwLpZTpAP/Y=
+
+
+ Resources/core/icons/balloon-heart-fill.svg
+
+ hash2
+
+ aKL9BHaMjzdpwa6oCopjtW9zDLhD28ONvqp+QvaCBjI=
+
+
+ Resources/core/icons/balloon-heart.svg
+
+ hash2
+
+ 7FUlIccNzhWinJixIytmT0snapiJVtDd2T7WRq8Da+0=
+
+
+ Resources/core/icons/balloon.svg
+
+ hash2
+
+ RP5dHUTL8vqffFIvZmKyv+mKv/NM/59TuzKnW+F5SzE=
+
+
+ Resources/core/icons/ban-fill.svg
+
+ hash2
+
+ qOaxlKfkiykkmvF1kWDVhVJZqd/vMdHy7WrX6Xkp0mg=
+
+
+ Resources/core/icons/ban.svg
+
+ hash2
+
+ UMcQU/Sf6tXDvZDTdcMe3KXChClJE4tg9kHlAYMftwM=
+
+
+ Resources/core/icons/bandaid-fill.svg
+
+ hash2
+
+ DzUd9/NilA9DrjHOhLJL0VtDnBGRKUmsxQDbYP4H3Gg=
+
+
+ Resources/core/icons/bandaid.svg
+
+ hash2
+
+ ecuE+7Hvc2chB6LH7ABY4l67CJmJ+j7hur/WPVyFZco=
+
+
+ Resources/core/icons/bank.svg
+
+ hash2
+
+ YinnH5DSsq+S7S6BhsDIV9Dgz4ALt+WxEjOcgIyK34U=
+
+
+ Resources/core/icons/bank2.svg
+
+ hash2
+
+ xW7Rr9f5/EwCSFQ/qy7vudPfAe2gq1xAc2lfW7jwfik=
+
+
+ Resources/core/icons/bar-chart-fill.svg
+
+ hash2
+
+ j48qcUFriy0762SYjBFCaddF8XtH7o62hdfVcyV/4Es=
+
+
+ Resources/core/icons/bar-chart-line-fill.svg
+
+ hash2
+
+ MTJwf7QkJZ2C+pRR2T9GmFCyTInukMg/XqcKm2E3NSg=
+
+
+ Resources/core/icons/bar-chart-line.svg
+
+ hash2
+
+ NPqAFUR6fOny172T92mwcQjaKnNdag3xBa/CHeIVAMk=
+
+
+ Resources/core/icons/bar-chart-steps.svg
+
+ hash2
+
+ UJ1pJgJVysLyppTpjuJRAncr2Kz7NDxdgxZm1B7p1zY=
+
+
+ Resources/core/icons/bar-chart.svg
+
+ hash2
+
+ q9WJ54yYNu2DhnuPX6hJfX1XWkKgnemTR14pZnkuc7U=
+
+
+ Resources/core/icons/basket-fill.svg
+
+ hash2
+
+ YNhnxD/jhtJPAcWRnsjkM7oz536tv3Y+r8rYz2nh3xo=
+
+
+ Resources/core/icons/basket.svg
+
+ hash2
+
+ +k6Y2UQ+lS+VS+cMAZy7DByF3Hilij1utdV1ynoZXu8=
+
+
+ Resources/core/icons/basket2-fill.svg
+
+ hash2
+
+ T3Ne5uXiwmDfpHKiTcJLUGVUTca/Tud8+rXjX0iOIaQ=
+
+
+ Resources/core/icons/basket2.svg
+
+ hash2
+
+ BhMGgs7L5P80Z/vn1nUmluSYjQuKWM4zODOD9Lws5CI=
+
+
+ Resources/core/icons/basket3-fill.svg
+
+ hash2
+
+ xfeSJjwSPs0jmwHbNUEVw+FGJJQG4OiNrEvvJGPzKfU=
+
+
+ Resources/core/icons/basket3.svg
+
+ hash2
+
+ JbYrhJSwjJNtZyH1I7EmafeQEBChIfpm/HpI0Po1JME=
+
+
+ Resources/core/icons/battery-charging.svg
+
+ hash2
+
+ v8aTbKXBK5h4t6/pctYNK3UeXWgVHhigvSMbFEuRbGA=
+
+
+ Resources/core/icons/battery-full.svg
+
+ hash2
+
+ kOu/LzuZ9rWt84opc2131ktQ20XYC152P0DDgndgFMU=
+
+
+ Resources/core/icons/battery-half.svg
+
+ hash2
+
+ 2eqma2Q7CkYBhdZU6bZLTKsxYpZwoxr3OTlMRT96Cf8=
+
+
+ Resources/core/icons/battery-low.svg
+
+ hash2
+
+ KO4w710/MYv9gx62j65T5czWYff1kEZDlDURaclidVg=
+
+
+ Resources/core/icons/battery.svg
+
+ hash2
+
+ 69VIz5DnEg2sntM3Tdcaf3pR/Gi9Jv0OH40HA1ceHEc=
+
+
+ Resources/core/icons/beaker-fill.svg
+
+ hash2
+
+ M3aSG7umhpEhl3xPsBC4TnukGoJ2KJHMBPY2MpL3mVI=
+
+
+ Resources/core/icons/beaker.svg
+
+ hash2
+
+ LYe/r0EjXQyuGvOzOy/pedD/5EFTXuBsmBovJqde7Vc=
+
+
+ Resources/core/icons/behance.svg
+
+ hash2
+
+ kIvoaOSvAmiAfc4unZz+6M3hwXLOUNURavnTFgbVl68=
+
+
+ Resources/core/icons/bell-fill.svg
+
+ hash2
+
+ AlpZriB31oH8Fzdzjx5tJMU/4+hQkXuKrtafnCAkWE8=
+
+
+ Resources/core/icons/bell-slash-fill.svg
+
+ hash2
+
+ DY4z7Uiz0m1jRxUNcDqvykNgpe2gI1J3XEaoPEh8Erg=
+
+
+ Resources/core/icons/bell-slash.svg
+
+ hash2
+
+ m+FkLUN8hblBE3xPnNBvUcTkPggQQ7ypbrtE6+wCE+Q=
+
+
+ Resources/core/icons/bell.svg
+
+ hash2
+
+ tJx2yTXBtlRMssJngouCaixB0EaqQ18+jvL9Asxh+9I=
+
+
+ Resources/core/icons/bezier.svg
+
+ hash2
+
+ 578kRJqg6AUjPYKGKC/uKLQMLBFu4EEn9hBMP5pd714=
+
+
+ Resources/core/icons/bezier2.svg
+
+ hash2
+
+ 8Kx04QuDNhDOUzuzJaL3Lp2kYb5ClsakIcJK7enpFJI=
+
+
+ Resources/core/icons/bicycle.svg
+
+ hash2
+
+ gDcqWP6SuIveTStHE2F35lkP4Q+xhXKYiHeFmumfqAs=
+
+
+ Resources/core/icons/bing.svg
+
+ hash2
+
+ NWKGOk6SRZxvIMxHH3AvB4ihWIbzOy8gZ31QCyxXUgI=
+
+
+ Resources/core/icons/binoculars-fill.svg
+
+ hash2
+
+ eTZYxeBiK5K8bq2fH//tb6wPgfPrYljlx1Yt7emW7hE=
+
+
+ Resources/core/icons/binoculars.svg
+
+ hash2
+
+ Goi81EpGz5NMaPiFE0WQemw8TYO0H3ju9wr/tzH33ow=
+
+
+ Resources/core/icons/blockquote-left.svg
+
+ hash2
+
+ ORrDWA+sqoofzesJDnhPNj1H/984fuqoclsAhIwiBkE=
+
+
+ Resources/core/icons/blockquote-right.svg
+
+ hash2
+
+ 6hDKGXdKddH+rj8/EcymwaFmkMhN/zt3zgfkRH6WU8A=
+
+
+ Resources/core/icons/bluesky.svg
+
+ hash2
+
+ spTznmLMN8/HqUoGZDR8q7Kn5Tlk+7jODKhu+VDPTs0=
+
+
+ Resources/core/icons/bluetooth.svg
+
+ hash2
+
+ F3W2R23p0GtNQ5zp55nWImWlifzF+9UUQKWKCxESL38=
+
+
+ Resources/core/icons/body-text.svg
+
+ hash2
+
+ wdtHFIK2Z2NGgpeTSB6fOVb8B0urqwQQEjx+3U/nrrQ=
+
+
+ Resources/core/icons/book-fill.svg
+
+ hash2
+
+ /831oI6vlutyLG3L9dGhbn1pfHBppig2zMR5qPpagX0=
+
+
+ Resources/core/icons/book-half.svg
+
+ hash2
+
+ SC6p7gNTmZvIcVK/BNlyeO/EmUVZWN/ZRDIMoSmtBWc=
+
+
+ Resources/core/icons/book.svg
+
+ hash2
+
+ 5RIoeNfxbwWFvc6z+YW2ffP6jgm2ZiWsWLVbqd9e6X0=
+
+
+ Resources/core/icons/bookmark-check-fill.svg
+
+ hash2
+
+ fDx+32SRkGIUG5cSm5KRZBxxKB0Fp8yW39hh+86dcs8=
+
+
+ Resources/core/icons/bookmark-check.svg
+
+ hash2
+
+ 1u/0AobiSWMEtAFM3xpEbUtiGiqGkNNkWcIin3hauEo=
+
+
+ Resources/core/icons/bookmark-dash-fill.svg
+
+ hash2
+
+ DoGx8Dp9Mnlm4cir0tmwfZjLRlmhx3eGjI0s8gRF4+w=
+
+
+ Resources/core/icons/bookmark-dash.svg
+
+ hash2
+
+ rOoJHnNCpfe0+BqIqblTeQ9nPM+Hl8HhLgFOBgKvO0g=
+
+
+ Resources/core/icons/bookmark-fill.svg
+
+ hash2
+
+ eTzlPXG0kampQxykSrcp0DUjLZwR3E1OhjXurT6NkPg=
+
+
+ Resources/core/icons/bookmark-heart-fill.svg
+
+ hash2
+
+ CwDy8rE1tZr2QM7WTUxeydZlBWsYIUuTlXP5vaAjE5I=
+
+
+ Resources/core/icons/bookmark-heart.svg
+
+ hash2
+
+ VM3JAUxF04tN5f51+LDKcYnfNHF5m5Zegnv8pBSqM6I=
+
+
+ Resources/core/icons/bookmark-plus-fill.svg
+
+ hash2
+
+ nT9oHhk/r3STrr1KNpJFF/sRww++/p7Bb/ZIuC3IjKo=
+
+
+ Resources/core/icons/bookmark-plus.svg
+
+ hash2
+
+ kpUEmkgJvzKdqpoD27sG6P9Y1cSUhPSCuIcNEnb4r/U=
+
+
+ Resources/core/icons/bookmark-star-fill.svg
+
+ hash2
+
+ 6aKK+KURPiKXEYgYeaG36bwwlK3HzRQUJYuapePvJCE=
+
+
+ Resources/core/icons/bookmark-star.svg
+
+ hash2
+
+ /O8gL7fUIQMG0WFMjTbr2lNqSlwDTVOZmdz1qBZCl0w=
+
+
+ Resources/core/icons/bookmark-x-fill.svg
+
+ hash2
+
+ dNwiQAN9bQVUSwyMOem1LSbHG8gVzGe+RHlPzi3O29o=
+
+
+ Resources/core/icons/bookmark-x.svg
+
+ hash2
+
+ ngJScBLUyzy51mes+ZjUQAxdR5GBjxAUxoe2KOHoCuQ=
+
+
+ Resources/core/icons/bookmark.svg
+
+ hash2
+
+ vE42e3nWJiib9k/F6EywP93Jkf8XAs9Ci5sVyDK/9XM=
+
+
+ Resources/core/icons/bookmarks-fill.svg
+
+ hash2
+
+ /1q48VKa+pHrHLOu64JNfikIeMdhmITbcCjTy3m63UQ=
+
+
+ Resources/core/icons/bookmarks.svg
+
+ hash2
+
+ UAdUI7juooIBp+/vhzZwEqjnFJFE1YM4xWttLiNKixI=
+
+
+ Resources/core/icons/bookshelf.svg
+
+ hash2
+
+ w4aIjKD6Xa0pO4VXeDLhvwZZ9UqaV03Y5pKjUULa7iw=
+
+
+ Resources/core/icons/boombox-fill.svg
+
+ hash2
+
+ AF76OudcJW2XukZFWi1FLwqHTLAIcxR+BLewWx5SXp4=
+
+
+ Resources/core/icons/boombox.svg
+
+ hash2
+
+ 7kMwKT2MuiUbqb7S8ROGdpLT/G/9D59VocMhavLj8XE=
+
+
+ Resources/core/icons/bootstrap-fill.svg
+
+ hash2
+
+ t9lG+FoBz5GPIQLyYvAypL+jE6TyfoetWrkJk0kX1CI=
+
+
+ Resources/core/icons/bootstrap-icons.css
+
+ hash2
+
+ AEMichyFVzMXWbxt2qy7aJsPBxXWiK7IK9BW0tW1zDs=
+
+
+ Resources/core/icons/bootstrap-icons.json
+
+ hash2
+
+ HJ3h46qqG7pZZ7rH6tp5R1CC3Ya0pBlV5Y08JWmyPPA=
+
+
+ Resources/core/icons/bootstrap-icons.min.css
+
+ hash2
+
+ pdY4ejLKO67E0CM2tbPtq1DJ3VGDVVdqAR6j3ZwdiE4=
+
+
+ Resources/core/icons/bootstrap-icons.scss
+
+ hash2
+
+ Wl4+JO5suK8BkJCDXbUj6Pnj1guy6s8mdASQtqRdD78=
+
+
+ Resources/core/icons/bootstrap-icons.svg
+
+ hash2
+
+ /nthMD7osWCqLVltq7tw9P/q+OokiRAE6bHQ0od2OLE=
+
+
+ Resources/core/icons/bootstrap-reboot.svg
+
+ hash2
+
+ jjP9onmvSLwObdv6ubcWFbSe6Kcpz3CgNnScl5wm+Bs=
+
+
+ Resources/core/icons/bootstrap.svg
+
+ hash2
+
+ yildZa2jun9CZCeN7aq3wYQgSWF9pBzBk8NTHGOtXdk=
+
+
+ Resources/core/icons/border-all.svg
+
+ hash2
+
+ aCWbFIqeN2DGwcrlRQh31e3srbToGu0YuilKo81y76E=
+
+
+ Resources/core/icons/border-bottom.svg
+
+ hash2
+
+ tKmpc9IFBrHU/brqGPDZEMzeo9Bzsc5nO46p91tY+sA=
+
+
+ Resources/core/icons/border-center.svg
+
+ hash2
+
+ aZpXDqFhVcfEqLETLj5zMRvI8FSiWBPmvBH9TC2Zp1g=
+
+
+ Resources/core/icons/border-inner.svg
+
+ hash2
+
+ S9NlHpHtVgtSs0nZQWtwBfPoq8yZ4Tgd0FGEk1q2iv8=
+
+
+ Resources/core/icons/border-left.svg
+
+ hash2
+
+ BFtBE55KcMcAkGOuda7QIghWFPmgs3LMkDhrUyhns54=
+
+
+ Resources/core/icons/border-middle.svg
+
+ hash2
+
+ zRhFwZ2hHWUr+i7s53OJppYcNBuc0ITMm8AkS/kntos=
+
+
+ Resources/core/icons/border-outer.svg
+
+ hash2
+
+ mKqUqMVKpAIBCZHc73tm0EoNbYBDSb4KyTPdps4H1ZU=
+
+
+ Resources/core/icons/border-right.svg
+
+ hash2
+
+ gccs+BnPpxYJ2xcn3Cn/PgMXPUQzTdpwevyPlAxpaTc=
+
+
+ Resources/core/icons/border-style.svg
+
+ hash2
+
+ xb1aLtVYUJh11qut95Rz89a9vgXYspFc3FVMQAttruc=
+
+
+ Resources/core/icons/border-top.svg
+
+ hash2
+
+ ZeX0odesSG1TsvdXooB0bPy/je31KFfIcQ61MGYO6+g=
+
+
+ Resources/core/icons/border-width.svg
+
+ hash2
+
+ ji1H8uXcVVhm/bkmNt/YWgAKYdJbqCM+dPP0scMrTZs=
+
+
+ Resources/core/icons/border.svg
+
+ hash2
+
+ 6z+OgT7G9Va0zBUpzx8pY9kxyrSeBEkQZA3vAi/b6Qc=
+
+
+ Resources/core/icons/bounding-box-circles.svg
+
+ hash2
+
+ rPdKjmmmYedo/soj5RtLa13qa/qkRXSrj3nVBO4LBS0=
+
+
+ Resources/core/icons/bounding-box.svg
+
+ hash2
+
+ rmZ8F9jVA4BIurpfXJrr9VSO+GEcHZGZbeiEnQioOME=
+
+
+ Resources/core/icons/box-arrow-down-left.svg
+
+ hash2
+
+ oyIFeoIoYjxDkuEnvPlmNQD2+FOXsc5AsUE0bR6Legw=
+
+
+ Resources/core/icons/box-arrow-down-right.svg
+
+ hash2
+
+ /l7II4xTFAN5zV/zF6ahtWfRlfHJ39jAX8Yu4MCnM1Y=
+
+
+ Resources/core/icons/box-arrow-down.svg
+
+ hash2
+
+ lHU0Y0sDGKl1n06m/SCS7NnkiBFgGlacmyDHAXhmFv8=
+
+
+ Resources/core/icons/box-arrow-in-down-left.svg
+
+ hash2
+
+ pGkAuerxwM1uJydYHEvCH2HdsBt5SW20iXfH1APCn8k=
+
+
+ Resources/core/icons/box-arrow-in-down-right.svg
+
+ hash2
+
+ vQ5uC/lithejMPxiNU2vMoPuhWCl+iESw8nvBF1BmKI=
+
+
+ Resources/core/icons/box-arrow-in-down.svg
+
+ hash2
+
+ XKg37VSR3DKULaYBdQeME4hnN8jNe66bWm4uaVlhRZc=
+
+
+ Resources/core/icons/box-arrow-in-left.svg
+
+ hash2
+
+ giX0Tb3l4jiXOUFJ6Ia17wS524AQq+mzGtUiO8CoKaA=
+
+
+ Resources/core/icons/box-arrow-in-right.svg
+
+ hash2
+
+ a3oNdlisMMTUrBWkJ4FhMYIbMdy4aS/LvGcDVA4DzD0=
+
+
+ Resources/core/icons/box-arrow-in-up-left.svg
+
+ hash2
+
+ JjbwRuM6uNCQO0zEupoL2mVyS9XiO2JWgsxhE067hDU=
+
+
+ Resources/core/icons/box-arrow-in-up-right.svg
+
+ hash2
+
+ s8eZcr7KkDLYpjyivMZAz94oSqA/v29YUdYgJpIAMM4=
+
+
+ Resources/core/icons/box-arrow-in-up.svg
+
+ hash2
+
+ BIop7CvmOfHgZQG3ivo08QHyAuIeJsKt5B4P3hL/8KU=
+
+
+ Resources/core/icons/box-arrow-left.svg
+
+ hash2
+
+ sjS8HxATaBooTZ/PQlldM99rQA2bU5ofxdA/qTaAleo=
+
+
+ Resources/core/icons/box-arrow-right.svg
+
+ hash2
+
+ omBOi25ir1b1+62GN8PpU64f+7y0Bd3RwZ31I4v5e1I=
+
+
+ Resources/core/icons/box-arrow-up-left.svg
+
+ hash2
+
+ JkH/afdD/S4EctbkTqFnP4Bn8tt9Rwd6HkDqSyEOho4=
+
+
+ Resources/core/icons/box-arrow-up-right.svg
+
+ hash2
+
+ azFNUAYYcEG0l2va17rEG2TRQlDzCym3/Ot8dUX4z7k=
+
+
+ Resources/core/icons/box-arrow-up.svg
+
+ hash2
+
+ aqUmU6twC+tQMuf0jBpr5svZOTTHxEe8LO2VFBvery0=
+
+
+ Resources/core/icons/box-fill.svg
+
+ hash2
+
+ yU5yDdf2bS0krY50/rU//7XItnC0q8vXBh65Uka3BS4=
+
+
+ Resources/core/icons/box-seam-fill.svg
+
+ hash2
+
+ BuwhXaBFFBFCc7+WwTQqK72L7SENwJFxYpPR08xHF/s=
+
+
+ Resources/core/icons/box-seam.svg
+
+ hash2
+
+ iNQU4GBnGU/gTryJfYIKhRP65XvQulbpYE/bDMalCeo=
+
+
+ Resources/core/icons/box.svg
+
+ hash2
+
+ s76DYb6Fi8gDLzYqK4KGnXv7ouRWfFKLY1BytOJoIUM=
+
+
+ Resources/core/icons/box2-fill.svg
+
+ hash2
+
+ 30IpYxU7fmpyVnuc9oTzwzryL3Dcc9PD73TeKARLOi8=
+
+
+ Resources/core/icons/box2-heart-fill.svg
+
+ hash2
+
+ Z36eV/O9x46HZ3/cREB8LqmfLfoP6pW97iBpZZdn/4w=
+
+
+ Resources/core/icons/box2-heart.svg
+
+ hash2
+
+ Gkt00mfQ2bquy5TLD79fJwUMVhDLU+uwD4mHln6wM04=
+
+
+ Resources/core/icons/box2.svg
+
+ hash2
+
+ 8JElCaoD15Sg/OLR2zTpUlj33AbpDW8y+gttabPTRhM=
+
+
+ Resources/core/icons/boxes.svg
+
+ hash2
+
+ jsI/+s2VNGSu1+tDMzGEt22DBF5Sq1hfUgehrADDyBA=
+
+
+ Resources/core/icons/braces-asterisk.svg
+
+ hash2
+
+ P22PLh9PPs/z9aAtTsWQ7KY5EX8ln23fgOK4liPkPy8=
+
+
+ Resources/core/icons/braces.svg
+
+ hash2
+
+ eQW9l4YRoUF/c07aA2o353elIKbs1e6eMX/kCEi4rVg=
+
+
+ Resources/core/icons/bricks.svg
+
+ hash2
+
+ BnUMN00FI83I0ymY1CzpCF2ev19esLhKJmAoDrUutko=
+
+
+ Resources/core/icons/briefcase-fill.svg
+
+ hash2
+
+ V+N26YbXWvclPDkcFc9iercVzUTUiX3SbVMwcogxRGk=
+
+
+ Resources/core/icons/briefcase.svg
+
+ hash2
+
+ dv7abopkI+C5s1Bvy18zBGoQpMqwYKDlFmvOvCyhSJc=
+
+
+ Resources/core/icons/brightness-alt-high-fill.svg
+
+ hash2
+
+ nw53X5s+xBjAyakPuR4Stmz/hzxWQqGZAvgpFo7WB5A=
+
+
+ Resources/core/icons/brightness-alt-high.svg
+
+ hash2
+
+ t4ffPjp1eJKv/PtM52//uMTyzxk1xgTBzACUKPL4jL8=
+
+
+ Resources/core/icons/brightness-alt-low-fill.svg
+
+ hash2
+
+ uYcLHupcRmACMIhHXwdUklplaUr5Ae62XfpILyRcdDU=
+
+
+ Resources/core/icons/brightness-alt-low.svg
+
+ hash2
+
+ wjNbxDqvYI3I9luCl/h0XPbQuC5Dt6pO/+dUzShRFls=
+
+
+ Resources/core/icons/brightness-high-fill.svg
+
+ hash2
+
+ pT51uTsupzGTDLL7cfZbhDQJ62QsuzAnigUTXvhQt0o=
+
+
+ Resources/core/icons/brightness-high.svg
+
+ hash2
+
+ Tn7iXczcvbu0lMQEGJ6mLXEdA4WKhbR51C97TwRaBRE=
+
+
+ Resources/core/icons/brightness-low-fill.svg
+
+ hash2
+
+ 9cas66uG3fykr8ia3wor9FJ2oulMkPV5FQ+Vn03w8/w=
+
+
+ Resources/core/icons/brightness-low.svg
+
+ hash2
+
+ 1yVZ7t0ClL3mZKAjIcyKfrTnFIESwasLlhcdRzN+Sbk=
+
+
+ Resources/core/icons/brilliance.svg
+
+ hash2
+
+ ymEHFY/RAeAoIT1Jsub7JvJUlY/SFhNkm1yX4OqnhoM=
+
+
+ Resources/core/icons/broadcast-pin.svg
+
+ hash2
+
+ jXnOKO0VpJt0T14QFIZxCAic2a0x32+ZgfUdwBKQMW4=
+
+
+ Resources/core/icons/broadcast.svg
+
+ hash2
+
+ uGhk+j03113BlNAcxMgbqPRn/A2j63PvWCpXIl7C8hc=
+
+
+ Resources/core/icons/browser-chrome.svg
+
+ hash2
+
+ zDujwRA7i6nylwIKEXk5MyQJ9T9Ful8z/6bceokDdHs=
+
+
+ Resources/core/icons/browser-edge.svg
+
+ hash2
+
+ kybGuZl1Z/mxtcrLx1IJNrtAeNSIR7TsQno5CuKNWt0=
+
+
+ Resources/core/icons/browser-firefox.svg
+
+ hash2
+
+ Jwsgj5xHI1at9P/d0yeIIbUbdcOKkUPD9KNgRIHTYRo=
+
+
+ Resources/core/icons/browser-safari.svg
+
+ hash2
+
+ Z0tmQH1IFopBQAPuUvnpXwHYVm/MUmW7GKSqR4Tx1SY=
+
+
+ Resources/core/icons/brush-fill.svg
+
+ hash2
+
+ VzB0TYSOpvRiSIHgzwTUztRBt9SwZqJKa0j82sKAsmE=
+
+
+ Resources/core/icons/brush.svg
+
+ hash2
+
+ w/QCDI2Jlf5PZ3FStsDGuCLGSJ81d6ls/+Z2YViJrNk=
+
+
+ Resources/core/icons/bucket-fill.svg
+
+ hash2
+
+ 93ehTRMPK8iB6AHr2eGNnAsFgGq5+uzt9GyyU7I3xBs=
+
+
+ Resources/core/icons/bucket.svg
+
+ hash2
+
+ XxxLSZhrOxMG1Q4tq0KKP7Q7Fk6jdNW/FJ92mSOLR1U=
+
+
+ Resources/core/icons/bug-fill.svg
+
+ hash2
+
+ +EeNSiR17qW5HSiGRD/DO3qeQPEH1MWanJvNnXDXlk4=
+
+
+ Resources/core/icons/bug.svg
+
+ hash2
+
+ 375/ZT+Jh4U3EPkEcEYq10H7CBmLHgabFSJEOufvH8E=
+
+
+ Resources/core/icons/building-add.svg
+
+ hash2
+
+ OQWSfJ8pfDasK46fAqjgExFaXjZIs2Hs5r6g5mgcbd4=
+
+
+ Resources/core/icons/building-check.svg
+
+ hash2
+
+ CPjSEjxgaOLYyRkq4bWAYoYEhNVMlfgS8uPUKe6baWI=
+
+
+ Resources/core/icons/building-dash.svg
+
+ hash2
+
+ qD9Loi4mODSdsyt2rT0bXe+JCYB0JsXEo5ewlNmXzLE=
+
+
+ Resources/core/icons/building-down.svg
+
+ hash2
+
+ buuWf9SavKSEijen6MpveFFBROTKghi4C7nn26tELaA=
+
+
+ Resources/core/icons/building-exclamation.svg
+
+ hash2
+
+ Ti+tI2j+ZeAQ8rkrBbgCQ7Q3jaWh70wFk2QvXpIxq3M=
+
+
+ Resources/core/icons/building-fill-add.svg
+
+ hash2
+
+ JVRSf4pbM2UhTWzWetGuSfaTrvoVShTs4TwZu+96Md0=
+
+
+ Resources/core/icons/building-fill-check.svg
+
+ hash2
+
+ AbQ5ggmle6t7nwfsZ+cTNl4t79x9whTS7cvWr7E3t1U=
+
+
+ Resources/core/icons/building-fill-dash.svg
+
+ hash2
+
+ pNgpz8WMf72xyz87ANoejD4PBZthGuOXBd9Pb15Lr9c=
+
+
+ Resources/core/icons/building-fill-down.svg
+
+ hash2
+
+ NIHTb1mz0Kh7bbznYOYGHZOrVF8SWlFFkU12UkxTycE=
+
+
+ Resources/core/icons/building-fill-exclamation.svg
+
+ hash2
+
+ waLrbZuxfpduN0k0RbJAtNtMueYTuMWBR66T63NiV/k=
+
+
+ Resources/core/icons/building-fill-gear.svg
+
+ hash2
+
+ 0OLORo9I7QklE2JFXIwJtEhvn8qa6Bzh1px1yNk6Xmw=
+
+
+ Resources/core/icons/building-fill-lock.svg
+
+ hash2
+
+ 7oWNe0qMrf7/e8RW4NbJN+9MSuNyyZR79C2G1VLiEX4=
+
+
+ Resources/core/icons/building-fill-slash.svg
+
+ hash2
+
+ y8RYfsw/tI4A1jAJTl98t40NlKJno813cbGg/UWBDw0=
+
+
+ Resources/core/icons/building-fill-up.svg
+
+ hash2
+
+ SnU0YUidJxuF/IFJZpPDU/CPUTUMdYsdTnAfRhYttL0=
+
+
+ Resources/core/icons/building-fill-x.svg
+
+ hash2
+
+ 9p6YKcDgqp3TxpRtvsJh3mpWpmnDLrB5Tt+DCAOjRRI=
+
+
+ Resources/core/icons/building-fill.svg
+
+ hash2
+
+ 2wAoB8ww8Cnh06qcdqTjXzHVkIABBoSfqZnjVaCTdl4=
+
+
+ Resources/core/icons/building-gear.svg
+
+ hash2
+
+ r+kqT1CqjlJVBr2CfFkpi6iQHltM4sydCqe3sDg78Gs=
+
+
+ Resources/core/icons/building-lock.svg
+
+ hash2
+
+ Afqno6cWvKGKu1ibCLugD7WGi30PVvlOx8NPsopHJ4Y=
+
+
+ Resources/core/icons/building-slash.svg
+
+ hash2
+
+ x5ioeZw40RNQ5r9uDTd2lKilAJDUkkagrTvbVB5P72s=
+
+
+ Resources/core/icons/building-up.svg
+
+ hash2
+
+ qRkgXi2JmicbCu8+imU8TJoGAc4V9MFUc9j8LnEBTcQ=
+
+
+ Resources/core/icons/building-x.svg
+
+ hash2
+
+ jw39k8QPWT49V4sVovogfI3z2kgTPeKvaoNt42yBLHc=
+
+
+ Resources/core/icons/building.svg
+
+ hash2
+
+ 9vfqKCkICupKbU0HqIQzuUk16PJGobJkQRr8qDrgANw=
+
+
+ Resources/core/icons/buildings-fill.svg
+
+ hash2
+
+ GtvWiLRHnGeIh+bxQe2xCcC7ClwV+ZGpwjAP/6/mWOY=
+
+
+ Resources/core/icons/buildings.svg
+
+ hash2
+
+ qKboqnGkmo92YwhU/QjLoBLutNplh2IsY6t/ePNGRoM=
+
+
+ Resources/core/icons/bullseye.svg
+
+ hash2
+
+ SbcN5Y3q2SAscf5EjyTjiRltyijK6Hx8SZD1wBVrkd8=
+
+
+ Resources/core/icons/bus-front-fill.svg
+
+ hash2
+
+ bcaaiCG2OF1gpapSSv1bEnTOAjGOWuMCmPGG+1Hqrz4=
+
+
+ Resources/core/icons/bus-front.svg
+
+ hash2
+
+ 7/bEbpYOc7VKY501D+wUWvL4toWhZrbMHYZLjE0v6ug=
+
+
+ Resources/core/icons/c-circle-fill.svg
+
+ hash2
+
+ V6tzChvOwWp4w89EmVHk/bvIQllDTN/fkO9F3rdUuFc=
+
+
+ Resources/core/icons/c-circle.svg
+
+ hash2
+
+ po7gWZDATY9k+2579s343qezCmaEpjZ+Jz7YR+fH0jA=
+
+
+ Resources/core/icons/c-square-fill.svg
+
+ hash2
+
+ v6n0py6llNFMnpR4QhmXIXmffChy0Ku8cePwRuY5Ns8=
+
+
+ Resources/core/icons/c-square.svg
+
+ hash2
+
+ I88jvSTUshn5PxzHW5A9TEbh+GlU4/Talr8/Nf5R57w=
+
+
+ Resources/core/icons/cake-fill.svg
+
+ hash2
+
+ Kjgzc0iIxg9bUXKNuZdzEBhK0Pal/Liy+VUUq1cJ+oo=
+
+
+ Resources/core/icons/cake.svg
+
+ hash2
+
+ ALToVl3LTOPTeWHVjH/GqTSe1bvfXYufejohN4sEZWk=
+
+
+ Resources/core/icons/cake2-fill.svg
+
+ hash2
+
+ HGUykg/Tk/dyezHX8RM94XCooLT1IQAtzfe2TIj4k78=
+
+
+ Resources/core/icons/cake2.svg
+
+ hash2
+
+ KnyR1MKl2ISsW5m8nm9vlTTFfkCgaelDrfWDWuny720=
+
+
+ Resources/core/icons/calculator-fill.svg
+
+ hash2
+
+ eX/TEEwJ2QB2KlyebYIuyx728yKJjgvfptsXVcUTafk=
+
+
+ Resources/core/icons/calculator.svg
+
+ hash2
+
+ U5FY+i9mYE9Nh+imtXjj7bzefUq1AWDP6sxQak26Vos=
+
+
+ Resources/core/icons/calendar-check-fill.svg
+
+ hash2
+
+ DTivnJKYMrSkUnSLGyO6RUAeur1GDX035I7rz6XssgE=
+
+
+ Resources/core/icons/calendar-check.svg
+
+ hash2
+
+ sV0jLc+NMDHF5OQ4RImPplQBIRmSe6njfB7nI9PDsq0=
+
+
+ Resources/core/icons/calendar-date-fill.svg
+
+ hash2
+
+ OuI1NjJ2DHWTC8EbL08Gb+DakBEWFm+WuBJ6AgvrIZY=
+
+
+ Resources/core/icons/calendar-date.svg
+
+ hash2
+
+ WkxRmGmde5tUYcQa3cmWFqyuE8qhHL2hw6m9IDU+fhY=
+
+
+ Resources/core/icons/calendar-day-fill.svg
+
+ hash2
+
+ 1cNUGGN+biFFHddVJbj5uqHOp0LWWwC5h2e1bNqBceQ=
+
+
+ Resources/core/icons/calendar-day.svg
+
+ hash2
+
+ V+Gq1zj46HzhybW1dF5TsosIzaaeJbVf2BRZCwDsT/A=
+
+
+ Resources/core/icons/calendar-event-fill.svg
+
+ hash2
+
+ p8l1sE0PmNSdLcSOKjTmVnkAH97jxZtjeL2MpLPPuY0=
+
+
+ Resources/core/icons/calendar-event.svg
+
+ hash2
+
+ cqt6V/61sD63+LZ4FGQVTdHHrfmDMS02FVAlYR53iCo=
+
+
+ Resources/core/icons/calendar-fill.svg
+
+ hash2
+
+ E0EgdYC5VKA2aTlPc+eiuW8UFL918pjO4hQbo+YjTns=
+
+
+ Resources/core/icons/calendar-heart-fill.svg
+
+ hash2
+
+ F3zZYoUQzoIlRoIzz6Xsz4NUru2PSRvqfSJVhX2MqzM=
+
+
+ Resources/core/icons/calendar-heart.svg
+
+ hash2
+
+ RMiKiFZjopjg+aF0zCyGRadAaH3GdjOv6ZsC2qUiOiI=
+
+
+ Resources/core/icons/calendar-minus-fill.svg
+
+ hash2
+
+ L6f9bcKqm84v61xqyF7qLq16CD+ajqx97mYqdzndDkA=
+
+
+ Resources/core/icons/calendar-minus.svg
+
+ hash2
+
+ PVT6YBNMTzK4KlbflhT6WWyU/4sRQ8VvYTic6+zlunc=
+
+
+ Resources/core/icons/calendar-month-fill.svg
+
+ hash2
+
+ sDc+QbmACVsBo4HpeUaGYR7hx7EqagqDz1f0b+hIKiA=
+
+
+ Resources/core/icons/calendar-month.svg
+
+ hash2
+
+ Ac36dkgzKyIYUjuypBD0BLUcfV1c4wMqzyqVkKryoA0=
+
+
+ Resources/core/icons/calendar-plus-fill.svg
+
+ hash2
+
+ ZpjPxqZWPKTWZVsQFu6Y+Bxy6XkjRqmBDi6LSjC7X0M=
+
+
+ Resources/core/icons/calendar-plus.svg
+
+ hash2
+
+ XEbZYnFAXxTKcvGGh3xa1rSbXle0+rGrkCxD/1iJ9WE=
+
+
+ Resources/core/icons/calendar-range-fill.svg
+
+ hash2
+
+ jK7RYc7mQGZVsiA7lbrm62GsTKMXsn8gRx5qmuBVx/o=
+
+
+ Resources/core/icons/calendar-range.svg
+
+ hash2
+
+ ETSsZhxgwSF5NwKNmgvv1wWjk5Q3xqvXMIgc5qVqVBo=
+
+
+ Resources/core/icons/calendar-week-fill.svg
+
+ hash2
+
+ U2wsyvgY6XSI5LZFT8ainC2DXrSkpwm+u8JKZgXsLQQ=
+
+
+ Resources/core/icons/calendar-week.svg
+
+ hash2
+
+ t0d+kFxDWT/LDzhPA9rVG83KjeDDfWPEZfaF4/E0XOE=
+
+
+ Resources/core/icons/calendar-x-fill.svg
+
+ hash2
+
+ 7+6TSnngUiPzbPh4+sFAJHUa+oiVczlio0lxPJwI6h4=
+
+
+ Resources/core/icons/calendar-x.svg
+
+ hash2
+
+ x0N6Ct/chf0IMubf62qHJokepx2WO+WUzzB0Xee09Jg=
+
+
+ Resources/core/icons/calendar.svg
+
+ hash2
+
+ ir5ba4Q8sSyaoJLDYoMDayb/a05xXLzX+Z4RCY5SC4o=
+
+
+ Resources/core/icons/calendar2-check-fill.svg
+
+ hash2
+
+ T9AjmnbFRMlej1baiLKZKDxWTUtCg/b09jpFAq7i3xg=
+
+
+ Resources/core/icons/calendar2-check.svg
+
+ hash2
+
+ Xxvspc4ZHiPLiSFwg0ALLLy2L/UG5c8xRcckqdQ3V+w=
+
+
+ Resources/core/icons/calendar2-date-fill.svg
+
+ hash2
+
+ DO2n7/xIBZA41duLJlXr7ceJ6ka62eqg7ePlfpTs4L4=
+
+
+ Resources/core/icons/calendar2-date.svg
+
+ hash2
+
+ iusTnIdFL7L/S+BQCcJrYcrNy1Vf2dIn8foUfB2R5/o=
+
+
+ Resources/core/icons/calendar2-day-fill.svg
+
+ hash2
+
+ hLvooIXS+6tWkLmKQHurpBERduh97xxTwoplUFyWylo=
+
+
+ Resources/core/icons/calendar2-day.svg
+
+ hash2
+
+ 7UeERF5FcyttHXFk2BsznH97Xwkk9vBWEiA2vaLRlnY=
+
+
+ Resources/core/icons/calendar2-event-fill.svg
+
+ hash2
+
+ VqZsxPXSea4KMkdCmLYRcYvaBR7ols/QdWQYRKgGl3k=
+
+
+ Resources/core/icons/calendar2-event.svg
+
+ hash2
+
+ OPNpLrUPTrLcflZlWD6XeZu5TDIwGaxjlqbXV86fawM=
+
+
+ Resources/core/icons/calendar2-fill.svg
+
+ hash2
+
+ TWp+zNTH36sSZWmbQbrLmE8q89nyfhQZjj9Z1l7B1wc=
+
+
+ Resources/core/icons/calendar2-heart-fill.svg
+
+ hash2
+
+ QiJl6H7CoEzLqwwjroIh/fwln6zOwq7di1KcxWj1iBg=
+
+
+ Resources/core/icons/calendar2-heart.svg
+
+ hash2
+
+ mHnjJuyahZMA2nBT0v+Ee/zMmeJEszdzjBxs0YVM91U=
+
+
+ Resources/core/icons/calendar2-minus-fill.svg
+
+ hash2
+
+ Q8/dx7Ki0VP7ozhS5XtsEFIzDNp2bhQ2z0aIvcJfTRk=
+
+
+ Resources/core/icons/calendar2-minus.svg
+
+ hash2
+
+ vhQ+3A+wH66pF9N6eIBOUhF2UJylL0VKbeiU0Rwk3ik=
+
+
+ Resources/core/icons/calendar2-month-fill.svg
+
+ hash2
+
+ Y0YKEQWeiAZjbBhBZFGBClSEL+6J1nRSWj60XRKrK/w=
+
+
+ Resources/core/icons/calendar2-month.svg
+
+ hash2
+
+ T6DTuFhQAm6vQ/tP6kb2UH/SSyEMEyPdzRxJNFOAUho=
+
+
+ Resources/core/icons/calendar2-plus-fill.svg
+
+ hash2
+
+ IJXTUXL7ob8J4hSHRUba9C1E5KTFLxkh0OSIbZq2f2c=
+
+
+ Resources/core/icons/calendar2-plus.svg
+
+ hash2
+
+ VYPk6lbFHCi+WNmCRrrbbxC+KKP/EubucBXifeXySOQ=
+
+
+ Resources/core/icons/calendar2-range-fill.svg
+
+ hash2
+
+ ukQoBEbKJx75e4tVvU04Kv2PwKjlibDh7NFB2qTrUOU=
+
+
+ Resources/core/icons/calendar2-range.svg
+
+ hash2
+
+ J50nQKqbY8k9QaR6+ZZo9aJFVpp27vcF7eIkruAu7Wo=
+
+
+ Resources/core/icons/calendar2-week-fill.svg
+
+ hash2
+
+ bA3Dk/bTRM7LXZN5G2NNat6a2HLHS+fdcGk9aFmkLTY=
+
+
+ Resources/core/icons/calendar2-week.svg
+
+ hash2
+
+ r3xjyw8YhDxVLQi7IraIywM7wbWn9ynsfUxobKgebD8=
+
+
+ Resources/core/icons/calendar2-x-fill.svg
+
+ hash2
+
+ z7TQ+XNwR66XbZ0/CY9ZGhGyvSIuGVzH8y4ZqKoVHcc=
+
+
+ Resources/core/icons/calendar2-x.svg
+
+ hash2
+
+ YSkncMarzHbR5afJePCl9t96nlchCA+CLB3XqehcXgA=
+
+
+ Resources/core/icons/calendar2.svg
+
+ hash2
+
+ eIkgUhgaj9WbZjoNRaRbIxk0Z8XUoFfZ0XCpKhLMW7M=
+
+
+ Resources/core/icons/calendar3-event-fill.svg
+
+ hash2
+
+ B9J4Qcuq4zM7LZGbLpecXMZ+9u3rAkeAV8xNzNguKTQ=
+
+
+ Resources/core/icons/calendar3-event.svg
+
+ hash2
+
+ NEP5sqEZr8XuU4GvAahDNGeN5eSXG3THeO7ErYR0/mE=
+
+
+ Resources/core/icons/calendar3-fill.svg
+
+ hash2
+
+ 0G3ANJNlTZyQHrFjx63RpE/489QSy47uW6b6xAogR5o=
+
+
+ Resources/core/icons/calendar3-range-fill.svg
+
+ hash2
+
+ 3W5RZoXrwJmrIGXjCvx+kMxKqTW+qliaOuphuoFnFsQ=
+
+
+ Resources/core/icons/calendar3-range.svg
+
+ hash2
+
+ rs32u/H05CYBHoQXaf/w3ELT7Pe8FZ+wXhPg66LTHJ8=
+
+
+ Resources/core/icons/calendar3-week-fill.svg
+
+ hash2
+
+ dF89QFuVDOpeAX9N41KMl7THip/JTubemkRtBAbhl2M=
+
+
+ Resources/core/icons/calendar3-week.svg
+
+ hash2
+
+ JTs3/ElhDCqxQEUCZIS7C+NHdmc6jWCp78MTmuNAIKg=
+
+
+ Resources/core/icons/calendar3.svg
+
+ hash2
+
+ 4x+ChgbHxxaVrBSu57s/7WMJ6GgtAwLJFJ8h+vhHWkI=
+
+
+ Resources/core/icons/calendar4-event.svg
+
+ hash2
+
+ X+Xz81xNmEGo37gT9V1YhX5GMkKnpt15DD62frJ4feM=
+
+
+ Resources/core/icons/calendar4-range.svg
+
+ hash2
+
+ M7dz1nHf1d0bhczboAxD9zZA/aYTSAZe6UcwEQtNixA=
+
+
+ Resources/core/icons/calendar4-week.svg
+
+ hash2
+
+ vSDKgxGCHzHs+NV0hFH6aoFupnsRRPLTmJ9p3XNCCuI=
+
+
+ Resources/core/icons/calendar4.svg
+
+ hash2
+
+ vtim/oTPHiZeoGM+nP1v/fusBlez6v4W07QJxxYDCSs=
+
+
+ Resources/core/icons/camera-fill.svg
+
+ hash2
+
+ 62mh2rEDP9GPj1Wq/MrHlEde6Cj1mRaM0at60+/xChY=
+
+
+ Resources/core/icons/camera-reels-fill.svg
+
+ hash2
+
+ jB8wT/k66Hlcb9E7A6BkV8JlMw5fxxVM8Dg2AlPkO50=
+
+
+ Resources/core/icons/camera-reels.svg
+
+ hash2
+
+ TrcN45QOxX/l02tj6tam4qeKFP/G3Xd2TqMqk1bcHgI=
+
+
+ Resources/core/icons/camera-video-fill.svg
+
+ hash2
+
+ W6A8+9+NwOQS6fWNIK9DoCnlN2CFBmu1IfeWQTvDj1s=
+
+
+ Resources/core/icons/camera-video-off-fill.svg
+
+ hash2
+
+ q2F3Gcp7aEvsb36QIU9FmgQXAelUt06gSAPi3aTMgNY=
+
+
+ Resources/core/icons/camera-video-off.svg
+
+ hash2
+
+ MtAQz5nM6ijgPo2aw8fTGhG7J+9spdOlonCU3UlP+C8=
+
+
+ Resources/core/icons/camera-video.svg
+
+ hash2
+
+ dVaumKBWT5Ohofa3Bch8vmLyyrBGTF4xDia7KprLPyw=
+
+
+ Resources/core/icons/camera.svg
+
+ hash2
+
+ RY3/unYZ2KXHA/u8UzAJqg5FdYf3vewP8G1NZlklkYk=
+
+
+ Resources/core/icons/camera2.svg
+
+ hash2
+
+ hN1GDS444/sh7xe5jXLzYCMmyJlQ4P3l0IGKY/cLqoc=
+
+
+ Resources/core/icons/capslock-fill.svg
+
+ hash2
+
+ /6V7f52bG9rAc0Yt0VbMObrr1X+TcCxLy4FKNqmQB4c=
+
+
+ Resources/core/icons/capslock.svg
+
+ hash2
+
+ 5zKs+pov+iyRmJzLmKxIo3B9BbNVAcCb9n8cktmjZxU=
+
+
+ Resources/core/icons/capsule-pill.svg
+
+ hash2
+
+ AwCJq1RcAe7Q4R1P8JQrKREXiSu5tVzCUEMj7s4l/Sc=
+
+
+ Resources/core/icons/capsule.svg
+
+ hash2
+
+ Qs7m8Khg8M5RuFouUZWTWaKWCaJtoPBRAf/28fx5R4U=
+
+
+ Resources/core/icons/car-front-fill.svg
+
+ hash2
+
+ 3hFvrK3PQMq/seFyw4u9AQN5WymRelhUUYDOY1JHMIc=
+
+
+ Resources/core/icons/car-front.svg
+
+ hash2
+
+ Z66feICkiNmC/No0naFJHRA+KWEoohMnQWwYa2R8sfA=
+
+
+ Resources/core/icons/card-checklist.svg
+
+ hash2
+
+ /FKvOMJtyy5wcVNtBhrpDMzPG5ljlyHxHXJIwN3AxVU=
+
+
+ Resources/core/icons/card-heading.svg
+
+ hash2
+
+ yKQ+yWuXDtvg/8Y3OdujAE5Tilhccc0dhjSaeUnlKpY=
+
+
+ Resources/core/icons/card-image.svg
+
+ hash2
+
+ g22Xu3PeKqQP/ZAJ1Zzq8mSUlCCFpj6MFOlpRo7iHEc=
+
+
+ Resources/core/icons/card-list.svg
+
+ hash2
+
+ OOjw5tkHTBsIQxTp822BrHoobE8zOQOolS621A0fiA4=
+
+
+ Resources/core/icons/card-text.svg
+
+ hash2
+
+ dnmtA27RbtlDZQlp/fT3yE2VYv3gfbmYTLH54raOykM=
+
+
+ Resources/core/icons/caret-down-fill.svg
+
+ hash2
+
+ hCrzNjQMTiwWLkhxek/AO/X2Q80p4Cw8IKHA/9laoQw=
+
+
+ Resources/core/icons/caret-down-square-fill.svg
+
+ hash2
+
+ b5D2lRhUwagITjjp9fqZOiXJZ2Jc7vKcaC9DCXkt1g4=
+
+
+ Resources/core/icons/caret-down-square.svg
+
+ hash2
+
+ huETM+i13GgAY3V1ZSOzYQGFOhkYa2je3VH/vR59fAg=
+
+
+ Resources/core/icons/caret-down.svg
+
+ hash2
+
+ uG8WYf6hl/uaskxZ1rgRtBw+Qnx2czyl9l2MprlH/Dw=
+
+
+ Resources/core/icons/caret-left-fill.svg
+
+ hash2
+
+ FxHRlYgOmJWElWMP4vhA2QPEfqawoz9K0YyqyEf8EiI=
+
+
+ Resources/core/icons/caret-left-square-fill.svg
+
+ hash2
+
+ XOVBhptHVithcZwYXls5yj8u/zcNN2IvkaiR7g4e9G0=
+
+
+ Resources/core/icons/caret-left-square.svg
+
+ hash2
+
+ y+65zk8axd8b90h6nJcFANSZWlyUY8nqXB/HNSiUAKc=
+
+
+ Resources/core/icons/caret-left.svg
+
+ hash2
+
+ 2oIA4qN5HQVz1YVz0aWdw7a3kV9h/2hPMBX8NGVwpLA=
+
+
+ Resources/core/icons/caret-right-fill.svg
+
+ hash2
+
+ cgCq1uloZQU9c2qxWJGqZrqqKfxT+HRA0Cx6mD0u+CY=
+
+
+ Resources/core/icons/caret-right-square-fill.svg
+
+ hash2
+
+ SDAufmqhRkUz7ODHtjfw2+FQOdKs0zN2xXoYMExjYe4=
+
+
+ Resources/core/icons/caret-right-square.svg
+
+ hash2
+
+ AEIswnKaYVo9C/x+mRKPbHVtp4w1ArsA15DShPJhwz0=
+
+
+ Resources/core/icons/caret-right.svg
+
+ hash2
+
+ 3M1QYLcRK3fAF7nZpCeaMyn3CnQFpqChKPGfR37rLBs=
+
+
+ Resources/core/icons/caret-up-fill.svg
+
+ hash2
+
+ aKyeU5IQ8ChnFb/x4kBzh+dg5Gi5WMbP/S52mx4aTvo=
+
+
+ Resources/core/icons/caret-up-square-fill.svg
+
+ hash2
+
+ +plfXWrKSUWcFCZlZ1JKUEHKpD3TBK/fyTkbqH0v82c=
+
+
+ Resources/core/icons/caret-up-square.svg
+
+ hash2
+
+ c5SwDaNvwbIbFxedc7mVJSV3CPIrGPydOPlsaS+MOC8=
+
+
+ Resources/core/icons/caret-up.svg
+
+ hash2
+
+ kJgXM9x3bGsyrHjZgp9lUqzYiSMFw6obgfu2lu/H3FA=
+
+
+ Resources/core/icons/cart-check-fill.svg
+
+ hash2
+
+ znCCT8IEDd6GixJBLKmT2odIMwZ8cy3cx3hbzLthXYc=
+
+
+ Resources/core/icons/cart-check.svg
+
+ hash2
+
+ ld0Y6Gul4qqPEgsL5jFhDAFIu32Xxyw48r6nzLtX8LE=
+
+
+ Resources/core/icons/cart-dash-fill.svg
+
+ hash2
+
+ ZqU5JGedDI5o/yPhzBu0QdRXB0kg5DIVEDJk85GbkDo=
+
+
+ Resources/core/icons/cart-dash.svg
+
+ hash2
+
+ 0g4ZvUgrVFUb9i6w2/34yc0udYxCTutgmAleHCz6yac=
+
+
+ Resources/core/icons/cart-fill.svg
+
+ hash2
+
+ 37HPP12ENbm8eHVnGywRxHzcludYarRmOaadxQo41Sg=
+
+
+ Resources/core/icons/cart-plus-fill.svg
+
+ hash2
+
+ jw8w6+x0utTmK8OfekFKFHw+kFaSzgzFW6HdguJu9Sg=
+
+
+ Resources/core/icons/cart-plus.svg
+
+ hash2
+
+ kvcjMblN/ki9jEewHIp/8tUhSkNF6qdNo3wgbCZXr7E=
+
+
+ Resources/core/icons/cart-x-fill.svg
+
+ hash2
+
+ apcZxsV9og0sWdXp+u7R+A7Pgn9R6lUcTo2RQDonaiM=
+
+
+ Resources/core/icons/cart-x.svg
+
+ hash2
+
+ lB4cgx2YVMJgErLGavY6wRoYwGv7APc8eJ6piaJBBdc=
+
+
+ Resources/core/icons/cart.svg
+
+ hash2
+
+ b37y1jH2c39cSXL+XMWptLFgbCeouHyn32UG1Jreysg=
+
+
+ Resources/core/icons/cart2.svg
+
+ hash2
+
+ 6HuQwLbTiXH3zEoK7O/ZOM5Ov+urbMdbdXldmSMyBLQ=
+
+
+ Resources/core/icons/cart3.svg
+
+ hash2
+
+ GVuhCueY+NE8R34A4tsPpTT/WsCPpgKTixQ/zwW3S6A=
+
+
+ Resources/core/icons/cart4.svg
+
+ hash2
+
+ X7crhNoSOR2ozcYhrAN+WPDhWV46z1wU38O9AnMjSuw=
+
+
+ Resources/core/icons/cash-coin.svg
+
+ hash2
+
+ dyqzIDEfz7hgsQ1k5Fy+Z1D71xzkZ5Nff30JQIKK7QE=
+
+
+ Resources/core/icons/cash-stack.svg
+
+ hash2
+
+ gjexE6wiADJipx0/siZZPFBp/8gslrMF6tImAGNyrlU=
+
+
+ Resources/core/icons/cash.svg
+
+ hash2
+
+ wNMxq/ss9TPA8VnuWjX5Tl4DWxIDLlCwFdYKZ2e5Hag=
+
+
+ Resources/core/icons/cassette-fill.svg
+
+ hash2
+
+ ZbXUT6UWubaY3ZbeqXGdI2pIeZT0po0k9fUskt50jJQ=
+
+
+ Resources/core/icons/cassette.svg
+
+ hash2
+
+ pJM0RkreyVpsvOLHUibVmouHWmACAINcklqVgmxk6Q4=
+
+
+ Resources/core/icons/cast.svg
+
+ hash2
+
+ lKs7vZdvDxKKSAMBCzTUoWATmuUMvXhxHi2t90w8lqI=
+
+
+ Resources/core/icons/cc-circle-fill.svg
+
+ hash2
+
+ X3zA8emz7dVl/Af46amucP6+EdRhtmxPjjdCTxnBMIo=
+
+
+ Resources/core/icons/cc-circle.svg
+
+ hash2
+
+ dyovKYvt0Ks+6JhvZYVgYKiwKJByP3NEGmNHUHD5Y74=
+
+
+ Resources/core/icons/cc-square-fill.svg
+
+ hash2
+
+ 7twDfcU+W2RxdgJ4Qt56R+IEXlPE4VMeDiKD8LBtjjc=
+
+
+ Resources/core/icons/cc-square.svg
+
+ hash2
+
+ VyUbOzmi1whHOJefYYZ6IWxwmNjs5S3WS6WhZsAsJi0=
+
+
+ Resources/core/icons/chat-dots-fill.svg
+
+ hash2
+
+ 6HiGNmmCRYs9Sav7/01/2Bn9BAtZMB30WWffD+NP91E=
+
+
+ Resources/core/icons/chat-dots.svg
+
+ hash2
+
+ f6bJn5qIC6FE/Xp5j7kE7vwpc4nAjOyomcboobXdrSQ=
+
+
+ Resources/core/icons/chat-fill.svg
+
+ hash2
+
+ 5AsNPclVHpwQg3Kjy0EOFBHTslDJ06MQkJjsxz4NyIw=
+
+
+ Resources/core/icons/chat-heart-fill.svg
+
+ hash2
+
+ wigrdcQ1T1oMwHE02CI+rErykh4Y6Kmh5fa9di6tMCc=
+
+
+ Resources/core/icons/chat-heart.svg
+
+ hash2
+
+ 51plb6sxwTWiplPp6MToYQl603SawT/IDaEhH6VmhbY=
+
+
+ Resources/core/icons/chat-left-dots-fill.svg
+
+ hash2
+
+ auln22xmtARkWaAZj+y2M0qs87nxXerzFw8hTthqLZ0=
+
+
+ Resources/core/icons/chat-left-dots.svg
+
+ hash2
+
+ wncWVIyn/Y+YkBPwbpQt8WWIRvrUMerrTbhI6Q+TO30=
+
+
+ Resources/core/icons/chat-left-fill.svg
+
+ hash2
+
+ gnb1SD+PauyVxJYKd9yZ3Zr4bgH9nfHB1X1+lDWMGWo=
+
+
+ Resources/core/icons/chat-left-heart-fill.svg
+
+ hash2
+
+ SVP7dFtkOLJzg+uXKLnWpKqTGz57259JJJxpYLBQV+Q=
+
+
+ Resources/core/icons/chat-left-heart.svg
+
+ hash2
+
+ ODM0xGumyLHsBm1snu/K/15Y+w9z9ADzouHI29e1tCo=
+
+
+ Resources/core/icons/chat-left-quote-fill.svg
+
+ hash2
+
+ YEwUdpIoAquswCNNDh8o4tShG6cHl0ZqAgQIB3BnNqc=
+
+
+ Resources/core/icons/chat-left-quote.svg
+
+ hash2
+
+ oAWx9MVrsR7L81Y+J1wt2BJ6tyqlKIfIhZGgqi0vgek=
+
+
+ Resources/core/icons/chat-left-text-fill.svg
+
+ hash2
+
+ 7MdZnVitTapdE1daG790Oiih1rIOHgSpG8s+H89TZ8E=
+
+
+ Resources/core/icons/chat-left-text.svg
+
+ hash2
+
+ bivyJr78OQPGYiiZQzdWSYe1NGw3oeXD/Wm1dQ/9his=
+
+
+ Resources/core/icons/chat-left.svg
+
+ hash2
+
+ WRY8ayM9B6N6cTAWDV/kgv/CHAkU3vsbNVtunXbd97c=
+
+
+ Resources/core/icons/chat-quote-fill.svg
+
+ hash2
+
+ WXlXZ4CT1oL8i7ZWDtnlV6MB8lBkuVceh+B5iKdYryU=
+
+
+ Resources/core/icons/chat-quote.svg
+
+ hash2
+
+ xsfyNSYvFvcKwtxEtz/vzVw0q7cODaY85ok5ZTqnj38=
+
+
+ Resources/core/icons/chat-right-dots-fill.svg
+
+ hash2
+
+ zDF31csbyxOuvKioiUBfhicYkPI/c/fdC7vf7/JkBNY=
+
+
+ Resources/core/icons/chat-right-dots.svg
+
+ hash2
+
+ rZpLOf9x3gViRpd/cZs+E16FltBSsPWDnR8RcDmQUaM=
+
+
+ Resources/core/icons/chat-right-fill.svg
+
+ hash2
+
+ hR5l9KXIEQvd84mXCIFc7l4xioJxgRQMGxTOo+Es8EM=
+
+
+ Resources/core/icons/chat-right-heart-fill.svg
+
+ hash2
+
+ lE5SyG0DhDIMXkMlymZnNHC67GxJE6FmKKjdGYNpNF4=
+
+
+ Resources/core/icons/chat-right-heart.svg
+
+ hash2
+
+ t2X7dGNe5+NSaaHqyCWZGkjagUsO/2alnx1vra2fQok=
+
+
+ Resources/core/icons/chat-right-quote-fill.svg
+
+ hash2
+
+ 6k0WVteB7ZBqSQLCXwysEIqmaL5Ulyuy40H7EQmKmk8=
+
+
+ Resources/core/icons/chat-right-quote.svg
+
+ hash2
+
+ ytytuuyroofgu6UvslXxXa4+d03qWA4HRbwvoCPkmQo=
+
+
+ Resources/core/icons/chat-right-text-fill.svg
+
+ hash2
+
+ 84ue/AL/r2yYqonjvKqJ+H/4tqnY1vAAyTBV3rpkml4=
+
+
+ Resources/core/icons/chat-right-text.svg
+
+ hash2
+
+ eb0DIWG7unjb3aZ7szUEMrivSHGTPjyVvyo1nvU365o=
+
+
+ Resources/core/icons/chat-right.svg
+
+ hash2
+
+ xxigDSAIk4AzjTCVLQla458i/l8ybXS5nzOy2nRA+KE=
+
+
+ Resources/core/icons/chat-square-dots-fill.svg
+
+ hash2
+
+ 86h8DzdSlrW7H+9Fl90svpYFY2rngslRwfDzY9bELcU=
+
+
+ Resources/core/icons/chat-square-dots.svg
+
+ hash2
+
+ bt3NnPfe0oNNpEDieTISV2GRs6ZvmDWS5yNbWB2UyYs=
+
+
+ Resources/core/icons/chat-square-fill.svg
+
+ hash2
+
+ Sh70btUonS0DWOoeYyejHZT+sABfjC0D1nw3nD/tr4Y=
+
+
+ Resources/core/icons/chat-square-heart-fill.svg
+
+ hash2
+
+ bMGyc7ETWvNMGu4e05GBC1rQi69+XaDzwimQuUUFrpA=
+
+
+ Resources/core/icons/chat-square-heart.svg
+
+ hash2
+
+ ZTWwNHYVU0kK90kQJpKhb+H4rKTKA8ppRjm902Gi4i0=
+
+
+ Resources/core/icons/chat-square-quote-fill.svg
+
+ hash2
+
+ q3FqtJ1RM128j2Y7iDddpjCvYBTMsW8M+II73WhCqjI=
+
+
+ Resources/core/icons/chat-square-quote.svg
+
+ hash2
+
+ wmEFQM+YsN7W5bFDMW6G1UmBAL8+cW7TRIS7ATFeBIk=
+
+
+ Resources/core/icons/chat-square-text-fill.svg
+
+ hash2
+
+ /1kMBMdPGLAgDt4keMCqeTZqjJRv1YDMNAsjqAKc2ac=
+
+
+ Resources/core/icons/chat-square-text.svg
+
+ hash2
+
+ mCYNXuni7/zdZaVRY29uH5KlU1kUxhfayJsNy/FLsFg=
+
+
+ Resources/core/icons/chat-square.svg
+
+ hash2
+
+ jqNohowmdjp36QF5LlcisrpWklC9tcB9K4frY6iiOhE=
+
+
+ Resources/core/icons/chat-text-fill.svg
+
+ hash2
+
+ LU0wwhLl2bf4y+gxhCcQN1hWmP2OgN5Sq3s7YBntYPw=
+
+
+ Resources/core/icons/chat-text.svg
+
+ hash2
+
+ twBmtDyfdyZ0U4C9lSohXdPrdZ7eVOYoyeR/0vEjFLQ=
+
+
+ Resources/core/icons/chat.svg
+
+ hash2
+
+ Kn3s8Y9bLgAYhA95wmhI5by0YZdKbkMkPjlEL93FXYU=
+
+
+ Resources/core/icons/check-all.svg
+
+ hash2
+
+ RPfMbFrmeqEFR29fgTFnvIyTgw3W3ZcIYZoxQVTSNRU=
+
+
+ Resources/core/icons/check-circle-fill.svg
+
+ hash2
+
+ xkYKC0yG0cudn+s8kASpQfvGWDlzntZuFu/nORccwbs=
+
+
+ Resources/core/icons/check-circle.svg
+
+ hash2
+
+ cjogFT4faoEB73311eOfpCrCLY9fkhkyF6D/URfJ5k0=
+
+
+ Resources/core/icons/check-lg.svg
+
+ hash2
+
+ kYIUH/8LFiDoKykAJI4xT1utH2eT2nJhQ+GSTBYlM2I=
+
+
+ Resources/core/icons/check-square-fill.svg
+
+ hash2
+
+ 6om1MjqcarRViLMYVtFwgd4x8HZWvQuRTWlvcCMg4wY=
+
+
+ Resources/core/icons/check-square.svg
+
+ hash2
+
+ j/9MtY2RUIptjcuMMO1twvQdxmSJeNy1eDH2BnlSSdQ=
+
+
+ Resources/core/icons/check.svg
+
+ hash2
+
+ 3b72S/Pqmg1SIz63U2KWTiX74BwUYXH2u/qvQ2nG52o=
+
+
+ Resources/core/icons/check2-all.svg
+
+ hash2
+
+ gqsgnDvKeNBD8RFE7RLD5EeaSpZ//Bj2XZ+peClsrKw=
+
+
+ Resources/core/icons/check2-circle.svg
+
+ hash2
+
+ TRweF6l3NbEbO+X9qpZv0nPdx6WK25kyBaf47mIlCGc=
+
+
+ Resources/core/icons/check2-square.svg
+
+ hash2
+
+ vkjX/TrogwKUoZF3aO+h3kZRMmskOCRtvOAF7QRm2NY=
+
+
+ Resources/core/icons/check2.svg
+
+ hash2
+
+ t1+0I1a9FBtS0RK5eBQItJpWwD85Q84NUsmZ5RF1vKM=
+
+
+ Resources/core/icons/chevron-bar-contract.svg
+
+ hash2
+
+ 74iwuQtKCXf2NUYIKglpdPyS8lwymG+5666AajqjSgw=
+
+
+ Resources/core/icons/chevron-bar-down.svg
+
+ hash2
+
+ ceDRYUzPGCPEKJDExo8sgt55qdJ5ERGYk9K4YlnnqPE=
+
+
+ Resources/core/icons/chevron-bar-expand.svg
+
+ hash2
+
+ gI/6sgN/lJbaK4dH6aFXcv1SlUnaPy02tFpI3QdJu7s=
+
+
+ Resources/core/icons/chevron-bar-left.svg
+
+ hash2
+
+ nzyz/v1MrJv2JlFcuuUQRzTDe0KWVEfH74YznDYhr5M=
+
+
+ Resources/core/icons/chevron-bar-right.svg
+
+ hash2
+
+ 1LiWJwE7ieHJQlbeaasM9hMLzqvHHpFzHVP1kCdgodQ=
+
+
+ Resources/core/icons/chevron-bar-up.svg
+
+ hash2
+
+ 0k0cSIGxJLSNzrshE01RO3VusX8cmSrbhXoQS4fieiA=
+
+
+ Resources/core/icons/chevron-compact-down.svg
+
+ hash2
+
+ vIWwkK1U/QNg/5sHyi3xW2qGFaJBtcoN9lmB3VtQGMc=
+
+
+ Resources/core/icons/chevron-compact-left.svg
+
+ hash2
+
+ MIlE0hpz26ZN4PNMJMyO9yDJDN6al1yG2jwYfqjlb8g=
+
+
+ Resources/core/icons/chevron-compact-right.svg
+
+ hash2
+
+ 5cj1Wu76boq7qvipcoTf2tXQaBLnbDG+XGwnd4kVq1k=
+
+
+ Resources/core/icons/chevron-compact-up.svg
+
+ hash2
+
+ QWk4HY+7hbJlDiPyqtzaiTgr+G+ShPatMkEGkpemHoQ=
+
+
+ Resources/core/icons/chevron-contract.svg
+
+ hash2
+
+ 1cWKvPXoJcLeUQHQbBqsUpeYp5B7xC8jQ2Kt8l5wHLo=
+
+
+ Resources/core/icons/chevron-double-down.svg
+
+ hash2
+
+ g8hfuAGmAjUSWHepjGPzOJL90boFINXEdXeWaIOe6wo=
+
+
+ Resources/core/icons/chevron-double-left.svg
+
+ hash2
+
+ +q6iEHG51tYZwTlfuLcyYfXxeduigdfrG66PO2/HtI0=
+
+
+ Resources/core/icons/chevron-double-right.svg
+
+ hash2
+
+ GAIOx2Czv3CbGsTNpHlkO/EO4A0yg925fIleGeg8Gtc=
+
+
+ Resources/core/icons/chevron-double-up.svg
+
+ hash2
+
+ yUt7DANPJ+V3RLzivYNxM4KqamUD3FVuDpxS4k/QCtM=
+
+
+ Resources/core/icons/chevron-down.svg
+
+ hash2
+
+ 7CNqM0FQgqmgY3N/ePaPWXgzvFo36hL4FAjznD+i5jM=
+
+
+ Resources/core/icons/chevron-expand.svg
+
+ hash2
+
+ 2+wkMUfSYt/GVN75adffBEsFvzRdXdy3BZE7SWlQH8k=
+
+
+ Resources/core/icons/chevron-left.svg
+
+ hash2
+
+ CMr3ZMQ5jDBJ9a02/w+seOEutP5UvSBKdePhGbfbecY=
+
+
+ Resources/core/icons/chevron-right.svg
+
+ hash2
+
+ jHbHrZ6pbd4rsCXF1sRsFNYqKQeB4z4QU3fp1GSV6s8=
+
+
+ Resources/core/icons/chevron-up.svg
+
+ hash2
+
+ 4F79yrfk/xuYGuhLxGAMr0i13zUZ9s8G1ZMHjF0GgXs=
+
+
+ Resources/core/icons/circle-fill.svg
+
+ hash2
+
+ BmV59hBMde3vz6Dtuxu2engMU2SpGL34BkqcbKFUJk0=
+
+
+ Resources/core/icons/circle-half.svg
+
+ hash2
+
+ 0p7fW0AYEvUWnCvrTUEbCNjjWwNIhHM3uAK2RPHXMPA=
+
+
+ Resources/core/icons/circle-square.svg
+
+ hash2
+
+ vkqsKz2//AeiGcgOX/di0XRnjeGe+NatQfoHItz0d74=
+
+
+ Resources/core/icons/circle.svg
+
+ hash2
+
+ QQghuzGfiT+WYI1ExFiTJEsQD+6RHfH6t7Oh3d+B1yo=
+
+
+ Resources/core/icons/claude.svg
+
+ hash2
+
+ IAk/FNMCnutzGjpIitulzAoT5E79M6VG+TDDna6tL/M=
+
+
+ Resources/core/icons/clipboard-check-fill.svg
+
+ hash2
+
+ voL0mGX/yyYipYSX+aD0ouvOK6DYLGYHJpAIFY+YqsI=
+
+
+ Resources/core/icons/clipboard-check.svg
+
+ hash2
+
+ +hdOVIw0K2YtwV6i16PbIQn+wVtILUNcNhLcctyPm18=
+
+
+ Resources/core/icons/clipboard-data-fill.svg
+
+ hash2
+
+ uNSKG5psSRtSxRP8ZCmK/z/urmcb26RaHkj1fJwG8Do=
+
+
+ Resources/core/icons/clipboard-data.svg
+
+ hash2
+
+ 3MegKlfbsA/EtRo9pvdkXs3tY2XKOx9JT/m+E5E70FU=
+
+
+ Resources/core/icons/clipboard-fill.svg
+
+ hash2
+
+ eBHZYc4p1UFXrfNAN13IceuFUoxYU2jWW2OU3Q870fU=
+
+
+ Resources/core/icons/clipboard-heart-fill.svg
+
+ hash2
+
+ X39J8mOcUwlgQBrqhGdzAEzRKVawOWzznrmEYAEcBMg=
+
+
+ Resources/core/icons/clipboard-heart.svg
+
+ hash2
+
+ wJv0/Q5ZPVqh++0Slmh2flEP0lszX7Ys2WXvcCZdj5k=
+
+
+ Resources/core/icons/clipboard-minus-fill.svg
+
+ hash2
+
+ ouQVgxy7AaGZmzUqIGmN8r7LB6VHr0Qb547IKGzm5RI=
+
+
+ Resources/core/icons/clipboard-minus.svg
+
+ hash2
+
+ Y7FvrszT962rLDcqeEqv7HnffI9k7vYciqNSFMbDBEk=
+
+
+ Resources/core/icons/clipboard-plus-fill.svg
+
+ hash2
+
+ 5tr1WUN+bDUEjon+sV1WzQynASGAgGKP3n0cjj/XEU0=
+
+
+ Resources/core/icons/clipboard-plus.svg
+
+ hash2
+
+ TgSfbwfxGjXpKMdSlU7/QQOXQanRVrm3z+UlZCW/HdU=
+
+
+ Resources/core/icons/clipboard-pulse.svg
+
+ hash2
+
+ yE8A+qTZbi2PgowZ6dD1fkD7WyqCxFpt/v+j/IE/4K4=
+
+
+ Resources/core/icons/clipboard-x-fill.svg
+
+ hash2
+
+ Tfz3CSBljlUcPFufb3VU3RpEOlDTvHct75lJa0DuONk=
+
+
+ Resources/core/icons/clipboard-x.svg
+
+ hash2
+
+ o31BSbzsUWblNKcL/UdmCa+OD2PjoWah+Cv9ncdPzTM=
+
+
+ Resources/core/icons/clipboard.svg
+
+ hash2
+
+ tCliwKRu/sC6tW9287lFKWc0+dShGFf/zcBHiFowX9k=
+
+
+ Resources/core/icons/clipboard2-check-fill.svg
+
+ hash2
+
+ 0d4dVFhzQxSWi7H3jQRHzYmKH5z1RegvXPr0SzH7BvI=
+
+
+ Resources/core/icons/clipboard2-check.svg
+
+ hash2
+
+ P6w0SAa/W1uTWRAq4NpMoiKwEmWTtrPGZIohKIyhIqw=
+
+
+ Resources/core/icons/clipboard2-data-fill.svg
+
+ hash2
+
+ slApv0u528/NsNNMKo82fiq+/uuBLhbXDMBCATGTZt8=
+
+
+ Resources/core/icons/clipboard2-data.svg
+
+ hash2
+
+ FLIDpX1xCY70y96L+Xr0GFYFkkv/WolTCoLn3cQYdmA=
+
+
+ Resources/core/icons/clipboard2-fill.svg
+
+ hash2
+
+ Rkta41YrSR3olXKh4gzWNtow/rw4nQwIKwfF8DeiHtw=
+
+
+ Resources/core/icons/clipboard2-heart-fill.svg
+
+ hash2
+
+ cyCN8g3i5mMxi/SS0L0BSsdUfYUi3jKDaITR0FVIJA8=
+
+
+ Resources/core/icons/clipboard2-heart.svg
+
+ hash2
+
+ S1Y9Tebvt71IdAMlugmvRY7V3JC004DUgkJw+SFD+nQ=
+
+
+ Resources/core/icons/clipboard2-minus-fill.svg
+
+ hash2
+
+ AAvrGD1bW0AVvcgh6tX51KP3RLyJwa6sMcjzbmVheHo=
+
+
+ Resources/core/icons/clipboard2-minus.svg
+
+ hash2
+
+ 1EZlXJf564T6p76Ath87vZYDG+HvCW//Mv7TYxI/Z68=
+
+
+ Resources/core/icons/clipboard2-plus-fill.svg
+
+ hash2
+
+ TXk0/Y3qkAxH7mK0LjKhPC9uePD3n5Z0ONU6m/TdKR8=
+
+
+ Resources/core/icons/clipboard2-plus.svg
+
+ hash2
+
+ dbBsWl6kEHvUfTdPnyp1s55qhIklDO7snrBBBDMUMdk=
+
+
+ Resources/core/icons/clipboard2-pulse-fill.svg
+
+ hash2
+
+ /xg0GOzd69Qi2ML2BJs6MqWBsK2Ch16M+DN6WBku8KM=
+
+
+ Resources/core/icons/clipboard2-pulse.svg
+
+ hash2
+
+ K7SCq/GAcUDfKg7muqL+PxcIMZeK64TbSV1G2jGIX10=
+
+
+ Resources/core/icons/clipboard2-x-fill.svg
+
+ hash2
+
+ mi2iWloMRbj2NDjIsYDg2jg3mVp0m8pc82pIJHn1FDA=
+
+
+ Resources/core/icons/clipboard2-x.svg
+
+ hash2
+
+ x6iaOpxe9/zOyQyTmD++2O8CRBOCGWC45KCE4+GVr/g=
+
+
+ Resources/core/icons/clipboard2.svg
+
+ hash2
+
+ b7yn9bfx330cdvxX+6Yv6FacC9fz4zNKceLo0YgpRgM=
+
+
+ Resources/core/icons/clock-fill.svg
+
+ hash2
+
+ PinUAe99et+UVD+KubvjjyeqFOXe4FzKi504ezecuZM=
+
+
+ Resources/core/icons/clock-history.svg
+
+ hash2
+
+ G4Ms4XQY1SU9CktlQ/W8VVTDmWStfQ+7fJ9ycVYQ0K0=
+
+
+ Resources/core/icons/clock.svg
+
+ hash2
+
+ uq/G9wSQaz9zoxqs6UL0+a6fV8TgLz6N55MDp3HEXgA=
+
+
+ Resources/core/icons/cloud-arrow-down-fill.svg
+
+ hash2
+
+ 4GjcTkaqc8gDsnNP9+YRYw3bf6CeMczLKlLxYy0KkSU=
+
+
+ Resources/core/icons/cloud-arrow-down.svg
+
+ hash2
+
+ CCaIEtZ9hne5UM4eC3tDlATIBl2Zvq/djavpURFxwQM=
+
+
+ Resources/core/icons/cloud-arrow-up-fill.svg
+
+ hash2
+
+ RhNVIjkON5PySIIUMDtY6LVg2HUOunmkgOKn8G0yQpY=
+
+
+ Resources/core/icons/cloud-arrow-up.svg
+
+ hash2
+
+ LhO/UK/KEOOVTPv/Pg0xZNMoYl/SpWzhnc+qxyIT/+M=
+
+
+ Resources/core/icons/cloud-check-fill.svg
+
+ hash2
+
+ uN+s3jbl4n1WLmclb1QZ8EPMbU7tF+Byv8tIdffwbHA=
+
+
+ Resources/core/icons/cloud-check.svg
+
+ hash2
+
+ kBFh/ZQ0oKpp2MaPkk9UMqUQMWaXdsYweUbx3do3/Ao=
+
+
+ Resources/core/icons/cloud-download-fill.svg
+
+ hash2
+
+ MeS/osSD60zx2noyITSqiKwhHMbbWOmAQWrJacvSwDg=
+
+
+ Resources/core/icons/cloud-download.svg
+
+ hash2
+
+ Q3uuH2mt/w52UcJ4yF7iJhZt1NMymy3cFMt4jOsReAI=
+
+
+ Resources/core/icons/cloud-drizzle-fill.svg
+
+ hash2
+
+ NiMuaVpv/yWZvzVSug/yT+F1W1Asa6s+r2WbVXbeOKU=
+
+
+ Resources/core/icons/cloud-drizzle.svg
+
+ hash2
+
+ Szkc7nSmOL2PGBr7mNVn1NB5xThnjsBWlqOFm/jhHd8=
+
+
+ Resources/core/icons/cloud-fill.svg
+
+ hash2
+
+ +ACBdhxlQ7y6oZUyGzuAQCMmtxW8CbcD64JpqPw6468=
+
+
+ Resources/core/icons/cloud-fog-fill.svg
+
+ hash2
+
+ HAnWvAM9uHiT5zv7fsoSUf2iNvpZsr6ksH17sM3XuJ0=
+
+
+ Resources/core/icons/cloud-fog.svg
+
+ hash2
+
+ dWLacHUMhpFHgC7gxAmo3j1Vl8b1sz1bqp48jy0GXGk=
+
+
+ Resources/core/icons/cloud-fog2-fill.svg
+
+ hash2
+
+ LRClen4R8O787/qoKZPXqacxU5TXTE5GQbKEvzRzXC8=
+
+
+ Resources/core/icons/cloud-fog2.svg
+
+ hash2
+
+ BRI8xH8yglKXwPnRvS06IR6lVQeGneT7flNE8DqwaTE=
+
+
+ Resources/core/icons/cloud-hail-fill.svg
+
+ hash2
+
+ ScaXCjJKQQisCakjCyRqBpDjW0vW85uFy1X0MFGsjLA=
+
+
+ Resources/core/icons/cloud-hail.svg
+
+ hash2
+
+ YFSOmOVPVJ0m7wX1jLKOPh9tkSh+v/+rRPNOjm7BnSg=
+
+
+ Resources/core/icons/cloud-haze-fill.svg
+
+ hash2
+
+ S5JMargfKZzVsHWpUw6SBUaLCY1aypgaBs+6nAEDjG4=
+
+
+ Resources/core/icons/cloud-haze.svg
+
+ hash2
+
+ 9S3JK/kMqbvQjPkb4YgaOrkoZZSOe5lAWITGrLa/0aA=
+
+
+ Resources/core/icons/cloud-haze2-fill.svg
+
+ hash2
+
+ 3/Vk/qH/5CgWaxLgv1boKxXi0Z2v/m4N1mYz93UmYw8=
+
+
+ Resources/core/icons/cloud-haze2.svg
+
+ hash2
+
+ ICOLytR40SlFVLqxBqw6SlRj1ZikpWC+mwEhdseZBMA=
+
+
+ Resources/core/icons/cloud-lightning-fill.svg
+
+ hash2
+
+ LPc3aaZNDVZTgBT75UKUKFHg502zRVWsPzOqLFdmdjg=
+
+
+ Resources/core/icons/cloud-lightning-rain-fill.svg
+
+ hash2
+
+ +wFtG+/pLy5VcKHaT2N8Gy5SNT5ZuYqWIVGh8kLqDck=
+
+
+ Resources/core/icons/cloud-lightning-rain.svg
+
+ hash2
+
+ x4CSWzGZUwYoPIe7eINZdPt4t3uM22osmEF2Y18hKtA=
+
+
+ Resources/core/icons/cloud-lightning.svg
+
+ hash2
+
+ 4gdk0UJwTYLADAGT5cXyu/AzkneD9En+pvDwj5RauBM=
+
+
+ Resources/core/icons/cloud-minus-fill.svg
+
+ hash2
+
+ Y2peb2rTyU3UdhPeu/AqcfuSFVAspsC0dVjuntBRLmA=
+
+
+ Resources/core/icons/cloud-minus.svg
+
+ hash2
+
+ 20oTnh2i1+zgcYSy+ZSEZNQcceX3l7SdWGTo32ZB+Ng=
+
+
+ Resources/core/icons/cloud-moon-fill.svg
+
+ hash2
+
+ 6q99InogtfPOgQnidfvXKp6k3KaiXA4pTdZfqJohVAA=
+
+
+ Resources/core/icons/cloud-moon.svg
+
+ hash2
+
+ 7/W8mDLPQb1TfMuinpvWLoUFLDnqLlxnPUy0+IfKkv4=
+
+
+ Resources/core/icons/cloud-plus-fill.svg
+
+ hash2
+
+ kedl3sACr39iRvtw4iWg/dbzA6c1ug/i5GYyg96/9jE=
+
+
+ Resources/core/icons/cloud-plus.svg
+
+ hash2
+
+ G1SYzYejIVj4XCT78G+NBA59qnOxyzRnf1RaZBfj1rg=
+
+
+ Resources/core/icons/cloud-rain-fill.svg
+
+ hash2
+
+ x49U6qPGp15mUMOAy1zXWfozIBaTba6Mf5AtqR/z1qA=
+
+
+ Resources/core/icons/cloud-rain-heavy-fill.svg
+
+ hash2
+
+ GM7Ej464K/Mwmb2C9bh4V0tj6Mf/d4FEXRiIvyxHok4=
+
+
+ Resources/core/icons/cloud-rain-heavy.svg
+
+ hash2
+
+ Kf/XP/tFLiO2mXDGPlPMBIvdu6NsU/8TAQZ1GGqfBPU=
+
+
+ Resources/core/icons/cloud-rain.svg
+
+ hash2
+
+ JiIEfWP2VKjYx+zCewFrPDwTx86MriosifHK3jGv97g=
+
+
+ Resources/core/icons/cloud-slash-fill.svg
+
+ hash2
+
+ WSR0bCQc0tueqjKlQjXNxPN+HgEmriO7V3fh0UZdmZs=
+
+
+ Resources/core/icons/cloud-slash.svg
+
+ hash2
+
+ ekTkVHXAqlVRZEuJjCJfcB5owz+c/taNXKIJy8YJMPI=
+
+
+ Resources/core/icons/cloud-sleet-fill.svg
+
+ hash2
+
+ CpZ4ghXJBlRa2aUF8bnzgFn92t46o7wRl39P4oOn4mk=
+
+
+ Resources/core/icons/cloud-sleet.svg
+
+ hash2
+
+ l0qxtxwVMy66z1WyboWohw0vjtkGuFhREW/D6WUpfEg=
+
+
+ Resources/core/icons/cloud-snow-fill.svg
+
+ hash2
+
+ SAnIcwRs46tXJ+9zfHbU83crajT+RZSJ5ehfyvIHRF4=
+
+
+ Resources/core/icons/cloud-snow.svg
+
+ hash2
+
+ 3unz8zh3LtAS3qM1cNr8nuWJOMG+l8Ly64Ir3cMpt+M=
+
+
+ Resources/core/icons/cloud-sun-fill.svg
+
+ hash2
+
+ QkwzVEZcFzVrxHNP+f8gT9SC4Xrl5jN2GXaAP9jX/Jk=
+
+
+ Resources/core/icons/cloud-sun.svg
+
+ hash2
+
+ N0b+Gazlr3GpWxGhYg5RbpPZ+x+ojZprZEt6dObZ51c=
+
+
+ Resources/core/icons/cloud-upload-fill.svg
+
+ hash2
+
+ ryfh0FH8XbhY8EsX9A4Przc9qLG8kXybIGInPvqCzi8=
+
+
+ Resources/core/icons/cloud-upload.svg
+
+ hash2
+
+ rI2u2Vz9RRv9Dw1YRP9JLeu3s861BRlr8uFlZD/yhAw=
+
+
+ Resources/core/icons/cloud.svg
+
+ hash2
+
+ t8LyzIHPP127a0Umwv9BJo05tBNiOraT4dX5fIJvdyA=
+
+
+ Resources/core/icons/clouds-fill.svg
+
+ hash2
+
+ eOE6SUrTTB3IiOqp2rKnLlAclYNnjOePGBvsrsBuaBo=
+
+
+ Resources/core/icons/clouds.svg
+
+ hash2
+
+ kagr5JCzE9zJpG3L42UKvpg2Ppo6OAZ7TjEP57sHoLw=
+
+
+ Resources/core/icons/cloudy-fill.svg
+
+ hash2
+
+ aP4+Xcl5r/w+uJPkcOAPbmLo3hEyiQf22m8UBJtc/iE=
+
+
+ Resources/core/icons/cloudy.svg
+
+ hash2
+
+ ncpLRv3N3JPMZRuVn1QlVdHrD5QcefmOfkD+x7/LAtQ=
+
+
+ Resources/core/icons/code-slash.svg
+
+ hash2
+
+ 2O2xhiSyqSDpu8hm8EBOP+6njXkWbJLzFa2v5Fe9bKc=
+
+
+ Resources/core/icons/code-square.svg
+
+ hash2
+
+ y1Ibtt6clzOey7xeb6z2Sen3Xkn4zIhj2eQ2Z04Qgy0=
+
+
+ Resources/core/icons/code.svg
+
+ hash2
+
+ +ErDuaMV7EYOwZ3xMkuVAndn6MgWs+6IfKQy5f+/c+Y=
+
+
+ Resources/core/icons/coin.svg
+
+ hash2
+
+ OIWLETD3OgvbHhZ4LOivI4dUZFjTkNY2PtzTie2bSBk=
+
+
+ Resources/core/icons/collection-fill.svg
+
+ hash2
+
+ JhTqMkpuiy2lRlX44bOY1pBHhIcxe+YGN2w4XbVA69A=
+
+
+ Resources/core/icons/collection-play-fill.svg
+
+ hash2
+
+ oax9lpztJQuLSLKoOiLMIWTFXA6f/H0Ys6g6I4ec85Y=
+
+
+ Resources/core/icons/collection-play.svg
+
+ hash2
+
+ vr1Weht/Nu6BBfmqiBUWz9B1GvHpSnVrr92wv8vlX2g=
+
+
+ Resources/core/icons/collection.svg
+
+ hash2
+
+ c00Mh/Xirnmgb/fATxnQrCHWxrC6DWP76MYosgXLdXQ=
+
+
+ Resources/core/icons/columns-gap.svg
+
+ hash2
+
+ VFbVa0GOG01D9Jx7RvcQdoDWF/gxxNUAYxTAu6KKQMI=
+
+
+ Resources/core/icons/columns.svg
+
+ hash2
+
+ i2U5OmCaoFrzNLEW0LZ6BFLxklqS50ABtydcoBc+o3Y=
+
+
+ Resources/core/icons/command.svg
+
+ hash2
+
+ uZewz6iJ3OcTMD386+5XmBKp7wG+cmXZdxKIillo2nc=
+
+
+ Resources/core/icons/compass-fill.svg
+
+ hash2
+
+ +UwYQftA+wJFI8otRFrhIqDbF8eOLGiDQZzPUzt9Pvo=
+
+
+ Resources/core/icons/compass.svg
+
+ hash2
+
+ FnUWNQEKNqI3JJHZ3WyODrdsopvFpL6empZetgkE4V0=
+
+
+ Resources/core/icons/cone-striped.svg
+
+ hash2
+
+ RjkkO8rO9ZoQb0AD6p4GNKB0AJ9yJrbA8r5Yn7QS7zo=
+
+
+ Resources/core/icons/cone.svg
+
+ hash2
+
+ BOkCdeIbDPN+a2coIOnF2YlhNxkH1His35X3dR2+FmM=
+
+
+ Resources/core/icons/controller.svg
+
+ hash2
+
+ py7OW+wRST451ypwmKRSjH52dNhFaGVO7get2ALNjE8=
+
+
+ Resources/core/icons/cookie.svg
+
+ hash2
+
+ 9QDrIKcUC3sVBsfTFHrHqyal0G37Miui0TuINee5Oqk=
+
+
+ Resources/core/icons/copy.svg
+
+ hash2
+
+ LnqralNWWRpTmjXb677cECMZ+PZ/IgVEVEiWpWiw+jE=
+
+
+ Resources/core/icons/cpu-fill.svg
+
+ hash2
+
+ 03d8ex8Rj3uWh757pweZwsmPU3Vv7sgMpP4+R2+L2LM=
+
+
+ Resources/core/icons/cpu.svg
+
+ hash2
+
+ GtWPIqRh2fjPpZJegoxSWRGTjQ/nIgwDu/oVTpitie0=
+
+
+ Resources/core/icons/credit-card-2-back-fill.svg
+
+ hash2
+
+ U54ZWGnKTykn8mFCwEj6grEYUpanSaUot/8ai/hkFlM=
+
+
+ Resources/core/icons/credit-card-2-back.svg
+
+ hash2
+
+ oJfD+cDXG7F+HE7C8k3ifDtXWvtSXIXm3bu3FiLfs4E=
+
+
+ Resources/core/icons/credit-card-2-front-fill.svg
+
+ hash2
+
+ xDlJL1obN8luAUewDjR0mbjFFV8k40G+FS0VfHjJUhE=
+
+
+ Resources/core/icons/credit-card-2-front.svg
+
+ hash2
+
+ FoXSQgksjpzWl8R8T4tVX2QfZOtGe8Kj4dl8G4uqtqs=
+
+
+ Resources/core/icons/credit-card-fill.svg
+
+ hash2
+
+ rntM7jEgIQJZX4Zd+p5oDrHhvEVqxHUK7ag1sMztTac=
+
+
+ Resources/core/icons/credit-card.svg
+
+ hash2
+
+ 68LKYt/QPPG/NwURqRe8vtvk5Kmo0QZSIF9k6BoztCI=
+
+
+ Resources/core/icons/crop.svg
+
+ hash2
+
+ EOGo4zmFtc59NcWvFglkAktVo/Yz+/sT2AxjQU+yyvM=
+
+
+ Resources/core/icons/crosshair.svg
+
+ hash2
+
+ XjDMq5EChcaU8taMCo5RY9LxE9iUd8sV/D6rGv1T7js=
+
+
+ Resources/core/icons/crosshair2.svg
+
+ hash2
+
+ nLh3c5pjSJ43jxj1Uc5vncheyE9K5r+3wzI1B9EeZz8=
+
+
+ Resources/core/icons/css.svg
+
+ hash2
+
+ 7kgVqUswZTHqyD0P9kICN0gDCW8fccDaUZ+e4UIiemA=
+
+
+ Resources/core/icons/cup-fill.svg
+
+ hash2
+
+ pQc0BhYD1h1YHkPATdxsyDrsbhIWLe3Q1dHKpd2f238=
+
+
+ Resources/core/icons/cup-hot-fill.svg
+
+ hash2
+
+ nleBDMpFSXQtQMn4CSwTdO4b3EWYcYGZKBR788asFNA=
+
+
+ Resources/core/icons/cup-hot.svg
+
+ hash2
+
+ GFlwVGTGZaGsdneX1VfMxpQGhH9MwOzhHOotY+wGnRM=
+
+
+ Resources/core/icons/cup-straw.svg
+
+ hash2
+
+ lPVFylqfwjLd3Vc0Ry9OIzHBLNodbDx6gHF87NAVO9U=
+
+
+ Resources/core/icons/cup.svg
+
+ hash2
+
+ 1L1tS6mc1D0HLbQ1qde7kqg7aV2YtYWN5va9p/4guOc=
+
+
+ Resources/core/icons/currency-bitcoin.svg
+
+ hash2
+
+ imjkHZKA/NUu0RDh0fWSrXHf9R1IKRjPiwMbhb2esRA=
+
+
+ Resources/core/icons/currency-dollar.svg
+
+ hash2
+
+ 4mBtlAmkaXzaELxTtwE8xZKE9HuPOeYeD7CBd2mFY/Q=
+
+
+ Resources/core/icons/currency-euro.svg
+
+ hash2
+
+ /DIORCHrs3hivrqcQNfjPXzWThU85OULlRSj6v3L8Fk=
+
+
+ Resources/core/icons/currency-exchange.svg
+
+ hash2
+
+ 1K32ZEw17XA+oUsHoviRocrlYiP/NBAhiEguTKtmJOE=
+
+
+ Resources/core/icons/currency-pound.svg
+
+ hash2
+
+ Z2Zr/d+FETP+OAFTZvTVDjaPCv9Kx4B95Vcq+RE8pNE=
+
+
+ Resources/core/icons/currency-rupee.svg
+
+ hash2
+
+ gsoK1iedqzj/F65Vvt3Z6wA7JktewsAR0qK+tBABO00=
+
+
+ Resources/core/icons/currency-yen.svg
+
+ hash2
+
+ uZcyJIGN/R6qD//xarHVOI4BrGbcQprC8eZFfrmR0Qw=
+
+
+ Resources/core/icons/cursor-fill.svg
+
+ hash2
+
+ r2uFnSTdT2L3wNCS3cYkYD2UZHGCklnN1zBVbiZIyE0=
+
+
+ Resources/core/icons/cursor-text.svg
+
+ hash2
+
+ h/btxODpTpIKeXYaEeZ7S1GtHFsfK5nD2uJq47isPVc=
+
+
+ Resources/core/icons/cursor.svg
+
+ hash2
+
+ +YZnYRRE/i4gOw21DGJ8MHiHjAwJK4rjq78uMLYo/uk=
+
+
+ Resources/core/icons/dash-circle-dotted.svg
+
+ hash2
+
+ 7L1jpeBdE1Qpr8mc3U0vS+27xruvV2wsnZmCVfDjYo8=
+
+
+ Resources/core/icons/dash-circle-fill.svg
+
+ hash2
+
+ dEmdLLkzj5MmMsMaj6uqlSAbmcEpMVq/8CGvfeVaCFo=
+
+
+ Resources/core/icons/dash-circle.svg
+
+ hash2
+
+ VRpXBTYyDm6eH9aWJDogZclyAhJM29Z/svrLn1nh6cM=
+
+
+ Resources/core/icons/dash-lg.svg
+
+ hash2
+
+ z7cea6KodI3iAsAf1Y8P9fQJoI9ybcKqacVE3abuEjI=
+
+
+ Resources/core/icons/dash-square-dotted.svg
+
+ hash2
+
+ /Zl9GmRLctkVL1f+UCZEtnwqRYegUJpC+9ahkCW6SAU=
+
+
+ Resources/core/icons/dash-square-fill.svg
+
+ hash2
+
+ ZY++GhZ5Pwfpzy46xe7UXzWuPsVlgqDvWK9wU2GSTJM=
+
+
+ Resources/core/icons/dash-square.svg
+
+ hash2
+
+ u7t/fHDZXWkY7TvA9iOrO3KpeV7E4ND1KBCmHdvzXPY=
+
+
+ Resources/core/icons/dash.svg
+
+ hash2
+
+ HlpFfqUhiXPjlP6nfVu2vmMws1DRjosdijtnB/pki7Y=
+
+
+ Resources/core/icons/database-add.svg
+
+ hash2
+
+ sHByL8/Pg4qcvEWdnNmRw7mTuaftLGqBOnFcNwgDb4s=
+
+
+ Resources/core/icons/database-check.svg
+
+ hash2
+
+ tHD3vCJeQ3It0fBlqIfLzUDecZVHrLPDn84GUdYXUnk=
+
+
+ Resources/core/icons/database-dash.svg
+
+ hash2
+
+ cCmjDGZHTxCGh6q3ZKCbnJvy07GYWz1uk0ojMW8vVOo=
+
+
+ Resources/core/icons/database-down.svg
+
+ hash2
+
+ /T1wq1ooxOgigGoIKChwcV/8wlaR+T8ieO1p+4fj0bQ=
+
+
+ Resources/core/icons/database-exclamation.svg
+
+ hash2
+
+ ihcFD092NL1cYwv9ZJDN36y4jq/L+tHt0oRrE43go/k=
+
+
+ Resources/core/icons/database-fill-add.svg
+
+ hash2
+
+ yXUHIgnsZIHIeoQR7P8cvUjYuAsPLwCvq0OGSFEk0mU=
+
+
+ Resources/core/icons/database-fill-check.svg
+
+ hash2
+
+ /8Jq7Rn4UgBNyo5sEJ84UUOnD38OcFLLyIrQAO0ay+I=
+
+
+ Resources/core/icons/database-fill-dash.svg
+
+ hash2
+
+ ClKrk4ngfW6ZvyD6qB/xXrAJz/6h7d1YvIhscd3pWpA=
+
+
+ Resources/core/icons/database-fill-down.svg
+
+ hash2
+
+ oPXpcjw43sDQACjdp/31a+jyvd5Vv7YRmdexMv58EKQ=
+
+
+ Resources/core/icons/database-fill-exclamation.svg
+
+ hash2
+
+ boWJuS4sHD4febrG6/z+S276GAhB5EmQn9iqeDPTgqo=
+
+
+ Resources/core/icons/database-fill-gear.svg
+
+ hash2
+
+ 20/GIRj1SgGkb4MpG1R4xY2hj3LFtvE/NWPj0uSFRcs=
+
+
+ Resources/core/icons/database-fill-lock.svg
+
+ hash2
+
+ SYn8QZjMpAyOt5XxZXFZDyG++AhumSGBSy69Lq0VFXI=
+
+
+ Resources/core/icons/database-fill-slash.svg
+
+ hash2
+
+ 1Pfy1DLg8gYU2KuYX91RfbHDyz8Hd/A+zFDDZ5M5o/4=
+
+
+ Resources/core/icons/database-fill-up.svg
+
+ hash2
+
+ 1H2RwmSVQ/krB5vCZ35A6CxdY6PsPnsEHUWk4b6G9tY=
+
+
+ Resources/core/icons/database-fill-x.svg
+
+ hash2
+
+ zN2DLF8nHJn9/stXphmz37S/Y6Nr/7vbljPgopHed6w=
+
+
+ Resources/core/icons/database-fill.svg
+
+ hash2
+
+ AJJZq0oI2//jt2THAXTMPfCFxwZtBqiyVwOL35OitOU=
+
+
+ Resources/core/icons/database-gear.svg
+
+ hash2
+
+ qY8EWe327vwqvJTBsWXxVj12KhnHbmRdk9UVHHRWpag=
+
+
+ Resources/core/icons/database-lock.svg
+
+ hash2
+
+ oYHoRRtp3OFOSN1rzZiTaE9QzklijqHBDWwQQIehrtU=
+
+
+ Resources/core/icons/database-slash.svg
+
+ hash2
+
+ qKzBw1je+9ha9rHhrmMQ1SRywwLkMnj1rvTvLeDmQRc=
+
+
+ Resources/core/icons/database-up.svg
+
+ hash2
+
+ xCS9X8yI9hma7Ki6IlyzPMA8GSBX7tf55HwaF7Wa0PA=
+
+
+ Resources/core/icons/database-x.svg
+
+ hash2
+
+ L6s71hGBxPt+VV30lBmIimpVXjPOLzGXSiE6gxuruM4=
+
+
+ Resources/core/icons/database.svg
+
+ hash2
+
+ RHF6Nmqo4W4p68pA6hkefvKbyFX0vJB58QztwfGjEew=
+
+
+ Resources/core/icons/device-hdd-fill.svg
+
+ hash2
+
+ G4bc6lPmrTFeIiQIYlxh4s51Dgl8kJubFvG1jLEz7kI=
+
+
+ Resources/core/icons/device-hdd.svg
+
+ hash2
+
+ z4pAowvgjLrEfqpb58Gx7X5O+9ItewOTVTNi4+b9bYE=
+
+
+ Resources/core/icons/device-ssd-fill.svg
+
+ hash2
+
+ sia5ojAC4HomsXhKD0ok2iZWfP+u2XW5JwAAZavR4Es=
+
+
+ Resources/core/icons/device-ssd.svg
+
+ hash2
+
+ An4vBWCHfQ9JTdI/UAvfOxqiHY6KFDzTPt7V2B3YthU=
+
+
+ Resources/core/icons/diagram-2-fill.svg
+
+ hash2
+
+ B8EBCVBrCkwVIUQR6hBwzzxXoeLlZYJsu/yZjjlqYZs=
+
+
+ Resources/core/icons/diagram-2.svg
+
+ hash2
+
+ NxSWIZkbfS+fT+8kXIsOGB4W3mWLZu7Oa/cKEEgfnCA=
+
+
+ Resources/core/icons/diagram-3-fill.svg
+
+ hash2
+
+ 96ivliN78lTed2VuThvalqSyDCh01W6YbfZh/ZFrxMM=
+
+
+ Resources/core/icons/diagram-3.svg
+
+ hash2
+
+ 6f2LgwtPkcsupzIJ7uBp3LctMbMgt0zqGy3WGMN6fz8=
+
+
+ Resources/core/icons/diamond-fill.svg
+
+ hash2
+
+ Sm3RAx1dfu/ywTui+Fyl6dn6kgeJLi0hlaUugl3uzo4=
+
+
+ Resources/core/icons/diamond-half.svg
+
+ hash2
+
+ 2F6SkmRvA0zI187+5/dNefLJQVUgSA2sTiCtCf85yF8=
+
+
+ Resources/core/icons/diamond.svg
+
+ hash2
+
+ 1BAh8gLpo+plYBsz5Z83jYqXaMoPu8jncngQBJ4bvss=
+
+
+ Resources/core/icons/dice-1-fill.svg
+
+ hash2
+
+ JHzh3KCOF/Nx+GVUgtGItara6hzqHVVx0Cy6Acvozng=
+
+
+ Resources/core/icons/dice-1.svg
+
+ hash2
+
+ P8xFNyC7KIlr/mz58ICJ3LgZ+YeDLjYusb3pVGT9YQc=
+
+
+ Resources/core/icons/dice-2-fill.svg
+
+ hash2
+
+ Mn9O0sA/ftsYixEn/8i8h27B2qHu6yLSsEsRvunHbKc=
+
+
+ Resources/core/icons/dice-2.svg
+
+ hash2
+
+ 6x5EjMH2e3FAPSlb+wudkRjYd5tnxd+H1GsrPHPdKtM=
+
+
+ Resources/core/icons/dice-3-fill.svg
+
+ hash2
+
+ GDGshZvGwmReBsqvO+qSvrw/VEm9OAEc7OOEYTokVH4=
+
+
+ Resources/core/icons/dice-3.svg
+
+ hash2
+
+ aOJ7lkHRHqmqDIjp20TyQL2Xxqm+8R1VOwG0b8sEnNc=
+
+
+ Resources/core/icons/dice-4-fill.svg
+
+ hash2
+
+ zIFz4QAsf+gFou6z7i0D2DY+hZnXvJIKTBjgh6mWLZA=
+
+
+ Resources/core/icons/dice-4.svg
+
+ hash2
+
+ xuvkyNoZbXiZU9b2zq2VBgbq23LUlSWrm8q4Id5YCrg=
+
+
+ Resources/core/icons/dice-5-fill.svg
+
+ hash2
+
+ 1dVp4PG/LOdowDymP3jfQvIeCmnxMa/8kEHmP0iIv84=
+
+
+ Resources/core/icons/dice-5.svg
+
+ hash2
+
+ o4FUBqd7BgGUDMDSquLMOQDB+QdoGsJPG5jW3zfEf2A=
+
+
+ Resources/core/icons/dice-6-fill.svg
+
+ hash2
+
+ g580zEQVDcTG42O3o7W9hRFjDIelLmYe8un3FP17A/w=
+
+
+ Resources/core/icons/dice-6.svg
+
+ hash2
+
+ m5FS7nekT+UNYW0bwf/bmavYrnMFL5QjciPUhQulQ9U=
+
+
+ Resources/core/icons/disc-fill.svg
+
+ hash2
+
+ +fdSbCHdU/Jgl3TcHV3DrH0rn4HhjWBm+j4MQN5m+MM=
+
+
+ Resources/core/icons/disc.svg
+
+ hash2
+
+ VJ/y/n8E9j+XB+0M8FQ8GUYq1fKBfxBXwZtDT5Rx7x8=
+
+
+ Resources/core/icons/discord.svg
+
+ hash2
+
+ MxpDdABvG9es+9j34nuvmQktynhn9gnZr8nwyGnylgo=
+
+
+ Resources/core/icons/display-fill.svg
+
+ hash2
+
+ A2QuQDub9pRp7oxmjqGNw1roEisoOAAlydAtZ/NWLkU=
+
+
+ Resources/core/icons/display.svg
+
+ hash2
+
+ SjS738L6Bf21uIr0KtNaI3OY25BtjpULXzG7PivgZzM=
+
+
+ Resources/core/icons/displayport-fill.svg
+
+ hash2
+
+ ApDuQOxGRPe6JY+npmrx/RMUr35JPdwpiIaJ9QN0S0o=
+
+
+ Resources/core/icons/displayport.svg
+
+ hash2
+
+ B7OZWB/bz/PoYffQMVtyAaVQRPj4e7RBu65qzHDi7gc=
+
+
+ Resources/core/icons/distribute-horizontal.svg
+
+ hash2
+
+ 7RkYOzjAFx+HSL6ZgdhSzzjkYy7vsH7IG3XFipJlYi0=
+
+
+ Resources/core/icons/distribute-vertical.svg
+
+ hash2
+
+ /rEJ1jbJeKE+9SB0r3JGrbAaO36o1cNXli1kEbnvbsI=
+
+
+ Resources/core/icons/door-closed-fill.svg
+
+ hash2
+
+ BLpuQoCzO6MPlYxep5dufRy/t6cPQqpS5EMWN2J5Jsk=
+
+
+ Resources/core/icons/door-closed.svg
+
+ hash2
+
+ 5Op5ODmP2MC7fTCRZBBKJcIFmFJMkF/iq2kmK4PgdUk=
+
+
+ Resources/core/icons/door-open-fill.svg
+
+ hash2
+
+ tmExnZFcwxmLFLgMBFc7xn3j9qMfaPa0rmO71bVvko4=
+
+
+ Resources/core/icons/door-open.svg
+
+ hash2
+
+ gpU+4GDAvXE4ZhAqdKJCG8oEDMhH9v9cIatDnPhexlg=
+
+
+ Resources/core/icons/dot.svg
+
+ hash2
+
+ qiBUu1GvAo7UiEs/1nu3WTDnpRUJ+WpoiVFCpbpwBmI=
+
+
+ Resources/core/icons/download.svg
+
+ hash2
+
+ wobzZ9mlK+TA4CGQ3JUJJFuu8AzmnlaSc6wpMuWoo4k=
+
+
+ Resources/core/icons/dpad-fill.svg
+
+ hash2
+
+ RJcAhrHGgpxfsUxZEAWy9jMU96tRTM/D5qlGFPO8n+w=
+
+
+ Resources/core/icons/dpad.svg
+
+ hash2
+
+ xUf/j6qD9Jlg/mYvbWHrPr5Wm8NQ/UrmbgKP1dn/FrQ=
+
+
+ Resources/core/icons/dribbble.svg
+
+ hash2
+
+ o4on7pzAB/K6NpOev8vsH0/9lzJ83YYlHAqVPZ64/lU=
+
+
+ Resources/core/icons/dropbox.svg
+
+ hash2
+
+ whv2x3eZ2Ds2JWCFd4LOU13MBcKQiQAfN7T28r0PWmc=
+
+
+ Resources/core/icons/droplet-fill.svg
+
+ hash2
+
+ 0cHfWaPtkTJ93aIjQzcCFRTSbt2CsBeQU7JOQOlWVoo=
+
+
+ Resources/core/icons/droplet-half.svg
+
+ hash2
+
+ PMHfkueJppo01gCxY+FQX0coFDPw45m1WhvpAnXYkOs=
+
+
+ Resources/core/icons/droplet.svg
+
+ hash2
+
+ 6fQ//W4FXH4/isPY3CHBaS5gckwZSAqvTmej8Xr8IQ4=
+
+
+ Resources/core/icons/duffle-fill.svg
+
+ hash2
+
+ UKBWlDNzbW+JFb7tPcpbzK4KH2sHr2h7yilExbBXQLc=
+
+
+ Resources/core/icons/duffle.svg
+
+ hash2
+
+ 2UpGa0aOR3wah9M0XNF5lsbwjGZgd9RAk10j4ZlZkhU=
+
+
+ Resources/core/icons/ear-fill.svg
+
+ hash2
+
+ BWMDkDhmqcHng2bgGunqfzoYRXKpKHvH//OFzXyaG7w=
+
+
+ Resources/core/icons/ear.svg
+
+ hash2
+
+ fSCGQVgKr1aQYHD7A/r9ZQ+rEmyQdKYqQUBsMrlE6ko=
+
+
+ Resources/core/icons/earbuds.svg
+
+ hash2
+
+ HKLGeo0nkV87BfAtVouxaBKxmj1B/4YAxou+M66ZMDk=
+
+
+ Resources/core/icons/easel-fill.svg
+
+ hash2
+
+ I29aKhApYp2wV+bezh1c/gDXSdrysybT4xi8SFBuLlM=
+
+
+ Resources/core/icons/easel.svg
+
+ hash2
+
+ h3N/6nbme6fgBQRxE0UvNJno9jOsWMxisAS6johSZN0=
+
+
+ Resources/core/icons/easel2-fill.svg
+
+ hash2
+
+ xSP4mUowFj2gONP+mLMtbBSSJbVXShkpieALN5vNzSE=
+
+
+ Resources/core/icons/easel2.svg
+
+ hash2
+
+ qaXnyq1yWXWBCx/fpNN1xPysd6YrKDNcW62JrlVZOFM=
+
+
+ Resources/core/icons/easel3-fill.svg
+
+ hash2
+
+ Uh59W9KXlgxUdTRrbHBUo6eQo2oRY5zesJPHAl01+vI=
+
+
+ Resources/core/icons/easel3.svg
+
+ hash2
+
+ kOpFtBrhGKiKeJEsq1pv219W2lCws3ugkBXfRVW5ab0=
+
+
+ Resources/core/icons/egg-fill.svg
+
+ hash2
+
+ /mLDl1dDnN8/nz65DWUTBAA8Zotb4WUA9V8LA+zS7ao=
+
+
+ Resources/core/icons/egg-fried.svg
+
+ hash2
+
+ SVuNAcprVVzYUVggR+nBXvxLuxhaHgJc0Eh7XFHGiQk=
+
+
+ Resources/core/icons/egg.svg
+
+ hash2
+
+ Dvff9ovFnLLR1PWA6pE3szlob8aswk1JfDB6mkWqkSo=
+
+
+ Resources/core/icons/eject-fill.svg
+
+ hash2
+
+ XfZ5SqSElsR/+DoKgLbnVsCpbEbzUzwbicQHA5RzHxI=
+
+
+ Resources/core/icons/eject.svg
+
+ hash2
+
+ CbNKTJF9LoSeG+Zzx1qxbss+RDey/eELZuiaWXPV44s=
+
+
+ Resources/core/icons/emoji-angry-fill.svg
+
+ hash2
+
+ TMfAgGKAf1DAPnhSCf3wCYlAhXMdqvc/Z0N/Fpal5dY=
+
+
+ Resources/core/icons/emoji-angry.svg
+
+ hash2
+
+ knpfpFZAhFwMyWdLTQTZ4x99WSO1pAI30Qh+EHvo/l0=
+
+
+ Resources/core/icons/emoji-astonished-fill.svg
+
+ hash2
+
+ qYhbLD+sRnxG+JosLxOzYaaGiY8F3jA6R7qBlMgVYXk=
+
+
+ Resources/core/icons/emoji-astonished.svg
+
+ hash2
+
+ ZDRrG7a0PcIBSGUDTXT6jnAjxYc2O4QsUsS1Ow4Gxtg=
+
+
+ Resources/core/icons/emoji-dizzy-fill.svg
+
+ hash2
+
+ 3E3DOZNa0Y5KQ5Fecqe7yhbo96yhozIv2CoK8C7VbsU=
+
+
+ Resources/core/icons/emoji-dizzy.svg
+
+ hash2
+
+ cvum8MwDhsSiCJCzcJO+U98gaVr0REoOUM6whQTp6Bg=
+
+
+ Resources/core/icons/emoji-expressionless-fill.svg
+
+ hash2
+
+ po40sct7d1rXLxoLctXVzuLFHAkDE9YxkE6JDByOvio=
+
+
+ Resources/core/icons/emoji-expressionless.svg
+
+ hash2
+
+ peFVqWS41hSjWH/p3b6YFr+Ggc4B3h5VeC1wMvVK5gI=
+
+
+ Resources/core/icons/emoji-frown-fill.svg
+
+ hash2
+
+ j/Cxt2kCShA9skAociwlFOrgi0ZMnME4a77Tk9Ei22A=
+
+
+ Resources/core/icons/emoji-frown.svg
+
+ hash2
+
+ 31jjeQeOP3tWE79QOM1PieXG/Q4kohY4Q/OmaJuGLNA=
+
+
+ Resources/core/icons/emoji-grimace-fill.svg
+
+ hash2
+
+ Ak9DHoW91qHXnoohGCEhWw6LqHuiWSyMD3c+gTWnboo=
+
+
+ Resources/core/icons/emoji-grimace.svg
+
+ hash2
+
+ IjJ5aRX7+3FSGu4f/yQkejTuxxmUiuE/f4k+8f4Il4w=
+
+
+ Resources/core/icons/emoji-grin-fill.svg
+
+ hash2
+
+ V4Jd70kpvGRw7O8XVGpezP2C6BnkeW5+Nd8UUzwFHpE=
+
+
+ Resources/core/icons/emoji-grin.svg
+
+ hash2
+
+ ybaeWJ9uLvYDDG0vRXJ0i5cUQbgoQsjKJ8St5bJ4yLI=
+
+
+ Resources/core/icons/emoji-heart-eyes-fill.svg
+
+ hash2
+
+ Z7GhS1ERwhcqnqHGEZ2BDWqfx/o9tsgjnJ44ZVMoPmc=
+
+
+ Resources/core/icons/emoji-heart-eyes.svg
+
+ hash2
+
+ dGOY3nnpfS7Riyy69yeY+eYfrh/jWlRxnWuGXy9foIk=
+
+
+ Resources/core/icons/emoji-kiss-fill.svg
+
+ hash2
+
+ SRN61ByMUjxQLzlppt/IKHoSnnU92+M/PG9+nNlq1gs=
+
+
+ Resources/core/icons/emoji-kiss.svg
+
+ hash2
+
+ mPCstvnGbLm8M20pxfif0qGQRF5fOST331wEeQi8N5g=
+
+
+ Resources/core/icons/emoji-laughing-fill.svg
+
+ hash2
+
+ iq0DGIDYYhMLPZ78bSfk1637bGMhedc2p15bIBAW6DM=
+
+
+ Resources/core/icons/emoji-laughing.svg
+
+ hash2
+
+ 1zg3oRt4SckY7ZSpJv+yFvQeKzFW3b6xKeMZzYgu7fk=
+
+
+ Resources/core/icons/emoji-neutral-fill.svg
+
+ hash2
+
+ CedD/4REFf9BzQZQrPpW1vDCOeLALYQ3MKjhSYnyLI8=
+
+
+ Resources/core/icons/emoji-neutral.svg
+
+ hash2
+
+ TlZCkG2rxyGgTWAnfsWmVN3iyVdu+gti+PbE8RO8sK8=
+
+
+ Resources/core/icons/emoji-smile-fill.svg
+
+ hash2
+
+ AlNMmaOozrqc8oK61UJMAtuXK4pvzQ1ypCQJm9v7goM=
+
+
+ Resources/core/icons/emoji-smile-upside-down-fill.svg
+
+ hash2
+
+ NTGnKNHRjRCDHDiRW9s4uipYXgWBob8QRAL9We0zVoQ=
+
+
+ Resources/core/icons/emoji-smile-upside-down.svg
+
+ hash2
+
+ BOV4gLCxZvUpt0XhIMrDRbo8SP1wGU1aZsLCPadsxIE=
+
+
+ Resources/core/icons/emoji-smile.svg
+
+ hash2
+
+ Wqgza8fCiyJohcqOHwkdLXv+4Qv2W4ztBgwwmHcYsZw=
+
+
+ Resources/core/icons/emoji-sunglasses-fill.svg
+
+ hash2
+
+ vDWgjZJHqW0b3W93QZpcr3LJAAkJJJbjlOLh0QqB8P0=
+
+
+ Resources/core/icons/emoji-sunglasses.svg
+
+ hash2
+
+ Vii3vzQ11zG9ahDQWEp4spkeMXd1DPAc3GRtROsIM2E=
+
+
+ Resources/core/icons/emoji-surprise-fill.svg
+
+ hash2
+
+ TkYKGicEOd3gHHzzhXYCu8YcrXuQqATVJFKJaV5MeVU=
+
+
+ Resources/core/icons/emoji-surprise.svg
+
+ hash2
+
+ PTlbf6uifkV+O+lzfdCiUXQRCyHxrM4q+1zLx+09y0c=
+
+
+ Resources/core/icons/emoji-tear-fill.svg
+
+ hash2
+
+ fP8quLX/LVAERevOC5PQzpNuRj6VowGV1pW+fIG9kXM=
+
+
+ Resources/core/icons/emoji-tear.svg
+
+ hash2
+
+ 7U9+/i4BldVBn8uA+sl0aJikjca8/awJzHIHYMbMoXc=
+
+
+ Resources/core/icons/emoji-wink-fill.svg
+
+ hash2
+
+ /ghMIRLDyNL6kYbXBn5nlz4AYRosUI9fULgKVogv0e4=
+
+
+ Resources/core/icons/emoji-wink.svg
+
+ hash2
+
+ sLBjjOGjYxLCfJkeuUFIbWVE6LbqPC+of83Iu3GYA4Y=
+
+
+ Resources/core/icons/envelope-arrow-down-fill.svg
+
+ hash2
+
+ +bTPf8cKapb/ulM6FJhjDiPogzP2K+ldvUKHWO+aWaA=
+
+
+ Resources/core/icons/envelope-arrow-down.svg
+
+ hash2
+
+ g2zK+6afdgB+9Xo4a84puTyat6edyKr2+L2CeW8ULu4=
+
+
+ Resources/core/icons/envelope-arrow-up-fill.svg
+
+ hash2
+
+ ozAGVBGdr+QGDPMMtMAgWLxIh3kOAE8lrQYaPz8tSV4=
+
+
+ Resources/core/icons/envelope-arrow-up.svg
+
+ hash2
+
+ sKKH1WENcqx9+A4nCA8dBqoqJCZRBtM0A5GgJbz1F7I=
+
+
+ Resources/core/icons/envelope-at-fill.svg
+
+ hash2
+
+ ZoKZhc9830tdVIiOFirFzgUWFVlFnu7bKfuOGth378s=
+
+
+ Resources/core/icons/envelope-at.svg
+
+ hash2
+
+ 1lngnbfSeyjF0na2KeTRrpaIAinuZpvW9QYIxI1zx1g=
+
+
+ Resources/core/icons/envelope-check-fill.svg
+
+ hash2
+
+ 2pMCpQLfcXBSuLY+cNdWsNIVl/HNwXQBXLsv1I0PT2U=
+
+
+ Resources/core/icons/envelope-check.svg
+
+ hash2
+
+ ZZrO6DFeTPZ5ILlmjEOX3LmFndNonOnIDEjnzzMThvk=
+
+
+ Resources/core/icons/envelope-dash-fill.svg
+
+ hash2
+
+ aX0YpuKs+KqmcLRqY/gXe98GOmR2GRjXuIh1+dU0tKM=
+
+
+ Resources/core/icons/envelope-dash.svg
+
+ hash2
+
+ Risza7JZKG1xM3wu839dKyWrb8KgVVRIXzCe/wrRbH8=
+
+
+ Resources/core/icons/envelope-exclamation-fill.svg
+
+ hash2
+
+ N6byatIGWMpahxW83zJWq7/fPCjghWLEKPlDmkYlIAk=
+
+
+ Resources/core/icons/envelope-exclamation.svg
+
+ hash2
+
+ ct98hBwqLTMZob4CxVl5qDS92ipyo7oTmOzz0Zt2nlY=
+
+
+ Resources/core/icons/envelope-fill.svg
+
+ hash2
+
+ 5O5EhlLZlPfznRIVH2HUy6HUv30Ar3VYSh9MMVcviSg=
+
+
+ Resources/core/icons/envelope-heart-fill.svg
+
+ hash2
+
+ DsY36QiYMqO//KgQTGea0RqebAsKLuYqGv098tP5pkQ=
+
+
+ Resources/core/icons/envelope-heart.svg
+
+ hash2
+
+ KuV4rFnAmUu2TKjKIFNBQabuB/vsGbnuc0Psp2m13ZA=
+
+
+ Resources/core/icons/envelope-open-fill.svg
+
+ hash2
+
+ FFaGczdXkk8B2Tu5n21tVxTt2d2gt0bwNghZnDq8IOo=
+
+
+ Resources/core/icons/envelope-open-heart-fill.svg
+
+ hash2
+
+ 0Ejp5es5qN8B2UI4zF5viDF0oaMY+tzgVXr744v8qz4=
+
+
+ Resources/core/icons/envelope-open-heart.svg
+
+ hash2
+
+ 8roFPEepYqFSmC4AYOcrF0rG8OiU7Kr0NPpGohBU9I0=
+
+
+ Resources/core/icons/envelope-open.svg
+
+ hash2
+
+ nUK8kznRAKjbGhW8CCkkyvA/4YS9sJbGxL/VQqqhXyM=
+
+
+ Resources/core/icons/envelope-paper-fill.svg
+
+ hash2
+
+ KJOtjp1D0XXTHm9H9ldsJeQp4P8cYwBzsk7aRwbCkh8=
+
+
+ Resources/core/icons/envelope-paper-heart-fill.svg
+
+ hash2
+
+ SzZQ76pB+x/2E8ADDPyC+Sak1eQ2e6bOQtY7TX/pA1I=
+
+
+ Resources/core/icons/envelope-paper-heart.svg
+
+ hash2
+
+ aJopLZSaHJOhcOYsZceq2ookzQJMMVofdPxAvZaoCSw=
+
+
+ Resources/core/icons/envelope-paper.svg
+
+ hash2
+
+ gty+RViP18QFdAO8X7PZFTPXO8AXewR8+ZdL0/0JY6c=
+
+
+ Resources/core/icons/envelope-plus-fill.svg
+
+ hash2
+
+ 3Ay5XAPKC5tfTrRQKBLs2daKAx92OrzY5v+nmavcXlg=
+
+
+ Resources/core/icons/envelope-plus.svg
+
+ hash2
+
+ HxuOcCoT4rpgNw07x3ezOpOtPUBT7mqZ3gcA8DpJSAc=
+
+
+ Resources/core/icons/envelope-slash-fill.svg
+
+ hash2
+
+ fRAGLLNde2cDauTcI/GOJmaXZGtn0kGAMbvxHUtgaVs=
+
+
+ Resources/core/icons/envelope-slash.svg
+
+ hash2
+
+ 7d6hLNjQ2rBESlohHL1hm4MR8XDpMQmT9b8+LMiUyEI=
+
+
+ Resources/core/icons/envelope-x-fill.svg
+
+ hash2
+
+ 0l0FIif/exImDklwnLykdoCw2OlO0DSrWVTIu3wUy6Q=
+
+
+ Resources/core/icons/envelope-x.svg
+
+ hash2
+
+ PH+cWSrDEYJGoeq44AL9bVi0vJbWUbWjpSNhIxLQ9eE=
+
+
+ Resources/core/icons/envelope.svg
+
+ hash2
+
+ R/Ye/j4erhWQL5jcVDKMXyEs9s6tGMUOUKxfLtgs5wY=
+
+
+ Resources/core/icons/eraser-fill.svg
+
+ hash2
+
+ 9Ssc4AlCU1VUlK/dkLbavXfQwYHrqJ9qH9exLPUX2qo=
+
+
+ Resources/core/icons/eraser.svg
+
+ hash2
+
+ AGEQN5STqdRiqqtj/Ur7TLd/GLfyL5pB0NslEJd9My8=
+
+
+ Resources/core/icons/error.svg
+
+ hash2
+
+ xYqDUkOX4vH3Vk0LHhceux5QaYPJcSw978OxGdP7yzY=
+
+
+ Resources/core/icons/escape.svg
+
+ hash2
+
+ QnAUm5k9dGTrs7egKTM3ABYflKzxvKvveoPvyIvhPLU=
+
+
+ Resources/core/icons/ethernet.svg
+
+ hash2
+
+ 9fsHShUGZiLFtNqNmgHbBLYBxABqCtFcxwLNiQSjUQ8=
+
+
+ Resources/core/icons/ev-front-fill.svg
+
+ hash2
+
+ NCtpSf7Ft1Trwb++a13PrWzi/816roe1km1zT2YVlpQ=
+
+
+ Resources/core/icons/ev-front.svg
+
+ hash2
+
+ CMxF+iPQO5omgmbTcOZdXKMdoJSmXA0nv7a97NcK9uc=
+
+
+ Resources/core/icons/ev-station-fill.svg
+
+ hash2
+
+ 1FCOPKuv7mBNzs9BauhCBq85JV+oUaSYJRRlPpHBmeo=
+
+
+ Resources/core/icons/ev-station.svg
+
+ hash2
+
+ AdexyfgNatGqmwaD2X+UFSjAg+bEJ4H18EjI127O6qw=
+
+
+ Resources/core/icons/exclamation-circle-fill.svg
+
+ hash2
+
+ D7X6e3ON1qKRu6fabqxuvMErKZboUVdQFMXhTPR7Ma0=
+
+
+ Resources/core/icons/exclamation-circle.svg
+
+ hash2
+
+ ofYmUGSJSRF8ErZsD+LBg/wF7V2j5/Dy2vfvaRF6KVg=
+
+
+ Resources/core/icons/exclamation-diamond-fill.svg
+
+ hash2
+
+ R2ArBFSO1ftnjAHFMSulxRRRrSx5Zy2T7E5MJXzJptU=
+
+
+ Resources/core/icons/exclamation-diamond.svg
+
+ hash2
+
+ DpAmcrcIlZ5KFBN4bMWy9aLgbv0svcKVgpe4VCYU45Q=
+
+
+ Resources/core/icons/exclamation-lg.svg
+
+ hash2
+
+ sdAXkaYl4gRN/wFu688E390ZHV8xeLeX4BHsJ69Io1U=
+
+
+ Resources/core/icons/exclamation-octagon-fill.svg
+
+ hash2
+
+ 3eAApzJ3nrYjjUGCl0lFFsp8u8PVi16bdSlqiMPDlfs=
+
+
+ Resources/core/icons/exclamation-octagon.svg
+
+ hash2
+
+ jFYMLxZ0C8lz9PtLUvCvwu/jiwA2cRKAmDJTHwJB5rU=
+
+
+ Resources/core/icons/exclamation-square-fill.svg
+
+ hash2
+
+ lpAphU4FAkrwP9bjaxGoMcrFTCCv1qYGrdV4Yphqf5U=
+
+
+ Resources/core/icons/exclamation-square.svg
+
+ hash2
+
+ yc4tRp0wsZMF7ST/2t6xdHsIkwjImqCwohbBO7GlXCE=
+
+
+ Resources/core/icons/exclamation-triangle-fill.svg
+
+ hash2
+
+ 5aBTvhlegy+pSta1+gLPGhs+6b+FjhQSCyX6xID+WKk=
+
+
+ Resources/core/icons/exclamation-triangle.svg
+
+ hash2
+
+ w4baJG5Td0kYV7Xw22+k8l+4KLlVzBmUh37jPJhxrgU=
+
+
+ Resources/core/icons/exclamation.svg
+
+ hash2
+
+ /z4ScYvlF1SCkM/wuHCYuNOLemoEiPXbualDk/sScng=
+
+
+ Resources/core/icons/exclude.svg
+
+ hash2
+
+ sGeF9zDquxrnXBuydW5jgVWi/nX8eTZ20W2rQz02ybs=
+
+
+ Resources/core/icons/explicit-fill.svg
+
+ hash2
+
+ 3RWy6Csrdetjo94CA94h889XR4Xldm0oJjaEYqJpyBY=
+
+
+ Resources/core/icons/explicit.svg
+
+ hash2
+
+ KJguhBP5pCgYffm49PLXnOSitesyXV+vuarKGntwHuI=
+
+
+ Resources/core/icons/exposure.svg
+
+ hash2
+
+ vHtldK9EEmgu9ZQRIwI0PU6PEHc84l3ikfsIXtmXoK0=
+
+
+ Resources/core/icons/eye-fill.svg
+
+ hash2
+
+ xJsiiDDo7i1aQraid0lBla8C77tkyCXoTOWn0QLAfWA=
+
+
+ Resources/core/icons/eye-slash-fill.svg
+
+ hash2
+
+ bvh1fZcDvC6zlyBaApQrtgVnxnFH5Kgulyu8YBOGpb8=
+
+
+ Resources/core/icons/eye-slash.svg
+
+ hash2
+
+ NO7ljo7DuDCTHbNzCxXxrow13xr4HTRiLiiCBdNo/Mg=
+
+
+ Resources/core/icons/eye.svg
+
+ hash2
+
+ xYb6iODQBiXIrzq/A9vVTY3Na8zLEB4DRwNHm5wa2qQ=
+
+
+ Resources/core/icons/eyedropper.svg
+
+ hash2
+
+ +D3/MaZBTSVBhrVLYDO56zD20j0KUTXkXRpxQb8aFOw=
+
+
+ Resources/core/icons/eyeglasses.svg
+
+ hash2
+
+ xdPWDfNwLKUYUMA5qxMAjIBRqdq0QeJnJY2hSg8/Hwg=
+
+
+ Resources/core/icons/facebook.svg
+
+ hash2
+
+ NIGhyJLUXxLFnFyKwkdpbUiO0z6g8wJ/jXNsVHvVpFY=
+
+
+ Resources/core/icons/fan.svg
+
+ hash2
+
+ IkfexssVgn4I0dOMhUKqkutdqDQmrqebu30I7WCL59I=
+
+
+ Resources/core/icons/fast-forward-btn-fill.svg
+
+ hash2
+
+ H5yh6DpWGC8/K1sslLji45G/mzHgycx6wm6ms6jxMCk=
+
+
+ Resources/core/icons/fast-forward-btn.svg
+
+ hash2
+
+ hKLSbKMS6LytpZvyfkbgk6TaclY5HdOt5Jrnh5E1Rj0=
+
+
+ Resources/core/icons/fast-forward-circle-fill.svg
+
+ hash2
+
+ V/fV2lFCsK4GkEnPPGCi53t+/AEb7+AG1u+KR0mgWs8=
+
+
+ Resources/core/icons/fast-forward-circle.svg
+
+ hash2
+
+ PDhdOdQJ0+v3hkxSO5lV6O6h8EVIxgqAR8b5/NDkgVQ=
+
+
+ Resources/core/icons/fast-forward-fill.svg
+
+ hash2
+
+ La8zbIx3z5g3vClqRNo/vBNPfOt3kQqASt8t3F9R2Yc=
+
+
+ Resources/core/icons/fast-forward.svg
+
+ hash2
+
+ kr+Tio7GA5LyjUocV/bVEY8bLj1hYFN8Et7OikOfli4=
+
+
+ Resources/core/icons/feather.svg
+
+ hash2
+
+ 7boTxvFySApzFeWEOBL8esq/dfFTV5yjWGoHfr3K1g4=
+
+
+ Resources/core/icons/feather2.svg
+
+ hash2
+
+ 1NEGT5psNU/6foF2hCd298+7U/QARgQxHXhItd8OTCE=
+
+
+ Resources/core/icons/file-arrow-down-fill.svg
+
+ hash2
+
+ 05gXQewYoKfCwX8d8QRKQF/Rc6sO7ixFKf3UozxX9R4=
+
+
+ Resources/core/icons/file-arrow-down.svg
+
+ hash2
+
+ VX+qfY/5/7q8qta4btpIOdurnHQLeG3kRDdIo8kyiR8=
+
+
+ Resources/core/icons/file-arrow-up-fill.svg
+
+ hash2
+
+ iKsgjGfchVWLP6WHV5vuxqXIMMbt80Y4h/+LHF9WKTo=
+
+
+ Resources/core/icons/file-arrow-up.svg
+
+ hash2
+
+ 2zEJQDL8iAzHfzFQA0ckvivjjAnv1ERcr8at9E1z1Ss=
+
+
+ Resources/core/icons/file-bar-graph-fill.svg
+
+ hash2
+
+ IBbYZ3dJk+UPdoYM+lEgt0XJbPcoIr6liT8zShMkzkY=
+
+
+ Resources/core/icons/file-bar-graph.svg
+
+ hash2
+
+ h6UjyFsvs5K63xF0LGRgNjEAjYUR3cfGKaCe2C8mP9s=
+
+
+ Resources/core/icons/file-binary-fill.svg
+
+ hash2
+
+ TB88xHMIoxyqqMuajGit8UAu/CzB5Bv8jAbFJj7s3wU=
+
+
+ Resources/core/icons/file-binary.svg
+
+ hash2
+
+ M+znkeIE0Kb8XRzuanU3ITUhcFhHTXJlykG+IAjI9QE=
+
+
+ Resources/core/icons/file-break-fill.svg
+
+ hash2
+
+ 7WsoUXq9dgiDItiI8rKJtUx9SAe7DEGCTF/O8zSHSU8=
+
+
+ Resources/core/icons/file-break.svg
+
+ hash2
+
+ +Wim0hjdrtqZYUYT2vic5tZv6FQNgquC3d9XYHNBoK8=
+
+
+ Resources/core/icons/file-check-fill.svg
+
+ hash2
+
+ 1VXOgHbU40i5Z70m7PvtJQl/XtKc6uM/sRsbQ9CuoQI=
+
+
+ Resources/core/icons/file-check.svg
+
+ hash2
+
+ j49dZkm58t32Ont/KZ171/94C/gSCOwMi5L5R2QfxbY=
+
+
+ Resources/core/icons/file-code-fill.svg
+
+ hash2
+
+ lbwK+OyU2xaPtPJzv0INVwcHreG72ZNgymwpdwIN0q0=
+
+
+ Resources/core/icons/file-code.svg
+
+ hash2
+
+ qrFmjGpexeCDcQtNYWfzU1L1hxubUVjZrspCJPkyC6I=
+
+
+ Resources/core/icons/file-diff-fill.svg
+
+ hash2
+
+ 3uy0ddzuoc429n0jVRMBQD4WWKzkjfNGmxSuOt92o/0=
+
+
+ Resources/core/icons/file-diff.svg
+
+ hash2
+
+ WpHsNUKNog4G0WfhxrBTz9/x+dtKr6lYwaWSK8ZfTH4=
+
+
+ Resources/core/icons/file-earmark-arrow-down-fill.svg
+
+ hash2
+
+ RM6rNZPLdBpXEUzfD/lh1fnXuNpDgWFVmvCapqfZw8g=
+
+
+ Resources/core/icons/file-earmark-arrow-down.svg
+
+ hash2
+
+ RkO4tm2Jis/MEl7/f9wkHYilUNqcd8BsOEzTqVsdq+4=
+
+
+ Resources/core/icons/file-earmark-arrow-up-fill.svg
+
+ hash2
+
+ MZdfx5m9dve0suDPRfQuyVqxCRzbL8nM8tU0W1GGrJc=
+
+
+ Resources/core/icons/file-earmark-arrow-up.svg
+
+ hash2
+
+ nlHgP0ofwMtDwbiT4N0GyDQc3iXxHEJx2Da0DMwdB04=
+
+
+ Resources/core/icons/file-earmark-bar-graph-fill.svg
+
+ hash2
+
+ y69MBaYeOEwMa1oGtm1Xm2fBI2Vg71cDT5YtePlqY1Y=
+
+
+ Resources/core/icons/file-earmark-bar-graph.svg
+
+ hash2
+
+ c1Ng4zeGNY2aAgMYaRmlGdrO5iSxDIueGR9o9NAtToo=
+
+
+ Resources/core/icons/file-earmark-binary-fill.svg
+
+ hash2
+
+ Ri2/SED30W0yUeTEhKAsa3kfQ7KGJbf+WEvDpDujU6Q=
+
+
+ Resources/core/icons/file-earmark-binary.svg
+
+ hash2
+
+ v1VjsflhceS38QFHzu3Vy4xD+PTbv3L/w1+3yMcxarY=
+
+
+ Resources/core/icons/file-earmark-break-fill.svg
+
+ hash2
+
+ 9FzJbe3pqLdzWDwSMNiPNBUL11oCR5DAfihjhCNWMZo=
+
+
+ Resources/core/icons/file-earmark-break.svg
+
+ hash2
+
+ if1Ci7ST46p2uAHd3xcFaUeGTdyNjma6ZzPT3Ri0SWM=
+
+
+ Resources/core/icons/file-earmark-check-fill.svg
+
+ hash2
+
+ 2pL0BFBRVdFNyFmKGVc4GbWRsaGWiOtted9kYG7QYvU=
+
+
+ Resources/core/icons/file-earmark-check.svg
+
+ hash2
+
+ jeJtnkuzlKZqWS8bqVXeuDyK+lwJeqc9KDaaZOom+p4=
+
+
+ Resources/core/icons/file-earmark-code-fill.svg
+
+ hash2
+
+ ZXp6UuVgvvBq2HUKICiIgnDmmoTYbz1bC78iGlxksIE=
+
+
+ Resources/core/icons/file-earmark-code.svg
+
+ hash2
+
+ HaiCfNSySrcli4jjFWigpITsmN4BoAMimrspeMl0N/g=
+
+
+ Resources/core/icons/file-earmark-diff-fill.svg
+
+ hash2
+
+ 7uAoC38J0jYPhz4Y0YiUVoJ9Y9IoAOB9LPzcdopJP+A=
+
+
+ Resources/core/icons/file-earmark-diff.svg
+
+ hash2
+
+ LIR+464in2yzhK8hH697/wtggO9Wd8eUm3ONVeGgqpQ=
+
+
+ Resources/core/icons/file-earmark-easel-fill.svg
+
+ hash2
+
+ 2sCwnBNxRQD/q+fm7/aVNKUgW/bsIYIeEvLkUYtvMO0=
+
+
+ Resources/core/icons/file-earmark-easel.svg
+
+ hash2
+
+ 1U4qJSY43s1pdFS7l5HqTIto6+ejaz1x8847isdNiWM=
+
+
+ Resources/core/icons/file-earmark-excel-fill.svg
+
+ hash2
+
+ 5o2PqI1llIVWUIVex9583AQJVgdb0Aa+fagteh5q7PE=
+
+
+ Resources/core/icons/file-earmark-excel.svg
+
+ hash2
+
+ i9I7oe2t2yXUFM1PQI4srNirCTGidkC5WLpspq2zHk0=
+
+
+ Resources/core/icons/file-earmark-fill.svg
+
+ hash2
+
+ jLI/bm/8/fb+JBwxpMcU4Znpk4qlLX8uHrm2ZdC+zqw=
+
+
+ Resources/core/icons/file-earmark-font-fill.svg
+
+ hash2
+
+ RyT9Qai4m13x3F7aS1r9t3lHtCByQGw9SMdFwZz38Iw=
+
+
+ Resources/core/icons/file-earmark-font.svg
+
+ hash2
+
+ awV/blYGJYFiraF61edAwR50LUfvbFRxpyDsSCJ1m8c=
+
+
+ Resources/core/icons/file-earmark-image-fill.svg
+
+ hash2
+
+ EBm2ycAr8C5Chbszc4xQqd1CZ1ftbL9B9iD3i2zMvcU=
+
+
+ Resources/core/icons/file-earmark-image.svg
+
+ hash2
+
+ xvkTI+AL6RcRCuwA/YKY4E1q3pxfeTgWI0VAW4L96Ho=
+
+
+ Resources/core/icons/file-earmark-lock-fill.svg
+
+ hash2
+
+ FiE914yT3ogdCY3rVlw/rrtUxwKEXGbq19CDpAhffuM=
+
+
+ Resources/core/icons/file-earmark-lock.svg
+
+ hash2
+
+ j2islIPHYRvmZCExsmtUhFek5ei18Imc+gqZLujkDaQ=
+
+
+ Resources/core/icons/file-earmark-lock2-fill.svg
+
+ hash2
+
+ I7AINXgXxBP6tmyQuLZYSPkFVSjBiw1xM8jtl5RCic8=
+
+
+ Resources/core/icons/file-earmark-lock2.svg
+
+ hash2
+
+ bzkQqdY5OUFf7InSy998TpQHjnPDRqq7KDLi7Ip818g=
+
+
+ Resources/core/icons/file-earmark-medical-fill.svg
+
+ hash2
+
+ glMVuB0AHt3d4SDDU7VXZ8TzMCSkUktRqurc61XEPCQ=
+
+
+ Resources/core/icons/file-earmark-medical.svg
+
+ hash2
+
+ RDBSjrLqdQE25Qor4fYmp6yngtivChebPB39M9qLNXA=
+
+
+ Resources/core/icons/file-earmark-minus-fill.svg
+
+ hash2
+
+ tVsqvXVEokSHKW7GacwsNkNu3/oJg2oAt3JXytwPdo0=
+
+
+ Resources/core/icons/file-earmark-minus.svg
+
+ hash2
+
+ KejDhFr0rL2qWMZpWqPc/ns0A90UitUyv9kpvUEqeBw=
+
+
+ Resources/core/icons/file-earmark-music-fill.svg
+
+ hash2
+
+ xC8jOH6+eIKn3r7ZcE4GmrE3NwSfLoG6zkqdIODgWtE=
+
+
+ Resources/core/icons/file-earmark-music.svg
+
+ hash2
+
+ LkmNApQanp88vsvxnSlYDz6o9D5vuCbUrZDuc1+7lHo=
+
+
+ Resources/core/icons/file-earmark-pdf-fill.svg
+
+ hash2
+
+ Qm2OAjDyiMCHBsmEhxbWfHtnL0FTEHdWuXsjcbFze64=
+
+
+ Resources/core/icons/file-earmark-pdf.svg
+
+ hash2
+
+ ldDxvvSyMjI3Pi4Eo67fKjX3oAVBXOp6TC8mE8if4gE=
+
+
+ Resources/core/icons/file-earmark-person-fill.svg
+
+ hash2
+
+ 0MSwQBforNwhm6tiHHbJbjiIEnQ1bxnXkbRzJjSDlws=
+
+
+ Resources/core/icons/file-earmark-person.svg
+
+ hash2
+
+ z2xp6vE3kXjF7gr0mq7XKtye0Qddb+WDSeBSOYEiB8Q=
+
+
+ Resources/core/icons/file-earmark-play-fill.svg
+
+ hash2
+
+ bha6b/wGc+gKZDT1G2LLwGgrHPqYCNBN9WxzMzUOSMU=
+
+
+ Resources/core/icons/file-earmark-play.svg
+
+ hash2
+
+ p2y/u3PhuYwJ7tV2PmhA7HebFGNZqbuDWgM7XmxKSPo=
+
+
+ Resources/core/icons/file-earmark-plus-fill.svg
+
+ hash2
+
+ tBuWOaU4+ahl8JVCD+h3+e/JJbzd09Pmki1lq9fqDW0=
+
+
+ Resources/core/icons/file-earmark-plus.svg
+
+ hash2
+
+ TE3Z+I/X4j9+pcHiElrwAztuI398OZH8jLbPPc5V4uw=
+
+
+ Resources/core/icons/file-earmark-post-fill.svg
+
+ hash2
+
+ hQCT768Wuf7sVgVbd9+mpCfS28wR7s2vZlnq5rmbKcQ=
+
+
+ Resources/core/icons/file-earmark-post.svg
+
+ hash2
+
+ 6qAn0ZBaw0hD4TKntf0IgWOmko31iv2huRHep1qxt88=
+
+
+ Resources/core/icons/file-earmark-ppt-fill.svg
+
+ hash2
+
+ J4MsZkRTBC6Tsv/OotbsnI2ghcDzjtj59NQ2DlEw9a0=
+
+
+ Resources/core/icons/file-earmark-ppt.svg
+
+ hash2
+
+ 0b3vxuf769p2DyIrq2NATQWtR7i7lGLVxS6Hp3vgtaE=
+
+
+ Resources/core/icons/file-earmark-richtext-fill.svg
+
+ hash2
+
+ xcesvXlt2zYHa87+B0UOSkgee5HvGT9REFyVgTmzJ04=
+
+
+ Resources/core/icons/file-earmark-richtext.svg
+
+ hash2
+
+ jANFSoXZH7bZHWk8DT47OCOKjHIoLjkNJlcaO03iViQ=
+
+
+ Resources/core/icons/file-earmark-ruled-fill.svg
+
+ hash2
+
+ knZ7rrp1PV0b8s1ZYII61EJsJE313Mp5lGsZ+uGT3IU=
+
+
+ Resources/core/icons/file-earmark-ruled.svg
+
+ hash2
+
+ 7a9iuPtDcLenH83/rz+InhccWIToyvwbumwJKrRgGQA=
+
+
+ Resources/core/icons/file-earmark-slides-fill.svg
+
+ hash2
+
+ LGwvK6gFLm+KdxqIEsGNdPUED3hHrnkWd5GAkHuQbK0=
+
+
+ Resources/core/icons/file-earmark-slides.svg
+
+ hash2
+
+ iRk6j33WciRHx0F9nYA+ZLS2KeOAfdy2zcPfDEF5kgc=
+
+
+ Resources/core/icons/file-earmark-spreadsheet-fill.svg
+
+ hash2
+
+ epShc0/zAy21x0aQ8Inmy7BrvzlfTKctYVrD47MrrgY=
+
+
+ Resources/core/icons/file-earmark-spreadsheet.svg
+
+ hash2
+
+ +HmDn3nje+NqYPKiAH23gq/PajcHdmoFVy3dflsD3RI=
+
+
+ Resources/core/icons/file-earmark-text-fill.svg
+
+ hash2
+
+ PJ9lZ0FiXMUh0POY0DwSCuv2A8Zhibxk5r4YpXcMmE8=
+
+
+ Resources/core/icons/file-earmark-text.svg
+
+ hash2
+
+ CTuBzLZfXJWBz9wrh1ptUyZNtEkI9jI09Ecq+MmpDTk=
+
+
+ Resources/core/icons/file-earmark-word-fill.svg
+
+ hash2
+
+ 7weQI/aFk2//xSQ9wy/W/pcMbce1MOtkFKvgCRjGCiM=
+
+
+ Resources/core/icons/file-earmark-word.svg
+
+ hash2
+
+ EyyUaET3+/WrK9FNfU7JyHjVyyPuRwh/yPNF2qhkqjo=
+
+
+ Resources/core/icons/file-earmark-x-fill.svg
+
+ hash2
+
+ 8Qi6HxcibLd1Ek7WfiZihZiJJn+G0qc8sxgt3+H6GBs=
+
+
+ Resources/core/icons/file-earmark-x.svg
+
+ hash2
+
+ f6w3nNP5/ogqkvGqA/iT53V/vtdn8/DMJU+tqhhN0Sw=
+
+
+ Resources/core/icons/file-earmark-zip-fill.svg
+
+ hash2
+
+ vBD5SxCBUy7Myy4i5lHWO0zJHPKY0QpXJSda1bqu7Ys=
+
+
+ Resources/core/icons/file-earmark-zip.svg
+
+ hash2
+
+ MEARtGmLopTjdkYBmMwt3SiM0KUHeo59uLlnjsxFrmg=
+
+
+ Resources/core/icons/file-earmark.svg
+
+ hash2
+
+ wOsZTmXsgaXo3EUoT2onyK81QiQo1dNlluwX+xHVs+M=
+
+
+ Resources/core/icons/file-easel-fill.svg
+
+ hash2
+
+ kggOE7GO/CkLsArRYmDM+EYEPKfTtVzUbRqqBkbmetE=
+
+
+ Resources/core/icons/file-easel.svg
+
+ hash2
+
+ qQTwYo0fHGRIcu4fryWxIugOiDIw5MC2C72e6qITo3w=
+
+
+ Resources/core/icons/file-excel-fill.svg
+
+ hash2
+
+ OfaoVI9Dz5648xsUx//gU2mbxLQE0R5lmjfgE6JKUu4=
+
+
+ Resources/core/icons/file-excel.svg
+
+ hash2
+
+ 3IgiCpdCkeemwM1avbzEZo2uN3IxrnPkSKESGX/DeJY=
+
+
+ Resources/core/icons/file-fill.svg
+
+ hash2
+
+ p/HDTfEEjDTkY4XU2spDKLevDMHm9cHo4yqPjjXfwWo=
+
+
+ Resources/core/icons/file-font-fill.svg
+
+ hash2
+
+ /3czDO140vJlYudt4FDRlO4LsZUAR12GVyQZSxb5q1A=
+
+
+ Resources/core/icons/file-font.svg
+
+ hash2
+
+ ophA/bZpTn0AmZr6nAMYKi09SpJsHW+xQfHmwIwNFWc=
+
+
+ Resources/core/icons/file-image-fill.svg
+
+ hash2
+
+ FgyPVQWpxHz/MRaWsIgJAS8WdT3hecx0mu/wq7d8ntk=
+
+
+ Resources/core/icons/file-image.svg
+
+ hash2
+
+ yoEQR+k5E5DTqzYcvpjgn6+b8buuscHf9V7KUOx5NBk=
+
+
+ Resources/core/icons/file-lock-fill.svg
+
+ hash2
+
+ 8+oYYgCuX2bXrFWmD26hlKr1psu27EX5mMgAD8YAv7U=
+
+
+ Resources/core/icons/file-lock.svg
+
+ hash2
+
+ BnCq00Rh7l8gXuvctwYdUKA7T6lYYylijUYTC+vICho=
+
+
+ Resources/core/icons/file-lock2-fill.svg
+
+ hash2
+
+ Tln/QnF8YeRigK3K+0k2xE2qH86HLtQusi/BCIYFiv4=
+
+
+ Resources/core/icons/file-lock2.svg
+
+ hash2
+
+ efwm+B0ke0JXMUkuFHSHHGPvuTVE893q/9sqFxpRsco=
+
+
+ Resources/core/icons/file-medical-fill.svg
+
+ hash2
+
+ dGKMzOILs6WmqCfFJ4XsH1Y6l+vZFeJgiFrV80rT41k=
+
+
+ Resources/core/icons/file-medical.svg
+
+ hash2
+
+ XYRyjYUgLGc0FkLDzbWBF84lskgyK7HS+2p3lVSFb04=
+
+
+ Resources/core/icons/file-minus-fill.svg
+
+ hash2
+
+ nbbDuYi7zb73cE5x12GL7fAdt3t16hfFK9XnhfdYWz4=
+
+
+ Resources/core/icons/file-minus.svg
+
+ hash2
+
+ usR8opOHcFNInVGvbh5E+doNOv395bRDN6KhFbKqM/g=
+
+
+ Resources/core/icons/file-music-fill.svg
+
+ hash2
+
+ Gqr8soFRyAJazC7uCJgYh0GgDi8K7J1U+34sgGLK6jY=
+
+
+ Resources/core/icons/file-music.svg
+
+ hash2
+
+ FXc1R/+FW8qUaWrDP96UbU5AnEQ/vBukIubtmLJBaRU=
+
+
+ Resources/core/icons/file-pdf-fill.svg
+
+ hash2
+
+ kjnAJbLVyKpQzt7APDWW1c96e3tYBqKeHs93JXxdjfc=
+
+
+ Resources/core/icons/file-pdf.svg
+
+ hash2
+
+ IVPn2NxFR7dr/KyxXPghK8PZUDnUxtk02CK3AfscEEU=
+
+
+ Resources/core/icons/file-person-fill.svg
+
+ hash2
+
+ /4FLrQzX6mVAAehIlEdhYxXEw8UKRY3oVdDRHzwCmmw=
+
+
+ Resources/core/icons/file-person.svg
+
+ hash2
+
+ pN/3nl9tkNzRT/MfOQizolYGhnJhV8zP74whD22wKMI=
+
+
+ Resources/core/icons/file-play-fill.svg
+
+ hash2
+
+ cKPv8G/SSGU+HDfYxFQpg67Fltkwz7nZ7Lxdav11JyA=
+
+
+ Resources/core/icons/file-play.svg
+
+ hash2
+
+ 79qIm1g0MF/i+p9cIEt6Wq5+AXOlFE/tDuaCGddJyeU=
+
+
+ Resources/core/icons/file-plus-fill.svg
+
+ hash2
+
+ 4vOE4YNulBg3EKvr3uZALSklwehuZ9mj10pTbj9Vkz8=
+
+
+ Resources/core/icons/file-plus.svg
+
+ hash2
+
+ clgpNuRyK1RV3pV+HwsIxy8XlHmrmIJ1OxRsrCga6FA=
+
+
+ Resources/core/icons/file-post-fill.svg
+
+ hash2
+
+ h5jG7jLZE61iYf8VztZtA5r9X/TRMCTdIDSUquBRwFg=
+
+
+ Resources/core/icons/file-post.svg
+
+ hash2
+
+ KwQ0uDS7F8sS2mN7aUD7lp5QJypKZXOPzRjfSRp//HQ=
+
+
+ Resources/core/icons/file-ppt-fill.svg
+
+ hash2
+
+ tf0wJozCvXAw8zS7pupjonBUh6qNVJzYTSu7kA1aGE8=
+
+
+ Resources/core/icons/file-ppt.svg
+
+ hash2
+
+ ID+33REbM8Oik3auTfQLOFjWnXoqdsiJ3HOtVNjvomY=
+
+
+ Resources/core/icons/file-richtext-fill.svg
+
+ hash2
+
+ 6ZSkOXtxDCihNOcCJiELx3wwAOZMEHGqBzxQ3HJsM2E=
+
+
+ Resources/core/icons/file-richtext.svg
+
+ hash2
+
+ brJ84ICXVpKbhoU4w2Uc4K4JN4PN79DRoPuCEQVW2pM=
+
+
+ Resources/core/icons/file-ruled-fill.svg
+
+ hash2
+
+ XbHJWm/G3Zgxpvdk+OFBM8VBK57+cBZuHo27thNCsOQ=
+
+
+ Resources/core/icons/file-ruled.svg
+
+ hash2
+
+ fj8FmkRuBDDGfST9rez12ybP6v3XwvNU7nSagyaVAJw=
+
+
+ Resources/core/icons/file-slides-fill.svg
+
+ hash2
+
+ Mxju6z8pUiF1lj+GgMbxaI2t8DiSjxlTqTjbo8HAtlE=
+
+
+ Resources/core/icons/file-slides.svg
+
+ hash2
+
+ aZ0aH+ypAqZ25xl8Em1LKJgY3qPPyS7pZz035ej2xXI=
+
+
+ Resources/core/icons/file-spreadsheet-fill.svg
+
+ hash2
+
+ eboeFUOLAvEnj5ggSX5s4lH2Rh2tVP3xuO0X5YpK0bI=
+
+
+ Resources/core/icons/file-spreadsheet.svg
+
+ hash2
+
+ Mlg6k7zLZX2EU/DKJFiOCICpiBw9DJ8t7KaZiz3Vsc4=
+
+
+ Resources/core/icons/file-text-fill.svg
+
+ hash2
+
+ QUrWIO7ia9PBl24/3MFeHL1odhT+uY0ke2hUE2cB6ZM=
+
+
+ Resources/core/icons/file-text.svg
+
+ hash2
+
+ 7AqEeCPoymZkk+HWN7WTYTy9Ye5ZdjHolBQtX95GHF8=
+
+
+ Resources/core/icons/file-word-fill.svg
+
+ hash2
+
+ aA3g6KCTTrpOWoWJxlSgy+5fhQouQ9YH/SXoPCKdgOM=
+
+
+ Resources/core/icons/file-word.svg
+
+ hash2
+
+ SMC4/P1ZAXk5vgzd5Vq3JhI0cKQc8s5ei/465oPP3u8=
+
+
+ Resources/core/icons/file-x-fill.svg
+
+ hash2
+
+ mb5lSHGtVmR5qzHL9FPzPsU7eBqNMCu1ZxhTp9hPs+U=
+
+
+ Resources/core/icons/file-x.svg
+
+ hash2
+
+ u2NQzneJU75sKJXBRD5bRaiT3opPG8pSHTScppZrBE8=
+
+
+ Resources/core/icons/file-zip-fill.svg
+
+ hash2
+
+ NUoXc3e9Pwg+jx3oY+5jHeW/xcG8EjgbQNYexn35mks=
+
+
+ Resources/core/icons/file-zip.svg
+
+ hash2
+
+ y+S54H8gKHA4Ve50I2m14FoVH421U6dsH0i6F2Vi22Q=
+
+
+ Resources/core/icons/file.svg
+
+ hash2
+
+ fC8Oyud81Tul96s/g53VsX0I/wJ6Y6gpG2FjbBnvnWo=
+
+
+ Resources/core/icons/files-alt.svg
+
+ hash2
+
+ qOtlx/XsNPhr6yorvYWq1L5Rw62kuuy58WVnd7N3Kgs=
+
+
+ Resources/core/icons/files.svg
+
+ hash2
+
+ 6qlFgaGfIZ2kOXAe5fvTEGYGKVnsocTYImAxWWBmP5c=
+
+
+ Resources/core/icons/filetype-aac.svg
+
+ hash2
+
+ 9hE4qKq7N/j09FJMxvExVNi5evGxRcr5DJVa7Wq5TPk=
+
+
+ Resources/core/icons/filetype-ai.svg
+
+ hash2
+
+ 1eRkud9GjS1ajeVXvHJJi7iY2pizaEQCQg7CyDTIDJE=
+
+
+ Resources/core/icons/filetype-bmp.svg
+
+ hash2
+
+ 3UChRpElUyLZUxXf8wrIL0iMVZ6InJyF0SUfHBe4FB8=
+
+
+ Resources/core/icons/filetype-cs.svg
+
+ hash2
+
+ naOxZSgP7Bc6cKajHBuN02ycRxdrfYikKj/ccsZSLKk=
+
+
+ Resources/core/icons/filetype-css.svg
+
+ hash2
+
+ UlpFjkfKQsCy+eTuN2VPyLplNX28ynEQsui0VZvtqRM=
+
+
+ Resources/core/icons/filetype-csv.svg
+
+ hash2
+
+ l01DxPDiEOPdTwOcdHFv1EIYy+a6N3w4RuSdgxoDnr0=
+
+
+ Resources/core/icons/filetype-doc.svg
+
+ hash2
+
+ bo03S5JZDCCQEZziqr17S7NQc6BzSBrws/qqN0uNL2I=
+
+
+ Resources/core/icons/filetype-docx.svg
+
+ hash2
+
+ iwACzJF3Ati4Lx1QIfNXbxkRMcMgA2jGjRaiSjyqSgw=
+
+
+ Resources/core/icons/filetype-exe.svg
+
+ hash2
+
+ uMl/65shfsFCRvqEdCXgzVr0Lg6KR9423QFv3M8GTNQ=
+
+
+ Resources/core/icons/filetype-gif.svg
+
+ hash2
+
+ n36vEASCabGawux6Il6fJx8BuImZ0ApR5i7AQKPbblY=
+
+
+ Resources/core/icons/filetype-heic.svg
+
+ hash2
+
+ vLLiZtHo9GVdVko/liGdap1gE3SqIUwOoP1s+w4yD8g=
+
+
+ Resources/core/icons/filetype-html.svg
+
+ hash2
+
+ uFOK4q9sU8L0byM2hdBuplz4TvhvoxsDSZGBNfF4Grg=
+
+
+ Resources/core/icons/filetype-java.svg
+
+ hash2
+
+ 142uiMkoGBm3mLkAM+EjoTmsXwGO3GGcwovwT6YxOr8=
+
+
+ Resources/core/icons/filetype-jpg.svg
+
+ hash2
+
+ ysCjyONUbT1LWozkvMoFmxs6sG4BkT0A7J8ZMHsHDyc=
+
+
+ Resources/core/icons/filetype-js.svg
+
+ hash2
+
+ QeGeaIdKBxNUPtJF3rern9z3F7JTqUwTXqiqtHdKEoo=
+
+
+ Resources/core/icons/filetype-json.svg
+
+ hash2
+
+ hYXSafhWDlAi7x84Mgq88/WnSLt+bHVm3OqFaw1R1Vo=
+
+
+ Resources/core/icons/filetype-jsx.svg
+
+ hash2
+
+ KVVxTKGJmXfGXMp/ZFcuYjF/FPnXQPLqiBWF+3tlM5M=
+
+
+ Resources/core/icons/filetype-key.svg
+
+ hash2
+
+ AOg/VkaO3HtQ9SEP9B3sCyWUQTBiR50Q99ZcvC0Erps=
+
+
+ Resources/core/icons/filetype-m4p.svg
+
+ hash2
+
+ fMFM5y+HVsDUXNzFWFOG6OCrsQ1wh5NJjl6Z3chU5fE=
+
+
+ Resources/core/icons/filetype-md.svg
+
+ hash2
+
+ No9o3/yoiH2LweFFyznu7WtCYNdfzpaT4+x6Iy4l2mY=
+
+
+ Resources/core/icons/filetype-mdx.svg
+
+ hash2
+
+ hMuULM1oFS6HxK20DKcK/rNNA58/szJf5gcXjfPZlsg=
+
+
+ Resources/core/icons/filetype-mov.svg
+
+ hash2
+
+ pN4wTAiVW688IbGZaIe7mMGYMjIMuCXRPGw13oy9Qg0=
+
+
+ Resources/core/icons/filetype-mp3.svg
+
+ hash2
+
+ pZ9OmRh6ERUvdocKyhHzkrkD8vJrBKjJBUnALgxfKJ8=
+
+
+ Resources/core/icons/filetype-mp4.svg
+
+ hash2
+
+ 9vxglaVEF+m7EhxX1JMlzYr62W6sKvbDh6O74kc3xZc=
+
+
+ Resources/core/icons/filetype-otf.svg
+
+ hash2
+
+ /ITc7PHSRuaHtxyTkWNNdOPxspeyW6xeJ98XrJWXxOg=
+
+
+ Resources/core/icons/filetype-pdf.svg
+
+ hash2
+
+ xxv4M9046iOIk6m8nWG6obBGIiHZRThS/XC/zuzhRlA=
+
+
+ Resources/core/icons/filetype-php.svg
+
+ hash2
+
+ ccV/ewXUbO+aUt4KGL0ZDSQiGt5V5TV4HPXwKb7AqpU=
+
+
+ Resources/core/icons/filetype-png.svg
+
+ hash2
+
+ BCd1obG6HlBZnZO2NH2RJBDbVjpnsrh8s+Yf71kHTKE=
+
+
+ Resources/core/icons/filetype-ppt.svg
+
+ hash2
+
+ iVb77FqHy17QlxLSMISPX/tVXwwSkZrIdf4fF+0PT+8=
+
+
+ Resources/core/icons/filetype-pptx.svg
+
+ hash2
+
+ C764vK/HANUgSRuSFmEFdPiqE74CfmxnqkSkt3s8O7I=
+
+
+ Resources/core/icons/filetype-psd.svg
+
+ hash2
+
+ PDK6YxNVfNKXczuMgV6amUJnrVxRHQW9+TZj4l0E240=
+
+
+ Resources/core/icons/filetype-py.svg
+
+ hash2
+
+ 2z+uE3+A+WtYHuAFotCCcKOD+/bW0XpcSxpiGogOOug=
+
+
+ Resources/core/icons/filetype-raw.svg
+
+ hash2
+
+ tPaXkh24GE3DzlkvlnovDjBGL0tqibx1FIWtKsS1jGw=
+
+
+ Resources/core/icons/filetype-rb.svg
+
+ hash2
+
+ US3H1Ng4dvididSJ1JpT1cLCJs0/Ys7UD4uSEoDJ7ZY=
+
+
+ Resources/core/icons/filetype-sass.svg
+
+ hash2
+
+ RqDrdYPNu2OPbrLSkEHbtwHa12tCVTwH501rs5WosCQ=
+
+
+ Resources/core/icons/filetype-scss.svg
+
+ hash2
+
+ 4U3Ncg9xwLBrfSCXQipAPDMblxmE09830xg3l6b5P40=
+
+
+ Resources/core/icons/filetype-sh.svg
+
+ hash2
+
+ 9jvZYYwd+2FsWzfrjB3beOwNsTfKyth1R+IeRjVcsuI=
+
+
+ Resources/core/icons/filetype-sql.svg
+
+ hash2
+
+ HpEDeLKWV9IW1MSxpyZeZdnX4c9LG9AF7/kBvHOUJNQ=
+
+
+ Resources/core/icons/filetype-svg.svg
+
+ hash2
+
+ 9i4SvtV4CfvT3gLPZuCfP6I6KQpDmUB76LKztW/BKtw=
+
+
+ Resources/core/icons/filetype-tiff.svg
+
+ hash2
+
+ guQ+MLiJEwwZ/zjTkbHDQiYxcyGpymJKlPp46mLEegk=
+
+
+ Resources/core/icons/filetype-tsx.svg
+
+ hash2
+
+ krgSZr8OJRx9e0iNS/InxzZ7N82JugCFWC0yY3n3r4A=
+
+
+ Resources/core/icons/filetype-ttf.svg
+
+ hash2
+
+ Vcu3obCGcf1bbaCd4UoUDIIJH0844lbzWK+AeacLikE=
+
+
+ Resources/core/icons/filetype-txt.svg
+
+ hash2
+
+ TcQlWJPzwrgLURcfinmZdgUqfZoPs8yi56W+0LUH25w=
+
+
+ Resources/core/icons/filetype-wav.svg
+
+ hash2
+
+ R6Be4mK43qKhkL/8zNKx659jeBYqC1G20t+78xI+uNs=
+
+
+ Resources/core/icons/filetype-woff.svg
+
+ hash2
+
+ H5C4Zd3+kXwnLzWjwI8hF83NxEuJGxXbwtEY22pDYaE=
+
+
+ Resources/core/icons/filetype-xls.svg
+
+ hash2
+
+ PNJD641UeSR4JAVBjZIEM9d6CgWaUJStDmdrVHUVs3s=
+
+
+ Resources/core/icons/filetype-xlsx.svg
+
+ hash2
+
+ AUjlIxX0WmE/GHQygy1KT4iEFEg+26h3T93t2Enbg9k=
+
+
+ Resources/core/icons/filetype-xml.svg
+
+ hash2
+
+ TuEa87i6yo7E+g/l20ncw9Y1UidbpDEKy6vISeHor14=
+
+
+ Resources/core/icons/filetype-yml.svg
+
+ hash2
+
+ cCeKSBbi7i9Cz4OsbhEA6Apq+SvrX+hzMfHgRYr6m5g=
+
+
+ Resources/core/icons/film.svg
+
+ hash2
+
+ qh04XMqn1q3wBS/y8SCko1zM2S93pTNPbxMpc+BAgbY=
+
+
+ Resources/core/icons/filter-circle-fill.svg
+
+ hash2
+
+ NPgGSBuAILg/J62rxhNWfmcufmsOnsMSN+Al/r8M+Vs=
+
+
+ Resources/core/icons/filter-circle.svg
+
+ hash2
+
+ zbDBRKCMLUBbM4WYmoYp8Q5NL9oNLonbaIsDpSvlxzM=
+
+
+ Resources/core/icons/filter-left.svg
+
+ hash2
+
+ Q0xP0WXtgx6TaGsKcR2ghNEK7ZK9xjqJ4mGQ4W/oNcs=
+
+
+ Resources/core/icons/filter-right.svg
+
+ hash2
+
+ gbQIEoX0jFbIgI3gf5bJGeZIMbLF8D9xr5mZQ2136Rg=
+
+
+ Resources/core/icons/filter-square-fill.svg
+
+ hash2
+
+ fi3TpGoSLm2EzkhJd1bEhBv2vNUHc5aO7fsEuWmdAGo=
+
+
+ Resources/core/icons/filter-square.svg
+
+ hash2
+
+ AZwUAZ/8FRAFGZXI3GIAiNwu/tIcbBBbUis4aS0GV3s=
+
+
+ Resources/core/icons/filter.svg
+
+ hash2
+
+ 6o4BibjA2AgxcrB/DgvGi4r0fzmvO+jdjPqEfTT2V3E=
+
+
+ Resources/core/icons/fingerprint.svg
+
+ hash2
+
+ v6qr3xjU6uImGBjWBhTtVwWMw/uHeH6raYTsjxPlv/o=
+
+
+ Resources/core/icons/fire.svg
+
+ hash2
+
+ XJuKN2Ws7bjefBmj0wYDMa/hGJOmgQillBm0nqKOh3I=
+
+
+ Resources/core/icons/flag-fill.svg
+
+ hash2
+
+ 4CSwAh4CKZ3rRSFUn08EVoy8Z+zLMsWnVsXFMuh77K8=
+
+
+ Resources/core/icons/flag.svg
+
+ hash2
+
+ gAWPq2YOmFX2N2x3st0f00PBQ5h49N8bt8Xaj6jEBRM=
+
+
+ Resources/core/icons/flask-fill.svg
+
+ hash2
+
+ 9rAe5ZFWPNH5D3yBEIVRTeGQOUGSeAyNmJR79pArMGU=
+
+
+ Resources/core/icons/flask-florence-fill.svg
+
+ hash2
+
+ ldCxOInT7KWKWR2H37ac4yYhNqEeKBGmxXTM2z3x3R4=
+
+
+ Resources/core/icons/flask-florence.svg
+
+ hash2
+
+ 5f7A+46i1GI02/bIX1RyolH+gyFrNobJLMpMi2MAd1w=
+
+
+ Resources/core/icons/flask.svg
+
+ hash2
+
+ ercH0VkNqjfOyEYUZV0QJ+YQMDHgFMHApXtY9dtZfIw=
+
+
+ Resources/core/icons/floppy-fill.svg
+
+ hash2
+
+ f8LTuRfqdgO1TQVjMcZr2GR79wVySNHQQw3SbmSeFXs=
+
+
+ Resources/core/icons/floppy.svg
+
+ hash2
+
+ ImTaMu8sW6s93YgjS32oKiejg/GPHLp643T2u8z+PRg=
+
+
+ Resources/core/icons/floppy2-fill.svg
+
+ hash2
+
+ pOu+TLtOWpylN9gxG9GoePO6wxJfclcEVD+2Ys9LscQ=
+
+
+ Resources/core/icons/floppy2.svg
+
+ hash2
+
+ Zcn82QBWwzGyjyFtnDCbK6kTGEtYYKKu0QpIigrAXD4=
+
+
+ Resources/core/icons/flower1.svg
+
+ hash2
+
+ Qnu4OowEfm9xnouZxtNjg99UO7zUS5m2GtbJRBlcW7w=
+
+
+ Resources/core/icons/flower2.svg
+
+ hash2
+
+ LVLtJJF0cAzf64SXzwyr8NNp+iGwUvrteejsdVrcprE=
+
+
+ Resources/core/icons/flower3.svg
+
+ hash2
+
+ DCmbHLnZePBMPV8K2F1fAkWlS56v92lUfv/dYOjY1jc=
+
+
+ Resources/core/icons/folder-check.svg
+
+ hash2
+
+ c+/HDKCY+iHuqgkfSattKgb4d1l36LwPO6d/gtyqOwU=
+
+
+ Resources/core/icons/folder-fill.svg
+
+ hash2
+
+ irz59uU3GpiVTj3IVbYYLWbBNHiOxCx+B3VdG+bP50c=
+
+
+ Resources/core/icons/folder-minus.svg
+
+ hash2
+
+ vg82TN2gRiKUdqOhc1th6ssrabt4nGIWUsfOlbTBStI=
+
+
+ Resources/core/icons/folder-plus.svg
+
+ hash2
+
+ TR/C3qFvOgEWL6GGd1HEmJU99gk8vJ54NgYne6J3beM=
+
+
+ Resources/core/icons/folder-symlink-fill.svg
+
+ hash2
+
+ jNV6vLEPItLsCwSYuiC9E2foTcvm9Q0QVcf1oUC1ZdU=
+
+
+ Resources/core/icons/folder-symlink.svg
+
+ hash2
+
+ F/qOB35BXt87iPx+wXj++fdsU6utF0iF66x8WIWN0KQ=
+
+
+ Resources/core/icons/folder-x.svg
+
+ hash2
+
+ kDbF80R6ZU6zIZSeWlcoHAhXUuA+zTUCCtvNEzYs5KI=
+
+
+ Resources/core/icons/folder.svg
+
+ hash2
+
+ 3jJd1wsBDaimnTZ1yIdHVDRUK9iHmVxZUpQ2KDg7b70=
+
+
+ Resources/core/icons/folder2-open.svg
+
+ hash2
+
+ a39CO6UFoDSR5sahUZ2gF2zb6KiZ9UPenNQOjDibbjs=
+
+
+ Resources/core/icons/folder2.svg
+
+ hash2
+
+ s5hk18oTeFH7Ymnk7BMj5uQYN0XJ6W0iMNAYwxX1R4g=
+
+
+ Resources/core/icons/fonts.svg
+
+ hash2
+
+ vzzqjbbzslcwZ8Vrov/ljVXl1L741JMHRBC/F1VsEuI=
+
+
+ Resources/core/icons/fonts/bootstrap-icons.woff
+
+ hash2
+
+ 9VUTt7WRy4SjuH/w406iTUgx1v7cIuVLkRymS1tUShU=
+
+
+ Resources/core/icons/fonts/bootstrap-icons.woff2
+
+ hash2
+
+ bHVxA2ShylYEJncW9tKJl7JjGf2weM8R4LQqtm/y6mE=
+
+
+ Resources/core/icons/fork-knife.svg
+
+ hash2
+
+ p5UzTkZzIGZ0DXCuadeXUEvO8TZBsoK6KtYPYj3C/KQ=
+
+
+ Resources/core/icons/forward-fill.svg
+
+ hash2
+
+ MXo1+LYubSwK2RUYUPyFge58ai/YdziYl1ZUZj3mN6w=
+
+
+ Resources/core/icons/forward.svg
+
+ hash2
+
+ fYqrvyOaX87OOglicsQVLXGZcg7AzpPf/HGcEjQiYmE=
+
+
+ Resources/core/icons/front.svg
+
+ hash2
+
+ 2hOF/22lkuvrv2kD53anGJo+dmonk0jMlXrnT5lsKI4=
+
+
+ Resources/core/icons/fuel-pump-diesel-fill.svg
+
+ hash2
+
+ KfwVguFx8ewZ1gmI1jBsQtXA2Qtye2ypQ5YBgdI1+Eg=
+
+
+ Resources/core/icons/fuel-pump-diesel.svg
+
+ hash2
+
+ i2Vq4X1x5/zb9WXb9giOHVVfFPx9DcM+2Ev0lwlN/xs=
+
+
+ Resources/core/icons/fuel-pump-fill.svg
+
+ hash2
+
+ wUyFPOtoXiRqCW4v63igQSJ1wtez6R+5p0FnRdR1aXg=
+
+
+ Resources/core/icons/fuel-pump.svg
+
+ hash2
+
+ CfVe0RB/eEpDgvxuf0EaiHC5rslmKSWgdMQttLdFvdA=
+
+
+ Resources/core/icons/fullscreen-exit.svg
+
+ hash2
+
+ IDLuBASwTBnVMy8dqQrTxzqNy7omPlRtDX292Fw8K7A=
+
+
+ Resources/core/icons/fullscreen.svg
+
+ hash2
+
+ Ei8hV0hUZVwH1hlk5zRJ/TdbfHAlIaxVjHd7LLnSdBs=
+
+
+ Resources/core/icons/funnel-fill.svg
+
+ hash2
+
+ IWJOQBj1UiGbrfJjG9POSYO/8puWsov0XlfMTnFTimk=
+
+
+ Resources/core/icons/funnel.svg
+
+ hash2
+
+ HH2Xxtj+vW+gKpRdY6Y+2frV/xFl+mKAIZj28f0xOTU=
+
+
+ Resources/core/icons/gear-fill.svg
+
+ hash2
+
+ BMktqBs/bjxtN07+JJo+IDmx8GLGVzJj/hRaQDYTPXI=
+
+
+ Resources/core/icons/gear-wide-connected.svg
+
+ hash2
+
+ KvpY3ZhyHZcviQ+WJl43BLFX2E0lwhVreH/O08EaSrA=
+
+
+ Resources/core/icons/gear-wide.svg
+
+ hash2
+
+ 3xS0KLA33fvGnsyrJqM+VJqe6SIjzWgmrlsj+Kwc4y0=
+
+
+ Resources/core/icons/gear.svg
+
+ hash2
+
+ wQs7ADd1lzwZlm8OdKpW2OYEist80woloWurIkocJ3U=
+
+
+ Resources/core/icons/gem.svg
+
+ hash2
+
+ zRlTdO7KlXRTI8Q0AHTrsrLFHiUG66cjhTXM2wVz2HM=
+
+
+ Resources/core/icons/gender-ambiguous.svg
+
+ hash2
+
+ SDCH0KyVura1c8eC8k0vR2eKn1N/tCzHdaiRHStuiMg=
+
+
+ Resources/core/icons/gender-female.svg
+
+ hash2
+
+ F5gxRSTidofKAqgzfF+HNig2KmW0u2pDR7qckK1y3vE=
+
+
+ Resources/core/icons/gender-male.svg
+
+ hash2
+
+ lNgmUyqGwL3SJOoXEyvQLk++9HSWTQVYJcL8ZmI65Y0=
+
+
+ Resources/core/icons/gender-neuter.svg
+
+ hash2
+
+ mresbCvm7a9LSgz9VgZZ2qg6Rdu72kRMSjP6hOoaheA=
+
+
+ Resources/core/icons/gender-trans.svg
+
+ hash2
+
+ 2vRiNkJtxM8fTBRWzhn3TEyI3GMLjXzq2pGCR6KUGTQ=
+
+
+ Resources/core/icons/geo-alt-fill.svg
+
+ hash2
+
+ 7ZTwX8gVYVr2Wyv5zkQkgsODCj4AhYxUyYaeP7l16vA=
+
+
+ Resources/core/icons/geo-alt.svg
+
+ hash2
+
+ WTUdWY1foZonXZ5yFH+Cpn+loukrhcolRniBTi3iAWs=
+
+
+ Resources/core/icons/geo-fill.svg
+
+ hash2
+
+ GO3uZUJ2Kwf121UTskRWIAYXxbtTYi+GY+fYxabKkZY=
+
+
+ Resources/core/icons/geo.svg
+
+ hash2
+
+ kPuJtf+PSuWsOMQVOSZZpgSTQjYC+0mDqXZxWO5vBls=
+
+
+ Resources/core/icons/gift-fill.svg
+
+ hash2
+
+ apucXcild0oGCsQtY4tpfy70EqKCltxvcO6pS1HuikU=
+
+
+ Resources/core/icons/gift.svg
+
+ hash2
+
+ ADy6iMsW7S/U+S8xwojf0fsxBYPoGxCNZ6n2YRKcCfI=
+
+
+ Resources/core/icons/git.svg
+
+ hash2
+
+ gUoZJ/n8//4N9eDW/myXDb/n3Q87RsUfekvjQKn5I2g=
+
+
+ Resources/core/icons/github.svg
+
+ hash2
+
+ PcEmZ/DECtEWhs2RPEKFF9oj3VJg8qBlUMcRgapbsq8=
+
+
+ Resources/core/icons/gitlab.svg
+
+ hash2
+
+ Ry1m3LVLKBaoazFauSxCl/4VPMg8BjnGoSYxip20gKU=
+
+
+ Resources/core/icons/globe-americas-fill.svg
+
+ hash2
+
+ xmA2jJLYbSO15Vj/veGosEvixDvvLJynMfn7pUplKck=
+
+
+ Resources/core/icons/globe-americas.svg
+
+ hash2
+
+ MNfz+vo3B7BTcXXlLw5Y0h6TdctkYXnEItaYtjkrT08=
+
+
+ Resources/core/icons/globe-asia-australia-fill.svg
+
+ hash2
+
+ UCA/2XdfmlR2GNgkjNGNpwpCFWIBknLadk29IYJsJP4=
+
+
+ Resources/core/icons/globe-asia-australia.svg
+
+ hash2
+
+ sKkwUvJOUwfy9jkPS3MnudUKdShqcpcR+h4JN8Z0v8E=
+
+
+ Resources/core/icons/globe-central-south-asia-fill.svg
+
+ hash2
+
+ h5BFSyks+Ilc/BbNBwsCtgDsWH70i86uKSQjCTIAATE=
+
+
+ Resources/core/icons/globe-central-south-asia.svg
+
+ hash2
+
+ 7+/Jt1pYEgb85FVAXyUtRVpBLxSZu1BW/lock2EQAqc=
+
+
+ Resources/core/icons/globe-europe-africa-fill.svg
+
+ hash2
+
+ fvtJSzjC3BoJGic9zhPvqjNUXwQ2dK6tVWheXSU8mww=
+
+
+ Resources/core/icons/globe-europe-africa.svg
+
+ hash2
+
+ c/haZi/268nE+TtZ0X0vWNBG3bWmmS6yuly4yGdFR7I=
+
+
+ Resources/core/icons/globe.svg
+
+ hash2
+
+ IdVjPaBZoNwzWvK6Z8ER7j/P3IK3DH3kl/d3CID+1Mg=
+
+
+ Resources/core/icons/globe2.svg
+
+ hash2
+
+ rb1DjZCgVcya6Yo0Y0tmGpkJuVVyNp5LMQYHNYK3T5c=
+
+
+ Resources/core/icons/google-play.svg
+
+ hash2
+
+ DG1stdf18PHTl8G4db6WAcd6DkxwEubzoVHjL8EJeI8=
+
+
+ Resources/core/icons/google.svg
+
+ hash2
+
+ 35bZd1Q7KJIWcPHVBuZlt9lslos+SkVYd/aG07W5MWc=
+
+
+ Resources/core/icons/gpu-card.svg
+
+ hash2
+
+ 3SGx1yn1MXM792JW+/iTakBJCcx4C3qUOOWhU5t0g84=
+
+
+ Resources/core/icons/graph-down-arrow.svg
+
+ hash2
+
+ 1sU9O0f6Kp993w9cNSKK1PF6hE5BUOwtb1T+yD8hla0=
+
+
+ Resources/core/icons/graph-down.svg
+
+ hash2
+
+ pXQMOU2I0fXE6PajMtvIhh/7Y/3tin70wQFDk/ktZzs=
+
+
+ Resources/core/icons/graph-up-arrow.svg
+
+ hash2
+
+ TTOmJua1YnGkleJ/FgMQjgHmupqFubLAMEpvczLPguc=
+
+
+ Resources/core/icons/graph-up.svg
+
+ hash2
+
+ XRP7pohJ0AqPkMp6R5R3e+ZfzzaHX9VCmaXa5q+Fw3g=
+
+
+ Resources/core/icons/grid-1x2-fill.svg
+
+ hash2
+
+ yQ7vVqGvaFWw0LmvdhCizMPRsdBqk9Y26JCvM3JMFME=
+
+
+ Resources/core/icons/grid-1x2.svg
+
+ hash2
+
+ CM1mgJBO7A5cIoVbSeUi+zHd3dDfR0dBnioY5uI8BlA=
+
+
+ Resources/core/icons/grid-3x2-gap-fill.svg
+
+ hash2
+
+ +W57fhGqMLWZ/4uTSf7d5H4k3PsBuvb6C7eVnqfLz0w=
+
+
+ Resources/core/icons/grid-3x2-gap.svg
+
+ hash2
+
+ BCSIkij1Fwh/wvsETF/Ieyx2Edh5vW+ocfLG7J4SIys=
+
+
+ Resources/core/icons/grid-3x2.svg
+
+ hash2
+
+ IYl/lrgCUHddl5SEVbpAfHQ3siOkDNHfJKehi+gfMuk=
+
+
+ Resources/core/icons/grid-3x3-gap-fill.svg
+
+ hash2
+
+ TbpvBSP+PQNHHjMPdL/RfLg6/FpT4bkrGDthn5oUQEs=
+
+
+ Resources/core/icons/grid-3x3-gap.svg
+
+ hash2
+
+ l2IfsFPRCARn6rMWl5TILsouYNrx1/Le+AkuPBS5Ack=
+
+
+ Resources/core/icons/grid-3x3.svg
+
+ hash2
+
+ toPdQcTYPegEPSo5yG6yORJz5+SlrOjEB6d5D9z2UGc=
+
+
+ Resources/core/icons/grid-fill.svg
+
+ hash2
+
+ i3welr3h3v00dli4ZuZral37z+PK3kazbWddQy+Pu70=
+
+
+ Resources/core/icons/grid.svg
+
+ hash2
+
+ goW3c6L/HNJZLEqO0K4yEME8SAWjxUoE0Y1zsWIx0Lg=
+
+
+ Resources/core/icons/grip-horizontal.svg
+
+ hash2
+
+ aze7Puw0ZN0hmO25azhIPmeDG0VntlDpzQsq7SrLKdc=
+
+
+ Resources/core/icons/grip-vertical.svg
+
+ hash2
+
+ 3+N9MLVIRKnkMIdkkCgPwrpRMjT5oJEjw7Yrw6DE2e8=
+
+
+ Resources/core/icons/h-circle-fill.svg
+
+ hash2
+
+ 3Ws8LMsYsaTePyWdY4YBtktqkVk4KveUB5G2PzMmsig=
+
+
+ Resources/core/icons/h-circle.svg
+
+ hash2
+
+ JUMygW8mHpS8JkvYKldICMoUz7JJVGxaEdlqszJpjFM=
+
+
+ Resources/core/icons/h-square-fill.svg
+
+ hash2
+
+ gh/OfMcf85p0WPOIxp54ZvhyuhbWeJgPFFP9LTCdJUo=
+
+
+ Resources/core/icons/h-square.svg
+
+ hash2
+
+ c5YySf4/at8RDMtfvU0nnQtLHYQDnFU7U7fuhj6H+Hs=
+
+
+ Resources/core/icons/hammer.svg
+
+ hash2
+
+ y11xjOeMBofAZnDo3pREkceTg8aHkkn7nBCpNn8PYjA=
+
+
+ Resources/core/icons/hand-index-fill.svg
+
+ hash2
+
+ wA2+I9IHaXLfyJqFXAOH7K8lIys6GFkkiE6AivEJqsc=
+
+
+ Resources/core/icons/hand-index-thumb-fill.svg
+
+ hash2
+
+ Kdz+6frZyeGS7YokCoh9zGjyfovVyIjrNL+YEkEjwpE=
+
+
+ Resources/core/icons/hand-index-thumb.svg
+
+ hash2
+
+ KTubhuSLz1Exkjo+j56goLjK50d4WQ+GzPoOGuNUPjQ=
+
+
+ Resources/core/icons/hand-index.svg
+
+ hash2
+
+ Hah6Yh87cpeP/M/O9iD1Ng+QEDvHyKJT29BDQkbDsrU=
+
+
+ Resources/core/icons/hand-thumbs-down-fill.svg
+
+ hash2
+
+ Zbx38GDO5pr/XWqFtP09IY727Jdy3qmhBrKiJBH2Od4=
+
+
+ Resources/core/icons/hand-thumbs-down.svg
+
+ hash2
+
+ NBWw/3ZGyHQ4oIa4Wafy4hkxFvCxbppBAV6hhJtREQg=
+
+
+ Resources/core/icons/hand-thumbs-up-fill.svg
+
+ hash2
+
+ SEFjvWmgd4qYOCjNj0ifGwptFGkE4a9i2HiCMcv+vxQ=
+
+
+ Resources/core/icons/hand-thumbs-up.svg
+
+ hash2
+
+ pS2yrvJs+7uNCmEFDtdVm7Wv/aCJPlm5me9w2+SMRco=
+
+
+ Resources/core/icons/handbag-fill.svg
+
+ hash2
+
+ pN2Q6fuJ2gplR537jkqHuql9pILukiLS/tQCT1v4dQQ=
+
+
+ Resources/core/icons/handbag.svg
+
+ hash2
+
+ hJrJcxC2NC1js+yzDSJJbZcmwUe+v51oyjNw6Pyhchc=
+
+
+ Resources/core/icons/hash.svg
+
+ hash2
+
+ uL62FMV1AoRhdtz1I2fZZBLSS8ly8S4IJfkjUsEK0rc=
+
+
+ Resources/core/icons/hdd-fill.svg
+
+ hash2
+
+ TyN1PfZKtkJxdfpam8HV0wqrVx0m8jnDJNfej7QVtOk=
+
+
+ Resources/core/icons/hdd-network-fill.svg
+
+ hash2
+
+ Vcchch3oIIjoockifZn9YWl6dpf57nTXSLuGZVkc6SY=
+
+
+ Resources/core/icons/hdd-network.svg
+
+ hash2
+
+ StHn2ETRgA3l9U618ttH/8vj+DTGUuEjNtMZ7vWEJbg=
+
+
+ Resources/core/icons/hdd-rack-fill.svg
+
+ hash2
+
+ Ql3K4QqJw6IrAf0/HNS3sGymHDujfy/CaPLF5PhAJ4E=
+
+
+ Resources/core/icons/hdd-rack.svg
+
+ hash2
+
+ HKHIdPw7HkrRi5nhCsY4s8QpBOBI8XOkkKFBR6cLccY=
+
+
+ Resources/core/icons/hdd-stack-fill.svg
+
+ hash2
+
+ rwsOUsvCDdFj5xJR1hxEEl0SGFmoR9ypqSnR5uU+LtM=
+
+
+ Resources/core/icons/hdd-stack.svg
+
+ hash2
+
+ ZKZuwAfdy2OmOokmoXqDyB/u5H6UiRTA7GQNIdJUztM=
+
+
+ Resources/core/icons/hdd.svg
+
+ hash2
+
+ 8fnjStV+HalRROXBR8BYX9MNpTc/z/XioAYTplQQSq8=
+
+
+ Resources/core/icons/hdmi-fill.svg
+
+ hash2
+
+ lIge+5G1n1MNKVJO+p0kiYhUkQaRdkkkzTDTPY4a0L0=
+
+
+ Resources/core/icons/hdmi.svg
+
+ hash2
+
+ u0ntsDGlvuUSncYYhywu4glBgmFRS3ZdTc+7Lp5/YGU=
+
+
+ Resources/core/icons/headphones.svg
+
+ hash2
+
+ 4ThW6HnnXs2G/s7x/tdjxL6wNmGRxNtVkJXD63QCD+0=
+
+
+ Resources/core/icons/headset-vr.svg
+
+ hash2
+
+ 8b06dGWNtQxjkhFvDIKDthXCrq8o7pljTkjg8JJHnrM=
+
+
+ Resources/core/icons/headset.svg
+
+ hash2
+
+ W67hQk0bwwh8DeOgB0limHnBxi3EnCMiOXNLot3pcEI=
+
+
+ Resources/core/icons/heart-arrow.svg
+
+ hash2
+
+ uCeilNcSUy2P0nBe55XmM5NIuEmOjNAUI4u0iQ3Ho2M=
+
+
+ Resources/core/icons/heart-fill.svg
+
+ hash2
+
+ 5rO1MAy5tvhhQcRuxDScdfGj34fX4uKQ7ZgDKdGP2hI=
+
+
+ Resources/core/icons/heart-half.svg
+
+ hash2
+
+ iXut7MpR9KGIvdFbmsZJSo6D7arpjdtuY3COImLf+qo=
+
+
+ Resources/core/icons/heart-pulse-fill.svg
+
+ hash2
+
+ HKmxSgIE4QsluLgcyqBg6uuKNjNs0DLCCxDcHnNLnbM=
+
+
+ Resources/core/icons/heart-pulse.svg
+
+ hash2
+
+ QVeH6+EpfbLsdv2qfSxevAjITGeoNub4+xxSORgWBAk=
+
+
+ Resources/core/icons/heart.svg
+
+ hash2
+
+ Q4hvG0Llsgb2i1Cc+n4Oxm43/UxT6/c0Bz850TutRPU=
+
+
+ Resources/core/icons/heartbreak-fill.svg
+
+ hash2
+
+ OqN3Xd3zOaR68JCxnSaRYLo4tHy5AUhM49bwTDnjaFQ=
+
+
+ Resources/core/icons/heartbreak.svg
+
+ hash2
+
+ bzRWoL1vZm1iIZLkeEk2pm4We4LDoUtYkods5NA1Rks=
+
+
+ Resources/core/icons/hearts.svg
+
+ hash2
+
+ nIDGaA3NbO9VR9WtACHAJMsFCp1PPqfS6UVY2LtrY8A=
+
+
+ Resources/core/icons/heptagon-fill.svg
+
+ hash2
+
+ ee3NON14825a8qziZSu8h7CpeOLjHeAui6gOuI6C2oA=
+
+
+ Resources/core/icons/heptagon-half.svg
+
+ hash2
+
+ WHIwbpQ2IvkVOO4HtPI/yvpGYttuKcV+EJuQ4UQCOiI=
+
+
+ Resources/core/icons/heptagon.svg
+
+ hash2
+
+ kQ+0B8Mzn6cIVfSBt0nOBNoY4qk747KacGL80o79/Oo=
+
+
+ Resources/core/icons/hexagon-fill.svg
+
+ hash2
+
+ KyCqZo3cNrowLodqo395ctod0Oei70ixIylGzf4WvjM=
+
+
+ Resources/core/icons/hexagon-half.svg
+
+ hash2
+
+ yag5wf+l3JFHVjfmY8xRlVsZ9sn7xpJCkIfWbOsQ1o4=
+
+
+ Resources/core/icons/hexagon.svg
+
+ hash2
+
+ CP17Iz4NN3JYX4YR8V125/uUkmrzyp8M0IUBQubDAqM=
+
+
+ Resources/core/icons/highlighter.svg
+
+ hash2
+
+ iTy+D5sRiZ24Hk0zqeGXFY21o4MACAFMnM1oMGFPAZU=
+
+
+ Resources/core/icons/highlights.svg
+
+ hash2
+
+ hvOtJF7ibp9g3KhhQ6nSI2ZbrYSMW68ItmfJPfPVldw=
+
+
+ Resources/core/icons/hospital-fill.svg
+
+ hash2
+
+ hMS4CqXMWg0G3zQep62cfjZDwzzcdGkX20EZZGZbx3U=
+
+
+ Resources/core/icons/hospital.svg
+
+ hash2
+
+ qbj1x6itp8dph1/o33xarMXf8pW+CcnIwgq830psrKI=
+
+
+ Resources/core/icons/hourglass-bottom.svg
+
+ hash2
+
+ cwXWjvofCih/IUfIjRKF7pNCSc6KMXBh8hhWQrTlipA=
+
+
+ Resources/core/icons/hourglass-split.svg
+
+ hash2
+
+ cIy8DUv7fIUrbkKMklq2uY8Ngj0MwVQ1Gi0gUNjii68=
+
+
+ Resources/core/icons/hourglass-top.svg
+
+ hash2
+
+ 0TOc6K+DzS8bIrdid1+i1/GXCaSQnwaHlFL4nFb0vjw=
+
+
+ Resources/core/icons/hourglass.svg
+
+ hash2
+
+ vtv12NVrPM4eOY+O6UkdinH58XPyR8+dpmKKA9hm92I=
+
+
+ Resources/core/icons/house-add-fill.svg
+
+ hash2
+
+ uhsSIuqWmG7MjUHWoKFxd/Uml/N9Kwf2xg2XEsg+qpE=
+
+
+ Resources/core/icons/house-add.svg
+
+ hash2
+
+ 4PuWHDI4Z+9mg3IpPODZQvwABhMSyo/2BsquspZzQZ4=
+
+
+ Resources/core/icons/house-check-fill.svg
+
+ hash2
+
+ V0219j+XgmmU1qvlPXBIxkp/s1Iph4XQU49dk4VkPxo=
+
+
+ Resources/core/icons/house-check.svg
+
+ hash2
+
+ MlPCR7BlOQ5+e3s3YITwPq74WJBC/5FAuhaxZUK3OrM=
+
+
+ Resources/core/icons/house-dash-fill.svg
+
+ hash2
+
+ geFqFb17MRekSRauOWgj81BeWA8OnfnlhWMaCOPygik=
+
+
+ Resources/core/icons/house-dash.svg
+
+ hash2
+
+ ISoBukaRDCKHL6ZhNCqq/W68mbu1R6+STZcUvOuakTw=
+
+
+ Resources/core/icons/house-door-fill.svg
+
+ hash2
+
+ PaIX0RL3564ipRk6Bbr9nzUcBakBZML499DDhjpKuAU=
+
+
+ Resources/core/icons/house-door.svg
+
+ hash2
+
+ YLB3pXAzmB7UJM4YsbVC8dXk+FpHjBiFfKEP8fd8Ih8=
+
+
+ Resources/core/icons/house-down-fill.svg
+
+ hash2
+
+ imYMTlCphAyBng1kgphR1oXN5cn7U8WpF2OdWw2v7Eg=
+
+
+ Resources/core/icons/house-down.svg
+
+ hash2
+
+ UU/uZYEyP7WhbMBMrehODn3nu0Ra5Aydxj6T6qUfxMY=
+
+
+ Resources/core/icons/house-exclamation-fill.svg
+
+ hash2
+
+ 3xA5k3yUhy74Hgqp1Q0i2tTn5VSmFr6oVToloSvapPE=
+
+
+ Resources/core/icons/house-exclamation.svg
+
+ hash2
+
+ W7XYWyHPJzH9aTEvp/0cL2ewYp5qZt8u7kuihUR0YtI=
+
+
+ Resources/core/icons/house-fill.svg
+
+ hash2
+
+ uLeZAlCw3fDmcnTfswhb+ZE2FQOLhXmy8f+Gx5KVgg8=
+
+
+ Resources/core/icons/house-gear-fill.svg
+
+ hash2
+
+ DwOueVW6AZthN2kXGJKsUKJQGFGIuXu7Z5pq/NNhWKA=
+
+
+ Resources/core/icons/house-gear.svg
+
+ hash2
+
+ 4uzUcmeyoqARfyKmgOsf/FP9q4i/vsftsU42C8M3aYs=
+
+
+ Resources/core/icons/house-heart-fill.svg
+
+ hash2
+
+ WMA+kHUVyVzqNJBjKn4rH3pd268SI/euq1kAeupLt+E=
+
+
+ Resources/core/icons/house-heart.svg
+
+ hash2
+
+ zORZ1WQUx+kk2UqiZa5+FtD+Xz2zNWx/7dgeSlrYfaM=
+
+
+ Resources/core/icons/house-lock-fill.svg
+
+ hash2
+
+ Hc8KJYEqW/DKBGSPTuWQOPk1twAd+/LCjnKwGxdne38=
+
+
+ Resources/core/icons/house-lock.svg
+
+ hash2
+
+ 0AO5GVtu1rzDxgxuCdBiK0haLOQB5nZbb0H37VEUPb4=
+
+
+ Resources/core/icons/house-slash-fill.svg
+
+ hash2
+
+ fcNyRKpNgHPop2np4rS+8TCoLIVAqCm/viym9isr13g=
+
+
+ Resources/core/icons/house-slash.svg
+
+ hash2
+
+ 72a3vPy2iEC3x+4tzL9EoXIJHw0bj1MpzvQPrHnxZzo=
+
+
+ Resources/core/icons/house-up-fill.svg
+
+ hash2
+
+ 9cgsOtRhWbRWlIA7fO7gObyULAgBI2dKz3HCDKUdZn4=
+
+
+ Resources/core/icons/house-up.svg
+
+ hash2
+
+ MVKgJ3OwVcp7VL/ft7pE5MzwTdl3IkjwtlWAcQ2OyhQ=
+
+
+ Resources/core/icons/house-x-fill.svg
+
+ hash2
+
+ 3Qwz7GsmNrEB4RDHKEdSdse8cxAb7w50Gv/DwGhuC6E=
+
+
+ Resources/core/icons/house-x.svg
+
+ hash2
+
+ WP2Prg2ePv7CK0vjoltNYHUN6M0SSqYxSSCcDcQ2/OU=
+
+
+ Resources/core/icons/house.svg
+
+ hash2
+
+ 3qXLhii0Z4XZNJ2eEBr7mrSfEVepEPv1cMiGCM3NOTM=
+
+
+ Resources/core/icons/houses-fill.svg
+
+ hash2
+
+ +/VutUYtNXLZJGywXbi+D8hZx0jpVXXjjJJNthIYcpY=
+
+
+ Resources/core/icons/houses.svg
+
+ hash2
+
+ Yeq1XjBRTwVZjextpoCOdHu1F0XGFXRECAt6aJ/QLG8=
+
+
+ Resources/core/icons/hr.svg
+
+ hash2
+
+ SkSCickqNoGY6Xj3Y/dAXhYoQ+rNr3zb2hqmE+bR7bk=
+
+
+ Resources/core/icons/hurricane.svg
+
+ hash2
+
+ PJzg44WNjOco4bpVFJGYZ37BZeYECvqR+hBV5e2JzS4=
+
+
+ Resources/core/icons/hypnotize.svg
+
+ hash2
+
+ d3uXBB56dJqD0LDp118FT4KX/dNnA3Gk5C0UgWn1TUI=
+
+
+ Resources/core/icons/image-alt.svg
+
+ hash2
+
+ gdujLXUjLc+f/LuRMsavxbr7SWLainxARoKWqXV3oC8=
+
+
+ Resources/core/icons/image-fill.svg
+
+ hash2
+
+ 6AMza9UwpA5UnMyNAa3GO97hFn46DZy5/19aLJZvAcs=
+
+
+ Resources/core/icons/image.svg
+
+ hash2
+
+ yDEeRHahvTZQ5MWi5bL4qOM+GbI5GIPctraBmmgQMTw=
+
+
+ Resources/core/icons/images.svg
+
+ hash2
+
+ 0bL3eXXSXFuAEHAQolWe7YB642MM3XGcDVXevh9T7dA=
+
+
+ Resources/core/icons/inbox-fill.svg
+
+ hash2
+
+ YyKCcJIOyWfsNhNpjr68WPVQnjcnw2A1zIdcqO77GQ8=
+
+
+ Resources/core/icons/inbox.svg
+
+ hash2
+
+ LwNPKgFdRf/G0SmhzaeVEDzq9+NpAqOacXmo5Nw4qsg=
+
+
+ Resources/core/icons/inboxes-fill.svg
+
+ hash2
+
+ +aFfW1YFtLIMP1NYMVBX4o/575J+Aq10K4S0VYew91c=
+
+
+ Resources/core/icons/inboxes.svg
+
+ hash2
+
+ JbsD1fbimDASStfw5FPm9V88auHwp1Fb26tmdZxcYlc=
+
+
+ Resources/core/icons/incognito.svg
+
+ hash2
+
+ xBAh4iM86Bohxa8Jq1aI2DaXyY9a8kRS7CrGblCiZFo=
+
+
+ Resources/core/icons/indent.svg
+
+ hash2
+
+ ZqlSAsdHCnucIekd8FOTXGF10IxlUnGzTFNIAYboDXM=
+
+
+ Resources/core/icons/infinity.svg
+
+ hash2
+
+ Flc97q8cnEIEC21U1pIi0yDK86lrptA/bVbOsLnFzP0=
+
+
+ Resources/core/icons/info-circle-fill.svg
+
+ hash2
+
+ eOSaCJxmJuZFNgDQKvdVDXnNdUtcSSLtC8/x44cPfy4=
+
+
+ Resources/core/icons/info-circle.svg
+
+ hash2
+
+ SPyxbmxefVVVtiTByzqb+OfiXHyKRxrtABI3qwGJz4E=
+
+
+ Resources/core/icons/info-lg.svg
+
+ hash2
+
+ YpK9XziDF/u10MxY1ZUNoM2OSVDXHbsFjWfW+lcsW9c=
+
+
+ Resources/core/icons/info-square-fill.svg
+
+ hash2
+
+ Ij8EWhEz0Kqzco3IPlm8bVq44oumgu1mIBzOZEbuQr8=
+
+
+ Resources/core/icons/info-square.svg
+
+ hash2
+
+ YC2td+61h3KoP40/eT4vbvRlks26nkHwFwR9lIl/+lI=
+
+
+ Resources/core/icons/info.svg
+
+ hash2
+
+ wnx4CWS1BCZF5PMdpw6XN6/xBvJf+pyUIoWFromd1zQ=
+
+
+ Resources/core/icons/input-cursor-text.svg
+
+ hash2
+
+ 2SkBKOVQdArpsav/MMyAfkeOlg2AT1rtjljlVL+nsvs=
+
+
+ Resources/core/icons/input-cursor.svg
+
+ hash2
+
+ fNvohFON2w2aOzacmRd3CUp8Fk3AEGebApg5gfjFOx8=
+
+
+ Resources/core/icons/instagram.svg
+
+ hash2
+
+ vd0ARYT9zqCpXJCVsU1pEWkKiUxOcyV6/0QGa7uhlz8=
+
+
+ Resources/core/icons/intersect.svg
+
+ hash2
+
+ s/FaYmuLofZSJbWKWsWf0+M3vJ3rqRhlDqtkmZX9zU0=
+
+
+ Resources/core/icons/javascript.svg
+
+ hash2
+
+ meCiyQzZE/c6KLm/H8eXj/kOsenbJVMOWMWQBIsPKv8=
+
+
+ Resources/core/icons/journal-album.svg
+
+ hash2
+
+ OEdD8tiJsms1+9eiNiWtZv4A6uoodWUGa6nc+1+/Vh0=
+
+
+ Resources/core/icons/journal-arrow-down.svg
+
+ hash2
+
+ NaTYKddWKFpYfIWj60u5W4lGtgyxYAb0Up4N+6tKQYA=
+
+
+ Resources/core/icons/journal-arrow-up.svg
+
+ hash2
+
+ ffmq7/Ckr1mTW9QUovZ1Wgpn1kiI0dgJhjsIgAd2AKk=
+
+
+ Resources/core/icons/journal-bookmark-fill.svg
+
+ hash2
+
+ kKkmWjhgUEAqTnm/hk0e+/vOc9wgdLxJuSGClmAwLGY=
+
+
+ Resources/core/icons/journal-bookmark.svg
+
+ hash2
+
+ hcfUo8UAcnxtdKlMmBo+vQd1VIlu/3mL9KqOskQajCU=
+
+
+ Resources/core/icons/journal-check.svg
+
+ hash2
+
+ DKOgJQs1oMbBGg37tJ5G7qHLgoSdN4/xruznGXFYhKg=
+
+
+ Resources/core/icons/journal-code.svg
+
+ hash2
+
+ 9bHc/d6tIt8Fp8yHao6Z+a9J8/fCPG5PFbhJ/lFm5X8=
+
+
+ Resources/core/icons/journal-medical.svg
+
+ hash2
+
+ bSzXjKxiDUDagmeaIkiGIc3iJ0Ei6p2IDHwCj9ZRR44=
+
+
+ Resources/core/icons/journal-minus.svg
+
+ hash2
+
+ 6h7xswNQy7QEUYogs/bpT9zsT+Xit9ZVj3+YBouJXBg=
+
+
+ Resources/core/icons/journal-plus.svg
+
+ hash2
+
+ W520L22W2rIcs67Hglx9ivkxMcAHS+rNe5Or1BHdygA=
+
+
+ Resources/core/icons/journal-richtext.svg
+
+ hash2
+
+ vGIysLzd/RUklnRJL6zBza2ZKlqe7UeDrtCnnJ/hVrg=
+
+
+ Resources/core/icons/journal-text.svg
+
+ hash2
+
+ jpHYkV+ZmqslDZD7eFosr8BX/LyDCLTmUWiPEALfegY=
+
+
+ Resources/core/icons/journal-x.svg
+
+ hash2
+
+ IfUs4JPR4lKFmcq4yKGQn6+k0D2h54ihfewNFa0epP8=
+
+
+ Resources/core/icons/journal.svg
+
+ hash2
+
+ ktwryIHL8aFGBZa+aLiCDV2tGtB4lP/8NtxmiT0jkZY=
+
+
+ Resources/core/icons/journals.svg
+
+ hash2
+
+ 0zq2aIQBT4NODCvxGxlQKd4i4tOymLvLslEQiKZCI4o=
+
+
+ Resources/core/icons/joystick.svg
+
+ hash2
+
+ 6GrV2oXqJjjHDQ/NzUIJ0NKZ5p3AoEjHIEWRGUaYDak=
+
+
+ Resources/core/icons/justify-left.svg
+
+ hash2
+
+ kIxM+9+49J2n/NJYMX62Qgi9kynynai2N06G+7wiiqA=
+
+
+ Resources/core/icons/justify-right.svg
+
+ hash2
+
+ HEm9sneV3elNG0/DirWZryTomRrHUzJbHB8lwmWwhfE=
+
+
+ Resources/core/icons/justify.svg
+
+ hash2
+
+ 95mBcQGfR5S5IQk8gnETohcBJYX6znvueU2BBIXs7jI=
+
+
+ Resources/core/icons/kanban-fill.svg
+
+ hash2
+
+ lpFjgKPNT21mBoqICON1Ys5u07IofToVP5RP5dj6FkU=
+
+
+ Resources/core/icons/kanban.svg
+
+ hash2
+
+ OxR7TJJHpqxvSDVXKCC+U+Ih6GG4c86LopIL8a5UUSI=
+
+
+ Resources/core/icons/key-fill.svg
+
+ hash2
+
+ NIRuUb0C0tAp/s0zVsNPZxNpo4be3AqPJaPnmV9y400=
+
+
+ Resources/core/icons/key.svg
+
+ hash2
+
+ vwQQ7bAWTx/1m2vZhoQNazgUFaUMRsdYAvmJb9X+M7w=
+
+
+ Resources/core/icons/keyboard-fill.svg
+
+ hash2
+
+ wmQi9tpp8sazQVNZXJds93YTvFSAi+kqTqQck855k4w=
+
+
+ Resources/core/icons/keyboard.svg
+
+ hash2
+
+ kGyIzmzYERBSDj7NftZa/yHdLx1kdWefvacE3G4Z6kw=
+
+
+ Resources/core/icons/ladder.svg
+
+ hash2
+
+ sqBJATKaQj1ZStSNYtl4zygu8Irl4q+hKG3QD6PValc=
+
+
+ Resources/core/icons/lamp-fill.svg
+
+ hash2
+
+ wczo1iQuePUIiAIf2twvNibQVSGmUB+BxrldM8V8yTg=
+
+
+ Resources/core/icons/lamp.svg
+
+ hash2
+
+ LdUMgD+0V2YS5R2NW9dcbP2VgZAvxeX6zgVrg5lMFtE=
+
+
+ Resources/core/icons/laptop-fill.svg
+
+ hash2
+
+ 4BQO8l1t1cB/upWbGq5Dx40zrdL6WDa9bqazr8gGQAE=
+
+
+ Resources/core/icons/laptop.svg
+
+ hash2
+
+ OTF/0z/MI4wUYnsEXH4qW4dR+YipQBW2GyRfLMWHPGs=
+
+
+ Resources/core/icons/layer-backward.svg
+
+ hash2
+
+ flE/hoC9GIQIYIKE4GysnT/GNbp2/qQ3DUEJalmTk8Q=
+
+
+ Resources/core/icons/layer-forward.svg
+
+ hash2
+
+ g8ys5tcLZjy/foPWHvWeSd7F54cmuCHRl7kqvotpX18=
+
+
+ Resources/core/icons/layers-fill.svg
+
+ hash2
+
+ 6/BPJsriUWoGP1g18SzpsGRQ6upn8Uaw1TGrY9yX7e0=
+
+
+ Resources/core/icons/layers-half.svg
+
+ hash2
+
+ FL8mDYlIchOJtCpwzKXjdxT6uSFxT3E11P1LQNdq89g=
+
+
+ Resources/core/icons/layers.svg
+
+ hash2
+
+ Ag2QP5CuFcsvCf+Agr0jy76YNxr7xSld18PHmq01tjk=
+
+
+ Resources/core/icons/layout-sidebar-inset-reverse.svg
+
+ hash2
+
+ YNVNcBxpKovJbkQ1x1jthfWav2Spg0UGcU+fZr3LOR0=
+
+
+ Resources/core/icons/layout-sidebar-inset.svg
+
+ hash2
+
+ 6bl36rKwv/JF23B2QoRz9iIv6Vcp5PGdVCtIMspbWH8=
+
+
+ Resources/core/icons/layout-sidebar-reverse.svg
+
+ hash2
+
+ WX7fzY6IN2Jp9WIOaG3szaTXUL//E4uGWKlYJw29kDw=
+
+
+ Resources/core/icons/layout-sidebar.svg
+
+ hash2
+
+ slZimtRZHNgXuWLDgdimzrRF8dOorbfc43GMj3uKh88=
+
+
+ Resources/core/icons/layout-split.svg
+
+ hash2
+
+ YqWQpXb2lLUN7jS3f3Ht5hHbWlAdQPgpGtiPwPgdees=
+
+
+ Resources/core/icons/layout-text-sidebar-reverse.svg
+
+ hash2
+
+ 12KVtnnVjpNuAoFVJoxieCIDQewsVOndSGv63DW9ivo=
+
+
+ Resources/core/icons/layout-text-sidebar.svg
+
+ hash2
+
+ yOpPyvenuC1N+knA8sYPYFnZQ4PYoJHB42bKLcKNk3A=
+
+
+ Resources/core/icons/layout-text-window-reverse.svg
+
+ hash2
+
+ Adq+K8QUHwrk611a/+hGE0fyJ1E3zel3ZmUWyp3B4Kc=
+
+
+ Resources/core/icons/layout-text-window.svg
+
+ hash2
+
+ U5EhqEWOhqQKCyxOXz0O9oWMfVBkAtwVoMBLEqhGsYA=
+
+
+ Resources/core/icons/layout-three-columns.svg
+
+ hash2
+
+ AB/QOBcLq5Wm778fHftg6j821ZPIN1xuGpfId2w5ykc=
+
+
+ Resources/core/icons/layout-wtf.svg
+
+ hash2
+
+ LQRoHFguJhcG8fcMpfx+tYOSwSUi/NCN0vYE9YuVwg8=
+
+
+ Resources/core/icons/leaf-fill.svg
+
+ hash2
+
+ bWtOQuzGEJB+snu0RZiaOOgpxhAVG4uGwUzFclDLBsM=
+
+
+ Resources/core/icons/leaf.svg
+
+ hash2
+
+ /GgiyE3nWnOIeZ5Ut/raWJk/3INMS1BvL0brTJNzMeg=
+
+
+ Resources/core/icons/life-preserver.svg
+
+ hash2
+
+ PFQm8581VuGAzk/QdZv8wAIGPcvNTvUseGoiayRHJYY=
+
+
+ Resources/core/icons/lightbulb-fill.svg
+
+ hash2
+
+ LgdiM97CcBdR7VoR6xCOsW4Ay3mko1MidAreLWk8+xo=
+
+
+ Resources/core/icons/lightbulb-off-fill.svg
+
+ hash2
+
+ g/JtBp+iz1TMjBHYjdbmbIUv9GOLHg+Ar7sbLGxlPgs=
+
+
+ Resources/core/icons/lightbulb-off.svg
+
+ hash2
+
+ VYJvnbbERiZBMQGl2WDVM+LH/PumtOVUUyvrK6RlHnA=
+
+
+ Resources/core/icons/lightbulb.svg
+
+ hash2
+
+ aUaG8OCMCaVWbR/kX5Uc9Xrik60eK7RXBVEFqhTmXuQ=
+
+
+ Resources/core/icons/lightning-charge-fill.svg
+
+ hash2
+
+ D7PA2mF3Uo+7685KdHvwtHx1P1PRgZIAAIcDatKtPTw=
+
+
+ Resources/core/icons/lightning-charge.svg
+
+ hash2
+
+ laTIeNpAYpBXNAepZqVwhLXInnh+UExrE+JPc+tGbMg=
+
+
+ Resources/core/icons/lightning-fill.svg
+
+ hash2
+
+ cRKA1SX2BItKP5RqiV8znZJqTGm80FYPpXWGnZnZIIU=
+
+
+ Resources/core/icons/lightning.svg
+
+ hash2
+
+ LirFRc7ZPPibm1XrhH7QTZon5s0DjyMbAcIzyzrvD9Y=
+
+
+ Resources/core/icons/line.svg
+
+ hash2
+
+ vcFI0iYGK09/F1tflo5MjuIRf/jffAhX/VnuLXAJbxo=
+
+
+ Resources/core/icons/link-45deg.svg
+
+ hash2
+
+ pCgMtkorjnkP8ukZSibMBIKN5i2FASe/CXj+uLL/yi0=
+
+
+ Resources/core/icons/link.svg
+
+ hash2
+
+ 0oEVX9GDANhbT68RDI7gp7AWf+N2TTB4uiztXG/Fnv8=
+
+
+ Resources/core/icons/linkedin.svg
+
+ hash2
+
+ pqClbcDuH2PNynobQUDeOj74vop01EH0UCkCep7vreY=
+
+
+ Resources/core/icons/list-check.svg
+
+ hash2
+
+ 92vdHnGDfG+WfHkreUWePyR5c1mw15ffKAmBVIx0xUM=
+
+
+ Resources/core/icons/list-columns-reverse.svg
+
+ hash2
+
+ Ns+EXdruPOGm8gFxDeRwfq8hzdYmU4LLNkdYxmK6k7c=
+
+
+ Resources/core/icons/list-columns.svg
+
+ hash2
+
+ N3YkA2CN3I8QwApPsFTT/hc4BM+wOiC0fgDSAueksg4=
+
+
+ Resources/core/icons/list-nested.svg
+
+ hash2
+
+ f06UQfiumFrkdUn5OKmY7SdFWsLxrsPfuBv/0vIHw7o=
+
+
+ Resources/core/icons/list-ol.svg
+
+ hash2
+
+ d5n7c55gQUfjhfhZOR5TOAMNQNSGZYqdsoVYK6esuMI=
+
+
+ Resources/core/icons/list-stars.svg
+
+ hash2
+
+ NkZ++lIfaMjzCzN0GVwe9iffHIP4Nu7KCS0jsb5d6bY=
+
+
+ Resources/core/icons/list-task.svg
+
+ hash2
+
+ aM8nJvRjaxNkOTFjvNPtlIpoHJ2OVBuIXPHwGGR9PI0=
+
+
+ Resources/core/icons/list-ul.svg
+
+ hash2
+
+ +QDebNVwAQyZBxAQz1v2rDeLVha7ftzkiqOVyKHErXo=
+
+
+ Resources/core/icons/list.svg
+
+ hash2
+
+ bFUPlza1WxQhcttKW4KnXmD+bHMxnYx6E3wc9Ssqfmo=
+
+
+ Resources/core/icons/lock-fill.svg
+
+ hash2
+
+ Z8sSfvWzYsMAasvklcCHbZxQef+kuXjxcOq8iYLXc9Y=
+
+
+ Resources/core/icons/lock.svg
+
+ hash2
+
+ Igl1lujQpMVdFVcA2RIn1uh811Wtad0iLIXeSNhKO6I=
+
+
+ Resources/core/icons/luggage-fill.svg
+
+ hash2
+
+ ulwTwTI2fz/jPt9EMNjeqnXgsUpn8zUHYjluOb20PXg=
+
+
+ Resources/core/icons/luggage.svg
+
+ hash2
+
+ Mut71SZVzMDdv5MeF6Ai/rU1J6nk9sN7YFLYrExgnik=
+
+
+ Resources/core/icons/lungs-fill.svg
+
+ hash2
+
+ 9swrCVo7/xMQAZknHcPriB+wr5zorG55BwQunRxnShg=
+
+
+ Resources/core/icons/lungs.svg
+
+ hash2
+
+ XyQ26T+dik99ZZcAH8B4obL8RQKDymefBcoLzv0Y9nc=
+
+
+ Resources/core/icons/magic.svg
+
+ hash2
+
+ LA5Km72xwxi7AKfPSZqURKcZfe2MFWfgUzE7pleaaKk=
+
+
+ Resources/core/icons/magnet-fill.svg
+
+ hash2
+
+ wOXX8WrKikgLj7K/VGGnMxM3amwyb2aj+NuB0p7BIIM=
+
+
+ Resources/core/icons/magnet.svg
+
+ hash2
+
+ +yB6/b1DNo3gUtS99NLBSjgvUHdFIwEhUMwxXgXep0o=
+
+
+ Resources/core/icons/mailbox-flag.svg
+
+ hash2
+
+ 2piDELOICV92JvNOhZ1CgvlGITKhthaEiP/Wu802MKw=
+
+
+ Resources/core/icons/mailbox.svg
+
+ hash2
+
+ yavvkXWsrbyx8t47/bh17+TzUXaXLMAZS49Bdi6qyuo=
+
+
+ Resources/core/icons/mailbox2-flag.svg
+
+ hash2
+
+ qS7Gzxv+0sKs8TmXJZnfCQay4TFrsfttjdDN75wWLaA=
+
+
+ Resources/core/icons/mailbox2.svg
+
+ hash2
+
+ 3m4fXHPPqQiG168tCLzHpGYNziUvlobY1C0s2XEv+M4=
+
+
+ Resources/core/icons/map-fill.svg
+
+ hash2
+
+ 48SBLnmE53HHUbdJCpO4yIiRZ7EmOJkp9I3omQdlxXo=
+
+
+ Resources/core/icons/map.svg
+
+ hash2
+
+ Sm9kD8FjrmtFyelRlUOW6E1rADRWD4Mlso2RApb5prI=
+
+
+ Resources/core/icons/markdown-fill.svg
+
+ hash2
+
+ P+IKQMw/Rb4XjNSgtYjuXdgd0YlARlexqk6FHednsD0=
+
+
+ Resources/core/icons/markdown.svg
+
+ hash2
+
+ wqoyET5gbF+mkXI2wrM9OMcQDVVZOGjBQBnSBcp250Q=
+
+
+ Resources/core/icons/marker-tip.svg
+
+ hash2
+
+ 8agT14ZokPfTRiU1sSRd9i/10Mc7zHljJrpW7hX8BJs=
+
+
+ Resources/core/icons/mask.svg
+
+ hash2
+
+ +VO+qvOev1Zj6FKIrC8/g0x82amMblpPjOJBEQ1xDBo=
+
+
+ Resources/core/icons/mastodon.svg
+
+ hash2
+
+ /7zjjsaxeB0HYQxeAS6z0lAVHY/toRmjVBaNWcgtW6I=
+
+
+ Resources/core/icons/measuring-cup-fill.svg
+
+ hash2
+
+ 1vzWdniliLxuoDUNwMQ1DvEeIMrgMvWIQfkvrYIimD0=
+
+
+ Resources/core/icons/measuring-cup.svg
+
+ hash2
+
+ Ayp8euC/EBU8vPuwpSq51pV6hPE3SqeAp7WCH3PIBUY=
+
+
+ Resources/core/icons/medium.svg
+
+ hash2
+
+ +7wDOwjt7jdX8ZZ4DOcgJv1iOH3rDklLzRZUwPZ8jA0=
+
+
+ Resources/core/icons/megaphone-fill.svg
+
+ hash2
+
+ onTnvLTwl7R0ZlvoBwWDsCMZfwn9zle6GJh0pCDHVWU=
+
+
+ Resources/core/icons/megaphone.svg
+
+ hash2
+
+ cQF0AU14TnKUpVkx/D97dtuTGfsI0NBD+t9BuEMR7is=
+
+
+ Resources/core/icons/memory.svg
+
+ hash2
+
+ asvgSnMos99fCDWuLcU0znKrU5rjgasHMXUeqLJyjyQ=
+
+
+ Resources/core/icons/menu-app-fill.svg
+
+ hash2
+
+ m1DPZOyaIxGD5WdwDFzAvhYuWcEk2txNSa5j8S1oCV8=
+
+
+ Resources/core/icons/menu-app.svg
+
+ hash2
+
+ 4I0bK+QaRX3XivE978DntrCglLKiCZPVlYq5hKoHFeA=
+
+
+ Resources/core/icons/menu-button-fill.svg
+
+ hash2
+
+ MsoweoRftKh0PVf2Dzct+zkk0Q29un+76LBgzdpbm/w=
+
+
+ Resources/core/icons/menu-button-wide-fill.svg
+
+ hash2
+
+ Nn724cgUlv15vGrNXL5IzY6Kati2CfGzDiys3O/ln6c=
+
+
+ Resources/core/icons/menu-button-wide.svg
+
+ hash2
+
+ U5/Vf8Ufsix/MPocpEGC2ZUHF8p0lPZTw43HwM3LKzU=
+
+
+ Resources/core/icons/menu-button.svg
+
+ hash2
+
+ a9+bKDA6mcps6gtZfx318k50EvD0O21OKxMGyT1ha5g=
+
+
+ Resources/core/icons/menu-down.svg
+
+ hash2
+
+ tbnyx2kMQNdNgtcXxro8A9zewbxCXSfEjEtR15whJQ0=
+
+
+ Resources/core/icons/menu-up.svg
+
+ hash2
+
+ ImYYbgQjF6EqXyxpMO0Ou+Yt/ug49o1XNPJSR1a3yQw=
+
+
+ Resources/core/icons/messenger.svg
+
+ hash2
+
+ hxLlyP9dd/ussOg1HznQOcjDM9CFwVN1tpFBduFSFjQ=
+
+
+ Resources/core/icons/meta.svg
+
+ hash2
+
+ W/bSTTNvj1lCfmf5M8wjgfOMS9R4aCdO34H5vMEQtoc=
+
+
+ Resources/core/icons/mic-fill.svg
+
+ hash2
+
+ KGNdPTeRzkPGgN4JVAlzJhnnq4+SPvahJO/1hxvxofA=
+
+
+ Resources/core/icons/mic-mute-fill.svg
+
+ hash2
+
+ L89tuVj2lftW26GfQ5WS5Jfl0OC3S+50TnwjpvyxFmw=
+
+
+ Resources/core/icons/mic-mute.svg
+
+ hash2
+
+ enxnPwdvDOBpEOG+9/Bnusm4hjs6ciIER42W8pUDU4s=
+
+
+ Resources/core/icons/mic.svg
+
+ hash2
+
+ jDEK4wwi3/RlOqVaoUr97C0EFWH053UaFBQCHjai28w=
+
+
+ Resources/core/icons/microsoft-teams.svg
+
+ hash2
+
+ T4b532DqhrmAciHPJJJZ7xyj6NI6jwL9F9i+gOnunKg=
+
+
+ Resources/core/icons/microsoft.svg
+
+ hash2
+
+ Z4C0964KSLSpl4sd3/uq0RmNXpqCnTFGrVY4JU19/qM=
+
+
+ Resources/core/icons/minecart-loaded.svg
+
+ hash2
+
+ 40zTcmji3NSc8DODU6WPtDeV30rA3dOl5td7xu+SyfU=
+
+
+ Resources/core/icons/minecart.svg
+
+ hash2
+
+ QLeTRnOqD+18axsrfmf+6nxPDGaFZT10ZMW66mveG1U=
+
+
+ Resources/core/icons/modem-fill.svg
+
+ hash2
+
+ 3EquJl4lqN1b+aEhWojyQlI423i0jkxX/h65IoizAAw=
+
+
+ Resources/core/icons/modem.svg
+
+ hash2
+
+ ul01xRgPxpwrJthFLGihxnOKbBxxUewAR+hb0Ij2aNI=
+
+
+ Resources/core/icons/moisture.svg
+
+ hash2
+
+ d4smP1MLJAcFTVQXHETSQFeP2L1NZJd/rJufDbXjSEs=
+
+
+ Resources/core/icons/moon-fill.svg
+
+ hash2
+
+ 5U3Djn//Wbr6PM8GDn63Zbu+s3C27SkHnNvaAZqMulc=
+
+
+ Resources/core/icons/moon-stars-fill.svg
+
+ hash2
+
+ lcTTdaNeTePRRm2rawJALwTRBElRip5VGaE3ed2N0VQ=
+
+
+ Resources/core/icons/moon-stars.svg
+
+ hash2
+
+ 0WbEpX5EWPD7bGq5RhUYdvveyAyWgcTHDhRAp9r75WU=
+
+
+ Resources/core/icons/moon.svg
+
+ hash2
+
+ jmOVbTIgz28pyzFJTvmnBfNNLjosh0fmnPw+rsLq3oU=
+
+
+ Resources/core/icons/mortarboard-fill.svg
+
+ hash2
+
+ hKJGEEi39rESbt2LsKBzlLYlWGzBGBOkYGuSkXqg1ec=
+
+
+ Resources/core/icons/mortarboard.svg
+
+ hash2
+
+ Yd30fknCwcf5BNR0HF/UNl8oxw1fiXPDb0Ubp0S6MvE=
+
+
+ Resources/core/icons/motherboard-fill.svg
+
+ hash2
+
+ PGi7n5K6MyNesH9tgmjms8k5W1NuyQ0SkWY/ZgcteRk=
+
+
+ Resources/core/icons/motherboard.svg
+
+ hash2
+
+ b7KQ+GIIRkWNZEG3T1j7EB+F3awq9X+hYszCDQ++lGM=
+
+
+ Resources/core/icons/mouse-fill.svg
+
+ hash2
+
+ KY+hdKHLp50aRYtYS1H7zDXLBZYIlswDUYYyjveqbK0=
+
+
+ Resources/core/icons/mouse.svg
+
+ hash2
+
+ t+U2RvxGAxnrVTEMDIK2v3++/htnW05Xyrl7k/JrtFo=
+
+
+ Resources/core/icons/mouse2-fill.svg
+
+ hash2
+
+ QbPkYzIl4+op+uRtzlH1LdSOhCZfylqI1RkydCdn9DA=
+
+
+ Resources/core/icons/mouse2.svg
+
+ hash2
+
+ O9osWTtyQK4M1oVpp5zIaGjM9maoODDQvkK1xV/7WZw=
+
+
+ Resources/core/icons/mouse3-fill.svg
+
+ hash2
+
+ Hc4Ypn1fLYSZp05Gr9NVXCTm+ONcu1bSgBckipW3Ap8=
+
+
+ Resources/core/icons/mouse3.svg
+
+ hash2
+
+ if9juMFWyKDlFe6w6u05R4iL7yu72b0u1SyCTFyYZZ4=
+
+
+ Resources/core/icons/music-note-beamed.svg
+
+ hash2
+
+ juh8kXOMe650/8W3+mV8M9ncVVzFbuL3d5S+L3jFcyk=
+
+
+ Resources/core/icons/music-note-list.svg
+
+ hash2
+
+ ECbogi+VjBqymkS2GLnX+9TfzGOolAXaMcZMHXLUoFI=
+
+
+ Resources/core/icons/music-note.svg
+
+ hash2
+
+ 54Men/9EQz2LgF0x/fM2F4JR0rpejH7PaPL5Y3v2vgE=
+
+
+ Resources/core/icons/music-player-fill.svg
+
+ hash2
+
+ Qng7Gzl4zxUlOW5RkgGbOUQgS8yvz37Jyub8dqWzgPo=
+
+
+ Resources/core/icons/music-player.svg
+
+ hash2
+
+ Gm87XbZPuZtiM2xFvYr+ZIWAUHEJXKvQANoApnZ5tnc=
+
+
+ Resources/core/icons/newspaper.svg
+
+ hash2
+
+ j8U0cnZ8Xf4TgG+ZvRzAWfZ0185iOujW8l2E1Vr7JnM=
+
+
+ Resources/core/icons/nintendo-switch.svg
+
+ hash2
+
+ NUlvQWEJlgIjwpGtfCgGoQCqbQT6HnnLKexokJmaWMQ=
+
+
+ Resources/core/icons/node-minus-fill.svg
+
+ hash2
+
+ 5dz1TJRvtzzNPLUyuaFJR8h0RUVnlTrWLezichQhW5Y=
+
+
+ Resources/core/icons/node-minus.svg
+
+ hash2
+
+ 79sLqOWzwaRR2Trjtmf4urvYX6VI+xpylUj6PO6no90=
+
+
+ Resources/core/icons/node-plus-fill.svg
+
+ hash2
+
+ r3FksYPmsp9NNRgHtoUMS9Dnbcm854+bACRT7cM4a0M=
+
+
+ Resources/core/icons/node-plus.svg
+
+ hash2
+
+ 5bkSGKkr5SgfStlraW2EWopDhozic3XqCDmIEYZuFcw=
+
+
+ Resources/core/icons/noise-reduction.svg
+
+ hash2
+
+ TBeNP6rfGj5ya4367lPlYp7nRyurHaTFPi0mmjhbzqo=
+
+
+ Resources/core/icons/nut-fill.svg
+
+ hash2
+
+ hHDgt4A6BPxectw1rSj0bdZxsCs4L1aO3EgG6yXtjJA=
+
+
+ Resources/core/icons/nut.svg
+
+ hash2
+
+ P2BLePyM7EQm3tYPJCwWd8w79qzPTKC1hf0iUoNrb7Q=
+
+
+ Resources/core/icons/nvidia.svg
+
+ hash2
+
+ kLV8+1IRkilt3P1gLKRKrHyEC8A/nRYXRqCrzIk/ras=
+
+
+ Resources/core/icons/nvme-fill.svg
+
+ hash2
+
+ URKnQwmQRrZhU+JY/5cUlyNNl5UwidJfSlxvSYv8Erw=
+
+
+ Resources/core/icons/nvme.svg
+
+ hash2
+
+ nilX6MlEIXEerAQxiNiT02HIbjcDiIbYie6TTU2ujMg=
+
+
+ Resources/core/icons/octagon-fill.svg
+
+ hash2
+
+ DRq+zNhRn7fFfOzP0+OY6KAG518aPWa1dERbFuNsZIw=
+
+
+ Resources/core/icons/octagon-half.svg
+
+ hash2
+
+ lcjnQ3QGpC8IlX8o1ueegP0Q2YFgYIlye3USIfISs2g=
+
+
+ Resources/core/icons/octagon.svg
+
+ hash2
+
+ 2tfTPHu493B7cIGfPgT7kFpWtxOZ3o4Y2XwTKU4eI+M=
+
+
+ Resources/core/icons/openai.svg
+
+ hash2
+
+ G6yUHGNxGiBaN6Bcn9rDoeB2G15LRlRcxJntdbq+NxY=
+
+
+ Resources/core/icons/opencollective.svg
+
+ hash2
+
+ Yk7V0vFYi9Lww2+C3g08zLOAzzQ+pGuBTZ93G/SlQLE=
+
+
+ Resources/core/icons/optical-audio-fill.svg
+
+ hash2
+
+ XJfvjxIGJTgnJoMuDeWLbDg5OVbEreU68rU8urb+Idc=
+
+
+ Resources/core/icons/optical-audio.svg
+
+ hash2
+
+ sTyvSb8hJ2SHDru2GeMAg63sWik06W429Znr8hwCiDs=
+
+
+ Resources/core/icons/option.svg
+
+ hash2
+
+ /nZEMC42HHNSXjGE4bFatUMacqbFi4/DyuRPkINN6wM=
+
+
+ Resources/core/icons/outlet.svg
+
+ hash2
+
+ R9NHwkSE28I0NtxkKy5ZXNFUFMqxI+Tnu6Nqg1ExeBw=
+
+
+ Resources/core/icons/p-circle-fill.svg
+
+ hash2
+
+ heX2N9rWNJWtDV0FvTjiTx5z/mY9s/HbF6LlyBsUlQQ=
+
+
+ Resources/core/icons/p-circle.svg
+
+ hash2
+
+ GgIt9U5tq9FTWlSdMggKbbb+i/OToV+Ki89CigeJAys=
+
+
+ Resources/core/icons/p-square-fill.svg
+
+ hash2
+
+ K6baMhzDe1aQv87sn6pFsdTGK8v89t1J99XBZAbYRHE=
+
+
+ Resources/core/icons/p-square.svg
+
+ hash2
+
+ loE2biiO9sy3VrI9rLXuyx3HItih9Vs74vXppQSPMcM=
+
+
+ Resources/core/icons/paint-bucket.svg
+
+ hash2
+
+ zm0ZCiJWutFCsGP6GqGrb0YnOAD6G5JD4B1RB/vu7SU=
+
+
+ Resources/core/icons/palette-fill.svg
+
+ hash2
+
+ D/9J9BYbGsxejXiT7RWXhs4njyr15fx8rpWmkQiYFW4=
+
+
+ Resources/core/icons/palette.svg
+
+ hash2
+
+ Mqi5Ab/UugUlNujBn9zwm6TGQZlbS0S9UBFSueMf4eU=
+
+
+ Resources/core/icons/palette2.svg
+
+ hash2
+
+ xKYqQ7BmKbB+HX/qxuRHcH5tC4rWhc2dPpH+VzfpkxQ=
+
+
+ Resources/core/icons/paperclip.svg
+
+ hash2
+
+ lbfgVObC8SvCmpyf5IJrFpxwnUjd1SQWZASoyk/nZ/U=
+
+
+ Resources/core/icons/paragraph.svg
+
+ hash2
+
+ Q30f3IVTsU/NGI3J9z9ghgZuqB3zH37tE+B7iV7Dboo=
+
+
+ Resources/core/icons/pass-fill.svg
+
+ hash2
+
+ n42pgvIFX+UdLVWfFUewuelrUy59KuE7o9/Uit6tuOk=
+
+
+ Resources/core/icons/pass.svg
+
+ hash2
+
+ ZOMe+G4kYEZCv/ri4rYjisNAjZHgP0RJdPtGuhvreK0=
+
+
+ Resources/core/icons/passport-fill.svg
+
+ hash2
+
+ QK8KkQCuYD1BJFoLH0dK7JwgiScmNJoaFgAalLgCqLc=
+
+
+ Resources/core/icons/passport.svg
+
+ hash2
+
+ f30KUxatEhGiiV2So4d0sYaNNKyNjuIyhg6/0CMtcNA=
+
+
+ Resources/core/icons/patch-check-fill.svg
+
+ hash2
+
+ cYc/HOk4Z6ngdTBnQFneQ1WAAJjKYH+8IfiZ1AFzlCQ=
+
+
+ Resources/core/icons/patch-check.svg
+
+ hash2
+
+ NSaN5+55k7B+tEYF4uOWYr/29yiy0nGVVH/Xv7F3Pps=
+
+
+ Resources/core/icons/patch-exclamation-fill.svg
+
+ hash2
+
+ TBXuDxvJwTwU2teIXFLg0vvtLuacayXc06kvvJjrcqo=
+
+
+ Resources/core/icons/patch-exclamation.svg
+
+ hash2
+
+ y3b+yLkXwzQr1jE6a3J7F5X3rF2DVpl7PkxmFo0yHC8=
+
+
+ Resources/core/icons/patch-minus-fill.svg
+
+ hash2
+
+ cm+oDuXv0yUjqoUN6sU2f1srmNDTJlkqPAJnKypPJxU=
+
+
+ Resources/core/icons/patch-minus.svg
+
+ hash2
+
+ d6uKVqmwmW27ux8DjchqQR0U3cMNSoFlE14r0SK1zTg=
+
+
+ Resources/core/icons/patch-plus-fill.svg
+
+ hash2
+
+ KwOSjGxTkbw1yYyH4+7WPGl80VaQDL9zjMKvmUpBHuE=
+
+
+ Resources/core/icons/patch-plus.svg
+
+ hash2
+
+ KFhym3h/iX+ipEO+JMsziJDJCe/M5Fetzgr+KumAu0g=
+
+
+ Resources/core/icons/patch-question-fill.svg
+
+ hash2
+
+ BQIi+R8AG5eh4AZm5J9TWCaRtFgS43tJzSZ/fphav+A=
+
+
+ Resources/core/icons/patch-question.svg
+
+ hash2
+
+ 6AwTBMsCr7dlDTy083dzmlZIoOhJjvNTV4fMyJry5zQ=
+
+
+ Resources/core/icons/pause-btn-fill.svg
+
+ hash2
+
+ iWtw6GbLUNKuXWoopCOjsBMNWqsuncWSmDMOT1KMPPo=
+
+
+ Resources/core/icons/pause-btn.svg
+
+ hash2
+
+ cqjYB0G01Rw/qd9H279GJ9MuMHQtcSG1n2ryh7Imq6s=
+
+
+ Resources/core/icons/pause-circle-fill.svg
+
+ hash2
+
+ N2AK4WjSVRCzzdiN/RQf6Tzsf38EJpZY9+stHyfOqfk=
+
+
+ Resources/core/icons/pause-circle.svg
+
+ hash2
+
+ AgsXiRdqSeA7CM9xsn3e/+0MjiEkM1OxhRpits6GX1Q=
+
+
+ Resources/core/icons/pause-fill.svg
+
+ hash2
+
+ nOhF615V8GZY2ZO9+4drJP0Hz7GvVuVGva6I7+/vSVw=
+
+
+ Resources/core/icons/pause.svg
+
+ hash2
+
+ zWbrkM1uVC9C7jWlbkkvF8DzS+A0KQZHdDuHLd6j7/I=
+
+
+ Resources/core/icons/paypal.svg
+
+ hash2
+
+ eUbP43qWgx76FHPtCgCMIFXFJ3Eo1OCxoMJYjnvIzQQ=
+
+
+ Resources/core/icons/pc-display-horizontal.svg
+
+ hash2
+
+ IUEBsZfLld52BWUUvBrPQ3ljuOhFkbbv8fZENK6ZL7E=
+
+
+ Resources/core/icons/pc-display.svg
+
+ hash2
+
+ RZhVxTiFk18+jJoXqFN7wV7lj5tob2KYHQZ7AXJrQQQ=
+
+
+ Resources/core/icons/pc-horizontal.svg
+
+ hash2
+
+ r1cAxm50RFVkV5577NpEy6Ij0CaCJxHUSZYXkOPS8fQ=
+
+
+ Resources/core/icons/pc.svg
+
+ hash2
+
+ n56giTOmt9wEha+zr6wHvFcIxGbiFgRAKxocCHk8JE8=
+
+
+ Resources/core/icons/pci-card-network.svg
+
+ hash2
+
+ L3MoCaZH+xIUkRPTbi6QMfv+SfZgsc/8r8WTVm1Dpyw=
+
+
+ Resources/core/icons/pci-card-sound.svg
+
+ hash2
+
+ bXKlSbzarjCiQ0aTXwjX+WK3sHl2oKdhHhPEeD1wd/0=
+
+
+ Resources/core/icons/pci-card.svg
+
+ hash2
+
+ oR3l2PqdztyttlrCLM+E9b1XRD3KG9vzqdYO80svUTU=
+
+
+ Resources/core/icons/peace-fill.svg
+
+ hash2
+
+ FTNxaKFMhdPb5Khrk8M0+NUaQsg5imEEOpinH3TM4XI=
+
+
+ Resources/core/icons/peace.svg
+
+ hash2
+
+ 8vAGcOULeMjD/PzvXLjPF2NQYp6B9wNwsPOYjZ1PcJ0=
+
+
+ Resources/core/icons/pen-fill.svg
+
+ hash2
+
+ LmKSFSZHMnrzl0GEmxJoZd7R+W0jyPH8J78YYqd6xJs=
+
+
+ Resources/core/icons/pen.svg
+
+ hash2
+
+ YCe6t2QauJcM6TcOtTLVQfjbmApcBiyk5+P0m8DTnCw=
+
+
+ Resources/core/icons/pencil-fill.svg
+
+ hash2
+
+ jAoYt66do0UchNng6ImNmFtcOB630BS9Pfuuib4LkKU=
+
+
+ Resources/core/icons/pencil-square.svg
+
+ hash2
+
+ Ia9nQW4efXxcCheSVdUdA+W8Fc5Yt6vlNk4sFuSNfEc=
+
+
+ Resources/core/icons/pencil.svg
+
+ hash2
+
+ 3xm6yRSg1xpZoZsvCxPZSHH+CWaBNfMMrD52ocOcVxI=
+
+
+ Resources/core/icons/pentagon-fill.svg
+
+ hash2
+
+ mm0Y9rqNuSQRRwTeFsf3nIqoHnn3ZI+9PVSYmVz1DhQ=
+
+
+ Resources/core/icons/pentagon-half.svg
+
+ hash2
+
+ +LuYC9S/wlkRv+8xoTiccbFRox4Xc+2DmiJo4q7faVg=
+
+
+ Resources/core/icons/pentagon.svg
+
+ hash2
+
+ uqGEdJPLatdn05MW+44Qx7DFbULA3kMLIrFDucLBz1I=
+
+
+ Resources/core/icons/people-fill.svg
+
+ hash2
+
+ pb87Dzz9UQYq4MHP5c+inXajrg96V7EOpcXd/9Zy5eg=
+
+
+ Resources/core/icons/people.svg
+
+ hash2
+
+ XGT4Kc2LxR6H5eSxDY6sGVGRvIQFu1uYb+nKpLZwBBg=
+
+
+ Resources/core/icons/percent.svg
+
+ hash2
+
+ bt8i0VxZvAKURHOfGUEZNtdB9QclSgV+a69FsBmb62Q=
+
+
+ Resources/core/icons/perplexity.svg
+
+ hash2
+
+ hKmcVit33+1DD4fOwmhwpwe9v/u58RIe8y7A45h0nLg=
+
+
+ Resources/core/icons/person-add.svg
+
+ hash2
+
+ qrcoL3NFKUiSaSqLyFYT7rxPhWRey2ztR2Yh8F/8rzo=
+
+
+ Resources/core/icons/person-arms-up.svg
+
+ hash2
+
+ JrDEwXPLQX2ii1V7aUvXY9j3oXEeB0kAUCTjYO17Mf8=
+
+
+ Resources/core/icons/person-badge-fill.svg
+
+ hash2
+
+ ZxNL27MzOQ2c3A5YSzxczRWhtCZ5csn36uDxB+Zr5K4=
+
+
+ Resources/core/icons/person-badge.svg
+
+ hash2
+
+ /l2EbzgIrOlilUvfiK+vAR3/YAyLsFOUqa7PW8V/OAM=
+
+
+ Resources/core/icons/person-bounding-box.svg
+
+ hash2
+
+ 6xZaYpIkuIybw7zM7IKufkOirBd890srbauDSuj8L68=
+
+
+ Resources/core/icons/person-check-fill.svg
+
+ hash2
+
+ cYQJmMRjxERq0/iNjY0l/huRuSYhYh4Y2sf1z/tQ0lQ=
+
+
+ Resources/core/icons/person-check.svg
+
+ hash2
+
+ V1bqCs6ug3FOmild9kbfc33srIi5m9ReXyjyVMMaSe8=
+
+
+ Resources/core/icons/person-circle.svg
+
+ hash2
+
+ ZwT/QkgKlmUwSM439jSV2hz/ezUu/r0b+HBXZu9HJEc=
+
+
+ Resources/core/icons/person-dash-fill.svg
+
+ hash2
+
+ UQjctUdungu/ZIBXHwZJby7wmeA1jTSn9zyBv+Up9MQ=
+
+
+ Resources/core/icons/person-dash.svg
+
+ hash2
+
+ YdCBfOFtj49s74wL0Peg5i8fW27U3EWr3YLUmV43ars=
+
+
+ Resources/core/icons/person-down.svg
+
+ hash2
+
+ PaT8++6R5bP17Ft6Ghy5arzb/E70kvVZ18o+6NsDd68=
+
+
+ Resources/core/icons/person-exclamation.svg
+
+ hash2
+
+ cLwtoyDQWdMvpkjwbgZG5LkstsnB0RIWZtf4DsQf8Eo=
+
+
+ Resources/core/icons/person-fill-add.svg
+
+ hash2
+
+ KgkgYUd0gY8jiqyunkxCbjc+Zxa6WbJ1k6OXFGdwHug=
+
+
+ Resources/core/icons/person-fill-check.svg
+
+ hash2
+
+ d6+LR9v5yrjXWGitth76TKcNwOjq1dhiTob3lrbC1zw=
+
+
+ Resources/core/icons/person-fill-dash.svg
+
+ hash2
+
+ 93pHWuJtlwc4re3tJmtEJFaFByqOctr0Rq/52V0Oe3Q=
+
+
+ Resources/core/icons/person-fill-down.svg
+
+ hash2
+
+ 1/9bFwBmNnHWy5iyBZHsgNxys8Q2akwatWtVYRbJoIY=
+
+
+ Resources/core/icons/person-fill-exclamation.svg
+
+ hash2
+
+ FtCQyJfnG+vEuB13JvxZCBFN726b/mnZE4Vi3Uh22/w=
+
+
+ Resources/core/icons/person-fill-gear.svg
+
+ hash2
+
+ l9AeypOpOZ7F34yky2B8FvPvjt0xjMZg5uxKLJ16jW8=
+
+
+ Resources/core/icons/person-fill-lock.svg
+
+ hash2
+
+ TFya1C5lT7SxObWdijKCpwUYslurAubl01o6s4IHP1s=
+
+
+ Resources/core/icons/person-fill-slash.svg
+
+ hash2
+
+ H5FM9C6+nivNwJ6buR7SxBKuxZNJxhIMlCCHQqcD4Wk=
+
+
+ Resources/core/icons/person-fill-up.svg
+
+ hash2
+
+ 6KNVXiEqDSBsleRprvO+hmY826c/u9SkfD2IXysiXu4=
+
+
+ Resources/core/icons/person-fill-x.svg
+
+ hash2
+
+ ymcwV+fHp7bcFfKRoTtZfa9KMGNT49oNti7jXy4/rzY=
+
+
+ Resources/core/icons/person-fill.svg
+
+ hash2
+
+ qiZMf1iC2AsSSRviicvWwA+mWFF0M9w+MzAuIpFqplE=
+
+
+ Resources/core/icons/person-gear.svg
+
+ hash2
+
+ eDmr8Cl1uvAaXfa5hvXTHX57HLATC8bh7ITs1ETA72Q=
+
+
+ Resources/core/icons/person-heart.svg
+
+ hash2
+
+ eNg/cDTp2mfU6GHX/cU6LTQqs4cN2LwZjAGBij3emmo=
+
+
+ Resources/core/icons/person-hearts.svg
+
+ hash2
+
+ EgGrH0+31uQMS5NijPNGsNCepW7cH4d2uHr+0aV7fuo=
+
+
+ Resources/core/icons/person-lines-fill.svg
+
+ hash2
+
+ +Y0G64BFoUhR7lOF5qXYgDri6qI35wVsIZuKN0F3i3w=
+
+
+ Resources/core/icons/person-lock.svg
+
+ hash2
+
+ 8Axi7WwTwCNlgT8NzrTG7LfQEKKyQb0ZmTsTLRLd3bU=
+
+
+ Resources/core/icons/person-plus-fill.svg
+
+ hash2
+
+ 57ym4ns394gQ7w2DVTXKkIBNA079LrbtXjsXHwQJCrQ=
+
+
+ Resources/core/icons/person-plus.svg
+
+ hash2
+
+ 8ODEMz2lHlhXxE33rNwgb41vKeOVSbktslZXVEgWSp8=
+
+
+ Resources/core/icons/person-raised-hand.svg
+
+ hash2
+
+ zq7ATvzIHbc0g1wyUYWrIX0TwdQ/ZayiUQ/IAUoqkUs=
+
+
+ Resources/core/icons/person-rolodex.svg
+
+ hash2
+
+ jV45MDo4WwtxIHyFsIEhj70Y9flEhCs6LyHrxr9XBMo=
+
+
+ Resources/core/icons/person-slash.svg
+
+ hash2
+
+ FwkLaCkCc7ch2Ogang9cBwMJw6KMFXjGcFP2GxIAU+s=
+
+
+ Resources/core/icons/person-square.svg
+
+ hash2
+
+ 4KaVoAL/HKJNrntKGZSo1peHCuuuFAutJ4flmvrmq4k=
+
+
+ Resources/core/icons/person-standing-dress.svg
+
+ hash2
+
+ XkZKtabrpMLdk8WbQJsfeFrBe36HWwQQz+AdQD7KKSc=
+
+
+ Resources/core/icons/person-standing.svg
+
+ hash2
+
+ UCkYZlH5ALmQRZWmAllzvqJAxnqWN/xEVy7lWonAdh0=
+
+
+ Resources/core/icons/person-up.svg
+
+ hash2
+
+ njIn7RUHJeANR5qbeqs3RuUuv+c4HRY3LctPqpEX9EA=
+
+
+ Resources/core/icons/person-vcard-fill.svg
+
+ hash2
+
+ 2VgJ2AR3r3gQJXeA/+u8b7o4jFVAHbF88KQikGmj3Gs=
+
+
+ Resources/core/icons/person-vcard.svg
+
+ hash2
+
+ nIEy4Wj3qNgXTrrE2CIebVLyBT2dDXHB8niNkw6nMeg=
+
+
+ Resources/core/icons/person-video.svg
+
+ hash2
+
+ ARqgv7O5PMHptitj9Xx+c2mSUX4B06ijUqWMRIV+Bhs=
+
+
+ Resources/core/icons/person-video2.svg
+
+ hash2
+
+ KuVz4fNG4Vhu1n/AxBvT3+6epnXDnQf8iJ46wtPGTCY=
+
+
+ Resources/core/icons/person-video3.svg
+
+ hash2
+
+ W/uphTrByhq/E/tbgfuWBPnjjR2ry6cYDh1IB4nYfgY=
+
+
+ Resources/core/icons/person-walking.svg
+
+ hash2
+
+ vXpW72VOQnlC65e2OaxtIeFd8xSAgSBz6dMckJdGAz8=
+
+
+ Resources/core/icons/person-wheelchair.svg
+
+ hash2
+
+ 5fPUExa6U+NOtuCqW+h1ookcQvkmRm3v+CNMjnayeYI=
+
+
+ Resources/core/icons/person-workspace.svg
+
+ hash2
+
+ akEPb1uy8THRM5ie5Gd7enT0Pqf47AO+qY9LTKtV0U4=
+
+
+ Resources/core/icons/person-x-fill.svg
+
+ hash2
+
+ tmJRWfGSVyhPuZGQyO2zhEUQkqt7htex/ofLg8OvwyI=
+
+
+ Resources/core/icons/person-x.svg
+
+ hash2
+
+ gJVEzYThrqQ9a1vWIENpzbKbEBC7o0s7s+5PTUfdlK0=
+
+
+ Resources/core/icons/person.svg
+
+ hash2
+
+ 02k7s0dCVDB7oWGfL0h3Mjdpt8cYbOxVC7ZZKoFhfno=
+
+
+ Resources/core/icons/phone-fill.svg
+
+ hash2
+
+ OYjBn9X5GBdEd4Gq8ThWjdPX0hCaniUamIkTZgRx1vE=
+
+
+ Resources/core/icons/phone-flip.svg
+
+ hash2
+
+ CgIQMpS/dUiIkdztRWIB35UlcKk6buq6HbiVkEZkl+o=
+
+
+ Resources/core/icons/phone-landscape-fill.svg
+
+ hash2
+
+ U4VtvkZJs6Xu63RjEomRvxxa39b2SduYfBAWsQZCa6U=
+
+
+ Resources/core/icons/phone-landscape.svg
+
+ hash2
+
+ EBDQsUotbGfzwqUN+T1INYOg9ryKq/IeEd97fT8uFLs=
+
+
+ Resources/core/icons/phone-vibrate-fill.svg
+
+ hash2
+
+ 02yQAvif71bJ9J6SAXeh6mkjzK7F4NS2qpFiCetK798=
+
+
+ Resources/core/icons/phone-vibrate.svg
+
+ hash2
+
+ TFTmsmph50pH5svuZ0dRwGX0nJwxdOhFLyjfY8QffPg=
+
+
+ Resources/core/icons/phone.svg
+
+ hash2
+
+ vxjEwy6ja3A3hNruMduPzG2ctlekx3qxJv0g+EloSKI=
+
+
+ Resources/core/icons/pie-chart-fill.svg
+
+ hash2
+
+ ew5Cc/m8/jNv7MeYXxyVS90VEoM2LmbfhCesV74jTPg=
+
+
+ Resources/core/icons/pie-chart.svg
+
+ hash2
+
+ lJvHe2Evp6NW5KIF3FNrHDF3gZ9zCCJjhQWhUO0yamI=
+
+
+ Resources/core/icons/piggy-bank-fill.svg
+
+ hash2
+
+ jtp4Lcr6mNkgWZy11AF5ZacbCVaImRVZyLqot9Ux91s=
+
+
+ Resources/core/icons/piggy-bank.svg
+
+ hash2
+
+ oiguLPY8HfRUtNCUzAUOFFvp213HCNwhl6zH9Oi8jCg=
+
+
+ Resources/core/icons/pin-angle-fill.svg
+
+ hash2
+
+ 6SwsqpseBgFAmapEWD86YU1uWj0mDQ678zMp02sw8Tk=
+
+
+ Resources/core/icons/pin-angle.svg
+
+ hash2
+
+ YGn9olP5BMlwafHYIKwNbWt0j9UoMIUfiqFOrRBgLns=
+
+
+ Resources/core/icons/pin-fill.svg
+
+ hash2
+
+ BTG4uoP6rzYJ2yLdiAA3Wzl0IiXbErwBa7q1SBR3vhg=
+
+
+ Resources/core/icons/pin-map-fill.svg
+
+ hash2
+
+ bONq2Ce99HcXTU0HQHeCZVrLDe5JVuaLuoH/m1kCme0=
+
+
+ Resources/core/icons/pin-map.svg
+
+ hash2
+
+ RSfQcRMftxWWfVPPd8QJoEIpb+C6LZP6SMIZQOc+7mk=
+
+
+ Resources/core/icons/pin.svg
+
+ hash2
+
+ 45OZto4tjzUQNQvl67DD+bKAbRLsmIciUGV02S7BsJI=
+
+
+ Resources/core/icons/pinterest.svg
+
+ hash2
+
+ Mh+1VH75OmRYF604TbNP+iY3K6jFWAQYVRiOE8G9wzw=
+
+
+ Resources/core/icons/pip-fill.svg
+
+ hash2
+
+ ilVVhW3bP+WTvxx8eX8aRvb0y3IBLXrcwkxYDTtpYNo=
+
+
+ Resources/core/icons/pip.svg
+
+ hash2
+
+ U9Ya5DhYjztwpFrPDi/n+7mq79yMXNFQeshWkY2U+j0=
+
+
+ Resources/core/icons/play-btn-fill.svg
+
+ hash2
+
+ LzTTUF9OcEQQC9zZn0dYzYkIA0vwVANVQC0S9Jkou8Q=
+
+
+ Resources/core/icons/play-btn.svg
+
+ hash2
+
+ e7zEZdBhf1MQHCLFVQnAoUg3JI8J18cLvEgbqKGpIo8=
+
+
+ Resources/core/icons/play-circle-fill.svg
+
+ hash2
+
+ 77cuMKZZA0DBiQ01KUhvyqrbOjFxrWiN/Y/02SsTMk0=
+
+
+ Resources/core/icons/play-circle.svg
+
+ hash2
+
+ wntBPEdzc3pBe4w9E0ZnjYu4pvqbsEIN+sP/LRPobUo=
+
+
+ Resources/core/icons/play-fill.svg
+
+ hash2
+
+ PWpMJUtwuPElwJ9WFC6mX5wICfQqEm/F77BuWKa3zLU=
+
+
+ Resources/core/icons/play.svg
+
+ hash2
+
+ OvDlXdl4uyckJmzltYLly8ZR+kWno7ArplS4Cq2N4+s=
+
+
+ Resources/core/icons/playstation.svg
+
+ hash2
+
+ IQ3ldsy9u5YPZxLqgkm45a7k5Ly3g36ZUjdDwrWgnmY=
+
+
+ Resources/core/icons/plug-fill.svg
+
+ hash2
+
+ zGBk4aeezR60OyzVfivjwXhXqp0vG1sgoYGPYzCL5WU=
+
+
+ Resources/core/icons/plug.svg
+
+ hash2
+
+ U6VYw9L/WwqYrddsNp0htp2/UhwJQmV/8nZ/AYhzxPM=
+
+
+ Resources/core/icons/plugin.svg
+
+ hash2
+
+ 8L9DwkgSZNVuf4/Go3aLVLr5CaQY3PiNNDhRGE6iOOY=
+
+
+ Resources/core/icons/plus-circle-dotted.svg
+
+ hash2
+
+ tk4AzeIMmcqk8MpzHS+mvNPjKKO+hugJJVdXr0ePqx8=
+
+
+ Resources/core/icons/plus-circle-fill.svg
+
+ hash2
+
+ uxWKMRdtrYMOhJq9v+RJqMcgE/FtUfhsObUl3nkuamY=
+
+
+ Resources/core/icons/plus-circle.svg
+
+ hash2
+
+ opWfAJzlBsRHPBXnES8lYgUJEJxZ7TzE1z91jCD+Q58=
+
+
+ Resources/core/icons/plus-lg.svg
+
+ hash2
+
+ lDJdI43Bzk8unmHYovmdWVcpZ6VVUwcu0Of/PMtzZAw=
+
+
+ Resources/core/icons/plus-slash-minus.svg
+
+ hash2
+
+ GmEEzRoE69qZTGZdNddQDglsvMtEkEwbGDzEeOc7zcE=
+
+
+ Resources/core/icons/plus-square-dotted.svg
+
+ hash2
+
+ 8o0Z3yZ5PAfBCmGN7EIQpSqSeJv+b0GXLI8clj+Bk44=
+
+
+ Resources/core/icons/plus-square-fill.svg
+
+ hash2
+
+ XglAMZy9Uk68FcZhIa4OY1hM1aO2L5e+QnW/J1rx5SU=
+
+
+ Resources/core/icons/plus-square.svg
+
+ hash2
+
+ qyd6N9iu/K6cFyPgMc/lTCo1+vJMqujnEhj/xssy/B8=
+
+
+ Resources/core/icons/plus.svg
+
+ hash2
+
+ QM9QlieelCVLbuTgHfoqNmoX5wmOGq0QhT3YL1kB2ao=
+
+
+ Resources/core/icons/postage-fill.svg
+
+ hash2
+
+ htXrraT6luXHvSPXUVODOg9E1ofQuPphsojimpyjd54=
+
+
+ Resources/core/icons/postage-heart-fill.svg
+
+ hash2
+
+ f6HvAfcLPd5POjLRMoskKvJFM0JlY7o3XICZudXGp64=
+
+
+ Resources/core/icons/postage-heart.svg
+
+ hash2
+
+ y/e9Wec0hwotknV2bXAE3FQpq4107buW+dbfdky5cwk=
+
+
+ Resources/core/icons/postage.svg
+
+ hash2
+
+ 96DLTfbEXO0S7VtBkWohvwpf6bBDuDW7zh+yQNhTAc8=
+
+
+ Resources/core/icons/postcard-fill.svg
+
+ hash2
+
+ +bZEFh7F+qBZNNnJey1eLbFboNGqxh6aYUjRlHZjoE0=
+
+
+ Resources/core/icons/postcard-heart-fill.svg
+
+ hash2
+
+ 5ALCNhkoNPms1OQwxh1NmW7NEikZMFyMFB6MWT0Pr3U=
+
+
+ Resources/core/icons/postcard-heart.svg
+
+ hash2
+
+ 1xwxqVhCu7YOzaNLXWBmmsRLBwplhCGIgi1YCEbQSzw=
+
+
+ Resources/core/icons/postcard.svg
+
+ hash2
+
+ zPnDawBzsNPnJkiMsW951QQX/NU8R1CTNwvcg7whdkw=
+
+
+ Resources/core/icons/power.svg
+
+ hash2
+
+ bYt9IKSEyef+6v0Qg5/FokKg1u2mwaqIFd/4wHxdxyU=
+
+
+ Resources/core/icons/prescription.svg
+
+ hash2
+
+ gNQfUXa5kDnYvBtOTZ+F88Cqh7mqqFFdcKx32tglPSQ=
+
+
+ Resources/core/icons/prescription2.svg
+
+ hash2
+
+ QhYJSspY/0D9y5sdmb+aSHpN8ym1jtXP76a57pPWJFM=
+
+
+ Resources/core/icons/printer-fill.svg
+
+ hash2
+
+ yGB+v8w9xAUBIPhcqKMhBv7uJuTYZI1mrcqxNCdAdm4=
+
+
+ Resources/core/icons/printer.svg
+
+ hash2
+
+ R/HdtT4YFI/c7CMS7n9H8cmM+Rg1fGBQ4sCeMuhF+II=
+
+
+ Resources/core/icons/projector-fill.svg
+
+ hash2
+
+ bF34MI9dZvGpLPYSEUPc+ZBHOV/nbqtHqZtOg3xFmH4=
+
+
+ Resources/core/icons/projector.svg
+
+ hash2
+
+ d9ulTR/rzbop5OzI86WolnxG9B89etXblqDhCntvHHY=
+
+
+ Resources/core/icons/puzzle-fill.svg
+
+ hash2
+
+ ibkNTDMg9vqpmqD+CWRqb0K2CM/p2LgEZ589uD3osz8=
+
+
+ Resources/core/icons/puzzle.svg
+
+ hash2
+
+ DXNWcslr8eNxlyxfOrLhWezUCLhf1WKQc6j9uXfjp4w=
+
+
+ Resources/core/icons/qr-code-scan.svg
+
+ hash2
+
+ uNQieIakoFkjjRrUc1ruYJwMhrEdaL0C8y/yoTovITI=
+
+
+ Resources/core/icons/qr-code.svg
+
+ hash2
+
+ kp23zCrOB3c3Da0/7Lo6ReIEeBTcD0y+tNevAJoRoHg=
+
+
+ Resources/core/icons/question-circle-fill.svg
+
+ hash2
+
+ UVt1pUGgHoWOFmBOJoQOxBmClrS2zS0e/D9K5rRLR/0=
+
+
+ Resources/core/icons/question-circle.svg
+
+ hash2
+
+ iNkIqNAW3pGOZm7FOWt4q+V2XhXxwbKLjQ42Zh1mioA=
+
+
+ Resources/core/icons/question-diamond-fill.svg
+
+ hash2
+
+ 2Sj8jwsLFwusP2J05Kqv4rT3PAJqvA/hBhJvgD5Lc+Y=
+
+
+ Resources/core/icons/question-diamond.svg
+
+ hash2
+
+ +TGcmlN/1Nm5au8oap6+PQ7Rv0t84zjei3gi6YGWxI0=
+
+
+ Resources/core/icons/question-lg.svg
+
+ hash2
+
+ fijQldedPzx/IHxuwC/sCpUzf+TVCZhkfXNlbRfCTZk=
+
+
+ Resources/core/icons/question-octagon-fill.svg
+
+ hash2
+
+ BnAp+x26Yyr0i7OJKc88Q+1pMHwpz3uQlD6OjNO8/3s=
+
+
+ Resources/core/icons/question-octagon.svg
+
+ hash2
+
+ WOAkyIPSGqehMH7pOJHJOa3JCRIKIBkfRGLa++YRDw8=
+
+
+ Resources/core/icons/question-square-fill.svg
+
+ hash2
+
+ O8dTtKb2/FY84GpBgaguJUtDZGgGAEbciM5fKmCXTpE=
+
+
+ Resources/core/icons/question-square.svg
+
+ hash2
+
+ wv7IIQLuOglFDXA3eIpHz9MvtpHdrcGEGvo4jG6yAqY=
+
+
+ Resources/core/icons/question.svg
+
+ hash2
+
+ OlundJV6sE7UjWOR/bKs3KJ3EvLogDO64lF9mXscpj4=
+
+
+ Resources/core/icons/quora.svg
+
+ hash2
+
+ /YI0fAPfd7B4SAGHv+InAKagDazBCIGeUn5vROFqr6k=
+
+
+ Resources/core/icons/quote.svg
+
+ hash2
+
+ PnqGAbQZjo76/Y8nE/Vu1tR7Ac9+FJDbGd++9LH8s98=
+
+
+ Resources/core/icons/r-circle-fill.svg
+
+ hash2
+
+ FLmBtqtqvoyMClKf8Sk4NY48oMY4MjQ1PVE5l08CEPM=
+
+
+ Resources/core/icons/r-circle.svg
+
+ hash2
+
+ pH8+fiCvgCKfQBYRPxq9LaVbaq4jFjeSc6SZ34k+C1M=
+
+
+ Resources/core/icons/r-square-fill.svg
+
+ hash2
+
+ fvhMB+afO9vnbiDH7IXDa5DZ3H2HNRoyBwryX4ZzbP8=
+
+
+ Resources/core/icons/r-square.svg
+
+ hash2
+
+ OeuTLfIyM6JgfqvEOfeVPnQBVj1bfgIcUVUTAhoEZL4=
+
+
+ Resources/core/icons/radar.svg
+
+ hash2
+
+ rfGhFUdfxrPnHZeEdpHUJ38L0LXISP8eLk2GhSA/UcQ=
+
+
+ Resources/core/icons/radioactive.svg
+
+ hash2
+
+ tdBzI06zO0xMRPADBqCwHmzzSw6F28d6JUQ5AQ1KIcY=
+
+
+ Resources/core/icons/rainbow.svg
+
+ hash2
+
+ Z/shWIpVEYktQcg39Z96O53ZUQzEaT+/ZmC9IGOkDb8=
+
+
+ Resources/core/icons/receipt-cutoff.svg
+
+ hash2
+
+ Nrt6hekCrx1n0cWDCTma3pelKxgqzaUR4GjvrebVC18=
+
+
+ Resources/core/icons/receipt.svg
+
+ hash2
+
+ 3OIyMfcwf2ceCC5NSiIaKVALGTDi05KnGrBu3r6mlIc=
+
+
+ Resources/core/icons/reception-0.svg
+
+ hash2
+
+ qo3yN5MMxqDcVnARQzhFA7eK0Y8qTD89r5WcpTOVtOo=
+
+
+ Resources/core/icons/reception-1.svg
+
+ hash2
+
+ s0Z5XH4HbUVAdr1suLBdhw6Cc/YVoXHHupp4h7CWIZE=
+
+
+ Resources/core/icons/reception-2.svg
+
+ hash2
+
+ ZkMuPA9WRhyRqYkZL51WpuT3FcLXlrsdeCH6OKf1nN4=
+
+
+ Resources/core/icons/reception-3.svg
+
+ hash2
+
+ hVle2Uellv8NsmMYk6v1RIVaUMjTfTPl+YRRbWNVewY=
+
+
+ Resources/core/icons/reception-4.svg
+
+ hash2
+
+ kPC4jIhpNwPlQLR4oTJTr96A6Dw584go5KNQfuVWVzo=
+
+
+ Resources/core/icons/record-btn-fill.svg
+
+ hash2
+
+ tHU1+PGtt/QHseGU1Lo6BuZ0TXCsqoGfRmJdj6swMVo=
+
+
+ Resources/core/icons/record-btn.svg
+
+ hash2
+
+ OV6dmUjU2bX/eSAWYXOtgyp0pV+d+BDSubnY31S0kLo=
+
+
+ Resources/core/icons/record-circle-fill.svg
+
+ hash2
+
+ 3IRdCwq+uX9WoQx0bEbnMQeSUbFZQsUmnPCGUNLr3ro=
+
+
+ Resources/core/icons/record-circle.svg
+
+ hash2
+
+ 1hUJsB3jtiVNnIx9rsnkBPGgOC6aad3KJn5JqDPfWCU=
+
+
+ Resources/core/icons/record-fill.svg
+
+ hash2
+
+ ApdDo2Ph5UNCtmSC7d5iprTYYR5oUTXczaMubkPBX3U=
+
+
+ Resources/core/icons/record.svg
+
+ hash2
+
+ 620DhDq54olayf0Q3drz8F7CUzmFcMxInLuCvPMn1OQ=
+
+
+ Resources/core/icons/record2-fill.svg
+
+ hash2
+
+ /7w4MWsW7Weri9xnf647J1pQJ9w+kaABqPJld1B3Fug=
+
+
+ Resources/core/icons/record2.svg
+
+ hash2
+
+ l2zT4ethZ7s3agFgrkw4K2m1GC6+XT8xKWHqS50C1Wc=
+
+
+ Resources/core/icons/recycle.svg
+
+ hash2
+
+ +jpT1s71J6e16re/+h9pJvCNTPhwuqcgfcfOJP7PRAI=
+
+
+ Resources/core/icons/reddit.svg
+
+ hash2
+
+ /m0M3rw1H4R443dPNVGIijzf1sI4pW6I6KbgzCcmssw=
+
+
+ Resources/core/icons/regex.svg
+
+ hash2
+
+ fEmb4KfiYLVYKSf7w0ZGZ1INaj7Zhyvwg2YUJGgupn0=
+
+
+ Resources/core/icons/repeat-1.svg
+
+ hash2
+
+ s6FtzJEhpDwEVtefNiQ0DSUXuvKpFVYcQgKVuFltZ1I=
+
+
+ Resources/core/icons/repeat.svg
+
+ hash2
+
+ XRk7KFcZzxPTd8tzLsVtJqQ/GjC9R1uYL7LGmIGIGE4=
+
+
+ Resources/core/icons/reply-all-fill.svg
+
+ hash2
+
+ 3sqQ+N4lZdqkRkvQjZZ1lnZpB2XmqK4jdh9e07jwxRU=
+
+
+ Resources/core/icons/reply-all.svg
+
+ hash2
+
+ bQBNQQNvwJxW9auf9cbDOkLq2NWTFEDCkhEMNll6U3U=
+
+
+ Resources/core/icons/reply-fill.svg
+
+ hash2
+
+ iAW2Ykj89cha+0hcppI3f+jcCU+Ln4EtNU7PtjteTMQ=
+
+
+ Resources/core/icons/reply.svg
+
+ hash2
+
+ XSMuTR9HgCvo6KBBlEzgmRf6+84+tyhbiOIgY8DJ5LI=
+
+
+ Resources/core/icons/rewind-btn-fill.svg
+
+ hash2
+
+ Dz2l213o8A1+oGbJhRruAj9jT77fUUU0xRmu7/+mzr0=
+
+
+ Resources/core/icons/rewind-btn.svg
+
+ hash2
+
+ 5kVHHK9ZPTdhjzZHi8+UOeG8pTkrTmwMpd57k7xiCR8=
+
+
+ Resources/core/icons/rewind-circle-fill.svg
+
+ hash2
+
+ yW+CuABJT8lq5Zdnx1shXM0iLAavwSvj9/C1oECd/wk=
+
+
+ Resources/core/icons/rewind-circle.svg
+
+ hash2
+
+ /M/Wq0VB+yf7AQLc5Dpm+DKey/GMjiaJGGFwqR7e+c0=
+
+
+ Resources/core/icons/rewind-fill.svg
+
+ hash2
+
+ Zs+CqNiUAid32xvrd9zH47n/b19Aw3W/UTWiJLnjeLY=
+
+
+ Resources/core/icons/rewind.svg
+
+ hash2
+
+ TkDGROce+/Ntv7W/boiJr6FX3tHDlL7+I7RbaJKQTHc=
+
+
+ Resources/core/icons/robot.svg
+
+ hash2
+
+ N3WH1wB+mGZMTP9atHDuPwCkXBBnmfvzHXS+6lagj8s=
+
+
+ Resources/core/icons/rocket-fill.svg
+
+ hash2
+
+ ik5NUheONyiRs7tcxr8I1QjbcDcT4pTkzw+E2yH16Go=
+
+
+ Resources/core/icons/rocket-takeoff-fill.svg
+
+ hash2
+
+ qkjXAyLj54SfAxIurIic6Bd4FwwIFOH3ez20df0lMro=
+
+
+ Resources/core/icons/rocket-takeoff.svg
+
+ hash2
+
+ Nqx5YihQ9MqxayXgs1RWUEs6qgZwDNp5SWil988Dh8w=
+
+
+ Resources/core/icons/rocket.svg
+
+ hash2
+
+ Fipc9KicWpwNoGPLyzamk2lo9tk89eXpD/GbNJ3miNk=
+
+
+ Resources/core/icons/router-fill.svg
+
+ hash2
+
+ BEC5AB/DikYFh14+91VEprDYiy2eOMCQ7BAag2mMRXk=
+
+
+ Resources/core/icons/router.svg
+
+ hash2
+
+ xSGunS60kAEqmdjqtd2aNmS6FtmiHT75dHt9p3CviM4=
+
+
+ Resources/core/icons/rss-fill.svg
+
+ hash2
+
+ /5VmUxW6oozrqpY9JS0r05OiVMKUqsg1mDthFYIBWbI=
+
+
+ Resources/core/icons/rss.svg
+
+ hash2
+
+ DeTD5+h0OoV9uhn7ngO3Dv5Oxmwq4+qPeAnFT5yQl44=
+
+
+ Resources/core/icons/rulers.svg
+
+ hash2
+
+ BEPZtW6eQ4IkcDlkmiSdtbUkAL9ZXJXYLv2wpdzvODs=
+
+
+ Resources/core/icons/safe-fill.svg
+
+ hash2
+
+ 9Iyes9CtREV980GDLujp4z4mvj6zv2CDuqjHzeUbdlI=
+
+
+ Resources/core/icons/safe.svg
+
+ hash2
+
+ MUnGu75xsfi6zxirtr0a+jyJHhXvApLKfCofrcujcW0=
+
+
+ Resources/core/icons/safe2-fill.svg
+
+ hash2
+
+ Zd+QbcYVBkbtkW8EJcBKK1I9Ex6ZrfrhvLu0YWAqrKM=
+
+
+ Resources/core/icons/safe2.svg
+
+ hash2
+
+ XJOfeGwcGN2BVO7E+N1nDq6S3TpNwGnSpC1diqoeZUw=
+
+
+ Resources/core/icons/save-fill.svg
+
+ hash2
+
+ rCfkvdafXsNnlXVk4hXjJsjdKGVpr8LIfRNrOvRavEw=
+
+
+ Resources/core/icons/save.svg
+
+ hash2
+
+ B4VP4XJwemlW8EFL8HbWKwzAJHwSRmG9SQAIL3n/+OM=
+
+
+ Resources/core/icons/save2-fill.svg
+
+ hash2
+
+ fZzPBP4NqRNsPPgegMmx4N1yNmMORui67fJfCxu8PkU=
+
+
+ Resources/core/icons/save2.svg
+
+ hash2
+
+ 8pccoqy/UyoILH1jnXn4+Dy16QMIh8tosZvbo06ooOs=
+
+
+ Resources/core/icons/scissors.svg
+
+ hash2
+
+ FNxI17c0iUWZcnas/CC+E8nQwMn4gegcjeHJ/wykJ3Q=
+
+
+ Resources/core/icons/scooter.svg
+
+ hash2
+
+ pkOyXSBpPLzAKidqza0Y0vfBRQU8rJedrc1wm8I/Rr0=
+
+
+ Resources/core/icons/screwdriver.svg
+
+ hash2
+
+ qY63bFsz5juxf+UROYbGGuBgNMfkOSKh2zxSaDjIvUw=
+
+
+ Resources/core/icons/sd-card-fill.svg
+
+ hash2
+
+ 3txC5USV6dpQIk5kRfy9k8YJ0RomXurwR/3INiWsWJA=
+
+
+ Resources/core/icons/sd-card.svg
+
+ hash2
+
+ Kpy03BxnKWXBzvrgEnVA0Unt/k8+F+fxul89HmO7dQs=
+
+
+ Resources/core/icons/search-heart-fill.svg
+
+ hash2
+
+ ovhXtk43i9nWG0T+8pJ87U/4DcuxVz6kC3zD5Rj4NzQ=
+
+
+ Resources/core/icons/search-heart.svg
+
+ hash2
+
+ HA1/QgV1zovzznAU+LZhv2BaK9NX3fgfESmgV/BZPxY=
+
+
+ Resources/core/icons/search.svg
+
+ hash2
+
+ jWwowHDs9IsfEKTtZc894o/JuVnGs5PFR0wIGvQOcnA=
+
+
+ Resources/core/icons/segmented-nav.svg
+
+ hash2
+
+ E6w7Hgwom09Ihx4b6CAk2mbWLz98B5rkZl5cjYBYQR8=
+
+
+ Resources/core/icons/send-arrow-down-fill.svg
+
+ hash2
+
+ e41fZoPh/gllEQJqvbd7nuRnXnBRRpOysqYqzCtQp4E=
+
+
+ Resources/core/icons/send-arrow-down.svg
+
+ hash2
+
+ 9nh04JsPSTEXZ4m/ZGdqWzcCUJ41mpofaS1HbsT/9rU=
+
+
+ Resources/core/icons/send-arrow-up-fill.svg
+
+ hash2
+
+ hTJzchNNl1zcEoHdtLEJ5NQiF8S6eGESesLJ62ajD+0=
+
+
+ Resources/core/icons/send-arrow-up.svg
+
+ hash2
+
+ HW8Y986Uzd4z9cPmznuGwdsABDhME5tKuyMR40cqdMs=
+
+
+ Resources/core/icons/send-check-fill.svg
+
+ hash2
+
+ 4zwfRDEgG6xzoCfVimG9dguLyteq5DPJa3Zz4Swkq4w=
+
+
+ Resources/core/icons/send-check.svg
+
+ hash2
+
+ 7hLb0Ormj/B1ByMvfU7gWGktSqPjTrnjT2ZWJPkV0hs=
+
+
+ Resources/core/icons/send-dash-fill.svg
+
+ hash2
+
+ WRiY6Y8HqS8i3WfX/RjdM9QVqbbbrvprq/a/PO7uVz4=
+
+
+ Resources/core/icons/send-dash.svg
+
+ hash2
+
+ hxHNd2WQacq5jPyIlbZ3wuWn+H0cGSYQuK3sdpPoyYw=
+
+
+ Resources/core/icons/send-exclamation-fill.svg
+
+ hash2
+
+ gZuSO4AXwQ+4riPG72lE4s4iurrnYjOaufhysH7IWV8=
+
+
+ Resources/core/icons/send-exclamation.svg
+
+ hash2
+
+ l6Zvu6+WR4bn392iOqKKj74J0LMLvNqrEc+Z5a1kljk=
+
+
+ Resources/core/icons/send-fill.svg
+
+ hash2
+
+ SzRnTleWs61tF7nGz94RW3OQ5zzCCLhf0quPF/RXbfI=
+
+
+ Resources/core/icons/send-plus-fill.svg
+
+ hash2
+
+ Pj3bXrx59EQWDDvhtLjdIvb5qAhwo/XyEQNe0ZkuLy4=
+
+
+ Resources/core/icons/send-plus.svg
+
+ hash2
+
+ ic2j9D8Ce4KW99T8QfhlYC+P6Zyu9oaq2/NP7d0Twfk=
+
+
+ Resources/core/icons/send-slash-fill.svg
+
+ hash2
+
+ eLNcm68Ey7KGbLopIlcGDvMo9sL3WFZvcJtlnzJKyp8=
+
+
+ Resources/core/icons/send-slash.svg
+
+ hash2
+
+ UneTFpK/Jnr15FN0/70RbWPgsBAP3EqmP76FehO4yFQ=
+
+
+ Resources/core/icons/send-x-fill.svg
+
+ hash2
+
+ w4eHcj/dHonBbLR2UmNfsdS0LOOJYbNQIwr+QV4fzDs=
+
+
+ Resources/core/icons/send-x.svg
+
+ hash2
+
+ cbQgGxIphXOoWbxWuJ940RFJ0wHo47u6b9T2gyNGsAs=
+
+
+ Resources/core/icons/send.svg
+
+ hash2
+
+ KPZGKObM1w3Rk8sfPYXUyJ6j6nIQ9XUUNvISlIgJc+A=
+
+
+ Resources/core/icons/server.svg
+
+ hash2
+
+ 8LjRSgt7v2954HQhW10TgRTlNZ9aBDTxe3CeofsDMOY=
+
+
+ Resources/core/icons/shadows.svg
+
+ hash2
+
+ xPI7SSVshMWQToqpu20UInJFcOSzscuDj4nJyPNWWHk=
+
+
+ Resources/core/icons/share-fill.svg
+
+ hash2
+
+ Xe3t/r+qiAkM+Utoyjx9JCnOgfr599j7uaB1cC1v6yw=
+
+
+ Resources/core/icons/share.svg
+
+ hash2
+
+ cnTkWO6lqKbTb5rX7gIOXfWSqmQZ0G7b3J9k49+6IIU=
+
+
+ Resources/core/icons/shield-check.svg
+
+ hash2
+
+ V9WAqwcEl3MSYGvDdIk9GhqsGYnJIlLYH1JnkSAeU4I=
+
+
+ Resources/core/icons/shield-exclamation.svg
+
+ hash2
+
+ 7jPbOixS/TePSmTfmpgZTLg2GG7A7ezC6Rfh1ppqCDA=
+
+
+ Resources/core/icons/shield-fill-check.svg
+
+ hash2
+
+ EDUjeLPR0qXKgFVQqRfRlZRnN/0EaeTT/J+3DvbqkUg=
+
+
+ Resources/core/icons/shield-fill-exclamation.svg
+
+ hash2
+
+ NVESrfGQUnbz+XaDYbBnJuOJZhT2SVC2DP0lu/Qgg+Q=
+
+
+ Resources/core/icons/shield-fill-minus.svg
+
+ hash2
+
+ ZLHXUZyQ8T9pm15OZF3Sv8XWSfHIDSXxIqJDnZRBGEg=
+
+
+ Resources/core/icons/shield-fill-plus.svg
+
+ hash2
+
+ 3DEZImkceDZNKvf51YFXsNds+f5BiFO667pl/MoWD5I=
+
+
+ Resources/core/icons/shield-fill-x.svg
+
+ hash2
+
+ bjSJaYZ8OnvbyCE36J7TO/QAK+ltWntLEx67SuaXKJw=
+
+
+ Resources/core/icons/shield-fill.svg
+
+ hash2
+
+ snTzXqIXqpmvLEbJivWsbkaSNP/CIrXK6b+a2T7ZocQ=
+
+
+ Resources/core/icons/shield-lock-fill.svg
+
+ hash2
+
+ NQ6C9Q1bgZzk53Ds7vExD/lS4RzrRYRbQevjJ2DRx2s=
+
+
+ Resources/core/icons/shield-lock.svg
+
+ hash2
+
+ uDqEn5+kS+qYmqZKGoaChB+8UeVOxEyKex3sx+0ZdqM=
+
+
+ Resources/core/icons/shield-minus.svg
+
+ hash2
+
+ TNtZcpS9EXGGmk+Rl6I38DqqlQV65rAKRna33II/Qmk=
+
+
+ Resources/core/icons/shield-plus.svg
+
+ hash2
+
+ 3QbGj4KjkCXHxuXze+8dsheOg46WzvV1vi+/apK8FCk=
+
+
+ Resources/core/icons/shield-shaded.svg
+
+ hash2
+
+ lwYDlAIZOcmYkYKhOzFV4RJJMRVgbhkO9adVeQS4Gk0=
+
+
+ Resources/core/icons/shield-slash-fill.svg
+
+ hash2
+
+ TPsKHrm9xFAeNGPm7rQTrrVaN/iW1zlc3T5EAqGdkRc=
+
+
+ Resources/core/icons/shield-slash.svg
+
+ hash2
+
+ tHwLw01sMbQgAXpyZ3QxOXQ8sla9DsrSHOjWGAbnoes=
+
+
+ Resources/core/icons/shield-x.svg
+
+ hash2
+
+ kmIscV3IvLZz9wDTeN4rvsBXUl/BpVH3BCxhnLzkyBE=
+
+
+ Resources/core/icons/shield.svg
+
+ hash2
+
+ H3uzjdgGiAsTdpNu5s+3ZQV9utrxjZAqTo7ItM4iBr4=
+
+
+ Resources/core/icons/shift-fill.svg
+
+ hash2
+
+ 0H7NPJBYcq7MIoN8N/RAz2qqIREkNctJ/KvAOeGRQHc=
+
+
+ Resources/core/icons/shift.svg
+
+ hash2
+
+ iPXlmJNLCdltAh+RcY4hw+S7GhwcCmVCK9m6Wg7k6S8=
+
+
+ Resources/core/icons/shop-window.svg
+
+ hash2
+
+ y1v/RCO/oLJ+SzhJphvlurOWlDnohyKBZpxCZzbGl40=
+
+
+ Resources/core/icons/shop.svg
+
+ hash2
+
+ Ov/BuElvsbuocnOxO4sl11BqMAdnUv9D3LC2ISX5kXQ=
+
+
+ Resources/core/icons/shuffle.svg
+
+ hash2
+
+ Gpe2yJbh0YUNfQ8vF3vFhxSPUfWHRbqbkSNIKKCmIhQ=
+
+
+ Resources/core/icons/sign-dead-end-fill.svg
+
+ hash2
+
+ 63lfgyb+nS+SRfV3IV22BFPQNfGo9zp9FRcVx3Fw2tc=
+
+
+ Resources/core/icons/sign-dead-end.svg
+
+ hash2
+
+ /KTiCXhCNBSqA0SNYFQIjblAdUTXpobdCAySflWTWYs=
+
+
+ Resources/core/icons/sign-do-not-enter-fill.svg
+
+ hash2
+
+ 1OmG2pDlfMZR/+RcrdJH1h1HErB4C6kIy9j9Ud7x404=
+
+
+ Resources/core/icons/sign-do-not-enter.svg
+
+ hash2
+
+ MMF5ijI7p2lme9K/Smi0CTICD9nOezZiNpdjGSWiL0c=
+
+
+ Resources/core/icons/sign-intersection-fill.svg
+
+ hash2
+
+ aY98wGEF1nbdsb7UBewrQKkumdxwqMIlB0CKUJQyzMM=
+
+
+ Resources/core/icons/sign-intersection-side-fill.svg
+
+ hash2
+
+ hKWbbe5C161fe9vskoVqME3MNN+20671T1hGYQ5I6c8=
+
+
+ Resources/core/icons/sign-intersection-side.svg
+
+ hash2
+
+ m4iQk0hKQxMLEeeUO70kVNrCvJ/BPmzxzVMjBxQ2vFg=
+
+
+ Resources/core/icons/sign-intersection-t-fill.svg
+
+ hash2
+
+ FIHlfiDTTrd2uBMQ+axjB/gw8RMbtnS0j3HxvPI5TS0=
+
+
+ Resources/core/icons/sign-intersection-t.svg
+
+ hash2
+
+ 3mX8nBvHpQbzl+zYj/xejCa3Upex+vrMzrzYA5rH4uY=
+
+
+ Resources/core/icons/sign-intersection-y-fill.svg
+
+ hash2
+
+ 8/w1jX2nbX5gIAdMUMK7FQzueCE4CNiCLsaI13yrZeQ=
+
+
+ Resources/core/icons/sign-intersection-y.svg
+
+ hash2
+
+ UROhb99bozOKo7a3lVaj4Opr2xpeKheQvpvnOGaiGRw=
+
+
+ Resources/core/icons/sign-intersection.svg
+
+ hash2
+
+ maANQg2CR3/V2Uj0dXQTB4MPDbrGgBKikonqLkTzxxs=
+
+
+ Resources/core/icons/sign-merge-left-fill.svg
+
+ hash2
+
+ g9++ALTKpPZJJFZJ+fWLNDmAqvw7vnIVB1yj0vKmg+Y=
+
+
+ Resources/core/icons/sign-merge-left.svg
+
+ hash2
+
+ 5DMJ7hYg5ywZWSduoEQ35bTcn8/i1rAcyVv4+zbUwhE=
+
+
+ Resources/core/icons/sign-merge-right-fill.svg
+
+ hash2
+
+ hLi31I4ngyhN2cVwZy5tZp1ZX5q2smRLbBXvjrQTpsU=
+
+
+ Resources/core/icons/sign-merge-right.svg
+
+ hash2
+
+ RPcE7XcLnilgitpRi9V+wkb7YVLJIYF3tgRRHbPC7L0=
+
+
+ Resources/core/icons/sign-no-left-turn-fill.svg
+
+ hash2
+
+ SCiP7lO78r2AqupdftcFuQUuT6T8vL3gY4jcxnviUpo=
+
+
+ Resources/core/icons/sign-no-left-turn.svg
+
+ hash2
+
+ z+i9XMpeuE6fizmx4W48hG5uY4Nx0PFtQ8Cten/IP8Y=
+
+
+ Resources/core/icons/sign-no-parking-fill.svg
+
+ hash2
+
+ hhXlwWCBvNnUn8scHbGsdjx3oE+zeyPktDM6XqDDxoU=
+
+
+ Resources/core/icons/sign-no-parking.svg
+
+ hash2
+
+ 6gIVuee+DBwYiP2ppOhFt0j2mCQKYiEbehvGDl/LEqY=
+
+
+ Resources/core/icons/sign-no-right-turn-fill.svg
+
+ hash2
+
+ RB7g3kYnuytsXBxJ/qyDjV5lfxw1/mB4Q7aaGBvJIZs=
+
+
+ Resources/core/icons/sign-no-right-turn.svg
+
+ hash2
+
+ lAxgNMTuIFk0SOIe8u4qJa5sqvv6mfMm6JvnrQUF9F4=
+
+
+ Resources/core/icons/sign-railroad-fill.svg
+
+ hash2
+
+ U+GwzUs4Xq0JXRhT7Bnzzgiiyxy5n5I/hGzhanWKa3E=
+
+
+ Resources/core/icons/sign-railroad.svg
+
+ hash2
+
+ EkEgzpM8Ee/UpmlUMWANMVSJJ4O6LgnchHdvH3D1JYU=
+
+
+ Resources/core/icons/sign-stop-fill.svg
+
+ hash2
+
+ C20XHTL02fT7bWK0uHPYrnaeuDrkNW6EdszTtmves2I=
+
+
+ Resources/core/icons/sign-stop-lights-fill.svg
+
+ hash2
+
+ CapwdU75PfTvFh012Czqul6mdkHH1WGHSlzn0v4ek/k=
+
+
+ Resources/core/icons/sign-stop-lights.svg
+
+ hash2
+
+ 04SBTfmMpy2ozA8BtwpffAWQKuKRPkEaipNdt47cxSY=
+
+
+ Resources/core/icons/sign-stop.svg
+
+ hash2
+
+ bQdg60KNX/CjeYQ4PzgQ9OkjHK1i5xu7y3bylgGrVCo=
+
+
+ Resources/core/icons/sign-turn-left-fill.svg
+
+ hash2
+
+ VPAFEyGsEvP6sP6064SeqySkJ1+xXJ+9SyUnWLedTz0=
+
+
+ Resources/core/icons/sign-turn-left.svg
+
+ hash2
+
+ dvFILa0QL7kWIMwUrwFkMfvgHLw5Tu/Ozaljscnx4X0=
+
+
+ Resources/core/icons/sign-turn-right-fill.svg
+
+ hash2
+
+ Fp+gxMAuZ/2gUfFIThiZQ3Tr/MwhSwcMZRW4JiMEVaw=
+
+
+ Resources/core/icons/sign-turn-right.svg
+
+ hash2
+
+ GagKQUgII8yrNzBq9rVsDA+y/GpOwiUPDmxTrJQTFWo=
+
+
+ Resources/core/icons/sign-turn-slight-left-fill.svg
+
+ hash2
+
+ 38zAkODrMNnWDTXQpvjXKBAFttgckX/DHTA+5ee0wBE=
+
+
+ Resources/core/icons/sign-turn-slight-left.svg
+
+ hash2
+
+ jN/ZgG7ICBdwHiabKEX6WLitxLrCTJ+pNccux+IQWq0=
+
+
+ Resources/core/icons/sign-turn-slight-right-fill.svg
+
+ hash2
+
+ wyGdoPL9MIsg3h9kHezIhnEzdmGvEPJH/AKpQt7chOk=
+
+
+ Resources/core/icons/sign-turn-slight-right.svg
+
+ hash2
+
+ +z7F8JC1heCYQDGopI2F5dw6exronhLrS8VQYqc0Cig=
+
+
+ Resources/core/icons/sign-yield-fill.svg
+
+ hash2
+
+ wUfbMnhdkZAtH5pWOeTRulf1+bHOjHnDbp/8EO5Rz24=
+
+
+ Resources/core/icons/sign-yield.svg
+
+ hash2
+
+ 2gFWln+le40w18WX6TJlo8lsyIZU2gkW4fIPEmwVbnA=
+
+
+ Resources/core/icons/signal.svg
+
+ hash2
+
+ ZouNG8gVUhCXGH1Eh18tAOIOetfAezIpLZFW8FLYHBI=
+
+
+ Resources/core/icons/signpost-2-fill.svg
+
+ hash2
+
+ W1nzg9h40m/u1mVAJ5A3oevndWnHUixE9H2j6naalTE=
+
+
+ Resources/core/icons/signpost-2.svg
+
+ hash2
+
+ 2gyPleNWgr+fwq/aVH/KAiZ5HBlFae8qQUdhhbJ/KEk=
+
+
+ Resources/core/icons/signpost-fill.svg
+
+ hash2
+
+ vlB6ZkwGSxY1RiWeZ2PgWeu8b+5t2cxwaWXLMlroki4=
+
+
+ Resources/core/icons/signpost-split-fill.svg
+
+ hash2
+
+ +pUUAA07gyqID2h5/2TbUg3JeZOW97RTg9RiyGke/kg=
+
+
+ Resources/core/icons/signpost-split.svg
+
+ hash2
+
+ hy23exHcUH/0AUZ/etMlRxXiam1v3wmI+vOoYmVvP0c=
+
+
+ Resources/core/icons/signpost.svg
+
+ hash2
+
+ hryfDIMAX5y3roSMal5raEZr9JSnihIJ6EUJMmjX4W0=
+
+
+ Resources/core/icons/sim-fill.svg
+
+ hash2
+
+ ey/pGxXxMDJRKr13StCtWyH1vW6EMWSgbBFj+bjInc4=
+
+
+ Resources/core/icons/sim-slash-fill.svg
+
+ hash2
+
+ O4uQN6/Jm2a6Yquahb8M2ik4mt8AM+Q3j8qX4zaWpKY=
+
+
+ Resources/core/icons/sim-slash.svg
+
+ hash2
+
+ SWQ0XjW9tTHlnOippdVal2KL4rkdyluSc3V4Z4EROBI=
+
+
+ Resources/core/icons/sim.svg
+
+ hash2
+
+ c8pinxyeRT0DvzOtPdseeAckFJxyXK0U5EtqtWS3+3g=
+
+
+ Resources/core/icons/sina-weibo.svg
+
+ hash2
+
+ iud1DLQLpv0UVNYzni3h0oZsCAh0Kaz/xogpTRDphJg=
+
+
+ Resources/core/icons/skip-backward-btn-fill.svg
+
+ hash2
+
+ HSveeKt3z9TDzyVf2pAfPqFfI4V7EMvxTonxV27IXbg=
+
+
+ Resources/core/icons/skip-backward-btn.svg
+
+ hash2
+
+ LE7K+f4NIHxTQ7JoH548W38DpD4USk2KHXGVqDIL+GU=
+
+
+ Resources/core/icons/skip-backward-circle-fill.svg
+
+ hash2
+
+ vvnQwXx62KgguwnQH/37/vvNl6/7KT7LP9T+HOftKVM=
+
+
+ Resources/core/icons/skip-backward-circle.svg
+
+ hash2
+
+ uqGFq6t5LIZmqrqZKHSelFoKcG/CIBUQoPGSV3RQWMo=
+
+
+ Resources/core/icons/skip-backward-fill.svg
+
+ hash2
+
+ Viq0hg3jo32v5kJRla7h3egSDsur3eQUYzYcqH5mrGQ=
+
+
+ Resources/core/icons/skip-backward.svg
+
+ hash2
+
+ D4FqEGXZG8qPEEo3VQVWAwCiJJaP97/1h7P2VMo/33c=
+
+
+ Resources/core/icons/skip-end-btn-fill.svg
+
+ hash2
+
+ gcLwymc/FY8t2cPJOfdciBD1ebyRPVnm9zUfcCYd3GI=
+
+
+ Resources/core/icons/skip-end-btn.svg
+
+ hash2
+
+ yV7OwXF26lQY9JVLKB6UlqrvNkkdY9E0pJuCYpn4TkM=
+
+
+ Resources/core/icons/skip-end-circle-fill.svg
+
+ hash2
+
+ B598aY6PbCj4IXh5P97G4/xAn6MiFRJObp6XaKHahHQ=
+
+
+ Resources/core/icons/skip-end-circle.svg
+
+ hash2
+
+ ydw2ONlQv95SNLrgyAeFK7JeWISCeaBBrtbsBfbtWtw=
+
+
+ Resources/core/icons/skip-end-fill.svg
+
+ hash2
+
+ nUR7Tz7K0fv9GCGmU7XKb6a8jrlj1FAfeuCOTh3D2bc=
+
+
+ Resources/core/icons/skip-end.svg
+
+ hash2
+
+ NoJkrBMgLh7V7uXBCl7xvYHSkFSS42Gk2d9RojkXydY=
+
+
+ Resources/core/icons/skip-forward-btn-fill.svg
+
+ hash2
+
+ bEopG+J/JhFX8aCGmh+3LFalIlvduuuKuBhudp2ihrQ=
+
+
+ Resources/core/icons/skip-forward-btn.svg
+
+ hash2
+
+ 7E0NNtPK/dre7uy1Ka+vZgpAFGbh/0RCUYKLpJqdVeA=
+
+
+ Resources/core/icons/skip-forward-circle-fill.svg
+
+ hash2
+
+ FxERyq/jCylRvROuQeJHH3Nb28WjUG/fP1YL+AY1+44=
+
+
+ Resources/core/icons/skip-forward-circle.svg
+
+ hash2
+
+ qPWAdMknlUmmpPzuVgGcXP3+qA9P1ZK1vaq2xwzH8FE=
+
+
+ Resources/core/icons/skip-forward-fill.svg
+
+ hash2
+
+ Bmm0if40UcS3MdDBFPZL0cYjrxjFO9spx2J7GY5xToE=
+
+
+ Resources/core/icons/skip-forward.svg
+
+ hash2
+
+ dtCJ0jy0aA8+lXHWpsoy7voPd0KLXBYkPeilvDsM5Dc=
+
+
+ Resources/core/icons/skip-start-btn-fill.svg
+
+ hash2
+
+ RggYcA88yylZcR1/eLk0YJ0XDePYYt9uhwMqJl071co=
+
+
+ Resources/core/icons/skip-start-btn.svg
+
+ hash2
+
+ St6pymndDG7mjusvQGsrVjSVK3LaUN9RoNZ97Af2ba8=
+
+
+ Resources/core/icons/skip-start-circle-fill.svg
+
+ hash2
+
+ 1u6Wsv/TZsgV15PfpckCy9B4kHJL7DfKxH5NUSvZZgA=
+
+
+ Resources/core/icons/skip-start-circle.svg
+
+ hash2
+
+ 7YXyyKNf0U4A+iZNYzb/yHlL/oLWGK5wH2lKGPgC7JM=
+
+
+ Resources/core/icons/skip-start-fill.svg
+
+ hash2
+
+ EN0waKj9x6f4OqKaaFjgWJPRlmoTjXSy6xEM/k/h+IE=
+
+
+ Resources/core/icons/skip-start.svg
+
+ hash2
+
+ bLOqailtCQbkc14dhIx4cZJl2Q6IRs6KhTvTtAkavv8=
+
+
+ Resources/core/icons/skype.svg
+
+ hash2
+
+ b382VZxnuHnKrE1uZ9Nns5XWZyL1Nwcjh+9m81vjBNc=
+
+
+ Resources/core/icons/slack.svg
+
+ hash2
+
+ GYZ8DRCfrmmppEKFwjLDwmAc8/PXK0AbDsugP/1UCgI=
+
+
+ Resources/core/icons/slash-circle-fill.svg
+
+ hash2
+
+ hHZY+2TuOdw1rax0EXoQkchg1R6U4JiFP7TyQmhCrcE=
+
+
+ Resources/core/icons/slash-circle.svg
+
+ hash2
+
+ wWnSayjrGotKxhQHOp0h4pc8WOc4QrJS7+TBbhkNSVo=
+
+
+ Resources/core/icons/slash-lg.svg
+
+ hash2
+
+ VjCub5JJ6o8iSad+z4N7Fgpe+snAAQt7z0JXCgD9JY0=
+
+
+ Resources/core/icons/slash-square-fill.svg
+
+ hash2
+
+ 5k5XnWCNpAgUAnhsxjJnYHV5Za13P88kRoNiE5YWbyM=
+
+
+ Resources/core/icons/slash-square.svg
+
+ hash2
+
+ dJi4rVigDw7Mzs8F5K0+lPoyMsyKJYm1k/o++/oP0Oc=
+
+
+ Resources/core/icons/slash.svg
+
+ hash2
+
+ FpDp3FQxVEDtXX976kt6snGDgBW96iHKmdkGEuLE3Ss=
+
+
+ Resources/core/icons/sliders.svg
+
+ hash2
+
+ XaUiBHQaV8K+yLSGPf2rwzWkwcBAGDkq/yZ7kKedU6I=
+
+
+ Resources/core/icons/sliders2-vertical.svg
+
+ hash2
+
+ LDQSpwqtxVNwZcwhcJWXJqhjIbJ1ovK53taiHZsMuiU=
+
+
+ Resources/core/icons/sliders2.svg
+
+ hash2
+
+ 4DriwWvRGqGuyajc0GRzkeVcvL1V8f2y4L+w5Pddtz8=
+
+
+ Resources/core/icons/smartwatch.svg
+
+ hash2
+
+ CwwI4jf3fN5g0vAYdtUrD5QgrLT7Ly/tBgqPcjRPKHs=
+
+
+ Resources/core/icons/snapchat.svg
+
+ hash2
+
+ CPyz0QWBvxOY9DVq1uHVFNWtBeWEq8fEkg2J3ZeYXZY=
+
+
+ Resources/core/icons/snow.svg
+
+ hash2
+
+ 7JRxL/9aw0W7bNeBn6UPPQvaNMcazpTw6hfSGugm/qk=
+
+
+ Resources/core/icons/snow2.svg
+
+ hash2
+
+ 8A++SrFl8WPBjj/kLENL5HO9uSo30QtHhHmN8QsPB7w=
+
+
+ Resources/core/icons/snow3.svg
+
+ hash2
+
+ 4+yxQ7ymUzu45aLT/HLW4d+WFHyfIXWxYgsqmLzzn38=
+
+
+ Resources/core/icons/sort-alpha-down-alt.svg
+
+ hash2
+
+ cRAxvrU9cROCfKYCngL557jFsOzAuR9b+7JZjL4OhpI=
+
+
+ Resources/core/icons/sort-alpha-down.svg
+
+ hash2
+
+ gwtSej5eKyY4B/uiM8AXaSIKsyOPeGp2EEgbnNYhB2g=
+
+
+ Resources/core/icons/sort-alpha-up-alt.svg
+
+ hash2
+
+ C8ubOFmWZip4zFPaMAdr45bUJzTuS+jxdstJ+ZYjBbc=
+
+
+ Resources/core/icons/sort-alpha-up.svg
+
+ hash2
+
+ AnsmJjumrwzLg9fnE69UFvTEnD/4GGlQqfrvX7TOZc0=
+
+
+ Resources/core/icons/sort-down-alt.svg
+
+ hash2
+
+ 6cEr0WTcTQHb+PkirgIkuUpmWYL1TlXDecKlvAsfIfI=
+
+
+ Resources/core/icons/sort-down.svg
+
+ hash2
+
+ d40cc2eYLDd+lAnaehiqhjfSoMUBgDxnCCyDCxNyYoE=
+
+
+ Resources/core/icons/sort-numeric-down-alt.svg
+
+ hash2
+
+ rWMhvApsJQHhhlQjA62vIDyWnQswF/aTC7l0xcbhlzo=
+
+
+ Resources/core/icons/sort-numeric-down.svg
+
+ hash2
+
+ lF9WDWm4hS73wN49NEAoJeaNmkhzStvXfG2oPFval0o=
+
+
+ Resources/core/icons/sort-numeric-up-alt.svg
+
+ hash2
+
+ LUlaHydNNiKx1Fbs7RRP9Fj4XmbO34a+rayH4JOO1nY=
+
+
+ Resources/core/icons/sort-numeric-up.svg
+
+ hash2
+
+ MxhWyE6Svzv6EnzE7V3EkidNJ1YjPF27WBohl0Td+1k=
+
+
+ Resources/core/icons/sort-up-alt.svg
+
+ hash2
+
+ K/Dmz9esY4czCkA5GE7Pz9ktaL/8Gsax6H0oZ3y0+4g=
+
+
+ Resources/core/icons/sort-up.svg
+
+ hash2
+
+ ca+lgJpdL5wk/OYgyyPbn/U4ieSY2tQaoOJZvkPW0H0=
+
+
+ Resources/core/icons/soundwave.svg
+
+ hash2
+
+ pL0wO2zot80LyUZ13TsR0cOLp3WN1Jk3t2iIdLylAQQ=
+
+
+ Resources/core/icons/sourceforge.svg
+
+ hash2
+
+ CnH6rbHNFxQhhlf6Rd6CkZGmI4fKMhXC3sH2vYFL7gk=
+
+
+ Resources/core/icons/speaker-fill.svg
+
+ hash2
+
+ Mp6tm4MNJaizzqcRWWepKXnbL5G8mF+hnuGy1LXQRlg=
+
+
+ Resources/core/icons/speaker.svg
+
+ hash2
+
+ 8pVnTgFObl5+Kka+j8DVmCH90yC+CP93+jzjj7dHdr0=
+
+
+ Resources/core/icons/speedometer.svg
+
+ hash2
+
+ YdJR/cFG36Z0vRBaykE2lbk5Sy/55VMbFUeC07Ma7vU=
+
+
+ Resources/core/icons/speedometer2.svg
+
+ hash2
+
+ paqimoUsWaHocJ889kCcUx3qmECK0Vw+bHxZR+Tp/pk=
+
+
+ Resources/core/icons/spellcheck.svg
+
+ hash2
+
+ DNGLU/Y0N0BFUSboRFL2YVroKK91Fu/X5P0p/ApEZL8=
+
+
+ Resources/core/icons/spinner.svg
+
+ hash2
+
+ NhQXgMYaJk9N8KLfH4cm+t2B22OvDesla1DMsBcjtAU=
+
+
+ Resources/core/icons/spotify.svg
+
+ hash2
+
+ bm6/RPNAaLnGPBioSOmKxIgDlJA4I11rP5WwRDSJs4w=
+
+
+ Resources/core/icons/square-fill.svg
+
+ hash2
+
+ 0WHtLcSFlx90hYjOCVwOGbn+wUtyFMi6rDO5ydTcuUU=
+
+
+ Resources/core/icons/square-half.svg
+
+ hash2
+
+ SQGdzYN3GNe9ZYeHleiQHcOIgpCFtPl/NHY0JXNvIVI=
+
+
+ Resources/core/icons/square.svg
+
+ hash2
+
+ 4qrwj5CWUP232zazKlxP1efCIrVOiF9eyCGvdraBddA=
+
+
+ Resources/core/icons/stack-overflow.svg
+
+ hash2
+
+ 7nWa0t6LToKFdO7umzUspVXKcXWre6m8GqOse5L8WOk=
+
+
+ Resources/core/icons/stack.svg
+
+ hash2
+
+ HMrbC803NrSatjLkx97aQutDQAptyWTWIH9p+BEnsp0=
+
+
+ Resources/core/icons/star-fill.svg
+
+ hash2
+
+ 7dW9krSW9Z1z9xBin0m4J7+s+mK8M6zwmAb2mrP7UdM=
+
+
+ Resources/core/icons/star-half.svg
+
+ hash2
+
+ QOqzEu/wQgUls1Z5T+bQ462yj3rNLqpA0L0qcnsFCaI=
+
+
+ Resources/core/icons/star.svg
+
+ hash2
+
+ XXkd3ACH1MmIHhE0tss0/c3GsJcb7AGYraKjvI/FFRI=
+
+
+ Resources/core/icons/stars.svg
+
+ hash2
+
+ DYiy2E9EcZ3dVNba53BYxBhh7OSqpckoVpOZweAMsCk=
+
+
+ Resources/core/icons/steam.svg
+
+ hash2
+
+ 7Wndc3hyJf4W8RcW3TsTaloCNIY71auXlgKaw/YNVJA=
+
+
+ Resources/core/icons/stickies-fill.svg
+
+ hash2
+
+ P4kUlzeCE6E1Ac6xFVk15QRakbC+ivZAgLX4tmufVUs=
+
+
+ Resources/core/icons/stickies.svg
+
+ hash2
+
+ rEhf9gwo3gcqc+pmpHYPwQpoz9ThcnMkzFIScnHmLNQ=
+
+
+ Resources/core/icons/sticky-fill.svg
+
+ hash2
+
+ +r/u1C1c8RmiDmT6dxX+TbfD7Tb297uDKMKOldgwhyk=
+
+
+ Resources/core/icons/sticky.svg
+
+ hash2
+
+ c5vxaRV5QBzWxUaZ8VzxzMCuzMpnNSTWvFujXdbtUhU=
+
+
+ Resources/core/icons/stop-btn-fill.svg
+
+ hash2
+
+ Y3ppsb/X3iV15sJ6atJJm2KH5PPJHze1jAH/pYCImTQ=
+
+
+ Resources/core/icons/stop-btn.svg
+
+ hash2
+
+ 8xu0WXV6I0xz1NMPZ3dQZpuR2YAgYSpTr7M2MsYW66U=
+
+
+ Resources/core/icons/stop-circle-fill.svg
+
+ hash2
+
+ vNAS+bERObe2i50yeEeLMeNxANevxnxTCgOUHz6DzlQ=
+
+
+ Resources/core/icons/stop-circle.svg
+
+ hash2
+
+ j5zbcGoK+JgNEw59EXfBoKDhs+2YASfJjhzapxY0XgA=
+
+
+ Resources/core/icons/stop-fill.svg
+
+ hash2
+
+ AeGVBa9wRpdVaWOJ0qV8AhcBXEbY6tVGonqlqhtGrYI=
+
+
+ Resources/core/icons/stop.svg
+
+ hash2
+
+ XvAyDHlHRUZP64o4aRs6Nz5dtiWXOI//1VGvEwdOlBY=
+
+
+ Resources/core/icons/stoplights-fill.svg
+
+ hash2
+
+ Y+zyBQEUnZUnMjMhZviyAdgf4MUvh0/l+s6ai07T2hw=
+
+
+ Resources/core/icons/stoplights.svg
+
+ hash2
+
+ clsqtRbvwG/jqXPGvf31a8AgIWdxZSZA11IdFW/Tjpw=
+
+
+ Resources/core/icons/stopwatch-fill.svg
+
+ hash2
+
+ BzJw8xxXQhH8Eq2bAkDMDFuf65Opw4t3ysrQs3OtkyU=
+
+
+ Resources/core/icons/stopwatch.svg
+
+ hash2
+
+ TwnfAWn54Fm9VGfFKcr3HIokIHaziERB84Nu3D/Vh0c=
+
+
+ Resources/core/icons/strava.svg
+
+ hash2
+
+ bEnOdq9X4/MhI4CCfy5jAMk/6g8yrD7QCDxNJgOGyKY=
+
+
+ Resources/core/icons/stripe.svg
+
+ hash2
+
+ njmfa2kYQko7RZOOOmlJXHUPMT75wsyF/a1sMTGklSk=
+
+
+ Resources/core/icons/subscript.svg
+
+ hash2
+
+ OkrAQRzxY9FyNZDo7aasICVuefpX75v7h7XYPVPOu/w=
+
+
+ Resources/core/icons/substack.svg
+
+ hash2
+
+ 2F/JKI38rFbRHxXAIJ6EvBHmVkYOQMAvCulU5Nb3EZQ=
+
+
+ Resources/core/icons/subtract.svg
+
+ hash2
+
+ jwXEleW0lK7vwfqqhffb6o6KioasLRdaLkR5XSNy5zI=
+
+
+ Resources/core/icons/success.svg
+
+ hash2
+
+ 6x6/ryeqU9YNv1omBK5nOINYH+mrsOfW8Qz7hLs+fzg=
+
+
+ Resources/core/icons/suit-club-fill.svg
+
+ hash2
+
+ mOrJAO/8PjFaU1xRvzuP0OcApT0tADbFOeanTUrjMvY=
+
+
+ Resources/core/icons/suit-club.svg
+
+ hash2
+
+ pWkWPBO9K2iOYffTZ1ONd56MIFbwzaF/MnKR95YMZnU=
+
+
+ Resources/core/icons/suit-diamond-fill.svg
+
+ hash2
+
+ peR/9Paq+ikul+wVc1YKnKMiOsMGXa4KGS68DdGZNRo=
+
+
+ Resources/core/icons/suit-diamond.svg
+
+ hash2
+
+ HU+XjE3P2VGO4waqo+aoJO1OoveMoypPqH0o9RcnQqg=
+
+
+ Resources/core/icons/suit-heart-fill.svg
+
+ hash2
+
+ cHA4u/0MPeIAe4QyhFw1WWLyi3byutRy/0ikaJ42MyY=
+
+
+ Resources/core/icons/suit-heart.svg
+
+ hash2
+
+ DmE4MrHFe/4dksoSlqX952+w9XdeIz0KzEHRtWcBbec=
+
+
+ Resources/core/icons/suit-spade-fill.svg
+
+ hash2
+
+ tmWWVj2n7Q3B37ffSxfB2swcCIB9+GARIsuAnkoqXRI=
+
+
+ Resources/core/icons/suit-spade.svg
+
+ hash2
+
+ vTuun5IUxuleKkL0zs0j2ziF3baAisK2+lwG8clBADU=
+
+
+ Resources/core/icons/suitcase-fill.svg
+
+ hash2
+
+ hoq5RaSwxVi4fwQOSMbli4Lmxd1RNWn1BK/dp+6B94Q=
+
+
+ Resources/core/icons/suitcase-lg-fill.svg
+
+ hash2
+
+ 82VNjxRMw+qzyBDhOadEbT+DSprW1WZ3pXlssR1wGWU=
+
+
+ Resources/core/icons/suitcase-lg.svg
+
+ hash2
+
+ UnatnHnOw0Yoz4fonVH2xfBlqQm51Ug2Io00SwYvc9A=
+
+
+ Resources/core/icons/suitcase.svg
+
+ hash2
+
+ aigrj6hTKHLGHrS4PXk734fXeZVFemW+JZ4xQ8PRStQ=
+
+
+ Resources/core/icons/suitcase2-fill.svg
+
+ hash2
+
+ eD2htzpsq642CsqDIZjkVpgEnMPy/am11ovk9wTGwEI=
+
+
+ Resources/core/icons/suitcase2.svg
+
+ hash2
+
+ 4wvQLpj5dOjClRsvJA9Glp2RpyEbeSg7o+ycMMtXyLU=
+
+
+ Resources/core/icons/sun-fill.svg
+
+ hash2
+
+ afyzDYADqVXfGJD9AOOQoT+ZZHDEP2lHIOgjoEiRCyg=
+
+
+ Resources/core/icons/sun.svg
+
+ hash2
+
+ +Km9dBfiihVvhhF1yxYwcn7sDAM/RKTxwvUMxQ12Pu4=
+
+
+ Resources/core/icons/sunglasses.svg
+
+ hash2
+
+ ITCTnuklcxHLr31/dH4OaPvM76ap1dttUil3Rf0KZoI=
+
+
+ Resources/core/icons/sunrise-fill.svg
+
+ hash2
+
+ jXaXjSSGPMjgEqDH2M8LlyssYqqALsxONcnuPIROBzA=
+
+
+ Resources/core/icons/sunrise.svg
+
+ hash2
+
+ 6HSLD+rOX7NgEpC/o14e3HF53YT3nEh0A5u4q+xwceU=
+
+
+ Resources/core/icons/sunset-fill.svg
+
+ hash2
+
+ BJ0H6P0zpnh7qGQm7yod7M2Z3ibUFvyKLUzQ/JY4Alw=
+
+
+ Resources/core/icons/sunset.svg
+
+ hash2
+
+ /qCpshFGyw0shN4ZV6G01AFDv6SJ+ioqNFoXQxcF2bA=
+
+
+ Resources/core/icons/superscript.svg
+
+ hash2
+
+ zKFRpLxUdItj7meyTEyr7zvhE7Ddmo0BXj6tBTcDqh8=
+
+
+ Resources/core/icons/symmetry-horizontal.svg
+
+ hash2
+
+ bwlFocaZjMMqE9RUIWPpV6kb531ujS+SXd/MnPzNdcg=
+
+
+ Resources/core/icons/symmetry-vertical.svg
+
+ hash2
+
+ OdrG2Rb3VT/2sQJ1WooX16ZxguCiNQ6Y1y5KE2GwKV0=
+
+
+ Resources/core/icons/table.svg
+
+ hash2
+
+ bBI7U2PXfquUdbL8Nc4rnC/dQumBm75GCRrm/cUe+18=
+
+
+ Resources/core/icons/tablet-fill.svg
+
+ hash2
+
+ q/DnUyrFsCkU88cbrKXnWQF6V9j+2eCnzk8XAI7T5oo=
+
+
+ Resources/core/icons/tablet-landscape-fill.svg
+
+ hash2
+
+ 2gfTF2LkaYAuQlsc2AzO43DDygU67YAz67BpdhP9XeI=
+
+
+ Resources/core/icons/tablet-landscape.svg
+
+ hash2
+
+ iMI/GRuSzfbJfh2rO/Fctn+U3Rx4Hj1AVArpsJ2WdAQ=
+
+
+ Resources/core/icons/tablet.svg
+
+ hash2
+
+ XX6D1/e+JKRjGdggJp04Gl4sMyge4cF4A0Stbn3eMqg=
+
+
+ Resources/core/icons/tag-fill.svg
+
+ hash2
+
+ SWaQ1tk8Zo7mMdZPv/f2Z0yscyn6JxV8i8CBj+cdG+Q=
+
+
+ Resources/core/icons/tag.svg
+
+ hash2
+
+ qFA3Bjf38dsDg/D+FIILBkITDREbHBHkfwRjQ4cFjVg=
+
+
+ Resources/core/icons/tags-fill.svg
+
+ hash2
+
+ kgQVJiqZm7F7V1usrbTJkfvibXX1RAxnqVU3JxdF0iY=
+
+
+ Resources/core/icons/tags.svg
+
+ hash2
+
+ iFMRtr8MY8xfN3+yjvHw0pKfFI/gKFRNZx3p3/iQ4ds=
+
+
+ Resources/core/icons/taxi-front-fill.svg
+
+ hash2
+
+ 9Gp2sVZSsHSkh1UNoqpZP78ImHLBkxGpgE0PE3j5lhw=
+
+
+ Resources/core/icons/taxi-front.svg
+
+ hash2
+
+ D+zQOBONJOpt+qTfsbXSq2H1cWd5sBitw1x2ofjT0KM=
+
+
+ Resources/core/icons/telegram.svg
+
+ hash2
+
+ jvToEHRyTRvIi9Y42rKgTWCoLzPgWc3YqLv7qKRkSYA=
+
+
+ Resources/core/icons/telephone-fill.svg
+
+ hash2
+
+ fTtTSSQbsESQ9E3ZC8fwExcanYQpVcKQXgdHq6nknxg=
+
+
+ Resources/core/icons/telephone-forward-fill.svg
+
+ hash2
+
+ iFiY7nkbnSw5LnRACI3m4IID1eCmI59hoi0oIygewnE=
+
+
+ Resources/core/icons/telephone-forward.svg
+
+ hash2
+
+ l7mDs6IRZPUghn2BGDcXVGZmmaquAro6n7KGlEHzpwc=
+
+
+ Resources/core/icons/telephone-inbound-fill.svg
+
+ hash2
+
+ ZDaBpNKaOIjqP+TOYoZ6v1ouRdNstNwblaHGpPTi4hU=
+
+
+ Resources/core/icons/telephone-inbound.svg
+
+ hash2
+
+ H9kkBdD7XOYLsP5HyBtFh2Pp9cTtbEeenMiwG31z0PM=
+
+
+ Resources/core/icons/telephone-minus-fill.svg
+
+ hash2
+
+ 90ZKvDz3B4hH69P1ym6BqV7oFXU6styM6UA9slW4EcY=
+
+
+ Resources/core/icons/telephone-minus.svg
+
+ hash2
+
+ x1Tj6xqxIghmRcSJwfF9F1RkWPByZ9yB4GAY1wnh08Q=
+
+
+ Resources/core/icons/telephone-outbound-fill.svg
+
+ hash2
+
+ hQiPS6KCTn+aRlvLKxU124eYI6gyREOP/tBVamNVziQ=
+
+
+ Resources/core/icons/telephone-outbound.svg
+
+ hash2
+
+ 9YKSa4BTSxSURBSFjiDw/Mi6Na91HCuQ1I51quWD6LI=
+
+
+ Resources/core/icons/telephone-plus-fill.svg
+
+ hash2
+
+ hid8lSBQSJBRA17PK2qw6Qz3nSbzNvIULHhWymB7+ls=
+
+
+ Resources/core/icons/telephone-plus.svg
+
+ hash2
+
+ n8CgdP5ilQ9AGO5/XbmccqMmXzksk8NarDs19aPO51g=
+
+
+ Resources/core/icons/telephone-x-fill.svg
+
+ hash2
+
+ bcNVBAtK1dtjQeC9qecB/hpuyF95KPfS0gommYTmU8M=
+
+
+ Resources/core/icons/telephone-x.svg
+
+ hash2
+
+ P1gz1/hz95XFIQ7ZCcBXTD1DwtPy1v1dGHwqln+oMd8=
+
+
+ Resources/core/icons/telephone.svg
+
+ hash2
+
+ tsChVJUksCArBJdKmI8ldrdy2MUC8Gl/WAFko07Xl7w=
+
+
+ Resources/core/icons/tencent-qq.svg
+
+ hash2
+
+ 5g/XPG/Z//vsYCkBtO4JzvWy4khjWBX+62Aihoio7Ns=
+
+
+ Resources/core/icons/terminal-dash.svg
+
+ hash2
+
+ 642wjWkuqhjAr1TbNt2urCoM6CogI7YPljyRYprYkjM=
+
+
+ Resources/core/icons/terminal-fill.svg
+
+ hash2
+
+ WBqWciJIpzUJUapnF68mZwjNd7lPCsCCUnWpTFZSLd0=
+
+
+ Resources/core/icons/terminal-plus.svg
+
+ hash2
+
+ GDE9lyuHR86wCTbWQ8l4gQw9q8diA3Vjck3akr/mDL8=
+
+
+ Resources/core/icons/terminal-split.svg
+
+ hash2
+
+ nKB24zSdmx0dTtk7PhUE5y78XN0+yMChEITJQYGq+hk=
+
+
+ Resources/core/icons/terminal-x.svg
+
+ hash2
+
+ OW+NUZy/3ZCqiVBMXe43o0gb6FWs3XIlqoYT7nEQbj4=
+
+
+ Resources/core/icons/terminal.svg
+
+ hash2
+
+ 1d+wePuQaMfrsj7aAh+3jVo8A1CFcGRXuGEdQOG1r4M=
+
+
+ Resources/core/icons/text-center.svg
+
+ hash2
+
+ Jglon9skrAyK/fUlPBQuVzu/ZqC8WHlOBppN6szfXXw=
+
+
+ Resources/core/icons/text-indent-left.svg
+
+ hash2
+
+ tTnyumbJlQJA1+OGO/9G8feAUDLBkN81g8rX9ggH5So=
+
+
+ Resources/core/icons/text-indent-right.svg
+
+ hash2
+
+ X7DizOjI9F5yt02b29LIagE5DiBrU8BKuutVel8TwU4=
+
+
+ Resources/core/icons/text-left.svg
+
+ hash2
+
+ cOfeVsxGQqHt8MJbpDsfijG5zA+oFlrBENT87Nxf2Vs=
+
+
+ Resources/core/icons/text-paragraph.svg
+
+ hash2
+
+ qa5tjdbU6k7WKVjTLtF51Uh2uF0zxNC095ediq4TGak=
+
+
+ Resources/core/icons/text-right.svg
+
+ hash2
+
+ lfVgIhnaWjTmqInXTDzyuaOqDG3bIU0by0Aw73p+1gE=
+
+
+ Resources/core/icons/text-wrap.svg
+
+ hash2
+
+ inz/hxSNtw4bW+wp0UP1chHJXlxd0VASdX+84c+n3Wo=
+
+
+ Resources/core/icons/textarea-resize.svg
+
+ hash2
+
+ Ra5+r7G8NopondzYUJ8vOgJV/1vFlD9jW0oMHPgwAKU=
+
+
+ Resources/core/icons/textarea-t.svg
+
+ hash2
+
+ RTwzeRK/o4BfqawQ1xS6kGHSB+mGL4gbWRWbvrWGhSw=
+
+
+ Resources/core/icons/textarea.svg
+
+ hash2
+
+ GiEhbV+wgIfQDVY2u3QOEaZBdXeWfzbYqow1MFbM7uc=
+
+
+ Resources/core/icons/thermometer-half.svg
+
+ hash2
+
+ AJrArTalBAv+svYcjj+n0j6l6N6B3OKjMOwPvbTH+MQ=
+
+
+ Resources/core/icons/thermometer-high.svg
+
+ hash2
+
+ OQWk+j5lVrM/2APcKguOzKCFbh5ZIpKyi6+P37N84hw=
+
+
+ Resources/core/icons/thermometer-low.svg
+
+ hash2
+
+ Vq340e08tgU6zIiVOtUqL2+RxrJPlYNIrc4sZBsarlM=
+
+
+ Resources/core/icons/thermometer-snow.svg
+
+ hash2
+
+ IYhIP5L81qUiL60kqx3chW3DR+ecb1mYhCCFVqw19fI=
+
+
+ Resources/core/icons/thermometer-sun.svg
+
+ hash2
+
+ mOXNskwB9cJVhstgk4ku9Jwzyf7sjLAO/9NytzEivkU=
+
+
+ Resources/core/icons/thermometer.svg
+
+ hash2
+
+ C3L6UKBaRdmwHFdtKcBqJmjzz7LSf6tKH5MXJNXfD3s=
+
+
+ Resources/core/icons/threads-fill.svg
+
+ hash2
+
+ tNbb6JqfVnKRJzzdHIjgryyH5npWQ18zSW0Yw+RBkWg=
+
+
+ Resources/core/icons/threads.svg
+
+ hash2
+
+ UgFXuolZqfYy3RKm6oDvOUluyPNlTmNhcBYHJl5kcoI=
+
+
+ Resources/core/icons/three-dots-vertical.svg
+
+ hash2
+
+ GxcQtV4co4EaCFQp+TMyP+/MIUWZ4hF297IGHuXAfyc=
+
+
+ Resources/core/icons/three-dots.svg
+
+ hash2
+
+ I/0zYes/GqDx6QaBE4KS1QOhY+5+enzECdzHQ/xIcq4=
+
+
+ Resources/core/icons/thunderbolt-fill.svg
+
+ hash2
+
+ i6xJij+QlPCZUPETfYLwc8aDO1b7LG6OjLvvTkyScSg=
+
+
+ Resources/core/icons/thunderbolt.svg
+
+ hash2
+
+ V2b7nm4CZTQuotiFxPdKChgnKNCyxHxaXygoaGSddTk=
+
+
+ Resources/core/icons/ticket-detailed-fill.svg
+
+ hash2
+
+ f06V8tOC7eTfYF8u1FMspHKfcKC8w8ekP8cdni+xxCU=
+
+
+ Resources/core/icons/ticket-detailed.svg
+
+ hash2
+
+ rxDnwtBeNHTLKjaFXiV2olWMAR7LdiJVX1eOdif5wH4=
+
+
+ Resources/core/icons/ticket-fill.svg
+
+ hash2
+
+ jLon+crNGxXyRgYC3lUOq+UFF4WU4z3pck+ovGbAqlI=
+
+
+ Resources/core/icons/ticket-perforated-fill.svg
+
+ hash2
+
+ TlW9vSQPNEpOBKN/mUwF9LZh5bvCumxywjjWmdApkKc=
+
+
+ Resources/core/icons/ticket-perforated.svg
+
+ hash2
+
+ CsgPS/KdYs1q9jlCpgKkdBdAcQw56cbvdBccXC6/uM4=
+
+
+ Resources/core/icons/ticket.svg
+
+ hash2
+
+ 9OKGbUGSeW+AKEEZZvrZEXbW6E1zDNgZ7Oy+FbqysW0=
+
+
+ Resources/core/icons/tiktok.svg
+
+ hash2
+
+ 3yXtV1/PDQ2W+nCMKgf8R9RRjlQsaPa7HvfVdEc2FTM=
+
+
+ Resources/core/icons/toggle-off.svg
+
+ hash2
+
+ BQHIOm1TdDzGwbW+XoN7sLcgNMI+28TyxqZgwOjCgcc=
+
+
+ Resources/core/icons/toggle-on.svg
+
+ hash2
+
+ qWaupG1BX6a7ZXF4sYw3gTeLReiAw3jiShkEqlKCPoQ=
+
+
+ Resources/core/icons/toggle2-off.svg
+
+ hash2
+
+ FNCOzRpSGQCgT9Kgeu7US118XKgr7lVJ05xHIXQAhBQ=
+
+
+ Resources/core/icons/toggle2-on.svg
+
+ hash2
+
+ RD7mRjxEQ+PT09OkWpVpCwrkIBmhrqwusx+VdIRjzck=
+
+
+ Resources/core/icons/toggles.svg
+
+ hash2
+
+ qg6pf9BI2ZE9Rt8vFqURH2vma1UTTfyCwmHcx2/2Sg0=
+
+
+ Resources/core/icons/toggles2.svg
+
+ hash2
+
+ J80NITB7jKw6Sb6LCjIWGH3SJXHf6xGOwYOQh+DIPII=
+
+
+ Resources/core/icons/tools.svg
+
+ hash2
+
+ ritJcoQT1/MuIqpkw9SQ2yqgJLoE/fYBf4UxreoljUs=
+
+
+ Resources/core/icons/tornado.svg
+
+ hash2
+
+ A1Md+Z/7jRzFAO9S/Sh+jk3ljLRLpnriitdJ2K2u3q0=
+
+
+ Resources/core/icons/train-freight-front-fill.svg
+
+ hash2
+
+ yV8NAOtw7q859m0k3Ik5FwiXqsGJDyGg2IsUPiYwylk=
+
+
+ Resources/core/icons/train-freight-front.svg
+
+ hash2
+
+ fDRALrU6kqug8dvamo3lsB75fvzkbNryAnpv3bYNf28=
+
+
+ Resources/core/icons/train-front-fill.svg
+
+ hash2
+
+ HIEwcowQCHrU0DEnQK66baCpfWlf/yTBJtGQIEvTWkQ=
+
+
+ Resources/core/icons/train-front.svg
+
+ hash2
+
+ 8hVBF+lp8qBdP7+IzM8yFOoFTbwL1UIc620sJw3C9w0=
+
+
+ Resources/core/icons/train-lightrail-front-fill.svg
+
+ hash2
+
+ RFMNiGZnGHZHUQcIg141tM/j/HRIa/NmsBkaVsISyn0=
+
+
+ Resources/core/icons/train-lightrail-front.svg
+
+ hash2
+
+ RfyLY9Ptkq9unfR//KBMnLx3mpQlSBkcSg43nmB2L2Y=
+
+
+ Resources/core/icons/translate.svg
+
+ hash2
+
+ 0cx9TzgBSygChWY1g1O8a29jIWDRsU9JTM7A9y/03Is=
+
+
+ Resources/core/icons/transparency.svg
+
+ hash2
+
+ Xyz322VGS9gSpF3FUE7R3g7/qloydYfwKoYE7Y7Zp5k=
+
+
+ Resources/core/icons/trash-fill.svg
+
+ hash2
+
+ B6ypAIwim3jPa7XMWvPp/ODJf4PO+x4D9KeXJqH3gwc=
+
+
+ Resources/core/icons/trash.svg
+
+ hash2
+
+ 3VdvRuGQub8wjL8UOq9vxWQS/7HkV6eIktS+9iTfjx8=
+
+
+ Resources/core/icons/trash2-fill.svg
+
+ hash2
+
+ JbKhFWnDu3VhTmxtEIv3xALh8OcacEJZx4BYrWx/4ug=
+
+
+ Resources/core/icons/trash2.svg
+
+ hash2
+
+ bmkOmqK4uCSbUQwX7Hx+837FiK84U+lwBDt8wLmRZss=
+
+
+ Resources/core/icons/trash3-fill.svg
+
+ hash2
+
+ UPh4IPcuHcKEsoiROrepBRIcsszmNZLUjklzacd7RRM=
+
+
+ Resources/core/icons/trash3.svg
+
+ hash2
+
+ gpXgRLW6I90ZOT4a+XxCggtse5+vh7JHHFdxdSnkuZw=
+
+
+ Resources/core/icons/tree-fill.svg
+
+ hash2
+
+ /d8BR/vQz5PF5krK+1wRHnDy+4YHOfNe5di9/pflThU=
+
+
+ Resources/core/icons/tree.svg
+
+ hash2
+
+ 3knrqaFRFIrXuKh1vVKxT4UiE4YlDF5wdkYAXLfXNPY=
+
+
+ Resources/core/icons/trello.svg
+
+ hash2
+
+ HLQhbTt7lsB1QtIgh73o/93zT59r8PQKnMFdhCeH0RU=
+
+
+ Resources/core/icons/triangle-fill.svg
+
+ hash2
+
+ iq0UnZkXMn6isPYjBuhHy97o7cL+0XyQawq3KwyWZRo=
+
+
+ Resources/core/icons/triangle-half.svg
+
+ hash2
+
+ SLd+6Tqd2TtNCMbifLlRm9u1kCfl+3uxp4JlmJrVmDA=
+
+
+ Resources/core/icons/triangle.svg
+
+ hash2
+
+ JDfGkpnIA9TRnV9Jkxgl+5XPpyZrDGSIed/0Z3X9Vyw=
+
+
+ Resources/core/icons/trophy-fill.svg
+
+ hash2
+
+ TkPk0FL8cv8/4jIb4g3RNKbzZe4zn0gYCamEuzYfih4=
+
+
+ Resources/core/icons/trophy.svg
+
+ hash2
+
+ lx2IHu1TnJHrt3bvTfYJ5AMhSn2Af3TSaCmKsEVPRCc=
+
+
+ Resources/core/icons/tropical-storm.svg
+
+ hash2
+
+ GFOU4yIz9/qmED0h4+TKo6VBLIUM8jlJiGfU1O0hO2M=
+
+
+ Resources/core/icons/truck-flatbed.svg
+
+ hash2
+
+ 2P57TxOfy2mqGDGMzMk1rOgNUpqmA3V7zOqBfF1BmPc=
+
+
+ Resources/core/icons/truck-front-fill.svg
+
+ hash2
+
+ l5TFD9+VjesH6E68GzELlrE3MdQCojUFceXpkTZp5zo=
+
+
+ Resources/core/icons/truck-front.svg
+
+ hash2
+
+ HOR/GVwMAD9RJajkAeM/+N7cLN4z2+aMALeVXNa9Ul4=
+
+
+ Resources/core/icons/truck.svg
+
+ hash2
+
+ JXcRJESPE5fFDNfzVwDVsozInI/Uf6uVSyDwut6YMq0=
+
+
+ Resources/core/icons/tsunami.svg
+
+ hash2
+
+ yf0HhXJ70YD047vHJwzZv54Ig5vbXr7/E2pwKLp+6bU=
+
+
+ Resources/core/icons/tux.svg
+
+ hash2
+
+ NCdKKF9wjVODB0c+XXHpl6tfGWtdyypB8wnsyfKEIvk=
+
+
+ Resources/core/icons/tv-fill.svg
+
+ hash2
+
+ drchedezZnenUuyqaSFQF2IG2FkIWQHfLd7io3YZKm8=
+
+
+ Resources/core/icons/tv.svg
+
+ hash2
+
+ 6iLdSsfdUYuqx0XcQ8p8MFah8dbgiIy6yoa6dyndbv4=
+
+
+ Resources/core/icons/twitch.svg
+
+ hash2
+
+ XuVkJF+8maCA764SSfBryZe7kkPA2k8Xlnz2OXuK1g0=
+
+
+ Resources/core/icons/twitter-x.svg
+
+ hash2
+
+ gSl/5NLMrhIVSQmNVhSrpYHlI8W35oPdzsXQNm9Ays8=
+
+
+ Resources/core/icons/twitter.svg
+
+ hash2
+
+ g5prgSUq85Bd+1LR84PelPfomeM4h5fVTD6ILTp/Gog=
+
+
+ Resources/core/icons/type-bold.svg
+
+ hash2
+
+ 2sKDRmuWDmtrh+8MZu3FNc4jupqvCdPz0BYUKDKCfnY=
+
+
+ Resources/core/icons/type-h1.svg
+
+ hash2
+
+ WHEiqa2CfUzgQEgXEQ2wGM0DyTCJLFFZVJMN+5QCIrc=
+
+
+ Resources/core/icons/type-h2.svg
+
+ hash2
+
+ IPwgCm+qk9XhHUAdkUhzedz3t+FtzdTVWg4+nWSPDYk=
+
+
+ Resources/core/icons/type-h3.svg
+
+ hash2
+
+ 8N4S4u7fKtIcE+FIpHfNwru/vT1VYE4wFiyXXQ/pneY=
+
+
+ Resources/core/icons/type-h4.svg
+
+ hash2
+
+ 9wjedQ38a+qv1twbtxxQ/AoMPI/g/bani26AlGAgLxc=
+
+
+ Resources/core/icons/type-h5.svg
+
+ hash2
+
+ LmhtvHSDwLn9P0fkHr53PPv+x4i0ItAdjTtxq+3Euds=
+
+
+ Resources/core/icons/type-h6.svg
+
+ hash2
+
+ c8FSl2mVUXoPenDCHQsbhSMAIETsDS8KoocDDzET+X4=
+
+
+ Resources/core/icons/type-italic.svg
+
+ hash2
+
+ gFj5Na6eUvdtRDaR8xOwmiRKHZbnYd3h/NUpH1DEzDA=
+
+
+ Resources/core/icons/type-strikethrough.svg
+
+ hash2
+
+ l1rxkJFqd8teLobDTBo8wpIVX/v8048PQWtvdkuzE+A=
+
+
+ Resources/core/icons/type-underline.svg
+
+ hash2
+
+ i0Z9vImrvcD+IBj1f3TTjhueGA9zp6SaYhQiuurc8+E=
+
+
+ Resources/core/icons/type.svg
+
+ hash2
+
+ n/oJp75lm5nu8SJNhQUYQUxo7NzuQk0qFPYbV97n9no=
+
+
+ Resources/core/icons/typescript.svg
+
+ hash2
+
+ zXTjLTOoOBHsZAwrWF9NVZjgXitPm5lROEBcIwvG76M=
+
+
+ Resources/core/icons/ubuntu.svg
+
+ hash2
+
+ 5f0ugkKllI8qJWV/F1+o3yrBB6UO5IAXp5Odkir4yUs=
+
+
+ Resources/core/icons/ui-checks-grid.svg
+
+ hash2
+
+ +Al2z2eFh/FBM4QcopsClYR3Md4yKaYPeY4G/Yg983s=
+
+
+ Resources/core/icons/ui-checks.svg
+
+ hash2
+
+ 24J1puvtiHHhzAKH4j2UUvQYUGasSpFpLTpNCNjwNf0=
+
+
+ Resources/core/icons/ui-radios-grid.svg
+
+ hash2
+
+ 0Yq/61ySh+adZas2u0tR1REKq7HWB9Yq60fEslYULN0=
+
+
+ Resources/core/icons/ui-radios.svg
+
+ hash2
+
+ tfz0en8lLW9Z33lWVC8rC5/aS4oyuDymV+yEmRcEvhc=
+
+
+ Resources/core/icons/umbrella-fill.svg
+
+ hash2
+
+ 7ax7Ohgf5RZ1I+ojNV7Oe6Q6FtT1t706AViFGl8cpu8=
+
+
+ Resources/core/icons/umbrella.svg
+
+ hash2
+
+ 8DbXfko9/rku6cRz1sUXAfM1Nnk+zQWlv4ZPEhhWqyM=
+
+
+ Resources/core/icons/unindent.svg
+
+ hash2
+
+ kAAJDreqO/QrqaiGcWhB3e0ehGVsFT6Spz9KkX8ney4=
+
+
+ Resources/core/icons/union.svg
+
+ hash2
+
+ eGpFPGwTYAo+1V/IgXCNMAnfMyoklyK//rat3C+6jkQ=
+
+
+ Resources/core/icons/unity.svg
+
+ hash2
+
+ qfWkhyj4qj9koU9/rfh0AdAYDH6y8WoJYqMYzPoJcDk=
+
+
+ Resources/core/icons/universal-access-circle.svg
+
+ hash2
+
+ l+/xgUhXBC/KovXpzqYNuk5I0XgJaetecgE8vVo7DSc=
+
+
+ Resources/core/icons/universal-access.svg
+
+ hash2
+
+ nPfVWfGdRpMJydiIcWxcUHyngIeqZg+hiBrDQbwRB5Q=
+
+
+ Resources/core/icons/unlock-fill.svg
+
+ hash2
+
+ 1kqXRX92PkDRu3gMKIUpX0+3teuPfzDYK6xQYWssa6Q=
+
+
+ Resources/core/icons/unlock.svg
+
+ hash2
+
+ NzQgw7IlYZijZZh++U4xeUHnXAxNkVCDa+NRrIao/jQ=
+
+
+ Resources/core/icons/unlock2-fill.svg
+
+ hash2
+
+ KpSVULGnVNnuSQiAan4q85KNg4BbFZDZ0PIIEx81Iq8=
+
+
+ Resources/core/icons/unlock2.svg
+
+ hash2
+
+ yEzfLG9ApW8vdB/qY5APK/2iEBQom/HmtbR0V3Pav4g=
+
+
+ Resources/core/icons/upc-scan.svg
+
+ hash2
+
+ 9tHf8WPJFUga7SI9T1ECp/XsI0JSY/lAlNcVo8yeAek=
+
+
+ Resources/core/icons/upc.svg
+
+ hash2
+
+ cBlKzA8uRjgH1/2lpsXD47wx5Lr+g9JAumXBqeG3znM=
+
+
+ Resources/core/icons/upload.svg
+
+ hash2
+
+ yuE5SYmBIbFAiMSzpvPgZ1XY+FQD/QVktpbTpFVIw7s=
+
+
+ Resources/core/icons/usb-c-fill.svg
+
+ hash2
+
+ C82lAKFd9vK8u7BJ6tjw+zGYQAJGz/+W9X4YeeXejzA=
+
+
+ Resources/core/icons/usb-c.svg
+
+ hash2
+
+ N56PgZ+jb1G707rj3H4LeTlYBMn2vWm6ljOQmjlegKw=
+
+
+ Resources/core/icons/usb-drive-fill.svg
+
+ hash2
+
+ W9E7aOpdSKtmaBdK9CpeY1Teb3ZRocYPRvWvp/PibGg=
+
+
+ Resources/core/icons/usb-drive.svg
+
+ hash2
+
+ TgCge1a2wEI1UbkaR4h5KLxYjMDqsICM+CkQP8VqWy4=
+
+
+ Resources/core/icons/usb-fill.svg
+
+ hash2
+
+ rq84924ifO5Hig4BPmuMocreaSGuhq9s2pYWTeT4rAk=
+
+
+ Resources/core/icons/usb-micro-fill.svg
+
+ hash2
+
+ heXnSCfWti806MlFZOacK+JhNMCKhL/MGoIOLSnCQJ8=
+
+
+ Resources/core/icons/usb-micro.svg
+
+ hash2
+
+ LUcvYZubVk96wQ6tqzeOfPHk7vKvP8ripiRT9B6JtaE=
+
+
+ Resources/core/icons/usb-mini-fill.svg
+
+ hash2
+
+ J0/PbNM34g/ihLPu4/fNYwIRg3hffpmAqhPL+/YL+Fk=
+
+
+ Resources/core/icons/usb-mini.svg
+
+ hash2
+
+ Q/gIlJvkcJaZGBT9KjXmZQH7abAs0PJDLVfodCj+8jA=
+
+
+ Resources/core/icons/usb-plug-fill.svg
+
+ hash2
+
+ LESlFOJb45jOi+CxVXSMcFex3QeQHWHtqAZqSKRX7Cw=
+
+
+ Resources/core/icons/usb-plug.svg
+
+ hash2
+
+ Q45TIQnE+KgVT/Zk6g++oHMND7L847mR5ImHHoBqEJs=
+
+
+ Resources/core/icons/usb-symbol.svg
+
+ hash2
+
+ xrOSsoqAUOYbpv2Fxxw4eBpJ++xY5jwi6JDgiDZF1yU=
+
+
+ Resources/core/icons/usb.svg
+
+ hash2
+
+ By1SwgXJc1km7jFekiDaBef6YOB7WF1wgYjf0tg0zgs=
+
+
+ Resources/core/icons/valentine.svg
+
+ hash2
+
+ Z0NsIX201Qwf4PHJpjg0GlhpQldXEdzCg8J8WNhwzgc=
+
+
+ Resources/core/icons/valentine2.svg
+
+ hash2
+
+ puQzUPRH7LPAbW6e4qtubwjHO0cX2Z0Yw0UiRjX9a2s=
+
+
+ Resources/core/icons/vector-pen.svg
+
+ hash2
+
+ w68Aj5NIx79tRc4iJnrfiTmPU576ZOnWhZGrt0My3Xs=
+
+
+ Resources/core/icons/view-list.svg
+
+ hash2
+
+ ilk/7wTSYwgaU1JOhZwzpp7zWegWIPQTTK5S4MDjZQI=
+
+
+ Resources/core/icons/view-stacked.svg
+
+ hash2
+
+ 9YyHVXIcfzncnKrLbSbsTpiMNQ8nJsCflvKCQ3CB29g=
+
+
+ Resources/core/icons/vignette.svg
+
+ hash2
+
+ b2VTLdyrNZ4EBmwkvG0SHkHWnaOhxUCwUPrrAAHWAgg=
+
+
+ Resources/core/icons/vimeo.svg
+
+ hash2
+
+ j0RHU8EmA7QV2W6FHh2DUZl5150N8IHSnPHCrYd8LnU=
+
+
+ Resources/core/icons/vinyl-fill.svg
+
+ hash2
+
+ m0pwBhKWKP/D4mof4TwcPYZ/0+8gWWfGPd6a2dzObWQ=
+
+
+ Resources/core/icons/vinyl.svg
+
+ hash2
+
+ w9qT86AXndKfFZWACVzKfEJdyOhZxPDgrUQcMGu7x48=
+
+
+ Resources/core/icons/virus.svg
+
+ hash2
+
+ rbn5ji5IjNdzKVQxJxutDuowlfcCUXyAOxwSCvWvqU0=
+
+
+ Resources/core/icons/virus2.svg
+
+ hash2
+
+ eZxDvUhdD1WAATzOMfz8pgrpD9o/vEYAbDQVbX4nKmo=
+
+
+ Resources/core/icons/voicemail.svg
+
+ hash2
+
+ lcYIF5VVl70aoJH3kyYruNW6vKya42Z4aP9StmdWLK8=
+
+
+ Resources/core/icons/volume-down-fill.svg
+
+ hash2
+
+ fppCNMJZCkR4jqrxDXUbvgHuetUKA+bU1bRV3F+zVQM=
+
+
+ Resources/core/icons/volume-down.svg
+
+ hash2
+
+ ydQM5aH+DmZfIpFRi9vN0JmqrFSDrza85G9FMG1f+B8=
+
+
+ Resources/core/icons/volume-mute-fill.svg
+
+ hash2
+
+ HsHbuuuD8pQkXX/XOMdHvq7nymst0eDiCt2tFMCGvXU=
+
+
+ Resources/core/icons/volume-mute.svg
+
+ hash2
+
+ gYplDwUHsqgK5wNpQwcMN6cgSBDxLNbg4o5YjUTarIU=
+
+
+ Resources/core/icons/volume-off-fill.svg
+
+ hash2
+
+ sIxNLGvvcHYQXxHNoqOSIPwu9u8I9xNKMnnEXAA5d4E=
+
+
+ Resources/core/icons/volume-off.svg
+
+ hash2
+
+ SFBPv7s3N5njgR1R4U8/HdbWAUOyI/oA+u/q56hN2Dc=
+
+
+ Resources/core/icons/volume-up-fill.svg
+
+ hash2
+
+ 2wjAracBVju4yTf4NE2RnjEMl7RPaPpT4R+Bd//BZPw=
+
+
+ Resources/core/icons/volume-up.svg
+
+ hash2
+
+ jg01TIPwNxf9ZCPwDaoecscjv/rGvJV8EF3sI+Mbm6A=
+
+
+ Resources/core/icons/vr.svg
+
+ hash2
+
+ i5yAPy1LDoLJUUxb5B8KqDobqjCXwm7DHgU2aqhfbGs=
+
+
+ Resources/core/icons/wallet-fill.svg
+
+ hash2
+
+ 5dGPTCDDpHwT1I7LgRG7+N6rZC/+cQ4UQfGwulS25D4=
+
+
+ Resources/core/icons/wallet.svg
+
+ hash2
+
+ ns3JwMr+DoNHa9r3tcsiFHMSxmXuoImduP+9CNnKipY=
+
+
+ Resources/core/icons/wallet2.svg
+
+ hash2
+
+ sTyaFnssg8B22ZolSA6+HSSEeviJIZfQRjJIm/EPjNM=
+
+
+ Resources/core/icons/warning.svg
+
+ hash2
+
+ /t9fnMtadSzwb8wiEBJ3MSDA376XmgUYSU5IXLpkhdg=
+
+
+ Resources/core/icons/watch.svg
+
+ hash2
+
+ U6EqBL0cYiBK8zV0xg6JyDC1sTcBYJ5y9/fheompMqw=
+
+
+ Resources/core/icons/water.svg
+
+ hash2
+
+ 2IuXTOX/Bi0/CzOhy962n1ni82Xbz4OKZ8Jp4vgLfTc=
+
+
+ Resources/core/icons/webcam-fill.svg
+
+ hash2
+
+ 3O4mH12RDbRDAq5k/l9pkAuZzYIiyHP2kZ+7UkgeD4c=
+
+
+ Resources/core/icons/webcam.svg
+
+ hash2
+
+ Nq5yawYOTfgBpg0FH+JEjcmGyQo3/Ic7nc9VQmsJWiw=
+
+
+ Resources/core/icons/wechat.svg
+
+ hash2
+
+ PxhxzLyuWAGolmdTpJHTzjfaVs3FLsiSbOVSn5RWGZ8=
+
+
+ Resources/core/icons/whatsapp.svg
+
+ hash2
+
+ Pw64xBxMwIy0x21Im4j1l9XColt8F6wcfJwrRiOpufM=
+
+
+ Resources/core/icons/wifi-1.svg
+
+ hash2
+
+ 7rI92SB9JmWC2XeYgpFbEFaMT+4E5JxfF5a9vPSEDoU=
+
+
+ Resources/core/icons/wifi-2.svg
+
+ hash2
+
+ ghrQB4Fs3oxSTP9bhwbCtxJusaq86MXRg13pV1QHSWc=
+
+
+ Resources/core/icons/wifi-off.svg
+
+ hash2
+
+ yfKhH8TwQEN/y0LCqXAmTBBRnf1RwcPjBgx89/+d7wY=
+
+
+ Resources/core/icons/wifi.svg
+
+ hash2
+
+ WkCupqVkg6+iTk2fJzmI3iNPXrobH4jyQwxgsiV9Eig=
+
+
+ Resources/core/icons/wikipedia.svg
+
+ hash2
+
+ Q8gMjgBbpVvQyTV4wAFlHrm/gtgygL69UOm1zGf4dT8=
+
+
+ Resources/core/icons/wind.svg
+
+ hash2
+
+ NB57DLfvWqsEzpQ2cSd4Ch6FCsZMzU3nvkHIK9s0w6o=
+
+
+ Resources/core/icons/window-dash.svg
+
+ hash2
+
+ IjpfxlfqLFZ8NTOGtM8OJu2Eva9CwOqD2YqkhQ9UoDo=
+
+
+ Resources/core/icons/window-desktop.svg
+
+ hash2
+
+ O0KER51+jF21k8amcbqqZdtz93zH/jNG4DrZnS+5gMM=
+
+
+ Resources/core/icons/window-dock.svg
+
+ hash2
+
+ EvgV5lWFGaoCP8bPaK0SZWne7ot/1cSP5lTHcnHIgEI=
+
+
+ Resources/core/icons/window-fullscreen.svg
+
+ hash2
+
+ pv9bvF2Saz7D+ajqvgA7CoBym0n7bdD/tSNUTqE/Dag=
+
+
+ Resources/core/icons/window-plus.svg
+
+ hash2
+
+ doXFJRWsDS0P0ZU33NFZWhsFdsY7EExgBd5krbU3FxY=
+
+
+ Resources/core/icons/window-sidebar.svg
+
+ hash2
+
+ H6kqJT6SWc9kPVDylaJHruTIWOOVUeV1X/2jwbFRiWk=
+
+
+ Resources/core/icons/window-split.svg
+
+ hash2
+
+ LJH1OpAXOknCb4ud7tUb6m7RYpAMsLD0rrXQ4+pVg1s=
+
+
+ Resources/core/icons/window-stack.svg
+
+ hash2
+
+ U4+Xd3LHFrXzIvTjRAah6L0VqFQmPEfGKtxeJB6eU2A=
+
+
+ Resources/core/icons/window-x.svg
+
+ hash2
+
+ lAivcliPiqUlf4Njj97nHtNPe9+NrrLegSQL7y9TBHI=
+
+
+ Resources/core/icons/window.svg
+
+ hash2
+
+ R98nPnIpJpQREk7wlKs2n5qpCtB1D49OtPuDa69z4nE=
+
+
+ Resources/core/icons/windows.svg
+
+ hash2
+
+ nwZcbpXZ08JapCLzdOglnDU6gcYgovIcXhH6FlOnOVE=
+
+
+ Resources/core/icons/wordpress.svg
+
+ hash2
+
+ txhunxNrHAJ5iuNznnmztcisUmWU/Yn4lwqcZGn/s0I=
+
+
+ Resources/core/icons/wrench-adjustable-circle-fill.svg
+
+ hash2
+
+ LHmw0tm+gNsNKp4WYwXkO7AA8Srejs9P8tL6FVWbRq0=
+
+
+ Resources/core/icons/wrench-adjustable-circle.svg
+
+ hash2
+
+ KtguPqmPlFEvFa7NdG9UP8s5W6tfU/ZjJENTxqjQyaw=
+
+
+ Resources/core/icons/wrench-adjustable.svg
+
+ hash2
+
+ NFzHXV7rljJ4IffmPRZd7Dyd5zakpG3vMbV2LriIci8=
+
+
+ Resources/core/icons/wrench.svg
+
+ hash2
+
+ AdRhV0pw3AwcabtU3GRWSs9EO++AmvgwbPJHD8xElEM=
+
+
+ Resources/core/icons/x-circle-fill.svg
+
+ hash2
+
+ 6ORfnWR1Qaqtiv87jwwJ2enNnJBgdFxMMYxqqnUEsnM=
+
+
+ Resources/core/icons/x-circle.svg
+
+ hash2
+
+ TCM0sPjYJDQxDJ2BFsF+khUAFeyOpj8NcHAQo7/YfPE=
+
+
+ Resources/core/icons/x-diamond-fill.svg
+
+ hash2
+
+ Q7K0F7AX9OTJ6N86eZ+RyokwVqezLBv9hfPLMYE4p2Y=
+
+
+ Resources/core/icons/x-diamond.svg
+
+ hash2
+
+ 3B7f4v2sJca2RjNqhXVRrlBnuiuxA8gUhyOarZEy8o0=
+
+
+ Resources/core/icons/x-lg.svg
+
+ hash2
+
+ MkbMK31MBPlESCmXRsljJ8sAEW7BQR7DbLs7ecBlLHA=
+
+
+ Resources/core/icons/x-octagon-fill.svg
+
+ hash2
+
+ 2dbUzpA3u81kN6AYFWZX62ZgHdNOPHBoDIVSMaGqjVs=
+
+
+ Resources/core/icons/x-octagon.svg
+
+ hash2
+
+ nX5WMI+NxpGjIE3um0g81gju00IsCHmZTaA6qYFOEus=
+
+
+ Resources/core/icons/x-square-fill.svg
+
+ hash2
+
+ hMBCS6elOQYO0afRUATQk5zBGnjNVo6E11pIlrEgzuY=
+
+
+ Resources/core/icons/x-square.svg
+
+ hash2
+
+ Dv3Y0BStD57waKAc0dnWTGHA68kUPxI+t93wOW6SJRo=
+
+
+ Resources/core/icons/x.svg
+
+ hash2
+
+ nGRdhZ7qNt66l1BpHvCb/PT4hXb2OMzVLr+7X+pY3Is=
+
+
+ Resources/core/icons/xbox.svg
+
+ hash2
+
+ Pe9HUc8qBMlLMykBj+gGmAu0PGXSRLB34jMZR+rQhug=
+
+
+ Resources/core/icons/yelp.svg
+
+ hash2
+
+ nkco5H9hl/vrwgCbIQgCIEjAAmWKeQsxX3Rslm0w3e4=
+
+
+ Resources/core/icons/yin-yang.svg
+
+ hash2
+
+ pNBG2IU7kDoUfXIFQ+bVkKUJn6VNJ8f4kY9CKthCpD8=
+
+
+ Resources/core/icons/youtube.svg
+
+ hash2
+
+ /tB0c6Ug90ligAMLLg+j3VFeoHmOAmyjJltr7gARq7Y=
+
+
+ Resources/core/icons/zoom-in.svg
+
+ hash2
+
+ a4QZRtF3JofOg0jpbg6j3uLYxTxG9baWIfq5redza0w=
+
+
+ Resources/core/icons/zoom-out.svg
+
+ hash2
+
+ nS/PsVTPHEvi/HlVCvTOa7/zqKv9gb1vlblnhC1ipyc=
+
+
+ Resources/core/log.py
+
+ hash2
+
+ bkq9ZYaVSyMzRHLrZOkx/1oCb5+M/I9BCehvJFnewoE=
+
+
+ Resources/core/network/__pycache__/diagnostic.cpython-311.pyc
+
+ hash2
+
+ GkdsMaNHqUx8RLGfNYR4nAmK9wePCf7opRxhlC3wKaM=
+
+
+ Resources/core/network/__pycache__/diagnostic.cpython-312.pyc
+
+ hash2
+
+ onv70udQXvnJkKaY0zmXjc1RQU2PUj6CcS3L2NjWOr8=
+
+
+ Resources/core/network/__pycache__/tools.cpython-311.pyc
+
+ hash2
+
+ AGiKl9lU3v6/SIHtqZpCO+r+Myn7+Sfzch8z739hJiw=
+
+
+ Resources/core/network/__pycache__/tools.cpython-312.pyc
+
+ hash2
+
+ 7/lqIoEi9ArUdRHksns/LLluuoNqsvurlwZvORnHaNk=
+
+
+ Resources/core/network/diagnostic.py
+
+ hash2
+
+ +rRiRIy1qzrI2wgKhdvXnVlZ8tz55up4PV8wmePUpgc=
+
+
+ Resources/core/network/tools.py
+
+ hash2
+
+ NH/0OFPnw1srKNYvrmcoN72xR7rN77/4wUdEwXBDDEw=
+
+
+ Resources/core/service.py
+
+ hash2
+
+ RNpM7u+p1lpzlFDEIty/K1yaRHP7TkXwnSJks/9uQ9w=
+
+
+ Resources/core/src/bin/nssm/windows/x86/nssm.exe
+
+ hash2
+
+ RyIyyoIbXC71YqsH9TY4vCzILq6EzqE/vmdNYCK2SBw=
+
+
+ Resources/core/src/bin/nssm/windows/x86_64/nssm.exe
+
+ hash2
+
+ 9onumvlLAOnj8LsHKzTKryB/Mty09Xgvyco1HfmgbJc=
+
+
+ Resources/core/styles/style.css
+
+ hash2
+
+ BuixOG8lj2u55nsAz0cVFGPAb7YGBPlgZIIyV5XaaY0=
+
+
+ Resources/core/ui.py
+
+ hash2
+
+ AMU2ewv+s4v01eAvVCp+7Ay0RzR3uBJ/HUgA6FJiwjM=
+
+
+ Resources/core/update.sh
+
+ hash2
+
+ mRjJbMVVN1xuiopjFUuA5KTVOLcLwW2dWF+z+utMK+g=
+
+
+ Resources/icon.icns
+
+ hash2
+
+ zC8szArVDkE5j6dHG3JJh2teij+70RTxBpcH5I1x3ys=
+
+
+ Resources/icons/icon.icns
+
+ hash2
+
+ zC8szArVDkE5j6dHG3JJh2teij+70RTxBpcH5I1x3ys=
+
+
+ Resources/icons/icon.png
+
+ hash2
+
+ 6L3lWiSILrc0v6HH1kpcLtwZpXq1HQt7Ud88nl7EPmI=
+
+
+ Resources/icons/icon.svg
+
+ hash2
+
+ 4tVGXSBwtlgpDUX2SGbo5Op7k9jD+VIHYgMZxdCv8RI=
+
+
+ Resources/libcrypto.3.dylib
+
+ symlink
+ ../Frameworks/libcrypto.3.dylib
+
+ Resources/liblzma.5.dylib
+
+ symlink
+ ../Frameworks/liblzma.5.dylib
+
+ Resources/libmpdec.4.dylib
+
+ symlink
+ ../Frameworks/libmpdec.4.dylib
+
+ Resources/libsqlite3.dylib
+
+ symlink
+ ../Frameworks/libsqlite3.dylib
+
+ Resources/libssl.3.dylib
+
+ symlink
+ ../Frameworks/libssl.3.dylib
+
+ Resources/python3.11
+
+ symlink
+ ../Frameworks/python3.11
+
+ Resources/replicator/__init__.py
+
+ hash2
+
+ 4pdtQs79K4WtyKIx/goMQzCHQCai9gS2W5tlkCL4oac=
+
+
+ Resources/replicator/__pycache__/__init__.cpython-311.pyc
+
+ hash2
+
+ a4jlPbZRcWo2ydglsxnksd7hWQUlxN6CMcBOubTmqbo=
+
+
+ Resources/replicator/__pycache__/__init__.cpython-312.pyc
+
+ hash2
+
+ 7QYywWRlhqHIyrdXkPk/w/03lD6+0gzmZUfXaX4Wxr4=
+
+
+ Resources/replicator/__pycache__/job.cpython-311.pyc
+
+ hash2
+
+ IpSHjE6PCtJJ4O+PB+3BlJVMY1rAOhSJM6jfXp+jGOs=
+
+
+ Resources/replicator/__pycache__/job.cpython-312.pyc
+
+ hash2
+
+ 63bQRpalIvRUM/p32rXYSAfw7cU+auDeaMAS50mpCvk=
+
+
+ Resources/replicator/__pycache__/migration.cpython-311.pyc
+
+ hash2
+
+ PLZvYyAcxm2dtixZHgCQfcRA8/ERsUY6WxWEsADSkTw=
+
+
+ Resources/replicator/__pycache__/migration.cpython-312.pyc
+
+ hash2
+
+ F/1rAfBMFpAayatJRqPmFMp9FSFUVfYtZHozSOW9ssM=
+
+
+ Resources/replicator/__pycache__/mount.cpython-311.pyc
+
+ hash2
+
+ Zaxj1AZVwLdTp5NwQbWm/YvEe1KvR3FE2Jzj8k9hHpU=
+
+
+ Resources/replicator/__pycache__/replicator.cpython-311.pyc
+
+ hash2
+
+ Zd22UhaBDjAmn2zSEgDY2Jb5EaYEXi67XuEkIVIQo8U=
+
+
+ Resources/replicator/__pycache__/replicator.cpython-312.pyc
+
+ hash2
+
+ nqv+8PFnOq1PXuLWvlzBAs3BnbYj73fGXyTCj5bj/Ok=
+
+
+ Resources/replicator/__pycache__/ui.cpython-311.pyc
+
+ hash2
+
+ ETBlRbaTmOUa8X6rJz1QK5THRrGGefbYa7gU8UdhtR4=
+
+
+ Resources/replicator/__pycache__/ui.cpython-312.pyc
+
+ hash2
+
+ aFjnqS9ZGZPJxqfoJAKgy0lvImu4JWLXRBLyeITLV8k=
+
+
+ Resources/replicator/job.py
+
+ hash2
+
+ 7xcjdhJ3qc9gaKMFMt3ZfJ/E0OaWGpf8W7ZqPdjNjuM=
+
+
+ Resources/replicator/migration.py
+
+ hash2
+
+ 60S76qF6yY3T5HboA2J3BWLeB//Hhs+Vf6j29JqAg3c=
+
+
+ Resources/replicator/mount.py
+
+ hash2
+
+ WPDR4GoQg/nRRenmd0MVqrKkVCqKwNHP4QnVWqnFIWg=
+
+
+ Resources/replicator/replicator.py
+
+ hash2
+
+ nnhcAy/M5+8oU8AtHNYs1zU2WDa04EfEFaBqvs/3VMk=
+
+
+ Resources/replicator/ui.py
+
+ hash2
+
+ /0iGoP0eoPYyUbt0ZV2huwdnFXgSTVirz8lPepVCkFA=
+
+
+
+ rules
+
+ ^Resources/
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^version.plist$
+
+
+ rules2
+
+ .*\.dSYM($|/)
+
+ weight
+ 11
+
+ ^(.*/)?\.DS_Store$
+
+ omit
+
+ weight
+ 2000
+
+ ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
+
+ nested
+
+ weight
+ 10
+
+ ^.*
+
+ ^Info\.plist$
+
+ omit
+
+ weight
+ 20
+
+ ^PkgInfo$
+
+ omit
+
+ weight
+ 20
+
+ ^Resources/
+
+ weight
+ 20
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^[^/]+$
+
+ nested
+
+ weight
+ 10
+
+ ^embedded\.provisionprofile$
+
+ weight
+ 20
+
+ ^version\.plist$
+
+ weight
+ 20
+
+
+
+
diff --git a/dist/macos/Replicator.dmg b/dist/macos/Replicator.dmg
new file mode 100644
index 00000000..f0532992
Binary files /dev/null and b/dist/macos/Replicator.dmg differ
diff --git a/dist/windows/Replicator-cli.exe b/dist/windows/Replicator-cli.exe
new file mode 100644
index 00000000..b3d181b7
Binary files /dev/null and b/dist/windows/Replicator-cli.exe differ
diff --git a/dist/windows/Replicator-cli.lnk b/dist/windows/Replicator-cli.lnk
new file mode 100644
index 00000000..e1ce1787
Binary files /dev/null and b/dist/windows/Replicator-cli.lnk differ
diff --git a/dist/windows/Replicator.exe b/dist/windows/Replicator.exe
new file mode 100644
index 00000000..cf0f615b
Binary files /dev/null and b/dist/windows/Replicator.exe differ
diff --git a/dist/windows/Replicator.lnk b/dist/windows/Replicator.lnk
new file mode 100644
index 00000000..71251869
Binary files /dev/null and b/dist/windows/Replicator.lnk differ
diff --git a/launch.sh b/launch.sh
new file mode 100755
index 00000000..9eea2dbe
--- /dev/null
+++ b/launch.sh
@@ -0,0 +1,73 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Replicator dev launcher (macOS/Linux + Windows Git Bash)
+# - Creates a virtualenv if missing
+# - Installs runtime deps (prefers requirements.txt if present)
+# - Runs src/main.py
+
+ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+cd "$ROOT_DIR"
+
+VENV_DIR=".venv"
+REQ_FILE="requirements.txt"
+
+# Detect Windows Git Bash (MSYS/MINGW/CYGWIN)
+UNAME_S="$(uname -s 2>/dev/null || echo '')"
+IS_WINDOWS=0
+case "$UNAME_S" in
+ MINGW*|MSYS*|CYGWIN*) IS_WINDOWS=1 ;;
+ *) IS_WINDOWS=0 ;;
+esac
+
+# Choose python command
+PY_CMD=""
+PY_ARGS=()
+
+# Prefer explicit python3.11 on macOS/Linux
+if command -v python3.11 >/dev/null 2>&1; then
+ PY_CMD="python3.11"
+elif [ "$IS_WINDOWS" -eq 1 ] && command -v py >/dev/null 2>&1; then
+ # Prefer Windows Python Launcher (does NOT require python.exe in PATH)
+ PY_CMD="py"
+ PY_ARGS=(-3.11)
+elif command -v python3 >/dev/null 2>&1; then
+ PY_CMD="python3"
+elif command -v python >/dev/null 2>&1; then
+ PY_CMD="python"
+else
+ echo "ERROR: Python not found in PATH. On Windows, install Python 3.11+ and/or ensure the Python Launcher (py) is available." >&2
+ exit 1
+fi
+
+# Resolve venv python/activate paths
+if [ "$IS_WINDOWS" -eq 1 ]; then
+ VENV_PY="$VENV_DIR/Scripts/python.exe"
+ ACTIVATE_SH="$VENV_DIR/Scripts/activate"
+else
+ VENV_PY="$VENV_DIR/bin/python"
+ ACTIVATE_SH="$VENV_DIR/bin/activate"
+fi
+
+# Create venv if needed
+if [ ! -x "$VENV_PY" ]; then
+ echo "Creating virtualenv: $VENV_DIR"
+ # shellcheck disable=SC2086
+ "$PY_CMD" "${PY_ARGS[@]}" -m venv "$VENV_DIR"
+fi
+
+# Activate venv
+# shellcheck disable=SC1090
+source "$ACTIVATE_SH"
+
+python -m pip install --upgrade pip wheel
+
+if [ -f "$REQ_FILE" ]; then
+ echo "Installing requirements from $REQ_FILE"
+ python -m pip install -r "$REQ_FILE"
+else
+ echo "Installing minimal runtime deps (PyQt5)"
+ python -m pip install "PyQt5>=5.15,<6"
+fi
+
+exec python "src/main.py" "$@"
diff --git a/launch.vbs b/launch.vbs
new file mode 100644
index 00000000..f9b3406b
--- /dev/null
+++ b/launch.vbs
@@ -0,0 +1,26 @@
+' Replicator Windows launcher (no console)
+' - Prefers launching the built binary if present
+' - Falls back to launching Git Bash + launch.sh if available
+
+Option Explicit
+
+Dim shell, fso, scriptDir, exePath, bashPath, cmd
+Set shell = CreateObject("WScript.Shell")
+Set fso = CreateObject("Scripting.FileSystemObject")
+
+scriptDir = fso.GetParentFolderName(WScript.ScriptFullName)
+
+' Try built EXE first
+exePath = scriptDir & "\\dist\\windows\\Replicator.exe"
+If fso.FileExists(exePath) Then
+ shell.CurrentDirectory = scriptDir
+ shell.Run Chr(34) & exePath & Chr(34), 1, False
+ WScript.Quit 0
+End If
+
+' Fallback: Git Bash launch.sh (if user is in a dev checkout)
+bashPath = shell.ExpandEnvironmentStrings("%ProgramFiles%") & "\\Git\\bin\\bash.exe"
+If fso.FileExists(bashPath) Then
+ cmd = Chr(34) & bashPath & Chr(34) & " -lc " & Chr(34) & "cd \"" & scriptDir & "\" && ./launch.sh" & Chr(34)
+ shell.Run cmd, 0, False
+End If
diff --git a/src/bin/nssm/windows/x86/nssm.exe b/src/bin/nssm/windows/x86/nssm.exe
new file mode 100644
index 00000000..8faee45b
Binary files /dev/null and b/src/bin/nssm/windows/x86/nssm.exe differ
diff --git a/src/bin/nssm/windows/x86_64/nssm.exe b/src/bin/nssm/windows/x86_64/nssm.exe
new file mode 100644
index 00000000..6ccfe3cf
Binary files /dev/null and b/src/bin/nssm/windows/x86_64/nssm.exe differ
diff --git a/src/core b/src/core
new file mode 160000
index 00000000..f592fc0f
--- /dev/null
+++ b/src/core
@@ -0,0 +1 @@
+Subproject commit f592fc0fd81f8ae66ebed35bab0e088ca6826a03
diff --git a/src/icons/icon.icns b/src/icons/icon.icns
new file mode 100644
index 00000000..e1a2c0f7
Binary files /dev/null and b/src/icons/icon.icns differ
diff --git a/src/icons/icon.png b/src/icons/icon.png
new file mode 100644
index 00000000..e683fe20
Binary files /dev/null and b/src/icons/icon.png differ
diff --git a/src/icons/icon.svg b/src/icons/icon.svg
new file mode 100644
index 00000000..d29f6262
--- /dev/null
+++ b/src/icons/icon.svg
@@ -0,0 +1,51 @@
+
diff --git a/src/main.py b/src/main.py
new file mode 100755
index 00000000..db2e6e80
--- /dev/null
+++ b/src/main.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+# src/main.py
+
+import sys
+import os, sys, traceback
+
+from pathlib import Path
+from core.application import Application
+from core.cli import CommandLine
+from replicator.replicator import Replicator
+
+# ---------------------------------------------------------------------------
+# Customization and start of the application
+# ---------------------------------------------------------------------------
+
+name = "Replicator"
+
+# ---------------------------------------------------------------------------
+# Helpers
+# ---------------------------------------------------------------------------
+
+def has_option():
+ return any(arg.startswith('--') for arg in sys.argv)
+
+def runtime_setup():
+ try:
+ if getattr(sys, "frozen", False):
+ os.chdir(Path(sys.executable).resolve().parent)
+ else:
+ os.chdir(Path(__file__).resolve().parent)
+
+ log_dir = Path.home() / "Library" / "Logs" / "Replicator"
+ log_dir.mkdir(parents=True, exist_ok=True)
+ log_file = log_dir / "replicator-crash.log"
+
+ def excepthook(t, e, tb):
+ with open(log_file, "a", encoding="utf-8") as f:
+ f.write("\n" + "="*80 + "\n")
+ traceback.print_exception(t, e, tb, file=f)
+ sys.__excepthook__(t, e, tb)
+
+ sys.excepthook = excepthook
+ except Exception:
+ pass
+
+# ---------------------------------------------------------------------------
+# Start of the application
+# ---------------------------------------------------------------------------
+
+def start_app():
+ # Perform any runtime setup
+ runtime_setup()
+
+ # Create application and register it with QApplication
+ app = Application(name,sys.argv)
+
+ # Create main window and register it with Application
+ win = Replicator()
+ app.set_mainWindow(win)
+
+ # All other code gets app via QApplication.instance()
+ sys.exit(app.exec_())
+
+def start_cli():
+ # Perform any runtime setup
+ runtime_setup()
+
+ # Create command line application and register it with QApplication
+ cli = CommandLine(name,sys.argv)
+
+ # Create main command line handler and register it with CommandLine
+ handler = Replicator()
+ handler.cli(cli)
+
+ # All other code gets app via QApplication.instance()
+ sys.exit(cli.exec())
+
+# ---------------------------------------------------------------------------
+# Main entry point
+# ---------------------------------------------------------------------------
+
+if __name__ == "__main__":
+ if has_option():
+ start_cli()
+ else:
+ start_app()
diff --git a/src/replicator/__init__.py b/src/replicator/__init__.py
new file mode 100644
index 00000000..7c3a9253
--- /dev/null
+++ b/src/replicator/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python3
+# src/core/__init__.py
+
+from .replicator import Replicator
+from .ui import JobDialog, ScheduleDialog
+from .job import Schedule, Endpoint, Job, JobRunResult, JobStore
+from .migration import Migration
+from .mount import RemoteMountError, MountedEndpoint, mount_endpoint_if_remote
+
+__version__ = "1.0.0"
+
+__all__ = [
+ "Replicator",
+ "JobDialog",
+ "ScheduleDialog",
+ "Schedule",
+ "Endpoint",
+ "Job",
+ "JobRunResult",
+ "JobStore",
+ "Migration",
+ "RemoteMountError",
+ "MountedEndpoint",
+ "mount_endpoint_if_remote",
+]
diff --git a/src/replicator/job.py b/src/replicator/job.py
new file mode 100644
index 00000000..56f102e6
--- /dev/null
+++ b/src/replicator/job.py
@@ -0,0 +1,766 @@
+#!/usr/bin/env python3
+# src/replicator/job.py
+
+from __future__ import annotations
+import json
+from dataclasses import dataclass, field
+from datetime import datetime, timedelta, timezone, time
+from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Tuple, Union
+from urllib.parse import urlparse
+from .mount import RemoteMountError, MountedEndpoint, mount_endpoint_if_remote
+
+try:
+ # corePY SQLite wrapper (preferred)
+ from core.database.sqlite import SQLite # type: ignore
+except Exception: # pragma: no cover
+ SQLite = None # type: ignore
+
+JsonDict = Dict[str, Any]
+
+# ---------------------------------------------------------------------------
+# Data models
+# ---------------------------------------------------------------------------
+
+@dataclass(frozen=True)
+class Endpoint:
+ type: str = "local"
+ location: str = ""
+ auth: JsonDict = field(default_factory=dict)
+
+ def validate(self, role: str) -> List[str]:
+ errs: List[str] = []
+ if not self.type:
+ errs.append(f"{role} endpoint type is required.")
+ if not self.location:
+ errs.append(f"{role} endpoint location is required.")
+ t = (self.type or "").lower()
+ # SMB port validation removed
+ return errs
+
+ def to_db_fields(self, role: str) -> JsonDict:
+ t = (self.type or "local").lower()
+ auth = dict(self.auth or {})
+
+ guest: int = 1
+ username: Optional[str] = None
+ password: Optional[str] = None
+ options: JsonDict = {}
+
+ if t == "local":
+ pass
+ elif t == "smb":
+ guest = 1 if bool(auth.get("guest", True)) else 0
+ username = auth.get("username") or None
+ password = auth.get("password") or None
+ # Optional SMB domain
+ options["domain"] = auth.get("domain")
+ # Optional rclone args (applies to all remote types)
+ if isinstance(auth.get("rcloneArgs"), list):
+ options["rcloneArgs"] = auth.get("rcloneArgs")
+
+ known_keys = {"guest", "username", "password", "domain"}
+ for k, v in auth.items():
+ if k not in known_keys:
+ options[k] = v
+
+ return {
+ "role": role,
+ "type": t,
+ "location": self.location,
+ "port": None,
+ "guest": guest,
+ "username": username,
+ "password": password,
+ "options": json.dumps(options) if options else None,
+ }
+
+ @staticmethod
+ def from_db_row(row: Mapping[str, Any]) -> "Endpoint":
+ t = (row.get("type") or "local").lower()
+ auth: JsonDict = {}
+
+ if t == "local":
+ auth = {}
+ elif t == "smb":
+ auth = {
+ "guest": bool(row.get("guest", 1)),
+ "username": row.get("username") or "",
+ "password": row.get("password") or "",
+ }
+ # domain is stored in options JSON when present
+
+ opt = row.get("options")
+ if opt:
+ try:
+ extra = json.loads(opt)
+ if isinstance(extra, dict):
+ auth.update(extra)
+ except Exception:
+ pass
+
+ return Endpoint(type=t, location=row.get("location") or "", auth=auth)
+
+@dataclass
+class Schedule:
+ enabled: bool = False
+ intervalSeconds: int = 3600
+ windows: Dict[str, Any] = field(default_factory=dict)
+
+ def to_dict(self) -> Dict[str, Any]:
+ return {
+ "enabled": bool(self.enabled),
+ "intervalSeconds": int(self.intervalSeconds or 0),
+ "windows": self.windows if isinstance(self.windows, dict) else {},
+ }
+
+ @staticmethod
+ def from_dict(d: Optional[Dict[str, Any]]) -> "Schedule":
+ d = d or {}
+ enabled = bool(d.get("enabled", False))
+ # intervalSeconds can be on the schedule itself, or in per-day window objects
+ try:
+ interval_s = int(d.get("intervalSeconds") or 0)
+ except Exception:
+ interval_s = 0
+ windows = d.get("windows") if isinstance(d.get("windows"), dict) else {}
+ if interval_s <= 0:
+ # best-effort: derive from first window on any day
+ try:
+ for _k, _v in windows.items():
+ if isinstance(_v, list) and _v and isinstance(_v[0], dict):
+ v = _v[0].get("intervalSeconds")
+ if v is not None:
+ interval_s = int(v or 0)
+ break
+ except Exception:
+ pass
+ if interval_s <= 0:
+ interval_s = 3600
+ return Schedule(enabled=enabled, intervalSeconds=interval_s, windows=windows)
+
+ def validate(self) -> List[str]:
+ errs: List[str] = []
+ if self.enabled:
+ try:
+ iv = int(self.intervalSeconds or 0)
+ except Exception:
+ iv = 0
+ if iv <= 0:
+ errs.append("Schedule intervalSeconds must be > 0 when schedule is enabled.")
+ # windows is optional; if provided, validate structure best-effort
+ if self.windows and not isinstance(self.windows, dict):
+ errs.append("Schedule windows must be an object/dict.")
+ return errs
+
+ def _parse_hhmm(val: Any) -> Optional[Tuple[int, int]]:
+ try:
+ parts = str(val).strip().split(":")
+ if len(parts) != 2:
+ return None
+ hh = int(parts[0]); mm = int(parts[1])
+ if hh < 0 or hh > 23 or mm < 0 or mm > 59:
+ return None
+ return (hh, mm)
+ except Exception:
+ return None
+
+ try:
+ for _day, arr in (self.windows or {}).items():
+ if not isinstance(arr, list):
+ errs.append("Schedule windows entries must be lists.")
+ continue
+ for w in arr:
+ if not isinstance(w, dict):
+ errs.append("Schedule window must be an object.")
+ continue
+ if _parse_hhmm(w.get("start")) is None:
+ errs.append("Schedule window start must be HH:MM.")
+ if _parse_hhmm(w.get("end")) is None:
+ errs.append("Schedule window end must be HH:MM.")
+ if "intervalSeconds" in w:
+ try:
+ if int(w.get("intervalSeconds") or 0) <= 0:
+ errs.append("Schedule window intervalSeconds must be > 0 when provided.")
+ except Exception:
+ errs.append("Schedule window intervalSeconds must be an integer.")
+ except Exception:
+ # best-effort validation only
+ pass
+
+ return errs
+
+ def _window_allows_now_local(self, now_local: datetime) -> bool:
+ # If windows is missing/not-a-dict, treat as unrestricted (legacy).
+ if self.windows is None or not isinstance(self.windows, dict):
+ return True
+ # If windows is an empty dict, treat as "no service window configured" => not allowed.
+ if not self.windows:
+ return False
+
+ wd = now_local.weekday() # 0..6 (Mon..Sun)
+ day_windows = self.windows.get(str(wd)) or self.windows.get(wd)
+ if not isinstance(day_windows, list) or not day_windows:
+ return False
+
+ tnow = now_local.time().replace(tzinfo=None)
+
+ def _parse_hhmm(val: Any) -> Optional[Tuple[int, int]]:
+ try:
+ parts = str(val).strip().split(":")
+ if len(parts) != 2:
+ return None
+ hh = int(parts[0]); mm = int(parts[1])
+ if hh < 0 or hh > 23 or mm < 0 or mm > 59:
+ return None
+ return (hh, mm)
+ except Exception:
+ return None
+
+ for w in day_windows:
+ if not isinstance(w, dict):
+ continue
+ ps = _parse_hhmm(w.get("start"))
+ pe = _parse_hhmm(w.get("end"))
+ if ps is None or pe is None:
+ continue
+ sh, sm = ps; eh, em = pe
+ ts = datetime(2000, 1, 1, sh, sm).time()
+ te = datetime(2000, 1, 1, eh, em).time()
+
+ if ts <= te:
+ if ts <= tnow <= te:
+ return True
+ else:
+ # overnight window (e.g. 22:00-06:00)
+ if tnow >= ts or tnow <= te:
+ return True
+
+ return False
+
+ def interval_seconds_for_now(self, now: Optional[datetime] = None) -> int:
+ now_local = (now or datetime.now(timezone.utc)).astimezone()
+ # Priority:
+ # 1) per-day window intervalSeconds (first window entry)
+ # 2) schedule.intervalSeconds
+ try:
+ wd = now_local.weekday()
+ day = None
+ if isinstance(self.windows, dict):
+ day = self.windows.get(str(wd)) or self.windows.get(wd)
+ if isinstance(day, list) and day and isinstance(day[0], dict):
+ v = day[0].get("intervalSeconds")
+ if v is not None:
+ iv = int(v or 0)
+ if iv > 0:
+ return iv
+ except Exception:
+ pass
+
+ try:
+ iv = int(self.intervalSeconds or 0)
+ return iv if iv > 0 else 3600
+ except Exception:
+ return 3600
+
+ def should_run_now(self, now: Optional[datetime] = None) -> bool:
+ if not self.enabled:
+ return False
+ now_local = (now or datetime.now(timezone.utc)).astimezone()
+ return self._window_allows_now_local(now_local)
+
+ def next_run_at(self, now: Optional[datetime] = None) -> Optional[datetime]:
+ # This domain object doesn't store lastScheduledRunAt; caller should decide cadence.
+ # We return "now + interval" if windows allow now; otherwise the next allowed window start.
+ if not self.enabled:
+ return None
+
+ now_utc = now or datetime.now(timezone.utc)
+ now_local = now_utc.astimezone()
+
+ if self._window_allows_now_local(now_local):
+ return now_utc + timedelta(seconds=int(self.interval_seconds_for_now(now_utc)))
+
+ # Find next allowed window start within the next 7 days (best-effort).
+ if not self.windows or not isinstance(self.windows, dict):
+ return now_utc + timedelta(seconds=int(self.interval_seconds_for_now(now_utc)))
+
+ def _parse_hhmm(val: Any) -> Optional[Tuple[int, int]]:
+ try:
+ parts = str(val).strip().split(":")
+ if len(parts) != 2:
+ return None
+ hh = int(parts[0]); mm = int(parts[1])
+ if hh < 0 or hh > 23 or mm < 0 or mm > 59:
+ return None
+ return (hh, mm)
+ except Exception:
+ return None
+
+ base_local = now_local.replace(second=0, microsecond=0)
+ for add_days in range(0, 8):
+ day_local = base_local + timedelta(days=add_days)
+ wd = day_local.weekday()
+ day_windows = self.windows.get(str(wd)) or self.windows.get(wd)
+ if not isinstance(day_windows, list) or not day_windows:
+ continue
+ # use the first window as the start candidate (UI currently writes a single window per day)
+ w0 = day_windows[0] if isinstance(day_windows[0], dict) else None
+ if not w0:
+ continue
+ ps = _parse_hhmm(w0.get("start"))
+ if ps is None:
+ continue
+ sh, sm = ps
+ candidate_local = day_local.replace(hour=sh, minute=sm)
+ candidate_utc = candidate_local.astimezone(timezone.utc)
+ if candidate_utc > now_utc:
+ return candidate_utc
+
+ return None
+
+@dataclass
+class JobRunResult:
+ ok: bool
+ started_at: str
+ ended_at: str
+ result: str
+ message: Optional[str] = None
+ stats: JsonDict = field(default_factory=dict)
+
+@dataclass
+class Job:
+ id: Optional[int] = None
+ name: str = ""
+ enabled: bool = True
+ mode: str = "mirror"
+ direction: str = "unidirectional"
+ preserveMetadata: bool = True
+ conflictPolicy: str = "newest"
+ pairId: Optional[str] = None
+
+ sourceEndpoint: Endpoint = field(default_factory=Endpoint)
+ targetEndpoint: Endpoint = field(default_factory=Endpoint)
+
+ schedule: Schedule = field(default_factory=Schedule)
+
+ lastRun: Optional[str] = None
+ lastResult: Optional[str] = None
+ lastError: Optional[str] = None
+
+ def validate(self) -> List[str]:
+ errs: List[str] = []
+ if not self.name.strip():
+ errs.append("Job name is required.")
+ if not self.mode:
+ errs.append("Job mode is required.")
+ if not self.direction:
+ errs.append("Job direction is required.")
+
+ errs.extend(self.sourceEndpoint.validate("source"))
+ errs.extend(self.targetEndpoint.validate("target"))
+ errs.extend(self.schedule.validate())
+
+ d = (self.direction or "").lower()
+ if d not in ("unidirectional", "bidirectional"):
+ errs.append("Job direction must be 'unidirectional' or 'bidirectional'.")
+
+ cp = (self.conflictPolicy or "newest").lower()
+ if cp not in ("newest", "keepa", "a", "keepb", "b"):
+ errs.append("conflictPolicy must be one of: newest, keepA/a, keepB/b.")
+
+ return errs
+
+ def should_run_now(self, now: Optional[datetime] = None) -> bool:
+ if not self.enabled:
+ return False
+ return self.schedule.should_run_now(now)
+
+ def next_run_at(self, now: Optional[datetime] = None) -> Optional[datetime]:
+ if not self.enabled:
+ return None
+ return self.schedule.next_run_at(now)
+
+ def run(
+ self,
+ *,
+ now: Optional[datetime] = None,
+ copy_func: Optional[Callable[..., bool]] = None,
+ bidirectional_func: Optional[Callable[..., Tuple[bool, JsonDict]]] = None,
+ logger: Optional[Callable[[str, str], None]] = None,
+ ) -> JobRunResult:
+ now_dt = now or datetime.now(timezone.utc)
+ started_at = now_dt.isoformat()
+
+ def _log(msg: str, level: str = "info") -> None:
+ if logger:
+ try:
+ logger(msg, level)
+ except Exception:
+ pass
+
+ errs = self.validate()
+ if errs:
+ msg = "; ".join(errs)
+ ended = datetime.now(timezone.utc).isoformat()
+ self.lastRun = ended
+ self.lastResult = "fail"
+ self.lastError = msg
+ return JobRunResult(
+ ok=False,
+ started_at=started_at,
+ ended_at=ended,
+ result="fail",
+ message=msg,
+ stats={},
+ )
+
+ if not self.enabled:
+ ended = datetime.now(timezone.utc).isoformat()
+ return JobRunResult(
+ ok=True,
+ started_at=started_at,
+ ended_at=ended,
+ result="ok",
+ message="Job disabled; skipped.",
+ stats={},
+ )
+
+ preserve = bool(self.preserveMetadata)
+ allow_del = (str(self.mode or "").lower() == "mirror")
+
+ # Resolve endpoints (mount SMB endpoints to local paths for the duration of the run)
+ mounted: List[MountedEndpoint] = []
+ ok: bool = False
+ stats: JsonDict = {}
+
+ # Define upfront so logging/error handling can't reference undefined vars.
+ src = ""
+ dst = ""
+
+ try:
+ # Mount endpoints inside the try so a failure mounting target still cleans up source.
+ src_m = mount_endpoint_if_remote(self.sourceEndpoint, self.id, "source", logger=logger)
+ mounted.append(src_m)
+ dst_m = mount_endpoint_if_remote(self.targetEndpoint, self.id, "target", logger=logger)
+ mounted.append(dst_m)
+
+ src = src_m.local_path
+ dst = dst_m.local_path
+
+ if (self.direction or "").lower() == "bidirectional":
+ if not bidirectional_func:
+ raise NotImplementedError("Bidirectional engine not provided.")
+
+ # IMPORTANT:
+ # The bidirectional engine historically enforced local endpoints only by checking
+ # endpoint types. Since remote endpoints are mounted to local paths for the duration
+ # of the run, we provide a local-view of this job to the engine.
+ job_local_view = Job(
+ id=self.id,
+ name=self.name,
+ enabled=self.enabled,
+ mode=self.mode,
+ direction=self.direction,
+ preserveMetadata=self.preserveMetadata,
+ conflictPolicy=self.conflictPolicy,
+ pairId=self.pairId,
+ sourceEndpoint=Endpoint(type="local", location=src, auth={}),
+ targetEndpoint=Endpoint(type="local", location=dst, auth={}),
+ schedule=self.schedule,
+ lastRun=self.lastRun,
+ lastResult=self.lastResult,
+ lastError=self.lastError,
+ )
+
+ _log(f"[Job] Running bidirectional job '{self.name}': {src} <-> {dst}", "info")
+ ok, stats = bidirectional_func(job_local_view, None)
+ else:
+ if not copy_func:
+ raise NotImplementedError("Copy function not provided.")
+ _log(f"[Job] Running unidirectional job '{self.name}': {src} -> {dst}", "info")
+ ok = bool(copy_func(src, dst, preserve_metadata=preserve, allow_deletion=allow_del))
+ except NotImplementedError as e:
+ ok = False
+ self.lastError = str(e)
+ _log(f"[Job] Not supported: {e}", "error")
+ except Exception as e:
+ ok = False
+ self.lastError = str(e)
+ _log(f"[Job] Execution failed: {e}", "error")
+ finally:
+ # Always unmount remote endpoints
+ for m in reversed(mounted):
+ try:
+ m.cleanup()
+ except Exception:
+ pass
+
+ ended_at = datetime.now(timezone.utc).isoformat()
+ self.lastRun = ended_at
+ self.lastResult = "ok" if ok else "fail"
+ if ok:
+ self.lastError = None
+ else:
+ self.lastError = self.lastError or "Failed"
+
+ return JobRunResult(
+ ok=ok,
+ started_at=started_at,
+ ended_at=ended_at,
+ result="ok" if ok else "fail",
+ message=None if ok else self.lastError,
+ stats=stats or {},
+ )
+
+ def to_row_dicts(self) -> Dict[str, Any]:
+ job_row = {
+ "id": self.id,
+ "name": self.name,
+ "enabled": 1 if self.enabled else 0,
+ "mode": self.mode or "mirror",
+ "direction": self.direction or "unidirectional",
+ # "allowDeletion" entry removed; will be derived at write time
+ "preserveMetadata": 1 if self.preserveMetadata else 0,
+ "pairId": self.pairId,
+ "conflictPolicy": self.conflictPolicy or "newest",
+ "lastRun": self.lastRun,
+ "lastResult": self.lastResult,
+ "lastError": self.lastError,
+ }
+
+ endpoint_rows = [
+ self.sourceEndpoint.to_db_fields("source"),
+ self.targetEndpoint.to_db_fields("target"),
+ ]
+
+ sched_row = {
+ "enabled": 1 if self.schedule and self.schedule.enabled else 0,
+ "intervalSeconds": int(self.schedule.intervalSeconds if self.schedule else 3600),
+ "windows": json.dumps(self.schedule.windows if self.schedule and isinstance(self.schedule.windows, dict) else {}),
+ }
+
+ return {
+ "job_row": job_row,
+ "endpoint_rows": endpoint_rows,
+ "schedule_row": sched_row,
+ }
+
+ @staticmethod
+ def from_db_rows(
+ job_row: Mapping[str, Any],
+ endpoint_rows: Iterable[Mapping[str, Any]],
+ schedule_row: Optional[Mapping[str, Any]] = None,
+ ) -> "Job":
+ j = Job(
+ id=int(job_row.get("id")) if job_row.get("id") is not None else None,
+ name=str(job_row.get("name") or ""),
+ enabled=bool(job_row.get("enabled", 1)),
+ mode=str(job_row.get("mode") or "mirror"),
+ direction=str(job_row.get("direction") or "unidirectional"),
+ preserveMetadata=bool(job_row.get("preserveMetadata", 1)),
+ conflictPolicy=str(job_row.get("conflictPolicy") or "newest"),
+ pairId=job_row.get("pairId"),
+ lastRun=job_row.get("lastRun"),
+ lastResult=job_row.get("lastResult"),
+ lastError=job_row.get("lastError"),
+ )
+
+ src = None
+ tgt = None
+ for r in endpoint_rows:
+ role = (r.get("role") or "").lower()
+ ep = Endpoint.from_db_row(r)
+ if role == "source":
+ src = ep
+ elif role == "target":
+ tgt = ep
+
+ j.sourceEndpoint = src or Endpoint()
+ j.targetEndpoint = tgt or Endpoint()
+
+ if schedule_row:
+ # Read intervalSeconds and windows as new model, fallback as needed
+ try:
+ interval_s = int(schedule_row.get("intervalSeconds") or 0)
+ except Exception:
+ interval_s = 0
+ # Remove legacy everyMinutes fallback
+ if interval_s <= 0:
+ interval_s = 3600
+ windows = {}
+ try:
+ raw = schedule_row.get("windows")
+ if raw:
+ windows = json.loads(raw) if isinstance(raw, str) else (raw if isinstance(raw, dict) else {})
+ except Exception:
+ windows = {}
+ j.schedule = Schedule(
+ enabled=bool(schedule_row.get("enabled", 1)),
+ intervalSeconds=int(interval_s),
+ windows=windows,
+ )
+ else:
+ j.schedule = Schedule()
+
+ return j
+
+
+
+# ---------------------------------------------------------------------------
+# JobStore (DB persistence)
+# ---------------------------------------------------------------------------
+
+class JobStore:
+ """SQLite persistence for Job domain objects (UI-agnostic)."""
+
+ def __init__(self, db: Any):
+ self._db = db
+ if SQLite is None or not isinstance(self._db, SQLite):
+ raise TypeError("JobStore requires core.database.sqlite.SQLite")
+
+ def _is_core_sqlite(self) -> bool:
+ return True
+
+ def _select(self, table: str, where: Optional[str] = None, params: Any = None, *, order_by: Optional[str] = None) -> List[Dict[str, Any]]:
+ return self._db.select(table, where=where, params=params, order_by=order_by) # type: ignore[union-attr]
+
+ def _one(self, table: str, where: str, params: Any) -> Optional[Dict[str, Any]]:
+ return self._db.one(f'SELECT * FROM "{table}" WHERE {where} LIMIT 1;', params) # type: ignore[union-attr]
+
+ def _insert(self, table: str, data: Dict[str, Any]) -> int:
+ return int(self._db.insert(table, data)) # type: ignore[union-attr]
+
+ def _update(self, table: str, data: Dict[str, Any], where: str, params: Any) -> None:
+ if not isinstance(params, dict):
+ raise ValueError("JobStore._update requires dict params")
+ self._db.update(table, data, where, params) # type: ignore[union-attr]
+
+ def _upsert(self, table: str, data: Dict[str, Any], conflict_columns: List[str], update_columns: Optional[List[str]] = None) -> None:
+ self._db.upsert(table, data, conflict_columns, update_columns) # type: ignore[union-attr]
+
+ def _delete(self, table: str, where: str, params: Any) -> None:
+ self._db.delete(table, where, params) # type: ignore[union-attr]
+
+ def _transaction(self):
+ return self._db.transaction() # type: ignore[union-attr]
+
+ # -------------------------------
+ # Reads
+ # -------------------------------
+
+ def fetch_all(self) -> List[Job]:
+ job_rows = self._select("jobs", order_by="id ASC")
+ jobs: List[Job] = []
+ for jr in job_rows:
+ jid = int(jr.get("id") or 0)
+ ep_rows = self.fetch_endpoints_rows(jid)
+ sched_row = self.fetch_schedule_row(jid)
+ jobs.append(Job.from_db_rows(jr, ep_rows, sched_row))
+ return jobs
+
+ def fetch_by_id(self, job_id: int) -> Optional[Job]:
+ jr = self._one("jobs", "id = ?", (int(job_id),))
+ if not jr:
+ return None
+ ep_rows = self.fetch_endpoints_rows(int(job_id))
+ sched_row = self.fetch_schedule_row(int(job_id))
+ return Job.from_db_rows(jr, ep_rows, sched_row)
+
+ def fetch_endpoints_rows(self, job_id: int) -> List[Dict[str, Any]]:
+ return self._select("endpoints", "jobId = ?", (int(job_id),))
+
+ def fetch_schedule_row(self, job_id: int) -> Optional[Dict[str, Any]]:
+ return self._one("schedule", "jobId = ?", (int(job_id),))
+
+ # -------------------------------
+ # Writes
+ # -------------------------------
+
+ def upsert(self, job: Job) -> int:
+ row_dicts = job.to_row_dicts()
+ job_row: Dict[str, Any] = row_dicts["job_row"]
+ endpoint_rows: List[Dict[str, Any]] = row_dicts["endpoint_rows"]
+ schedule_row: Dict[str, Any] = row_dicts["schedule_row"]
+
+ with self._transaction():
+ # --- jobs ---
+ data = {
+ "name": job_row.get("name"),
+ "enabled": int(job_row.get("enabled") or 0),
+ "mode": job_row.get("mode") or "mirror",
+ "direction": job_row.get("direction") or "unidirectional",
+ # mode is the source of truth for deletion behavior
+ "allowDeletion": 1 if (str(job_row.get("mode") or "mirror").lower() == "mirror") else 0,
+ "preserveMetadata": int(job_row.get("preserveMetadata") or 0),
+ "pairId": job_row.get("pairId"),
+ "conflictPolicy": job_row.get("conflictPolicy") or "newest",
+ "lastRun": job_row.get("lastRun"),
+ "lastResult": job_row.get("lastResult"),
+ "lastError": job_row.get("lastError"),
+ }
+
+ if job.id:
+ self._update("jobs", data, "id = :id", {"id": int(job.id)})
+ job_id = int(job.id)
+ else:
+ job_id = self._insert("jobs", data)
+ job.id = job_id
+
+ # --- endpoints (unique: jobId+role) ---
+ for ep in endpoint_rows:
+ role = ep.get("role")
+ if role not in ("source", "target"):
+ continue
+ ep_data = {
+ "jobId": job_id,
+ "role": role,
+ "type": ep.get("type"),
+ "location": ep.get("location"),
+ "port": ep.get("port"),
+ "guest": ep.get("guest"),
+ "username": ep.get("username"),
+ "password": ep.get("password"),
+ "options": ep.get("options"),
+ }
+ self._upsert(
+ "endpoints",
+ ep_data,
+ ["jobId", "role"],
+ update_columns=["type", "location", "port", "guest", "username", "password", "options"],
+ )
+
+ # --- schedule (unique: jobId) ---
+ s_data = {
+ "jobId": job_id,
+ "enabled": int(schedule_row.get("enabled") or 0),
+ "intervalSeconds": int(schedule_row.get("intervalSeconds") or 0),
+ "windows": schedule_row.get("windows"),
+ }
+ self._upsert(
+ "schedule",
+ s_data,
+ ["jobId"],
+ update_columns=["enabled", "intervalSeconds", "windows"],
+ )
+
+ return int(job.id or 0)
+
+ def delete(self, job_id: int) -> None:
+ jid = int(job_id)
+ with self._transaction():
+ # Delete dependent rows first to avoid orphan data (and to work even without FK cascades)
+ try:
+ self._delete("endpoints", "jobId = ?", (jid,))
+ except Exception:
+ pass
+ try:
+ self._delete("schedule", "jobId = ?", (jid,))
+ except Exception:
+ pass
+ # These tables may exist depending on enabled features; delete best-effort.
+ for tbl in ("runs", "conflicts", "file_state"):
+ try:
+ self._delete(tbl, "jobId = ?", (jid,))
+ except Exception:
+ pass
+ self._delete("jobs", "id = ?", (jid,))
diff --git a/src/replicator/migration.py b/src/replicator/migration.py
new file mode 100644
index 00000000..e4cddada
--- /dev/null
+++ b/src/replicator/migration.py
@@ -0,0 +1,280 @@
+#!/usr/bin/env python3
+# src/replicator/migration.py
+
+from __future__ import annotations
+
+from typing import List, Optional, Sequence, Tuple
+
+try:
+ from core.database.sqlite import SQLite
+ from core.log import Log
+except ImportError:
+ from database.sqlite import SQLite
+ from log import Log
+
+
+class Migration:
+ """Database schema creation + migrations for Replicator.
+
+ This class intentionally *does not* re-implement a DB wrapper.
+ It relies on corePY's `SQLite` for connections/transactions and only
+ owns Replicator's schema + migration history + small meta KV helpers.
+ """
+
+ def __init__(self, db: SQLite, logger: Optional[Log] = None):
+ self._db = db
+ self._logger = logger
+
+ # ------------------------------------------------------------------
+ # Public API
+ # ------------------------------------------------------------------
+
+ def ensure(self) -> None:
+ """Ensure base tables exist and apply all pending migrations."""
+ self._ensure_schema_migrations_table()
+ self._apply_migrations(self._migrations())
+
+ def get_meta(self, key: str, default: Optional[str] = None) -> Optional[str]:
+ """Read a value from the `meta` table. Returns default if missing."""
+ try:
+ row = self._db.one("SELECT value FROM meta WHERE key = ?", (key,))
+ if not row:
+ return default
+ val = row.get("value")
+ return default if val is None else str(val)
+ except Exception:
+ return default
+
+ def set_meta(self, key: str, value: Optional[str]) -> None:
+ """Upsert a value into the `meta` table."""
+ try:
+ with self._db.transaction():
+ exists = self._db.scalar("SELECT 1 FROM meta WHERE key = ? LIMIT 1", (key,))
+ if exists:
+ self._db.execute(
+ "UPDATE meta SET value = ?, modified = CURRENT_TIMESTAMP WHERE key = ?",
+ (value, key),
+ )
+ else:
+ self._db.execute(
+ "INSERT INTO meta (key, value) VALUES (?, ?)",
+ (key, value),
+ )
+ except Exception:
+ # best effort
+ pass
+
+ # ------------------------------------------------------------------
+ # Internals
+ # ------------------------------------------------------------------
+
+ def _log(self, msg: str, *, level: str = "info", channel: str = "migration") -> None:
+ if self._logger is not None and hasattr(self._logger, "append"):
+ self._logger.append(msg, level=level, channel=channel) # type: ignore[call-arg]
+ else:
+ print(msg)
+
+ def _ensure_schema_migrations_table(self) -> None:
+ self._db.execute(
+ """
+ CREATE TABLE IF NOT EXISTS schema_migrations (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ name TEXT NOT NULL UNIQUE
+ );
+ """
+ )
+
+ def _is_applied(self, name: str) -> bool:
+ r = self._db.scalar(
+ "SELECT 1 FROM schema_migrations WHERE name = ? LIMIT 1;",
+ (name,),
+ )
+ return bool(r)
+
+ def _apply_migrations(self, migrations: Sequence[Tuple[str, Sequence[str]]]) -> None:
+ for name, stmts in migrations:
+ if self._is_applied(name):
+ continue
+
+ try:
+ with self._db.transaction():
+ for stmt in stmts:
+ self._db.execute(stmt)
+ self._db.execute("INSERT INTO schema_migrations (name) VALUES (?)", (name,))
+
+ self._log(f"[Migration] applied {name}", level="debug")
+ except Exception as e:
+ raise RuntimeError(f"Failed to apply migration {name}: {e}")
+
+ def _migrations(self) -> List[Tuple[str, List[str]]]:
+ return [
+ (
+ "0001_init",
+ [
+ # jobs
+ """
+ CREATE TABLE IF NOT EXISTS jobs (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ name TEXT NOT NULL,
+ enabled INTEGER NOT NULL DEFAULT 1,
+ mode TEXT NOT NULL DEFAULT 'mirror',
+ direction TEXT NOT NULL DEFAULT 'unidirectional',
+ allowDeletion INTEGER NOT NULL DEFAULT 0,
+ preserveMetadata INTEGER NOT NULL DEFAULT 1,
+ pairId TEXT NULL,
+ conflictPolicy TEXT NOT NULL DEFAULT 'newest',
+ lastRun TEXT NULL,
+ lastResult TEXT NULL,
+ lastError TEXT NULL
+ );
+ """,
+ "CREATE INDEX IF NOT EXISTS idx_jobs_enabled ON jobs(enabled);",
+
+ # endpoints
+ """
+ CREATE TABLE IF NOT EXISTS endpoints (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ jobId INTEGER NOT NULL,
+ role TEXT NOT NULL,
+ type TEXT NOT NULL,
+ location TEXT NOT NULL,
+ port INTEGER NULL,
+ guest INTEGER NOT NULL DEFAULT 1,
+ username TEXT NULL,
+ password TEXT NULL,
+ options TEXT NULL,
+ FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE
+ );
+ """,
+ "CREATE UNIQUE INDEX IF NOT EXISTS uq_endpoints_job_role ON endpoints(jobId, role);",
+
+ # schedule
+ """
+ CREATE TABLE IF NOT EXISTS schedule (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ jobId INTEGER NOT NULL UNIQUE,
+ -- Schedule is controlled via per-day windows and intervalSeconds.
+ enabled INTEGER NOT NULL DEFAULT 1,
+ intervalSeconds INTEGER NOT NULL DEFAULT 3600,
+ nextRunAt TEXT NULL,
+ lastScheduledRunAt TEXT NULL,
+ windows TEXT NULL,
+ FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE
+ );
+ """,
+
+ # runs
+ """
+ CREATE TABLE IF NOT EXISTS runs (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ jobId INTEGER NOT NULL,
+ startedAt TEXT NOT NULL,
+ endedAt TEXT NULL,
+ result TEXT NOT NULL,
+ message TEXT NULL,
+ stats TEXT NULL,
+ FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE
+ );
+ """,
+ "CREATE INDEX IF NOT EXISTS idx_runs_job_started ON runs(jobId, startedAt);",
+
+ # file_state
+ """
+ CREATE TABLE IF NOT EXISTS file_state (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ jobId INTEGER NOT NULL,
+ side TEXT NOT NULL,
+ relPath TEXT NOT NULL,
+ size INTEGER NOT NULL DEFAULT 0,
+ mtime INTEGER NOT NULL DEFAULT 0,
+ hash TEXT NULL,
+ isDir INTEGER NOT NULL DEFAULT 0,
+ deleted INTEGER NOT NULL DEFAULT 0,
+ deletedAt TEXT NULL,
+ meta TEXT NULL,
+ lastSeenAt TEXT NULL,
+ lastSeenRunId INTEGER NULL,
+ FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE
+ );
+ """,
+ "CREATE UNIQUE INDEX IF NOT EXISTS uq_file_state ON file_state(jobId, side, relPath);",
+
+ # conflicts
+ """
+ CREATE TABLE IF NOT EXISTS conflicts (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ jobId INTEGER NOT NULL,
+ runId INTEGER NULL,
+ relPath TEXT NOT NULL,
+ a_size INTEGER NULL,
+ a_mtime INTEGER NULL,
+ a_hash TEXT NULL,
+ b_size INTEGER NULL,
+ b_mtime INTEGER NULL,
+ b_hash TEXT NULL,
+ status TEXT NOT NULL DEFAULT 'open',
+ resolution TEXT NULL,
+ note TEXT NULL,
+ FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE,
+ FOREIGN KEY(runId) REFERENCES runs(id) ON DELETE SET NULL
+ );
+ """,
+ "CREATE INDEX IF NOT EXISTS idx_conflicts_job_status ON conflicts(jobId, status);",
+
+ # meta (simple KV store)
+ """
+ CREATE TABLE IF NOT EXISTS meta (
+ key TEXT PRIMARY KEY,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ value TEXT NULL
+ );
+ """,
+ ],
+ ),
+ (
+ "0002_schedule_enabled_default_off",
+ [
+ # SQLite cannot ALTER COLUMN defaults directly; rebuild the schedule table so
+ # new rows default to enabled=0 while preserving existing data.
+ "PRAGMA foreign_keys=OFF;",
+ """
+ CREATE TABLE IF NOT EXISTS schedule_new (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ modified DATETIME DEFAULT CURRENT_TIMESTAMP,
+ jobId INTEGER NOT NULL UNIQUE,
+ -- Schedule is controlled via per-day windows and intervalSeconds.
+ enabled INTEGER NOT NULL DEFAULT 0,
+ intervalSeconds INTEGER NOT NULL DEFAULT 3600,
+ nextRunAt TEXT NULL,
+ lastScheduledRunAt TEXT NULL,
+ windows TEXT NULL,
+ FOREIGN KEY(jobId) REFERENCES jobs(id) ON DELETE CASCADE
+ );
+ """.strip(),
+ """
+ INSERT INTO schedule_new (id, created, modified, jobId, enabled, intervalSeconds, nextRunAt, lastScheduledRunAt, windows)
+ SELECT id, created, modified, jobId, enabled, intervalSeconds, nextRunAt, lastScheduledRunAt, windows
+ FROM schedule;
+ """.strip(),
+ "DROP TABLE schedule;",
+ "ALTER TABLE schedule_new RENAME TO schedule;",
+ "PRAGMA foreign_keys=ON;",
+ ],
+ ),
+ ]
diff --git a/src/replicator/mount.py b/src/replicator/mount.py
new file mode 100644
index 00000000..30f3276a
--- /dev/null
+++ b/src/replicator/mount.py
@@ -0,0 +1,317 @@
+
+#!/usr/bin/env python3
+# src/replicator/mount.py
+
+"""Remote mount helpers for Replicator.
+
+Purpose
+-------
+Centralize SMB mounting logic so it can be reused by both:
+ - UI runner (replicator.py) which often works with legacy dict endpoints
+ - Domain runner (job.py) which uses Endpoint dataclass objects
+
+Supported
+---------
+- SMB (CIFS / Windows shares)
+
+This module is intentionally thin and delegates to corePY's Share helper:
+ from core.filesystem.share import Share, ShareAuth
+
+Notes
+-----
+- Remote "location" strings are accepted in common forms:
+ * \\host\\Share\\dir
+ * //host/Share/dir
+ * host/Share/dir
+- On macOS, `mount_smbfs` expects URL-style paths; the remote part is
+ percent-encoded (spaces, etc.) while keeping '/' separators intact.
+- Secrets are never logged; obvious fields are redacted.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Any, Callable, Dict, Optional, Tuple
+
+import os
+import re
+import tempfile
+from urllib.parse import quote
+
+# corePY Share helper (import once at module import time)
+try:
+ from core.filesystem.share import Share, ShareAuth # type: ignore
+except Exception as _e: # pragma: no cover
+ Share = None # type: ignore
+ ShareAuth = None # type: ignore
+ _COREPY_SHARE_IMPORT_ERROR = _e
+else:
+ _COREPY_SHARE_IMPORT_ERROR = None
+
+# ---------------------------------------------------------------------------
+# Exceptions
+# ---------------------------------------------------------------------------
+
+class RemoteMountError(RuntimeError):
+ """Raised when a remote endpoint cannot be mounted or parsed."""
+
+# ---------------------------------------------------------------------------
+# Logging helpers
+# ---------------------------------------------------------------------------
+
+def redact_secrets(s: str) -> str:
+ """Best-effort secret redaction for log lines."""
+ if not s:
+ return s
+ # key=value patterns
+ s = re.sub(r"(pass=)([^,\s]+)", r"\1***", s, flags=re.IGNORECASE)
+ s = re.sub(r"(password=)([^,\s]+)", r"\1***", s, flags=re.IGNORECASE)
+ # CLI flags
+ s = re.sub(r"(--password\s+)(\S+)", r"\1***", s, flags=re.IGNORECASE)
+ s = re.sub(r"(--pass\s+)(\S+)", r"\1***", s, flags=re.IGNORECASE)
+ return s
+
+class ShareLoggerAdapter:
+ """Adapter to feed Share's logging into Replicator/corePY style append()."""
+
+ def __init__(self, append_fn: Callable[[str, str], None] | Callable[..., None]):
+ self._append = append_fn
+
+ def debug(self, msg: str) -> None:
+ self._append(redact_secrets(msg), level="debug") # type: ignore[misc]
+
+ def info(self, msg: str) -> None:
+ self._append(redact_secrets(msg), level="info") # type: ignore[misc]
+
+ def warning(self, msg: str) -> None:
+ self._append(redact_secrets(msg), level="warning") # type: ignore[misc]
+
+ def error(self, msg: str) -> None:
+ self._append(redact_secrets(msg), level="error") # type: ignore[misc]
+
+# ---------------------------------------------------------------------------
+# Models
+# ---------------------------------------------------------------------------
+
+@dataclass
+class MountedEndpoint:
+ """Represents a mounted (or local) endpoint."""
+
+ local_path: str
+ mount_point: Optional[str] = None
+ share: Any = None # Share instance (only present when mounted)
+
+ def cleanup(self, *, elevate: bool = False) -> None:
+ """Unmount the share if mounted; best-effort.
+
+ Notes
+ -----
+ Some platforms/targets may require elevation to unmount. We keep the
+ default as `False` to match current behavior; callers can opt-in.
+ """
+ if self.share is not None and self.mount_point:
+ try:
+ self.share.umount(self.mount_point, elevate=bool(elevate))
+ except Exception:
+ pass
+
+# ---------------------------------------------------------------------------
+# Parsing
+# ---------------------------------------------------------------------------
+
+def parse_smb_location(location: str) -> Tuple[str, str]:
+ """Parse SMB location into (host, remote).
+
+ Accepts forms:
+ - \\host\\Share
+ - \\host\\Share\\dir\\sub
+ - //host/Share/dir
+ - host/Share/dir
+
+ Returns:
+ host, remote where remote is "Share" or "Share/dir/sub".
+ """
+ s = (location or "").strip()
+ if not s:
+ raise RemoteMountError("SMB location is empty")
+
+ # Normalize to backslashes then split.
+ s = s.replace("/", "\\")
+ while s.startswith("\\"):
+ s = s[1:]
+
+ parts = [p for p in s.split("\\") if p]
+ if len(parts) < 2:
+ raise RemoteMountError(
+ f"Invalid SMB location '{location}'. Expected //host/Share[/path] or \\\\host\\Share[/path]."
+ )
+
+ host = parts[0]
+ remote = "/".join(parts[1:])
+ return host, remote
+
+# ---------------------------------------------------------------------------
+# Mounting
+# ---------------------------------------------------------------------------
+
+def _get_mount_point(job_id: Optional[int], role: str, *, unique: bool = False) -> str:
+ """Compute the local mount point for a job/role."""
+ base = Path(tempfile.gettempdir()) / "replicator" / "mounts" / (str(job_id or "new")) / role
+ if unique:
+ # Avoid collisions if caller mounts multiple times quickly.
+ base = base / str(os.getpid())
+ base.mkdir(parents=True, exist_ok=True)
+ return str(base)
+
+def mount_endpoint_if_remote(
+ endpoint: Any,
+ job_id: Optional[int],
+ role: str,
+ *,
+ share: Any = None,
+ log_append: Optional[Callable[..., None]] = None,
+ logger: Any = None,
+ timeout: int = 60,
+) -> MountedEndpoint:
+ """Mount an endpoint if it is remote; otherwise return the local path.
+
+ Parameters
+ ----------
+ endpoint:
+ Can be either:
+ - dict-like: {type, location, auth?} or legacy endpoint row fields
+ - an object with attributes: .type, .location, .auth
+ job_id:
+ Job id (used for mount dir structure).
+ role:
+ "source" or "target" (used for mount dir structure).
+ share:
+ Optional Share instance to reuse.
+ log_append:
+ Function compatible with core Log.append(msg, level=..., channel=...).
+ If provided, used for safe debug messages.
+ logger:
+ Backward-compatible alias used by some callers (e.g. job.py).
+ Can be either:
+ - an object exposing `.append(msg, level=...)` (core Log-like)
+ - a callable compatible with `log_append`
+ If provided and `log_append` is None, it will be used.
+ """
+
+ # Share support is optional at import time; raise a meaningful error at runtime.
+ if Share is None or ShareAuth is None:
+ raise RemoteMountError(
+ "Share support not available (corePY missing or failed to import). "
+ f"Import error: {_COREPY_SHARE_IMPORT_ERROR}"
+ )
+
+ # Backward-compatible: allow callers to pass `logger=` instead of `log_append=`.
+ if log_append is None and logger is not None:
+ if callable(logger):
+ log_append = logger # type: ignore[assignment]
+ else:
+ append_fn = getattr(logger, "append", None)
+ if callable(append_fn):
+ def _append(msg: str, *, level: str = "info") -> None:
+ # core Log.append may accept (msg, level=..., channel=...) or (msg, level)
+ try:
+ append_fn(msg, level=level) # type: ignore[misc]
+ except TypeError:
+ append_fn(msg, level) # type: ignore[misc]
+
+ log_append = _append
+
+ def _get(d: Any, k: str, default: Any = None) -> Any:
+ if isinstance(d, dict):
+ return d.get(k, default)
+ return getattr(d, k, default)
+
+ t = str(_get(endpoint, "type", "local") or "local").lower()
+ loc = str(_get(endpoint, "location", "") or "").strip()
+
+ if t == "local":
+ return MountedEndpoint(local_path=loc)
+
+ if t != "smb":
+ raise RemoteMountError(f"Unsupported endpoint type: {t}")
+
+ mount_point = _get_mount_point(job_id, role, unique=False)
+
+ # Build auth dict from endpoint.auth + legacy flat fields
+ auth: Dict[str, Any] = {}
+ raw_auth = _get(endpoint, "auth", None)
+ if isinstance(raw_auth, dict):
+ auth.update(raw_auth)
+
+ # Backward compatible: top-level fields (as stored in endpoints table)
+ for k in ("port", "guest", "username", "password", "options", "domain", "workgroup", "read_only", "ro"):
+ v = _get(endpoint, k, None)
+ if v is not None and k not in auth:
+ auth[k] = v
+
+ # Parse host/remote
+ host, remote = parse_smb_location(loc)
+
+ # Percent-encode remote path (keep '/' separators)
+ remote_enc = quote(remote, safe="/")
+
+ # Port
+ port = 445
+ try:
+ if auth.get("port") is not None:
+ port = int(auth.get("port") or 445)
+ except Exception:
+ port = 445
+
+ # Options
+ opts: Dict[str, Any] = {}
+ if isinstance(auth.get("options"), dict):
+ opts.update(auth.get("options") or {})
+
+ # ShareAuth (best effort)
+ share_auth = ShareAuth(
+ username=auth.get("username"),
+ password=auth.get("password"),
+ domain=auth.get("domain") or auth.get("workgroup"),
+ )
+
+ # Share instance (reuse if provided). If we create one here, wire it with our adapter.
+ if share is None:
+ share_logger = ShareLoggerAdapter(log_append) if log_append is not None else None
+ share = Share(logger=share_logger)
+
+ # Emit safe debug line
+ if log_append is not None:
+ safe_auth = dict(auth)
+ for k in ("password", "pass"):
+ if k in safe_auth and safe_auth[k]:
+ safe_auth[k] = "***"
+ msg = (
+ f"[Replicator][Mount] protocol={t} host={host} remote={remote_enc} "
+ f"mount_point={mount_point} auth={safe_auth}"
+ )
+ try:
+ log_append(msg, level="debug") # type: ignore[misc]
+ except TypeError:
+ # Some callables accept only positional args.
+ log_append(msg) # type: ignore[misc]
+
+ # Mount
+ try:
+ share.mount(
+ t,
+ host,
+ remote_enc,
+ mount_point,
+ auth=share_auth,
+ port=port,
+ options=opts,
+ read_only=bool(auth.get("read_only") or auth.get("ro") or False),
+ elevate=False,
+ timeout=int(timeout or 60),
+ )
+ except Exception as e:
+ raise RemoteMountError(f"Failed to mount SMB endpoint '{loc}': {e}")
+
+ return MountedEndpoint(local_path=mount_point, mount_point=mount_point, share=share)
diff --git a/src/replicator/replicator.py b/src/replicator/replicator.py
new file mode 100644
index 00000000..208ea9a7
--- /dev/null
+++ b/src/replicator/replicator.py
@@ -0,0 +1,1424 @@
+#!/usr/bin/env python3
+# src/replicator/replicator.py
+
+from __future__ import annotations
+import os
+import json
+import shutil
+import time
+from typing import Optional, Any, Dict, List
+from datetime import datetime, timezone, timedelta
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QPixmap
+from PyQt5.QtWidgets import (
+ QApplication,
+ QMainWindow,
+ QWidget,
+ QVBoxLayout,
+ QHBoxLayout,
+ QLabel,
+ QTableWidget,
+ QTableWidgetItem,
+ QHeaderView,
+ QDialog,
+)
+from .ui import JobDialog, ScheduleDialog
+from .migration import Migration
+from .job import Job, Endpoint, Schedule, JobStore
+from .mount import RemoteMountError, MountedEndpoint, mount_endpoint_if_remote
+
+try:
+ from core.helper import Helper
+ from core.configuration import Configuration
+ from core.log import Log
+ from core.ui import MsgBox, Form
+ from core.filesystem.filesystem import FileSystem
+ from core.filesystem.share import Share, ShareAuth
+ from core.database.sqlite import SQLite
+except ImportError:
+ from helper import Helper
+ from configuration import Configuration
+ from log import Log
+ from ui import MsgBox, Form
+ from filesystem.filesystem import FileSystem
+ from filesystem.share import Share, ShareAuth
+ from database.sqlite import SQLite
+
+# ---------------------------------------------------------------------------
+# Remote endpoints (Share mount)
+# ---------------------------------------------------------------------------
+
+class _ShareLogger:
+ """Adapter that forwards Share logs into Replicator's logger."""
+
+ def __init__(self, append_fn):
+ self._append = append_fn
+
+ def debug(self, msg: str) -> None:
+ self._append(f"[Share] {msg}", level="debug")
+
+ def info(self, msg: str) -> None:
+ self._append(f"[Share] {msg}", level="info")
+
+ def warning(self, msg: str) -> None:
+ self._append(f"[Share] {msg}", level="warning")
+
+ def error(self, msg: str) -> None:
+ self._append(f"[Share] {msg}", level="error")
+
+class Replicator(QMainWindow):
+
+ # ------------------------------------------------------------------
+ # Initialization
+ # ------------------------------------------------------------------
+
+ def __init__(
+ self,
+ helper: Optional[Helper] = None,
+ configuration: Optional[Configuration] = None,
+ logger: Optional[Log] = None,
+ ):
+ super().__init__()
+
+ self._app = QApplication.instance()
+ if self._app is None:
+ raise RuntimeError("Replicator must be created after QApplication/Application.")
+
+ helper = helper or getattr(self._app, "helper", None)
+ configuration = configuration or getattr(self._app, "configuration", None)
+ logger = logger or getattr(self._app, "logger", None)
+
+ if helper is None or configuration is None:
+ raise RuntimeError("Replicator requires corePY Helper + Configuration.")
+
+ self._helper: Helper = helper
+ self._configuration: Configuration = configuration
+ self._logger: Optional[Log] = logger
+
+ self._fs = FileSystem(helper=self._helper, logger=self._logger)
+
+ # Shared Share instance (for SMB mounts) so we don't recreate it on every job run.
+ self._share_logger = _ShareLogger(self._log)
+ self._share = Share(logger=self._share_logger)
+
+ # --- Database path setup ---
+ # Use Helper.get_data_path to locate data/replicator.db
+ data_dir = self._helper.get_data_path("data", scope="system")
+ if data_dir is None:
+ raise RuntimeError("Could not locate data/ directory via Helper.get_data_path().")
+ os.makedirs(data_dir, exist_ok=True)
+ db_path = os.path.join(data_dir, "replicator.db")
+
+ # --- Database (corePY SQLite wrapper) + migrations ---
+ self._db = SQLite(db_path=db_path)
+ self._migration = Migration(self._db, logger=self._logger)
+ self._migration.ensure()
+
+ self._store = JobStore(self._db)
+
+ self._log(f"[Replicator] Database: {db_path}", level="debug")
+ self._log("[Replicator] Database migrations applied.", level="info")
+
+ # Domain jobs (Job objects)
+ self._jobs: List[Job] = []
+ self._table: Optional[QTableWidget] = None
+
+ # After DB is ready, log a snapshot of DB layout/counts (non-verbose)
+ self._db_debug_snapshot(verbose=False)
+
+ # ------------------------------------------------------------------
+ # Scheduler (service mode)
+ # ------------------------------------------------------------------
+
+ def _parse_iso_dt(self, s: Any) -> Optional[datetime]:
+ if not s:
+ return None
+ try:
+ dt = datetime.fromisoformat(str(s))
+ if dt.tzinfo is None:
+ dt = dt.replace(tzinfo=timezone.utc)
+ return dt
+ except Exception:
+ return None
+
+ def _should_run_scheduled(self, job: Job, *, now_utc: datetime) -> bool:
+ """Decide if a job should run in SERVICE/SCHEDULED mode.
+
+ Manual 'Run now' intentionally bypasses this.
+ """
+ if not job.id:
+ return False
+
+ sched = self._db.one(
+ "SELECT enabled, nextRunAt, lastScheduledRunAt FROM schedule WHERE jobId=?",
+ (int(job.id),),
+ )
+ if not sched:
+ # No schedule row => do not auto-run (manual run still works)
+ return False
+
+ # Schedule must be explicitly enabled in DB
+ if not bool(sched.get("enabled", 0)):
+ return False
+
+ # Schedule windows/interval are defined on the Job domain object
+ if not job.schedule.should_run_now(now_utc):
+ return False
+
+ # Respect nextRunAt if present
+ next_dt = self._parse_iso_dt(sched.get("nextRunAt"))
+ if next_dt and now_utc < next_dt:
+ return False
+
+ # Respect lastScheduledRunAt + interval
+ last_dt = self._parse_iso_dt(sched.get("lastScheduledRunAt"))
+ if last_dt is None:
+ return True
+
+ interval_s = int(job.schedule.interval_seconds_for_now(now_utc) or 0)
+ if interval_s <= 0:
+ interval_s = self._default_interval_seconds()
+
+ elapsed = (now_utc - last_dt).total_seconds()
+ return elapsed >= float(interval_s)
+ # Bidirectional sync helpers (local-only for now)
+ # ------------------------------------------------------------------
+
+ def _endpoint_local_root(self, ep: Dict[str, Any]) -> str:
+ """Return the local root path for an endpoint or raise.
+ This is a simple extractor for the already-mounted path."""
+ if not isinstance(ep, dict):
+ raise ValueError("Endpoint is missing")
+ loc = (ep.get("location") or "").strip()
+ if not loc:
+ raise ValueError("Endpoint location is required")
+ return loc
+
+ def _scan_local_tree(self, root: str) -> Dict[str, Dict[str, Any]]:
+ """Return a map relPath -> {isDir,size,mtime} for a local filesystem root."""
+ root = os.path.abspath(root)
+ out: Dict[str, Dict[str, Any]] = {}
+ if not os.path.exists(root):
+ return out
+
+ # Walk directories; include dirs as entries so deletions can be propagated.
+ for dirpath, dirnames, filenames in os.walk(root):
+ # Normalize and skip hidden special entries if needed (keep simple for now)
+ rel_dir = os.path.relpath(dirpath, root)
+ if rel_dir == ".":
+ rel_dir = ""
+
+ # Record directory itself (except root)
+ if rel_dir:
+ try:
+ st = os.stat(dirpath)
+ out[rel_dir] = {
+ "isDir": True,
+ "size": 0,
+ "mtime": int(st.st_mtime),
+ }
+ except Exception:
+ # If stat fails, still record directory
+ out[rel_dir] = {"isDir": True, "size": 0, "mtime": 0}
+
+ for fn in filenames:
+ full = os.path.join(dirpath, fn)
+ rel = os.path.relpath(full, root)
+ try:
+ st = os.stat(full)
+ out[rel] = {
+ "isDir": False,
+ "size": int(st.st_size),
+ "mtime": int(st.st_mtime),
+ }
+ except Exception:
+ out[rel] = {"isDir": False, "size": 0, "mtime": 0}
+
+ return out
+
+ def _load_prev_file_state(self, job_id: int, side: str) -> Dict[str, Dict[str, Any]]:
+ rows = self._db.query(
+ "SELECT relPath, size, mtime, isDir, deleted, deletedAt, lastSeenAt, lastSeenRunId FROM file_state WHERE jobId=? AND side=?",
+ (job_id, side),
+ )
+ prev: Dict[str, Dict[str, Any]] = {}
+ for r in rows or []:
+ prev[str(r.get("relPath"))] = {
+ "size": int(r.get("size") or 0),
+ "mtime": int(r.get("mtime") or 0),
+ "isDir": bool(r.get("isDir")),
+ "deleted": bool(r.get("deleted")),
+ "deletedAt": r.get("deletedAt"),
+ "lastSeenAt": r.get("lastSeenAt"),
+ "lastSeenRunId": r.get("lastSeenRunId"),
+ }
+ return prev
+
+ def _persist_file_state(self, job_id: int, side: str, cur: Dict[str, Dict[str, Any]], run_id: Optional[int]) -> None:
+ # Use wrapper transaction; no raw connection needed.
+ now_iso = datetime.now(timezone.utc).isoformat()
+ with self._db.transaction():
+ # Upsert all current entries
+ for rel, meta in cur.items():
+ is_dir = 1 if meta.get("isDir") else 0
+ size = int(meta.get("size", 0) or 0)
+ mtime = int(meta.get("mtime", 0) or 0)
+
+ exists = self._db.scalar(
+ "SELECT id FROM file_state WHERE jobId=? AND side=? AND relPath=?",
+ (job_id, side, rel),
+ )
+ if exists:
+ self._db.execute(
+ "UPDATE file_state SET size=?, mtime=?, isDir=?, deleted=0, deletedAt=NULL, lastSeenAt=?, lastSeenRunId=? WHERE jobId=? AND side=? AND relPath=?",
+ (size, mtime, is_dir, now_iso, run_id, job_id, side, rel),
+ )
+ else:
+ self._db.execute(
+ "INSERT INTO file_state (jobId, side, relPath, size, mtime, isDir, deleted, deletedAt, lastSeenAt, lastSeenRunId) VALUES (?,?,?,?,?,?,0,NULL,?,?)",
+ (job_id, side, rel, size, mtime, is_dir, now_iso, run_id),
+ )
+
+ # Mark missing as deleted (single UPDATE)
+ rels = list(cur.keys())
+ if rels:
+ placeholders = ",".join(["?"] * len(rels))
+ self._db.execute(
+ f"UPDATE file_state SET deleted=1, deletedAt=COALESCE(deletedAt, ?) WHERE jobId=? AND side=? AND deleted=0 AND relPath NOT IN ({placeholders})",
+ (now_iso, job_id, side, *rels),
+ )
+ else:
+ # cur is empty: mark all as deleted for this job/side
+ self._db.execute(
+ "UPDATE file_state SET deleted=1, deletedAt=COALESCE(deletedAt, ?) WHERE jobId=? AND side=? AND deleted=0",
+ (now_iso, job_id, side),
+ )
+
+ def _ensure_parent_dir(self, path: str) -> None:
+ parent = os.path.dirname(path)
+ if parent and not os.path.exists(parent):
+ os.makedirs(parent, exist_ok=True)
+
+ def _copy_local_path(self, src_root: str, dst_root: str, rel: str, is_dir: bool, preserve_metadata: bool) -> None:
+ src_full = os.path.join(src_root, rel)
+ dst_full = os.path.join(dst_root, rel)
+
+ if is_dir:
+ os.makedirs(dst_full, exist_ok=True)
+ return
+
+ self._ensure_parent_dir(dst_full)
+ if preserve_metadata:
+ shutil.copy2(src_full, dst_full)
+ else:
+ shutil.copy(src_full, dst_full)
+
+ def _delete_local_path(self, root: str, rel: str, is_dir: bool) -> None:
+ full = os.path.join(root, rel)
+ if not os.path.exists(full):
+ return
+ if is_dir:
+ # Only remove if empty; never rmtree blindly in sync engine.
+ try:
+ os.rmdir(full)
+ except OSError:
+ pass
+ else:
+ try:
+ os.remove(full)
+ except Exception:
+ pass
+
+ def _record_conflict(
+ self,
+ job_id: int,
+ run_id: Optional[int],
+ rel: str,
+ a: Dict[str, Any],
+ b: Dict[str, Any],
+ note: str = "",
+ ) -> None:
+ try:
+ # Avoid inserting duplicate open conflicts for the same path/note.
+ existing = self._db.one(
+ "SELECT id FROM conflicts WHERE jobId=? AND relPath=? AND status='open' AND COALESCE(note,'')=COALESCE(?, '') ORDER BY id DESC LIMIT 1",
+ (job_id, rel, note or ""),
+ )
+ if existing:
+ return
+ self._db.execute(
+ """
+ INSERT INTO conflicts (jobId, runId, relPath, a_size, a_mtime, a_hash, b_size, b_mtime, b_hash, status, note)
+ VALUES (?,?,?,?,?,?,?,?,?,'open',?)
+ """,
+ (
+ job_id,
+ run_id,
+ rel,
+ int(a.get("size", 0) or 0),
+ int(a.get("mtime", 0) or 0),
+ a.get("hash"),
+ int(b.get("size", 0) or 0),
+ int(b.get("mtime", 0) or 0),
+ b.get("hash"),
+ note or None,
+ ),
+ )
+ except Exception as e:
+ self._log(f"[Replicator][DB] Failed to record conflict for '{rel}': {e}", level="warning")
+
+ def _run_job_bidirectional(self, job: Dict[str, Any], run_id: Optional[int]) -> tuple[bool, Dict[str, Any]]:
+ """Bidirectional sync using `file_state` as the persistent baseline.
+
+ Currently implemented for local<->local only.
+ """
+ job_id = int(job.get("id") or 0)
+ if job_id <= 0:
+ raise ValueError("Job must have an id to run bidirectional sync")
+
+ src_ep = job.get("sourceEndpoint")
+ dst_ep = job.get("targetEndpoint")
+ a_root = self._endpoint_local_root(src_ep)
+ b_root = self._endpoint_local_root(dst_ep)
+
+ # Deletion behavior is derived from mode; mirror allows deletions.
+ allow_deletion = str(job.get("mode") or "mirror").lower() == "mirror"
+ preserve_metadata = bool(job.get("preserveMetadata", True))
+ conflict_policy = (job.get("conflictPolicy") or "newest").lower()
+
+ # Load previous baseline before scanning/persisting.
+ prev_a = self._load_prev_file_state(job_id, "A")
+ prev_b = self._load_prev_file_state(job_id, "B")
+
+ # Scan current trees
+ cur_a = self._scan_local_tree(a_root)
+ cur_b = self._scan_local_tree(b_root)
+
+ def _meta_changed(cur: Dict[str, Any], prev: Dict[str, Any]) -> bool:
+ return (
+ bool(cur.get("isDir")) != bool(prev.get("isDir"))
+ or int(cur.get("size", 0) or 0) != int(prev.get("size", 0) or 0)
+ or int(cur.get("mtime", 0) or 0) != int(prev.get("mtime", 0) or 0)
+ )
+
+ def _changed_set(cur: Dict[str, Dict[str, Any]], prev: Dict[str, Dict[str, Any]]) -> set[str]:
+ out = set()
+ for rel, meta in cur.items():
+ p = prev.get(rel)
+ if p is None or p.get("deleted", False):
+ out.add(rel)
+ else:
+ if _meta_changed(meta, p):
+ out.add(rel)
+ return out
+
+ def _deleted_set(cur: Dict[str, Dict[str, Any]], prev: Dict[str, Dict[str, Any]]) -> set[str]:
+ # deleted since last baseline means it existed (not deleted) and now missing
+ out = set()
+ for rel, p in prev.items():
+ if p.get("deleted", False):
+ continue
+ if rel not in cur:
+ out.add(rel)
+ return out
+
+ changed_a = _changed_set(cur_a, prev_a)
+ changed_b = _changed_set(cur_b, prev_b)
+ deleted_a = _deleted_set(cur_a, prev_a)
+ deleted_b = _deleted_set(cur_b, prev_b)
+
+ self._log(
+ f"[Replicator][BiDi] Snapshot A={len(cur_a)} entries, B={len(cur_b)} entries; changedA={len(changed_a)} changedB={len(changed_b)} deletedA={len(deleted_a)} deletedB={len(deleted_b)}",
+ level="debug",
+ )
+
+ # Build unified rel set
+ all_paths = set(cur_a.keys()) | set(cur_b.keys()) | set(prev_a.keys()) | set(prev_b.keys())
+
+ actions_copy_a_to_b: list[tuple[str, bool]] = []
+ actions_copy_b_to_a: list[tuple[str, bool]] = []
+ actions_del_a: list[tuple[str, bool]] = []
+ actions_del_b: list[tuple[str, bool]] = []
+
+ # Helper for newest
+ def _winner_newest(a: Dict[str, Any], b: Dict[str, Any]) -> str:
+ am = int(a.get("mtime", 0) or 0)
+ bm = int(b.get("mtime", 0) or 0)
+ if am == bm:
+ # tie-breaker: larger size wins
+ return "A" if int(a.get("size", 0) or 0) >= int(b.get("size", 0) or 0) else "B"
+ return "A" if am > bm else "B"
+
+ for rel in sorted(all_paths):
+ a_cur = cur_a.get(rel)
+ b_cur = cur_b.get(rel)
+
+ a_exists = a_cur is not None
+ b_exists = b_cur is not None
+
+ a_changed = rel in changed_a
+ b_changed = rel in changed_b
+
+ a_deleted = rel in deleted_a
+ b_deleted = rel in deleted_b
+
+ # If exists only on one side.
+ # When deletions are enabled and the missing side deleted it since the last baseline,
+ # deletion should win unless the existing side also changed (conflict).
+ if a_exists and not b_exists:
+ if b_deleted and allow_deletion:
+ if a_changed:
+ # conflict: B deleted while A changed
+ self._record_conflict(job_id, run_id, rel, a_cur, {"deleted": True}, note="B deleted, A changed")
+ winner = "A" if conflict_policy == "newest" else "A"
+ if winner == "A":
+ actions_copy_a_to_b.append((rel, bool(a_cur.get("isDir"))))
+ else:
+ actions_del_a.append((rel, bool(a_cur.get("isDir"))))
+ else:
+ # propagate deletion (keep B deleted, delete A)
+ actions_del_a.append((rel, bool(a_cur.get("isDir"))))
+ elif b_deleted and not allow_deletion:
+ # If B was deleted and deletions are not allowed, do NOT copy A->B (do nothing)
+ pass
+ else:
+ # no deletion involved: treat as create on A, copy to B
+ actions_copy_a_to_b.append((rel, bool(a_cur.get("isDir"))))
+ continue
+
+ if b_exists and not a_exists:
+ if a_deleted and allow_deletion:
+ if b_changed:
+ # conflict: A deleted while B changed
+ self._record_conflict(job_id, run_id, rel, {"deleted": True}, b_cur, note="A deleted, B changed")
+ winner = "B" if conflict_policy == "newest" else "B"
+ if winner == "B":
+ actions_copy_b_to_a.append((rel, bool(b_cur.get("isDir"))))
+ else:
+ actions_del_b.append((rel, bool(b_cur.get("isDir"))))
+ else:
+ # propagate deletion (keep A deleted, delete B)
+ actions_del_b.append((rel, bool(b_cur.get("isDir"))))
+ elif a_deleted and not allow_deletion:
+ # If A was deleted and deletions are not allowed, do NOT copy B->A (do nothing)
+ pass
+ else:
+ actions_copy_b_to_a.append((rel, bool(b_cur.get("isDir"))))
+ continue
+
+ # Missing on both sides: maybe deletion propagation; nothing to do.
+ if not a_exists and not b_exists:
+ continue
+
+ # Exists on both: check if identical
+ if a_exists and b_exists:
+ same = (
+ bool(a_cur.get("isDir")) == bool(b_cur.get("isDir"))
+ and int(a_cur.get("size", 0) or 0) == int(b_cur.get("size", 0) or 0)
+ and int(a_cur.get("mtime", 0) or 0) == int(b_cur.get("mtime", 0) or 0)
+ )
+
+ if same:
+ # Maybe propagate deletions if one side deleted previously (should not happen if same exists)
+ continue
+
+ # Not same: detect changes since baseline
+ if a_changed and b_changed:
+ # true conflict
+ self._record_conflict(job_id, run_id, rel, a_cur, b_cur, note="Changed on both sides")
+ if conflict_policy == "newest":
+ winner = _winner_newest(a_cur, b_cur)
+ elif conflict_policy in ("keepa", "a"):
+ winner = "A"
+ elif conflict_policy in ("keepb", "b"):
+ winner = "B"
+ else:
+ winner = _winner_newest(a_cur, b_cur)
+
+ if winner == "A":
+ actions_copy_a_to_b.append((rel, bool(a_cur.get("isDir"))))
+ else:
+ actions_copy_b_to_a.append((rel, bool(b_cur.get("isDir"))))
+ elif a_changed and not b_changed:
+ actions_copy_a_to_b.append((rel, bool(a_cur.get("isDir"))))
+ elif b_changed and not a_changed:
+ actions_copy_b_to_a.append((rel, bool(b_cur.get("isDir"))))
+ else:
+ # differs but we can't prove which changed vs baseline (e.g., first run with baseline empty)
+ # Choose newest as default.
+ winner = _winner_newest(a_cur, b_cur)
+ if winner == "A":
+ actions_copy_a_to_b.append((rel, bool(a_cur.get("isDir"))))
+ else:
+ actions_copy_b_to_a.append((rel, bool(b_cur.get("isDir"))))
+
+ # Deletions propagation (safe rules)
+ if allow_deletion:
+ # If A deleted and B did not change since baseline, delete on B
+ for rel in sorted(deleted_a):
+ if rel in changed_b:
+ # conflict: deleted on A but changed on B
+ self._record_conflict(job_id, run_id, rel, {"deleted": True}, cur_b.get(rel, {}), note="A deleted, B changed")
+ continue
+ pb = prev_b.get(rel)
+ if pb and not pb.get("deleted", False) and rel in cur_b:
+ actions_del_b.append((rel, bool(cur_b[rel].get("isDir"))))
+
+ # If B deleted and A did not change since baseline, delete on A
+ for rel in sorted(deleted_b):
+ if rel in changed_a:
+ self._record_conflict(job_id, run_id, rel, cur_a.get(rel, {}), {"deleted": True}, note="B deleted, A changed")
+ continue
+ pa = prev_a.get(rel)
+ if pa and not pa.get("deleted", False) and rel in cur_a:
+ actions_del_a.append((rel, bool(cur_a[rel].get("isDir"))))
+
+ # Execute actions
+ try:
+ # Copy directories first to ensure parents exist
+ for rel, is_dir in actions_copy_a_to_b:
+ self._copy_local_path(a_root, b_root, rel, is_dir, preserve_metadata)
+ for rel, is_dir in actions_copy_b_to_a:
+ self._copy_local_path(b_root, a_root, rel, is_dir, preserve_metadata)
+
+ # Then copy files (copy method handles both, but ordering helps for deep paths)
+ # (Already handled in _copy_local_path)
+
+ # Deletions last
+ for rel, is_dir in actions_del_a:
+ self._delete_local_path(a_root, rel, is_dir)
+ for rel, is_dir in actions_del_b:
+ self._delete_local_path(b_root, rel, is_dir)
+
+ except Exception as e:
+ self._log(f"[Replicator][BiDi] Execution failed: {e}", level="error")
+ return False, {}
+
+ # After actions, scan again and persist final file state as baseline
+ final_a = self._scan_local_tree(a_root)
+ final_b = self._scan_local_tree(b_root)
+ self._persist_file_state(job_id, "A", final_a, run_id)
+ self._persist_file_state(job_id, "B", final_b, run_id)
+
+ stats = {
+ "copyAtoB": len(actions_copy_a_to_b),
+ "copyBtoA": len(actions_copy_b_to_a),
+ "delA": len(actions_del_a),
+ "delB": len(actions_del_b),
+ "changedA": len(changed_a),
+ "changedB": len(changed_b),
+ "deletedA": len(deleted_a),
+ "deletedB": len(deleted_b),
+ }
+ self._log(f"[Replicator][BiDi] Actions: {stats}", level="debug")
+
+ # Update run stats if possible
+ if run_id:
+ try:
+ self._db.execute(
+ "UPDATE runs SET stats=? WHERE id=?",
+ (json.dumps(stats), run_id),
+ )
+ except Exception:
+ pass
+
+ return True, stats
+
+ # ------------------------------------------------------------------
+ # CLI integration
+ # ------------------------------------------------------------------
+
+ def cli(self, cli: QApplication = None) -> None:
+ """
+ Called by corePY CommandLine when running in CLI mode.
+ """
+ if cli is None:
+ return
+ cli.add("run", "Run all replication jobs.", self.run)
+ cli.service.add("run", "Run all replication jobs", self.run_scheduled, interval=1)
+
+ # ------------------------------------------------------------------
+ # UI
+ # ------------------------------------------------------------------
+
+ def show(self):
+ self.init()
+ super().show()
+
+ def init(self):
+ self.setWindowTitle(getattr(self._app, "name", "Replicator"))
+ self.setObjectName("Replicator")
+ # Ensure minimum window width for table visibility
+ self.setMinimumWidth(800)
+
+ central = QWidget(self)
+ self.setCentralWidget(central)
+ central.setStyleSheet("background-color: #212121;")
+ root = QVBoxLayout(central)
+ root.setContentsMargins(16, 16, 16, 16)
+ root.setSpacing(12)
+
+ # Logo (top, centered)
+ logo = QLabel()
+ logo.setAlignment(Qt.AlignCenter)
+
+ # Use app icon/logo if you have one; otherwise harmless
+ # Adjust path to wherever you store icons in Replicator
+ candidate = self._helper.get_path("icons/icon.png") or self._helper.get_path("core/icons/info.svg")
+ name = getattr(self._app, "name", None) or self._app.applicationName()
+ if candidate and self._helper.file_exists(candidate) and candidate.lower().endswith(".png"):
+ pm = QPixmap(candidate)
+ if not pm.isNull():
+ logo.setPixmap(pm.scaled(256, 256, Qt.KeepAspectRatio, Qt.SmoothTransformation))
+ else:
+ logo.setText(name)
+ logo.setStyleSheet("font-size: 32px; font-weight: 200; opacity: 0.8; padding: 0px; margin: 0px;")
+
+ root.addWidget(logo)
+
+ # Application name (under logo)
+ app_name = QLabel()
+ app_name.setText(str(name) if name else "")
+ app_name.setAlignment(Qt.AlignCenter)
+ app_name.setStyleSheet("font-size: 32px; font-weight: 200; opacity: 0.8; padding: 0px; margin: 0px;")
+ root.addWidget(app_name)
+
+ # --- Actions row (top, after logo) ---
+ actions_row = QHBoxLayout()
+ # No stretch at start; left-aligned by default
+ self._add_btn = Form.button(label="", icon="plus-circle", action=self._add_job)
+ self._add_btn.setToolTip("Add job")
+ self._edit_btn = Form.button(label="", icon="pencil-square", action=self._edit_job)
+ self._edit_btn.setToolTip("Edit job")
+ self._edit_btn.setVisible(False)
+ self._dup_btn = Form.button(label="", icon="files", action=self._duplicate_job)
+ self._dup_btn.setToolTip("Duplicate job")
+ self._dup_btn.setVisible(False)
+ self._del_btn = Form.button(label="", icon="trash", action=self._delete_job)
+ self._del_btn.setToolTip("Delete job")
+ self._del_btn.setVisible(False)
+ self._schedule_btn = Form.button(label="", icon="calendar-event", action=self._edit_schedule)
+ self._schedule_btn.setToolTip("Schedule")
+ self._schedule_btn.setVisible(False)
+ actions_row.addWidget(self._add_btn)
+ actions_row.addWidget(self._edit_btn)
+ actions_row.addWidget(self._dup_btn)
+ actions_row.addWidget(self._del_btn)
+ actions_row.addWidget(self._schedule_btn)
+ actions_row.addStretch(1)
+ root.addLayout(actions_row)
+
+ # Table
+ self._table = QTableWidget(0, 6, self)
+ self._table.setHorizontalHeaderLabels([
+ "Name", "Enabled", "Source", "Target", "Last run", "Result"
+ ])
+ self._table.setSelectionBehavior(QTableWidget.SelectRows)
+ self._table.setEditTriggers(QTableWidget.NoEditTriggers)
+
+ # Hide row numbers
+ self._table.verticalHeader().setVisible(False)
+
+ # Connect selection change to update visibility of action buttons
+ self._table.selectionModel().selectionChanged.connect(self._on_selection_changed)
+
+ # Ensure columns never shrink below their content (use scrollbar for overflow)
+ header = self._table.horizontalHeader()
+ # Performance: ResizeToContents on every column can get painfully slow.
+ header.setSectionResizeMode(0, QHeaderView.Stretch) # Name
+ for i in range(1, self._table.columnCount()):
+ header.setSectionResizeMode(i, QHeaderView.ResizeToContents)
+
+ # Keep horizontal scrollbar available for overflow
+ header.setStretchLastSection(False)
+
+ root.addWidget(self._table)
+
+ # --- Bottom row: Service Manager, Configurator and Run now (right aligned) ---
+ bottom_row = QHBoxLayout()
+ bottom_row.addStretch(1)
+
+ service_btn = Form.button(label="", icon="hdd-network", action=self._open_service_manager)
+ service_btn.setToolTip("Service Manager")
+
+ config_btn = Form.button(label="", icon="gear-fill", action=self._configuration.show)
+ config_btn.setToolTip("Configurator")
+
+ run_btn = Form.button(label="", icon="play-fill", action=lambda: self._run_with_ui_feedback())
+ run_btn.setToolTip("Run now")
+
+ bottom_row.addWidget(service_btn)
+ bottom_row.addWidget(config_btn)
+ bottom_row.addWidget(run_btn)
+ root.addLayout(bottom_row)
+
+ self._reload_jobs()
+ self._on_selection_changed()
+
+ def _on_selection_changed(self, *_args):
+ has = self._selected_index() >= 0
+ if hasattr(self, "_edit_btn"):
+ self._edit_btn.setVisible(has)
+ if hasattr(self, "_dup_btn"):
+ self._dup_btn.setVisible(has)
+ if hasattr(self, "_del_btn"):
+ self._del_btn.setVisible(has)
+ if hasattr(self, "_schedule_btn"):
+ self._schedule_btn.setVisible(has)
+
+ def _selected_index(self) -> int:
+ if not self._table:
+ return -1
+ rows = self._table.selectionModel().selectedRows()
+ if not rows:
+ return -1
+ return rows[0].row()
+
+ def _reload_jobs(self):
+ self._jobs = self._store.fetch_all()
+ self._refresh_table()
+
+ def _save_jobs(self):
+ return
+
+ def _refresh_table(self):
+ if not self._table:
+ return
+ self._table.setRowCount(0)
+
+ for job in self._jobs:
+ r = self._table.rowCount()
+ self._table.insertRow(r)
+
+ def _it(v: Any) -> QTableWidgetItem:
+ item = QTableWidgetItem(str(v) if v is not None else "")
+ item.setFlags(item.flags() ^ Qt.ItemIsEditable)
+ return item
+
+ self._table.setItem(r, 0, _it(job.name))
+ self._table.setItem(r, 1, _it("On" if job.enabled else "Off"))
+
+ src_str = f"{job.sourceEndpoint.type}:{job.sourceEndpoint.location}"
+ tgt_str = f"{job.targetEndpoint.type}:{job.targetEndpoint.location}"
+ self._table.setItem(r, 2, _it(src_str))
+ self._table.setItem(r, 3, _it(tgt_str))
+
+ last_run = job.lastRun
+ self._table.setItem(r, 4, _it(last_run if last_run else "Never"))
+
+ self._table.setItem(r, 5, _it(job.lastResult or ""))
+
+ def _default_interval_seconds(self) -> int:
+ """Return the configured default interval in seconds (service.defaultInterval), fallback to 3600."""
+ try:
+ v = self._configuration.get("service.defaultInterval", 3600)
+ iv = int(v)
+ return iv if iv > 0 else 3600
+ except Exception:
+ return 3600
+
+ def _default_schedule_dict(self) -> Dict[str, Any]:
+ interval = self._default_interval_seconds()
+ windows: Dict[str, Any] = {}
+ for wd in range(7):
+ windows[str(wd)] = [{"start": "00:00", "end": "23:59", "intervalSeconds": interval}]
+ return {
+ "enabled": False,
+ "intervalSeconds": interval,
+ "windows": windows,
+ }
+
+ def _schedule_interval_seconds_for_now(self, sched: Dict[str, Any]) -> int:
+ # Kept for backward compatibility with existing log formatting.
+ # Prefer the Job/Schedule domain helpers where possible.
+ try:
+ j = self._job_from_legacy_dict({"schedule": sched, "name": "_tmp", "enabled": True, "sourceEndpoint": {"type": "local", "location": "/"}, "targetEndpoint": {"type": "local", "location": "/"}})
+ return int(j.schedule.interval_seconds_for_now(datetime.now(timezone.utc)) or 0) or self._default_interval_seconds()
+ except Exception:
+ return self._default_interval_seconds()
+
+ def _job_from_legacy_dict(self, d: Dict[str, Any], *, existing_id: Optional[int] = None) -> Job:
+ src = d.get("sourceEndpoint") or {"type": "local", "location": d.get("source", ""), "auth": {}}
+ tgt = d.get("targetEndpoint") or {"type": "local", "location": d.get("target", ""), "auth": {}}
+
+ # Schedule defaults:
+ # - enabled
+ # - every day, all day
+ # - interval 3600 seconds (1h)
+ sched = d.get("schedule")
+ if not isinstance(sched, dict):
+ sched = self._default_schedule_dict()
+
+ default_interval = self._default_interval_seconds()
+
+ # Normalize intervalSeconds (no everyMinutes support)
+ try:
+ iv = int(sched.get("intervalSeconds") or 0)
+ except Exception:
+ iv = 0
+ if iv <= 0:
+ iv = default_interval
+ sched["intervalSeconds"] = iv
+
+ # Ensure windows exist; if missing, make it "every day, all day" with per-day interval
+ windows = sched.get("windows")
+ if not isinstance(windows, dict) or not windows:
+ windows = {}
+ for wd in range(7):
+ windows[str(wd)] = [{"start": "00:00", "end": "23:59", "intervalSeconds": iv}]
+ sched["windows"] = windows
+ else:
+ # Ensure each enabled day window includes intervalSeconds (per-day) for the upcoming scheduler logic
+ for k, v in list(windows.items()):
+ if isinstance(v, list) and v and isinstance(v[0], dict):
+ if "intervalSeconds" not in v[0]:
+ v[0]["intervalSeconds"] = iv
+
+ j = Job(
+ id=existing_id,
+ name=str(d.get("name") or ""),
+ enabled=bool(d.get("enabled", True)),
+ mode=str(d.get("mode") or "mirror"),
+ direction=str(d.get("direction") or "unidirectional"),
+ # Deletion behavior is derived from mode; mirror allows deletions.
+ allowDeletion=(str(d.get("mode") or "mirror").lower() == "mirror"),
+ preserveMetadata=bool(d.get("preserveMetadata", True)),
+ conflictPolicy=str(d.get("conflictPolicy") or "newest"),
+ pairId=d.get("pairId"),
+ lastRun=d.get("lastRun"),
+ lastResult=d.get("lastResult"),
+ lastError=d.get("lastError"),
+ )
+
+ j.sourceEndpoint = Endpoint(
+ type=str(src.get("type") or "local"),
+ location=str(src.get("location") or ""),
+ auth=dict(src.get("auth") or {}),
+ )
+ j.targetEndpoint = Endpoint(
+ type=str(tgt.get("type") or "local"),
+ location=str(tgt.get("location") or ""),
+ auth=dict(tgt.get("auth") or {}),
+ )
+
+ windows = sched.get("windows") if isinstance(sched.get("windows"), dict) else {}
+
+ j.schedule = Schedule(
+ enabled=bool(sched.get("enabled", False)),
+ intervalSeconds=int(iv),
+ windows=windows,
+ )
+
+ return j
+
+ def _add_job(self):
+ # Pass configured service.defaultInterval into the dialog so Schedule defaults match user config
+ dlg = JobDialog(self, default_interval_seconds=self._default_interval_seconds())
+ if dlg.exec_() == QDialog.Accepted:
+ new_job_dict = dlg.value()
+ if "schedule" not in new_job_dict or not isinstance(new_job_dict.get("schedule"), dict):
+ new_job_dict["schedule"] = self._default_schedule_dict()
+ job_obj = self._job_from_legacy_dict(new_job_dict)
+ job_id = self._store.upsert(job_obj)
+ job_obj.id = job_id
+ self._reload_jobs()
+
+ def _edit_job(self):
+ idx = self._selected_index()
+ if idx < 0:
+ MsgBox.show(self, "Jobs", "Select a job to edit.", icon="info")
+ return
+
+ current = self._jobs[idx]
+ # Pass configured service.defaultInterval into the dialog so Schedule defaults remain consistent
+ dlg = JobDialog(self, current.to_legacy_dict(), default_interval_seconds=self._default_interval_seconds())
+ if dlg.exec_() == QDialog.Accepted:
+ updated_dict = dlg.value()
+ job_obj = self._job_from_legacy_dict(updated_dict, existing_id=current.id)
+ self._store.upsert(job_obj)
+ self._reload_jobs()
+
+ def _duplicate_job(self):
+ idx = self._selected_index()
+ if idx < 0:
+ MsgBox.show(self, "Jobs", "Select a job to duplicate.", icon="info")
+ return
+
+ orig = self._jobs[idx]
+ d = orig.to_legacy_dict()
+ d.pop("id", None)
+ d["name"] = f"{orig.name} (copy)" if orig.name else "Copy"
+
+ job_obj = self._job_from_legacy_dict(d)
+ self._store.upsert(job_obj)
+ self._reload_jobs()
+
+ if self._table:
+ # Select the newest row (best-effort)
+ self._table.selectRow(self._table.rowCount() - 1)
+
+ def _delete_job(self):
+ idx = self._selected_index()
+ if idx < 0:
+ MsgBox.show(self, "Jobs", "Select a job to delete.", icon="info")
+ return
+
+ job = self._jobs[idx]
+ choice = MsgBox.show(
+ self,
+ title="Delete",
+ message=f"Delete job '{job.name}'?",
+ icon="question",
+ buttons=("Cancel", "Delete"),
+ default="Cancel",
+ )
+ if choice == "Delete":
+ if job.id:
+ jid = int(job.id)
+ try:
+ with self._db.transaction():
+ # Explicitly delete dependent rows to avoid orphans.
+ self._db.execute("DELETE FROM endpoints WHERE jobId = ?", (jid,))
+ self._db.execute("DELETE FROM schedule WHERE jobId = ?", (jid,))
+ self._db.execute("DELETE FROM runs WHERE jobId = ?", (jid,))
+ self._db.execute("DELETE FROM conflicts WHERE jobId = ?", (jid,))
+ self._db.execute("DELETE FROM file_state WHERE jobId = ?", (jid,))
+ self._db.execute("DELETE FROM jobs WHERE id = ?", (jid,))
+ except Exception as e:
+ self._log(f"[Replicator][DB] Failed to delete job {jid} cascade: {e}", level="warning")
+ # Fallback to store deletion if cascade failed.
+ try:
+ self._store.delete(jid)
+ except Exception:
+ pass
+ self._reload_jobs()
+
+ def _edit_schedule(self):
+ idx = self._selected_index()
+ if idx < 0:
+ MsgBox.show(self, "Jobs", "Select a job to edit schedule.", icon="info")
+ return
+
+ current = self._jobs[idx]
+ # Pass configured service.defaultInterval into the schedule editor so defaults match user config
+ dlg = ScheduleDialog(self, job=current.to_legacy_dict(), default_interval_seconds=self._default_interval_seconds())
+ if dlg.exec_() == QDialog.Accepted:
+ d = current.to_legacy_dict()
+ d["schedule"] = dlg.value()
+ job_obj = self._job_from_legacy_dict(d, existing_id=current.id)
+ self._store.upsert(job_obj)
+ self._reload_jobs()
+
+ def _run_with_ui_feedback(self):
+ ok = self.run()
+ MsgBox.show(
+ self,
+ title="Replicator",
+ message="Replication completed successfully." if ok else "Replication finished with errors.",
+ icon="info" if ok else "warning",
+ )
+
+ # ------------------------------------------------------------------
+ # Execution
+ # ------------------------------------------------------------------
+
+ def run_scheduled(self) -> bool:
+ """
+ Scheduled/service execution entrypoint.
+ Respects per-job schedule (enabled/windows/interval).
+ Returns True if all eligible jobs succeeded.
+ """
+ self._reload_jobs()
+
+ verbose_db = False
+ try:
+ verbose_db = bool(self._configuration.get("log.verbose", False))
+ except Exception:
+ verbose_db = False
+ self._db_debug_snapshot(verbose=verbose_db)
+
+ if not self._jobs:
+ self._log("[Replicator] No jobs configured.", level="warning")
+ return False
+
+ now_utc = datetime.now(timezone.utc)
+
+ any_ran = False
+ all_ok = True
+
+ for job in self._jobs:
+ if not bool(job.enabled):
+ continue
+
+ if not self._should_run_scheduled(job, now_utc=now_utc):
+ continue
+
+ any_ran = True
+ ok = self._run_job(job)
+ all_ok = all_ok and ok
+
+ # Update schedule bookkeeping for this job
+ try:
+ interval_s = int(job.schedule.interval_seconds_for_now(datetime.now(timezone.utc)) or 0) or self._default_interval_seconds()
+ next_run = (datetime.now(timezone.utc) + timedelta(seconds=int(interval_s))).isoformat()
+ self._db.update(
+ "schedule",
+ {
+ "lastScheduledRunAt": datetime.now(timezone.utc).isoformat(),
+ "nextRunAt": next_run,
+ },
+ "jobId = :jobId",
+ {"jobId": int(job.id)},
+ )
+ except Exception:
+ pass
+
+ if not any_ran:
+ self._log("[Replicator] No jobs due per schedule.", level="debug")
+
+ return all_ok
+
+ def run(self) -> bool:
+ """
+ Run all configured jobs (from DB).
+ Returns True if all jobs succeeded.
+ """
+ self._reload_jobs()
+ # After reloading jobs, log a DB snapshot for debugging.
+ # Only log verbose DB details when log.verbose is enabled.
+ verbose_db = False
+ try:
+ verbose_db = bool(self._configuration.get("log.verbose", False))
+ except Exception:
+ verbose_db = False
+
+ self._db_debug_snapshot(verbose=verbose_db)
+ jobs: List[Job] = self._jobs
+ if not jobs:
+ self._log("[Replicator] No jobs configured.", level="warning")
+ return False
+
+ all_ok = True
+ for job in jobs:
+ if not bool(job.enabled):
+ self._log(f"[Replicator] Skipping disabled job '{job.name or 'Unnamed'}'.", level="info")
+ continue
+ ok = self._run_job(job)
+ all_ok = all_ok and ok
+
+ # Run DB maintenance at most once per day.
+ try:
+ last = self._migration.get_meta("maintenance.lastRunAt")
+ should = True
+ if last:
+ try:
+ last_dt = datetime.fromisoformat(str(last))
+ if last_dt.tzinfo is None:
+ last_dt = last_dt.replace(tzinfo=timezone.utc)
+ should = (datetime.now(timezone.utc) - last_dt).total_seconds() >= 86400
+ except Exception:
+ should = True
+ if should:
+ self._db_maintenance()
+ except Exception:
+ pass
+
+ return all_ok
+
+ def _run_job(self, job: Job) -> bool:
+ name = job.name or "Unnamed"
+ if not bool(job.enabled):
+ self._log(f"[Replicator] Job '{name}' is disabled; skipping.", level="info")
+ return True
+ job_id = job.id
+ src = job.sourceEndpoint.location
+ src_type = job.sourceEndpoint.type
+ dst = job.targetEndpoint.location
+ dst_type = job.targetEndpoint.type
+ mode = job.mode
+ # Deletion behavior is derived from mode; mirror allows deletions.
+ allow_deletion = str(mode or "mirror").lower() == "mirror"
+ preserve_metadata = bool(job.preserveMetadata)
+ direction = job.direction
+
+ if not src or not dst:
+ self._log(f"[Replicator] Job '{name}' invalid: missing source or target path (src='{src}', dst='{dst}').", level="error")
+ return False
+
+ sched_str = ""
+ try:
+ if job_id:
+ sched = self._db.one(
+ "SELECT enabled FROM schedule WHERE jobId=?",
+ (int(job_id),),
+ )
+ if sched and bool(sched.get("enabled", 0)):
+ interval_s = int(job.schedule.interval_seconds_for_now(datetime.now(timezone.utc)) or 0) or self._default_interval_seconds()
+ sched_str = f", schedule=Interval {interval_s}s"
+ except Exception:
+ pass
+ logline = f"[Replicator] Running job '{name}': {src} -> {dst} (mode={mode}, direction={direction}, delete={allow_deletion}, meta={preserve_metadata}{sched_str}"
+ logline += f", srcType={src_type}, dstType={dst_type})"
+ self._log(logline)
+
+ # Insert a run record at start
+ started_at = datetime.now(timezone.utc).isoformat()
+ run_id = None
+ bidi_stats = None
+ try:
+ run_id = None
+ if job_id:
+ try:
+ run_id = int(self._db.insert("runs", {"jobId": int(job_id), "startedAt": started_at, "result": "running"}))
+ except Exception as e:
+ self._log(f"[Replicator] Failed to insert run record: {e}", level="warning")
+ except Exception as e:
+ self._log(f"[Replicator] Failed to insert run record: {e}", level="warning")
+
+ record_noop_runs = bool(self._configuration.get("db.recordNoopRuns", False))
+
+ ok = False
+ try:
+ # Always build a legacy dict so we can reuse the endpoint records + auth.
+ job_dict = job.to_legacy_dict()
+ src_ep = job_dict.get("sourceEndpoint") or {"type": "local", "location": src, "auth": {}}
+ dst_ep = job_dict.get("targetEndpoint") or {"type": "local", "location": dst, "auth": {}}
+
+ # Mount remote endpoints (SMB only) via Share so BOTH uni + bidi operate on
+ # local filesystem paths, and we get connectivity debug logs.
+ mounted: list[MountedEndpoint] = []
+
+ try:
+ src_m = mount_endpoint_if_remote(src_ep, job_id, "source", share=self._share, log_append=self._log)
+ mounted.append(src_m)
+ dst_m = mount_endpoint_if_remote(dst_ep, job_id, "target", share=self._share, log_append=self._log)
+ mounted.append(dst_m)
+
+ # Use the mounted/local view for execution
+ local_src = src_m.local_path
+ local_dst = dst_m.local_path
+
+ if str(direction).lower() == "bidirectional":
+ # Local view for the bidirectional sync engine
+ job_dict["sourceEndpoint"] = {"type": "local", "location": local_src, "auth": {}}
+ job_dict["targetEndpoint"] = {"type": "local", "location": local_dst, "auth": {}}
+ ok, bidi_stats = self._run_job_bidirectional(job_dict, run_id)
+ else:
+ # Unidirectional copy (mirror) also uses mounted/local paths
+ ok = self._fs.copy(
+ local_src,
+ local_dst,
+ preserve_metadata=preserve_metadata,
+ allow_deletion=allow_deletion,
+ )
+ finally:
+ for m in reversed(mounted):
+ try:
+ m.cleanup()
+ except Exception:
+ pass
+ except NotImplementedError as e:
+ self._log(f"[Replicator] Job '{name}' not supported: {e}", level="error")
+ ok = False
+ except RemoteMountError as e:
+ self._log(f"[Replicator] Job '{name}' failed to mount remote endpoint: {e}", level="error")
+ ok = False
+ except Exception as e:
+ self._log(f"[Replicator] Job '{name}' failed: {e}", level="error")
+ ok = False
+
+ # If this was a bidirectional run with no actions/changes, optionally drop the run row.
+ if run_id and bidi_stats is not None and not record_noop_runs:
+ try:
+ noop = (
+ int(bidi_stats.get("copyAtoB", 0) or 0) == 0
+ and int(bidi_stats.get("copyBtoA", 0) or 0) == 0
+ and int(bidi_stats.get("delA", 0) or 0) == 0
+ and int(bidi_stats.get("delB", 0) or 0) == 0
+ and int(bidi_stats.get("changedA", 0) or 0) == 0
+ and int(bidi_stats.get("changedB", 0) or 0) == 0
+ and int(bidi_stats.get("deletedA", 0) or 0) == 0
+ and int(bidi_stats.get("deletedB", 0) or 0) == 0
+ )
+ if noop:
+ self._db.execute("DELETE FROM runs WHERE id = ?", (run_id,))
+ run_id = None
+ except Exception:
+ pass
+
+ # Persist lastRun and lastResult, refresh UI, save to DB
+ now_str = datetime.now(timezone.utc).isoformat()
+ job.lastRun = now_str
+ job.lastResult = "ok" if ok else "fail"
+ job.lastError = None if ok else (job.lastError or "Failed")
+ self._store.upsert(job)
+ if run_id:
+ try:
+ self._db.update(
+ "runs",
+ {"endedAt": now_str, "result": ("ok" if ok else "fail"), "message": (None if ok else (job.lastError or "Failed"))},
+ "id = :id",
+ {"id": int(run_id)},
+ )
+ except Exception as e:
+ self._log(f"[Replicator] Failed to update run record: {e}", level="warning")
+ if self._table:
+ self._reload_jobs()
+
+ self._log(f"[Replicator] Job '{job.name}' result: {'OK' if ok else 'FAIL'} (lastResult={job.lastResult})", level="info" if ok else "warning")
+ return ok
+
+ def _log(self, msg: str, level: str = "info", channel: str = "replicator") -> None:
+ if self._logger is not None and hasattr(self._logger, "append"):
+ self._logger.append(msg, level=level, channel=channel) # type: ignore[call-arg]
+ else:
+ print(msg)
+
+ def _db_debug_snapshot(self, verbose: bool = False) -> None:
+ """
+ Log a compact snapshot of the current DB layout + row counts.
+ If verbose=True, also logs the most recent rows for key tables.
+ """
+ try:
+ # List tables
+ tables = [r["name"] for r in self._db.query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name")]
+
+ self._log(f"[Replicator][DB] Tables: {', '.join(tables) if tables else '(none)'}", level="debug")
+
+ # Layout + counts
+ for t in tables:
+ try:
+ cols = self._db.query(f"PRAGMA table_info({t})")
+ col_names = [c.get("name") for c in cols]
+ count = int(self._db.scalar(f"SELECT COUNT(*) FROM {t}") or 0)
+ self._log(f"[Replicator][DB] {t}: columns={len(col_names)} rows={count}", level="debug")
+ if verbose:
+ self._log(f"[Replicator][DB] {t}: {', '.join(col_names)}", level="debug")
+ except Exception as e:
+ self._log(f"[Replicator][DB] Failed introspecting {t}: {e}", level="warning")
+
+ if not verbose:
+ return
+
+ for t, order_col in (("jobs", "id"), ("runs", "id"), ("schedule", "id"), ("conflicts", "id")):
+ if t in tables:
+ try:
+ rows = self._db.query(f"SELECT * FROM {t} ORDER BY {order_col} DESC LIMIT 3")
+ self._log(f"[Replicator][DB] {t}: latest {len(rows)} row(s)", level="debug")
+ for r in rows:
+ dr = dict(r)
+ for k in ("password", "pass"):
+ if k in dr and dr[k]:
+ dr[k] = "***"
+ self._log(f"[Replicator][DB] {t}: {dr}", level="debug")
+ except Exception as e:
+ self._log(f"[Replicator][DB] Failed reading latest rows from {t}: {e}", level="warning")
+
+ except Exception as e:
+ self._log(f"[Replicator][DB] Snapshot failed: {e}", level="warning")
+
+ def _db_maintenance(self) -> None:
+ """Prune old history and run lightweight SQLite maintenance.
+
+ Safe defaults:
+ - Keep last N runs per job (db.retention.runs.keepLast)
+ - Also prune runs older than X days (db.retention.runs.keepDays)
+ - Keep open conflicts; prune resolved conflicts older than X days
+ - Optionally VACUUM (off by default)
+
+ This is designed to be called periodically (e.g. daily) by the service loop.
+ """
+ try:
+ keep_last = int(self._configuration.get("db.retention.runs.keepLast", 500))
+ keep_days = int(self._configuration.get("db.retention.runs.keepDays", 30))
+ prune_conflict_days = int(self._configuration.get("db.retention.conflicts.keepDays", 90))
+ prune_deleted_state_days = int(self._configuration.get("db.retention.fileState.deletedKeepDays", 30))
+ do_vacuum = bool(self._configuration.get("db.maintenance.vacuum", False))
+ except Exception:
+ keep_last, keep_days = 500, 30
+ prune_conflict_days, prune_deleted_state_days = 90, 30
+ do_vacuum = False
+
+ now = datetime.now(timezone.utc)
+ now_iso = now.isoformat()
+
+ # Runs: keep last N per job + prune anything older than keep_days
+ try:
+ job_ids = [int(r.get("id")) for r in (self._db.query("SELECT id FROM jobs") or [])]
+ with self._db.transaction():
+ for jid in job_ids:
+ if keep_last > 0:
+ self._db.execute(
+ """
+ DELETE FROM runs
+ WHERE jobId = ?
+ AND id NOT IN (
+ SELECT id FROM runs WHERE jobId = ? ORDER BY id DESC LIMIT ?
+ )
+ """,
+ (jid, jid, keep_last),
+ )
+
+ if keep_days > 0:
+ self._db.execute(
+ "DELETE FROM runs WHERE julianday(created) < julianday('now', ?) ",
+ (f'-{keep_days} days',),
+ )
+ except Exception as e:
+ self._log(f"[Replicator][DB] Maintenance: runs prune failed: {e}", level="warning")
+
+ # Conflicts: keep all open; prune non-open older than prune_conflict_days
+ try:
+ if prune_conflict_days > 0:
+ with self._db.transaction():
+ self._db.execute(
+ "DELETE FROM conflicts WHERE status <> 'open' AND julianday(created) < julianday('now', ?) ",
+ (f'-{prune_conflict_days} days',),
+ )
+ except Exception as e:
+ self._log(f"[Replicator][DB] Maintenance: conflicts prune failed: {e}", level="warning")
+
+ # file_state: prune deleted entries that have been deleted for a long time
+ try:
+ if prune_deleted_state_days > 0:
+ with self._db.transaction():
+ self._db.execute(
+ "DELETE FROM file_state WHERE deleted = 1 AND deletedAt IS NOT NULL AND julianday(deletedAt) < julianday('now', ?) ",
+ (f'-{prune_deleted_state_days} days',),
+ )
+ except Exception as e:
+ self._log(f"[Replicator][DB] Maintenance: file_state prune failed: {e}", level="warning")
+
+ # SQLite maintenance: optimize; optionally vacuum
+ try:
+ self._db.execute("PRAGMA optimize;")
+ if do_vacuum:
+ self._db.execute("VACUUM;")
+ self._migration.set_meta("maintenance.lastRunAt", now_iso)
+ self._log("[Replicator][DB] Maintenance completed.", level="debug")
+ except Exception as e:
+ self._log(f"[Replicator][DB] Maintenance failed: {e}", level="warning")
+
+ def _open_service_manager(self) -> None:
+ """Open the corePY Service Manager dialog (if available)."""
+ try:
+ svc = getattr(self._app, "service", None)
+ if svc is None:
+ raise RuntimeError("Service is not available on the application instance.")
+
+ # Preferred helper added in corePY Service
+ if hasattr(svc, "open_manager_dialog"):
+ svc.open_manager_dialog(parent=self)
+ return
+
+ # Fallback: if the dialog class exists, try to instantiate it
+ dlg_cls = getattr(svc, "ServiceManagerDialog", None)
+ if dlg_cls is not None:
+ dlg = dlg_cls(parent=self, service=svc)
+ dlg.exec_()
+ return
+
+ raise RuntimeError("Service manager UI is not available in this build.")
+
+ except Exception as e:
+ MsgBox.show(self, "Service", f"Unable to open Service Manager: {e}", icon="warning")
diff --git a/src/replicator/ui.py b/src/replicator/ui.py
new file mode 100644
index 00000000..e0c96b10
--- /dev/null
+++ b/src/replicator/ui.py
@@ -0,0 +1,816 @@
+#!/usr/bin/env python3
+# src/replicator/ui.py
+
+from __future__ import annotations
+
+from typing import Optional, Any, Dict, List, Tuple
+
+from PyQt5.QtCore import Qt, QTime, pyqtSignal
+from PyQt5.QtWidgets import (
+ QWidget,
+ QVBoxLayout,
+ QHBoxLayout,
+ QLabel,
+ QPushButton,
+ QDialog,
+ QFormLayout,
+ QLineEdit,
+ QCheckBox,
+ QComboBox,
+ QSpinBox,
+ QTextEdit,
+ QFrame,
+ QSizePolicy,
+ QLayout,
+ QTimeEdit,
+ QFileDialog,
+)
+
+try:
+ from core.ui import MsgBox
+except ImportError:
+ from ui import MsgBox
+
+# ------------------------------------------------------------------
+# Helpers
+# ------------------------------------------------------------------
+class BrowseLineEdit(QLineEdit):
+ """QLineEdit that emits a signal when clicked (used to open browse dialogs)."""
+ clicked = pyqtSignal()
+
+ def mousePressEvent(self, event):
+ try:
+ self.clicked.emit()
+ except Exception:
+ pass
+ super().mousePressEvent(event)
+
+# ------------------------------------------------------------------
+# Job Dialog
+# ------------------------------------------------------------------
+class JobDialog(QDialog):
+ def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None, *, default_interval_seconds: Optional[int] = None):
+ super().__init__(parent)
+ self.setWindowTitle("Replication Job")
+ self.setModal(True)
+ self.setFixedHeight(400)
+ self.setMinimumHeight(400)
+ self.setMinimumWidth(1200)
+
+ job = job or {}
+ self._original_job = job
+
+ # Default interval for new schedules (seconds). Provided by the app (e.g. Configuration service.defaultInterval).
+ try:
+ di = int(default_interval_seconds) if default_interval_seconds is not None else 3600
+ except Exception:
+ di = 3600
+ if di <= 0:
+ di = 3600
+ self._default_interval_seconds = di
+
+ layout = QVBoxLayout(self)
+ layout.setAlignment(Qt.AlignTop)
+ layout.setSizeConstraint(QLayout.SetMinimumSize)
+
+ # ------------------------------
+ # Name row
+ # ------------------------------
+ name_row = QHBoxLayout()
+ name_row.addWidget(QLabel("Name"))
+ self.name = QLineEdit(job.get("name", ""))
+ name_row.addWidget(self.name, 1)
+ layout.addLayout(name_row)
+
+ # Helper to get endpoint dict from job or fallback
+ def _get_endpoint(key_endpoint: str, key_str: str, default_type: str = "local") -> Dict[str, Any]:
+ ep = job.get(key_endpoint)
+ if isinstance(ep, dict):
+ return dict(ep)
+ return {"type": default_type, "location": job.get(key_str, ""), "auth": {}}
+
+ self._source_ep = _get_endpoint("sourceEndpoint", "source", "local")
+ self._target_ep = _get_endpoint("targetEndpoint", "target", "local")
+
+ # ------------------------------
+ # Two-column Source / Destination
+ # ------------------------------
+ cols = QHBoxLayout()
+ cols.setSpacing(20)
+ cols.setAlignment(Qt.AlignTop)
+
+ src_frame = QFrame()
+ src_frame.setObjectName("EndpointFrame")
+ src_frame.setFrameShape(QFrame.NoFrame)
+ src_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
+ src_frame.setMinimumHeight(0)
+ src_frame.setStyleSheet(
+ "#EndpointFrame { border: 1px solid rgba(255,255,255,0.12); border-radius: 8px; padding: 10px; }"
+ )
+ src_col = QVBoxLayout(src_frame)
+ src_col.setContentsMargins(10, 10, 10, 10)
+ src_col.setSpacing(8)
+ src_col.setAlignment(Qt.AlignTop)
+
+ dst_frame = QFrame()
+ dst_frame.setObjectName("EndpointFrame")
+ dst_frame.setFrameShape(QFrame.NoFrame)
+ dst_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
+ dst_frame.setMinimumHeight(0)
+ dst_frame.setStyleSheet(
+ "#EndpointFrame { border: 1px solid rgba(255,255,255,0.12); border-radius: 8px; padding: 10px; }"
+ )
+ dst_col = QVBoxLayout(dst_frame)
+ dst_col.setContentsMargins(10, 10, 10, 10)
+ dst_col.setSpacing(8)
+ dst_col.setAlignment(Qt.AlignTop)
+
+ src_title = QLabel("Source")
+ src_title.setStyleSheet("font-weight: 600;")
+ dst_title = QLabel("Destination")
+ dst_title.setStyleSheet("font-weight: 600;")
+
+ src_col.addWidget(src_title)
+ dst_col.addWidget(dst_title)
+
+ (
+ self._source_type_combo,
+ self._source_location_edit,
+ self._source_port_spin,
+ self._source_endpoint_row,
+ self._source_auth_widget,
+ self._source_auth_widgets,
+ ) = self._build_endpoint(existing=self._source_ep)
+
+ (
+ self._target_type_combo,
+ self._target_location_edit,
+ self._target_port_spin,
+ self._target_endpoint_row,
+ self._target_auth_widget,
+ self._target_auth_widgets,
+ ) = self._build_endpoint(existing=self._target_ep)
+
+ src_col.addWidget(self._source_endpoint_row)
+ src_col.addWidget(self._source_auth_widget)
+
+ dst_col.addWidget(self._target_endpoint_row)
+ dst_col.addWidget(self._target_auth_widget)
+
+ cols.addWidget(src_frame, 1)
+ cols.addWidget(dst_frame, 1)
+ layout.addLayout(cols)
+
+ # ------------------------------
+ # Mode + Direction on same line
+ # ------------------------------
+ self.mode = QComboBox()
+ # Mode is now a first-class control.
+ # mirror => allowDeletion=True
+ # incremental => allowDeletion=False
+ self.mode.addItems(["mirror", "incremental"])
+
+ # Backward compatibility:
+ # - Older jobs may only have allowDeletion without a mode.
+ # - Some jobs may still have mode="mirror".
+ mode_val = str(job.get("mode") or "").strip().lower()
+ if mode_val not in ("mirror", "incremental"):
+ # Infer from allowDeletion when mode is missing/legacy.
+ mode_val = "mirror" if bool(job.get("allowDeletion", False)) else "incremental"
+
+ idx = self.mode.findText(mode_val)
+ if idx >= 0:
+ self.mode.setCurrentIndex(idx)
+
+ self.direction = QComboBox()
+ self.direction.addItems(["unidirectional", "bidirectional"])
+ direction_val = job.get("direction", "unidirectional")
+ idx = self.direction.findText(direction_val)
+ if idx >= 0:
+ self.direction.setCurrentIndex(idx)
+
+ mode_dir_row = QHBoxLayout()
+
+ mode_wrap = QWidget()
+ mode_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
+ mode_lay = QHBoxLayout(mode_wrap)
+ mode_lay.setContentsMargins(0, 0, 0, 0)
+ mode_lay.setSpacing(8)
+ mode_lay.addWidget(QLabel("Mode"))
+ mode_lay.addWidget(self.mode, 1)
+
+ dir_wrap = QWidget()
+ dir_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
+ dir_lay = QHBoxLayout(dir_wrap)
+ dir_lay.setContentsMargins(0, 0, 0, 0)
+ dir_lay.setSpacing(8)
+ dir_lay.addWidget(QLabel("Direction"))
+ dir_lay.addWidget(self.direction, 1)
+
+ mode_dir_row.addWidget(mode_wrap, 1)
+ mode_dir_row.addWidget(dir_wrap, 1)
+ layout.addLayout(mode_dir_row)
+
+ # ------------------------------
+ # Other options
+ # ------------------------------
+ opts_row = QHBoxLayout()
+
+ # Deletion behavior is controlled by mode now.
+ # Keep the UI simple: show metadata + enabled toggles here.
+ self.preserve_metadata = QCheckBox("Preserve metadata")
+ self.preserve_metadata.setChecked(bool(job.get("preserveMetadata", True)))
+ self.enabled = QCheckBox("Enabled")
+ self.enabled.setChecked(bool(job.get("enabled", True)))
+
+ opts_row.addWidget(self.preserve_metadata)
+ opts_row.addSpacing(16)
+ opts_row.addWidget(self.enabled)
+ opts_row.addStretch(1)
+
+ # Schedule button (UI stays here)
+ self._schedule_btn = QPushButton("Schedule…")
+ self._schedule_btn.clicked.connect(self._open_schedule)
+ opts_row.addWidget(self._schedule_btn)
+
+ layout.addLayout(opts_row)
+
+ # ------------------------------
+ # Buttons
+ # ------------------------------
+ btn_row = QHBoxLayout()
+ btn_row.addStretch(1)
+ cancel_btn = QPushButton("Cancel")
+ ok_btn = QPushButton("Save")
+ cancel_btn.clicked.connect(self.reject)
+ ok_btn.clicked.connect(self._on_ok)
+ btn_row.addWidget(cancel_btn)
+ btn_row.addWidget(ok_btn)
+ layout.addLayout(btn_row)
+
+ # Wire type changes
+ self._source_type_combo.currentIndexChanged.connect(lambda _i: self._on_type_changed(
+ self._source_type_combo,
+ self._source_location_edit,
+ self._source_port_spin,
+ self._source_auth_widget,
+ self._source_auth_widgets,
+ ))
+ self._target_type_combo.currentIndexChanged.connect(lambda _i: self._on_type_changed(
+ self._target_type_combo,
+ self._target_location_edit,
+ self._target_port_spin,
+ self._target_auth_widget,
+ self._target_auth_widgets,
+ ))
+
+ # Trigger initial state
+ self._on_type_changed(
+ self._source_type_combo,
+ self._source_location_edit,
+ self._source_port_spin,
+ self._source_auth_widget,
+ self._source_auth_widgets,
+ initial=True,
+ )
+ self._on_type_changed(
+ self._target_type_combo,
+ self._target_location_edit,
+ self._target_port_spin,
+ self._target_auth_widget,
+ self._target_auth_widgets,
+ initial=True,
+ )
+
+ def _open_schedule(self) -> None:
+ job = self._original_job if isinstance(self._original_job, dict) else {}
+ dlg = ScheduleDialog(self, job=job, default_interval_seconds=self._default_interval_seconds)
+ if dlg.exec_() == QDialog.Accepted:
+ # store schedule back into original job dict so value() preserves it
+ if not isinstance(self._original_job, dict):
+ self._original_job = {}
+ self._original_job["schedule"] = dlg.value()
+
+ # ------------------------------------------------------------------
+ # Endpoint UI
+ # ------------------------------------------------------------------
+
+ def _build_endpoint(self, existing: Dict[str, Any]):
+ type_combo = QComboBox()
+ type_combo.addItems(["local", "smb"])
+ idx = type_combo.findText(existing.get("type", "local"))
+ if idx >= 0:
+ type_combo.setCurrentIndex(idx)
+ if idx < 0:
+ type_combo.setCurrentIndex(0)
+ type_combo.setFixedWidth(110)
+ type_combo.setProperty("existing_type", existing.get("type", "local"))
+ type_combo.setProperty("existing_auth", existing.get("auth", {}) or {})
+
+ location_edit = BrowseLineEdit(existing.get("location", ""))
+
+ port_spin = QSpinBox()
+ port_spin.setRange(1, 65535)
+ port_spin.setFixedWidth(110)
+ # Ports are not used for local/SMB endpoints.
+ port_spin.setValue(1)
+ port_spin.setVisible(False)
+
+ endpoint_fields = QWidget()
+ fields_lay = QHBoxLayout(endpoint_fields)
+ fields_lay.setContentsMargins(0, 0, 0, 0)
+ fields_lay.setSpacing(8)
+ fields_lay.addWidget(type_combo)
+ fields_lay.addWidget(location_edit, 1)
+ fields_lay.addWidget(port_spin)
+
+ def _browse_location():
+ try:
+ typ = (type_combo.currentText() or "").lower()
+ if typ != "local":
+ return
+ start_dir = location_edit.text().strip() or ""
+ # If the user typed a file path, prefer its directory.
+ if start_dir and not start_dir.endswith("/") and not start_dir.endswith("\\"):
+ try:
+ import os
+ if os.path.isfile(start_dir):
+ start_dir = os.path.dirname(start_dir)
+ except Exception:
+ pass
+ selected = QFileDialog.getExistingDirectory(self, "Select folder", start_dir)
+ if selected:
+ location_edit.setText(selected)
+ except Exception:
+ return
+
+ # Click-to-browse for local paths (no extra button needed).
+ location_edit.clicked.connect(_browse_location)
+
+ endpoint_row = QWidget()
+ endpoint_row.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
+ endpoint_form = QFormLayout(endpoint_row)
+ endpoint_form.setContentsMargins(0, 0, 0, 0)
+ endpoint_form.setSpacing(6)
+ endpoint_form.addRow(endpoint_fields)
+
+ auth_widget = QFrame()
+ auth_widget.setFrameShape(QFrame.NoFrame)
+ auth_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
+ auth_widget.setMinimumHeight(0)
+
+ auth_lay = QVBoxLayout(auth_widget)
+ auth_lay.setContentsMargins(0, 0, 0, 0)
+ auth_lay.setSpacing(6)
+ auth_lay.setAlignment(Qt.AlignTop)
+
+ auth_title = QLabel("Authentication")
+ auth_title.setStyleSheet("font-weight: 600;")
+
+ widgets: Dict[str, Any] = {}
+
+ # SMB auth
+ smb_wrap = QWidget()
+ smb_wrap.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
+ smb_form = QFormLayout(smb_wrap)
+ smb_form.setContentsMargins(0, 0, 0, 0)
+ smb_guest = QCheckBox("Login as Guest")
+ smb_user_lbl = QLabel("Username (user or user@domain)")
+ smb_username = QLineEdit()
+ smb_pass_lbl = QLabel("Password")
+ smb_password = QLineEdit()
+ smb_password.setEchoMode(QLineEdit.Password)
+
+ smb_auth = existing.get("auth", {}) if existing.get("type") == "smb" else {}
+ smb_guest.setChecked(bool(smb_auth.get("guest", True)))
+ smb_username.setText(smb_auth.get("username", ""))
+ smb_password.setText(smb_auth.get("password", ""))
+
+ smb_form.addRow(smb_guest)
+ smb_form.addRow(smb_user_lbl, smb_username)
+ smb_form.addRow(smb_pass_lbl, smb_password)
+
+ def _smb_guest_update():
+ guest = smb_guest.isChecked()
+ smb_user_lbl.setVisible(not guest)
+ smb_username.setVisible(not guest)
+ smb_pass_lbl.setVisible(not guest)
+ smb_password.setVisible(not guest)
+
+ smb_guest.stateChanged.connect(_smb_guest_update)
+ _smb_guest_update()
+
+ auth_lay.addWidget(auth_title)
+ auth_lay.addWidget(smb_wrap)
+
+ widgets["smb"] = {
+ "wrap": smb_wrap,
+ "guest": smb_guest,
+ "user_lbl": smb_user_lbl,
+ "username": smb_username,
+ "pass_lbl": smb_pass_lbl,
+ "password": smb_password,
+ }
+
+ return type_combo, location_edit, port_spin, endpoint_row, auth_widget, widgets
+
+ def _on_type_changed(
+ self,
+ type_combo: QComboBox,
+ location_edit: QLineEdit,
+ port_spin: QSpinBox,
+ auth_widget: QWidget,
+ widgets: Dict[str, Any],
+ initial: bool = False,
+ ):
+ typ = type_combo.currentText()
+
+ placeholder = {
+ "local": "Local path (e.g. /data or C:\\Data)",
+ "smb": "SMB path (e.g. \\\\SERVER\\Share\\Folder)",
+ }.get(typ, "")
+ location_edit.setPlaceholderText(placeholder)
+ # For local endpoints, clicking the location field opens a folder chooser.
+ # For non-local endpoints, keep normal typing behavior.
+ if (typ or "").lower() == "local":
+ location_edit.setCursorPosition(len(location_edit.text()))
+ # Ports are not used for local/SMB endpoints.
+ port_spin.setVisible(False)
+
+ if typ == "local":
+ auth_widget.setVisible(False)
+ auth_widget.setMaximumHeight(0)
+ else:
+ auth_widget.setVisible(True)
+ auth_widget.setMaximumHeight(16777215)
+
+ # Hide all auth sections
+ for key in ("smb",):
+ if key in widgets and "wrap" in widgets[key]:
+ widgets[key]["wrap"].setVisible(False)
+
+ # Show only the relevant auth section
+ if typ == "smb":
+ widgets["smb"]["wrap"].setVisible(True)
+
+ auth_widget.adjustSize()
+ if auth_widget.parentWidget() is not None:
+ auth_widget.parentWidget().adjustSize()
+ self.adjustSize()
+
+ # ------------------------------------------------------------------
+ # Validation
+ # ------------------------------------------------------------------
+
+ def _on_ok(self):
+ if not self.name.text().strip():
+ MsgBox.show(self, "Job", "Name is required.", icon="warning")
+ return
+
+ src_type = self._source_type_combo.currentText()
+ tgt_type = self._target_type_combo.currentText()
+
+ src_loc = self._source_location_edit.text().strip()
+ tgt_loc = self._target_location_edit.text().strip()
+
+ if not src_loc:
+ MsgBox.show(self, "Job", "Source location is required.", icon="warning")
+ return
+ if not tgt_loc:
+ MsgBox.show(self, "Job", "Target location is required.", icon="warning")
+ return
+
+ if src_type == "smb":
+ w = self._source_auth_widgets["smb"]
+ if not w["guest"].isChecked():
+ # Either Guest is enabled OR both username+password are provided.
+ # If either field is missing, force Guest and block save.
+ if not w["username"].text().strip() or not w["password"].text().strip():
+ w["guest"].setChecked(True)
+ MsgBox.show(
+ self,
+ "Job",
+ "For the Source SMB endpoint, either enable Guest or provide BOTH a username and a password.",
+ icon="warning",
+ )
+ return
+
+ if tgt_type == "smb":
+ w = self._target_auth_widgets["smb"]
+ if not w["guest"].isChecked():
+ # Either Guest is enabled OR both username+password are provided.
+ # If either field is missing, force Guest and block save.
+ if not w["username"].text().strip() or not w["password"].text().strip():
+ w["guest"].setChecked(True)
+ MsgBox.show(
+ self,
+ "Job",
+ "For the Destination SMB endpoint, either enable Guest or provide BOTH a username and a password.",
+ icon="warning",
+ )
+ return
+
+ self.accept()
+
+ # ------------------------------------------------------------------
+ # Value extraction
+ # ------------------------------------------------------------------
+
+ def value(self) -> Dict[str, Any]:
+ def _extract(type_combo: QComboBox, location_edit: QLineEdit, port_spin: QSpinBox, widgets: Dict[str, Any]) -> Dict[str, Any]:
+ typ = type_combo.currentText()
+ location = location_edit.text().strip()
+ auth: Dict[str, Any] = {}
+
+ if typ == "local":
+ auth = {}
+ elif typ == "smb":
+ w = widgets["smb"]
+ guest = bool(w["guest"].isChecked())
+ # If Guest is enabled, credentials are irrelevant; clear them on save.
+ auth = {
+ "guest": guest,
+ "username": "" if guest else w["username"].text().strip(),
+ "password": "" if guest else w["password"].text(),
+ }
+ else:
+ # Unsupported/legacy types fall back to local
+ typ = "local"
+ auth = {}
+
+ return {"type": typ, "location": location, "auth": auth}
+
+ source_ep = _extract(self._source_type_combo, self._source_location_edit, self._source_port_spin, self._source_auth_widgets)
+ target_ep = _extract(self._target_type_combo, self._target_location_edit, self._target_port_spin, self._target_auth_widgets)
+
+ val: Dict[str, Any] = {
+ "name": self.name.text().strip(),
+ "sourceEndpoint": source_ep,
+ "targetEndpoint": target_ep,
+ "source": source_ep["location"], # backward compatibility
+ "target": target_ep["location"], # backward compatibility
+ # Backward compatible field: allowDeletion derives from mode.
+ "allowDeletion": bool((self.mode.currentText() or "").lower() == "mirror"),
+ "preserveMetadata": bool(self.preserve_metadata.isChecked()),
+ "mode": self.mode.currentText(),
+ "direction": self.direction.currentText(),
+ "enabled": bool(self.enabled.isChecked()),
+ }
+
+ # Preserve schedule (and update if ScheduleDialog was used)
+ if self._original_job and isinstance(self._original_job, dict):
+ sched = self._original_job.get("schedule")
+ if sched is not None:
+ val["schedule"] = sched
+
+ return val
+ # Remove any remaining references to self.allow_deletion in this class.
+
+# ------------------------------------------------------------------
+# Schedule Dialog
+# ------------------------------------------------------------------
+class ScheduleDialog(QDialog):
+ """Schedule editor UI.
+
+ Persists as:
+ schedule = {
+ "enabled": bool,
+ "intervalSeconds": int, # computed fallback (minimum per-day interval if enabled, else default)
+ "windows": {
+ "0":[{"start":"22:00","end":"06:00","intervalSeconds":3600}],
+ ...
+ }
+ }
+ """
+
+ _DAYS: List[Tuple[int, str]] = [
+ (0, "Monday"),
+ (1, "Tuesday"),
+ (2, "Wednesday"),
+ (3, "Thursday"),
+ (4, "Friday"),
+ (5, "Saturday"),
+ (6, "Sunday"),
+ ]
+
+ def __init__(self, parent=None, job: Optional[Dict[str, Any]] = None, *, default_interval_seconds: Optional[int] = None):
+ super().__init__(parent)
+ self.setWindowTitle("Schedule")
+ self.setModal(True)
+
+ # Default interval for new schedules (seconds). Provided by the app (e.g. Configuration service.defaultInterval).
+ try:
+ di = int(default_interval_seconds) if default_interval_seconds is not None else 3600
+ except Exception:
+ di = 3600
+ if di <= 0:
+ di = 3600
+ self._default_interval_seconds = di
+
+ job = job or {}
+ schedule = job.get("schedule") if isinstance(job.get("schedule"), dict) else {}
+
+ # IMPORTANT semantics:
+ # - schedule.enabled == False => job can be run manually but will NOT be executed by the service.
+ # - schedule.enabled == True => job may run in the service, but ONLY within selected day windows.
+ # - enabled with no days selected means "run never" (no service runs).
+
+ if not schedule:
+ # New schedule default: enabled, every day, all day, default interval
+ schedule = {"enabled": True, "intervalSeconds": int(self._default_interval_seconds), "windows": {}}
+ for wd in range(7):
+ schedule["windows"][str(wd)] = [
+ {"start": "00:00", "end": "23:59", "intervalSeconds": int(self._default_interval_seconds)}
+ ]
+ else:
+ # Existing schedule: preserve intent.
+ # If windows is missing/empty, DO NOT auto-populate days (that would change meaning).
+ if not isinstance(schedule.get("windows"), dict):
+ schedule["windows"] = {}
+
+ # Ensure each existing day window has intervalSeconds
+ try:
+ for _k, _v in list(schedule["windows"].items()):
+ if isinstance(_v, list) and _v and isinstance(_v[0], dict) and "intervalSeconds" not in _v[0]:
+ _v[0]["intervalSeconds"] = int(schedule.get("intervalSeconds") or self._default_interval_seconds)
+ except Exception:
+ pass
+
+ self._windows: Dict[str, Any] = schedule.get("windows", {}) if isinstance(schedule.get("windows", {}), dict) else {}
+
+ layout = QVBoxLayout(self)
+ layout.setAlignment(Qt.AlignTop)
+ layout.setSizeConstraint(QLayout.SetMinimumSize)
+
+ form = QFormLayout()
+ layout.addLayout(form)
+
+ self.enabled = QCheckBox()
+ self.enabled.setChecked(bool(schedule.get("enabled", False)))
+
+ form.addRow("Enabled", self.enabled)
+
+ hint = QLabel("Tip: Enable the schedule to allow service runs. If enabled and no days are selected, the job will not run in the service.")
+ hint.setWordWrap(True)
+ hint.setStyleSheet("opacity: 0.75;")
+ layout.addWidget(hint)
+
+ # Windows editor
+ win_frame = QFrame()
+ win_frame.setFrameShape(QFrame.NoFrame)
+ win_lay = QVBoxLayout(win_frame)
+ win_lay.setContentsMargins(0, 0, 0, 0)
+ win_lay.setSpacing(6)
+
+ title = QLabel("Allowed run windows")
+ title.setStyleSheet("font-weight: 600;")
+ win_lay.addWidget(title)
+
+ self._day_controls: Dict[int, Dict[str, Any]] = {}
+
+ for wd, label in self._DAYS:
+ row = QHBoxLayout()
+ row.setSpacing(10)
+
+ day_enabled = QCheckBox(label)
+ start = QTimeEdit()
+ end = QTimeEdit()
+ start.setDisplayFormat("HH:mm")
+ end.setDisplayFormat("HH:mm")
+ start.setTime(QTime(0, 0))
+ end.setTime(QTime(23, 59))
+
+ interval = QSpinBox()
+ interval.setRange(1, 604800) # 1s .. 7 days
+ interval.setFixedWidth(130)
+ interval.setValue(int(schedule.get("intervalSeconds", self._default_interval_seconds) or self._default_interval_seconds))
+
+ # Load from existing windows if present
+ key = str(wd)
+ day_windows = self._windows.get(key)
+ if isinstance(day_windows, list) and len(day_windows) > 0 and isinstance(day_windows[0], dict):
+ w0 = day_windows[0]
+ s = str(w0.get("start", "00:00"))
+ e = str(w0.get("end", "23:59"))
+ day_enabled.setChecked(True)
+ start.setTime(self._parse_qtime(s, QTime(0, 0)))
+ end.setTime(self._parse_qtime(e, QTime(23, 59)))
+ try:
+ interval.setValue(int(w0.get("intervalSeconds", schedule.get("intervalSeconds", self._default_interval_seconds)) or self._default_interval_seconds))
+ except Exception:
+ interval.setValue(int(schedule.get("intervalSeconds", self._default_interval_seconds) or self._default_interval_seconds))
+ else:
+ day_enabled.setChecked(False)
+ try:
+ interval.setValue(int(schedule.get("intervalSeconds", self._default_interval_seconds) or self._default_interval_seconds))
+ except Exception:
+ interval.setValue(int(self._default_interval_seconds))
+
+ # Disable edits when not enabled
+ def _apply_enabled_state(_=None, *, _day_enabled=day_enabled, _start=start, _end=end, _interval=interval):
+ en = _day_enabled.isChecked()
+ _start.setEnabled(en)
+ _end.setEnabled(en)
+ _interval.setEnabled(en)
+
+ day_enabled.stateChanged.connect(_apply_enabled_state)
+ _apply_enabled_state()
+
+ row.addWidget(day_enabled, 1)
+ row.addWidget(QLabel("Start"))
+ row.addWidget(start)
+ row.addWidget(QLabel("End"))
+ row.addWidget(end)
+ row.addWidget(QLabel("Interval (s)"))
+ row.addWidget(interval)
+
+ win_lay.addLayout(row)
+
+ self._day_controls[wd] = {"enabled": day_enabled, "start": start, "end": end, "interval": interval}
+
+ layout.addWidget(win_frame)
+
+ btn_row = QHBoxLayout()
+ btn_row.addStretch(1)
+ cancel_btn = QPushButton("Cancel")
+ ok_btn = QPushButton("Save")
+ cancel_btn.clicked.connect(self.reject)
+ ok_btn.clicked.connect(self._on_ok)
+ btn_row.addWidget(cancel_btn)
+ btn_row.addWidget(ok_btn)
+ layout.addLayout(btn_row)
+
+ self._sync_enabled_state()
+
+ self.enabled.stateChanged.connect(lambda _i: self._sync_enabled_state())
+
+ def _sync_enabled_state(self):
+ en = self.enabled.isChecked()
+ for wd, ctrls in self._day_controls.items():
+ ctrls["enabled"].setEnabled(en)
+ # if schedule disabled, visually disable time edits too
+ if not en:
+ ctrls["start"].setEnabled(False)
+ ctrls["end"].setEnabled(False)
+ ctrls["interval"].setEnabled(False)
+ else:
+ # restore per-day checkbox logic
+ day_en = ctrls["enabled"].isChecked()
+ ctrls["start"].setEnabled(day_en)
+ ctrls["end"].setEnabled(day_en)
+ ctrls["interval"].setEnabled(day_en)
+
+ def _parse_qtime(self, s: str, default: QTime) -> QTime:
+ try:
+ parts = s.strip().split(":")
+ if len(parts) != 2:
+ return default
+ hh = int(parts[0])
+ mm = int(parts[1])
+ if hh < 0 or hh > 23 or mm < 0 or mm > 59:
+ return default
+ return QTime(hh, mm)
+ except Exception:
+ return default
+
+ def _on_ok(self):
+ if self.enabled.isChecked():
+ any_day = any(bool(ctrls["enabled"].isChecked()) for _wd, ctrls in self._day_controls.items())
+ if not any_day:
+ MsgBox.show(self, "Schedule", "Select at least one day when the schedule is enabled (otherwise the job will never run in the service).", icon="warning")
+ return
+ for _wd, ctrls in self._day_controls.items():
+ if not ctrls["enabled"].isChecked():
+ continue
+ if int(ctrls["interval"].value() or 0) < 1:
+ MsgBox.show(self, "Schedule", "Per-day interval must be at least 1 second for enabled days.", icon="warning")
+ return
+
+ # Basic validation: ensure enabled days have valid times (QTimeEdit ensures format)
+ # Allow overnight windows naturally (start > end) — that's intended.
+ self.accept()
+
+ def value(self) -> Dict[str, Any]:
+ windows: Dict[str, Any] = {}
+ enabled_intervals = []
+ if self.enabled.isChecked():
+ for wd, ctrls in self._day_controls.items():
+ if not ctrls["enabled"].isChecked():
+ continue
+ s = ctrls["start"].time().toString("HH:mm")
+ e = ctrls["end"].time().toString("HH:mm")
+ itv = int(ctrls["interval"].value())
+ windows[str(wd)] = [{"start": s, "end": e, "intervalSeconds": itv}]
+ enabled_intervals.append(itv)
+
+ # Compute fallback intervalSeconds: min per-day interval if enabled, else default
+ if self.enabled.isChecked() and enabled_intervals:
+ computed_interval_seconds = min(enabled_intervals)
+ else:
+ computed_interval_seconds = int(self._default_interval_seconds)
+
+ return {
+ "enabled": bool(self.enabled.isChecked()),
+ "intervalSeconds": int(computed_interval_seconds),
+ "windows": windows,
+ }
diff --git a/test.sh b/test.sh
new file mode 100755
index 00000000..b7ea6c02
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+touch tmp/a/file-$(date +%H-%M).txt
+touch tmp/c/file-$(date +%H-%M).txt
diff --git a/update.sh b/update.sh
new file mode 120000
index 00000000..ce091d3c
--- /dev/null
+++ b/update.sh
@@ -0,0 +1 @@
+src/core/update.sh
\ No newline at end of file