diff --git a/.cargo/config.toml b/.cargo/config.toml index ba19587f5..578d0c9f6 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,8 @@ [alias] -prisma = "run -p prisma-cli --" \ No newline at end of file +prisma = "run -p prisma-cli --" + +[target.x86_64-apple-darwin] +rustflags = [ + "-C", "link-arg=-undefined", + "-C", "link-arg=dynamic_lookup", +] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index e367c7232..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '[BUG] Give a suitable title' -labels: 'bug' -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information):** - - OS: [e.g. macOS, Windows, Linux, iOS, watchOS, Android] - - Version [e.g. 22] - -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Version [e.g. 22] - -**Additional context** -Add any other context about the problem here. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..1c8c5ffe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,61 @@ +name: 🐞 Bug Report +description: Report a bug +labels: + - kind/bug + - status/needs-triage + +body: + - type: markdown + attributes: + value: | + ## First of all + 1. Please search for [existing issues](https://github.com/spacedriveapp/spacedrive/issues?q=is%3Aissue) about this problem first. + 2. Make sure you run have the latest version of Rust (`rustup update`) and PNPM (`pnpm add -g pnpm`) along with all relevant Spacedrive dependencies. + 3. Make sure it's an issue with Spacedrive and not something else you are using. + 4. Remember to follow our community guidelines and be friendly. + + - type: textarea + id: description + attributes: + label: Describe the bug + description: A clear description of what the bug is. Include screenshots if applicable. + placeholder: Bug description + validations: + required: true + + - type: textarea + id: reproduction + attributes: + label: Reproduction + description: Steps to reproduce the behavior. + placeholder: | + 1. Go to ... + 2. Click on ... + 3. See error + + - type: textarea + id: expected-behavior + attributes: + label: Expected behavior + description: A clear description of what you expected to happen. + + - type: textarea + id: info + attributes: + label: Platform and versions + description: "Please include the output of `pnpm --version && cargo --version && rustc --version` along with information about your Operating System such as version and/or specific distribution if revelant." + render: shell + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Stack trace + render: shell + + - type: textarea + id: context + attributes: + label: Additional context + description: Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..5439e7a3c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: 🙏 Get Help + url: https://github.com/spacedriveapp/spacedrive/discussions/new?category=help + about: If you can't get something to work the way you expect, open a question in our discussion forums. + - name: 💡 Feature Request + url: https://github.com/spacedriveapp/spacedrive/discussions/new?category=ideas + about: Suggest any ideas you have using our discussion forums. + - name: 💬 Discord Chat + url: https://discord.gg/gTaF2Z44f5 + about: Ask questions and talk to other Spacedrive users and the maintainers \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 2d9fe9cda..000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '[FEATURE] Give a suitable title' -labels: 'enhancement' -assignees: '' - ---- - -**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. \ No newline at end of file diff --git a/.github/actions/build-and-publish-server/action.yml b/.github/actions/build-server-image/action.yml similarity index 91% rename from .github/actions/build-and-publish-server/action.yml rename to .github/actions/build-server-image/action.yml index efbb4ee93..6b6f86e38 100644 --- a/.github/actions/build-and-publish-server/action.yml +++ b/.github/actions/build-server-image/action.yml @@ -1,4 +1,4 @@ -name: Build and Publish Server +name: Build Server Image description: Builds and publishes the docker image for the Spacedrive server inputs: gh_token: @@ -34,10 +34,15 @@ runs: echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV echo "Building $IMAGE_NAME:$IMAGE_TAG" - - name: Build & push Docker image + - name: Build Docker image shell: bash run: | docker build ./apps/server --tag $IMAGE_NAME:$IMAGE_TAG + + - name: Push Docker image + shell: bash + if: github.event_name != 'pull_request' + run: | docker push $IMAGE_NAME:$IMAGE_TAG - name: Tag & push image as latest staging image diff --git a/.github/actions/install-deps/action.yaml b/.github/actions/install-deps/action.yaml new file mode 100644 index 000000000..320f2a92f --- /dev/null +++ b/.github/actions/install-deps/action.yaml @@ -0,0 +1,44 @@ +name: Install dependencies +description: Installs OS-specific dependencies for builds +runs: + using: "composite" + steps: + - name: Install ffmpeg & tauri deps (Ubuntu) + if: matrix.platform == 'ubuntu-latest' + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + libssl-dev \ + libavcodec-dev \ + libavdevice-dev \ + libavfilter-dev \ + libavformat-dev \ + libavresample-dev \ + libavutil-dev \ + libswscale-dev \ + libswresample-dev \ + ffmpeg \ + libgtk-3-dev \ + webkit2gtk-4.0 \ + libappindicator3-dev \ + librsvg2-dev \ + patchelf + + - name: Install ffmpeg (macOS) + if: matrix.platform == 'macos-latest' + shell: bash + run: brew install ffmpeg + + - name: Install vcpkg & ffmpeg (Windows) + if: matrix.platform == 'windows-latest' + shell: powershell + run: | + $VCINSTALLDIR = $(& "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath) + Add-Content $env:GITHUB_ENV "LIBCLANG_PATH=${VCINSTALLDIR}\VC\Tools\LLVM\x64\bin`n" + Invoke-WebRequest "https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full-shared.7z" -OutFile ffmpeg-release-full-shared.7z + 7z x ffmpeg-release-full-shared.7z + mkdir ffmpeg + mv ffmpeg-*/* ffmpeg/ + Add-Content $env:GITHUB_ENV "FFMPEG_DIR=${pwd}\ffmpeg`n" + Add-Content $env:GITHUB_PATH "${pwd}\ffmpeg\bin`n" \ No newline at end of file diff --git a/.github/actions/install-ffmpeg-macos/action.yml b/.github/actions/install-ffmpeg-macos/action.yml new file mode 100644 index 000000000..73c7eb6ee --- /dev/null +++ b/.github/actions/install-ffmpeg-macos/action.yml @@ -0,0 +1,5 @@ +name: 'Install ffmpeg macOS' +description: 'Installs ffmpeg with caching for macOS' +runs: + using: 'node16' + main: 'index.js' diff --git a/.github/actions/install-ffmpeg-macos/index.js b/.github/actions/install-ffmpeg-macos/index.js new file mode 100644 index 000000000..700837043 --- /dev/null +++ b/.github/actions/install-ffmpeg-macos/index.js @@ -0,0 +1,8 @@ +// @ts-check +const core = require('@actions/core'); +const exec = require('@actions/exec'); +const github = require('@actions/github'); + +// const folders = + +exec.exec('brew', ['install', 'ffmpeg']); diff --git a/.github/actions/install-ffmpeg-macos/package.json b/.github/actions/install-ffmpeg-macos/package.json new file mode 100644 index 000000000..6d0070abe --- /dev/null +++ b/.github/actions/install-ffmpeg-macos/package.json @@ -0,0 +1,17 @@ +{ + "name": "install-ffmpeg-macos", + "version": "0.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "Brendan Allan", + "license": "ISC", + "dependencies": { + "@actions/core": "^1.6.0", + "@actions/exec": "^1.1.1", + "@actions/github": "^5.0.1" + } +} diff --git a/.github/actions/publish-desktop-artifacts/action.yaml b/.github/actions/publish-desktop-artifacts/action.yaml new file mode 100644 index 000000000..fd54bed43 --- /dev/null +++ b/.github/actions/publish-desktop-artifacts/action.yaml @@ -0,0 +1,36 @@ +name: Publish desktop artifacts +description: Publishes desktop artifacts after Tauri build +runs: + using: 'composite' + steps: + - name: Make AppImage executable + if: matrix.platform == 'ubuntu-latest' + shell: bash + run: chmod +x ./target/release/bundle/appimage/spacedrive*.AppImage + + - name: Determine short GitHub SHA + shell: bash + run: | + export GITHUB_SHA_SHORT=$(git rev-parse --short "$GITHUB_SHA") + echo "GITHUB_SHA_SHORT=$GITHUB_SHA_SHORT" >> $GITHUB_ENV + + - name: Publish artifacts (AppImage) + if: matrix.platform == 'ubuntu-latest' + uses: actions/upload-artifact@v3 + with: + name: Spacedrive-AppImage-${{ env.GITHUB_SHA_SHORT }} + path: ./target/release/bundle/appimage/spacedrive*.AppImage + + - name: Publish artifacts (Windows) + if: matrix.platform == 'windows-latest' + uses: actions/upload-artifact@v3 + with: + name: Spacedrive-Windows-${{ env.GITHUB_SHA_SHORT }} + path: .\target\release\bundle\msi\*.msi + + - name: Publish artifacts (macOS) + if: matrix.platform == 'macos-latest' + uses: actions/upload-artifact@v3 + with: + name: Spacedrive-macOS-${{ env.GITHUB_SHA_SHORT }} + path: ./target/release/bundle/macos/*.app diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..eb3361eda --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,10 @@ + + + + + + +Closes #(issue) diff --git a/.github/scripts/setup-system.sh b/.github/scripts/setup-system.sh new file mode 100755 index 000000000..3c6ed85b3 --- /dev/null +++ b/.github/scripts/setup-system.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +echo "Setting up your system for Spacedrive development!" + +which cargo &> /dev/null +if [ $? -eq 1 ]; then + echo "Rust was not detected on your system. Ensure the 'rustc' and 'cargo' binaries are in your \$PATH." + exit 1 +fi + +which pnpm &> /dev/null +if [ $? -eq 1 ]; then + echo "PNPM was not detected on your system. Ensure the 'pnpm' command is in your \$PATH. You are **not** able to use Yarn or NPM." + exit 1 +fi + +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + if which apt-get &> /dev/null; then + echo "Detected 'apt' based distro!" + DEBIAN_TAURI_DEPS="libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libappindicator3-dev librsvg2-dev" # Tauri dependencies + DEBIAN_FFMPEG_DEPS="libavcodec-dev libavdevice-dev libavfilter-dev libavformat-dev libavresample-dev libavutil-dev libswscale-dev libswresample-dev ffmpeg" # FFMPEG dependencies + DEBIAN_BINDGEN_DEPS="pkg-config clang" # Bindgen dependencies - it's used by a dependency of Spacedrive + + sudo apt-get -y update + sudo apt-get -y install $DEBIAN_TAURI_DEPS $DEBIAN_FFMPEG_DEPS $DEBIAN_BINDGEN_DEPS + elif which pacman &> /dev/null; then + echo "Detected 'pacman' based distro!" + sudo pacman -S --needed webkit2gtk base-devel curl wget openssl appmenu-gtk-module gtk3 libappindicator-gtk3 librsvg libvips + + ARCH_TAURI_DEPS="libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libappindicator3-dev librsvg2-dev" # Tauri dependencies + ARCH_FFMPEG_DEPS="" # FFMPEG dependencies # TODO + ARCH_BINDGEN_DEPS="clang" # Bindgen dependencies - it's used by a dependency of Spacedrive + + sudo pacman -Syu + sudo pacman -S --needed $ARCH_TAURI_DEPS $ARCH_FFMPEG_DEPS $ARCH_BINDGEN_DEPS + + # TODO: Remove warning + echo "The FFMPEG dependencies are not yet included in this script for your Linux Distro. Please install them manually." + echo "It would also be greatly appreciated if you could ping @oscartbeaumont in the Discord or GitHub so that you can help me work these out and update the script for everyone." + exit 1 + elif which dnf &> /dev/null; then + echo "Detected 'dnf' based distro!" + FEDORA_TAURI_DEPS="webkit2gtk3-devel.x86_64 openssl-devel curl wget libappindicator-gtk3 librsvg2-devel" # Tauri dependencies + FEDORA_FFMPEG_DEPS="ffmpeg ffmpeg-devel" # FFMPEG dependencies + FEDORA_BINDGEN_DEPS="clang" # Bindgen dependencies - it's used by a dependency of Spacedrive + + sudo dnf check-update + sudo dnf install $FEDORA_TAURI_DEPS $FEDORA_FFMPEG_DEPS $FEDORA_BINDGEN_DEPS + sudo dnf group install "C Development Tools and Libraries" + else + echo "Your Linux distro '$(lsb_release -s -d)' is not supported by this script. We would welcome a PR or some help adding your OS to this script. https://github.com/spacedriveapp/spacedrive/issues" + exit 1 + fi + + echo "Your machine has been setup for Spacedrive development!" +elif [[ "$OSTYPE" == "darwin"* ]]; then + brew install ffmpeg +else + echo "Your OS '$OSTYPE' is not supported by this script. We would welcome a PR or some help adding your OS to this script. https://github.com/spacedriveapp/spacedrive/issues" + exit 1 + +fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d53df5bf..e075b6109 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,51 +1,86 @@ name: CI + on: pull_request: push: branches: - main - - ci + - new-ci + paths-ignore: + - '**/.md' workflow_dispatch: +env: + CARGO_INCREMENTAL: 1 + jobs: - build: - strategy: - fail-fast: false - matrix: - platform: [macos-latest, ubuntu-latest, windows-latest] - env: - FFMPEG_DOWNLOAD_URL: https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full-shared.7z - RUST_CACHE_VERSION: 0 - runs-on: ${{ matrix.platform }} + typescript: + name: TypeScript + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - # from https://github.com/zmwangx/rust-ffmpeg/blob/master/.github/workflows/build.yml - - name: Install ffmpeg (Windows) - if: matrix.platform == 'windows-latest' - run: | - $VCINSTALLDIR = $(& "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath) - Add-Content $env:GITHUB_ENV "LIBCLANG_PATH=${VCINSTALLDIR}\VC\Tools\LLVM\x64\bin`n" - Invoke-WebRequest "${env:FFMPEG_DOWNLOAD_URL}" -OutFile ffmpeg-release-full-shared.7z - 7z x ffmpeg-release-full-shared.7z - mkdir ffmpeg - mv ffmpeg-*/* ffmpeg/ - Add-Content $env:GITHUB_ENV "FFMPEG_DIR=${pwd}\ffmpeg`n" - Add-Content $env:GITHUB_PATH "${pwd}\ffmpeg\bin`n" - - - name: Install CMake (Windows) - uses: lukka/get-cmake@latest - if: matrix.platform == 'windows-latest' - - # Optimisation for windows - - name: Rename existing rust toolchain (Windows) - if: matrix.platform == 'windows-latest' - run: Rename-Item C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc.old + - name: Checkout repository + uses: actions/checkout@v2 - name: Setup Node uses: actions/setup-node@v1 with: - node-version: 16 + node-version: 17 + + - name: Install pnpm + uses: pnpm/action-setup@v2.2.1 + with: + version: 7.x.x + + - name: Install dependencies + run: pnpm i --frozen-lockfile + + - name: Perform typechecks + run: pnpm -r exec tsc + + build-js: + name: Build JS + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Setup Node + uses: actions/setup-node@v1 + with: + node-version: 17 + + - name: Install pnpm + uses: pnpm/action-setup@v2.2.1 + with: + version: 7.x.x + + - name: Install dependencies + run: pnpm i --frozen-lockfile + + - name: Build Desktop + run: pnpm desktop build + + - name: Build Web + run: pnpm web build + + build-core: + name: Build Core (${{ matrix.platform }}) + runs-on: ${{ matrix.platform }} + strategy: + fail-fast: true + matrix: + platform: [ubuntu-latest, macos-latest, windows-latest] + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Install dependencies + uses: ./.github/actions/install-deps + + - name: Setup Node + uses: actions/setup-node@v1 + with: + node-version: 17 - name: Install Rust stable uses: actions-rs/toolchain@v1 @@ -56,74 +91,110 @@ jobs: components: rustfmt, rust-src - name: Cache Rust Dependencies - uses: Swatinem/rust-cache@cb2cf0cc7c5198d3364b9630e2c3d457f160790c + uses: Swatinem/rust-cache@v1 with: - sharedKey: ${{ env.RUST_CACHE_VERSION }} + sharedKey: core-v1-${{ hashFiles('**/Cargo.lock') }} - - name: Cache pnpm dependencies - uses: actions/cache@v2 + - name: Generate Prisma client + working-directory: core + run: cargo run -p prisma-cli --release -- generate + + - name: Build Core + run: cargo build -p sdcore --release + + package-desktop: + name: Package desktop (${{ matrix.platform }}) + runs-on: ${{ matrix.platform }} + needs: [typescript, build-js, build-core] + strategy: + matrix: + platform: [ubuntu-latest, macos-latest, windows-latest] + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Install dependencies + uses: ./.github/actions/install-deps + + - name: Setup Node + uses: actions/setup-node@v1 with: - path: ~/.pnpm-store - key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}- + node-version: 17 + + - name: Install Rust stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + components: rustfmt, rust-src + + - name: Cache Rust Dependencies + uses: Swatinem/rust-cache@v1 + with: + sharedKey: core-v1-${{ hashFiles('**/Cargo.lock') }} - name: Install pnpm uses: pnpm/action-setup@v2.2.1 with: - version: 6.32.6 - - - name: Install dependencies (Ubuntu) - if: matrix.platform == 'ubuntu-latest' - run: | - sudo apt-get update - sudo apt-get install -y --no-install-recommends \ - libgtk-3-dev \ - webkit2gtk-4.0 \ - libappindicator3-dev \ - librsvg2-dev \ - patchelf \ - libssl-dev \ - libavcodec-dev \ - libavdevice-dev \ - libavfilter-dev \ - libavformat-dev \ - libavresample-dev \ - libavutil-dev \ - libswscale-dev \ - libswresample-dev \ - pkg-config \ - ffmpeg - - - name: Install dependencies (macOS) - if: matrix.platform == 'macos-latest' - run: | - brew install ffmpeg + version: 7.x.x - name: Install pnpm dependencies - run: pnpm i - - - name: Build codegen - run: pnpm prep:ci + run: pnpm i --frozen-lockfile - name: Build frontend - run: pnpm desktop build:vite + run: pnpm desktop build - - name: Build Tauri app - uses: tauri-apps/tauri-action@v0 + - name: Generate Prisma client + working-directory: core + run: cargo run -p prisma-cli --release -- generate + + - name: Bundle + run: pnpm desktop tauri build + + - name: Publish artifacts + uses: ./.github/actions/publish-desktop-artifacts + + build-server: + name: Build server + runs-on: ubuntu-latest + needs: build-core + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Install dependencies + uses: ./.github/actions/install-deps + + - name: Install Rust stable + uses: actions-rs/toolchain@v1 with: - projectPath: apps/desktop - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + toolchain: stable + profile: minimal + override: true + components: rustfmt, rust-src - - name: Build and publish server - if: matrix.platform == 'ubuntu-latest' - uses: ./.github/actions/build-and-publish-server + - name: Cache Rust Dependencies + uses: Swatinem/rust-cache@v1 + with: + sharedKey: core-v1-${{ hashFiles('**/Cargo.lock') }} + + - name: Generate Prisma client + working-directory: core + run: cargo run -p prisma-cli --release -- generate + + - name: Build server image + uses: ./.github/actions/build-server-image with: gh_token: ${{ secrets.GITHUB_TOKEN }} + deploy-server: + name: Deploy Server + runs-on: ubuntu-latest + needs: build-server + if: github.event_name != 'pull_request' + steps: - name: Deploy Spacedrive Server to Kubernetes - if: matrix.platform == 'ubuntu-latest' env: K8S_KUBECONFIG: ${{ secrets.K8S_KUBECONFIG }} run: | diff --git a/.github/workflows/org-readme.yml b/.github/workflows/org-readme.yml index 4ff9ff360..829909799 100644 --- a/.github/workflows/org-readme.yml +++ b/.github/workflows/org-readme.yml @@ -23,6 +23,6 @@ jobs: source_file: 'README.md' destination_repo: 'spacedriveapp/.github' destination_folder: 'profile' - user_email: 'actions@spacedrive.app' + user_email: 'actions@spacedrive.com' user_name: 'GH Actions' commit_message: 'Update README' diff --git a/.vscode/settings.json b/.vscode/settings.json index 6bc61929f..5b4dd87f2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,7 @@ "cSpell.words": [ "actix", "bpfrpt", + "consts", "creationdate", "ipfs", "Keepsafe", @@ -17,6 +18,9 @@ "tsparticles", "upsert" ], + "[rust]": { + "editor.defaultFormatter": "matklad.rust-analyzer" + }, "rust-analyzer.procMacro.enable": true, "rust-analyzer.diagnostics.enableExperimental": false, "rust-analyzer.inlayHints.parameterHints": false, diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..5df1a2aa4 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,133 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[hello@jamiepine.com](mailto:hello@jamiepine.com). +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available +at [https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..c1fd32d10 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,80 @@ +# Welcome to the Spacedrive contributing guide + +Thank you for investing your time in contributing to our project! + +Read our [Code of Conduct](./CODE_OF_CONDUCT.md) to keep our community approachable and respectable. + +In this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR. + +## New contributor guide + +To get an overview of the project, read the [README](README.md). Here are some resources to help you get started with open source contributions: + +- [Finding ways to contribute to open source on GitHub](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github) +- [Set up Git](https://docs.github.com/en/get-started/quickstart/set-up-git) +- [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow) +- [Collaborating with pull requests](https://docs.github.com/en/github/collaborating-with-pull-requests) +- [Your First Tauri App](https://tauri.studio/guides/getting-started/beginning-tutorial) +- [pnpm CLI](https://pnpm.io/pnpm-cli) + +## Getting started + +### Issues + +#### Create a new issue + +If you find an issue with the repository or have a feature request with Spacedrive, [search if an issue already exists](https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github/searching-issues-and-pull-requests#search-by-the-title-body-or-comments). If a related issue doesn't exist, you can open a new issue using a relevant [issue form](https://github.com/spacedriveapp/spacedrive/issues/new/choose). + +#### Solve an issue + +Scan through our [existing issues](https://github.com/spacedriveapp/spacedrive/issues) to find one that interests you. You can narrow down the search using `labels` as filters. See [Labels](https://github.com/spacedriveapp/spacedrive/labels) for more information. As a general rule. If you find an issue to work on, you are welcome to open a PR with a fix. + +### Make Changes + +#### Make changes locally + +This project uses [Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) and [pnpm](https://pnpm.io/installation). Ensure you have them installed before continuing. + +> Note: MacOS M1 users should choose the customize option in the rustup init script and enter `x86_64-apple-darwin` as the default host triple instead of the default `aarch64-apple-darwin` + +- `$ git clone https://github.com/spacedriveapp/spacedrive` +- For Linux or MacOS users run: `chmod +x ./.github/scripts/setup-system.sh && ./.github/scripts/setup-system.sh` + - This will install FFMPEG and any other required dependencies for Spacedrive to build. +- `$ cd spacedrive` +- `$ pnpm i` +- `$ pnpm prep` - Runs all necessary codegen & builds required dependencies. + +To quickly run only the desktop app after `prep` you can use: + +- `$ pnpm desktop dev` + +To run the landing page + +- `$ pnpm web dev` - runs the web app for the embed +- `$ pnpm landing dev` + +If you are having issues ensure you are using the following versions of Rust and Node: + +- Rust version: **1.60.0** +- Node version: **17** + +### Pull Request + +When you're finished with the changes, create a pull request, also known as a PR. +- Fill the "Ready for review" template so that we can review your PR. This template helps reviewers understand your changes as well as the purpose of your pull request. +- Don't forget to [link PR to issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) if you are solving one. +- Enable the checkbox to [allow maintainer edits](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork) so the branch can be updated for a merge. +Once you submit your PR, a team member will review your proposal. We may ask questions or request for additional information. +- We may ask for changes to be made before a PR can be merged, either using [suggested changes](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request) or pull request comments. You can apply suggested changes directly through the UI. You can make any other changes in your fork, then commit them to your branch. +- As you update your PR and apply changes, mark each conversation as [resolved](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request#resolving-conversations). +- If you run into any merge issues, checkout this [git tutorial](https://lab.github.com/githubtraining/managing-merge-conflicts) to help you resolve merge conflicts and other issues. + +### Your PR is merged! + +Congratulations :tada::tada: The Spacedrive team thanks you :sparkles:. + +Once your PR is merged, your contributions will be included in the next release of the application. + +### Credits + +This CONTRIBUTING.md file was modelled after the [github/docs CONTRIBUTING.md](https://github.com/github/docs/blob/main/CONTRIBUTING.md) file, and we thank the original author. diff --git a/Cargo.lock b/Cargo.lock index 46ebfb61d..626db3070 100644 Binary files a/Cargo.lock and b/Cargo.lock differ diff --git a/README.md b/README.md index 9eee795df..1b289f443 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@

Spacedrive

- The universal file manager. + A file explorer from the future.
- spacedrive.app » + spacedrive.com »

Download for @@ -29,17 +29,18 @@

Spacedrive is an open source cross-platform file manager, powered by a virtual distributed filesystem (VDFS) written in Rust. -

-Organize files across many devices in one place. From cloud services to offline hard drives, Spacedrive combines the storage capacity and processing power of your devices into one personal distributed cloud, that is both secure and intuitive to use. -
-
-For independent creatives, hoarders and those that want to own their digital footprint. Spacedrive provides a file management experience like no other, and its completely free. -
-
+ +> NOTE: Spacedrive is under active development, most of the listed features are still experimental and subject to change. + +Organize files across many devices in one place. From cloud services to offline hard drives, Spacedrive combines the storage capacity and processing power of your devices into one personal distributed cloud, that is both secure and intuitive to use. + +For independent creatives, hoarders and those that want to own their digital footprint. Spacedrive provides a file management experience like no other, and it's completely free. + +

- Logo + Logo

@@ -57,7 +58,7 @@ For independent creatives, hoarders and those that want to own their digital foo

-> NOTE: Spacedrive is under active development, most of the listed features are still experimental and subject to change. +> NOTE: Spacedrive is under active development, most of the listed features are still experimental and subject to change. Additionally, most of the links on this page are broken but will be working once the repository is made public. # What is a VDFS? @@ -70,31 +71,42 @@ The first implementation of a VDFS can be found in this UC Berkeley [paper](http Many of us have multiple cloud accounts, drives that aren’t backed up and data at risk of loss. We depend on cloud services like Google Photos and iCloud, but are locked in with limited capacity and almost zero interoperability between services and operating systems. Photo albums shouldn’t be stuck in a device ecosystem, or harvested for advertising data. They should be OS agnostic, permanent and personally owned. Data we create is our legacy, that will long outlive us—open source technology is the only way to ensure we retain absolute control over the data that defines our lives, at unlimited scale. # Features -Feature list moved to the [roadmap](docs/product/roadmap.md). -# Developer Installation Instructions +_Note: Links are for highlight purposes only until feature specific documentation is complete._ -This environment uses [Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) and [pnpm](https://pnpm.io/installation). Ensure you have them installed before continuing. +**Complete:** _(in testing)_ -- `$ git clone https://github.com/spacedriveapp/spacedrive` -- IMPORTANT: _Install [FFMPEG](https://www.ffmpeg.org/download.html) if you don't have it already_ -- `$ cd spacedrive` -- `$ pnpm i` -- `$ pnpm prep` - Runs all necessary codegen & builds required dependencies. +- **[File discovery](#features)** - Scan devices, drives and cloud accounts to build a directory of all files with metadata. +- **[Preview generation](#features)** - Auto generate lower resolution stand-ins for image and video. +- **[Statistics](#features)** - Total capacity, index size, preview media size, free space etc. -To quickly run only the desktop app after `prep` you can use: +**In progress:** -- `$ pnpm desktop dev` +- **[File Explorer](#features)** - Browse online/offline storage locations, view files with metadata, perform basic CRUD. +- **[Realtime synchronization](#features)** - Data index synchronized in realtime between devices, prioritizing peer-to-peer LAN connections (WiFi sync). -To run the landing page +**To be developed (MVP):** -- `$ pnpm web dev` - runs the web app for the embed -- `$ pnpm landing dev` +- **[Photos](#features)** - Photo and video albums similar to Apple/Google photos. +- **[Search](#features)** - Deep search into your filesystem with a keybind, including offline locations. +- **[Tags](#features)** - Define routines on custom tags to automate workflows, easily tag files individually, in bulk and automatically via rules. +- **[Extensions](#features)** - Build tools on top of Spacedrive, extend functionality and integrate third party services. Extension directory on [spacedrive.com/extensions](#features). -If you are having issues ensure you are using the following versions of Rust and Node: +**To be developed (Post-MVP):** -- Rust version: **1.58.1** -- Node version: **17** +- **[Cloud integration](#features)** - Index & backup to Apple Photos, Google Drive, Dropbox, OneDrive & Mega + easy API for the community to add more. +- **[Encrypted vault(s)](#features)** - Effortlessly manage & encrypt sensitive files, built on top of VeraCrypt. Encrypt individual files or create flexible-size vaults. +- **[Key manager](#features)** - View, mount, dismount and hide keys. Mounted keys automatically unlock respective areas of your filesystem. +- **[Redundancy Goal](#features)** - Ensure a specific amount of copies exist for your important data, discover at-risk files and monitor device/drive health. +- **[Timeline](#features)** - View a linear timeline of content, travel to any time and see media represented visually. +- **[Media encoder](#features)** - Encode video and audio into various formats, use Tags to automate. Built with FFMPEG. +- **[Workers](#features)** - Utilize the compute power of your devices in unison to encode and perform tasks at increased speeds. +- **[Spacedrive Cloud](#features)** - We'll host an always-on cloud device for you, with pay-as-you-go plans for storage. +- **[Self hosted](#features)** - Spacedrive can be deployed as a service, behaving as just another device powering your personal cloud. + +# Developer Guide + +Please refer to the [contributing guide](CONTRIBUTING.md) for how to install Spacedrive from sources. # Architecture @@ -103,16 +115,15 @@ This project is using what I'm calling the **"PRRTT"** stack (Prisma, Rust, Reac - Prisma on the front-end? 🤯 Made possible thanks to [prisma-client-rust](https://github.com/brendonovich/prisma-client-rust), developed by [Brendonovich](https://github.com/brendonovich). Gives us access to the powerful migration CLI in development, along with the Prisma syntax for our schema. The application bundles with the Prisma query engine and codegen for a beautiful Rust API. Our lightweight migration runner is custom built for a desktop app context. - Tauri allows us to create a pure Rust native OS webview, without the overhead of your average Electron app. This brings the bundle size and average memory usage down dramatically. It also contributes to a more native feel, especially on macOS due to Safari's close integration with the OS. - The core (`sdcore`) is written in pure Rust. -- Typesafe communication for an RPC-like message passing system between Rust and React Query. ## Monorepo structure: ### Apps: -- `desktop`: A [Tauri](https://tauri.studio) app with embedded `sdcore` Rust binary. -- `mobile`: A [React Native](https://reactnative.dev/) app with embedded `sdcore` Rust binary. -- `web`: A [React](https://reactjs.org) webapp as a light wrapper around the `interface` with a websocket Transport. -- `landing`: A [React](https://reactjs.org) app using Vite pages, Tailwind Typography +- `desktop`: A [Tauri](https://tauri.studio) app. +- `mobile`: A [React Native](https://reactnative.dev/) app. +- `web`: A [React](https://reactjs.org) webapp. +- `landing`: A [React](https://reactjs.org) app using Vite SSR & Vite pages. ### Core: diff --git a/apps/desktop/dist/.placeholder b/apps/desktop/dist/.placeholder new file mode 100644 index 000000000..e69de29bb diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 28d9344f9..0aaedc26f 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -8,11 +8,7 @@ "vite": "vite", "dev": "concurrently \"pnpm tauri dev\" \"vite\"", "tauri": "tauri", - "build:vite": "vite build", - "build": "vite build && tauri build" - }, - "resolutions": { - "react-virtualized": "patch:react-virtualized@9.22.3#./path/to/react-virtualized-9.22.3.patch" + "build": "vite build" }, "dependencies": { "@sd/client": "workspace:*", @@ -25,12 +21,12 @@ }, "devDependencies": { "@tauri-apps/cli": "^1.0.0-rc.8", + "@tauri-apps/tauricon": "github:tauri-apps/tauricon", "@types/babel-core": "^6.25.7", "@types/byte-size": "^8.1.0", - "@types/react": "^18.0.0", + "@types/react": "^18.0.8", "@types/react-dom": "^18.0.0", "@types/react-router-dom": "^5.3.3", - "@types/react-virtualized-auto-sizer": "^1.0.1", "@types/react-window": "^1.8.5", "@types/tailwindcss": "^3.0.10", "@vitejs/plugin-react": "^1.3.1", @@ -40,8 +36,6 @@ "typescript": "^4.6.3", "vite": "^2.9.5", "vite-plugin-filter-replace": "^0.1.9", - "vite-plugin-react-svg": "^0.2.0", - "vite-plugin-svgr": "^1.1.0", - "vite-tsconfig-paths": "^3.4.1" + "vite-plugin-svgr": "^1.1.0" } } diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml index 5e96a5f9d..fac1c3a93 100644 --- a/apps/desktop/src-tauri/Cargo.toml +++ b/apps/desktop/src-tauri/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "The next gen private virtual filesystem." authors = ["Jamie Pine"] license = "" -repository = "https://github.com/jamiepine/spacedrive" +repository = "https://github.com/spacedriveapp/spacedrive" default-run = "spacedrive" edition = "2021" build = "src/build.rs" @@ -23,6 +23,10 @@ sdcore = { path = "../../../core" } tokio = { version = "1.17.0", features = ["sync"] } window-shadows = "0.1.2" +# macOS system libs +[target.'cfg(target_os = "macos")'.dependencies] +cocoa = "0.24.0" + [features] default = [ "custom-protocol" ] custom-protocol = [ "tauri/custom-protocol" ] diff --git a/apps/desktop/src-tauri/src/main.rs b/apps/desktop/src-tauri/src/main.rs index 27ed97faf..fdc82d8cd 100644 --- a/apps/desktop/src-tauri/src/main.rs +++ b/apps/desktop/src-tauri/src/main.rs @@ -1,9 +1,13 @@ use std::time::{Duration, Instant}; -use sdcore::{ClientCommand, ClientQuery, Core, CoreController, CoreEvent, CoreResponse}; +use sdcore::{ClientCommand, ClientQuery, CoreController, CoreEvent, CoreResponse, Node}; use tauri::api::path; use tauri::Manager; + mod menu; +mod window; + +use window::WindowExt; #[tauri::command(async)] async fn client_query_transport( @@ -33,18 +37,32 @@ async fn client_command_transport( } } +#[tauri::command(async)] +async fn app_ready(app_handle: tauri::AppHandle) { + let window = app_handle.get_window("main").unwrap(); + + window.show().unwrap(); + + #[cfg(target_os = "macos")] + { + std::thread::sleep(std::time::Duration::from_millis(1000)); + println!("fixing shadow for, {:?}", window.ns_window().unwrap()); + window.fix_shadow(); + } +} + #[tokio::main] async fn main() { let data_dir = path::data_dir().unwrap_or(std::path::PathBuf::from("./")); // create an instance of the core - let (mut core, mut event_receiver) = Core::new(data_dir).await; + let (mut node, mut event_receiver) = Node::new(data_dir).await; // run startup tasks - core.initializer().await; - // extract the core controller - let controller = core.get_controller(); - // throw the core into a dedicated thread + node.initializer().await; + // extract the node controller + let controller = node.get_controller(); + // throw the node into a dedicated thread tokio::spawn(async move { - core.start().await; + node.start().await; }); // create tauri app tauri::Builder::default() @@ -53,14 +71,15 @@ async fn main() { .setup(|app| { let app = app.handle(); - #[cfg(not(target_os = "linux"))] - { - app.windows().iter().for_each(|(_, window)| { - window_shadows::set_shadow(&window, true).unwrap_or(()); + app.windows().iter().for_each(|(_, window)| { + window.hide().unwrap(); - window.start_dragging().unwrap_or(()); - }); - } + #[cfg(target_os = "windows")] + window.set_decorations(true).unwrap(); + + #[cfg(target_os = "macos")] + window.set_transparent_titlebar(true, true); + }); // core event transport tokio::spawn(async move { @@ -85,9 +104,11 @@ async fn main() { Ok(()) }) .on_menu_event(|event| menu::handle_menu_event(event)) + .on_window_event(|event| window::handle_window_event(event)) .invoke_handler(tauri::generate_handler![ client_query_transport, client_command_transport, + app_ready, ]) .menu(menu::get_menu()) .run(tauri::generate_context!()) diff --git a/apps/desktop/src-tauri/src/menu.rs b/apps/desktop/src-tauri/src/menu.rs index 089517e90..eb1c6ed9a 100644 --- a/apps/desktop/src-tauri/src/menu.rs +++ b/apps/desktop/src-tauri/src/menu.rs @@ -56,12 +56,17 @@ fn custom_menu_bar() -> Menu { )) .add_item(CustomMenuItem::new("jeffd".to_string(), "Layout")), ); + let window = Submenu::new( + "Window", + Menu::new().add_native_item(MenuItem::EnterFullScreen), + ); let menu = Menu::new() .add_submenu(spacedrive) .add_submenu(file) .add_submenu(edit) - .add_submenu(view); + .add_submenu(view) + .add_submenu(window); menu } diff --git a/apps/desktop/src-tauri/src/window.rs b/apps/desktop/src-tauri/src/window.rs new file mode 100644 index 000000000..8c31a7713 --- /dev/null +++ b/apps/desktop/src-tauri/src/window.rs @@ -0,0 +1,94 @@ +use tauri::{GlobalWindowEvent, Runtime, Window, Wry}; + +pub(crate) fn handle_window_event(event: GlobalWindowEvent) { + match event.event() { + _ => {} + } +} + +pub trait WindowExt { + #[cfg(target_os = "macos")] + fn set_toolbar(&self, shown: bool); + #[cfg(target_os = "macos")] + fn set_transparent_titlebar(&self, transparent: bool, large: bool); + #[cfg(target_os = "macos")] + fn fix_shadow(&self); +} + +impl WindowExt for Window { + #[cfg(target_os = "macos")] + fn set_toolbar(&self, shown: bool) { + use cocoa::{ + appkit::{NSToolbar, NSWindow}, + base::nil, + foundation::NSString, + }; + + unsafe { + let id = self.ns_window().unwrap() as cocoa::base::id; + + if shown { + let toolbar = + NSToolbar::alloc(nil).initWithIdentifier_(NSString::alloc(nil).init_str("wat")); + toolbar.setShowsBaselineSeparator_(false); + id.setToolbar_(toolbar); + } else { + id.setToolbar_(nil); + } + } + } + + #[cfg(target_os = "macos")] + fn set_transparent_titlebar(&self, transparent: bool, large: bool) { + use cocoa::appkit::{NSWindow, NSWindowStyleMask, NSWindowTitleVisibility}; + + unsafe { + let id = self.ns_window().unwrap() as cocoa::base::id; + + let mut style_mask = id.styleMask(); + // println!("existing style mask, {:#?}", style_mask); + style_mask.set( + NSWindowStyleMask::NSFullSizeContentViewWindowMask, + transparent, + ); + style_mask.set( + NSWindowStyleMask::NSTexturedBackgroundWindowMask, + transparent, + ); + style_mask.set( + NSWindowStyleMask::NSUnifiedTitleAndToolbarWindowMask, + transparent && large, + ); + id.setStyleMask_(style_mask); + + if large { + self.set_toolbar(true); + } + + id.setTitleVisibility_(if transparent { + NSWindowTitleVisibility::NSWindowTitleHidden + } else { + NSWindowTitleVisibility::NSWindowTitleVisible + }); + + id.setTitlebarAppearsTransparent_(if transparent { + cocoa::base::YES + } else { + cocoa::base::NO + }); + } + } + + #[cfg(target_os = "macos")] + fn fix_shadow(&self) { + use cocoa::appkit::NSWindow; + + unsafe { + let id = self.ns_window().unwrap() as cocoa::base::id; + + println!("recomputing shadow for window {:?}", id.title()); + + id.invalidateShadow(); + } + } +} diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index addb13087..34a5757fc 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -64,12 +64,14 @@ "title": "Spacedrive", "width": 1200, "height": 725, + "minWidth": 700, + "minHeight": 500, "resizable": true, "fullscreen": false, "alwaysOnTop": false, - "focus": true, + "focus": false, "fileDropEnabled": false, - "decorations": false, + "decorations": true, "transparent": true, "center": true } diff --git a/apps/desktop/src/index.tsx b/apps/desktop/src/index.tsx index 8ea97d03f..34835a780 100644 --- a/apps/desktop/src/index.tsx +++ b/apps/desktop/src/index.tsx @@ -1,17 +1,15 @@ import React, { useEffect, useState } from 'react'; import { createRoot } from 'react-dom/client'; - // import Spacedrive interface import SpacedriveInterface, { Platform } from '@sd/interface'; -import { emit, listen, Event } from '@tauri-apps/api/event'; +import { listen, Event } from '@tauri-apps/api/event'; // import types from Spacedrive core (TODO: re-export from client would be cleaner) import { ClientCommand, ClientQuery, CoreEvent } from '@sd/core'; // import Spacedrive JS client import { BaseTransport } from '@sd/client'; // import tauri apis -import { dialog, invoke, os } from '@tauri-apps/api'; +import { dialog, invoke, os, shell } from '@tauri-apps/api'; import { convertFileSrc } from '@tauri-apps/api/tauri'; - import '@sd/ui/style'; import { appWindow } from '@tauri-apps/api/window'; @@ -47,9 +45,21 @@ function App() { } const [platform, setPlatform] = useState('macOS'); + const [focused, setFocused] = useState(true); useEffect(() => { os.platform().then((platform) => setPlatform(getPlatform(platform))); + invoke('app_ready'); + }, []); + + useEffect(() => { + const unlistenFocus = listen('tauri://focus', () => setFocused(true)); + const unlistenBlur = listen('tauri://blur', () => setFocused(false)); + + return () => { + unlistenFocus.then((unlisten) => unlisten()); + unlistenBlur.then((unlisten) => unlisten()); + }; }, []); return ( @@ -65,9 +75,11 @@ function App() { }): Promise { return dialog.open(options); }} + isFocused={focused} onClose={() => appWindow.close()} onFullscreen={() => appWindow.setFullscreen(true)} onMinimize={() => appWindow.minimize()} + onOpen={(path: string) => shell.open(path)} /> ); } diff --git a/apps/desktop/tsconfig.json b/apps/desktop/tsconfig.json index df04b3c8c..c44e3731e 100644 --- a/apps/desktop/tsconfig.json +++ b/apps/desktop/tsconfig.json @@ -1,26 +1,5 @@ { - "compilerOptions": { - "target": "ESNext", - "lib": ["DOM", "DOM.Iterable", "ESNext"], - "allowJs": false, - "skipLibCheck": false, - "esModuleInterop": false, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "ESNext", - "moduleResolution": "Node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react", - "outDir": "dist", - "baseUrl": "./", - "paths": { - "@sd/interface": ["../../packages/interface/src/index.ts"], - "@sd/ui": ["../../packages/ui/src/index.ts"], - "@sd/client": ["../../packages/client/src/index.ts"] - } - }, + "extends": "../../packages/config/interface.tsconfig.json", + "compilerOptions": {}, "include": ["src"] } diff --git a/apps/desktop/vite.config.ts b/apps/desktop/vite.config.ts index 8dfe3cf2e..d669f544a 100644 --- a/apps/desktop/vite.config.ts +++ b/apps/desktop/vite.config.ts @@ -1,8 +1,7 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import { name, version } from './package.json'; -import * as path from 'path'; -import svg from 'vite-plugin-svgr'; +import svg from "vite-plugin-svgr" // https://vitejs.dev/config/ export default defineConfig({ diff --git a/apps/landing/env.d.ts b/apps/landing/env.d.ts deleted file mode 100644 index d00f17c88..000000000 --- a/apps/landing/env.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -/// - -interface ImportMetaEnv { - readonly VITE_SDWEB_BASE_URL: string; -} - -interface ImportMeta { - readonly env: ImportMetaEnv; -} - -declare module '*.md' { - // "unknown" would be more detailed depends on how you structure frontmatter - const attributes: Record; - - // When "Mode.TOC" is requested - const toc: { level: string; content: string }[]; - - // When "Mode.HTML" is requested - const html: string; - - // When "Mode.React" is requested. VFC could take a generic like React.VFC<{ MyComponent: TypeOfMyComponent }> - import React from 'react'; - const ReactComponent: React.VFC; -} diff --git a/apps/landing/package.json b/apps/landing/package.json index 06135ab75..a9b21cdc8 100644 --- a/apps/landing/package.json +++ b/apps/landing/package.json @@ -31,14 +31,13 @@ "react-router-dom": "6.3.0", "react-tsparticles": "^2.0.6", "simple-icons": "^6.19.0", - "tsparticles": "^2.0.6", - "vite-plugin-markdown": "^2.0.2" + "tsparticles": "^2.0.6" }, "devDependencies": { "@babel/preset-react": "^7.16.7", "@types/lodash": "^4.14.182", "@types/prismjs": "^1.26.0", - "@types/react": "^18.0.0", + "@types/react": "^18.0.8", "@types/react-dom": "^18.0.0", "@types/react-helmet": "^6.1.5", "@vitejs/plugin-react": "^1.3.1", @@ -50,6 +49,7 @@ "ts-node": "^10.7.0", "typescript": "^4.6.3", "vite": "^2.9.5", + "vite-plugin-markdown": "^2.0.2", "vite-plugin-md": "^0.13.0", "vite-plugin-pages": "^0.23.0", "vite-plugin-pages-sitemap": "^1.2.2", diff --git a/apps/landing/src/components/AppEmbed.tsx b/apps/landing/src/components/AppEmbed.tsx index a29184d8d..9f31c0c73 100644 --- a/apps/landing/src/components/AppEmbed.tsx +++ b/apps/landing/src/components/AppEmbed.tsx @@ -1,16 +1,32 @@ import clsx from 'clsx'; -import React, { useState } from 'react'; +import React, { useRef, useState } from 'react'; import { useEffect } from 'react'; import { isMobile } from 'react-device-detect'; export default function AppEmbed() { const [showApp, setShowApp] = useState(false); const [iFrameAppReady, setIframeAppReady] = useState(false); + const [forceImg, setForceImg] = useState(false); const [imgFallback, setImageFallback] = useState(false); + const iFrame = useRef(null); + + function handleResize() { + if (window.innerWidth < 1000) { + setForceImg(true); + } else if (forceImg) { + setForceImg(false); + } + } + + useEffect(() => { + window.addEventListener('resize', handleResize); + handleResize(); + return () => window.removeEventListener('resize', handleResize); + }, []); function handleEvent(e: any) { if (e.data === 'spacedrive-hello') { - if (!iFrameAppReady && !isMobile) setIframeAppReady(true); + if (!iFrameAppReady) setIframeAppReady(true); } } @@ -35,24 +51,33 @@ export default function AppEmbed() { }, 1000); }, []); + const renderImage = (imgFallback && !iFrameAppReady) || forceImg; + return (
-
- {showApp && ( -