diff --git a/.github/actions/build-rust/action.yml b/.github/actions/build-rust/action.yml index f187641f85..cf396ea372 100644 --- a/.github/actions/build-rust/action.yml +++ b/.github/actions/build-rust/action.yml @@ -29,6 +29,13 @@ runs: env: CARGO_INCREMENTAL: '1' + - name: Set CC + if: ${{ contains(inputs.target, 'linux') && inputs.package != '@affine/native' }} + shell: bash + run: | + echo "CC=clang" >> "$GITHUB_ENV" + echo "TARGET_CC=clang" >> "$GITHUB_ENV" + - name: Cache cargo uses: actions/cache@v3 with: @@ -36,51 +43,13 @@ runs: ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ - .cargo-cache + ~/.napi-rs target/${{ inputs.target }} key: stable-${{ inputs.target }}-cargo-cache - name: Build - if: ${{ inputs.target != 'x86_64-unknown-linux-gnu' && inputs.target != 'aarch64-unknown-linux-gnu' }} shell: bash run: | - yarn workspace ${{ inputs.package }} nx build ${{ inputs.package }} --target ${{ inputs.target }} + yarn workspace ${{ inputs.package }} nx build ${{ inputs.package }} --target ${{ inputs.target }} --use-napi-cross env: NX_CLOUD_ACCESS_TOKEN: ${{ inputs.nx_token }} - - - name: Build - if: ${{ inputs.target == 'x86_64-unknown-linux-gnu' }} - uses: addnab/docker-run-action@v3 - with: - image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian - options: --user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build -e NX_CLOUD_ACCESS_TOKEN=${{ inputs.nx_token }} - run: | - export CC=x86_64-unknown-linux-gnu-gcc - export CC_x86_64_unknown_linux_gnu=x86_64-unknown-linux-gnu-gcc - rm -rf /usr/local/rustup/downloads/* - rustup target add x86_64-unknown-linux-gnu - export RUSTFLAGS="-C debuginfo=1" - yarn workspace ${{ inputs.package }} nx build ${{ inputs.package }} --target ${{ inputs.target }} - if [ -d "node_modules/.cache" ]; then - chmod -R 777 node_modules/.cache - fi - if [ -d "target" ]; then - chmod -R 777 target; - fi - - - name: Build - if: ${{ inputs.target == 'aarch64-unknown-linux-gnu' }} - uses: addnab/docker-run-action@v3 - with: - image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64 - options: --user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build -e NX_CLOUD_ACCESS_TOKEN=${{ inputs.nx_token }} - run: | - export RUSTFLAGS="-C debuginfo=1" - rm -rf /usr/local/rustup/downloads/* - rustup target add aarch64-unknown-linux-gnu - yarn workspace ${{ inputs.package }} nx build ${{ inputs.package }} --target ${{ inputs.target }} - if [ -d "node_modules/.cache" ]; then - chmod -R 777 node_modules/.cache - fi - if [ -d "target" ]; then - chmod -R 777 target; - fi + DEBUG: 'napi:*' diff --git a/.github/actions/deploy/action.yml b/.github/actions/deploy/action.yml index 703d058721..e4245c0d78 100644 --- a/.github/actions/deploy/action.yml +++ b/.github/actions/deploy/action.yml @@ -26,7 +26,7 @@ runs: echo "GIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV" - uses: azure/setup-helm@v3 - id: auth - uses: google-github-actions/auth@v1 + uses: google-github-actions/auth@v2 with: workload_identity_provider: 'projects/${{ inputs.gcp-project-number }}/locations/global/workloadIdentityPools/github-actions/providers/github-actions-helm-deploy' service_account: '${{ inputs.service-account }}' @@ -34,7 +34,7 @@ runs: project_id: '${{ inputs.gcp-project-id }}' - name: 'Setup gcloud cli' - uses: 'google-github-actions/setup-gcloud@v1' + uses: 'google-github-actions/setup-gcloud@v2' with: install_components: 'gke-gcloud-auth-plugin' diff --git a/.github/actions/deploy/deploy.mjs b/.github/actions/deploy/deploy.mjs index 64baadd937..126d3b111d 100644 --- a/.github/actions/deploy/deploy.mjs +++ b/.github/actions/deploy/deploy.mjs @@ -1,6 +1,7 @@ import { execSync } from 'node:child_process'; const { + APP_VERSION, BUILD_TYPE, DEPLOY_HOST, CANARY_DEPLOY_HOST, @@ -79,6 +80,7 @@ const createHelmCommand = ({ isDryRun }) => { `--set global.ingress.enabled=true`, `--set-json global.ingress.annotations=\"{ \\"kubernetes.io/ingress.class\\": \\"gce\\", \\"kubernetes.io/ingress.allow-http\\": \\"true\\", \\"kubernetes.io/ingress.global-static-ip-name\\": \\"${staticIpName}\\" }\"`, `--set-string global.ingress.host="${host}"`, + `--set-string global.version="${APP_VERSION}"`, ...redisAndPostgres, `--set web.replicaCount=${webReplicaCount}`, `--set-string web.image.tag="${imageTag}"`, @@ -105,7 +107,7 @@ const createHelmCommand = ({ isDryRun }) => { `--set sync.replicaCount=${syncReplicaCount}`, `--set-string sync.image.tag="${imageTag}"`, ...serviceAnnotations, - `--version "0.0.0-${buildType}.${GIT_SHORT_HASH}" --timeout 10m`, + `--timeout 10m`, flag, ].join(' '); return deployCommand; diff --git a/.github/actions/download-core/action.yml b/.github/actions/download-core/action.yml new file mode 100644 index 0000000000..c70fcfbe9b --- /dev/null +++ b/.github/actions/download-core/action.yml @@ -0,0 +1,22 @@ +name: 'Download core artifacts' +description: 'Download core artifacts and extract to dist' +inputs: + path: + description: 'Path to extract' + required: true + +runs: + using: 'composite' + steps: + - name: Download tar.gz + uses: actions/download-artifact@v3 + with: + name: core + path: . + + - name: Extract core artifacts + shell: bash + run: | + mkdir -p ${{ inputs.path }} + tar -xvf dist.tar.gz --directory ${{ inputs.path }} + rm dist.tar.gz diff --git a/.github/actions/setup-node/action.yml b/.github/actions/setup-node/action.yml index c9dbcdba32..79d2602c77 100644 --- a/.github/actions/setup-node/action.yml +++ b/.github/actions/setup-node/action.yml @@ -36,6 +36,9 @@ inputs: description: 'Set enableScripts in .yarnrc.yml' required: false default: 'true' + full-cache: + description: 'Full installation cache' + required: false runs: using: 'composite' @@ -46,7 +49,6 @@ runs: node-version-file: '.nvmrc' registry-url: https://npm.pkg.github.com scope: '@toeverything' - cache: 'yarn' - name: Set nmMode if: ${{ inputs.hard-link-nm == 'false' }} @@ -63,6 +65,48 @@ runs: shell: bash run: yarn config set enableScripts false + - name: Set yarn global cache path + shell: bash + id: yarn-cache + run: node -e "const p = $(yarn config cacheFolder --json).effective; console.log('yarn_global_cache=' + p)" >> $GITHUB_OUTPUT + + - name: Cache non-full yarn cache on Linux + uses: actions/cache@v3 + if: ${{ inputs.full-cache != 'true' && runner.os == 'Linux' }} + with: + path: | + node_modules + ${{ steps.yarn-cache.outputs.yarn_global_cache }} + key: node_modules-cache-${{ github.job }}-${{ runner.os }} + + # The network performance on macOS is very poor + # and the decompression performance on Windows is very terrible + # so we reduce the number of cached files on non-Linux systems by remove node_modules from cache path. + - name: Cache non-full yarn cache on non-Linux + uses: actions/cache@v3 + if: ${{ inputs.full-cache != 'true' && runner.os != 'Linux' }} + with: + path: | + ${{ steps.yarn-cache.outputs.yarn_global_cache }} + key: node_modules-cache-${{ github.job }}-${{ runner.os }} + + - name: Cache full yarn cache on Linux + uses: actions/cache@v3 + if: ${{ inputs.full-cache == 'true' && runner.os == 'Linux' }} + with: + path: | + node_modules + ${{ steps.yarn-cache.outputs.yarn_global_cache }} + key: node_modules-cache-full-${{ runner.os }} + + - name: Cache full yarn cache on non-Linux + uses: actions/cache@v3 + if: ${{ inputs.full-cache == 'true' && runner.os != 'Linux' }} + with: + path: | + ${{ steps.yarn-cache.outputs.yarn_global_cache }} + key: node_modules-cache-full-${{ runner.os }} + - name: yarn install if: ${{ inputs.package-install == 'true' }} continue-on-error: true @@ -102,8 +146,8 @@ runs: id: playwright-cache if: ${{ inputs.playwright-install == 'true' }} with: - path: '~/.cache/ms-playwright' - key: '${{ runner.os }}-${{ runner.arch }}-playwright-${{ steps.playwright-version.outputs.version }}' + path: ${{ github.workspace }}/node_modules/.cache/ms-playwright + key: '${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.version }}' # As a fallback, if the Playwright version has changed, try use the # most recently cached version. There's a good chance that at least one # of the browser binary versions haven't been updated, so Playwright can @@ -113,7 +157,7 @@ runs: # date cache, but still let Playwright decide if it needs to download # new binaries or not. restore-keys: | - ${{ runner.os }}-${{ runner.arch }}-playwright- + ${{ runner.os }}-playwright- # If the Playwright browser binaries weren't able to be restored, we tell # playwright to install everything for us. @@ -121,6 +165,8 @@ runs: shell: bash if: inputs.playwright-install == 'true' run: yarn playwright install --with-deps chromium + env: + PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/node_modules/.cache/ms-playwright - name: Get installed Electron version id: electron-version @@ -134,16 +180,16 @@ runs: if: ${{ inputs.electron-install == 'true' }} with: path: 'node_modules/.cache/electron' - key: '${{ runner.os }}-{{ runner.arch }}-electron-${{ steps.electron-version.outputs.version }}' + key: '${{ runner.os }}-electron-${{ steps.electron-version.outputs.version }}' restore-keys: | - ${{ runner.os }}-{{ runner.arch }}-electron- + ${{ runner.os }}-electron- - name: Install Electron binary shell: bash if: inputs.electron-install == 'true' run: node ./node_modules/electron/install.js env: - ELECTRON_OVERRIDE_DIST_PATH: ./node_modules/.cache/electron + electron_config_cache: ./node_modules/.cache/electron - name: Build Infra shell: bash diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 5df6aa4e2d..0000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: 2 -updates: - - package-ecosystem: 'npm' - directory: '/' - groups: - all-npm-dependencies: - patterns: - - '*' - schedule: - interval: 'weekly' - versioning-strategy: increase - commit-message: - prefix: 'chore' - - package-ecosystem: 'cargo' - directory: '/' - schedule: - interval: 'weekly' - versioning-strategy: auto - commit-message: - prefix: 'chore' - groups: - all-cargo-dependencies: - patterns: - - '*' - - - package-ecosystem: 'github-actions' - directory: '/' - schedule: - interval: 'daily' - commit-message: - prefix: 'ci' diff --git a/.github/helm/affine-cloud/Chart.lock b/.github/helm/affine-cloud/Chart.lock index e43c19b00a..211ccfd106 100644 --- a/.github/helm/affine-cloud/Chart.lock +++ b/.github/helm/affine-cloud/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: postgresql repository: https://charts.bitnami.com/bitnami - version: 12.5.8 -digest: sha256:c91c0dc1370e879538dc9d6e435e731a726ef99d6a3b081372318483792b48a7 -generated: "2023-06-27T18:34:12.683806+08:00" + version: 13.2.23 +digest: sha256:5b64538509bd067bb0f67bf082847a2c5d66dc37d0b9d7948a40405d9c446400 +generated: "2023-12-05T03:04:57.997927753Z" diff --git a/.github/helm/affine-cloud/Chart.yaml b/.github/helm/affine-cloud/Chart.yaml index 7eea4ee1af..18465fef65 100644 --- a/.github/helm/affine-cloud/Chart.yaml +++ b/.github/helm/affine-cloud/Chart.yaml @@ -8,5 +8,5 @@ appVersion: '0.6.1' dependencies: - name: postgresql - version: 12.5.8 + version: 13.2.23 repository: https://charts.bitnami.com/bitnami diff --git a/.github/helm/affine/Chart.yaml b/.github/helm/affine/Chart.yaml index d2e490ed86..281ccd9bd0 100644 --- a/.github/helm/affine/Chart.yaml +++ b/.github/helm/affine/Chart.yaml @@ -3,4 +3,4 @@ name: affine description: AFFiNE cloud chart type: application version: 0.0.0 -appVersion: '0.7.0-canary.18' +appVersion: "0.10.3-canary.2" diff --git a/.github/helm/affine/charts/graphql/Chart.yaml b/.github/helm/affine/charts/graphql/Chart.yaml index acb3e90b7b..8a66ffa1ae 100644 --- a/.github/helm/affine/charts/graphql/Chart.yaml +++ b/.github/helm/affine/charts/graphql/Chart.yaml @@ -3,4 +3,4 @@ name: graphql description: AFFiNE GraphQL server type: application version: 0.0.0 -appVersion: '0.7.0-canary.18' +appVersion: "0.10.3-canary.2" diff --git a/.github/helm/affine/charts/sync/Chart.yaml b/.github/helm/affine/charts/sync/Chart.yaml index 9fde73dfcd..071318a561 100644 --- a/.github/helm/affine/charts/sync/Chart.yaml +++ b/.github/helm/affine/charts/sync/Chart.yaml @@ -3,4 +3,4 @@ name: sync description: A Helm chart for Kubernetes type: application version: 0.0.0 -appVersion: "0.7.0-canary.18" +appVersion: "0.10.3-canary.2" diff --git a/.github/labeler.yml b/.github/labeler.yml index 2f50fc2a97..41ed2112f8 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,62 +1,115 @@ docs: - - 'docs/**/*' - - '**/README.md' - - 'packages/frontend/templates/**/*' + - changed-files: + - any-glob-to-any-file: + - 'docs/**/*' + - '**/README.md' + - 'packages/frontend/templates/**/*' test: - - 'tests/**/*' - - '**/tests/**/*' - - '**/__tests__/**/*' + - changed-files: + - any-glob-to-any-file: + - 'tests/**/*' + - '**/tests/**/*' + - '**/__tests__/**/*' mod:dev: - - 'scripts/**/*' - - 'tools/cli/**/*' - - 'packages/common/debug/**/*' + - changed-files: + - any-glob-to-any-file: + - 'scripts/**/*' + - 'tools/cli/**/*' + - 'packages/common/debug/**/*' mod:plugin: - - 'packages/plugins/**/*' + - changed-files: + - any-glob-to-any-file: + - 'packages/plugins/**/*' plugin:copilot: - - 'packages/plugins/copilot/**/*' + - changed-files: + - any-glob-to-any-file: + - 'packages/plugins/copilot/**/*' mod:infra: - - 'packages/common/infra/**/*' + - changed-files: + - any-glob-to-any-file: + - 'packages/common/infra/**/*' mod:sdk: - - 'packages/common/sdk/**/*' + - changed-files: + - any-glob-to-any-file: + - 'packages/common/sdk/**/*' mod:plugin-cli: - - 'tools/plugin-cli/**/*' + - changed-files: + - any-glob-to-any-file: + - 'tools/plugin-cli/**/*' -mod:workspace: 'packages/frontend/workspace/**/*' +mod:workspace: + - changed-files: + - any-glob-to-any-file: + - 'packages/frontend/workspace/**/*' -mod:i18n: 'packages/frontend/i18n/**/*' +mod:i18n: + - changed-files: + - any-glob-to-any-file: + - 'packages/frontend/i18n/**/*' -mod:env: 'packages/common/env/**/*' +mod:env: + - changed-files: + - any-glob-to-any-file: + - 'packages/common/env/**/*' -mod:hooks: 'packages/frontend/hooks/**/*' +mod:hooks: + - changed-files: + - any-glob-to-any-file: + - 'packages/frontend/hooks/**/*' -mod:component: 'packages/frontend/component/**/*' +mod:component: + - changed-files: + - any-glob-to-any-file: + - 'packages/frontend/component/**/*' -mod:storage: 'packages/backend/storage/**/*' +mod:storage: + - changed-files: + - any-glob-to-any-file: + - 'packages/backend/storage/**/*' -mod:native: 'packages/frontend/native/**/*' +mod:native: + - changed-files: + - any-glob-to-any-file: + - 'packages/frontend/native/**/*' mod:store: - - '**/atoms/**/*' + - changed-files: + - any-glob-to-any-file: + - '**/atoms/**/*' rust: - - '**/*.rs' - - '**/Cargo.toml' - - '**/Cargo.lock' - - '**/rust-toolchain' - - '**/rust-toolchain.toml' - - '**/rustfmt.toml' + - changed-files: + - any-glob-to-any-file: + - '**/*.rs' + - '**/Cargo.toml' + - '**/Cargo.lock' + - '**/rust-toolchain' + - '**/rust-toolchain.toml' + - '**/rustfmt.toml' -package:y-indexeddb: 'packages/common/y-indexeddb/**/*' +package:y-indexeddb: + - changed-files: + - any-glob-to-any-file: + - 'packages/common/y-indexeddb/**/*' -app:core: 'packages/frontend/core/**/*' +app:core: + - changed-files: + - any-glob-to-any-file: + - 'packages/frontend/core/**/*' -app:electron: 'packages/frontend/electron/**/*' +app:electron: + - changed-files: + - any-glob-to-any-file: + - 'packages/frontend/electron/**/*' -app:server: 'packages/backend/server/**/*' +app:server: + - changed-files: + - any-glob-to-any-file: + - 'packages/backend/server/**/*' diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000000..74771bc859 --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,53 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base", + "group:allNonMajor", + ":preserveSemverRanges", + ":disablePeerDependencies" + ], + "labels": ["dependencies"], + "packageRules": [ + { + "matchPackageNames": ["napi", "napi-build", "napi-derive"], + "groupName": "napi-rs" + }, + { + "matchPackagePatterns": ["^eslint", "^@typescript-eslint"], + "groupName": "linter" + }, + { + "matchPackagePatterns": ["^@nestjs"], + "groupName": "nestjs" + }, + { + "matchPackagePatterns": ["^@opentelemetry"], + "groupName": "opentelemetry" + }, + { + "matchPackageNames": [ + "@prisma/client", + "@prisma/instrumentation", + "prisma" + ], + "groupName": "prisma" + }, + { + "matchPackagePatterns": ["^@electron-forge"], + "groupName": "electron-forge" + }, + { + "matchPackagePatterns": ["^@blocksuite"], + "excludePackageNames": ["@blocksuite/icons"], + "followTag": "nightly" + } + ], + "commitMessagePrefix": "chore: ", + "commitMessageAction": "bump up", + "commitMessageTopic": "{{depName}} version", + "ignoreDeps": [], + "lockFileMaintenance": { + "enabled": true, + "extends": ["schedule:weekly"] + } +} diff --git a/.github/workflows/auto-labeler.yml b/.github/workflows/auto-labeler.yml index 4aa28fe2fd..1e3fdd376e 100644 --- a/.github/workflows/auto-labeler.yml +++ b/.github/workflows/auto-labeler.yml @@ -9,4 +9,5 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v4 + - uses: actions/checkout@v4 + - uses: actions/labeler@v5 diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml deleted file mode 100644 index 8017e0edf7..0000000000 --- a/.github/workflows/build-desktop.yml +++ /dev/null @@ -1,190 +0,0 @@ -name: Build(Desktop) & Test - -on: - push: - branches: - - canary - - v[0-9]+.[0-9]+.x-staging - - v[0-9]+.[0-9]+.x - paths-ignore: - - README.md - - .github/** - - '!.github/workflows/build-desktop.yml' - - '!.github/actions/build-rust/action.yml' - - '!.github/actions/setup-node/action.yml' - pull_request: - merge_group: - branches: - - canary - - v[0-9]+.[0-9]+.x-staging - - v[0-9]+.[0-9]+.x - paths-ignore: - - README.md - - .github/** - - '!.github/workflows/build-desktop.yml' - - '!.github/actions/build-rust/action.yml' - - '!.github/actions/setup-node/action.yml' - -env: - DEBUG: napi:* - BUILD_TYPE: canary - APP_NAME: affine - COVERAGE: true - DISTRIBUTION: desktop - MACOSX_DEPLOYMENT_TARGET: '10.13' - NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - -jobs: - build-core: - name: Build @affine/core - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - electron-install: false - - name: Build Core - run: yarn nx build @affine/core - - name: Upload core artifact - uses: actions/upload-artifact@v3 - with: - name: core - path: ./packages/frontend/core/dist - if-no-files-found: error - - build-native: - name: Build Native - runs-on: ubuntu-latest - needs: build-core - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - - name: Build AFFiNE native - uses: ./.github/actions/build-rust - with: - target: x86_64-unknown-linux-gnu - package: '@affine/native' - nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - - name: Run tests - run: yarn test - working-directory: ./packages/frontend/native - - desktop-test: - name: Desktop Test - runs-on: ${{ matrix.spec.os }} - strategy: - fail-fast: false - # all combinations: macos-latest x64, macos-latest arm64, windows-latest x64, ubuntu-latest x64 - matrix: - spec: - - { - os: macos-latest, - platform: macos, - arch: x64, - target: x86_64-apple-darwin, - test: true, - } - - { - os: macos-latest, - platform: macos, - arch: arm64, - target: aarch64-apple-darwin, - test: false, - } - - { - os: ubuntu-latest, - platform: linux, - arch: x64, - target: x86_64-unknown-linux-gnu, - test: true, - } - - { - os: windows-latest, - platform: windows, - arch: x64, - target: x86_64-pc-windows-msvc, - test: true, - } - needs: build-core - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - timeout-minutes: 10 - with: - extra-flags: workspaces focus @affine/electron @affine/monorepo @affine-test/affine-desktop - playwright-install: true - hard-link-nm: false - enableScripts: false - - - name: Build AFFiNE native - uses: ./.github/actions/build-rust - with: - target: ${{ matrix.spec.target }} - package: '@affine/native' - nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - - - name: Run unit tests - if: ${{ matrix.spec.test }} - shell: bash - run: yarn vitest - working-directory: packages/frontend/electron - - - name: Download core artifact - uses: actions/download-artifact@v3 - with: - name: core - path: packages/frontend/electron/resources/web-static - - - name: Build Desktop Layers - run: yarn workspace @affine/electron build - - - name: Run desktop tests - if: ${{ matrix.spec.test && matrix.spec.os == 'ubuntu-latest' }} - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine-test/affine-desktop e2e - env: - COVERAGE: true - - - name: Run desktop tests - if: ${{ matrix.spec.test && matrix.spec.os != 'ubuntu-latest' }} - run: yarn workspace @affine-test/affine-desktop e2e - env: - COVERAGE: true - - - name: Make bundle - if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }} - env: - SKIP_BUNDLE: true - SKIP_WEB_BUILD: true - HOIST_NODE_MODULES: 1 - run: yarn workspace @affine/electron package --platform=darwin --arch=arm64 - - - name: Output check - if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }} - run: | - yarn workspace @affine/electron ts-node ./scripts/macos-arm64-output-check.ts - - - name: Collect code coverage report - if: ${{ matrix.spec.test }} - run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov - - - name: Upload e2e test coverage results - if: ${{ matrix.spec.test }} - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./.coverage/lcov.info - flags: e2etest-${{ matrix.spec.os }}-${{ matrix.spec.arch }} - name: affine - fail_ci_if_error: false - - - name: Upload test results - if: ${{ failure() }} - uses: actions/upload-artifact@v3 - with: - name: test-results-e2e-${{ matrix.spec.os }}-${{ matrix.spec.arch }} - path: ./test-results - if-no-files-found: ignore diff --git a/.github/workflows/build-server.yml b/.github/workflows/build-server.yml deleted file mode 100644 index 9fb88605c4..0000000000 --- a/.github/workflows/build-server.yml +++ /dev/null @@ -1,311 +0,0 @@ -name: Build(Server) & Test - -on: - push: - branches: - - canary - - v[0-9]+.[0-9]+.x-staging - - v[0-9]+.[0-9]+.x - paths-ignore: - - README.md - - .github/** - - '!.github/workflows/build-server.yml' - - '!.github/actions/build-rust/action.yml' - - '!.github/actions/setup-node/action.yml' - pull_request: - merge_group: - branches: - - canary - - v[0-9]+.[0-9]+.x-staging - - v[0-9]+.[0-9]+.x - paths-ignore: - - README.md - - .github/** - - '!.github/workflows/build-server.yml' - - '!.github/actions/build-rust/action.yml' - - '!.github/actions/setup-node/action.yml' - -env: - DEBUG: napi:* - BUILD_TYPE: canary - APP_NAME: affine - COVERAGE: true - DISTRIBUTION: browser - NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - -jobs: - build-storage: - name: Build Storage - runs-on: ubuntu-latest - env: - RUSTFLAGS: '-C debuginfo=1' - - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - extra-flags: workspaces focus @affine/storage - electron-install: false - build-infra: false - build-plugins: false - - name: Build Rust - uses: ./.github/actions/build-rust - with: - target: 'x86_64-unknown-linux-gnu' - package: '@affine/storage' - nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - - name: Upload storage.node - uses: actions/upload-artifact@v3 - with: - name: storage.node - path: ./packages/backend/storage/storage.node - if-no-files-found: error - - server-test: - name: Server Test - runs-on: ubuntu-latest - needs: build-storage - services: - postgres: - image: postgres - env: - POSTGRES_PASSWORD: affine - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5432:5432 - mailer: - image: mailhog/mailhog - ports: - - 1025:1025 - - 8025:8025 - steps: - - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - electron-install: false - - - name: Initialize database - run: | - psql -h localhost -U postgres -c "CREATE DATABASE affine;" - psql -h localhost -U postgres -c "CREATE USER affine WITH PASSWORD 'affine';" - psql -h localhost -U postgres -c "ALTER USER affine WITH SUPERUSER;" - env: - PGPASSWORD: affine - - - name: Generate prisma client - run: | - yarn workspace @affine/server exec prisma generate - yarn workspace @affine/server exec prisma db push - env: - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - - - name: Run init-db script - run: yarn workspace @affine/server exec ts-node ./scripts/init-db.ts - env: - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - - - name: Download storage.node - uses: actions/download-artifact@v3 - with: - name: storage.node - path: ./packages/backend/server - - - name: Run server tests - run: yarn workspace @affine/server test:coverage - env: - CARGO_TARGET_DIR: '${{ github.workspace }}/target' - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - - - name: Upload server test coverage results - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./packages/backend/server/.coverage/lcov.info - flags: server-test - name: affine - fail_ci_if_error: false - - server-e2e-test: - name: Server E2E Test - runs-on: ubuntu-latest - needs: build-storage - services: - postgres: - image: postgres - env: - POSTGRES_PASSWORD: affine - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5432:5432 - mailer: - image: mailhog/mailhog - ports: - - 1025:1025 - - 8025:8025 - steps: - - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - playwright-install: true - - - name: Initialize database - run: | - psql -h localhost -U postgres -c "CREATE DATABASE affine;" - psql -h localhost -U postgres -c "CREATE USER affine WITH PASSWORD 'affine';" - psql -h localhost -U postgres -c "ALTER USER affine WITH SUPERUSER;" - env: - PGPASSWORD: affine - - - name: Generate prisma client - run: | - yarn workspace @affine/server exec prisma generate - yarn workspace @affine/server exec prisma db push - env: - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - - - name: Run init-db script - run: yarn workspace @affine/server exec ts-node ./scripts/init-db.ts - env: - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - - - name: Download storage.node - uses: actions/download-artifact@v3 - with: - name: storage.node - path: ./packages/backend/server - - - name: Run playwright tests - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine-test/affine-cloud e2e --forbid-only - env: - COVERAGE: true - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - - - name: Collect code coverage report - run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov - - - name: Upload e2e test coverage results - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./.coverage/lcov.info - flags: server-e2etest - name: affine - fail_ci_if_error: false - - - name: Upload test results - if: ${{ failure() }} - uses: actions/upload-artifact@v3 - with: - name: test-results-e2e-server - path: ./tests/affine-cloud/test-results - if-no-files-found: ignore - - server-desktop-e2e-test: - name: Server Desktop E2E Test - runs-on: ubuntu-latest - needs: build-storage - services: - postgres: - image: postgres - env: - POSTGRES_PASSWORD: affine - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5432:5432 - mailer: - image: mailhog/mailhog - ports: - - 1025:1025 - - 8025:8025 - steps: - - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - playwright-install: true - hard-link-nm: false - - - name: Build AFFiNE native - uses: ./.github/actions/build-rust - with: - target: x86_64-unknown-linux-gnu - package: '@affine/native' - nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - - - name: Initialize database - run: | - psql -h localhost -U postgres -c "CREATE DATABASE affine;" - psql -h localhost -U postgres -c "CREATE USER affine WITH PASSWORD 'affine';" - psql -h localhost -U postgres -c "ALTER USER affine WITH SUPERUSER;" - env: - PGPASSWORD: affine - - - name: Generate prisma client - run: | - yarn workspace @affine/server exec prisma generate - yarn workspace @affine/server prisma db push - env: - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - - - name: Run init-db script - run: yarn workspace @affine/server exec ts-node ./scripts/init-db.ts - env: - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - - - name: Download storage.node - uses: actions/download-artifact@v3 - with: - name: storage.node - path: ./packages/backend/server - - - name: Build Plugins - run: yarn run build:plugins - - - name: Build Desktop Layers - run: yarn workspace @affine/electron build:dev - - - name: Run playwright tests - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" yarn workspace @affine-test/affine-desktop-cloud e2e - env: - COVERAGE: true - DEV_SERVER_URL: http://localhost:8080 - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - ENABLE_LOCAL_EMAIL: true - - - name: Collect code coverage report - run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov - - - name: Upload e2e test coverage results - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./.coverage/lcov.info - flags: server-e2etest - name: affine - fail_ci_if_error: false - - - name: Upload test results - if: ${{ failure() }} - uses: actions/upload-artifact@v3 - with: - name: test-results-e2e-server - path: ./tests/affine-cloud/test-results - if-no-files-found: ignore diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml new file mode 100644 index 0000000000..cb26bc18c1 --- /dev/null +++ b/.github/workflows/build-test.yml @@ -0,0 +1,599 @@ +name: Build & Test + +on: + push: + branches: + - canary + - v[0-9]+.[0-9]+.x-staging + - v[0-9]+.[0-9]+.x + paths-ignore: + - README.md + pull_request: + +env: + DEBUG: napi:* + BUILD_TYPE: canary + APP_NAME: affine + AFFINE_ENV: dev + COVERAGE: true + MACOSX_DEPLOYMENT_TARGET: '10.13' + NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} + PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/node_modules/.cache/ms-playwright + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: ['javascript', 'typescript'] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Run oxlint + # oxlint is fast, so wrong code will fail quickly + run: yarn dlx $(node -e "console.log(require('./package.json').scripts['lint:ox'])") + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + electron-install: false + full-cache: true + - name: Run i18n codegen + run: yarn i18n-codegen gen + - name: Run ESLint + run: yarn lint:eslint --max-warnings=0 + - name: Run Prettier + # Set nmMode in `actions/setup-node` will modify the .yarnrc.yml + run: | + git checkout .yarnrc.yml + yarn lint:prettier + - name: Run Type Check + run: yarn typecheck + + check-yarn-binary: + name: Check yarn binary + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run check + run: | + yarn set version $(node -e "console.log(require('./package.json').packageManager.split('@')[1])") + git diff --exit-code + + e2e-plugin-test: + name: E2E Plugin Test + runs-on: ubuntu-latest + env: + DISTRIBUTION: browser + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + playwright-install: true + electron-install: false + full-cache: true + - name: Run playwright tests + run: yarn e2e --forbid-only + working-directory: tests/affine-plugin + env: + COVERAGE: true + - name: Collect code coverage report + run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov + + - name: Upload e2e test coverage results + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./.coverage/lcov.info + flags: e2e-plugin-test + name: affine + fail_ci_if_error: false + + - name: Upload test results + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: test-results-e2e-plugin + path: ./test-results + if-no-files-found: ignore + + e2e-test: + name: E2E Test + runs-on: ubuntu-latest + env: + DISTRIBUTION: browser + strategy: + fail-fast: false + matrix: + shard: [1, 2, 3, 4, 5] + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + playwright-install: true + electron-install: false + full-cache: true + + - name: Run playwright tests + run: yarn workspace @affine-test/affine-local e2e --forbid-only --shard=${{ matrix.shard }}/${{ strategy.job-total }} + + - name: Upload test results + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: test-results-e2e-${{ matrix.shard }} + path: ./test-results + if-no-files-found: ignore + + e2e-migration-test: + name: E2E Migration Test + runs-on: ubuntu-latest + env: + DISTRIBUTION: browser + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + playwright-install: true + electron-install: false + full-cache: true + + - name: Run playwright tests + run: yarn workspace @affine-test/affine-migration e2e --forbid-only + + - name: Upload test results + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: test-results-e2e-migration + path: ./tests/affine-migration/test-results + if-no-files-found: ignore + + unit-test: + name: Unit Test + runs-on: ubuntu-latest + needs: + - build-native + env: + DISTRIBUTION: browser + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + electron-install: false + full-cache: true + + - name: Download affine.linux-x64-gnu.node + uses: actions/download-artifact@v3 + with: + name: affine.linux-x64-gnu.node + path: ./packages/frontend/native + + - name: Unit Test + run: yarn nx test:coverage @affine/monorepo + + - name: Upload unit test coverage results + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./.coverage/store/lcov.info + flags: unittest + name: affine + fail_ci_if_error: false + + build-native: + name: Build AFFiNE native (${{ matrix.spec.target }}) + runs-on: ${{ matrix.spec.os }} + env: + CARGO_PROFILE_RELEASE_DEBUG: '1' + strategy: + fail-fast: false + matrix: + spec: + - { os: ubuntu-latest, target: x86_64-unknown-linux-gnu } + - { os: windows-latest, target: x86_64-pc-windows-msvc } + - { os: macos-latest, target: x86_64-apple-darwin } + - { os: macos-latest, target: aarch64-apple-darwin } + + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + extra-flags: workspaces focus @affine/native + electron-install: false + build-infra: false + build-plugins: false + - name: Setup filename + id: filename + shell: bash + run: | + export PLATFORM_ARCH_ABI=$(node -e "console.log(require('@napi-rs/cli').parseTriple('${{ matrix.spec.target }}').platformArchABI)") + echo "filename=affine.$PLATFORM_ARCH_ABI.node" >> "$GITHUB_OUTPUT" + - name: Build AFFiNE native + uses: ./.github/actions/build-rust + with: + target: ${{ matrix.spec.target }} + package: '@affine/native' + nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} + - name: Upload ${{ steps.filename.outputs.filename }} + uses: actions/upload-artifact@v3 + with: + name: ${{ steps.filename.outputs.filename }} + path: ./packages/frontend/native/${{ steps.filename.outputs.filename }} + if-no-files-found: error + + build-storage: + name: Build Storage + runs-on: ubuntu-latest + env: + CARGO_PROFILE_RELEASE_DEBUG: '1' + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + extra-flags: workspaces focus @affine/storage + electron-install: false + build-infra: false + build-plugins: false + - name: Build Rust + uses: ./.github/actions/build-rust + with: + target: 'x86_64-unknown-linux-gnu' + package: '@affine/storage' + nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} + - name: Upload storage.node + uses: actions/upload-artifact@v3 + with: + name: storage.node + path: ./packages/backend/storage/storage.node + if-no-files-found: error + + build-core: + name: Build @affine/core + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + electron-install: false + build-plugins: false + full-cache: true + - name: Build Core + # always skip cache because its fast, and cache configuration is always changing + run: yarn nx build @affine/core --skip-nx-cache + - name: zip core + run: tar -czf dist.tar.gz --directory=packages/frontend/core/dist . + - name: Upload core artifact + uses: actions/upload-artifact@v3 + with: + name: core + path: dist.tar.gz + if-no-files-found: error + + server-test: + name: Server Test + runs-on: ubuntu-latest + needs: build-storage + env: + DISTRIBUTION: browser + services: + postgres: + image: postgres + env: + POSTGRES_PASSWORD: affine + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + mailer: + image: mailhog/mailhog + ports: + - 1025:1025 + - 8025:8025 + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + electron-install: false + full-cache: true + + - name: Initialize database + run: | + psql -h localhost -U postgres -c "CREATE DATABASE affine;" + psql -h localhost -U postgres -c "CREATE USER affine WITH PASSWORD 'affine';" + psql -h localhost -U postgres -c "ALTER USER affine WITH SUPERUSER;" + env: + PGPASSWORD: affine + + - name: Generate prisma client + run: | + yarn workspace @affine/server exec prisma generate + yarn workspace @affine/server exec prisma db push + env: + DATABASE_URL: postgresql://affine:affine@localhost:5432/affine + + - name: Run init-db script + run: yarn workspace @affine/server exec node --loader ts-node/esm/transpile-only ./scripts/init-db.ts + env: + DATABASE_URL: postgresql://affine:affine@localhost:5432/affine + + - name: Download storage.node + uses: actions/download-artifact@v3 + with: + name: storage.node + path: ./packages/backend/server + + - name: Run server tests + run: yarn workspace @affine/server test:coverage + env: + CARGO_TARGET_DIR: '${{ github.workspace }}/target' + DATABASE_URL: postgresql://affine:affine@localhost:5432/affine + + - name: Upload server test coverage results + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./packages/backend/server/.coverage/lcov.info + flags: server-test + name: affine + fail_ci_if_error: false + + server-e2e-test: + name: ${{ matrix.tests.name }} + runs-on: ubuntu-latest + env: + DISTRIBUTION: browser + DATABASE_URL: postgresql://affine:affine@localhost:5432/affine + strategy: + fail-fast: false + matrix: + tests: + - name: 'Server E2E Test 1/3' + script: yarn workspace @affine-test/affine-cloud e2e --forbid-only --shard=1/3 + - name: 'Server E2E Test 2/3' + script: yarn workspace @affine-test/affine-cloud e2e --forbid-only --shard=2/3 + - name: 'Server E2E Test 3/3' + script: yarn workspace @affine-test/affine-cloud e2e --forbid-only --shard=3/3 + - name: 'Server Desktop E2E Test' + script: | + yarn workspace @affine/electron build:dev + xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine-test/affine-desktop-cloud e2e + needs: + - build-storage + - build-native + services: + postgres: + image: postgres + env: + POSTGRES_PASSWORD: affine + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + mailer: + image: mailhog/mailhog + ports: + - 1025:1025 + - 8025:8025 + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + playwright-install: true + hard-link-nm: false + + - name: Initialize database + run: | + psql -h localhost -U postgres -c "CREATE DATABASE affine;" + psql -h localhost -U postgres -c "CREATE USER affine WITH PASSWORD 'affine';" + psql -h localhost -U postgres -c "ALTER USER affine WITH SUPERUSER;" + env: + PGPASSWORD: affine + + - name: Generate prisma client + run: | + yarn workspace @affine/server exec prisma generate + yarn workspace @affine/server exec prisma db push + env: + DATABASE_URL: postgresql://affine:affine@localhost:5432/affine + + - name: Run init-db script + run: yarn workspace @affine/server exec node --loader ts-node/esm/transpile-only ./scripts/init-db.ts + - name: Download storage.node + uses: actions/download-artifact@v3 + with: + name: storage.node + path: ./packages/backend/server + + - name: Download affine.linux-x64-gnu.node + uses: actions/download-artifact@v3 + with: + name: affine.linux-x64-gnu.node + path: ./packages/frontend/native + + - name: ${{ matrix.tests.name }} + run: | + ${{ matrix.tests.script }} + env: + DEV_SERVER_URL: http://localhost:8080 + ENABLE_LOCAL_EMAIL: true + + - name: Upload test results + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: test-results-e2e-server + path: ./tests/affine-cloud/test-results + if-no-files-found: ignore + + desktop-test: + name: Desktop Test (${{ matrix.spec.os }}, ${{ matrix.spec.platform }}, ${{ matrix.spec.arch }}, ${{ matrix.spec.target }}, ${{ matrix.spec.test }}) + runs-on: ${{ matrix.spec.os }} + strategy: + fail-fast: false + # all combinations: macos-latest x64, macos-latest arm64, windows-latest x64, ubuntu-latest x64 + matrix: + spec: + - { + os: macos-latest, + platform: macos, + arch: x64, + target: x86_64-apple-darwin, + test: true, + } + - { + os: macos-latest, + platform: macos, + arch: arm64, + target: aarch64-apple-darwin, + test: false, + } + - { + os: ubuntu-latest, + platform: linux, + arch: x64, + target: x86_64-unknown-linux-gnu, + test: true, + } + - { + os: windows-latest, + platform: windows, + arch: x64, + target: x86_64-pc-windows-msvc, + test: true, + } + needs: + - build-core + - build-native + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: ./.github/actions/setup-node + timeout-minutes: 10 + with: + extra-flags: workspaces focus @affine/electron @affine/monorepo @affine-test/affine-desktop + playwright-install: true + hard-link-nm: false + enableScripts: false + + - name: Setup filename + id: filename + shell: bash + run: | + export PLATFORM_ARCH_ABI=$(node -e "console.log(require('@napi-rs/cli').parseTriple('${{ matrix.spec.target }}').platformArchABI)") + echo "filename=affine.$PLATFORM_ARCH_ABI.node" >> "$GITHUB_OUTPUT" + + - name: Download ${{ steps.filename.outputs.filename }} + uses: actions/download-artifact@v3 + with: + name: ${{ steps.filename.outputs.filename }} + path: ./packages/frontend/native + + - name: Run unit tests + if: ${{ matrix.spec.test }} + shell: bash + run: yarn vitest + working-directory: packages/frontend/electron + + - name: Download core artifact + uses: ./.github/actions/download-core + with: + path: packages/frontend/electron/resources/web-static + + - name: Build Desktop Layers + run: yarn workspace @affine/electron build + + - name: Run desktop tests + if: ${{ matrix.spec.test && matrix.spec.os == 'ubuntu-latest' }} + run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine-test/affine-desktop e2e + + - name: Run desktop tests + if: ${{ matrix.spec.test && matrix.spec.os != 'ubuntu-latest' }} + run: yarn workspace @affine-test/affine-desktop e2e + + - name: Make bundle + if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }} + env: + SKIP_BUNDLE: true + SKIP_WEB_BUILD: true + HOIST_NODE_MODULES: 1 + run: yarn workspace @affine/electron package --platform=darwin --arch=arm64 + + - name: Output check + if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }} + run: | + yarn workspace @affine/electron exec node --loader ts-node/esm/transpile-only ./scripts/macos-arm64-output-check.ts + + - name: Upload test results + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: test-results-e2e-${{ matrix.spec.os }}-${{ matrix.spec.arch }} + path: ./test-results + if-no-files-found: ignore diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index ee01a2e891..0000000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,199 +0,0 @@ -name: Build & Test - -on: - push: - branches: - - canary - - v[0-9]+.[0-9]+.x-staging - - v[0-9]+.[0-9]+.x - paths-ignore: - - README.md - - .github/** - - '!.github/workflows/build.yml' - - '!.github/actions/build-rust/action.yml' - - '!.github/actions/setup-node/action.yml' - pull_request: - merge_group: - branches: - - canary - - v[0-9]+.[0-9]+.x-staging - - v[0-9]+.[0-9]+.x - paths-ignore: - - README.md - - .github/** - - '!.github/workflows/build.yml' - - '!.github/actions/build-rust/action.yml' - - '!.github/actions/setup-node/action.yml' - -env: - DEBUG: napi:* - BUILD_TYPE: canary - APP_NAME: affine - AFFINE_ENV: dev - COVERAGE: true - DISTRIBUTION: browser - MACOSX_DEPLOYMENT_TARGET: '10.13' - NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - -jobs: - lint: - name: Lint - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Run oxlint - # oxlint is fast, so wrong code will fail quickly - run: yarn dlx $(node -e "console.log(require('./package.json').scripts['lint:ox'])") - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - electron-install: false - - name: Run i18n codegen - run: yarn i18n-codegen gen - - name: Run ESLint - run: yarn lint:eslint --max-warnings=0 - - name: Run Prettier - # Set nmMode in `actions/setup-node` will modify the .yarnrc.yml - run: | - git checkout .yarnrc.yml - yarn lint:prettier - - name: Run Type Check - run: yarn typecheck - - check-yarn-binary: - name: Check yarn binary - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Run check - run: | - yarn set version $(node -e "console.log(require('./package.json').packageManager.split('@')[1])") - git diff --exit-code - - e2e-plugin-test: - name: E2E Plugin Test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - playwright-install: true - electron-install: false - - name: Run playwright tests - run: yarn e2e --forbid-only - working-directory: tests/affine-plugin - env: - COVERAGE: true - - name: Collect code coverage report - run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov - - - name: Upload e2e test coverage results - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./.coverage/lcov.info - flags: e2e-plugin-test - name: affine - fail_ci_if_error: false - - - name: Upload test results - if: ${{ failure() }} - uses: actions/upload-artifact@v3 - with: - name: test-results-e2e-plugin - path: ./test-results - if-no-files-found: ignore - - e2e-test: - name: E2E Test - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - shard: [1, 2, 3, 4, 5] - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - playwright-install: true - electron-install: false - - - name: Run playwright tests - run: yarn e2e --forbid-only --shard=${{ matrix.shard }}/${{ strategy.job-total }} - working-directory: tests/affine-local - env: - COVERAGE: true - - - name: Collect code coverage report - run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov - - - name: Upload e2e test coverage results - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./.coverage/lcov.info - flags: e2etest - name: affine - fail_ci_if_error: false - - - name: Upload test results - if: ${{ failure() }} - uses: actions/upload-artifact@v3 - with: - name: test-results-e2e-${{ matrix.shard }} - path: ./test-results - if-no-files-found: ignore - - e2e-migration-test: - name: E2E Migration Test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - playwright-install: true - electron-install: false - - - name: Run playwright tests - run: yarn workspace @affine-test/affine-migration e2e --forbid-only - - - name: Upload test results - if: ${{ failure() }} - uses: actions/upload-artifact@v3 - with: - name: test-results-e2e-migration - path: ./tests/affine-migration/test-results - if-no-files-found: ignore - - unit-test: - name: Unit Test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - electron-install: false - - - name: Build AFFiNE native - uses: ./.github/actions/build-rust - with: - target: x86_64-unknown-linux-gnu - package: '@affine/native' - nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - - - name: Unit Test - run: yarn nx test:coverage @affine/monorepo - - - name: Upload unit test coverage results - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./.coverage/store/lcov.info - flags: unittest - name: affine - fail_ci_if_error: false diff --git a/.github/workflows/cache-cleanup.yml b/.github/workflows/cache-cleanup.yml deleted file mode 100644 index 87d8415d49..0000000000 --- a/.github/workflows/cache-cleanup.yml +++ /dev/null @@ -1,36 +0,0 @@ -# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#force-deleting-cache-entries -name: Cleanup caches for closed branches - -on: - pull_request: - types: - - closed - workflow_dispatch: - -jobs: - cleanup: - runs-on: ubuntu-latest - steps: - - name: Check out code - uses: actions/checkout@v4 - - - name: Cleanup - run: | - gh extension install actions/gh-actions-cache - - REPO=${{ github.repository }} - BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge" - - echo "Fetching list of cache key" - cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 ) - - ## Setting this to not fail the workflow while deleting cache keys. - set +e - echo "Deleting caches..." - for cacheKey in $cacheKeysForPR - do - gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm - done - echo "Done" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/cancel.yml b/.github/workflows/cancel.yml deleted file mode 100644 index 4924afafe0..0000000000 --- a/.github/workflows/cancel.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Cancel -on: - pull_request_target: - types: - - edited - - synchronize - -jobs: - cancel: - name: 'Cancel Previous Runs' - runs-on: ubuntu-latest - timeout-minutes: 2 - steps: - - uses: styfle/cancel-workflow-action@0.12.0 - with: - # See https://api.github.com/repos/toeverything/AFFiNE/actions/workflows - workflow_id: 44038251, 61883931, 65188160, 66789140 - access_token: ${{ github.token }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 3e219c0dce..0000000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,70 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: 'CodeQL' - -on: - push: - branches: [canary] - pull_request: - merge_group: - # The branches below must be a subset of the branches above - branches: [canary] - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: ['javascript'] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # â„šī¸ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 31ec20581b..52e03ae9c8 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -69,7 +69,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Node.js uses: ./.github/actions/setup-node - - name: Setup Rust + - name: Build Rust uses: ./.github/actions/build-rust with: target: 'x86_64-unknown-linux-gnu' @@ -90,7 +90,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Node.js uses: ./.github/actions/setup-node - - name: Setup Rust + - name: Build Rust uses: ./.github/actions/build-rust with: target: 'aarch64-unknown-linux-gnu' @@ -207,6 +207,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: setup deploy version + id: version + run: | + export APP_VERSION=`node -e "console.log(require('./package.json').version)"` + echo $APP_VERSION + echo "APP_VERSION=$APP_VERSION" >> "$GITHUB_OUTPUT" - name: Deploy to ${{ github.event.inputs.flavor }} uses: ./.github/actions/deploy with: @@ -217,6 +223,7 @@ jobs: cluster-name: ${{ secrets.GCP_CLUSTER_NAME }} cluster-location: ${{ secrets.GCP_CLUSTER_LOCATION }} env: + APP_VERSION: ${{ steps.version.outputs.APP_VERSION }} DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }} CANARY_DEPLOY_HOST: ${{ secrets.CANARY_DEPLOY_HOST }} R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }} diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index a8930db8c0..2a0de72264 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -231,6 +231,11 @@ jobs: node ./packages/frontend/electron/scripts/generate-yml.js env: RELEASE_VERSION: ${{ needs.set-build-version.outputs.version }} + - name: Generate SHA512 checksums + run: | + sha512sum *-linux-* > SHA512SUMS.txt + sha512sum *-macos-* >> SHA512SUMS.txt + sha512sum *-windows-* >> SHA512SUMS.txt - name: Create Release Draft uses: softprops/action-gh-release@v1 env: @@ -241,6 +246,7 @@ jobs: tag_name: ${{ needs.set-build-version.outputs.version }} prerelease: true files: | + ./SHA512SUMS.txt ./VERSION ./*.zip ./*.dmg diff --git a/.husky/pre-commit b/.husky/pre-commit index 2f1bef1ce9..3ea16f0a05 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,4 @@ #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" -yarn lint-staged && yarn lint:ox && cargo fmt --all && git add . +yarn lint-staged && yarn lint:ox diff --git a/README.md b/README.md index 513a0ff604..8bef8a5965 100644 --- a/README.md +++ b/README.md @@ -107,12 +107,11 @@ If you have questions, you are welcome to contact us. One of the best places to ## Ecosystem -| Name | | | -| ----------------------------------------------------------------------------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | -| [@toeverything/component](https://github.com/toeverything/design/tree/main/packages/components) | Toeverything Shared Component Resources | | -| [@affine/component](packages/frontend/component) | AFFiNE Component Resources | [![](https://img.shields.io/codecov/c/github/toeverything/affine?style=flat-square)](https://affine-storybook.vercel.app/) | -| [@toeverything/y-indexeddb](packages/common/y-indexeddb) | IndexedDB database adapter for Yjs | [![](https://img.shields.io/npm/dm/@toeverything/y-indexeddb?style=flat-square&color=eee)](https://www.npmjs.com/package/@toeverything/y-indexeddb) | -| [@toeverything/theme](packages/common/theme) | AFFiNE theme | [![](https://img.shields.io/npm/dm/@toeverything/theme?style=flat-square&color=eee)](https://www.npmjs.com/package/@toeverything/theme) | +| Name | | | +| -------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| [@affine/component](packages/frontend/component) | AFFiNE Component Resources | [![](https://img.shields.io/codecov/c/github/toeverything/affine?style=flat-square)](https://affine-storybook.vercel.app/) | +| [@toeverything/y-indexeddb](packages/common/y-indexeddb) | IndexedDB database adapter for Yjs | [![](https://img.shields.io/npm/dm/@toeverything/y-indexeddb?style=flat-square&color=eee)](https://www.npmjs.com/package/@toeverything/y-indexeddb) | +| [@toeverything/theme](packages/common/theme) | AFFiNE theme | [![](https://img.shields.io/npm/dm/@toeverything/theme?style=flat-square&color=eee)](https://www.npmjs.com/package/@toeverything/theme) | ## Plugins diff --git a/package.json b/package.json index e1be4c6888..28c963234f 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,9 @@ ], "*.toml": [ "taplo format" + ], + "*.rs": [ + "cargo fmt --" ] }, "devDependencies": { @@ -104,7 +107,7 @@ "string-width": "^7.0.0", "ts-node": "^10.9.1", "typescript": "^5.3.2", - "vite": "^5.0.1", + "vite": "^5.0.6", "vite-plugin-istanbul": "^5.0.0", "vite-plugin-static-copy": "^0.17.1", "vite-tsconfig-paths": "^4.2.1", @@ -114,7 +117,7 @@ }, "packageManager": "yarn@4.0.1", "resolutions": { - "vite": "^4.4.11", + "vite": "^5.0.6", "array-buffer-byte-length": "npm:@nolyfill/array-buffer-byte-length@latest", "array-includes": "npm:@nolyfill/array-includes@latest", "array.prototype.flat": "npm:@nolyfill/array.prototype.flat@latest", diff --git a/packages/backend/server/package.json b/packages/backend/server/package.json index 406ce7596a..4ed47ca843 100644 --- a/packages/backend/server/package.json +++ b/packages/backend/server/package.json @@ -41,11 +41,11 @@ "@opentelemetry/core": "^1.18.1", "@opentelemetry/exporter-prometheus": "^0.45.1", "@opentelemetry/exporter-zipkin": "^1.18.1", - "@opentelemetry/host-metrics": "^0.33.2", + "@opentelemetry/host-metrics": "^0.34.0", "@opentelemetry/instrumentation": "^0.45.1", "@opentelemetry/instrumentation-graphql": "^0.36.0", "@opentelemetry/instrumentation-http": "^0.45.1", - "@opentelemetry/instrumentation-ioredis": "^0.35.3", + "@opentelemetry/instrumentation-ioredis": "^0.36.0", "@opentelemetry/instrumentation-nestjs-core": "^0.33.3", "@opentelemetry/instrumentation-socket.io": "^0.34.3", "@opentelemetry/resources": "^1.18.1", @@ -102,7 +102,7 @@ "@types/sinon": "^17.0.2", "@types/supertest": "^2.0.16", "@types/ws": "^8.5.10", - "ava": "^5.3.1", + "ava": "^6.0.0", "c8": "^8.0.1", "nodemon": "^3.0.1", "sinon": "^17.0.1", diff --git a/packages/backend/server/src/app.ts b/packages/backend/server/src/app.ts index cab18af060..afc5ab7b1a 100644 --- a/packages/backend/server/src/app.ts +++ b/packages/backend/server/src/app.ts @@ -3,6 +3,7 @@ import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { CacheModule } from './cache'; import { ConfigModule } from './config'; +import { EventModule } from './event'; import { BusinessModules } from './modules'; import { AuthModule } from './modules/auth'; import { PrismaModule } from './prisma'; @@ -14,6 +15,7 @@ const BasicModules = [ PrismaModule, ConfigModule.forRoot(), CacheModule, + EventModule, StorageModule.forRoot(), SessionModule, RateLimiterModule, diff --git a/packages/backend/server/src/event/events.ts b/packages/backend/server/src/event/events.ts new file mode 100644 index 0000000000..025d664663 --- /dev/null +++ b/packages/backend/server/src/event/events.ts @@ -0,0 +1,23 @@ +import type { Snapshot, Workspace } from '@prisma/client'; + +import { Flatten, Payload } from './types'; + +interface EventDefinitions { + workspace: { + deleted: Payload; + }; + + snapshot: { + updated: Payload< + Pick & { + previous: Pick; + } + >; + deleted: Payload>; + }; +} + +export type EventKV = Flatten; + +export type Event = keyof EventKV; +export type EventPayload = EventKV[E]; diff --git a/packages/backend/server/src/event/index.ts b/packages/backend/server/src/event/index.ts new file mode 100644 index 0000000000..0c70582e59 --- /dev/null +++ b/packages/backend/server/src/event/index.ts @@ -0,0 +1,45 @@ +import { Global, Injectable, Module } from '@nestjs/common'; +import { + EventEmitter2, + EventEmitterModule, + OnEvent as RawOnEvent, +} from '@nestjs/event-emitter'; + +import type { Event, EventPayload } from './events'; + +@Injectable() +export class EventEmitter { + constructor(private readonly emitter: EventEmitter2) {} + + emit(event: E, payload: EventPayload) { + return this.emitter.emit(event, payload); + } + + emitAsync(event: E, payload: EventPayload) { + return this.emitter.emitAsync(event, payload); + } + + on(event: E, handler: (payload: EventPayload) => void) { + return this.emitter.on(event, handler); + } + + once(event: E, handler: (payload: EventPayload) => void) { + return this.emitter.once(event, handler); + } +} + +export const OnEvent = ( + event: Event, + opts?: Parameters[1] +) => { + return RawOnEvent(event, opts); +}; + +@Global() +@Module({ + imports: [EventEmitterModule.forRoot()], + providers: [EventEmitter], + exports: [EventEmitter], +}) +export class EventModule {} +export { EventPayload }; diff --git a/packages/backend/server/src/event/types.ts b/packages/backend/server/src/event/types.ts new file mode 100644 index 0000000000..65d48b293d --- /dev/null +++ b/packages/backend/server/src/event/types.ts @@ -0,0 +1,33 @@ +export type Payload = { + __payload: true; + data: T; +}; + +export type Join = A extends '' + ? B + : `${A}.${B}`; + +export type PathType = string extends Path + ? unknown + : Path extends keyof T + ? T[Path] + : Path extends `${infer K}.${infer R}` + ? K extends keyof T + ? PathType + : unknown + : unknown; + +export type Leaves = T extends Payload + ? P + : T extends Record + ? { + [K in keyof T]: K extends string ? Leaves> : never; + }[keyof T] + : never; + +export type Flatten = Leaves extends infer R + ? { + // @ts-expect-error yo, ts can't make it + [K in R]: PathType extends Payload ? U : never; + } + : never; diff --git a/packages/backend/server/src/modules/doc/history.ts b/packages/backend/server/src/modules/doc/history.ts index 7502355950..fa52ecda97 100644 --- a/packages/backend/server/src/modules/doc/history.ts +++ b/packages/backend/server/src/modules/doc/history.ts @@ -1,15 +1,15 @@ import { isDeepStrictEqual } from 'node:util'; import { Injectable, Logger } from '@nestjs/common'; -import { OnEvent } from '@nestjs/event-emitter'; import { Cron, CronExpression } from '@nestjs/schedule'; -import type { Snapshot } from '@prisma/client'; import { Config } from '../../config'; +import { type EventPayload, OnEvent } from '../../event'; import { metrics } from '../../metrics'; import { PrismaService } from '../../prisma'; import { SubscriptionStatus } from '../payment/service'; import { Permission } from '../workspaces/types'; +import { isEmptyBuffer } from './manager'; @Injectable() export class DocHistoryManager { @@ -19,16 +19,38 @@ export class DocHistoryManager { private readonly db: PrismaService ) {} - @OnEvent('doc:manager:snapshot:beforeUpdate') - async onDocUpdated(snapshot: Snapshot, forceCreate = false) { - const last = await this.last(snapshot.workspaceId, snapshot.id); + @OnEvent('workspace.deleted') + onWorkspaceDeleted(workspaceId: EventPayload<'workspace.deleted'>) { + return this.db.snapshotHistory.deleteMany({ + where: { + workspaceId, + }, + }); + } + + @OnEvent('snapshot.deleted') + onSnapshotDeleted({ workspaceId, id }: EventPayload<'snapshot.deleted'>) { + return this.db.snapshotHistory.deleteMany({ + where: { + workspaceId, + id, + }, + }); + } + + @OnEvent('snapshot.updated') + async onDocUpdated( + { workspaceId, id, previous }: EventPayload<'snapshot.updated'>, + forceCreate = false + ) { + const last = await this.last(workspaceId, id); let shouldCreateHistory = false; if (!last) { // never created shouldCreateHistory = true; - } else if (last.timestamp === snapshot.updatedAt) { + } else if (last.timestamp === previous.updatedAt) { // no change shouldCreateHistory = false; } else if ( @@ -36,16 +58,23 @@ export class DocHistoryManager { forceCreate || // last history created before interval in configs last.timestamp.getTime() < - snapshot.updatedAt.getTime() - this.config.doc.history.interval + previous.updatedAt.getTime() - this.config.doc.history.interval ) { shouldCreateHistory = true; } if (shouldCreateHistory) { // skip the history recording when no actual update on snapshot happended - if (last && isDeepStrictEqual(last.state, snapshot.state)) { + if (last && isDeepStrictEqual(last.state, previous.state)) { this.logger.debug( - `State matches, skip creating history record for ${snapshot.id} in workspace ${snapshot.workspaceId}` + `State matches, skip creating history record for ${id} in workspace ${workspaceId}` + ); + return; + } + + if (isEmptyBuffer(previous.blob)) { + this.logger.debug( + `Doc is empty, skip creating history record for ${id} in workspace ${workspaceId}` ); return; } @@ -56,12 +85,12 @@ export class DocHistoryManager { timestamp: true, }, data: { - workspaceId: snapshot.workspaceId, - id: snapshot.id, - timestamp: snapshot.updatedAt, - blob: snapshot.blob, - state: snapshot.state, - expiredAt: await this.getExpiredDateFromNow(snapshot.workspaceId), + workspaceId, + id, + timestamp: previous.updatedAt, + blob: previous.blob, + state: previous.state, + expiredAt: await this.getExpiredDateFromNow(workspaceId), }, }) .catch(() => { @@ -73,9 +102,7 @@ export class DocHistoryManager { description: 'How many times the snapshot history created', }) .add(1); - this.logger.log( - `History created for ${snapshot.id} in workspace ${snapshot.workspaceId}.` - ); + this.logger.log(`History created for ${id} in workspace ${workspaceId}.`); } } @@ -180,7 +207,7 @@ export class DocHistoryManager { } // save old snapshot as one history record - await this.onDocUpdated(oldSnapshot, true); + await this.onDocUpdated({ workspaceId, id, previous: oldSnapshot }, true); // WARN: // we should never do the snapshot updating in recovering, // which is not the solution in CRDT. diff --git a/packages/backend/server/src/modules/doc/manager.ts b/packages/backend/server/src/modules/doc/manager.ts index 326902b35f..57151bb760 100644 --- a/packages/backend/server/src/modules/doc/manager.ts +++ b/packages/backend/server/src/modules/doc/manager.ts @@ -5,12 +5,12 @@ import { OnModuleDestroy, OnModuleInit, } from '@nestjs/common'; -import { EventEmitter2 } from '@nestjs/event-emitter'; import { Snapshot, Update } from '@prisma/client'; import { chunk } from 'lodash-es'; import { defer, retry } from 'rxjs'; import { applyUpdate, + decodeStateVector, Doc, encodeStateAsUpdate, encodeStateVector, @@ -19,6 +19,7 @@ import { import { Cache } from '../../cache'; import { Config } from '../../config'; +import { EventEmitter, type EventPayload, OnEvent } from '../../event'; import { metrics } from '../../metrics/metrics'; import { PrismaService } from '../../prisma'; import { mergeUpdatesInApplyWay as jwstMergeUpdates } from '../../storage'; @@ -40,7 +41,37 @@ function compare(yBinary: Buffer, jwstBinary: Buffer, strict = false): boolean { return compare(yBinary, yBinary2, true); } -function isEmptyBuffer(buf: Buffer): boolean { +/** + * Detect whether rhs state is newer than lhs state. + * + * How could we tell a state is newer: + * + * i. if the state vector size is larger, it's newer + * ii. if the state vector size is same, compare each client's state + */ +function isStateNewer(lhs: Buffer, rhs: Buffer): boolean { + const lhsVector = decodeStateVector(lhs); + const rhsVector = decodeStateVector(rhs); + + if (lhsVector.size < rhsVector.size) { + return true; + } + + for (const [client, state] of lhsVector) { + const rstate = rhsVector.get(client); + if (!rstate) { + return false; + } + + if (state < rstate) { + return true; + } + } + + return false; +} + +export function isEmptyBuffer(buf: Buffer): boolean { return ( buf.length === 0 || // 0x0000 @@ -71,7 +102,7 @@ export class DocManager implements OnModuleInit, OnModuleDestroy { private readonly db: PrismaService, private readonly config: Config, private readonly cache: Cache, - private readonly event: EventEmitter2 + private readonly event: EventEmitter ) {} onModuleInit() { @@ -193,6 +224,33 @@ export class DocManager implements OnModuleInit, OnModuleDestroy { } } + @OnEvent('workspace.deleted') + async onWorkspaceDeleted(workspaceId: string) { + await this.db.snapshot.deleteMany({ + where: { + workspaceId, + }, + }); + await this.db.update.deleteMany({ + where: { + workspaceId, + }, + }); + } + + @OnEvent('snapshot.deleted') + async onSnapshotDeleted({ + id, + workspaceId, + }: EventPayload<'snapshot.deleted'>) { + await this.db.update.deleteMany({ + where: { + id, + workspaceId, + }, + }); + } + /** * add update to manager for later processing. */ @@ -374,23 +432,17 @@ export class DocManager implements OnModuleInit, OnModuleDestroy { } const { id, workspaceId } = candidate; - // acquire lock - const ok = await this.lockUpdatesForAutoSquash(workspaceId, id); - if (!ok) { - return; - } - - try { - await this._get(workspaceId, id); - } catch (e) { - this.logger.error( - `Failed to apply updates for workspace: ${workspaceId}, guid: ${id}` - ); - this.logger.error(e); - } finally { - await this.unlockUpdatesForAutoSquash(workspaceId, id); - } + await this.lockUpdatesForAutoSquash(workspaceId, id, async () => { + try { + await this._get(workspaceId, id); + } catch (e) { + this.logger.error( + `Failed to apply updates for workspace: ${workspaceId}, guid: ${id}` + ); + this.logger.error(e); + } + }); } private async getAutoSquashCandidate() { @@ -414,34 +466,67 @@ export class DocManager implements OnModuleInit, OnModuleDestroy { doc: Doc, initialSeq?: number ) { - const blob = Buffer.from(encodeStateAsUpdate(doc)); - const state = Buffer.from(encodeStateVector(doc)); + return this.lockSnapshotForUpsert(workspaceId, guid, async () => { + const blob = Buffer.from(encodeStateAsUpdate(doc)); - if (isEmptyBuffer(blob)) { - return; - } + if (isEmptyBuffer(blob)) { + return false; + } - await this.db.snapshot.upsert({ - select: { - seq: true, - }, - where: { - id_workspaceId: { - id: guid, - workspaceId, - }, - }, - create: { - id: guid, - workspaceId, - blob, - state, - seq: initialSeq, - }, - update: { - blob, - state, - }, + const state = Buffer.from(encodeStateVector(doc)); + + return await this.db.$transaction(async db => { + const snapshot = await db.snapshot.findUnique({ + where: { + id_workspaceId: { + id: guid, + workspaceId, + }, + }, + }); + + // update + if (snapshot) { + // only update if state is newer + if (isStateNewer(snapshot.state ?? Buffer.from([0]), state)) { + await db.snapshot.update({ + select: { + seq: true, + }, + where: { + id_workspaceId: { + workspaceId, + id: guid, + }, + }, + data: { + blob, + state, + }, + }); + + return true; + } else { + return false; + } + } else { + // create + await db.snapshot.create({ + select: { + seq: true, + }, + data: { + id: guid, + workspaceId, + blob, + state, + seq: initialSeq, + }, + }); + + return true; + } + }); }); } @@ -480,25 +565,39 @@ export class DocManager implements OnModuleInit, OnModuleDestroy { ...updates.map(u => u.blob) ); - if (snapshot) { - this.event.emit('doc:manager:snapshot:beforeUpdate', snapshot); - } - await this.upsert(workspaceId, id, doc, last.seq); - this.logger.debug( - `Squashed ${updates.length} updates for ${id} in workspace ${workspaceId}` - ); - await this.db.update.deleteMany({ - where: { + if (snapshot) { + this.event.emit('snapshot.updated', { id, workspaceId, - seq: { - in: updates.map(u => u.seq), + previous: { + blob: snapshot.blob, + state: snapshot.state, + updatedAt: snapshot.updatedAt, }, - }, - }); + }); + } + + const done = await this.upsert(workspaceId, id, doc, last.seq); + + if (done) { + this.logger.debug( + `Squashed ${updates.length} updates for ${id} in workspace ${workspaceId}` + ); + + await this.db.update.deleteMany({ + where: { + id, + workspaceId, + seq: { + in: updates.map(u => u.seq), + }, + }, + }); + + await this.updateCachedUpdatesCount(workspaceId, id, -updates.length); + } - await this.updateCachedUpdatesCount(workspaceId, id, -updates.length); return doc; } @@ -581,22 +680,44 @@ export class DocManager implements OnModuleInit, OnModuleDestroy { return null; } - private async lockUpdatesForAutoSquash(workspaceId: string, guid: string) { - return this.cache.setnx( + private async doWithLock(lock: string, job: () => Promise) { + const acquired = await this.cache.setnx(lock, 1, { + ttl: 60 * 1000, + }); + + if (!acquired) { + return; + } + + try { + return await job(); + } finally { + await this.cache.delete(lock).catch(e => { + // safe, the lock will be expired when ttl ends + this.logger.error(`Failed to release lock ${lock}`, e); + }); + } + } + + private async lockUpdatesForAutoSquash( + workspaceId: string, + guid: string, + job: () => Promise + ) { + return this.doWithLock( `doc:manager:updates-lock:${workspaceId}::${guid}`, - 1, - { - ttl: 60 * 1000, - } + job ); } - private async unlockUpdatesForAutoSquash(workspaceId: string, guid: string) { - return this.cache - .delete(`doc:manager:updates-lock:${workspaceId}::${guid}`) - .catch(e => { - // safe, the lock will be expired when ttl ends - this.logger.error('Failed to release updates lock', e); - }); + async lockSnapshotForUpsert( + workspaceId: string, + guid: string, + job: () => Promise + ) { + return this.doWithLock( + `doc:manager:snapshot-lock:${workspaceId}::${guid}`, + job + ); } } diff --git a/packages/backend/server/src/modules/index.ts b/packages/backend/server/src/modules/index.ts index d021dd8bfa..422b276265 100644 --- a/packages/backend/server/src/modules/index.ts +++ b/packages/backend/server/src/modules/index.ts @@ -1,5 +1,4 @@ import { DynamicModule, Type } from '@nestjs/common'; -import { EventEmitterModule } from '@nestjs/event-emitter'; import { ScheduleModule } from '@nestjs/schedule'; import { GqlModule } from '../graphql.module'; @@ -11,11 +10,7 @@ import { SyncModule } from './sync'; import { UsersModule } from './users'; import { WorkspaceModule } from './workspaces'; -const BusinessModules: (Type | DynamicModule)[] = [ - EventEmitterModule.forRoot({ - global: true, - }), -]; +const BusinessModules: (Type | DynamicModule)[] = []; switch (SERVER_FLAVOR) { case 'sync': diff --git a/packages/backend/server/src/modules/sync/events/events.gateway.ts b/packages/backend/server/src/modules/sync/events/events.gateway.ts index f847842c1c..4953241185 100644 --- a/packages/backend/server/src/modules/sync/events/events.gateway.ts +++ b/packages/backend/server/src/modules/sync/events/events.gateway.ts @@ -25,7 +25,6 @@ import { EventError, InternalError, NotInWorkspaceError, - WorkspaceNotFoundError, } from './error'; export const GatewayErrorWrapper = (): MethodDecorator => { @@ -319,9 +318,7 @@ export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect { if (!doc) { return { - error: docId.isWorkspace - ? new WorkspaceNotFoundError(workspaceId) - : new DocNotFoundError(workspaceId, docId.guid), + error: new DocNotFoundError(workspaceId, docId.guid), }; } diff --git a/packages/backend/server/src/modules/workspaces/resolver.ts b/packages/backend/server/src/modules/workspaces/resolver.ts index 6b83076e3a..bbb772d76f 100644 --- a/packages/backend/server/src/modules/workspaces/resolver.ts +++ b/packages/backend/server/src/modules/workspaces/resolver.ts @@ -33,6 +33,7 @@ import type { import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs'; import { applyUpdate, Doc } from 'yjs'; +import { EventEmitter } from '../../event'; import { PrismaService } from '../../prisma'; import { StorageProvide } from '../../storage'; import { CloudThrottlerGuard, Throttle } from '../../throttler'; @@ -146,6 +147,7 @@ export class WorkspaceResolver { private readonly prisma: PrismaService, private readonly permissions: PermissionService, private readonly users: UsersService, + private readonly event: EventEmitter, @Inject(StorageProvide) private readonly storage: Storage ) {} @@ -308,22 +310,11 @@ export class WorkspaceResolver { }) async createWorkspace( @CurrentUser() user: UserType, - @Args({ name: 'init', type: () => GraphQLUpload }) - update: FileUpload + // we no longer support init workspace with a preload file + // use sync system to uploading them once created + @Args({ name: 'init', type: () => GraphQLUpload, nullable: true }) + init: FileUpload | null ) { - // convert stream to buffer - const buffer = await new Promise((resolve, reject) => { - const stream = update.createReadStream(); - const chunks: Uint8Array[] = []; - stream.on('data', chunk => { - chunks.push(chunk); - }); - stream.on('error', reject); - stream.on('end', () => { - resolve(Buffer.concat(chunks)); - }); - }); - const workspace = await this.prisma.workspace.create({ data: { public: false, @@ -341,14 +332,31 @@ export class WorkspaceResolver { }, }); - if (buffer.length) { - await this.prisma.snapshot.create({ - data: { - id: workspace.id, - workspaceId: workspace.id, - blob: buffer, - }, + if (init) { + // convert stream to buffer + const buffer = await new Promise(resolve => { + const stream = init.createReadStream(); + const chunks: Uint8Array[] = []; + stream.on('data', chunk => { + chunks.push(chunk); + }); + stream.on('error', () => { + resolve(Buffer.from([])); + }); + stream.on('end', () => { + resolve(Buffer.concat(chunks)); + }); }); + + if (buffer.length) { + await this.prisma.snapshot.create({ + data: { + id: workspace.id, + workspaceId: workspace.id, + blob: buffer, + }, + }); + } } return workspace; @@ -382,18 +390,7 @@ export class WorkspaceResolver { }, }); - await this.prisma.$transaction([ - this.prisma.update.deleteMany({ - where: { - workspaceId: id, - }, - }), - this.prisma.snapshot.deleteMany({ - where: { - workspaceId: id, - }, - }), - ]); + this.event.emit('workspace.deleted', id); return true; } diff --git a/packages/backend/server/src/schema.gql b/packages/backend/server/src/schema.gql index 68dc28c522..c6ee591b28 100644 --- a/packages/backend/server/src/schema.gql +++ b/packages/backend/server/src/schema.gql @@ -292,7 +292,7 @@ type Mutation { sendVerifyChangeEmail(token: String!, email: String!, callbackUrl: String!): Boolean! """Create a new workspace""" - createWorkspace(init: Upload!): WorkspaceType! + createWorkspace(init: Upload): WorkspaceType! """Update workspace""" updateWorkspace(input: UpdateWorkspaceInput!): WorkspaceType! diff --git a/packages/backend/server/tests/doc.spec.ts b/packages/backend/server/tests/doc.spec.ts index 83147b96fe..76ff2a1ca1 100644 --- a/packages/backend/server/tests/doc.spec.ts +++ b/packages/backend/server/tests/doc.spec.ts @@ -1,15 +1,20 @@ import { mock } from 'node:test'; import type { INestApplication } from '@nestjs/common'; -import { EventEmitterModule } from '@nestjs/event-emitter'; import { Test, TestingModule } from '@nestjs/testing'; import test from 'ava'; import { register } from 'prom-client'; import * as Sinon from 'sinon'; -import { Doc as YDoc, encodeStateAsUpdate } from 'yjs'; +import { + applyUpdate, + decodeStateVector, + Doc as YDoc, + encodeStateAsUpdate, +} from 'yjs'; import { CacheModule } from '../src/cache'; import { Config, ConfigModule } from '../src/config'; +import { EventModule } from '../src/event'; import { DocManager, DocModule } from '../src/modules/doc'; import { PrismaModule, PrismaService } from '../src/prisma'; import { flushDB } from './utils'; @@ -19,7 +24,7 @@ const createModule = () => { imports: [ PrismaModule, CacheModule, - EventEmitterModule.forRoot(), + EventModule, ConfigModule.forRoot(), DocModule.forRoot(), ], @@ -283,3 +288,73 @@ test('should throw if meet max retry times', async t => { ); t.is(stub.callCount, 5); }); + +test('should not update snapshot if state is outdated', async t => { + const db = m.get(PrismaService); + const manager = m.get(DocManager); + + await db.snapshot.create({ + data: { + id: '2', + workspaceId: '2', + blob: Buffer.from([0, 0]), + seq: 1, + }, + }); + const doc = new YDoc(); + const text = doc.getText('content'); + const updates: Buffer[] = []; + + doc.on('update', update => { + updates.push(Buffer.from(update)); + }); + + text.insert(0, 'hello'); + text.insert(5, 'world'); + text.insert(5, ' '); + + await Promise.all(updates.map(update => manager.push('2', '2', update))); + + const updateWith3Records = await manager.getUpdates('2', '2'); + text.insert(11, '!'); + await manager.push('2', '2', updates[3]); + const updateWith4Records = await manager.getUpdates('2', '2'); + + // Simulation: + // Node A get 3 updates and squash them at time 1, will finish at time 10 + // Node B get 4 updates and squash them at time 3, will finish at time 8 + // Node B finish the squash first, and update the snapshot + // Node A finish the squash later, and update the snapshot to an outdated state + // Time: ----------------------> + // A: ^get ^upsert + // B: ^get ^upsert + // + // We should avoid such situation + // @ts-expect-error private + await manager.squash(updateWith4Records, null); + // @ts-expect-error private + await manager.squash(updateWith3Records, null); + + const result = await db.snapshot.findUnique({ + where: { + id_workspaceId: { + id: '2', + workspaceId: '2', + }, + }, + }); + + if (!result) { + t.fail('snapshot not found'); + return; + } + + const state = decodeStateVector(result.state!); + t.is(state.get(doc.clientID), 12); + + const d = new YDoc(); + applyUpdate(d, result.blob!); + + const dtext = d.getText('content'); + t.is(dtext.toString(), 'hello world!'); +}); diff --git a/packages/backend/server/tests/history.spec.ts b/packages/backend/server/tests/history.spec.ts index c2ed148574..0c77b1facd 100644 --- a/packages/backend/server/tests/history.spec.ts +++ b/packages/backend/server/tests/history.spec.ts @@ -6,6 +6,7 @@ import test from 'ava'; import * as Sinon from 'sinon'; import { ConfigModule } from '../src/config'; +import type { EventPayload } from '../src/event'; import { DocHistoryManager } from '../src/modules/doc'; import { PrismaModule, PrismaService } from '../src/prisma'; import { flushDB } from './utils'; @@ -41,21 +42,28 @@ test.afterEach(async () => { const snapshot: Snapshot = { workspaceId: '1', id: 'doc1', - blob: Buffer.from([0, 0]), - state: Buffer.from([0, 0]), + blob: Buffer.from([1, 0]), + state: Buffer.from([0]), seq: 0, updatedAt: new Date(), createdAt: new Date(), }; +function getEventData( + timestamp: Date = new Date() +): EventPayload<'snapshot.updated'> { + return { + workspaceId: snapshot.workspaceId, + id: snapshot.id, + previous: { ...snapshot, updatedAt: timestamp }, + }; +} + test('should create doc history if never created before', async t => { Sinon.stub(manager, 'last').resolves(null); const timestamp = new Date(); - await manager.onDocUpdated({ - ...snapshot, - updatedAt: timestamp, - }); + await manager.onDocUpdated(getEventData(timestamp)); const history = await db.snapshotHistory.findFirst({ where: { @@ -72,10 +80,7 @@ test('should not create history if timestamp equals to last record', async t => const timestamp = new Date(); Sinon.stub(manager, 'last').resolves({ timestamp, state: null }); - await manager.onDocUpdated({ - ...snapshot, - updatedAt: timestamp, - }); + await manager.onDocUpdated(getEventData(timestamp)); const history = await db.snapshotHistory.findFirst({ where: { @@ -94,10 +99,7 @@ test('should not create history if state equals to last record', async t => { state: snapshot.state, }); - await manager.onDocUpdated({ - ...snapshot, - updatedAt: timestamp, - }); + await manager.onDocUpdated(getEventData(timestamp)); const history = await db.snapshotHistory.findFirst({ where: { @@ -116,10 +118,7 @@ test('should not create history if time diff is less than interval config', asyn state: Buffer.from([0, 1]), }); - await manager.onDocUpdated({ - ...snapshot, - updatedAt: timestamp, - }); + await manager.onDocUpdated(getEventData(timestamp)); const history = await db.snapshotHistory.findFirst({ where: { @@ -138,10 +137,7 @@ test('should create history if time diff is larger than interval config and stat state: Buffer.from([0, 1]), }); - await manager.onDocUpdated({ - ...snapshot, - updatedAt: timestamp, - }); + await manager.onDocUpdated(getEventData(timestamp)); const history = await db.snapshotHistory.findFirst({ where: { @@ -160,13 +156,7 @@ test('should create history with force flag even if time diff in small', async t state: Buffer.from([0, 1]), }); - await manager.onDocUpdated( - { - ...snapshot, - updatedAt: timestamp, - }, - true - ); + await manager.onDocUpdated(getEventData(timestamp), true); const history = await db.snapshotHistory.findFirst({ where: { @@ -224,13 +214,7 @@ test('should correctly list all history records', async t => { test('should be able to get history data', async t => { const timestamp = new Date(); - await manager.onDocUpdated( - { - ...snapshot, - updatedAt: timestamp, - }, - true - ); + await manager.onDocUpdated(getEventData(timestamp), true); const history = await manager.get( snapshot.workspaceId, @@ -274,10 +258,7 @@ test('should be able to recover from history', async t => { }, }); const history1Timestamp = snapshot.updatedAt.getTime() - 10; - await manager.onDocUpdated({ - ...snapshot, - updatedAt: new Date(history1Timestamp), - }); + await manager.onDocUpdated(getEventData(new Date(history1Timestamp))); await manager.recover( snapshot.workspaceId, diff --git a/packages/backend/storage/index.d.ts b/packages/backend/storage/index.d.ts index bd6060f9fb..28e5de90c0 100644 --- a/packages/backend/storage/index.d.ts +++ b/packages/backend/storage/index.d.ts @@ -1,21 +1,6 @@ -/* tslint:disable */ +/* auto-generated by NAPI-RS */ /* eslint-disable */ -/* auto-generated by NAPI-RS */ - -export function verifyChallengeResponse(response: string, bits: number, resource: string): Promise -export function mintChallengeResponse(resource: string, bits?: number | undefined | null): Promise -export interface Blob { - contentType: string - lastModified: string - size: number - data: Buffer -} -/** - * Merge updates in form like `Y.applyUpdate(doc, update)` way and return the - * result binary. - */ -export function mergeUpdatesInApplyWay(updates: Array): Buffer export class Storage { /** Create a storage instance and establish connection to persist store. */ static connect(database: string, debugOnlyAutoMigrate?: boolean | undefined | null): Promise @@ -30,3 +15,21 @@ export class Storage { /** Workspace size taken by blobs. */ blobsSize(workspaces: Array): Promise } + +export interface Blob { + contentType: string + lastModified: string + size: number + data: Buffer +} + +/** + * Merge updates in form like `Y.applyUpdate(doc, update)` way and return the + * result binary. + */ +export function mergeUpdatesInApplyWay(updates: Array): Buffer + +export function mintChallengeResponse(resource: string, bits?: number | undefined | null): Promise + +export function verifyChallengeResponse(response: string, bits: number, resource: string): Promise + diff --git a/packages/backend/storage/package.json b/packages/backend/storage/package.json index b6cf2af776..a652ada5b1 100644 --- a/packages/backend/storage/package.json +++ b/packages/backend/storage/package.json @@ -16,27 +16,26 @@ } }, "napi": { - "name": "storage", + "binaryName": "storage", "targets": [ "aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "aarch64-pc-windows-msvc", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", - "x86_64-unknown-linux-gnu", - "universal-apple-darwin" + "x86_64-unknown-linux-gnu" ] }, "scripts": { "test": "node --test ./__tests__/**/*.spec.js", - "build": "napi build --release --strip", + "build": "napi build --release --strip --no-const-enum", "build:debug": "napi build", "prepublishOnly": "napi prepublish -t npm", "artifacts": "napi artifacts", "version": "napi version" }, "devDependencies": { - "@napi-rs/cli": "^2.16.5", + "@napi-rs/cli": "3.0.0-alpha.15", "lib0": "^0.2.87", "nx": "^17.1.3", "nx-cloud": "^16.5.2", diff --git a/packages/common/env/package.json b/packages/common/env/package.json index ae3ed57436..82286ee011 100644 --- a/packages/common/env/package.json +++ b/packages/common/env/package.json @@ -3,8 +3,8 @@ "private": true, "type": "module", "devDependencies": { - "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/global": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/store": "0.11.0-nightly-202312070955-2b5bb47", "react": "18.2.0", "react-dom": "18.2.0", "vitest": "0.34.6", @@ -21,8 +21,7 @@ }, "peerDependencies": { "@affine/templates": "workspace:*", - "@blocksuite/global": "0.0.0-20230409084303-221991d4-nightly", - "@toeverything/infra": "workspace:*" + "@blocksuite/global": "0.0.0-20230409084303-221991d4-nightly" }, "dependencies": { "lit": "^3.0.2" diff --git a/packages/common/env/src/global.ts b/packages/common/env/src/global.ts index 88c86f24ad..0532ff1248 100644 --- a/packages/common/env/src/global.ts +++ b/packages/common/env/src/global.ts @@ -6,12 +6,9 @@ import { isDesktop, isServer } from './constant.js'; import { UaHelper } from './ua-helper.js'; export const blockSuiteFeatureFlags = z.object({ - enable_set_remote_flag: z.boolean(), - enable_block_hub: z.boolean(), - - enable_toggle_block: z.boolean(), - enable_bookmark_operation: z.boolean(), - enable_note_index: z.boolean(), + enable_transformer_clipboard: z.boolean(), + enable_expand_database_block: z.boolean(), + enable_bultin_ledits: z.boolean(), }); export const runtimeFlagsSchema = z.object({ @@ -21,6 +18,7 @@ export const runtimeFlagsSchema = z.object({ enableBroadcastChannelProvider: z.boolean(), enableDebugPage: z.boolean(), changelogUrl: z.string(), + downloadUrl: z.string(), // see: tools/workers imageProxyUrl: z.string(), enablePreloading: z.boolean(), diff --git a/packages/common/env/src/workspace.ts b/packages/common/env/src/workspace.ts index 32ea9b1eca..4ac9e7d4d3 100644 --- a/packages/common/env/src/workspace.ts +++ b/packages/common/env/src/workspace.ts @@ -1,5 +1,3 @@ -import type { EditorContainer } from '@blocksuite/editor'; -import type { Page } from '@blocksuite/store'; import type { ActiveDocProvider, PassiveDocProvider, @@ -111,7 +109,7 @@ export const settingPanel = { Export: 'export', Sync: 'sync', } as const; -export const settingPanelValues = [...Object.values(settingPanel)] as const; +export const settingPanelValues = Object.values(settingPanel); export type SettingPanel = (typeof settingPanel)[keyof typeof settingPanel]; // built-in workspaces @@ -134,18 +132,6 @@ type UIBaseProps<_Flavour extends keyof WorkspaceRegistry> = { currentWorkspaceId: string; }; -export type WorkspaceHeaderProps = - UIBaseProps & { - rightSlot?: ReactNode; - currentEntry: - | { - subPath: WorkspaceSubPath; - } - | { - pageId: string; - }; - }; - type NewSettingProps = UIBaseProps & { onDeleteLocalWorkspace: () => void; @@ -161,18 +147,11 @@ type NewSettingProps = ) => void; }; -type PageDetailProps = - UIBaseProps & { - currentPageId: string; - onLoadEditor: (page: Page, editor: EditorContainer) => () => void; - }; - interface FC

{ (props: P): ReactNode; } export interface WorkspaceUISchema { - PageDetail: FC>; NewSettingsDetail: FC>; Provider: FC; LoginCard?: FC; diff --git a/packages/common/env/tsconfig.json b/packages/common/env/tsconfig.json index d3368cce03..60bef5cacc 100644 --- a/packages/common/env/tsconfig.json +++ b/packages/common/env/tsconfig.json @@ -7,9 +7,6 @@ "outDir": "lib" }, "references": [ - { - "path": "../infra" - }, { "path": "../../../tests/fixtures" } diff --git a/packages/common/infra/package.json b/packages/common/infra/package.json index 4ad26cf3a2..7746ede36e 100644 --- a/packages/common/infra/package.json +++ b/packages/common/infra/package.json @@ -54,10 +54,12 @@ "dev": "vite build --watch" }, "dependencies": { + "@affine/debug": "workspace:*", + "@affine/env": "workspace:*", "@affine/sdk": "workspace:*", - "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/blocks": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/global": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/store": "0.11.0-nightly-202312070955-2b5bb47", "jotai": "^2.5.1", "jotai-effect": "^0.2.3", "tinykeys": "^2.1.0", @@ -66,22 +68,22 @@ "devDependencies": { "@affine-test/fixtures": "workspace:*", "@affine/templates": "workspace:*", - "@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/lit": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/lit": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/presets": "0.11.0-nightly-202312070955-2b5bb47", "@testing-library/react": "^14.0.0", "async-call-rpc": "^6.3.1", "electron": "link:../../frontend/electron/node_modules/electron", "nanoid": "^5.0.3", "react": "^18.2.0", "rxjs": "^7.8.1", - "vite": "^4.4.11", + "vite": "^5.0.6", "vite-plugin-dts": "3.6.0", "vitest": "0.34.6", "yjs": "^13.6.10" }, "peerDependencies": { "@affine/templates": "*", - "@blocksuite/editor": "*", + "@blocksuite/presets": "*", "async-call-rpc": "*", "electron": "*", "react": "*", @@ -91,7 +93,7 @@ "@affine/templates": { "optional": true }, - "@blocksuite/editor": { + "@blocksuite/presets": { "optional": true }, "async-call-rpc": { diff --git a/packages/common/infra/src/atom/index.ts b/packages/common/infra/src/atom/index.ts new file mode 100644 index 0000000000..8b5f2a3104 --- /dev/null +++ b/packages/common/infra/src/atom/index.ts @@ -0,0 +1,8 @@ +import { atom } from 'jotai'; + +export const loadedPluginNameAtom = atom([]); + +export * from './layout'; +export * from './root-store'; +export * from './settings'; +export * from './workspace'; diff --git a/packages/common/infra/src/atom.ts b/packages/common/infra/src/atom/layout.ts similarity index 50% rename from packages/common/infra/src/atom.ts rename to packages/common/infra/src/atom/layout.ts index 4957094507..ed6ec20fe7 100644 --- a/packages/common/infra/src/atom.ts +++ b/packages/common/infra/src/atom/layout.ts @@ -1,34 +1,5 @@ import type { ExpectedLayout } from '@affine/sdk/entry'; -import { assertExists } from '@blocksuite/global/utils'; -import type { Workspace } from '@blocksuite/store'; -import { atom, createStore } from 'jotai/vanilla'; - -import { getBlockSuiteWorkspaceAtom } from './__internal__/workspace'; - -// global store -let rootStore = createStore(); - -export function getCurrentStore() { - return rootStore; -} - -/** - * @internal do not use this function unless you know what you are doing - */ -export function _setCurrentStore(store: ReturnType) { - rootStore = store; -} - -export const loadedPluginNameAtom = atom([]); - -export const currentWorkspaceIdAtom = atom(null); -export const currentPageIdAtom = atom(null); -export const currentWorkspaceAtom = atom>(async get => { - const workspaceId = get(currentWorkspaceIdAtom); - assertExists(workspaceId); - const [currentWorkspaceAtom] = getBlockSuiteWorkspaceAtom(workspaceId); - return get(currentWorkspaceAtom); -}); +import { atom } from 'jotai'; const contentLayoutBaseAtom = atom('editor'); diff --git a/packages/common/infra/src/atom/root-store.ts b/packages/common/infra/src/atom/root-store.ts new file mode 100644 index 0000000000..7bbb1ae73e --- /dev/null +++ b/packages/common/infra/src/atom/root-store.ts @@ -0,0 +1,15 @@ +import { createStore } from 'jotai'; + +// global store +let rootStore = createStore(); + +export function getCurrentStore() { + return rootStore; +} + +/** + * @internal do not use this function unless you know what you are doing + */ +export function _setCurrentStore(store: ReturnType) { + rootStore = store; +} diff --git a/packages/frontend/core/src/atoms/settings.ts b/packages/common/infra/src/atom/settings.ts similarity index 68% rename from packages/frontend/core/src/atoms/settings.ts rename to packages/common/infra/src/atom/settings.ts index 835dd16ce1..229335ce48 100644 --- a/packages/frontend/core/src/atoms/settings.ts +++ b/packages/common/infra/src/atom/settings.ts @@ -1,5 +1,9 @@ +import { setupGlobal } from '@affine/env/global'; import { atom } from 'jotai'; import { atomWithStorage } from 'jotai/utils'; +import { atomEffect } from 'jotai-effect'; + +setupGlobal(); export type DateFormats = | 'MM/dd/YYYY' @@ -63,15 +67,35 @@ const appSettingBaseAtom = atomWithStorage('affine-settings', { type SetStateAction = Value | ((prev: Value) => Value); +const appSettingEffect = atomEffect(get => { + const settings = get(appSettingBaseAtom); + // some values in settings should be synced into electron side + if (environment.isDesktop) { + console.log('set config', settings); + window.apis?.updater + .setConfig({ + autoCheckUpdate: settings.autoCheckUpdate, + autoDownloadUpdate: settings.autoDownloadUpdate, + }) + .catch(err => { + console.error(err); + }); + } +}); + export const appSettingAtom = atom< AppSetting, [SetStateAction>], void >( - get => get(appSettingBaseAtom), - (get, set, apply) => { - const prev = get(appSettingBaseAtom); - const next = typeof apply === 'function' ? apply(prev) : apply; - set(appSettingBaseAtom, { ...prev, ...next }); + get => { + get(appSettingEffect); + return get(appSettingBaseAtom); + }, + (_get, set, apply) => { + set(appSettingBaseAtom, prev => { + const next = typeof apply === 'function' ? apply(prev) : apply; + return { ...prev, ...next }; + }); } ); diff --git a/packages/common/infra/src/atom/workspace.ts b/packages/common/infra/src/atom/workspace.ts new file mode 100644 index 0000000000..c73ab5a5e7 --- /dev/null +++ b/packages/common/infra/src/atom/workspace.ts @@ -0,0 +1,14 @@ +import { assertExists } from '@blocksuite/global/utils'; +import type { Workspace } from '@blocksuite/store'; +import { atom } from 'jotai'; + +import { getBlockSuiteWorkspaceAtom } from '../__internal__/workspace'; + +export const currentWorkspaceIdAtom = atom(null); +export const currentPageIdAtom = atom(null); +export const currentWorkspaceAtom = atom>(async get => { + const workspaceId = get(currentWorkspaceIdAtom); + assertExists(workspaceId); + const [currentWorkspaceAtom] = getBlockSuiteWorkspaceAtom(workspaceId); + return get(currentWorkspaceAtom); +}); diff --git a/packages/common/infra/src/command/registry.ts b/packages/common/infra/src/command/registry.ts index 11443a93e1..c77292d4c3 100644 --- a/packages/common/infra/src/command/registry.ts +++ b/packages/common/infra/src/command/registry.ts @@ -1,3 +1,4 @@ +import { DebugLogger } from '@affine/debug'; // @ts-expect-error upstream type is wrong import { tinykeys } from 'tinykeys'; @@ -7,12 +8,14 @@ import { createAffineCommand, } from './command'; +const commandLogger = new DebugLogger('command:registry'); + export const AffineCommandRegistry = new (class { readonly commands: Map = new Map(); register(options: AffineCommandOptions) { if (this.commands.has(options.id)) { - console.warn(`Command ${options.id} already registered.`); + commandLogger.warn(`Command ${options.id} already registered.`); return () => {}; } const command = createAffineCommand(options); @@ -38,17 +41,17 @@ export const AffineCommandRegistry = new (class { }); } - console.debug(`Registered command ${command.id}`); + commandLogger.debug(`Registered command ${command.id}`); return () => { unsubKb?.(); this.commands.delete(command.id); - console.debug(`Unregistered command ${command.id}`); + commandLogger.debug(`Unregistered command ${command.id}`); }; } get(id: string): AffineCommand | undefined { if (!this.commands.has(id)) { - console.warn(`Command ${id} not registered.`); + commandLogger.warn(`Command ${id} not registered.`); return undefined; } return this.commands.get(id); diff --git a/packages/common/infra/src/type.ts b/packages/common/infra/src/type.ts index 4467e27734..732c8d1d4d 100644 --- a/packages/common/infra/src/type.ts +++ b/packages/common/infra/src/type.ts @@ -201,7 +201,7 @@ export type UpdaterHandlers = { downloadUpdate: () => Promise; getConfig: () => Promise; setConfig: (newConfig: Partial) => Promise; - checkForUpdatesAndNotify: () => Promise<{ version: string } | null>; + checkForUpdates: () => Promise<{ version: string } | null>; }; export type WorkspaceHandlers = { diff --git a/packages/common/infra/tsconfig.json b/packages/common/infra/tsconfig.json index dadee14e7d..1e81c00cd2 100644 --- a/packages/common/infra/tsconfig.json +++ b/packages/common/infra/tsconfig.json @@ -11,6 +11,12 @@ { "path": "../sdk" }, + { + "path": "../env" + }, + { + "path": "../debug" + }, { "path": "./tsconfig.node.json" } diff --git a/packages/common/infra/vite.config.ts b/packages/common/infra/vite.config.ts index ca5dc954e7..efd6f41b66 100644 --- a/packages/common/infra/vite.config.ts +++ b/packages/common/infra/vite.config.ts @@ -13,7 +13,7 @@ export default defineConfig({ entry: { blocksuite: resolve(root, 'src/blocksuite/index.ts'), index: resolve(root, 'src/index.ts'), - atom: resolve(root, 'src/atom.ts'), + atom: resolve(root, 'src/atom/index.ts'), command: resolve(root, 'src/command/index.ts'), type: resolve(root, 'src/type.ts'), 'core/event-emitter': resolve(root, 'src/core/event-emitter.ts'), diff --git a/packages/common/sdk/package.json b/packages/common/sdk/package.json index 1323343945..8b5b85fbf5 100644 --- a/packages/common/sdk/package.json +++ b/packages/common/sdk/package.json @@ -22,16 +22,16 @@ "dist" ], "dependencies": { - "@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/block-std": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/blocks": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/global": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/presets": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/store": "0.11.0-nightly-202312070955-2b5bb47", "jotai": "^2.5.1", "zod": "^3.22.4" }, "devDependencies": { - "vite": "^4.4.11", + "vite": "^5.0.6", "vite-plugin-dts": "3.6.0" } } diff --git a/packages/common/sdk/src/entry.ts b/packages/common/sdk/src/entry.ts index 5288cb3260..2e4025a229 100644 --- a/packages/common/sdk/src/entry.ts +++ b/packages/common/sdk/src/entry.ts @@ -1,5 +1,5 @@ import type { BaseSelection } from '@blocksuite/block-std'; -import type { EditorContainer } from '@blocksuite/editor'; +import type { EditorContainer } from '@blocksuite/presets'; import type { Page } from '@blocksuite/store'; import type { Workspace } from '@blocksuite/store'; import type { Atom, getDefaultStore } from 'jotai/vanilla'; diff --git a/packages/common/y-indexeddb/package.json b/packages/common/y-indexeddb/package.json index 886f038512..b34fc1847e 100644 --- a/packages/common/y-indexeddb/package.json +++ b/packages/common/y-indexeddb/package.json @@ -32,15 +32,15 @@ } }, "dependencies": { - "idb": "^7.1.1", + "idb": "^8.0.0", "nanoid": "^5.0.3", "y-provider": "workspace:*" }, "devDependencies": { - "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/blocks": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/store": "0.11.0-nightly-202312070955-2b5bb47", "fake-indexeddb": "^5.0.0", - "vite": "^4.4.11", + "vite": "^5.0.6", "vite-plugin-dts": "3.6.0", "vitest": "0.34.6", "y-indexeddb": "^9.0.11", diff --git a/packages/common/y-indexeddb/src/__tests__/index.spec.ts b/packages/common/y-indexeddb/src/__tests__/index.spec.ts index d03ef745ce..2d597dd0cd 100644 --- a/packages/common/y-indexeddb/src/__tests__/index.spec.ts +++ b/packages/common/y-indexeddb/src/__tests__/index.spec.ts @@ -65,7 +65,7 @@ beforeEach(() => { id = nanoid(); workspace = new Workspace({ id, - isSSR: true, + schema, }); vi.useFakeTimers({ toFake: ['requestIdleCallback'] }); @@ -383,7 +383,7 @@ describe('subDoc', () => { { const newWorkspace = new Workspace({ id, - isSSR: true, + schema, }); const provider = createIndexedDBProvider(newWorkspace.doc, rootDBName); @@ -423,7 +423,7 @@ describe('utils', () => { expect(update).toBeInstanceOf(Uint8Array); const newWorkspace = new Workspace({ id, - isSSR: true, + schema, }); applyUpdate(newWorkspace.doc, update); diff --git a/packages/common/y-provider/package.json b/packages/common/y-provider/package.json index 915ea6358e..d943afdf7d 100644 --- a/packages/common/y-provider/package.json +++ b/packages/common/y-provider/package.json @@ -24,8 +24,8 @@ "build": "vite build" }, "devDependencies": { - "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", - "vite": "^4.4.11", + "@blocksuite/store": "0.11.0-nightly-202312070955-2b5bb47", + "vite": "^5.0.6", "vite-plugin-dts": "3.6.0", "vitest": "0.34.6", "yjs": "^13.6.10" diff --git a/packages/frontend/component/.storybook/main.ts b/packages/frontend/component/.storybook/main.ts new file mode 100644 index 0000000000..2e6b6a0463 --- /dev/null +++ b/packages/frontend/component/.storybook/main.ts @@ -0,0 +1,54 @@ +import { StorybookConfig } from '@storybook/react-vite'; +import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin'; +import { fileURLToPath } from 'url'; +import { mergeConfig } from 'vite'; +import tsconfigPaths from 'vite-tsconfig-paths'; +import { getRuntimeConfig } from '../../core/.webpack/runtime-config'; + +export default { + stories: ['../src/ui/**/*.stories.@(js|jsx|ts|tsx|mdx)'], + addons: [ + '@storybook/addon-links', + '@storybook/addon-essentials', + '@storybook/addon-interactions', + '@storybook/addon-mdx-gfm', + 'storybook-dark-mode', + ], + framework: { + name: '@storybook/react-vite', + options: {}, + }, + features: { + storyStoreV7: true, + }, + docs: { + autodocs: true, + }, + async viteFinal(config, _options) { + return mergeConfig(config, { + plugins: [ + vanillaExtractPlugin(), + tsconfigPaths({ + root: fileURLToPath(new URL('../../../../', import.meta.url)), + ignoreConfigErrors: true, + }), + ], + define: { + 'process.on': '(() => void 0)', + 'process.env': {}, + 'process.env.COVERAGE': JSON.stringify(!!process.env.COVERAGE), + 'process.env.SHOULD_REPORT_TRACE': `${Boolean( + process.env.SHOULD_REPORT_TRACE === 'true' + )}`, + 'process.env.TRACE_REPORT_ENDPOINT': `"${process.env.TRACE_REPORT_ENDPOINT}"`, + 'process.env.CAPTCHA_SITE_KEY': `"${process.env.CAPTCHA_SITE_KEY}"`, + runtimeConfig: getRuntimeConfig({ + distribution: 'browser', + mode: 'development', + channel: 'canary', + coverage: false, + }), + }, + }); + }, +} satisfies StorybookConfig; diff --git a/packages/frontend/component/.storybook/preview.css.ts b/packages/frontend/component/.storybook/preview.css.ts new file mode 100644 index 0000000000..08ed839414 --- /dev/null +++ b/packages/frontend/component/.storybook/preview.css.ts @@ -0,0 +1,27 @@ +import { darkCssVariables, lightCssVariables } from '@toeverything/theme'; +import { globalStyle } from '@vanilla-extract/css'; + +globalStyle('*', { + margin: 0, + padding: 0, +}); + +globalStyle('body', { + color: 'var(--affine-text-primary-color)', + fontFamily: 'var(--affine-font-family)', + fontSize: 'var(--affine-font-base)', + lineHeight: 'var(--affine-font-height)', + backgroundColor: 'var(--affine-background-primary-color)', +}); + +globalStyle('html', { + vars: lightCssVariables, +}); + +globalStyle('html[data-theme="dark"]', { + vars: darkCssVariables, +}); + +globalStyle('.docs-story', { + backgroundColor: 'var(--affine-background-primary-color)', +}); diff --git a/packages/frontend/component/.storybook/preview.html b/packages/frontend/component/.storybook/preview.html new file mode 100644 index 0000000000..e551040105 --- /dev/null +++ b/packages/frontend/component/.storybook/preview.html @@ -0,0 +1,3 @@ + diff --git a/packages/frontend/component/.storybook/preview.tsx b/packages/frontend/component/.storybook/preview.tsx new file mode 100644 index 0000000000..942413a879 --- /dev/null +++ b/packages/frontend/component/.storybook/preview.tsx @@ -0,0 +1,60 @@ +import './preview.css'; +import { ThemeProvider, useTheme } from 'next-themes'; +import type { ComponentType } from 'react'; +import { useEffect } from 'react'; +import { useDarkMode } from 'storybook-dark-mode'; + +import type { Preview } from '@storybook/react'; +import React from 'react'; + +export const parameters: Preview = { + argTypes: { + param: { + table: { category: 'Group' }, + }, + }, + globalTypes: { + theme: { + description: 'Global theme for components', + defaultValue: 'light', + toolbar: { + title: 'Theme', + icon: 'circlehollow', + items: ['light', 'dark'], + dynamicTitle: true, + }, + }, + }, +}; + +const ThemeChange = () => { + const isDark = useDarkMode(); + const theme = useTheme(); + if (theme.resolvedTheme === 'dark' && !isDark) { + theme.setTheme('light'); + } else if (theme.resolvedTheme === 'light' && isDark) { + theme.setTheme('dark'); + } + return null; +}; + +const Component = () => { + const isDark = useDarkMode(); + const theme = useTheme(); + useEffect(() => { + theme.setTheme(isDark ? 'dark' : 'light'); + }, [isDark]); + return null; +}; + +export const decorators = [ + (Story: ComponentType, context) => { + return ( + + + + + + ); + }, +]; diff --git a/packages/frontend/component/package.json b/packages/frontend/component/package.json index f691a46287..83fe196a97 100644 --- a/packages/frontend/component/package.json +++ b/packages/frontend/component/package.json @@ -5,13 +5,17 @@ "exports": { ".": "./src/index.ts", "./theme/*": "./src/theme/*", + "./ui/*": "./src/ui/*/index.ts", "./*": "./src/components/*/index.tsx" }, + "scripts": { + "dev": "storybook dev -p 6006" + }, "peerDependencies": { "@blocksuite/blocks": "*", - "@blocksuite/editor": "*", "@blocksuite/global": "*", "@blocksuite/icons": "2.1.34", + "@blocksuite/presets": "*", "@blocksuite/store": "*" }, "dependencies": { @@ -20,7 +24,7 @@ "@affine/i18n": "workspace:*", "@affine/workspace": "workspace:*", "@dnd-kit/core": "^6.0.8", - "@dnd-kit/modifiers": "^6.0.1", + "@dnd-kit/modifiers": "^7.0.0", "@dnd-kit/sortable": "^8.0.0", "@emotion/cache": "^11.11.0", "@emotion/react": "^11.11.1", @@ -29,11 +33,14 @@ "@popperjs/core": "^2.11.8", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-collapsible": "^1.0.3", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-radio-group": "^1.1.3", "@radix-ui/react-scroll-area": "^1.0.5", "@radix-ui/react-toast": "^1.1.5", "@radix-ui/react-toolbar": "^1.0.4", + "@radix-ui/react-tooltip": "^1.0.7", "@toeverything/hooks": "workspace:*", "@toeverything/infra": "workspace:*", "@toeverything/theme": "^0.7.24", @@ -64,13 +71,24 @@ "uuid": "^9.0.1" }, "devDependencies": { - "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/blocks": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/global": "0.11.0-nightly-202312070955-2b5bb47", "@blocksuite/icons": "2.1.36", - "@blocksuite/lit": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/lit": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/presets": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/store": "0.11.0-nightly-202312070955-2b5bb47", + "@storybook/addon-actions": "^7.5.3", + "@storybook/addon-essentials": "^7.5.3", + "@storybook/addon-interactions": "^7.5.3", + "@storybook/addon-links": "^7.5.3", + "@storybook/addon-mdx-gfm": "^7.5.3", + "@storybook/addon-storysource": "^7.5.3", + "@storybook/blocks": "^7.5.3", + "@storybook/builder-vite": "^7.5.3", "@storybook/jest": "^0.2.3", + "@storybook/react": "^7.5.3", + "@storybook/react-vite": "^7.5.3", + "@storybook/test-runner": "^0.15.2", "@storybook/testing-library": "^0.2.2", "@testing-library/react": "^14.0.0", "@types/bytes": "^3.1.3", @@ -80,8 +98,10 @@ "@types/react-dom": "^18.2.13", "@vanilla-extract/css": "^1.13.0", "fake-indexeddb": "^5.0.0", + "storybook": "^7.5.3", + "storybook-dark-mode": "^3.0.1", "typescript": "^5.3.2", - "vite": "^4.4.11", + "vite": "^5.0.6", "vitest": "0.34.6", "yjs": "^13.6.10" }, diff --git a/packages/frontend/component/src/components/affine-banner/index.css.ts b/packages/frontend/component/src/components/affine-banner/index.css.ts index 345f9eb30f..b105bdeef1 100644 --- a/packages/frontend/component/src/components/affine-banner/index.css.ts +++ b/packages/frontend/component/src/components/affine-banner/index.css.ts @@ -40,6 +40,11 @@ export const tipsContainer = style({ position: 'sticky', gap: '16px', containerType: 'inline-size', + '@media': { + 'screen and (max-width: 520px)': { + flexWrap: 'wrap', + }, + }, }); export const tipsMessage = style({ @@ -54,4 +59,9 @@ export const tipsRightItem = style({ justifyContent: 'space-between', alignItems: 'center', gap: '16px', + '@media': { + 'screen and (max-width: 520px)': { + width: '100%', + }, + }, }); diff --git a/packages/frontend/component/src/components/affine-banner/local-demo-tips.tsx b/packages/frontend/component/src/components/affine-banner/local-demo-tips.tsx index 3a9eea2ef2..770ab3340a 100644 --- a/packages/frontend/component/src/components/affine-banner/local-demo-tips.tsx +++ b/packages/frontend/component/src/components/affine-banner/local-demo-tips.tsx @@ -1,5 +1,6 @@ +import { Button, IconButton } from '@affine/component/ui/button'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { CloseIcon } from '@blocksuite/icons'; -import { Button, IconButton } from '@toeverything/components/button'; import { useCallback } from 'react'; import * as styles from './index.css'; @@ -17,13 +18,10 @@ export const LocalDemoTips = ({ onLogin, onEnableCloud, }: LocalDemoTipsProps) => { - const content = isLoggedIn - ? 'This is a local demo workspace, and the data is stored locally. We recommend enabling AFFiNE Cloud.' - : 'This is a local demo workspace, and the data is stored locally in the browser. We recommend Enabling AFFiNE Cloud or downloading the client for a better experience.'; - + const t = useAFFiNEI18N(); const buttonLabel = isLoggedIn - ? 'Enable AFFiNE Cloud' - : 'Sign in with AFFiNE Cloud'; + ? t['Enable AFFiNE Cloud']() + : t['Sign in and Enable'](); const handleClick = useCallback(() => { if (isLoggedIn) { @@ -34,7 +32,9 @@ export const LocalDemoTips = ({ return (
-
{content}
+
+ {t['com.affine.banner.local-warning']()} +
diff --git a/packages/frontend/component/src/components/affine-other-page-layout/desktop-navbar.tsx b/packages/frontend/component/src/components/affine-other-page-layout/desktop-navbar.tsx new file mode 100644 index 0000000000..5b8aeb860b --- /dev/null +++ b/packages/frontend/component/src/components/affine-other-page-layout/desktop-navbar.tsx @@ -0,0 +1,24 @@ +import * as styles from './index.css'; +import { useNavConfig } from './use-nav-config'; + +export const DesktopNavbar = () => { + const config = useNavConfig(); + + return ( +
+ {config.map(item => { + return ( + + {item.title} + + ); + })} +
+ ); +}; diff --git a/packages/frontend/component/src/components/affine-other-page-layout/index.css.ts b/packages/frontend/component/src/components/affine-other-page-layout/index.css.ts new file mode 100644 index 0000000000..0910728906 --- /dev/null +++ b/packages/frontend/component/src/components/affine-other-page-layout/index.css.ts @@ -0,0 +1,89 @@ +import { style } from '@vanilla-extract/css'; + +export const root = style({ + height: '100vh', + width: '100vw', + display: 'flex', + flexDirection: 'column', + fontSize: 'var(--affine-font-base)', + position: 'relative', + background: 'var(--affine-background-primary-color)', +}); + +export const affineLogo = style({ + color: 'inherit', +}); + +export const topNav = style({ + top: 0, + left: 0, + right: 0, + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + padding: '16px 120px', + selectors: { + '&.mobile': { + padding: '16px 20px', + }, + }, +}); + +export const topNavLinks = style({ + display: 'flex', + columnGap: 4, +}); + +export const topNavLink = style({ + color: 'var(--affine-text-primary-color)', + fontSize: 'var(--affine-font-sm)', + fontWeight: 500, + textDecoration: 'none', + padding: '4px 18px', +}); + +export const iconButton = style({ + fontSize: '24px', + pointerEvents: 'auto', + selectors: { + '&.plain': { + color: 'var(--affine-text-primary-color)', + }, + }, +}); + +export const menu = style({ + width: '100vw', + height: '100vh', + padding: '0', + background: 'var(--affine-background-primary-color)', + borderRadius: '0', + border: 'none', + boxShadow: 'none', +}); + +export const menuItem = style({ + color: 'var(--affine-text-primary-color)', + fontSize: 'var(--affine-font-sm)', + fontWeight: 500, + textDecoration: 'none', + padding: '12px 20px', + maxWidth: '100%', + position: 'relative', + borderRadius: '0', + transition: 'background 0.3s ease', + selectors: { + '&:after': { + position: 'absolute', + content: '""', + bottom: 0, + display: 'block', + width: 'calc(100% - 40px)', + height: '0.5px', + background: 'var(--affine-black-10)', + }, + '&:not(:last-of-type)': { + marginBottom: '0', + }, + }, +}); diff --git a/packages/frontend/component/src/components/affine-other-page-layout/index.tsx b/packages/frontend/component/src/components/affine-other-page-layout/index.tsx new file mode 100644 index 0000000000..5d15fe1b3c --- /dev/null +++ b/packages/frontend/component/src/components/affine-other-page-layout/index.tsx @@ -0,0 +1 @@ +export * from './layout'; diff --git a/packages/frontend/component/src/components/affine-other-page-layout/layout.tsx b/packages/frontend/component/src/components/affine-other-page-layout/layout.tsx new file mode 100644 index 0000000000..c58891324c --- /dev/null +++ b/packages/frontend/component/src/components/affine-other-page-layout/layout.tsx @@ -0,0 +1,49 @@ +import { Button } from '@affine/component/ui/button'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { Logo1Icon } from '@blocksuite/icons'; +import clsx from 'clsx'; +import { useCallback } from 'react'; + +import { DesktopNavbar } from './desktop-navbar'; +import * as styles from './index.css'; +import { MobileNavbar } from './mobile-navbar'; + +export const AffineOtherPageLayout = ({ + isSmallScreen, + children, +}: { + isSmallScreen: boolean; + children: React.ReactNode; +}) => { + const t = useAFFiNEI18N(); + + const openDownloadLink = useCallback(() => { + open(runtimeConfig.downloadUrl, '_blank'); + }, []); + + return ( +
+
+ + + + {isSmallScreen ? ( + + ) : ( + <> + + + + )} +
+ + {children} +
+ ); +}; diff --git a/packages/frontend/component/src/components/affine-other-page-layout/mobile-navbar.tsx b/packages/frontend/component/src/components/affine-other-page-layout/mobile-navbar.tsx new file mode 100644 index 0000000000..1d544b27fd --- /dev/null +++ b/packages/frontend/component/src/components/affine-other-page-layout/mobile-navbar.tsx @@ -0,0 +1,50 @@ +import { IconButton } from '@affine/component/ui/button'; +import { Menu, MenuItem } from '@affine/component/ui/menu'; +import { CloseIcon, PropertyIcon } from '@blocksuite/icons'; +import { useState } from 'react'; + +import * as styles from './index.css'; +import { useNavConfig } from './use-nav-config'; + +export const MobileNavbar = () => { + const [openMenu, setOpenMenu] = useState(false); + const navConfig = useNavConfig(); + + const menuItems = ( + <> + {navConfig.map(item => { + return ( + { + open(item.path, '_blank'); + }} + className={styles.menuItem} + > + {item.title} + + ); + })} + + ); + + return ( +
+ + + {openMenu ? : } + + +
+ ); +}; diff --git a/packages/frontend/component/src/components/affine-other-page-layout/use-nav-config.ts b/packages/frontend/component/src/components/affine-other-page-layout/use-nav-config.ts new file mode 100644 index 0000000000..379da19bf7 --- /dev/null +++ b/packages/frontend/component/src/components/affine-other-page-layout/use-nav-config.ts @@ -0,0 +1,27 @@ +import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { useMemo } from 'react'; + +export const useNavConfig = () => { + const t = useAFFiNEI18N(); + return useMemo( + () => [ + { + title: t['com.affine.other-page.nav.official-website'](), + path: 'https://affine.pro', + }, + { + title: t['com.affine.other-page.nav.affine-community'](), + path: 'https://community.affine.pro/home', + }, + { + title: t['com.affine.other-page.nav.blog'](), + path: 'https://affine.pro/blog', + }, + { + title: t['com.affine.other-page.nav.contact-us'](), + path: 'https://affine.pro/about-us', + }, + ], + [t] + ); +}; diff --git a/packages/frontend/component/src/components/app-sidebar/app-updater-button/index.css.ts b/packages/frontend/component/src/components/app-sidebar/app-updater-button/index.css.ts index a2d56ad2ed..8e1aa32d92 100644 --- a/packages/frontend/component/src/components/app-sidebar/app-updater-button/index.css.ts +++ b/packages/frontend/component/src/components/app-sidebar/app-updater-button/index.css.ts @@ -85,12 +85,12 @@ export const installLabel = style({ flex: 1, fontSize: 'var(--affine-font-sm)', whiteSpace: 'nowrap', + justifyContent: 'space-between', }); export const installLabelNormal = style([ installLabel, { - justifyContent: 'space-between', selectors: { [`${root}:hover &, ${root}[data-updating=true] &`]: { display: 'none', @@ -103,6 +103,7 @@ export const installLabelHover = style([ installLabel, { display: 'none', + justifyContent: 'flex-start', selectors: { [`${root}:hover &, ${root}[data-updating=true] &`]: { display: 'flex', diff --git a/packages/frontend/component/src/components/app-sidebar/app-updater-button/index.tsx b/packages/frontend/component/src/components/app-sidebar/app-updater-button/index.tsx index 4d7a442c81..bf9025d78f 100644 --- a/packages/frontend/component/src/components/app-sidebar/app-updater-button/index.tsx +++ b/packages/frontend/component/src/components/app-sidebar/app-updater-button/index.tsx @@ -1,20 +1,11 @@ import { Unreachable } from '@affine/env/constant'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { CloseIcon, NewIcon, ResetIcon } from '@blocksuite/icons'; -import { Tooltip } from '@toeverything/components/tooltip'; -import { - changelogCheckedAtom, - currentChangelogUnreadAtom, - currentVersionAtom, - downloadProgressAtom, - updateAvailableAtom, - updateReadyAtom, - useAppUpdater, -} from '@toeverything/hooks/use-app-updater'; +import { useAppUpdater } from '@toeverything/hooks/use-app-updater'; import clsx from 'clsx'; -import { useAtomValue, useSetAtom } from 'jotai'; -import { startTransition, useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; +import { Tooltip } from '../../../ui/tooltip'; import * as styles from './index.css'; export interface AddPageButtonPureProps { @@ -26,36 +17,178 @@ export interface AddPageButtonPureProps { version: string; allowAutoUpdate: boolean; } | null; + autoDownload: boolean; downloadProgress: number | null; appQuitting: boolean; className?: string; style?: React.CSSProperties; } +interface ButtonContentProps { + updateReady: boolean; + updateAvailable: { + version: string; + allowAutoUpdate: boolean; + } | null; + autoDownload: boolean; + downloadProgress: number | null; + appQuitting: boolean; + currentChangelogUnread: boolean; + onDismissCurrentChangelog: () => void; +} + +function DownloadUpdate({ updateAvailable }: ButtonContentProps) { + const t = useAFFiNEI18N(); + return ( +
+ + {t['com.affine.appUpdater.downloadUpdate']()} + + {updateAvailable?.version} +
+ ); +} + +function UpdateReady({ updateAvailable, appQuitting }: ButtonContentProps) { + const t = useAFFiNEI18N(); + return ( +
+
+ + {t['com.affine.appUpdater.updateAvailable']()} + + {updateAvailable?.version} +
+ +
+ + + {t[appQuitting ? 'Loading' : 'com.affine.appUpdater.installUpdate']()} + +
+
+ ); +} + +function DownloadingUpdate({ + updateAvailable, + downloadProgress, +}: ButtonContentProps) { + const t = useAFFiNEI18N(); + return ( +
+
+ + {t['com.affine.appUpdater.downloading']()} + + {updateAvailable?.version} +
+ +
+
+
+
+ ); +} + +function OpenDownloadPage({ updateAvailable }: ButtonContentProps) { + const t = useAFFiNEI18N(); + return ( + <> +
+ + {t['com.affine.appUpdater.updateAvailable']()} + + {updateAvailable?.version} +
+ +
+ + {t['com.affine.appUpdater.openDownloadPage']()} + +
+ + ); +} + +function WhatsNew({ onDismissCurrentChangelog }: ButtonContentProps) { + const t = useAFFiNEI18N(); + const onClickClose: React.MouseEventHandler = useCallback( + e => { + onDismissCurrentChangelog(); + e.stopPropagation(); + }, + [onDismissCurrentChangelog] + ); + return ( + <> +
+ + + {t['com.affine.appUpdater.whatsNew']()} + +
+
+ +
+ + ); +} + +const getButtonContentRenderer = (props: ButtonContentProps) => { + if (props.updateReady) { + return UpdateReady; + } else if (props.updateAvailable?.allowAutoUpdate) { + if (props.autoDownload && props.updateAvailable.allowAutoUpdate) { + return DownloadingUpdate; + } else { + return DownloadUpdate; + } + } else if (props.updateAvailable && !props.updateAvailable?.allowAutoUpdate) { + return OpenDownloadPage; + } else if (props.currentChangelogUnread) { + return WhatsNew; + } + return null; +}; + export function AppUpdaterButtonPure({ updateReady, onClickUpdate, onDismissCurrentChangelog, currentChangelogUnread, updateAvailable, + autoDownload, downloadProgress, appQuitting, className, style, }: AddPageButtonPureProps) { - const t = useAFFiNEI18N(); + const contentProps = useMemo( + () => ({ + updateReady, + updateAvailable, + currentChangelogUnread, + autoDownload, + downloadProgress, + appQuitting, + onDismissCurrentChangelog, + }), + [ + updateReady, + updateAvailable, + currentChangelogUnread, + autoDownload, + downloadProgress, + appQuitting, + onDismissCurrentChangelog, + ] + ); - if (!updateAvailable && !currentChangelogUnread) { - return null; - } - - const updateAvailableNode = updateAvailable - ? updateAvailable.allowAutoUpdate - ? renderUpdateAvailableAllowAutoUpdate() - : renderUpdateAvailableNotAllowAutoUpdate() - : null; - const whatsNew = - !updateAvailable && currentChangelogUnread ? renderWhatsNew() : null; + const ContentComponent = getButtonContentRenderer(contentProps); const wrapWithTooltip = ( node: React.ReactElement, @@ -72,102 +205,38 @@ export function AppUpdaterButtonPure({ ); }; + const disabled = useMemo(() => { + if (appQuitting) { + return true; + } + + if (updateAvailable?.allowAutoUpdate) { + return !updateReady && autoDownload; + } + + return false; + }, [ + appQuitting, + autoDownload, + updateAvailable?.allowAutoUpdate, + updateReady, + ]); + return wrapWithTooltip( , updateAvailable?.version ); - - function renderUpdateAvailableAllowAutoUpdate() { - return ( -
-
- - {!updateReady - ? t['com.affine.appUpdater.downloading']() - : t['com.affine.appUpdater.updateAvailable']()} - - - {updateAvailable?.version} - -
- - {updateReady ? ( -
- - - {t[ - appQuitting ? 'Loading' : 'com.affine.appUpdater.installUpdate' - ]()} - -
- ) : ( -
-
-
- )} -
- ); - } - - function renderUpdateAvailableNotAllowAutoUpdate() { - return ( - <> -
- - {t['com.affine.appUpdater.updateAvailable']()} - - - {updateAvailable?.version} - -
- -
- - {t['com.affine.appUpdater.openDownloadPage']()} - -
- - ); - } - - function renderWhatsNew() { - return ( - <> -
- - - {t['com.affine.appUpdater.whatsNew']()} - -
-
{ - onDismissCurrentChangelog(); - e.stopPropagation(); - }} - > - -
- - ); - } } // Although it is called an input, it is actually a button. @@ -178,62 +247,64 @@ export function AppUpdaterButton({ className?: string; style?: React.CSSProperties; }) { - const currentChangelogUnread = useAtomValue(currentChangelogUnreadAtom); - const updateReady = useAtomValue(updateReadyAtom); - const updateAvailable = useAtomValue(updateAvailableAtom); - const downloadProgress = useAtomValue(downloadProgressAtom); - const currentVersion = useAtomValue(currentVersionAtom); - const { quitAndInstall, appQuitting } = useAppUpdater(); - const setChangelogCheckAtom = useSetAtom(changelogCheckedAtom); - - const dismissCurrentChangelog = useCallback(() => { - if (!currentVersion) { - return; - } - startTransition(() => - setChangelogCheckAtom(mapping => { - return { - ...mapping, - [currentVersion]: true, - }; - }) - ); - }, [currentVersion, setChangelogCheckAtom]); + const { + quitAndInstall, + appQuitting, + autoDownload, + downloadUpdate, + readChangelog, + changelogUnread, + updateReady, + updateAvailable, + downloadProgress, + currentVersion, + } = useAppUpdater(); const handleClickUpdate = useCallback(() => { if (updateReady) { quitAndInstall(); } else if (updateAvailable) { if (updateAvailable.allowAutoUpdate) { - // wait for download to finish + if (autoDownload) { + // wait for download to finish + } else { + downloadUpdate(); + } } else { window.open( `https://github.com/toeverything/AFFiNE/releases/tag/v${currentVersion}`, '_blank' ); } - } else if (currentChangelogUnread) { + } else if (changelogUnread) { window.open(runtimeConfig.changelogUrl, '_blank'); - dismissCurrentChangelog(); + readChangelog(); } else { throw new Unreachable(); } }, [ updateReady, - quitAndInstall, updateAvailable, - currentChangelogUnread, - dismissCurrentChangelog, + changelogUnread, + quitAndInstall, + autoDownload, + downloadUpdate, currentVersion, + readChangelog, ]); + if (!updateAvailable && !changelogUnread) { + return null; + } + return ( = ({ }) => { return (
{label ? : null} { @@ -40,7 +41,7 @@ export const AuthInput: FC = ({ /> {error && errorHint && !withoutHint ? (
diff --git a/packages/frontend/component/src/components/auth-components/auth-page-container.tsx b/packages/frontend/component/src/components/auth-components/auth-page-container.tsx index 10f6d29ba6..5cea173e53 100644 --- a/packages/frontend/component/src/components/auth-components/auth-page-container.tsx +++ b/packages/frontend/component/src/components/auth-components/auth-page-container.tsx @@ -1,32 +1,41 @@ -import type { FC, PropsWithChildren, ReactNode } from 'react'; +import { + type FC, + type PropsWithChildren, + type ReactNode, + useEffect, + useState, +} from 'react'; import { Empty } from '../../ui/empty'; -import { Wrapper } from '../../ui/layout'; -import { Logo } from './logo'; +import { AffineOtherPageLayout } from '../affine-other-page-layout'; import { authPageContainer } from './share.css'; export const AuthPageContainer: FC< PropsWithChildren<{ title?: ReactNode; subtitle?: ReactNode }> > = ({ children, title, subtitle }) => { + const [isSmallScreen, setIsSmallScreen] = useState(false); + + useEffect(() => { + const checkScreenSize = () => { + setIsSmallScreen(window.innerWidth <= 1024); + }; + checkScreenSize(); + window.addEventListener('resize', checkScreenSize); + return () => window.removeEventListener('resize', checkScreenSize); + }, []); + return ( -
- - - -
-
-

{title}

-

{subtitle}

- {children} + +
+
+
+

{title}

+

{subtitle}

+ {children} +
+ {isSmallScreen ? null : }
-
-
+ ); }; diff --git a/packages/frontend/component/src/components/auth-components/back-button.tsx b/packages/frontend/component/src/components/auth-components/back-button.tsx index a63d417902..1291160176 100644 --- a/packages/frontend/component/src/components/auth-components/back-button.tsx +++ b/packages/frontend/component/src/components/auth-components/back-button.tsx @@ -1,8 +1,9 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { ArrowLeftSmallIcon } from '@blocksuite/icons'; -import { Button, type ButtonProps } from '@toeverything/components/button'; import { type FC } from 'react'; +import { Button, type ButtonProps } from '../../ui/button'; + export const BackButton: FC = props => { const t = useAFFiNEI18N(); return ( diff --git a/packages/frontend/component/src/components/auth-components/change-email-page.tsx b/packages/frontend/component/src/components/auth-components/change-email-page.tsx index c39ccb7b3c..c7b6291eb4 100644 --- a/packages/frontend/component/src/components/auth-components/change-email-page.tsx +++ b/packages/frontend/component/src/components/auth-components/change-email-page.tsx @@ -1,7 +1,7 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Button } from '@toeverything/components/button'; import { useCallback, useState } from 'react'; +import { Button } from '../../ui/button'; import { AuthInput } from './auth-input'; import { AuthPageContainer } from './auth-page-container'; import { emailRegex } from './utils'; @@ -44,7 +44,6 @@ export const ChangeEmailPage = ({ > <> void; diff --git a/packages/frontend/component/src/components/auth-components/password-input/index.tsx b/packages/frontend/component/src/components/auth-components/password-input/index.tsx index 5d2f612061..354716f81c 100644 --- a/packages/frontend/component/src/components/auth-components/password-input/index.tsx +++ b/packages/frontend/component/src/components/auth-components/password-input/index.tsx @@ -4,6 +4,7 @@ import { type FC, useEffect } from 'react'; import { useCallback, useState } from 'react'; import { Input, type InputProps } from '../../../ui/input'; +import * as styles from '../share.css'; import { ErrorIcon } from './error'; import { SuccessIcon } from './success'; import { Tag } from './tag'; @@ -74,6 +75,7 @@ export const PasswordInput: FC< return ( <> { setPasswordPass(true); passwordRef.current = password; diff --git a/packages/frontend/component/src/components/auth-components/share.css.ts b/packages/frontend/component/src/components/auth-components/share.css.ts index 0f5d8bcc5d..a1fde37f4d 100644 --- a/packages/frontend/component/src/components/auth-components/share.css.ts +++ b/packages/frontend/component/src/components/auth-components/share.css.ts @@ -151,14 +151,25 @@ export const authPageContainer = style({ justifyContent: 'center', alignItems: 'center', fontSize: 'var(--affine-font-base)', + '@media': { + 'screen and (max-width: 1024px)': { + flexDirection: 'column', + padding: '100px 20px', + justifyContent: 'flex-start', + }, + }, }); globalStyle(`${authPageContainer} .wrapper`, { display: 'flex', alignItems: 'center', + '@media': { + 'screen and (max-width: 1024px)': { + flexDirection: 'column', + }, + }, }); globalStyle(`${authPageContainer} .content`, { maxWidth: '700px', - minWidth: '550px', }); globalStyle(`${authPageContainer} .title`, { @@ -178,3 +189,12 @@ export const signInPageContainer = style({ width: '400px', margin: '205px auto 0', }); + +export const input = style({ + width: '330px', + '@media': { + 'screen and (max-width: 520px)': { + width: '100%', + }, + }, +}); diff --git a/packages/frontend/component/src/components/auth-components/sign-in-success-page.tsx b/packages/frontend/component/src/components/auth-components/sign-in-success-page.tsx index 807269d8a1..37b0083e8f 100644 --- a/packages/frontend/component/src/components/auth-components/sign-in-success-page.tsx +++ b/packages/frontend/component/src/components/auth-components/sign-in-success-page.tsx @@ -1,7 +1,7 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Button } from '@toeverything/components/button'; import type { FC } from 'react'; +import { Button } from '../../ui/button'; import { AuthPageContainer } from './auth-page-container'; export const SignInSuccessPage: FC<{ diff --git a/packages/frontend/component/src/components/auth-components/sign-up-page.tsx b/packages/frontend/component/src/components/auth-components/sign-up-page.tsx index 7fb6eba910..684b6cdd9e 100644 --- a/packages/frontend/component/src/components/auth-components/sign-up-page.tsx +++ b/packages/frontend/component/src/components/auth-components/sign-up-page.tsx @@ -1,9 +1,9 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Button } from '@toeverything/components/button'; import { useSetAtom } from 'jotai'; import type { FC } from 'react'; import { useCallback, useState } from 'react'; +import { Button } from '../../ui/button'; import { pushNotificationAtom } from '../notification-center'; import { AuthPageContainer } from './auth-page-container'; import { SetPassword } from './set-password'; diff --git a/packages/frontend/component/src/components/block-hub/index.tsx b/packages/frontend/component/src/components/block-hub/index.tsx deleted file mode 100644 index a6f27b9937..0000000000 --- a/packages/frontend/component/src/components/block-hub/index.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { rootBlockHubAtom } from '@affine/workspace/atom'; -import { useAtomValue } from 'jotai'; -import { useEffect, useRef } from 'react'; - -export const RootBlockHub = () => { - const ref = useRef(null); - const blockHub = useAtomValue(rootBlockHubAtom); - useEffect(() => { - if (ref.current) { - const div = ref.current; - if (blockHub) { - if (div.hasChildNodes()) { - (div.firstChild as ChildNode).remove(); - } - div.append(blockHub); - } - } - }, [blockHub]); - return
; -}; diff --git a/packages/frontend/component/src/components/block-suite-editor/index.tsx b/packages/frontend/component/src/components/block-suite-editor/index.tsx index 41a4b26c51..e0f0a0b1d5 100644 --- a/packages/frontend/component/src/components/block-suite-editor/index.tsx +++ b/packages/frontend/component/src/components/block-suite-editor/index.tsx @@ -1,5 +1,5 @@ -import { EditorContainer } from '@blocksuite/editor'; import { assertExists } from '@blocksuite/global/utils'; +import { EditorContainer } from '@blocksuite/presets'; import type { Page } from '@blocksuite/store'; import clsx from 'clsx'; import { use } from 'foxact/use'; diff --git a/packages/frontend/component/src/components/card/workspace-card/index.tsx b/packages/frontend/component/src/components/card/workspace-card/index.tsx index 3df908a3f9..a8d6049a9c 100644 --- a/packages/frontend/component/src/components/card/workspace-card/index.tsx +++ b/packages/frontend/component/src/components/card/workspace-card/index.tsx @@ -2,16 +2,16 @@ import { WorkspaceFlavour } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import type { RootWorkspaceMetadata } from '@affine/workspace/atom'; import { CollaborationIcon, SettingsIcon } from '@blocksuite/icons'; -import { Avatar } from '@toeverything/components/avatar'; -import { Divider } from '@toeverything/components/divider'; -import { Tooltip } from '@toeverything/components/tooltip'; import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-block-suite-workspace-avatar-url'; import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name'; import { getBlockSuiteWorkspaceAtom } from '@toeverything/infra/__internal__/workspace'; import { useAtomValue } from 'jotai/react'; import { useCallback } from 'react'; +import { Avatar } from '../../../ui/avatar'; +import { Divider } from '../../../ui/divider'; import { Skeleton } from '../../../ui/skeleton'; +import { Tooltip } from '../../../ui/tooltip'; import { StyledCard, StyledIconContainer, diff --git a/packages/frontend/component/src/components/card/workspace-card/styles.ts b/packages/frontend/component/src/components/card/workspace-card/styles.ts index bd1d645003..46d48c2de2 100644 --- a/packages/frontend/component/src/components/card/workspace-card/styles.ts +++ b/packages/frontend/component/src/components/card/workspace-card/styles.ts @@ -1,6 +1,5 @@ -import { IconButton } from '@toeverything/components/button'; - import { displayFlex, styled, textEllipsis } from '../../../styles'; +import { IconButton } from '../../../ui/button'; export const StyledWorkspaceInfo = styled('div')(() => { return { diff --git a/packages/frontend/component/src/components/disable-public-link/index.tsx b/packages/frontend/component/src/components/disable-public-link/index.tsx index 5402eff74c..0b4868f828 100644 --- a/packages/frontend/component/src/components/disable-public-link/index.tsx +++ b/packages/frontend/component/src/components/disable-public-link/index.tsx @@ -1,8 +1,6 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { - ConfirmModal, - type ConfirmModalProps, -} from '@toeverything/components/modal'; + +import { ConfirmModal, type ConfirmModalProps } from '../../ui/modal'; export const PublicLinkDisableModal = (props: ConfirmModalProps) => { const t = useAFFiNEI18N(); diff --git a/packages/frontend/component/src/components/import-page/index.tsx b/packages/frontend/component/src/components/import-page/index.tsx index 66563ba8da..53784b1af7 100644 --- a/packages/frontend/component/src/components/import-page/index.tsx +++ b/packages/frontend/component/src/components/import-page/index.tsx @@ -6,9 +6,9 @@ import { NewIcon, NotionIcon, } from '@blocksuite/icons'; -import { IconButton } from '@toeverything/components/button'; -import { Tooltip } from '@toeverything/components/tooltip'; +import { IconButton } from '../../ui/button'; +import { Tooltip } from '../../ui/tooltip'; import { BlockCard } from '../card/block-card'; import { importPageBodyStyle, diff --git a/packages/frontend/component/src/components/member-components/accept-invite-page.tsx b/packages/frontend/component/src/components/member-components/accept-invite-page.tsx index 574a19a8ec..03cae33dae 100644 --- a/packages/frontend/component/src/components/member-components/accept-invite-page.tsx +++ b/packages/frontend/component/src/components/member-components/accept-invite-page.tsx @@ -1,9 +1,9 @@ import { AuthPageContainer } from '@affine/component/auth-components'; import { type GetInviteInfoQuery } from '@affine/graphql'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Avatar } from '@toeverything/components/avatar'; -import { Button } from '@toeverything/components/button'; +import { Avatar } from '../../ui/avatar'; +import { Button } from '../../ui/button'; import { FlexWrapper } from '../../ui/layout'; import * as styles from './styles.css'; export const AcceptInvitePage = ({ diff --git a/packages/frontend/component/src/components/member-components/invite-modal.tsx b/packages/frontend/component/src/components/member-components/invite-modal.tsx index 9371a50a78..920671319b 100644 --- a/packages/frontend/component/src/components/member-components/invite-modal.tsx +++ b/packages/frontend/component/src/components/member-components/invite-modal.tsx @@ -1,8 +1,8 @@ import { Permission } from '@affine/graphql'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { ConfirmModal } from '@toeverything/components/modal'; import { useCallback, useEffect, useState } from 'react'; +import { ConfirmModal } from '../../ui/modal'; import { AuthInput } from '..//auth-components'; import { emailRegex } from '..//auth-components/utils'; diff --git a/packages/frontend/component/src/components/not-found-page/not-found-page.tsx b/packages/frontend/component/src/components/not-found-page/not-found-page.tsx index fe3aa2cc00..aad290e815 100644 --- a/packages/frontend/component/src/components/not-found-page/not-found-page.tsx +++ b/packages/frontend/component/src/components/not-found-page/not-found-page.tsx @@ -1,9 +1,9 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { SignOutIcon } from '@blocksuite/icons'; -import { Avatar } from '@toeverything/components/avatar'; -import { Button, IconButton } from '@toeverything/components/button'; -import { Tooltip } from '@toeverything/components/tooltip'; +import { Avatar } from '../../ui/avatar'; +import { Button, IconButton } from '../../ui/button'; +import { Tooltip } from '../../ui/tooltip'; import { NotFoundPattern } from './not-found-pattern'; import { largeButtonEffect, diff --git a/packages/frontend/component/src/components/not-found-page/styles.css.ts b/packages/frontend/component/src/components/not-found-page/styles.css.ts index c1f4467c34..7ecf6cb4ac 100644 --- a/packages/frontend/component/src/components/not-found-page/styles.css.ts +++ b/packages/frontend/component/src/components/not-found-page/styles.css.ts @@ -8,6 +8,7 @@ export const notFoundPageContainer = style({ alignItems: 'center', justifyContent: 'center', width: '100vw', + padding: '0 20px', }); export const wrapper = style({ diff --git a/packages/frontend/component/src/components/notification-center/index.tsx b/packages/frontend/component/src/components/notification-center/index.tsx index 5574b82a80..73870ad836 100644 --- a/packages/frontend/component/src/components/notification-center/index.tsx +++ b/packages/frontend/component/src/components/notification-center/index.tsx @@ -4,7 +4,6 @@ import { CloseIcon, InformationFillDuotoneIcon } from '@blocksuite/icons'; import * as Toast from '@radix-ui/react-toast'; -import { IconButton } from '@toeverything/components/button'; import clsx from 'clsx'; import { useAtom, useAtomValue, useSetAtom } from 'jotai'; import type { ReactNode } from 'react'; @@ -17,6 +16,7 @@ import { useState, } from 'react'; +import { IconButton } from '../../ui/button'; import { SuccessIcon } from './icons'; import * as styles from './index.css'; import type { Notification } from './index.jotai'; diff --git a/packages/frontend/component/src/components/page-list/components/favorite-tag.tsx b/packages/frontend/component/src/components/page-list/components/favorite-tag.tsx index 7256ff2e1e..b035f68113 100644 --- a/packages/frontend/component/src/components/page-list/components/favorite-tag.tsx +++ b/packages/frontend/component/src/components/page-list/components/favorite-tag.tsx @@ -1,13 +1,10 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { FavoritedIcon, FavoriteIcon } from '@blocksuite/icons'; -import { - IconButton, - type IconButtonProps, -} from '@toeverything/components/button'; -import { Tooltip } from '@toeverything/components/tooltip'; import Lottie from 'lottie-react'; import { forwardRef, useCallback, useState } from 'react'; +import { IconButton, type IconButtonProps } from '../../../ui/button'; +import { Tooltip } from '../../../ui/tooltip'; import favoritedAnimation from './favorited-animation/data.json'; export const FavoriteTag = forwardRef< diff --git a/packages/frontend/component/src/components/page-list/components/new-page-buttton.tsx b/packages/frontend/component/src/components/page-list/components/new-page-buttton.tsx index 613277af2e..5267b25b26 100644 --- a/packages/frontend/component/src/components/page-list/components/new-page-buttton.tsx +++ b/packages/frontend/component/src/components/page-list/components/new-page-buttton.tsx @@ -1,9 +1,9 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { EdgelessIcon, ImportIcon, PageIcon } from '@blocksuite/icons'; -import { Menu } from '@toeverything/components/menu'; import { type PropsWithChildren, useCallback, useState } from 'react'; import { DropdownButton } from '../../../ui/button'; +import { Menu } from '../../../ui/menu'; import { BlockCard } from '../../card/block-card'; import { menuContent } from './new-page-button.css'; diff --git a/packages/frontend/component/src/components/page-list/filter/condition.tsx b/packages/frontend/component/src/components/page-list/filter/condition.tsx index 462223c95f..76db433231 100644 --- a/packages/frontend/component/src/components/page-list/filter/condition.tsx +++ b/packages/frontend/component/src/components/page-list/filter/condition.tsx @@ -1,9 +1,9 @@ import type { Filter, Literal } from '@affine/env/filter'; import type { PropertiesMeta } from '@affine/env/filter'; -import { Menu, MenuItem } from '@toeverything/components/menu'; import type { ReactNode } from 'react'; import { useMemo } from 'react'; +import { Menu, MenuItem } from '../../../ui/menu'; import { FilterTag } from './filter-tag-translation'; import * as styles from './index.css'; import { literalMatcher } from './literal-matcher'; diff --git a/packages/frontend/component/src/components/page-list/filter/filter-list.tsx b/packages/frontend/component/src/components/page-list/filter/filter-list.tsx index 3169814ca7..56d8a00783 100644 --- a/packages/frontend/component/src/components/page-list/filter/filter-list.tsx +++ b/packages/frontend/component/src/components/page-list/filter/filter-list.tsx @@ -2,10 +2,10 @@ import type { Filter } from '@affine/env/filter'; import type { PropertiesMeta } from '@affine/env/filter'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { CloseIcon, PlusIcon } from '@blocksuite/icons'; -import { Button } from '@toeverything/components/button'; -import { IconButton } from '@toeverything/components/button'; -import { Menu } from '@toeverything/components/menu'; +import { Button } from '../../../ui/button'; +import { IconButton } from '../../../ui/button'; +import { Menu } from '../../../ui/menu'; import { Condition } from './condition'; import * as styles from './index.css'; import { CreateFilterMenu } from './vars'; diff --git a/packages/frontend/component/src/components/page-list/filter/filter-tag-translation.tsx b/packages/frontend/component/src/components/page-list/filter/filter-tag-translation.tsx index 587ca00d75..1c3d8c27b6 100644 --- a/packages/frontend/component/src/components/page-list/filter/filter-tag-translation.tsx +++ b/packages/frontend/component/src/components/page-list/filter/filter-tag-translation.tsx @@ -19,6 +19,8 @@ const useFilterTag = ({ name }: FilterTagProps) => { return t['com.affine.filter.after'](); case 'before': return t['com.affine.filter.before'](); + case 'last': + return t['com.affine.filter.last'](); case 'is': return t['com.affine.filter.is'](); case 'is not empty': diff --git a/packages/frontend/component/src/components/page-list/filter/literal-matcher.tsx b/packages/frontend/component/src/components/page-list/filter/literal-matcher.tsx index 132fed81d5..6a4a77e1dc 100644 --- a/packages/frontend/component/src/components/page-list/filter/literal-matcher.tsx +++ b/packages/frontend/component/src/components/page-list/filter/literal-matcher.tsx @@ -1,11 +1,13 @@ import type { LiteralValue, Tag } from '@affine/env/filter'; import dayjs from 'dayjs'; -import type { ReactNode } from 'react'; +import { type ReactNode } from 'react'; +import Input from '../../../ui/input'; +import { Menu, MenuItem } from '../../../ui/menu'; import { AFFiNEDatePicker } from '../../date-picker'; import { FilterTag } from './filter-tag-translation'; import { inputStyle } from './index.css'; -import { tBoolean, tDate, tTag } from './logical/custom-type'; +import { tBoolean, tDate, tDateRange, tTag } from './logical/custom-type'; import { Matcher } from './logical/matcher'; import type { TType } from './logical/typesystem'; import { tArray, typesystem } from './logical/typesystem'; @@ -21,6 +23,37 @@ export const literalMatcher = new Matcher<{ return typesystem.isSubtype(type, target); }); +literalMatcher.register(tDateRange.create(), { + render: ({ value, onChange }) => ( + + (i ? onChange(parseInt(i)) : onChange(0))} + /> + {[1, 2, 3, 7, 14, 30].map(i => ( + { + // Handle the menu item click and update the value accordingly + onChange(i); + }} + > + {i} {i > 1 ? 'days' : 'day'} + + ))} +
+ } + > +
+ {value.toString()} {(value as number) > 1 ? 'days' : 'day'} +
+ + ), +}); + literalMatcher.register(tBoolean.create(), { render: ({ value, onChange }) => (
({ name: 'Tag', supers: [], }); + +export const tDateRange = typesystem.defineData( + DataHelper.create<{ value: number }>('DateRange') +); diff --git a/packages/frontend/component/src/components/page-list/filter/multi-select.tsx b/packages/frontend/component/src/components/page-list/filter/multi-select.tsx index 797a248761..84264c6ff6 100644 --- a/packages/frontend/component/src/components/page-list/filter/multi-select.tsx +++ b/packages/frontend/component/src/components/page-list/filter/multi-select.tsx @@ -1,7 +1,7 @@ -import { Menu, MenuItem } from '@toeverything/components/menu'; import type { MouseEvent } from 'react'; import { useMemo } from 'react'; +import { Menu, MenuItem } from '../../../ui/menu'; import * as styles from './multi-select.css'; export const MultiSelect = ({ diff --git a/packages/frontend/component/src/components/page-list/filter/vars.tsx b/packages/frontend/component/src/components/page-list/filter/vars.tsx index bf4af661af..a1b36cf8b0 100644 --- a/packages/frontend/component/src/components/page-list/filter/vars.tsx +++ b/packages/frontend/component/src/components/page-list/filter/vars.tsx @@ -5,17 +5,13 @@ import type { VariableMap, } from '@affine/env/filter'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { - MenuIcon, - MenuItem, - MenuSeparator, -} from '@toeverything/components/menu'; import dayjs from 'dayjs'; import type { ReactNode } from 'react'; +import { MenuIcon, MenuItem, MenuSeparator } from '../../../ui/menu'; import { FilterTag } from './filter-tag-translation'; import * as styles from './index.css'; -import { tBoolean, tDate, tTag } from './logical/custom-type'; +import { tBoolean, tDate, tDateRange, tTag } from './logical/custom-type'; import { Matcher } from './logical/matcher'; import type { TFunction } from './logical/typesystem'; import { @@ -165,6 +161,24 @@ filterMatcher.register( } ); +filterMatcher.register( + tFunction({ + args: [tDate.create(), tDateRange.create()], + rt: tBoolean.create(), + }), + { + name: 'last', + defaultArgs: () => [30], // Default to the last 30 days + impl: (date, n) => { + if (typeof date !== 'number' || typeof n !== 'number') { + throw new Error('Argument type error: date and n must be numbers'); + } + const startDate = dayjs().subtract(n, 'day').startOf('day').valueOf(); + return date > startDate; + }, + } +); + filterMatcher.register( tFunction({ args: [tDate.create(), tDate.create()], diff --git a/packages/frontend/component/src/components/page-list/operation-cell.tsx b/packages/frontend/component/src/components/page-list/operation-cell.tsx index 7e080448bc..a8d8d8d08d 100644 --- a/packages/frontend/component/src/components/page-list/operation-cell.tsx +++ b/packages/frontend/component/src/components/page-list/operation-cell.tsx @@ -7,13 +7,13 @@ import { OpenInNewIcon, ResetIcon, } from '@blocksuite/icons'; -import { IconButton } from '@toeverything/components/button'; -import { Menu, MenuIcon, MenuItem } from '@toeverything/components/menu'; -import { ConfirmModal } from '@toeverything/components/modal'; -import { Tooltip } from '@toeverything/components/tooltip'; import { useState } from 'react'; import { Link } from 'react-router-dom'; +import { IconButton } from '../../ui/button'; +import { Menu, MenuIcon, MenuItem } from '../../ui/menu'; +import { ConfirmModal } from '../../ui/modal'; +import { Tooltip } from '../../ui/tooltip'; import { FavoriteTag } from './components/favorite-tag'; import { DisablePublicSharing, MoveToTrash } from './operation-menu-items'; import * as styles from './page-list.css'; @@ -66,6 +66,7 @@ export const OperationCell = ({ {!environment.isDesktop && ( { diff --git a/packages/frontend/component/src/components/page-list/operation-menu-items/export.tsx b/packages/frontend/component/src/components/page-list/operation-menu-items/export.tsx index a924f71e58..d37bb64a18 100644 --- a/packages/frontend/component/src/components/page-list/operation-menu-items/export.tsx +++ b/packages/frontend/component/src/components/page-list/operation-menu-items/export.tsx @@ -6,9 +6,9 @@ import { ExportToPdfIcon, ExportToPngIcon, } from '@blocksuite/icons'; -import { MenuIcon, MenuItem, MenuSub } from '@toeverything/components/menu'; import { type ReactNode, useMemo } from 'react'; +import { MenuIcon, MenuItem, MenuSub } from '../../../ui/menu'; import { transitionStyle } from './index.css'; interface ExportMenuItemProps { diff --git a/packages/frontend/component/src/components/page-list/operation-menu-items/move-to-trash.tsx b/packages/frontend/component/src/components/page-list/operation-menu-items/move-to-trash.tsx index 9738381081..82abf8c15d 100644 --- a/packages/frontend/component/src/components/page-list/operation-menu-items/move-to-trash.tsx +++ b/packages/frontend/component/src/components/page-list/operation-menu-items/move-to-trash.tsx @@ -1,14 +1,8 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { DeleteIcon } from '@blocksuite/icons'; -import { - MenuIcon, - MenuItem, - type MenuItemProps, -} from '@toeverything/components/menu'; -import { - ConfirmModal, - type ConfirmModalProps, -} from '@toeverything/components/modal'; + +import { MenuIcon, MenuItem, type MenuItemProps } from '../../../ui/menu'; +import { ConfirmModal, type ConfirmModalProps } from '../../../ui/modal'; export const MoveToTrash = (props: MenuItemProps) => { const t = useAFFiNEI18N(); diff --git a/packages/frontend/component/src/components/page-list/page-list.css.ts b/packages/frontend/component/src/components/page-list/page-list.css.ts index 64a855b9ed..2be091713f 100644 --- a/packages/frontend/component/src/components/page-list/page-list.css.ts +++ b/packages/frontend/component/src/components/page-list/page-list.css.ts @@ -120,3 +120,14 @@ export const favoriteCell = style({ }, }, }); + +export const clearLinkStyle = style({ + color: 'inherit', + textDecoration: 'none', + ':visited': { + color: 'inherit', + }, + ':active': { + color: 'inherit', + }, +}); diff --git a/packages/frontend/component/src/components/page-list/page-tags.tsx b/packages/frontend/component/src/components/page-list/page-tags.tsx index 3fd43ea9df..7fb86e9dce 100644 --- a/packages/frontend/component/src/components/page-list/page-tags.tsx +++ b/packages/frontend/component/src/components/page-list/page-tags.tsx @@ -1,10 +1,10 @@ import type { Tag } from '@affine/env/filter'; import { MoreHorizontalIcon } from '@blocksuite/icons'; -import { Menu } from '@toeverything/components/menu'; import { assignInlineVars } from '@vanilla-extract/dynamic'; import clsx from 'clsx'; import { useMemo } from 'react'; +import { Menu } from '../../ui/menu'; import * as styles from './page-tags.css'; import { stopPropagation } from './utils'; diff --git a/packages/frontend/component/src/components/page-list/view/collection-bar.tsx b/packages/frontend/component/src/components/page-list/view/collection-bar.tsx index 453dafa6ae..2b8c102158 100644 --- a/packages/frontend/component/src/components/page-list/view/collection-bar.tsx +++ b/packages/frontend/component/src/components/page-list/view/collection-bar.tsx @@ -2,11 +2,11 @@ import type { DeleteCollectionInfo, PropertiesMeta } from '@affine/env/filter'; import type { GetPageInfoById } from '@affine/env/page-info'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { ViewLayersIcon } from '@blocksuite/icons'; -import { Button } from '@toeverything/components/button'; -import { Tooltip } from '@toeverything/components/tooltip'; import clsx from 'clsx'; import { useState } from 'react'; +import { Button } from '../../../ui/button'; +import { Tooltip } from '../../../ui/tooltip'; import { type CollectionsCRUDAtom, useCollectionManager, diff --git a/packages/frontend/component/src/components/page-list/view/collection-list.tsx b/packages/frontend/component/src/components/page-list/view/collection-list.tsx index 563bef0414..80a31205b1 100644 --- a/packages/frontend/component/src/components/page-list/view/collection-list.tsx +++ b/packages/frontend/component/src/components/page-list/view/collection-list.tsx @@ -6,11 +6,11 @@ import type { import type { PropertiesMeta } from '@affine/env/filter'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { FilterIcon } from '@blocksuite/icons'; -import { Button } from '@toeverything/components/button'; -import { Menu } from '@toeverything/components/menu'; import { useCallback, useState } from 'react'; +import { Button } from '../../../ui/button'; import { FlexWrapper } from '../../../ui/layout'; +import { Menu } from '../../../ui/menu'; import { CreateFilterMenu } from '../filter/vars'; import type { useCollectionManager } from '../use-collection-manager'; import * as styles from './collection-list.css'; diff --git a/packages/frontend/component/src/components/page-list/view/collection-operations.tsx b/packages/frontend/component/src/components/page-list/view/collection-operations.tsx index c7deb837a4..fc2564c812 100644 --- a/packages/frontend/component/src/components/page-list/view/collection-operations.tsx +++ b/packages/frontend/component/src/components/page-list/view/collection-operations.tsx @@ -1,12 +1,6 @@ import type { Collection, DeleteCollectionInfo } from '@affine/env/filter'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { DeleteIcon, EditIcon, FilterIcon } from '@blocksuite/icons'; -import { - Menu, - MenuIcon, - MenuItem, - type MenuItemProps, -} from '@toeverything/components/menu'; import { type PropsWithChildren, type ReactElement, @@ -14,6 +8,7 @@ import { useMemo, } from 'react'; +import { Menu, MenuIcon, MenuItem, type MenuItemProps } from '../../../ui/menu'; import type { useCollectionManager } from '../use-collection-manager'; import type { AllPageListConfig } from '.'; import * as styles from './collection-operations.css'; diff --git a/packages/frontend/component/src/components/page-list/view/create-collection.tsx b/packages/frontend/component/src/components/page-list/view/create-collection.tsx index a769e1c078..086c449e8c 100644 --- a/packages/frontend/component/src/components/page-list/view/create-collection.tsx +++ b/packages/frontend/component/src/components/page-list/view/create-collection.tsx @@ -1,9 +1,9 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Button } from '@toeverything/components/button'; -import { Modal } from '@toeverything/components/modal'; import { useCallback, useMemo, useState } from 'react'; +import { Button } from '../../../ui/button'; import Input from '../../../ui/input'; +import { Modal } from '../../../ui/modal'; import * as styles from './create-collection.css'; export interface CreateCollectionModalProps { diff --git a/packages/frontend/component/src/components/page-list/view/edit-collection/edit-collection.tsx b/packages/frontend/component/src/components/page-list/view/edit-collection/edit-collection.tsx index ad91403861..f789467b83 100644 --- a/packages/frontend/component/src/components/page-list/view/edit-collection/edit-collection.tsx +++ b/packages/frontend/component/src/components/page-list/view/edit-collection/edit-collection.tsx @@ -2,11 +2,11 @@ import type { Collection } from '@affine/env/filter'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import type { PageMeta, Workspace } from '@blocksuite/store'; import type { DialogContentProps } from '@radix-ui/react-dialog'; -import { Button } from '@toeverything/components/button'; -import { Modal } from '@toeverything/components/modal'; import { type ReactNode, useCallback, useMemo, useState } from 'react'; -import { RadioButton, RadioButtonGroup } from '../../../../ui/button'; +import { RadioButton, RadioButtonGroup } from '../../../../index'; +import { Button } from '../../../../ui/button'; +import { Modal } from '../../../../ui/modal'; import * as styles from './edit-collection.css'; import { PagesMode } from './pages-mode'; import { RulesMode } from './rules-mode'; diff --git a/packages/frontend/component/src/components/page-list/view/edit-collection/hooks.tsx b/packages/frontend/component/src/components/page-list/view/edit-collection/hooks.tsx index 58fde6a7ed..b21106f0fc 100644 --- a/packages/frontend/component/src/components/page-list/view/edit-collection/hooks.tsx +++ b/packages/frontend/component/src/components/page-list/view/edit-collection/hooks.tsx @@ -1,6 +1,6 @@ -import { Modal } from '@toeverything/components/modal'; import { useCallback, useState } from 'react'; +import { Modal } from '../../../../ui/modal'; import type { AllPageListConfig } from './edit-collection'; import { SelectPage } from './select-page'; export const useSelectPage = ({ diff --git a/packages/frontend/component/src/components/page-list/view/edit-collection/pages-mode.tsx b/packages/frontend/component/src/components/page-list/view/edit-collection/pages-mode.tsx index 4f25b105e7..d04a681e0c 100644 --- a/packages/frontend/component/src/components/page-list/view/edit-collection/pages-mode.tsx +++ b/packages/frontend/component/src/components/page-list/view/edit-collection/pages-mode.tsx @@ -2,10 +2,10 @@ import type { Collection } from '@affine/env/filter'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { FilterIcon } from '@blocksuite/icons'; import type { PageMeta } from '@blocksuite/store'; -import { Menu } from '@toeverything/components/menu'; import clsx from 'clsx'; import { type ReactNode, useCallback } from 'react'; +import { Menu } from '../../../../ui/menu'; import { FilterList } from '../../filter/filter-list'; import { VariableSelect } from '../../filter/vars'; import { VirtualizedPageList } from '../../virtualized-page-list'; diff --git a/packages/frontend/component/src/components/page-list/view/edit-collection/select-page.tsx b/packages/frontend/component/src/components/page-list/view/edit-collection/select-page.tsx index f4a4e7ffcf..28616a4df1 100644 --- a/packages/frontend/component/src/components/page-list/view/edit-collection/select-page.tsx +++ b/packages/frontend/component/src/components/page-list/view/edit-collection/select-page.tsx @@ -1,11 +1,11 @@ import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { FilterIcon } from '@blocksuite/icons'; -import { Button } from '@toeverything/components/button'; -import { Menu } from '@toeverything/components/menu'; import clsx from 'clsx'; import { useCallback, useState } from 'react'; +import { Button } from '../../../../ui/button'; +import { Menu } from '../../../../ui/menu'; import { FilterList } from '../../filter'; import { VariableSelect } from '../../filter/vars'; import { VirtualizedPageList } from '../../virtualized-page-list'; diff --git a/packages/frontend/component/src/components/page-list/view/save-as-collection-button.tsx b/packages/frontend/component/src/components/page-list/view/save-as-collection-button.tsx index bcdd35c44f..98e7fb4234 100644 --- a/packages/frontend/component/src/components/page-list/view/save-as-collection-button.tsx +++ b/packages/frontend/component/src/components/page-list/view/save-as-collection-button.tsx @@ -1,10 +1,10 @@ import type { Collection } from '@affine/env/filter'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { SaveIcon } from '@blocksuite/icons'; -import { Button } from '@toeverything/components/button'; import { nanoid } from 'nanoid'; import { useCallback } from 'react'; +import { Button } from '../../../ui/button'; import { createEmptyCollection } from '../use-collection-manager'; import { useEditCollectionName } from './use-edit-collection'; diff --git a/packages/frontend/component/src/components/setting-components/storage-progess.tsx b/packages/frontend/component/src/components/setting-components/storage-progess.tsx index e98c564069..174b6c83d8 100644 --- a/packages/frontend/component/src/components/setting-components/storage-progess.tsx +++ b/packages/frontend/component/src/components/setting-components/storage-progess.tsx @@ -1,11 +1,11 @@ import { SubscriptionPlan } from '@affine/graphql'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Button } from '@toeverything/components/button'; -import { Tooltip } from '@toeverything/components/tooltip'; import bytes from 'bytes'; import clsx from 'clsx'; import { useMemo } from 'react'; +import { Button } from '../../ui/button'; +import { Tooltip } from '../../ui/tooltip'; import * as styles from './share.css'; export interface StorageProgressProgress { diff --git a/packages/frontend/component/src/components/tour-modal/tour-modal.tsx b/packages/frontend/component/src/components/tour-modal/tour-modal.tsx index 84e51eced1..95610743ab 100644 --- a/packages/frontend/component/src/components/tour-modal/tour-modal.tsx +++ b/packages/frontend/component/src/components/tour-modal/tour-modal.tsx @@ -1,10 +1,10 @@ /// import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { ArrowLeftSmallIcon, ArrowRightSmallIcon } from '@blocksuite/icons'; -import { Modal, type ModalProps } from '@toeverything/components/modal'; import clsx from 'clsx'; import { useState } from 'react'; +import { Modal, type ModalProps } from '../../ui/modal'; import editingVideo from './editingVideo.mp4'; import { arrowStyle, diff --git a/packages/frontend/component/src/components/workspace/index.css.ts b/packages/frontend/component/src/components/workspace/index.css.ts index 02a86963a5..a5a91dee95 100644 --- a/packages/frontend/component/src/components/workspace/index.css.ts +++ b/packages/frontend/component/src/components/workspace/index.css.ts @@ -23,7 +23,7 @@ export const appStyle = style({ inset: 0, opacity: 'var(--affine-noise-opacity, 0)', backgroundRepeat: 'repeat', - backgroundSize: '2.5%', + backgroundSize: '3%', // todo: figure out how to use vanilla-extract webpack plugin to inject img url backgroundImage: `var(--noise-background)`, }, @@ -32,13 +32,13 @@ export const appStyle = style({ globalStyle(`html[data-theme="light"] ${appStyle}`, { vars: { - '--affine-noise-opacity': '0.25', + '--affine-noise-opacity': '0.35', }, }); globalStyle(`html[data-theme="dark"] ${appStyle}`, { vars: { - '--affine-noise-opacity': '0.1', + '--affine-noise-opacity': '1', }, '@media': { @@ -54,13 +54,11 @@ export const mainContainerStyle = style({ width: 0, flex: 1, maxWidth: '100%', - backgroundColor: 'var(--affine-background-primary-color)', selectors: { '&[data-show-padding="true"]': { margin: '8px', borderRadius: '5px', overflow: 'hidden', - boxShadow: 'var(--affine-shadow-1)', '@media': { print: { overflow: 'visible', @@ -72,12 +70,6 @@ export const mainContainerStyle = style({ '&[data-show-padding="true"][data-is-macos="true"]': { borderRadius: '6px', }, - '&[data-in-trash-page="true"]': { - marginBottom: '66px', - }, - '&[data-in-trash-page="true"][data-show-padding="true"]': { - marginBottom: '66px', - }, '&[data-show-padding="true"]:before': { content: '""', position: 'absolute', @@ -124,7 +116,7 @@ globalStyle( ); export const toolStyle = style({ - position: 'fixed', + position: 'absolute', right: '30px', bottom: '30px', zIndex: 'var(--affine-z-index-popover)', @@ -143,20 +135,4 @@ export const toolStyle = style({ display: 'none', }, }, - selectors: { - '&[data-in-trash-page="true"]': { - bottom: '70px', - '@media': { - 'screen and (max-width: 960px)': { - bottom: '80px', - }, - 'screen and (max-width: 640px)': { - bottom: '85px', - }, - print: { - display: 'none', - }, - }, - }, - }, }); diff --git a/packages/frontend/component/src/components/workspace/index.tsx b/packages/frontend/component/src/components/workspace/index.tsx index 3472660124..157df78619 100644 --- a/packages/frontend/component/src/components/workspace/index.tsx +++ b/packages/frontend/component/src/components/workspace/index.tsx @@ -35,14 +35,13 @@ export const AppContainer = ({ export interface MainContainerProps extends HTMLAttributes { className?: string; padding?: boolean; - inTrashPage?: boolean; } export const MainContainer = forwardRef< HTMLDivElement, PropsWithChildren >(function MainContainer( - { className, padding, inTrashPage, children, ...props }, + { className, padding, children, ...props }, ref ): ReactElement { return ( @@ -51,7 +50,6 @@ export const MainContainer = forwardRef< className={clsx(mainContainerStyle, className)} data-is-macos={environment.isDesktop && environment.isMacOs} data-show-padding={!!padding} - data-in-trash-page={!!inTrashPage} ref={ref} > {children} @@ -61,14 +59,8 @@ export const MainContainer = forwardRef< MainContainer.displayName = 'MainContainer'; -export const ToolContainer = ( - props: PropsWithChildren & { inTrashPage: boolean } -): ReactElement => { - return ( -
- {props.children} -
- ); +export const ToolContainer = (props: PropsWithChildren): ReactElement => { + return
{props.children}
; }; export const WorkspaceFallback = (): ReactElement => { diff --git a/packages/frontend/component/src/index.ts b/packages/frontend/component/src/index.ts index 25218e5bc3..aed6a07316 100644 --- a/packages/frontend/component/src/index.ts +++ b/packages/frontend/component/src/index.ts @@ -1,13 +1,21 @@ +// TODO: Check `input` , `loading`, not migrated from `design` export * from './styles'; +export * from './ui/avatar'; export * from './ui/button'; export * from './ui/checkbox'; +export * from './ui/divider'; export * from './ui/empty'; export * from './ui/input'; export * from './ui/layout'; +export * from './ui/loading'; export * from './ui/lottie/collections-icon'; export * from './ui/lottie/delete-icon'; +export * from './ui/menu'; +export * from './ui/modal'; +export * from './ui/popover'; export * from './ui/scrollbar'; export * from './ui/skeleton'; export * from './ui/switch'; export * from './ui/table'; export * from './ui/toast'; +export * from './ui/tooltip'; diff --git a/packages/frontend/component/src/theme/global.css b/packages/frontend/component/src/theme/global.css index 31754752bd..015a003d28 100644 --- a/packages/frontend/component/src/theme/global.css +++ b/packages/frontend/component/src/theme/global.css @@ -9,7 +9,7 @@ } :root { - --noise-background: url(./noise.png); + --noise-background: url(./noise.avif); } html, @@ -174,6 +174,7 @@ legend { border: 0; font-size: var(--affine-font-base); font-family: var(--affine-font-family); + font-feature-settings: 'calt' 0; } body { diff --git a/packages/frontend/component/src/theme/noise.avif b/packages/frontend/component/src/theme/noise.avif new file mode 100644 index 0000000000..c08002c5bd Binary files /dev/null and b/packages/frontend/component/src/theme/noise.avif differ diff --git a/packages/frontend/component/src/theme/noise.png b/packages/frontend/component/src/theme/noise.png deleted file mode 100644 index 1610efbf3c..0000000000 Binary files a/packages/frontend/component/src/theme/noise.png and /dev/null differ diff --git a/packages/frontend/component/src/theme/theme.css.ts b/packages/frontend/component/src/theme/theme.css.ts index 24187dc84a..c471d45a1f 100644 --- a/packages/frontend/component/src/theme/theme.css.ts +++ b/packages/frontend/component/src/theme/theme.css.ts @@ -11,8 +11,12 @@ globalStyle('html', { vars: lightCssVariables, }); -globalStyle('html[data-theme="dark"]', { - vars: darkCssVariables, +globalStyle('html', { + '@media': { + '(prefers-color-scheme: dark)': { + vars: darkCssVariables, + }, + }, }); if (process.env.NODE_ENV === 'development') { diff --git a/packages/frontend/component/src/ui/avatar/avatar.stories.tsx b/packages/frontend/component/src/ui/avatar/avatar.stories.tsx new file mode 100644 index 0000000000..f7e3447c5b --- /dev/null +++ b/packages/frontend/component/src/ui/avatar/avatar.stories.tsx @@ -0,0 +1,52 @@ +import { CameraIcon } from '@blocksuite/icons'; +import type { Meta, StoryFn } from '@storybook/react'; + +import { Avatar, type AvatarProps } from './avatar'; + +export default { + title: 'UI/Avatar', + component: Avatar, + argTypes: { + onClick: () => console.log('Click button'), + }, +} satisfies Meta; + +const Template: StoryFn = args => ; + +export const DefaultAvatar: StoryFn = Template.bind(undefined); +DefaultAvatar.args = { + name: 'AFFiNE', + url: 'https://affine.pro/favicon-96.png', + size: 50, +}; +export const Fallback: StoryFn = Template.bind(undefined); +Fallback.args = { + name: 'AFFiNE', + size: 50, +}; +export const ColorfulFallback: StoryFn = Template.bind(undefined); +ColorfulFallback.args = { + size: 50, + colorfulFallback: true, + name: 'blocksuite', +}; +export const WithHover: StoryFn = Template.bind(undefined); +WithHover.args = { + size: 50, + colorfulFallback: true, + name: 'With Hover', + hoverIcon: , +}; + +export const WithRemove: StoryFn = Template.bind(undefined); +WithRemove.args = { + size: 50, + colorfulFallback: true, + name: 'With Hover', + hoverIcon: , + removeTooltipOptions: { content: 'This is remove tooltip' }, + avatarTooltipOptions: { content: 'This is avatar tooltip' }, + onRemove: e => { + console.log('on remove', e); + }, +}; diff --git a/packages/frontend/component/src/ui/avatar/avatar.tsx b/packages/frontend/component/src/ui/avatar/avatar.tsx new file mode 100644 index 0000000000..a1efd89949 --- /dev/null +++ b/packages/frontend/component/src/ui/avatar/avatar.tsx @@ -0,0 +1,143 @@ +import { CloseIcon } from '@blocksuite/icons'; +import { + type AvatarFallbackProps, + type AvatarImageProps, + type AvatarProps as RadixAvatarProps, + Fallback as AvatarFallback, + Image as AvatarImage, + Root as AvatarRoot, +} from '@radix-ui/react-avatar'; +import { assignInlineVars } from '@vanilla-extract/dynamic'; +import clsx from 'clsx'; +import type { CSSProperties, HTMLAttributes, MouseEvent } from 'react'; +import { forwardRef, type ReactElement, useMemo, useState } from 'react'; + +import { IconButton } from '../button'; +import { Tooltip, type TooltipProps } from '../tooltip'; +import { ColorfulFallback } from './colorful-fallback'; +import * as style from './style.css'; +import { sizeVar } from './style.css'; + +export type AvatarProps = { + size?: number; + url?: string | null; + name?: string; + className?: string; + style?: CSSProperties; + colorfulFallback?: boolean; + hoverIcon?: ReactElement; + onRemove?: (e: MouseEvent) => void; + avatarTooltipOptions?: Omit; + removeTooltipOptions?: Omit; + + fallbackProps?: AvatarFallbackProps; + imageProps?: Omit; + avatarProps?: RadixAvatarProps; + hoverWrapperProps?: HTMLAttributes; + removeButtonProps?: HTMLAttributes; +} & HTMLAttributes; + +export const Avatar = forwardRef( + ( + { + size = 20, + style: propsStyles = {}, + url, + name, + className, + colorfulFallback = false, + hoverIcon, + fallbackProps: { className: fallbackClassName, ...fallbackProps } = {}, + imageProps, + avatarProps, + onRemove, + hoverWrapperProps: { + className: hoverWrapperClassName, + ...hoverWrapperProps + } = {}, + avatarTooltipOptions, + removeTooltipOptions, + removeButtonProps: { + className: removeButtonClassName, + ...removeButtonProps + } = {}, + ...props + }, + ref + ) => { + const firstCharOfName = useMemo(() => { + return name?.slice(0, 1) || 'A'; + }, [name]); + const [imageDom, setImageDom] = useState(null); + const [removeButtonDom, setRemoveButtonDom] = + useState(null); + + return ( + + +
+ + + + {colorfulFallback ? ( + + ) : ( + firstCharOfName + )} + + {hoverIcon ? ( +
+ {hoverIcon} +
+ ) : null} +
+
+ + {onRemove ? ( + + + + + + ) : null} +
+ ); + } +); + +Avatar.displayName = 'Avatar'; diff --git a/packages/frontend/component/src/ui/avatar/colorful-fallback.tsx b/packages/frontend/component/src/ui/avatar/colorful-fallback.tsx new file mode 100644 index 0000000000..0318e60fbc --- /dev/null +++ b/packages/frontend/component/src/ui/avatar/colorful-fallback.tsx @@ -0,0 +1,67 @@ +import clsx from 'clsx'; +import { useMemo, useRef, useState } from 'react'; + +import { + DefaultAvatarBottomItemStyle, + DefaultAvatarBottomItemWithAnimationStyle, + DefaultAvatarContainerStyle, + DefaultAvatarMiddleItemStyle, + DefaultAvatarMiddleItemWithAnimationStyle, + DefaultAvatarTopItemStyle, +} from './style.css'; + +const colorsSchema = [ + ['#FF0000', '#FF00E5', '#FFAE73'], + ['#FF5C00', '#FFC700', '#FFE073'], + ['#FFDA16', '#FFFBA6', '#FFBE73'], + ['#8CD317', '#FCFF5C', '#67CAE9'], + ['#28E19F', '#89FFC6', '#39A880'], + ['#35B7E0', '#77FFCE', '#5076FF'], + ['#3D39FF', '#77BEFF', '#3502FF'], + ['#BD08EB', '#755FFF', '#6967E4'], +]; + +export const ColorfulFallback = ({ char }: { char: string }) => { + const colors = useMemo(() => { + const index = char.toUpperCase().charCodeAt(0); + return colorsSchema[index % colorsSchema.length]; + }, [char]); + + const timer = useRef>(); + + const [topColor, middleColor, bottomColor] = colors; + const [isHover, setIsHover] = useState(false); + + return ( +
{ + timer.current = setTimeout(() => { + setIsHover(true); + }, 300); + }} + onMouseLeave={() => { + clearTimeout(timer.current); + setIsHover(false); + }} + > +
+
+
+
+ ); +}; +export default ColorfulFallback; diff --git a/packages/frontend/component/src/ui/avatar/index.ts b/packages/frontend/component/src/ui/avatar/index.ts new file mode 100644 index 0000000000..a7424ca311 --- /dev/null +++ b/packages/frontend/component/src/ui/avatar/index.ts @@ -0,0 +1 @@ +export * from './avatar'; diff --git a/packages/frontend/component/src/ui/avatar/style.css.ts b/packages/frontend/component/src/ui/avatar/style.css.ts new file mode 100644 index 0000000000..46753c359a --- /dev/null +++ b/packages/frontend/component/src/ui/avatar/style.css.ts @@ -0,0 +1,210 @@ +import { createVar, globalStyle, keyframes, style } from '@vanilla-extract/css'; +export const sizeVar = createVar('sizeVar'); + +const bottomAnimation = keyframes({ + '0%': { + top: '-44%', + left: '-11%', + transform: 'matrix(-0.29, -0.96, 0.94, -0.35, 0, 0)', + }, + '16%': { + left: '-18%', + top: '-51%', + transform: 'matrix(-0.73, -0.69, 0.64, -0.77, 0, 0)', + }, + '32%': { + left: '-7%', + top: '-40%', + transform: 'matrix(-0.97, -0.23, 0.16, -0.99, 0, 0)', + }, + '48%': { + left: '-15%', + top: '-39%', + transform: 'matrix(-0.88, 0.48, -0.6, -0.8, 0, 0)', + }, + '64%': { + left: '-7%', + top: '-40%', + transform: 'matrix(-0.97, -0.23, 0.16, -0.99, 0, 0)', + }, + '80%': { + left: '-18%', + top: '-51%', + transform: 'matrix(-0.73, -0.69, 0.64, -0.77, 0, 0)', + }, + '100%': { + top: '-44%', + left: '-11%', + transform: 'matrix(-0.29, -0.96, 0.94, -0.35, 0, 0)', + }, +}); +const middleAnimation = keyframes({ + '0%': { + left: '-30px', + top: '-30px', + transform: 'matrix(-0.48, -0.88, 0.8, -0.6, 0, 0)', + }, + '16%': { + left: '-37px', + top: '-37px', + transform: 'matrix(-0.86, -0.52, 0.39, -0.92, 0, 0)', + }, + '32%': { + left: '-20px', + top: '-10px', + transform: 'matrix(-1, -0.02, -0.12, -0.99, 0, 0)', + }, + '48%': { + left: '-27px', + top: '-2px', + transform: 'matrix(-0.88, 0.48, -0.6, -0.8, 0, 0)', + }, + '64%': { + left: '-20px', + top: '-10px', + transform: 'matrix(-1, -0.02, -0.12, -0.99, 0, 0)', + }, + '80%': { + left: '-37px', + top: '-37px', + transform: 'matrix(-0.86, -0.52, 0.39, -0.92, 0, 0)', + }, + '100%': { + left: '-30px', + top: '-30px', + transform: 'matrix(-0.48, -0.88, 0.8, -0.6, 0, 0)', + }, +}); + +export const DefaultAvatarContainerStyle = style({ + width: '100%', + height: '100%', + position: 'relative', + borderRadius: '50%', + overflow: 'hidden', +}); + +export const DefaultAvatarMiddleItemStyle = style({ + width: '83%', + height: '81%', + position: 'absolute', + left: '-30%', + top: '-30%', + transform: 'matrix(-0.48, -0.88, 0.8, -0.6, 0, 0)', + opacity: '0.8', + filter: 'blur(12px)', + transformOrigin: 'center center', + animation: `${middleAnimation} 3s ease-in-out forwards infinite`, + animationPlayState: 'paused', +}); +export const DefaultAvatarMiddleItemWithAnimationStyle = style({ + animationPlayState: 'running', +}); +export const DefaultAvatarBottomItemStyle = style({ + width: '98%', + height: '97%', + position: 'absolute', + top: '-44%', + left: '-11%', + transform: 'matrix(-0.29, -0.96, 0.94, -0.35, 0, 0)', + opacity: '0.8', + filter: 'blur(12px)', + transformOrigin: 'center center', + willChange: 'left, top, transform', + animation: `${bottomAnimation} 3s ease-in-out forwards infinite`, + animationPlayState: 'paused', +}); +export const DefaultAvatarBottomItemWithAnimationStyle = style({ + animationPlayState: 'running', +}); +export const DefaultAvatarTopItemStyle = style({ + width: '104%', + height: '94%', + position: 'absolute', + right: '-30%', + top: '-30%', + opacity: '0.8', + filter: 'blur(12px)', + transform: 'matrix(-0.28, -0.96, 0.93, -0.37, 0, 0)', + transformOrigin: 'center center', +}); + +export const avatarRoot = style({ + position: 'relative', + display: 'inline-flex', + flexShrink: 0, +}); +export const avatarWrapper = style({ + vars: { + [sizeVar]: 'unset', + }, + width: sizeVar, + height: sizeVar, + fontSize: `calc(${sizeVar} / 2)`, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + verticalAlign: 'middle', + userSelect: 'none', + position: 'relative', +}); + +export const avatarImage = style({ + width: '100%', + height: '100%', + objectFit: 'cover', + borderRadius: '50%', +}); + +export const avatarFallback = style({ + width: '100%', + height: '100%', + borderRadius: '50%', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: 'var(--affine-primary-color)', + color: 'var(--affine-white)', + lineHeight: '1', + fontWeight: '500', +}); + +export const hoverWrapper = style({ + width: '100%', + height: '100%', + borderRadius: '50%', + position: 'absolute', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + backgroundColor: 'rgba(60, 61, 63, 0.5)', + zIndex: '1', + color: 'var(--affine-white)', + opacity: 0, + transition: 'opacity .15s', + cursor: 'pointer', + selectors: { + '&:hover': { + opacity: 1, + }, + }, +}); + +export const removeButton = style({ + position: 'absolute', + right: '-8px', + top: '-2px', + visibility: 'hidden', + zIndex: '1', + selectors: { + '&:hover': { + background: '#f6f6f6', + }, + }, +}); +globalStyle(`${avatarRoot}:hover ${removeButton}`, { + visibility: 'visible', +}); +globalStyle(`${avatarRoot} ${removeButton}:hover`, { + background: '#f6f6f6', +}); diff --git a/packages/frontend/component/src/ui/button/button.css.ts b/packages/frontend/component/src/ui/button/button.css.ts new file mode 100644 index 0000000000..bb48c4c0e4 --- /dev/null +++ b/packages/frontend/component/src/ui/button/button.css.ts @@ -0,0 +1,374 @@ +import { globalStyle, style } from '@vanilla-extract/css'; + +export const button = style({ + display: 'inline-flex', + justifyContent: 'center', + alignItems: 'center', + userSelect: 'none', + touchAction: 'manipulation', + flexShrink: 0, + outline: '0', + border: '1px solid', + padding: '0 18px', + borderRadius: '8px', + fontSize: 'var(--affine-font-xs)', + fontWeight: 500, + transition: 'all .3s', + ['WebkitAppRegion' as string]: 'no-drag', + cursor: 'pointer', + + // changeable + height: '28px', + background: 'var(--affine-white)', + borderColor: 'var(--affine-border-color)', + color: 'var(--affine-text-primary-color)', + + selectors: { + '&.text-bold': { + fontWeight: 600, + }, + '&:not(.without-hover):hover': { + background: 'var(--affine-hover-color)', + }, + '&.disabled': { + opacity: '.4', + cursor: 'default', + color: 'var(--affine-disable-color)', + pointerEvents: 'none', + }, + '&.loading': { + cursor: 'default', + color: 'var(--affine-disable-color)', + pointerEvents: 'none', + }, + '&.disabled:not(.without-hover):hover, &.loading:not(.without-hover):hover': + { + background: 'inherit', + }, + + '&.block': { display: 'flex', width: '100%' }, + + '&.circle': { + borderRadius: '50%', + }, + '&.round': { + borderRadius: '14px', + }, + // size + '&.large': { + height: '32px', + fontSize: 'var(--affine-font-base)', + fontWeight: 600, + }, + '&.round.large': { + borderRadius: '16px', + }, + '&.extraLarge': { + height: '40px', + fontSize: 'var(--affine-font-base)', + fontWeight: 700, + }, + '&.extraLarge.primary': { + boxShadow: 'var(--affine-large-button-effect) !important', + }, + '&.round.extraLarge': { + borderRadius: '20px', + }, + + // type + '&.plain': { + color: 'var(--affine-text-primary-color)', + borderColor: 'transparent', + background: 'transparent', + }, + + '&.primary': { + color: 'var(--affine-pure-white)', + background: 'var(--affine-primary-color)', + borderColor: 'var(--affine-black-10)', + boxShadow: 'var(--affine-button-inner-shadow)', + }, + '&.primary:not(.without-hover):hover': { + background: + 'linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), var(--affine-primary-color)', + }, + '&.primary.disabled': { + opacity: '.4', + cursor: 'default', + }, + '&.primary.disabled:not(.without-hover):hover': { + background: 'var(--affine-primary-color)', + }, + + '&.error': { + color: 'var(--affine-pure-white)', + background: 'var(--affine-error-color)', + borderColor: 'var(--affine-black-10)', + boxShadow: 'var(--affine-button-inner-shadow)', + }, + '&.error:not(.without-hover):hover': { + background: + 'linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), var(--affine-error-color)', + }, + '&.error.disabled': { + opacity: '.4', + cursor: 'default', + }, + '&.error.disabled:not(.without-hover):hover': { + background: 'var(--affine-error-color)', + }, + + '&.warning': { + color: 'var(--affine-pure-white)', + background: 'var(--affine-warning-color)', + borderColor: 'var(--affine-black-10)', + boxShadow: 'var(--affine-button-inner-shadow)', + }, + '&.warning:not(.without-hover):hover': { + background: + 'linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), var(--affine-warning-color)', + }, + '&.warning.disabled': { + opacity: '.4', + cursor: 'default', + }, + '&.warning.disabled:not(.without-hover):hover': { + background: 'var(--affine-warning-color)', + }, + + '&.success': { + color: 'var(--affine-pure-white)', + background: 'var(--affine-success-color)', + borderColor: 'var(--affine-black-10)', + boxShadow: 'var(--affine-button-inner-shadow)', + }, + '&.success:not(.without-hover):hover': { + background: + 'linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), var(--affine-success-color)', + }, + '&.success.disabled': { + opacity: '.4', + cursor: 'default', + }, + '&.success.disabled:not(.without-hover):hover': { + background: 'var(--affine-success-color)', + }, + + '&.processing': { + color: 'var(--affine-pure-white)', + background: 'var(--affine-processing-color)', + borderColor: 'var(--affine-black-10)', + boxShadow: 'var(--affine-button-inner-shadow)', + }, + '&.processing:not(.without-hover):hover': { + background: + 'linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), var(--affine-processing-color)', + }, + '&.processing.disabled': { + opacity: '.4', + cursor: 'default', + }, + '&.processing.disabled:not(.without-hover):hover': { + background: 'var(--affine-processing-color)', + }, + }, +}); + +globalStyle(`${button} > span`, { + // flex: 1, + lineHeight: 1, + padding: '0 4px', +}); + +export const buttonIcon = style({ + flexShrink: 0, + display: 'inline-flex', + justifyContent: 'center', + alignItems: 'center', + color: 'var(--affine-icon-color)', + fontSize: '16px', + width: '16px', + height: '16px', + selectors: { + '&.start': { + marginRight: '4px', + }, + '&.end': { + marginLeft: '4px', + }, + '&.large': { + fontSize: '20px', + width: '20px', + height: '20px', + }, + '&.extraLarge': { + fontSize: '20px', + width: '20px', + height: '20px', + }, + '&.color-white': { + color: 'var(--affine-pure-white)', + }, + }, +}); + +export const iconButton = style({ + display: 'inline-flex', + justifyContent: 'center', + alignItems: 'center', + userSelect: 'none', + touchAction: 'manipulation', + outline: '0', + border: '1px solid', + borderRadius: '4px', + transition: 'all .3s', + ['WebkitAppRegion' as string]: 'no-drag', + cursor: 'pointer', + background: 'var(--affine-white)', + + // changeable + width: '24px', + height: '24px', + fontSize: '20px', + color: 'var(--affine-text-primary-color)', + borderColor: 'var(--affine-border-color)', + selectors: { + '&.without-padding': { + margin: '-2px', + }, + '&.active': { + color: 'var(--affine-primary-color)', + }, + + '&:not(.without-hover):hover': { + background: 'var(--affine-hover-color)', + }, + '&.disabled': { + opacity: '.4', + cursor: 'default', + color: 'var(--affine-disable-color)', + pointerEvents: 'none', + }, + '&.loading': { + cursor: 'default', + color: 'var(--affine-disable-color)', + pointerEvents: 'none', + }, + '&.disabled:not(.without-hover):hover, &.loading:not(.without-hover):hover': + { + background: 'inherit', + }, + + // size + '&.large': { + width: '32px', + height: '32px', + fontSize: '24px', + }, + '&.large.without-padding': { + margin: '-4px', + }, + '&.small': { width: '20px', height: '20px', fontSize: '16px' }, + '&.extra-small': { width: '16px', height: '16px', fontSize: '12px' }, + + // type + '&.plain': { + color: 'var(--affine-icon-color)', + borderColor: 'transparent', + background: 'transparent', + }, + '&.plain.active': { + color: 'var(--affine-primary-color)', + }, + + '&.primary': { + color: 'var(--affine-white)', + background: 'var(--affine-primary-color)', + borderColor: 'var(--affine-black-10)', + boxShadow: '0px 1px 2px 0px rgba(255, 255, 255, 0.25) inset', + }, + '&.primary:not(.without-hover):hover': { + background: + 'linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), var(--affine-primary-color)', + }, + '&.primary.disabled': { + opacity: '.4', + cursor: 'default', + }, + '&.primary.disabled:not(.without-hover):hover': { + background: 'var(--affine-primary-color)', + }, + + '&.error': { + color: 'var(--affine-white)', + background: 'var(--affine-error-color)', + borderColor: 'var(--affine-black-10)', + boxShadow: '0px 1px 2px 0px rgba(255, 255, 255, 0.25) inset', + }, + '&.error:not(.without-hover):hover': { + background: + 'linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), var(--affine-error-color)', + }, + '&.error.disabled': { + opacity: '.4', + cursor: 'default', + }, + '&.error.disabled:not(.without-hover):hover': { + background: 'var(--affine-error-color)', + }, + + '&.warning': { + color: 'var(--affine-white)', + background: 'var(--affine-warning-color)', + borderColor: 'var(--affine-black-10)', + boxShadow: '0px 1px 2px 0px rgba(255, 255, 255, 0.25) inset', + }, + '&.warning:not(.without-hover):hover': { + background: + 'linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), var(--affine-warning-color)', + }, + '&.warning.disabled': { + opacity: '.4', + cursor: 'default', + }, + '&.warning.disabled:not(.without-hover):hover': { + background: 'var(--affine-warning-color)', + }, + + '&.success': { + color: 'var(--affine-white)', + background: 'var(--affine-success-color)', + borderColor: 'var(--affine-black-10)', + boxShadow: '0px 1px 2px 0px rgba(255, 255, 255, 0.25) inset', + }, + '&.success:not(.without-hover):hover': { + background: + 'linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), var(--affine-success-color)', + }, + '&.success.disabled': { + opacity: '.4', + cursor: 'default', + }, + '&.success.disabled:not(.without-hover):hover': { + background: 'var(--affine-success-color)', + }, + + '&.processing': { + color: 'var(--affine-white)', + background: 'var(--affine-processing-color)', + borderColor: 'var(--affine-black-10)', + boxShadow: '0px 1px 2px 0px rgba(255, 255, 255, 0.25) inset', + }, + '&.processing:not(.without-hover):hover': { + background: + 'linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), var(--affine-processing-color)', + }, + '&.processing.disabled': { + opacity: '.4', + cursor: 'default', + }, + '&.processing.disabled:not(.without-hover):hover': { + background: 'var(--affine-processing-color)', + }, + }, +}); diff --git a/packages/frontend/component/src/ui/button/button.stories.tsx b/packages/frontend/component/src/ui/button/button.stories.tsx new file mode 100644 index 0000000000..93739ce78b --- /dev/null +++ b/packages/frontend/component/src/ui/button/button.stories.tsx @@ -0,0 +1,46 @@ +import { InformationIcon } from '@blocksuite/icons'; +import type { Meta, StoryFn } from '@storybook/react'; + +import { Button, type ButtonProps } from './button'; +export default { + title: 'UI/Button', + component: Button, + argTypes: { + onClick: () => console.log('Click button'), + }, +} satisfies Meta; + +const Template: StoryFn = args => + ); + } +); +Button.displayName = 'Button'; +export default Button; diff --git a/packages/frontend/component/src/ui/button/icon-button.stories.tsx b/packages/frontend/component/src/ui/button/icon-button.stories.tsx new file mode 100644 index 0000000000..7f7d23b888 --- /dev/null +++ b/packages/frontend/component/src/ui/button/icon-button.stories.tsx @@ -0,0 +1,48 @@ +import { InformationIcon } from '@blocksuite/icons'; +import type { Meta, StoryFn } from '@storybook/react'; + +import { IconButton, type IconButtonProps } from './icon-button'; +export default { + title: 'UI/IconButton', + component: IconButton, + argTypes: { + onClick: () => console.log('Click button'), + }, +} satisfies Meta; + +const Template: StoryFn = args => ; + +export const Plain: StoryFn = Template.bind(undefined); +Plain.args = { + children: , +}; + +export const Primary: StoryFn = Template.bind(undefined); +Primary.args = { + type: 'primary', + icon: , +}; + +export const Disabled: StoryFn = Template.bind(undefined); +Disabled.args = { + disabled: true, + icon: , +}; +export const ExtraSmallSizeButton: StoryFn = + Template.bind(undefined); +ExtraSmallSizeButton.args = { + size: 'extraSmall', + icon: , +}; +export const SmallSizeButton: StoryFn = + Template.bind(undefined); +SmallSizeButton.args = { + size: 'small', + icon: , +}; +export const LargeSizeButton: StoryFn = + Template.bind(undefined); +LargeSizeButton.args = { + size: 'large', + icon: , +}; diff --git a/packages/frontend/component/src/ui/button/icon-button.tsx b/packages/frontend/component/src/ui/button/icon-button.tsx new file mode 100644 index 0000000000..880bed1414 --- /dev/null +++ b/packages/frontend/component/src/ui/button/icon-button.tsx @@ -0,0 +1,88 @@ +import clsx from 'clsx'; +import type { HTMLAttributes, PropsWithChildren } from 'react'; +import { forwardRef, type ReactElement } from 'react'; + +import { Loading } from '../loading'; +import type { ButtonType } from './button'; +import { iconButton } from './button.css'; + +export type IconButtonSize = 'default' | 'large' | 'small' | 'extraSmall'; +export type IconButtonProps = Omit, 'type'> & + PropsWithChildren<{ + type?: ButtonType; + disabled?: boolean; + size?: IconButtonSize; + loading?: boolean; + withoutPadding?: boolean; + active?: boolean; + withoutHoverStyle?: boolean; + icon?: ReactElement; + }>; + +const defaultProps = { + type: 'plain', + disabled: false, + size: 'default', + loading: false, + withoutPadding: false, + active: false, + withoutHoverStyle: false, +} as const; + +export const IconButton = forwardRef( + (props, ref) => { + const { + type, + size, + withoutPadding, + children, + disabled, + loading, + active, + withoutHoverStyle, + icon: propsIcon, + className, + ...otherProps + } = { + ...defaultProps, + ...props, + }; + + return ( + + ); + } +); + +IconButton.displayName = 'IconButton'; +export default IconButton; diff --git a/packages/frontend/component/src/ui/button/index.ts b/packages/frontend/component/src/ui/button/index.ts index d0456a6981..a8957d85da 100644 --- a/packages/frontend/component/src/ui/button/index.ts +++ b/packages/frontend/component/src/ui/button/index.ts @@ -1,2 +1,4 @@ +export * from './button'; export * from './dropdown-button'; +export * from './icon-button'; export * from './radio'; diff --git a/packages/frontend/component/src/ui/checkbox/checkbox.stories.tsx b/packages/frontend/component/src/ui/checkbox/checkbox.stories.tsx new file mode 100644 index 0000000000..db5d156734 --- /dev/null +++ b/packages/frontend/component/src/ui/checkbox/checkbox.stories.tsx @@ -0,0 +1,65 @@ +import type { Meta, StoryFn } from '@storybook/react'; +import { useState } from 'react'; + +import { Checkbox } from './checkbox'; + +export default { + title: 'UI/Checkbox', + component: Checkbox, + parameters: { + chromatic: { disableSnapshot: true }, + }, +} satisfies Meta; + +export const Basic: StoryFn = props => { + const [checked, setChecked] = useState(props.checked); + const handleChange = ( + _event: React.ChangeEvent, + checked: boolean + ) => { + setChecked(checked); + props.onChange?.(_event, checked); + }; + return ( +
+ + + + +
+ ); +}; + +Basic.args = { + checked: true, + disabled: false, + indeterminate: false, + onChange: console.log, +}; diff --git a/packages/frontend/component/src/ui/divider/divider.stories.tsx b/packages/frontend/component/src/ui/divider/divider.stories.tsx new file mode 100644 index 0000000000..61bf7db8c3 --- /dev/null +++ b/packages/frontend/component/src/ui/divider/divider.stories.tsx @@ -0,0 +1,25 @@ +import type { Meta, StoryFn } from '@storybook/react'; + +import { Divider, type DividerProps } from '.'; + +export default { + title: 'UI/Divider', + component: Divider, +} satisfies Meta; + +const Template: StoryFn = args => ( +
+ +
+); + +export const Default: StoryFn = Template.bind(undefined); +Default.args = {}; diff --git a/packages/frontend/component/src/ui/divider/divider.tsx b/packages/frontend/component/src/ui/divider/divider.tsx new file mode 100644 index 0000000000..de051b5461 --- /dev/null +++ b/packages/frontend/component/src/ui/divider/divider.tsx @@ -0,0 +1,51 @@ +import clsx from 'clsx'; +import type { HTMLAttributes, PropsWithChildren } from 'react'; +import { forwardRef } from 'react'; + +import * as styles from './style.css'; +export type DividerOrientation = 'horizontal' | 'vertical'; +export type DividerProps = PropsWithChildren & + Omit, 'type'> & { + orientation?: DividerOrientation; + size?: 'thinner' | 'default'; + dividerColor?: string; + }; + +export const Divider = forwardRef( + ( + { + orientation = 'horizontal', + size = 'default', + dividerColor = 'var(--affine-border-color)', + style, + className, + ...otherProps + }, + ref + ) => { + return ( +
+ ); + } +); + +Divider.displayName = 'Divider'; +export default Divider; diff --git a/packages/frontend/component/src/ui/divider/index.ts b/packages/frontend/component/src/ui/divider/index.ts new file mode 100644 index 0000000000..bf4ed01967 --- /dev/null +++ b/packages/frontend/component/src/ui/divider/index.ts @@ -0,0 +1 @@ +export * from './divider'; diff --git a/packages/frontend/component/src/ui/divider/style.css.ts b/packages/frontend/component/src/ui/divider/style.css.ts new file mode 100644 index 0000000000..48f1dd1346 --- /dev/null +++ b/packages/frontend/component/src/ui/divider/style.css.ts @@ -0,0 +1,21 @@ +import { style } from '@vanilla-extract/css'; + +export const divider = style({ + height: '1px', + backgroundColor: 'var(--affine-border-color)', + borderRadius: '8px', + margin: '8px 0', + width: '100%', +}); +export const thinner = style({ + height: '0.5px', +}); +export const verticalDivider = style({ + width: '1px', + borderRadius: '8px', + height: '100%', + margin: '0 2px', +}); +export const verticalThinner = style({ + width: '0.5px', +}); diff --git a/packages/frontend/component/src/ui/empty/empty.stories.tsx b/packages/frontend/component/src/ui/empty/empty.stories.tsx new file mode 100644 index 0000000000..3ef2e3f893 --- /dev/null +++ b/packages/frontend/component/src/ui/empty/empty.stories.tsx @@ -0,0 +1,16 @@ +import type { Meta, StoryFn } from '@storybook/react'; + +import { Empty, type EmptyContentProps } from '.'; + +export default { + title: 'UI/Empty', + component: Empty, +} satisfies Meta; + +const Template: StoryFn = args => ; + +export const Default: StoryFn = Template.bind(undefined); +Default.args = { + title: 'No Data', + description: 'No Data', +}; diff --git a/packages/frontend/component/src/ui/input/input.stories.tsx b/packages/frontend/component/src/ui/input/input.stories.tsx new file mode 100644 index 0000000000..1c7572a596 --- /dev/null +++ b/packages/frontend/component/src/ui/input/input.stories.tsx @@ -0,0 +1,58 @@ +import { InformationIcon } from '@blocksuite/icons'; +import type { Meta, StoryFn } from '@storybook/react'; + +import { Input, type InputProps } from '.'; + +export default { + title: 'UI/Input', + component: Input, +} satisfies Meta; + +const Template: StoryFn = args => ( +
+ +
+); + +export const Default: StoryFn = Template.bind(undefined); +Default.args = { + defaultValue: 'This is a default input', +}; + +export const WithPrefix: StoryFn = Template.bind(undefined); +WithPrefix.args = { + defaultValue: 'This is a input with prefix', + preFix: , +}; + +export const Large: StoryFn = Template.bind(undefined); +Large.args = { + placeholder: 'This is a large input', + size: 'large', +}; +export const ExtraLarge: StoryFn = Template.bind(undefined); +ExtraLarge.args = { + placeholder: 'This is a extraLarge input', + size: 'extraLarge', +}; + +export const CustomWidth: StoryFn = Template.bind(undefined); +CustomWidth.args = { + width: 300, + placeholder: 'This is a custom width input, default is 100%', +}; +export const ErrorStatus: StoryFn = Template.bind(undefined); +ErrorStatus.args = { + status: 'error', + placeholder: 'This is a error status input', +}; +export const WarningStatus: StoryFn = Template.bind(undefined); +WarningStatus.args = { + status: 'warning', + placeholder: 'This is a warning status input', +}; +export const Disabled: StoryFn = Template.bind(undefined); +Disabled.args = { + disabled: true, + placeholder: 'This is a disabled input', +}; diff --git a/packages/frontend/component/src/ui/input/style.css.ts b/packages/frontend/component/src/ui/input/style.css.ts index 1a28265a05..2c49ca54f2 100644 --- a/packages/frontend/component/src/ui/input/style.css.ts +++ b/packages/frontend/component/src/ui/input/style.css.ts @@ -8,17 +8,17 @@ export const inputWrapper = style({ }, width: widthVar, height: 28, - padding: '4px 10px', - color: 'var(--affine-icon-color)', - border: '1px solid var(--affine-border-color)', - backgroundColor: 'var(--affine-white-10)', + lineHeight: '22px', + padding: '0 10px', + color: 'var(--affine-text-primary-color)', + border: '1px solid', + backgroundColor: 'var(--affine-white)', borderRadius: 8, display: 'flex', justifyContent: 'center', alignItems: 'center', - gap: 8, - // icon size - fontSize: '16px', + fontSize: 'var(--affine-font-base)', + boxSizing: 'border-box', selectors: { '&.no-border': { @@ -27,14 +27,10 @@ export const inputWrapper = style({ // size '&.large': { height: 32, - // icon size - fontSize: '20px', }, '&.extra-large': { height: 40, - padding: '8px 10px', - // icon size - fontSize: '20px', + fontWeight: 600, }, // color '&.disabled': { @@ -49,45 +45,34 @@ export const inputWrapper = style({ '&.warning': { borderColor: 'var(--affine-warning-color)', }, + '&.default': { + borderColor: 'var(--affine-border-color)', + }, '&.default.focus': { borderColor: 'var(--affine-primary-color)', - boxShadow: 'var(--affine-active-shadow)', + boxShadow: '0px 0px 0px 2px rgba(30, 150, 235, 0.30);', }, }, }); export const input = style({ + height: '100%', width: '0', flex: 1, - fontSize: 'var(--affine-font-xs)', - lineHeight: '20px', - fontWeight: '500', - color: 'var(--affine-text-primary-color)', boxSizing: 'border-box', // prevent default style WebkitAppearance: 'none', WebkitTapHighlightColor: 'transparent', outline: 'none', border: 'none', + background: 'transparent', selectors: { '&::placeholder': { color: 'var(--affine-placeholder-color)', }, - '&:autofill, &:-webkit-autofill, &:-internal-autofill-selected, &:-webkit-autofill:hover, &:-webkit-autofill:focus, &:-webkit-autofill:active': - { - // The reason for using ‘!important’ here is: - // The user agent style sheets of many browsers utilise !important in their :-webkit-autofill style declarations. - // https://developer.mozilla.org/en-US/docs/Web/CSS/:autofill#:~:text=%2C%20254)-,!important,-%3B%0Abackground%2Dimage - backgroundColor: 'var(--affine-white-10) !important', - ['-webkit-box-shadow' as string]: 'none !important', - }, '&:disabled': { color: 'var(--affine-text-disable-color)', }, - '&.large, &.extra-large': { - fontSize: 'var(--affine-font-base)', - lineHeight: '24px', - }, }, }); diff --git a/packages/frontend/component/src/ui/loading/loading.stories.tsx b/packages/frontend/component/src/ui/loading/loading.stories.tsx new file mode 100644 index 0000000000..ec1d8ca25a --- /dev/null +++ b/packages/frontend/component/src/ui/loading/loading.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryFn } from '@storybook/react'; + +import { Loading, type LoadingProps } from './loading'; + +export default { + title: 'UI/Loading', + component: Loading, +} satisfies Meta; + +const Template: StoryFn = args => ; + +export const Default: StoryFn = Template.bind(undefined); +Default.args = {}; diff --git a/packages/frontend/component/src/ui/loading/loading.tsx b/packages/frontend/component/src/ui/loading/loading.tsx index 78f7f39513..49e72705c2 100644 --- a/packages/frontend/component/src/ui/loading/loading.tsx +++ b/packages/frontend/component/src/ui/loading/loading.tsx @@ -11,20 +11,25 @@ export const Loading = ({ size, speed = 1.2 }: LoadingProps) => { return ( ); }; diff --git a/packages/frontend/component/src/ui/lottie/collections-icon.stories.tsx b/packages/frontend/component/src/ui/lottie/collections-icon.stories.tsx new file mode 100644 index 0000000000..c6d07f3489 --- /dev/null +++ b/packages/frontend/component/src/ui/lottie/collections-icon.stories.tsx @@ -0,0 +1,18 @@ +import type { Meta, StoryFn } from '@storybook/react'; + +import { + AnimatedCollectionsIcon, + type CollectionsIconProps, +} from './collections-icon'; + +export default { + title: 'UI/Lottie/Collection Icons', + component: AnimatedCollectionsIcon, +} satisfies Meta; + +const Template: StoryFn = args => ( + +); + +export const Default: StoryFn = Template.bind(undefined); +Default.args = {}; diff --git a/packages/frontend/component/src/ui/lottie/delete-icon.stories.tsx b/packages/frontend/component/src/ui/lottie/delete-icon.stories.tsx new file mode 100644 index 0000000000..4d72581a29 --- /dev/null +++ b/packages/frontend/component/src/ui/lottie/delete-icon.stories.tsx @@ -0,0 +1,15 @@ +import type { Meta, StoryFn } from '@storybook/react'; + +import { AnimatedDeleteIcon, type DeleteIconProps } from './delete-icon'; + +export default { + title: 'UI/Lottie/Delete Icon', + component: AnimatedDeleteIcon, +} satisfies Meta; + +const Template: StoryFn = args => ( + +); + +export const Default: StoryFn = Template.bind(undefined); +Default.args = {}; diff --git a/packages/frontend/component/src/ui/menu/index.ts b/packages/frontend/component/src/ui/menu/index.ts new file mode 100644 index 0000000000..405ef1e04c --- /dev/null +++ b/packages/frontend/component/src/ui/menu/index.ts @@ -0,0 +1,7 @@ +export * from './menu'; +export * from './menu.types'; +export * from './menu-icon'; +export * from './menu-item'; +export * from './menu-separator'; +export * from './menu-sub'; +export * from './menu-trigger'; diff --git a/packages/frontend/component/src/ui/menu/menu-icon.tsx b/packages/frontend/component/src/ui/menu/menu-icon.tsx new file mode 100644 index 0000000000..f0b1497f01 --- /dev/null +++ b/packages/frontend/component/src/ui/menu/menu-icon.tsx @@ -0,0 +1,39 @@ +import clsx from 'clsx'; +import type { PropsWithChildren, ReactNode } from 'react'; +import { forwardRef, type HTMLAttributes, useMemo } from 'react'; + +import { menuItemIcon } from './styles.css'; + +export interface MenuIconProps + extends PropsWithChildren, + HTMLAttributes { + icon?: ReactNode; + position?: 'start' | 'end'; +} + +export const MenuIcon = forwardRef( + ({ children, icon, position = 'start', className, ...otherProps }, ref) => { + return ( +
+ clsx( + menuItemIcon, + { + end: position === 'end', + start: position === 'start', + }, + className + ), + [className, position] + )} + {...otherProps} + > + {icon || children} +
+ ); + } +); + +MenuIcon.displayName = 'MenuIcon'; diff --git a/packages/frontend/component/src/ui/menu/menu-item.tsx b/packages/frontend/component/src/ui/menu/menu-item.tsx new file mode 100644 index 0000000000..a6278f9648 --- /dev/null +++ b/packages/frontend/component/src/ui/menu/menu-item.tsx @@ -0,0 +1,33 @@ +import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; + +import type { MenuItemProps } from './menu.types'; +import { useMenuItem } from './use-menu-item'; + +export const MenuItem = ({ + children: propsChildren, + type = 'default', + className: propsClassName, + preFix, + endFix, + checked, + selected, + block, + ...otherProps +}: MenuItemProps) => { + const { className, children } = useMenuItem({ + children: propsChildren, + className: propsClassName, + type, + preFix, + endFix, + checked, + selected, + block, + }); + + return ( + + {children} + + ); +}; diff --git a/packages/frontend/component/src/ui/menu/menu-separator.tsx b/packages/frontend/component/src/ui/menu/menu-separator.tsx new file mode 100644 index 0000000000..dc306549e0 --- /dev/null +++ b/packages/frontend/component/src/ui/menu/menu-separator.tsx @@ -0,0 +1,21 @@ +import type { MenuSeparatorProps } from '@radix-ui/react-dropdown-menu'; +import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; +import clsx from 'clsx'; +import { useMemo } from 'react'; + +import * as styles from './styles.css'; + +export const MenuSeparator = ({ + className, + ...otherProps +}: MenuSeparatorProps) => { + return ( + clsx(styles.menuSeparator, className), + [className] + )} + {...otherProps} + /> + ); +}; diff --git a/packages/frontend/component/src/ui/menu/menu-sub.tsx b/packages/frontend/component/src/ui/menu/menu-sub.tsx new file mode 100644 index 0000000000..6b947feece --- /dev/null +++ b/packages/frontend/component/src/ui/menu/menu-sub.tsx @@ -0,0 +1,72 @@ +import { ArrowRightSmallIcon } from '@blocksuite/icons'; +import type { + DropdownMenuSubProps, + MenuPortalProps, + MenuSubContentProps, +} from '@radix-ui/react-dropdown-menu'; +import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; +import clsx from 'clsx'; +import type { ReactNode } from 'react'; +import { useMemo } from 'react'; + +import type { MenuItemProps } from './menu.types'; +import { MenuIcon } from './menu-icon'; +import * as styles from './styles.css'; +import { useMenuItem } from './use-menu-item'; +export interface MenuSubProps { + children: ReactNode; + items: ReactNode; + triggerOptions?: Omit; + portalOptions?: Omit; + subOptions?: Omit; + subContentOptions?: Omit; +} + +export const MenuSub = ({ + children: propsChildren, + items, + portalOptions, + subOptions, + triggerOptions: { + className: propsClassName, + preFix, + endFix, + type, + ...otherTriggerOptions + } = {}, + subContentOptions: { + className: subContentClassName = '', + ...otherSubContentOptions + } = {}, +}: MenuSubProps) => { + const { className, children } = useMenuItem({ + children: propsChildren, + className: propsClassName, + type, + preFix, + endFix, + }); + + return ( + + + {children} + + + + + + clsx(styles.menuContent, subContentClassName), + [subContentClassName] + )} + {...otherSubContentOptions} + > + {items} + + + + ); +}; diff --git a/packages/frontend/component/src/ui/menu/menu-trigger.stories.tsx b/packages/frontend/component/src/ui/menu/menu-trigger.stories.tsx new file mode 100644 index 0000000000..c6222af142 --- /dev/null +++ b/packages/frontend/component/src/ui/menu/menu-trigger.stories.tsx @@ -0,0 +1,17 @@ +import type { Meta, StoryFn } from '@storybook/react'; + +import { MenuTrigger, type MenuTriggerProps } from '.'; + +export default { + title: 'UI/MenuTrigger', + component: MenuTrigger, +} satisfies Meta; + +const Template: StoryFn = args => ( +
+ This is a menu trigger +
+); + +export const Default: StoryFn = Template.bind(undefined); +Default.args = {}; diff --git a/packages/frontend/component/src/ui/menu/menu-trigger.tsx b/packages/frontend/component/src/ui/menu/menu-trigger.tsx new file mode 100644 index 0000000000..483e83c8d0 --- /dev/null +++ b/packages/frontend/component/src/ui/menu/menu-trigger.tsx @@ -0,0 +1,87 @@ +import { ArrowDownSmallIcon } from '@blocksuite/icons'; +import { assignInlineVars } from '@vanilla-extract/dynamic'; +import clsx from 'clsx'; +import type { PropsWithChildren } from 'react'; +import { + type CSSProperties, + forwardRef, + type HTMLAttributes, + type ReactNode, +} from 'react'; + +import { MenuIcon } from './menu-icon'; +import * as styles from './styles.css'; +import { triggerWidthVar } from './styles.css'; + +export interface MenuTriggerProps + extends PropsWithChildren, + HTMLAttributes { + width?: CSSProperties['width']; + disabled?: boolean; + noBorder?: boolean; + status?: 'error' | 'success' | 'warning' | 'default'; + size?: 'default' | 'large' | 'extraLarge'; + preFix?: ReactNode; + endFix?: ReactNode; + block?: boolean; +} + +export const MenuTrigger = forwardRef( + ( + { + disabled, + noBorder = false, + className, + status = 'default', + size = 'default', + preFix, + endFix, + block = false, + children, + width, + style = {}, + ...otherProps + }, + ref + ) => { + return ( + + ); + } +); + +MenuTrigger.displayName = 'MenuTrigger'; diff --git a/packages/frontend/component/src/ui/menu/menu.stories.tsx b/packages/frontend/component/src/ui/menu/menu.stories.tsx new file mode 100644 index 0000000000..6509f3e7ba --- /dev/null +++ b/packages/frontend/component/src/ui/menu/menu.stories.tsx @@ -0,0 +1,203 @@ +import { InformationIcon } from '@blocksuite/icons'; +import type { Meta, StoryFn } from '@storybook/react'; +import { type ReactNode, useCallback, useState } from 'react'; + +import { Button } from '../button'; +import { Tooltip } from '../tooltip'; +import { + Menu, + MenuIcon, + MenuItem, + type MenuItemProps, + type MenuProps, + MenuSeparator, + MenuSub, + MenuTrigger, +} from '.'; + +export default { + title: 'UI/Menu', + component: Menu, +} satisfies Meta; + +const Template: StoryFn = args => ( + + menu trigger + +); + +interface Items { + label: ReactNode; + type?: MenuItemProps['type']; + preFix?: MenuItemProps['preFix']; + disabled?: boolean; + divider?: boolean; + subItems?: Items[]; + block?: boolean; +} + +const items: Items[] = [ + { + label: 'default menu item 1', + }, + { + label: 'menu item with icon', + preFix: ( + + + + + + ), + }, + { + label: ( + + + Write, Draw, and Plan All at Once Notion Open Source Alternative One + hyper-fused platform for wildly creative minds + + + ), + block: true, + }, + { + label: 'default disabled menu item', + disabled: true, + }, + { + label: 'danger menu item', + type: 'danger', + block: true, + preFix: ( + + + + + + ), + }, + { + label: 'warning menu item', + type: 'warning', + divider: true, + }, + + { + label: 'menu item with sub menu', + subItems: [ + { + label: 'sub menu item 1', + }, + { + label: 'sub menu item 1', + }, + ], + }, + + { + label: 'menu item with deep sub menu', + subItems: [ + { + label: 'sub menu item 1', + }, + { + label: 'sub menu with sub', + subItems: [ + { + label: 'sub menu item 2-1', + }, + { + label: 'sub menu item 2-2', + }, + ], + }, + ], + }, +]; + +export const Default: StoryFn = Template.bind(undefined); + +const ItemRender = ({ label, divider, subItems, ...otherProps }: Items) => { + const onSelect = useCallback(() => { + console.log('value', label); + }, [label]); + + if (subItems) { + return ( + <> + ( + + ))} + triggerOptions={otherProps} + > + {label} + + {divider ? : null} + + ); + } + + return ( + <> + + {label} + + {divider ? : null} + + ); +}; + +Default.args = { + items: items.map((props, i) => { + return ; + }), +}; + +const selectList = [ + { name: 'AFFiNE', value: '1' }, + { name: 'blocksuite', value: '2' }, + { name: 'octobase', value: '3' }, + { name: 'virgo', value: '4' }, +]; +const SelectItems = ({ + selectedValue, + onSelect, +}: { + selectedValue: string; + onSelect: (value: string) => void; +}) => { + return selectList.map(({ name, value }) => ( + onSelect(value)} + > + {name} + + )); +}; + +const AsSelectTemplate: StoryFn = () => { + const [value, setValue] = useState('1'); + const name = selectList.find(item => item.value === value)?.name; + return ( + }> + + + ); +}; + +export const AsSelect: StoryFn = AsSelectTemplate.bind({}); diff --git a/packages/frontend/component/src/ui/menu/menu.tsx b/packages/frontend/component/src/ui/menu/menu.tsx new file mode 100644 index 0000000000..620e3e9efe --- /dev/null +++ b/packages/frontend/component/src/ui/menu/menu.tsx @@ -0,0 +1,52 @@ +import type { + DropdownMenuProps, + MenuContentProps, + MenuPortalProps, +} from '@radix-ui/react-dropdown-menu'; +import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; +import clsx from 'clsx'; +import type { ReactNode } from 'react'; +import { useMemo } from 'react'; + +import * as styles from './styles.css'; + +export interface MenuProps { + children: ReactNode; + items: ReactNode; + portalOptions?: Omit; + rootOptions?: Omit; + contentOptions?: Omit; +} + +export const Menu = ({ + children, + items, + portalOptions, + rootOptions, + contentOptions: { + className = '', + style: contentStyle = {}, + ...otherContentOptions + } = {}, +}: MenuProps) => { + return ( + + {children} + + + clsx(styles.menuContent, className), + [className] + )} + sideOffset={5} + align="start" + style={{ zIndex: 'var(--affine-z-index-popover)', ...contentStyle }} + {...otherContentOptions} + > + {items} + + + + ); +}; diff --git a/packages/frontend/component/src/ui/menu/menu.types.ts b/packages/frontend/component/src/ui/menu/menu.types.ts new file mode 100644 index 0000000000..658f2b02d7 --- /dev/null +++ b/packages/frontend/component/src/ui/menu/menu.types.ts @@ -0,0 +1,11 @@ +import type { MenuItemProps as MenuItemPropsPrimitive } from '@radix-ui/react-dropdown-menu'; + +export interface MenuItemProps + extends Omit { + type?: 'default' | 'warning' | 'danger'; + preFix?: React.ReactNode; + endFix?: React.ReactNode; + checked?: boolean; + selected?: boolean; + block?: boolean; +} diff --git a/packages/frontend/component/src/ui/menu/styles.css.ts b/packages/frontend/component/src/ui/menu/styles.css.ts new file mode 100644 index 0000000000..8a95ad1ac8 --- /dev/null +++ b/packages/frontend/component/src/ui/menu/styles.css.ts @@ -0,0 +1,157 @@ +import { createVar, style } from '@vanilla-extract/css'; +export const triggerWidthVar = createVar('triggerWidthVar'); + +export const menuContent = style({ + minWidth: '180px', + color: 'var(--affine-text-primary-color)', + borderRadius: '8px', + padding: '8px', + fontSize: 'var(--affine-font-sm)', + fontWeight: '400', + backgroundColor: 'var(--affine-background-overlay-panel-color)', + boxShadow: 'var(--affine-menu-shadow)', + userSelect: 'none', +}); + +export const menuItem = style({ + maxWidth: '296px', + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + padding: '4px 12px', + borderRadius: '4px', + lineHeight: '22px', + border: 'none', + outline: 'none', + cursor: 'pointer', + boxSizing: 'border-box', + selectors: { + '&:not(:last-of-type)': { + marginBottom: '4px', + }, + '&.block': { maxWidth: '100%' }, + '&[data-disabled]': { + color: 'var(--affine-text-disable-color)', + pointerEvents: 'none', + cursor: 'not-allowed', + }, + '&[data-highlighted]': { + backgroundColor: 'var(--affine-hover-color)', + }, + + '&:hover': { + backgroundColor: 'var(--affine-hover-color)', + }, + '&.danger:hover': { + color: 'var(--affine-error-color)', + backgroundColor: 'var(--affine-background-error-color)', + }, + + '&.warning:hover': { + color: 'var(--affine-warning-color)', + backgroundColor: 'var(--affine-background-warning-color)', + }, + + '&.selected, &.checked': { + backgroundColor: 'var(--affine-hover-color)', + color: 'var(--affine-primary-color)', + }, + }, +}); + +export const menuSpan = style({ + flex: 1, + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + textAlign: 'left', +}); +export const menuItemIcon = style({ + display: 'flex', + flexShrink: 0, + fontSize: 'var(--affine-font-h-5)', + color: 'var(--affine-icon-color)', + selectors: { + '&.start': { marginRight: '8px' }, + '&.end': { marginLeft: '8px' }, + '&.selected, &.checked': { + color: 'var(--affine-primary-color)', + }, + + [`${menuItem}.danger:hover &`]: { + color: 'var(--affine-error-color)', + }, + [`${menuItem}.warning:hover &`]: { + color: 'var(--affine-warning-color)', + }, + }, +}); + +export const menuSeparator = style({ + height: '1px', + backgroundColor: 'var(--affine-border-color)', + marginTop: '12px', + marginBottom: '8px', +}); + +export const menuTrigger = style({ + vars: { + [triggerWidthVar]: 'auto', + }, + width: triggerWidthVar, + height: 28, + lineHeight: '22px', + padding: '0 10px', + color: 'var(--affine-text-primary-color)', + border: '1px solid', + backgroundColor: 'var(--affine-white)', + borderRadius: 8, + display: 'inline-flex', + justifyContent: 'space-between', + alignItems: 'center', + fontSize: 'var(--affine-font-xs)', + cursor: 'pointer', + ['WebkitAppRegion' as string]: 'no-drag', + borderColor: 'var(--affine-border-color)', + outline: 'none', + + selectors: { + '&:hover': { + background: 'var(--affine-hover-color)', + }, + '&.no-border': { + border: 'unset', + }, + '&.block': { + display: 'flex', + width: '100%', + }, + // size + '&.large': { + height: 32, + }, + '&.extra-large': { + height: 40, + fontWeight: 600, + }, + // color + '&.disabled': { + cursor: 'default', + color: 'var(--affine-disable-color)', + pointerEvents: 'none', + }, + // TODO: wait for design + '&.error': { + // borderColor: 'var(--affine-error-color)', + }, + '&.success': { + // borderColor: 'var(--affine-success-color)', + }, + '&.warning': { + // borderColor: 'var(--affine-warning-color)', + }, + '&.default': { + // borderColor: 'var(--affine-border-color)', + }, + }, +}); diff --git a/packages/frontend/component/src/ui/menu/use-menu-item.tsx b/packages/frontend/component/src/ui/menu/use-menu-item.tsx new file mode 100644 index 0000000000..a3b4a3a296 --- /dev/null +++ b/packages/frontend/component/src/ui/menu/use-menu-item.tsx @@ -0,0 +1,73 @@ +import { DoneIcon } from '@blocksuite/icons'; +import clsx from 'clsx'; +import { useMemo } from 'react'; + +import { type MenuItemProps } from './menu.types'; +import { MenuIcon } from './menu-icon'; +import * as styles from './styles.css'; + +interface useMenuItemProps { + children: MenuItemProps['children']; + type: MenuItemProps['type']; + className: MenuItemProps['className']; + preFix: MenuItemProps['preFix']; + endFix: MenuItemProps['endFix']; + checked?: MenuItemProps['checked']; + selected?: MenuItemProps['selected']; + block?: MenuItemProps['block']; +} + +export const useMenuItem = ({ + children: propsChildren, + type = 'default', + className: propsClassName, + preFix, + endFix, + checked, + selected, + block, +}: useMenuItemProps) => { + const className = useMemo( + () => + clsx( + styles.menuItem, + { + danger: type === 'danger', + warning: type === 'warning', + checked, + selected, + block, + }, + propsClassName + ), + [block, checked, propsClassName, selected, type] + ); + + const children = useMemo( + () => ( + <> + {preFix} + {propsChildren} + {endFix} + + {checked || selected ? ( + + + + ) : null} + + ), + [checked, endFix, preFix, propsChildren, selected] + ); + + return { + children, + className, + }; +}; diff --git a/packages/frontend/component/src/ui/modal/confirm-modal.tsx b/packages/frontend/component/src/ui/modal/confirm-modal.tsx new file mode 100644 index 0000000000..64ea4e2ae8 --- /dev/null +++ b/packages/frontend/component/src/ui/modal/confirm-modal.tsx @@ -0,0 +1,47 @@ +import { DialogTrigger } from '@radix-ui/react-dialog'; +import clsx from 'clsx'; + +import type { ButtonProps } from '../button'; +import { Button } from '../button'; +import { Modal, type ModalProps } from './modal'; +import * as styles from './styles.css'; + +export interface ConfirmModalProps extends ModalProps { + confirmButtonOptions?: ButtonProps; + onConfirm?: () => void; + cancelText?: string; + cancelButtonOptions?: ButtonProps; +} + +export const ConfirmModal = ({ + children, + confirmButtonOptions, + // FIXME: we need i18n + cancelText = 'Cancel', + cancelButtonOptions, + onConfirm, + width = 480, + ...props +}: ConfirmModalProps) => { + return ( + + {children ? ( +
{children}
+ ) : null} +
+ + + + +
+
+ ); +}; diff --git a/packages/frontend/component/src/ui/modal/index.ts b/packages/frontend/component/src/ui/modal/index.ts new file mode 100644 index 0000000000..1d7b6892d3 --- /dev/null +++ b/packages/frontend/component/src/ui/modal/index.ts @@ -0,0 +1,2 @@ +export * from './confirm-modal'; +export * from './modal'; diff --git a/packages/frontend/component/src/ui/modal/modal.stories.tsx b/packages/frontend/component/src/ui/modal/modal.stories.tsx new file mode 100644 index 0000000000..906ed94fb6 --- /dev/null +++ b/packages/frontend/component/src/ui/modal/modal.stories.tsx @@ -0,0 +1,69 @@ +import type { Meta, StoryFn } from '@storybook/react'; +import { useCallback, useState } from 'react'; + +import { Button } from '../button'; +import { Input, type InputProps } from '../input'; +import { ConfirmModal, type ConfirmModalProps } from './confirm-modal'; +import { Modal, type ModalProps } from './modal'; + +export default { + title: 'UI/Modal', + component: Modal, + argTypes: {}, +} satisfies Meta; + +const Template: StoryFn = args => { + const [open, setOpen] = useState(false); + + return ( + <> + + + + ); +}; + +export const Default: StoryFn = Template.bind(undefined); +Default.args = { + title: 'Modal Title', + description: + 'If the day is done, if birds sing no more, if the wind has flagged tired, then draw the veil of darkness thick upon me, even as thou hast wrapt the earth with the coverlet of sleep and tenderly closed the petals of the drooping lotus at dusk.', +}; + +const wait = () => new Promise(resolve => setTimeout(resolve, 1000)); +const ConfirmModalTemplate: StoryFn = () => { + const [open, setOpen] = useState(false); + const [loading, setLoading] = useState(false); + const [inputStatus, setInputStatus] = + useState('default'); + + const handleConfirm = useCallback(async () => { + setLoading(true); + await wait(); + setInputStatus(inputStatus !== 'error' ? 'error' : 'success'); + setLoading(false); + }, [inputStatus]); + + return ( + <> + + + + + + ); +}; + +export const Confirm: StoryFn = + ConfirmModalTemplate.bind(undefined); diff --git a/packages/frontend/component/src/ui/modal/modal.tsx b/packages/frontend/component/src/ui/modal/modal.tsx new file mode 100644 index 0000000000..bf14329fce --- /dev/null +++ b/packages/frontend/component/src/ui/modal/modal.tsx @@ -0,0 +1,115 @@ +import { CloseIcon } from '@blocksuite/icons'; +import type { + DialogContentProps, + DialogOverlayProps, + DialogPortalProps, + DialogProps, +} from '@radix-ui/react-dialog'; +import * as Dialog from '@radix-ui/react-dialog'; +import { assignInlineVars } from '@vanilla-extract/dynamic'; +import clsx from 'clsx'; +import { type CSSProperties, forwardRef } from 'react'; + +import { IconButton, type IconButtonProps } from '../button'; +import * as styles from './styles.css'; + +export interface ModalProps extends DialogProps { + width?: CSSProperties['width']; + height?: CSSProperties['height']; + minHeight?: CSSProperties['minHeight']; + title?: string; + description?: string; + withoutCloseButton?: boolean; + + portalOptions?: DialogPortalProps; + contentOptions?: DialogContentProps; + overlayOptions?: DialogOverlayProps; + closeButtonOptions?: IconButtonProps; +} + +const getVar = (style: number | string = '', defaultValue = '') => { + return style + ? typeof style === 'number' + ? `${style}px` + : style + : defaultValue; +}; + +export const Modal = forwardRef( + ( + { + width, + height, + minHeight = 194, + title, + description, + withoutCloseButton = false, + + portalOptions, + contentOptions: { + style: contentStyle, + className: contentClassName, + ...otherContentOptions + } = {}, + overlayOptions: { + className: overlayClassName, + ...otherOverlayOptions + } = {}, + closeButtonOptions = {}, + children, + ...props + }, + ref + ) => ( + + + +
+ + {withoutCloseButton ? null : ( + + + + + + )} + {title ? ( + + {title} + + ) : null} + {description ? ( + + {description} + + ) : null} + + {children} + +
+
+
+ ) +); + +Modal.displayName = 'Modal'; diff --git a/packages/frontend/component/src/ui/modal/styles.css.ts b/packages/frontend/component/src/ui/modal/styles.css.ts new file mode 100644 index 0000000000..116e9c1021 --- /dev/null +++ b/packages/frontend/component/src/ui/modal/styles.css.ts @@ -0,0 +1,84 @@ +import { createVar, style } from '@vanilla-extract/css'; + +export const widthVar = createVar('widthVar'); +export const heightVar = createVar('heightVar'); +export const minHeightVar = createVar('minHeightVar'); + +export const modalOverlay = style({ + position: 'fixed', + inset: 0, + backgroundColor: 'var(--affine-background-modal-color)', + zIndex: 'var(--affine-z-index-modal)', +}); + +export const modalContentWrapper = style({ + position: 'fixed', + inset: 0, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + zIndex: 'var(--affine-z-index-modal)', +}); + +export const modalContent = style({ + vars: { + [widthVar]: '', + [heightVar]: '', + [minHeightVar]: '', + }, + width: widthVar, + height: heightVar, + minHeight: minHeightVar, + boxSizing: 'border-box', + fontSize: 'var(--affine-font-base)', + fontWeight: '400', + lineHeight: '1.6', + padding: '20px 24px', + position: 'relative', + backgroundColor: 'var(--affine-background-overlay-panel-color)', + boxShadow: 'var(--affine-popover-shadow)', + borderRadius: '12px', + maxHeight: 'calc(100vh - 32px)', + // :focus-visible will set outline + outline: 'none', +}); + +export const closeButton = style({ + position: 'absolute', + top: '22px', + right: '20px', +}); + +export const modalHeader = style({ + fontSize: 'var(--affine-font-h-6)', + fontWeight: '600', + lineHeight: '1.45', + marginBottom: '12px', +}); +export const modalDescription = style({ + // marginBottom: '20px', +}); + +export const modalFooter = style({ + display: 'flex', + justifyContent: 'flex-end', + alignItems: 'center', + paddingTop: '40px', + marginTop: 'auto', + gap: '20px', + selectors: { + '&.modalFooterWithChildren': { + paddingTop: '20px', + }, + }, +}); + +export const confirmModalContent = style({ + marginTop: '12px', + marginBottom: '20px', +}); + +export const confirmModalContainer = style({ + display: 'flex', + flexDirection: 'column', +}); diff --git a/packages/frontend/component/src/ui/popover/index.ts b/packages/frontend/component/src/ui/popover/index.ts new file mode 100644 index 0000000000..1b82f8c720 --- /dev/null +++ b/packages/frontend/component/src/ui/popover/index.ts @@ -0,0 +1,4 @@ +/** + * @deprecated + */ +export * from './popover'; diff --git a/packages/frontend/component/src/ui/popover/popover.tsx b/packages/frontend/component/src/ui/popover/popover.tsx new file mode 100644 index 0000000000..e9031c79c8 --- /dev/null +++ b/packages/frontend/component/src/ui/popover/popover.tsx @@ -0,0 +1,50 @@ +import type { + PopoverContentProps, + PopoverPortalProps, + PopoverProps as PopoverPrimitiveProps, +} from '@radix-ui/react-popover'; +import * as PopoverPrimitive from '@radix-ui/react-popover'; +import clsx from 'clsx'; +import { type ReactNode, useMemo } from 'react'; + +import * as styles from './styles.css'; + +export interface PopoverProps extends PopoverPrimitiveProps { + content?: ReactNode; + portalOptions?: PopoverPortalProps; + contentOptions?: PopoverContentProps; +} +export const Popover = ({ + content, + children, + portalOptions, + contentOptions: { + className: contentClassName, + style: contentStyle, + ...otherContentOptions + } = {}, + ...props +}: PopoverProps) => { + return ( + + {children} + + + clsx(styles.popoverContent, contentClassName), + [contentClassName] + )} + sideOffset={5} + align="start" + style={{ zIndex: 'var(--affine-z-index-popover)', ...contentStyle }} + {...otherContentOptions} + > + {content} + + + + ); +}; + +Popover.displayName = 'Popover'; diff --git a/packages/frontend/component/src/ui/popover/styles.css.ts b/packages/frontend/component/src/ui/popover/styles.css.ts new file mode 100644 index 0000000000..af9e7b509c --- /dev/null +++ b/packages/frontend/component/src/ui/popover/styles.css.ts @@ -0,0 +1,13 @@ +import { style } from '@vanilla-extract/css'; + +export const popoverContent = style({ + minWidth: '180px', + color: 'var(--affine-text-primary-color)', + borderRadius: '8px', + padding: '8px', + fontSize: 'var(--affine-font-sm)', + fontWeight: '400', + backgroundColor: 'var(--affine-background-overlay-panel-color)', + boxShadow: 'var(--affine-menu-shadow)', + userSelect: 'none', +}); diff --git a/packages/frontend/component/src/ui/scrollbar/scrollbar.stories.tsx b/packages/frontend/component/src/ui/scrollbar/scrollbar.stories.tsx new file mode 100644 index 0000000000..ff8bd3b3bc --- /dev/null +++ b/packages/frontend/component/src/ui/scrollbar/scrollbar.stories.tsx @@ -0,0 +1,24 @@ +import type { Meta, StoryFn } from '@storybook/react'; + +import { ScrollableContainer, type ScrollableContainerProps } from '.'; + +export default { + title: 'UI/Scrollbar', + component: ScrollableContainer, +} satisfies Meta; + +const Template: StoryFn = args => ( +
+ +
    + {Array.from({ length: 100 }).map((_, index) => ( +
  • item {index}
  • + ))} +
+
+
+); + +export const Default: StoryFn = + Template.bind(undefined); +Default.args = {}; diff --git a/packages/frontend/component/src/ui/skeleton/skeleton.stories.tsx b/packages/frontend/component/src/ui/skeleton/skeleton.stories.tsx new file mode 100644 index 0000000000..58b5b94b0f --- /dev/null +++ b/packages/frontend/component/src/ui/skeleton/skeleton.stories.tsx @@ -0,0 +1,39 @@ +import type { Meta, StoryFn } from '@storybook/react'; + +import { Skeleton, type SkeletonProps } from '.'; + +export default { + title: 'UI/Skeleton', + component: Skeleton, +} satisfies Meta; + +const Template: StoryFn = args => ( + <> + {Array.from({ length: 4 }).map(i => ( +
+ +
+ ))} + +); + +export const Default: StoryFn = Template.bind(undefined); +Default.args = {}; + +export const Circle: StoryFn = Template.bind(undefined); +Circle.args = { + variant: 'circular', +}; + +export const Rectangle: StoryFn = Template.bind(undefined); +Rectangle.args = { + variant: 'rectangular', +}; + +export const Text: StoryFn = Template.bind(undefined); +Text.args = { + variant: 'text', +}; diff --git a/packages/frontend/component/src/ui/skeleton/types.ts b/packages/frontend/component/src/ui/skeleton/types.ts index e0ad3eaae9..bda6b78418 100644 --- a/packages/frontend/component/src/ui/skeleton/types.ts +++ b/packages/frontend/component/src/ui/skeleton/types.ts @@ -1,5 +1,8 @@ import type { HTMLAttributes, PropsWithChildren } from 'react'; +/** + * @reference These props are migrated from [MUI Skeleton props](https://mui.com/material-ui/api/skeleton/#props) + */ export interface SkeletonProps extends PropsWithChildren, HTMLAttributes { @@ -12,22 +15,19 @@ export interface SkeletonProps * The type of content that will be rendered. * @default `'text'` */ - variant?: 'circular' | 'rectangular' | 'rounded' | 'text' | string; + variant?: 'circular' | 'rectangular' | 'rounded' | 'text'; /** * Width of the skeleton. Useful when the skeleton is inside an inline element with no width of its own. + * Number values are treated as pixels. */ width?: number | string; /** * Height of the skeleton. Useful when you don't want to adapt the skeleton to a text element but for instance a card. + * Number values are treated as pixels. */ height?: number | string; - - /** - * Wrapper component. If not provided, the default element is a div. - */ - wrapper?: string; } export type PickStringFromUnion = T extends string ? T : never; diff --git a/packages/frontend/component/src/ui/switch/switch.stories.tsx b/packages/frontend/component/src/ui/switch/switch.stories.tsx new file mode 100644 index 0000000000..f50875ac78 --- /dev/null +++ b/packages/frontend/component/src/ui/switch/switch.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryFn } from '@storybook/react'; + +import { Switch, type SwitchProps } from '.'; + +export default { + title: 'UI/Switch', + component: Switch, +} satisfies Meta; + +const Template: StoryFn = args => ; + +export const Default: StoryFn = Template.bind(undefined); +Default.args = {}; diff --git a/packages/frontend/component/src/ui/switch/switch.tsx b/packages/frontend/component/src/ui/switch/switch.tsx index fb65a98869..b26c896396 100644 --- a/packages/frontend/component/src/ui/switch/switch.tsx +++ b/packages/frontend/component/src/ui/switch/switch.tsx @@ -9,7 +9,7 @@ import { import * as styles from './index.css'; -type SwitchProps = Omit, 'onChange'> & { +export type SwitchProps = Omit, 'onChange'> & { checked?: boolean; onChange?: (checked: boolean) => void; children?: ReactNode; diff --git a/packages/frontend/component/src/ui/table/table.stories.tsx b/packages/frontend/component/src/ui/table/table.stories.tsx new file mode 100644 index 0000000000..2d48a07b16 --- /dev/null +++ b/packages/frontend/component/src/ui/table/table.stories.tsx @@ -0,0 +1,46 @@ +import type { Meta, StoryFn } from '@storybook/react'; + +import { + Table, + TableBody, + TableBodyRow, + TableCell, + TableHead, + TableHeadRow, +} from '.'; + +export default { + title: 'UI/Table', + component: Table, +} satisfies Meta; + +const Template: StoryFn = args => ( + + + + Title 1 + Title 2 + Title 3 + Title 4 + + + + + {Array.from({ length: 10 }).map((_, rowNum) => { + return ( + + {Array.from({ length: 4 }).map((_, colNum) => { + return ( + + Cell {rowNum}-{colNum} + + ); + })} + + ); + })} + +
+); + +export const Default: StoryFn = Template.bind(undefined); diff --git a/packages/frontend/component/src/ui/toast/toast.stories.tsx b/packages/frontend/component/src/ui/toast/toast.stories.tsx new file mode 100644 index 0000000000..0a058f473c --- /dev/null +++ b/packages/frontend/component/src/ui/toast/toast.stories.tsx @@ -0,0 +1,20 @@ +import { useCallback, useState } from 'react'; + +import { Button } from '../button'; +import { toast } from '.'; + +export default { + title: 'UI/Toast', + component: () => null, +}; + +export const Default = () => { + const [count, setCount] = useState(1); + + const showToast = useCallback(() => { + toast(`Toast ${count}`); + setCount(count + 1); + }, [count]); + + return ; +}; diff --git a/packages/frontend/component/src/ui/tooltip/index.ts b/packages/frontend/component/src/ui/tooltip/index.ts new file mode 100644 index 0000000000..d605d91ad0 --- /dev/null +++ b/packages/frontend/component/src/ui/tooltip/index.ts @@ -0,0 +1,5 @@ +import { Tooltip } from './tooltip'; + +export * from './tooltip'; + +export default Tooltip; diff --git a/packages/frontend/component/src/ui/tooltip/styles.css.ts b/packages/frontend/component/src/ui/tooltip/styles.css.ts new file mode 100644 index 0000000000..0de1af2cf6 --- /dev/null +++ b/packages/frontend/component/src/ui/tooltip/styles.css.ts @@ -0,0 +1,11 @@ +import { style } from '@vanilla-extract/css'; + +export const tooltipContent = style({ + backgroundColor: 'var(--affine-tooltip)', + color: 'var(--affine-white)', + padding: '5px 12px', + fontSize: 'var(--affine-font-sm)', + lineHeight: '22px', + borderRadius: '4px', + maxWidth: '280px', +}); diff --git a/packages/frontend/component/src/ui/tooltip/tooltip.stories.tsx b/packages/frontend/component/src/ui/tooltip/tooltip.stories.tsx new file mode 100644 index 0000000000..fcba72f979 --- /dev/null +++ b/packages/frontend/component/src/ui/tooltip/tooltip.stories.tsx @@ -0,0 +1,32 @@ +import type { Meta, StoryFn } from '@storybook/react'; + +import { Button } from '../button'; +import Tooltip, { type TooltipProps } from '.'; + +export default { + title: 'UI/Tooltip', + component: Tooltip, +} satisfies Meta; + +const Template: StoryFn = args => ( + + + +); + +export const Default: StoryFn = Template.bind(undefined); +Default.args = {}; + +export const WithCustomContent: StoryFn = args => ( + +
  • This is a tooltip
  • +
  • With custom content
  • + + } + {...args} + > + +
    +); diff --git a/packages/frontend/component/src/ui/tooltip/tooltip.tsx b/packages/frontend/component/src/ui/tooltip/tooltip.tsx new file mode 100644 index 0000000000..4dc827865c --- /dev/null +++ b/packages/frontend/component/src/ui/tooltip/tooltip.tsx @@ -0,0 +1,60 @@ +import type { + TooltipContentProps, + TooltipPortalProps, + TooltipProps as RootProps, +} from '@radix-ui/react-tooltip'; +import * as TooltipPrimitive from '@radix-ui/react-tooltip'; +import type { ReactElement, ReactNode } from 'react'; + +import * as styles from './styles.css'; + +export interface TooltipProps { + // `children` can not be string, number or even undefined + children: ReactElement; + content?: ReactNode; + side?: TooltipContentProps['side']; + align?: TooltipContentProps['align']; + + rootOptions?: Omit; + portalOptions?: TooltipPortalProps; + options?: Omit; +} + +export const Tooltip = ({ + children, + content, + side = 'top', + align = 'center', + options, + rootOptions, + portalOptions, +}: TooltipProps) => { + if (!content) { + return children; + } + return ( + + + {children} + + + + {content} + + + + + + ); +}; diff --git a/packages/frontend/core/.webpack/config.ts b/packages/frontend/core/.webpack/config.ts index 15e1c1e46c..7b1be2f05b 100644 --- a/packages/frontend/core/.webpack/config.ts +++ b/packages/frontend/core/.webpack/config.ts @@ -168,13 +168,13 @@ export const createConfiguration: ( 'blocks', 'dist' ), - '@blocksuite/editor': blocksuiteBaseDir - ? join(blocksuiteBaseDir, 'packages', 'editor', 'src') + '@blocksuite/presets': blocksuiteBaseDir + ? join(blocksuiteBaseDir, 'packages', 'presets', 'src') : join( workspaceRoot, 'node_modules', '@blocksuite', - 'editor', + 'presets', 'dist' ), '@blocksuite/global': blocksuiteBaseDir diff --git a/packages/frontend/core/.webpack/runtime-config.ts b/packages/frontend/core/.webpack/runtime-config.ts index d613fa11aa..459f5ec857 100644 --- a/packages/frontend/core/.webpack/runtime-config.ts +++ b/packages/frontend/core/.webpack/runtime-config.ts @@ -6,11 +6,9 @@ const require = createRequire(import.meta.url); const packageJson = require('../package.json'); const editorFlags: BlockSuiteFeatureFlags = { - enable_block_hub: true, - enable_toggle_block: false, - enable_bookmark_operation: false, - enable_note_index: false, - enable_set_remote_flag: false, + enable_transformer_clipboard: false, + enable_expand_database_block: false, + enable_bultin_ledits: false, }; export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig { @@ -22,6 +20,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig { enableBroadcastChannelProvider: true, enableDebugPage: true, changelogUrl: 'https://affine.pro/what-is-new', + downloadUrl: 'https://affine.pro/download', imageProxyUrl: 'https://workers.toeverything.workers.dev/proxy/image', enablePreloading: true, enableNewSettingModal: true, @@ -37,7 +36,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig { serverUrlPrefix: 'https://insider.affine.pro', // Let insider be stable environment temporarily. editorFlags, appVersion: packageJson.version, - editorVersion: packageJson.dependencies['@blocksuite/editor'], + editorVersion: packageJson.dependencies['@blocksuite/presets'], appBuildType: 'stable', }, get beta() { @@ -69,6 +68,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig { enableBroadcastChannelProvider: true, enableDebugPage: true, changelogUrl: 'https://github.com/toeverything/AFFiNE/releases', + downloadUrl: 'https://affine.pro/download', imageProxyUrl: 'https://workers.toeverything.workers.dev/proxy/image', enablePreloading: true, enableNewSettingModal: true, @@ -84,7 +84,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig { serverUrlPrefix: 'https://affine.fail', editorFlags, appVersion: packageJson.version, - editorVersion: packageJson.dependencies['@blocksuite/editor'], + editorVersion: packageJson.dependencies['@blocksuite/presets'], appBuildType: 'canary', }, }; diff --git a/packages/frontend/core/.webpack/template.html b/packages/frontend/core/.webpack/template.html index 4c0ee95014..0b8b6e4710 100644 --- a/packages/frontend/core/.webpack/template.html +++ b/packages/frontend/core/.webpack/template.html @@ -2,6 +2,7 @@ + import('../../components/cloud/login-card').then(({ LoginCard }) => ({ @@ -24,36 +16,6 @@ const LoginCard = lazy(() => export const UI = { Provider, LoginCard, - PageDetail: ({ currentWorkspaceId, currentPageId, onLoadEditor }) => { - const workspace = useWorkspace(currentWorkspaceId); - const page = workspace.blockSuiteWorkspace.getPage(currentPageId); - if (!page) { - throw new PageNotFoundError(workspace.blockSuiteWorkspace, currentPageId); - } - // this should be safe because we are under cloud workspace adapter - const currentUser = useCurrentUser(); - const onLoad = useCallback( - (...args) => { - const dispose = onLoadEditor(...args); - workspace.blockSuiteWorkspace.awarenessStore.awareness.setLocalStateField( - 'user', - { - name: currentUser.name, - } - ); - return dispose; - }, - [currentUser, workspace, onLoadEditor] - ); - - return ( - - ); - }, NewSettingsDetail: ({ currentWorkspaceId, onTransformWorkspace, diff --git a/packages/frontend/core/src/adapters/local/index.tsx b/packages/frontend/core/src/adapters/local/index.tsx index 1b99823f23..fbb0bee268 100644 --- a/packages/frontend/core/src/adapters/local/index.tsx +++ b/packages/frontend/core/src/adapters/local/index.tsx @@ -1,8 +1,5 @@ import { DebugLogger } from '@affine/debug'; -import { - DEFAULT_WORKSPACE_NAME, - PageNotFoundError, -} from '@affine/env/constant'; +import { DEFAULT_WORKSPACE_NAME } from '@affine/env/constant'; import type { WorkspaceAdapter } from '@affine/env/workspace'; import { LoadPriority, @@ -14,19 +11,13 @@ import { saveWorkspaceToLocalStorage, } from '@affine/workspace/local/crud'; import { getOrCreateWorkspace } from '@affine/workspace/manager'; -import { getBlockSuiteWorkspaceAtom } from '@toeverything/infra/__internal__/workspace'; import { getCurrentStore } from '@toeverything/infra/atom'; import { initEmptyPage } from '@toeverything/infra/blocksuite'; import { buildShowcaseWorkspace } from '@toeverything/infra/blocksuite'; -import { useAtomValue } from 'jotai'; import { nanoid } from 'nanoid'; import { setPageModeAtom } from '../../atoms'; -import { - NewWorkspaceSettingDetail, - PageDetailEditor, - Provider, -} from '../shared'; +import { NewWorkspaceSettingDetail, Provider } from '../shared'; const logger = new DebugLogger('use-create-first-workspace'); @@ -68,21 +59,6 @@ export const LocalAdapter: WorkspaceAdapter = { CRUD, UI: { Provider, - PageDetail: ({ currentWorkspaceId, currentPageId, onLoadEditor }) => { - const [workspaceAtom] = getBlockSuiteWorkspaceAtom(currentWorkspaceId); - const workspace = useAtomValue(workspaceAtom); - const page = workspace.getPage(currentPageId); - if (!page) { - throw new PageNotFoundError(workspace, currentPageId); - } - return ( - - ); - }, NewSettingsDetail: ({ currentWorkspaceId, onTransformWorkspace, diff --git a/packages/frontend/core/src/adapters/public-cloud/ui.tsx b/packages/frontend/core/src/adapters/public-cloud/ui.tsx index 120be63620..0e033dd58a 100644 --- a/packages/frontend/core/src/adapters/public-cloud/ui.tsx +++ b/packages/frontend/core/src/adapters/public-cloud/ui.tsx @@ -1,26 +1,10 @@ -import { PageNotFoundError } from '@affine/env/constant'; import type { WorkspaceFlavour } from '@affine/env/workspace'; import { type WorkspaceUISchema } from '@affine/env/workspace'; -import { useWorkspace } from '../../hooks/use-workspace'; -import { PageDetailEditor, Provider } from '../shared'; +import { Provider } from '../shared'; export const UI = { Provider, - PageDetail: ({ currentWorkspaceId, currentPageId, onLoadEditor }) => { - const workspace = useWorkspace(currentWorkspaceId); - const page = workspace.blockSuiteWorkspace.getPage(currentPageId); - if (!page) { - throw new PageNotFoundError(workspace.blockSuiteWorkspace, currentPageId); - } - return ( - - ); - }, NewSettingsDetail: () => { throw new Error('Not implemented'); }, diff --git a/packages/frontend/core/src/adapters/shared.ts b/packages/frontend/core/src/adapters/shared.ts index 79f369bf34..6a075b042f 100644 --- a/packages/frontend/core/src/adapters/shared.ts +++ b/packages/frontend/core/src/adapters/shared.ts @@ -13,9 +13,3 @@ export const NewWorkspaceSettingDetail = lazy(() => }) ) ); - -export const PageDetailEditor = lazy(() => - import('../components/page-detail-editor').then(({ PageDetailEditor }) => ({ - default: PageDetailEditor, - })) -); diff --git a/packages/frontend/core/src/app.tsx b/packages/frontend/core/src/app.tsx index be9deb4d55..d7db06564a 100644 --- a/packages/frontend/core/src/app.tsx +++ b/packages/frontend/core/src/app.tsx @@ -1,6 +1,5 @@ import '@affine/component/theme/global.css'; import '@affine/component/theme/theme.css'; -import '@toeverything/components/style.css'; import { AffineContext } from '@affine/component/context'; import { GlobalLoading } from '@affine/component/global-loading'; diff --git a/packages/frontend/core/src/atoms/element.ts b/packages/frontend/core/src/atoms/element.ts deleted file mode 100644 index 2f33acdc67..0000000000 --- a/packages/frontend/core/src/atoms/element.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { atom } from 'jotai/vanilla'; - -export const appHeaderAtom = atom(null); - -export const mainContainerAtom = atom(null); diff --git a/packages/frontend/core/src/bootstrap/plugins/setup.ts b/packages/frontend/core/src/bootstrap/plugins/setup.ts index f894165369..8fc87d11b4 100644 --- a/packages/frontend/core/src/bootstrap/plugins/setup.ts +++ b/packages/frontend/core/src/bootstrap/plugins/setup.ts @@ -156,12 +156,8 @@ function createSetupImpl(rootStore: ReturnType) { }, '@blocksuite/global/utils': import('@blocksuite/global/utils'), '@toeverything/infra/atom': import('@toeverything/infra/atom'), - '@toeverything/components/button': import( - '@toeverything/components/button' - ), - '@toeverything/components/tooltip': import( - '@toeverything/components/tooltip' - ), + '@affine/component/ui/button': import('@affine/component/ui/button'), + '@affine/component/ui/tooltip': import('@affine/component/ui/tooltip'), }); // pluginName -> module -> importName -> updater[] diff --git a/packages/frontend/core/src/bootstrap/register-blocksuite-components.ts b/packages/frontend/core/src/bootstrap/register-blocksuite-components.ts new file mode 100644 index 0000000000..d5f5b07f84 --- /dev/null +++ b/packages/frontend/core/src/bootstrap/register-blocksuite-components.ts @@ -0,0 +1,12 @@ +import { registerTOCComponents } from '@blocksuite/blocks'; + +registerTOCComponents(components => { + for (const compName in components) { + if (window.customElements.get(compName)) continue; + + window.customElements.define( + compName, + components[compName as keyof typeof components] + ); + } +}); diff --git a/packages/frontend/core/src/bootstrap/setup.ts b/packages/frontend/core/src/bootstrap/setup.ts index 36af514255..caa79875a3 100644 --- a/packages/frontend/core/src/bootstrap/setup.ts +++ b/packages/frontend/core/src/bootstrap/setup.ts @@ -1,3 +1,5 @@ +import './register-blocksuite-components'; + import { setupGlobal } from '@affine/env/global'; import type { WorkspaceAdapter } from '@affine/env/workspace'; import type { WorkspaceFlavour } from '@affine/env/workspace'; diff --git a/packages/frontend/core/src/commands/affine-settings.tsx b/packages/frontend/core/src/commands/affine-settings.tsx index f7e9a98908..ca575f7bbe 100644 --- a/packages/frontend/core/src/commands/affine-settings.tsx +++ b/packages/frontend/core/src/commands/affine-settings.tsx @@ -1,5 +1,6 @@ import type { useAFFiNEI18N } from '@affine/i18n/hooks'; import { SettingsIcon } from '@blocksuite/icons'; +import { appSettingAtom } from '@toeverything/infra/atom'; import { PreconditionStrategy, registerAffineCommand, @@ -8,7 +9,6 @@ import { type createStore } from 'jotai'; import type { useTheme } from 'next-themes'; import { openQuickSearchModalAtom } from '../atoms'; -import { appSettingAtom } from '../atoms/settings'; import type { useLanguageHelper } from '../hooks/affine/use-language-helper'; export function registerAffineSettingsCommands({ diff --git a/packages/frontend/core/src/components/affine/affine-error-boundary/error-assets/dark-500-status.assets.svg b/packages/frontend/core/src/components/affine/affine-error-boundary/error-assets/dark-500-status.assets.svg new file mode 100644 index 0000000000..f87766bfd6 --- /dev/null +++ b/packages/frontend/core/src/components/affine/affine-error-boundary/error-assets/dark-500-status.assets.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/frontend/core/src/components/affine/affine-error-boundary/error-assets/500-status.assets.svg b/packages/frontend/core/src/components/affine/affine-error-boundary/error-assets/light-500-status.assets.svg similarity index 100% rename from packages/frontend/core/src/components/affine/affine-error-boundary/error-assets/500-status.assets.svg rename to packages/frontend/core/src/components/affine/affine-error-boundary/error-assets/light-500-status.assets.svg diff --git a/packages/frontend/core/src/components/affine/affine-error-boundary/error-basic/error-detail.tsx b/packages/frontend/core/src/components/affine/affine-error-boundary/error-basic/error-detail.tsx index 2aa22a1f7b..0e5ffc1e74 100644 --- a/packages/frontend/core/src/components/affine/affine-error-boundary/error-basic/error-detail.tsx +++ b/packages/frontend/core/src/components/affine/affine-error-boundary/error-basic/error-detail.tsx @@ -1,7 +1,8 @@ +import { Button } from '@affine/component/ui/button'; import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Button } from '@toeverything/components/button'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; +import { useTheme } from 'next-themes'; import { type FC, type PropsWithChildren, @@ -10,7 +11,8 @@ import { } from 'react'; import imageUrlFor404 from '../error-assets/404-status.assets.svg'; -import imageUrlFor500 from '../error-assets/500-status.assets.svg'; +import imageUrlForDark500 from '../error-assets/dark-500-status.assets.svg'; +import imageUrlForLight500 from '../error-assets/light-500-status.assets.svg'; import * as styles from './error-detail.css'; export enum ErrorStatus { @@ -30,8 +32,20 @@ export interface ErrorDetailProps extends PropsWithChildren { } const imageMap = new Map([ - [ErrorStatus.NotFound, imageUrlFor404], - [ErrorStatus.Unexpected, imageUrlFor500], + [ + ErrorStatus.NotFound, + { + light: imageUrlFor404, // TODO: Ask designer for dark/light mode image. + dark: imageUrlFor404, + }, + ], + [ + ErrorStatus.Unexpected, + { + light: imageUrlForLight500, // TODO: Split assets lib and use image hook to handle light and dark. + dark: imageUrlForDark500, + }, + ], ]); /** @@ -49,6 +63,7 @@ export const ErrorDetail: FC = props => { const descriptions = Array.isArray(description) ? description : [description]; const [isBtnLoading, setBtnLoading] = useState(false); const t = useAFFiNEI18N(); + const { resolvedTheme } = useTheme(); const onBtnClick = useAsyncCallback(async () => { try { @@ -83,7 +98,11 @@ export const ErrorDetail: FC = props => { {withoutImage ? null : (
    )}
    diff --git a/packages/frontend/core/src/components/affine/auth/after-sign-in-send-email.tsx b/packages/frontend/core/src/components/affine/auth/after-sign-in-send-email.tsx index db76cc27bd..e9e578d1a8 100644 --- a/packages/frontend/core/src/components/affine/auth/after-sign-in-send-email.tsx +++ b/packages/frontend/core/src/components/affine/auth/after-sign-in-send-email.tsx @@ -4,9 +4,9 @@ import { CountDownRender, ModalHeader, } from '@affine/component/auth-components'; +import { Button } from '@affine/component/ui/button'; import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Button } from '@toeverything/components/button'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import React, { useCallback } from 'react'; diff --git a/packages/frontend/core/src/components/affine/auth/after-sign-up-send-email.tsx b/packages/frontend/core/src/components/affine/auth/after-sign-up-send-email.tsx index 3841104620..dd5dd74b46 100644 --- a/packages/frontend/core/src/components/affine/auth/after-sign-up-send-email.tsx +++ b/packages/frontend/core/src/components/affine/auth/after-sign-up-send-email.tsx @@ -4,8 +4,8 @@ import { CountDownRender, ModalHeader, } from '@affine/component/auth-components'; +import { Button } from '@affine/component/ui/button'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Button } from '@toeverything/components/button'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { type FC, useCallback } from 'react'; diff --git a/packages/frontend/core/src/components/affine/auth/send-email.tsx b/packages/frontend/core/src/components/affine/auth/send-email.tsx index 0b3399c1d8..bd0b72dcfc 100644 --- a/packages/frontend/core/src/components/affine/auth/send-email.tsx +++ b/packages/frontend/core/src/components/affine/auth/send-email.tsx @@ -6,6 +6,7 @@ import { ModalHeader, } from '@affine/component/auth-components'; import { pushNotificationAtom } from '@affine/component/notification-center'; +import { Button } from '@affine/component/ui/button'; import { sendChangeEmailMutation, sendChangePasswordEmailMutation, @@ -13,7 +14,6 @@ import { } from '@affine/graphql'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useMutation } from '@affine/workspace/affine/gql'; -import { Button } from '@toeverything/components/button'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { useSetAtom } from 'jotai/react'; import { useCallback, useState } from 'react'; diff --git a/packages/frontend/core/src/components/affine/auth/sign-in-with-password.tsx b/packages/frontend/core/src/components/affine/auth/sign-in-with-password.tsx index 9657c850c3..8bea5bd67e 100644 --- a/packages/frontend/core/src/components/affine/auth/sign-in-with-password.tsx +++ b/packages/frontend/core/src/components/affine/auth/sign-in-with-password.tsx @@ -4,8 +4,8 @@ import { BackButton, ModalHeader, } from '@affine/component/auth-components'; +import { Button } from '@affine/component/ui/button'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Button } from '@toeverything/components/button'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; // eslint-disable-next-line @typescript-eslint/no-restricted-imports import { useSession } from 'next-auth/react'; diff --git a/packages/frontend/core/src/components/affine/auth/sign-in.tsx b/packages/frontend/core/src/components/affine/auth/sign-in.tsx index 93bd0d42be..df4e87d183 100644 --- a/packages/frontend/core/src/components/affine/auth/sign-in.tsx +++ b/packages/frontend/core/src/components/affine/auth/sign-in.tsx @@ -3,12 +3,12 @@ import { CountDownRender, ModalHeader, } from '@affine/component/auth-components'; +import { Button } from '@affine/component/ui/button'; import { type GetUserQuery, getUserQuery } from '@affine/graphql'; import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useMutation } from '@affine/workspace/affine/gql'; import { ArrowDownBigIcon, GoogleDuotoneIcon } from '@blocksuite/icons'; -import { Button } from '@toeverything/components/button'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { GraphQLError } from 'graphql'; import { type FC, useState } from 'react'; diff --git a/packages/frontend/core/src/components/affine/auth/subscription-redirect.tsx b/packages/frontend/core/src/components/affine/auth/subscription-redirect.tsx index 26b720b60f..c206883a29 100644 --- a/packages/frontend/core/src/components/affine/auth/subscription-redirect.tsx +++ b/packages/frontend/core/src/components/affine/auth/subscription-redirect.tsx @@ -1,5 +1,7 @@ import { SignUpPage } from '@affine/component/auth-components'; import { AffineShapeIcon } from '@affine/component/page-list'; +import { Button } from '@affine/component/ui/button'; +import { Loading } from '@affine/component/ui/loading'; import type { SubscriptionRecurring } from '@affine/graphql'; import { changePasswordMutation, @@ -8,8 +10,6 @@ import { } from '@affine/graphql'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useMutation, useQuery } from '@affine/workspace/affine/gql'; -import { Button } from '@toeverything/components/button'; -import { Loading } from '@toeverything/components/loading'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { nanoid } from 'nanoid'; import { Suspense, useCallback, useEffect, useMemo } from 'react'; diff --git a/packages/frontend/core/src/components/affine/auth/user-plan-button.tsx b/packages/frontend/core/src/components/affine/auth/user-plan-button.tsx index dc1ecfdb29..fdc007828b 100644 --- a/packages/frontend/core/src/components/affine/auth/user-plan-button.tsx +++ b/packages/frontend/core/src/components/affine/auth/user-plan-button.tsx @@ -1,6 +1,6 @@ +import { Tooltip } from '@affine/component/ui/tooltip'; import { SubscriptionPlan } from '@affine/graphql'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import Tooltip from '@toeverything/components/tooltip'; import { useSetAtom } from 'jotai'; import { useCallback } from 'react'; import { withErrorBoundary } from 'react-error-boundary'; diff --git a/packages/frontend/core/src/components/affine/awareness/index.tsx b/packages/frontend/core/src/components/affine/awareness/index.tsx new file mode 100644 index 0000000000..db308bd264 --- /dev/null +++ b/packages/frontend/core/src/components/affine/awareness/index.tsx @@ -0,0 +1,45 @@ +import { Suspense, useEffect } from 'react'; + +import { useCurrentLoginStatus } from '../../../hooks/affine/use-current-login-status'; +import { useCurrentUser } from '../../../hooks/affine/use-current-user'; +import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace'; + +const SyncAwarenessInnerLoggedIn = () => { + const currentUser = useCurrentUser(); + const [{ blockSuiteWorkspace: workspace }] = useCurrentWorkspace(); + + useEffect(() => { + if (currentUser && workspace) { + workspace.awarenessStore.awareness.setLocalStateField('user', { + name: currentUser.name, + // todo: add avatar? + }); + + return () => { + workspace.awarenessStore.awareness.setLocalStateField('user', null); + }; + } + return; + }, [currentUser, workspace]); + + return null; +}; + +const SyncAwarenessInner = () => { + const loginStatus = useCurrentLoginStatus(); + + if (loginStatus === 'authenticated') { + return ; + } + + return null; +}; + +// todo: we could do something more interesting here, e.g., show where the current user is +export const SyncAwareness = () => { + return ( + + + + ); +}; diff --git a/packages/frontend/core/src/components/affine/create-workspace-modal/index.tsx b/packages/frontend/core/src/components/affine/create-workspace-modal/index.tsx index 0ec4f8920d..682da9fa7d 100644 --- a/packages/frontend/core/src/components/affine/create-workspace-modal/index.tsx +++ b/packages/frontend/core/src/components/affine/create-workspace-modal/index.tsx @@ -1,14 +1,14 @@ import { Input, toast } from '@affine/component'; -import { DebugLogger } from '@affine/debug'; -import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { HelpIcon } from '@blocksuite/icons'; -import { Button } from '@toeverything/components/button'; +import { Button } from '@affine/component/ui/button'; import { ConfirmModal, type ConfirmModalProps, Modal, -} from '@toeverything/components/modal'; -import { Tooltip } from '@toeverything/components/tooltip'; +} from '@affine/component/ui/modal'; +import { Tooltip } from '@affine/component/ui/tooltip'; +import { DebugLogger } from '@affine/debug'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { HelpIcon } from '@blocksuite/icons'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import type { LoadDBFileResult, diff --git a/packages/frontend/core/src/components/affine/enable-affine-cloud-modal/index.tsx b/packages/frontend/core/src/components/affine/enable-affine-cloud-modal/index.tsx index e26990481d..441e0a82b9 100644 --- a/packages/frontend/core/src/components/affine/enable-affine-cloud-modal/index.tsx +++ b/packages/frontend/core/src/components/affine/enable-affine-cloud-modal/index.tsx @@ -1,8 +1,8 @@ -import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { ConfirmModal, type ConfirmModalProps, -} from '@toeverything/components/modal'; +} from '@affine/component/ui/modal'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useSetAtom } from 'jotai'; import { useCallback } from 'react'; diff --git a/packages/frontend/core/src/components/affine/hub-island/index.tsx b/packages/frontend/core/src/components/affine/hub-island/index.tsx new file mode 100644 index 0000000000..732c220b82 --- /dev/null +++ b/packages/frontend/core/src/components/affine/hub-island/index.tsx @@ -0,0 +1,11 @@ +import { ToolContainer } from '@affine/component/workspace'; + +import { HelpIsland } from '../../pure/help-island'; + +export const HubIsland = () => { + return ( + + + + ); +}; diff --git a/packages/frontend/core/src/components/affine/language-menu/index.tsx b/packages/frontend/core/src/components/affine/language-menu/index.tsx index 8856057dcc..e16b3b4bfa 100644 --- a/packages/frontend/core/src/components/affine/language-menu/index.tsx +++ b/packages/frontend/core/src/components/affine/language-menu/index.tsx @@ -1,4 +1,4 @@ -import { Menu, MenuItem, MenuTrigger } from '@toeverything/components/menu'; +import { Menu, MenuItem, MenuTrigger } from '@affine/component/ui/menu'; import { memo, type ReactElement } from 'react'; import { useLanguageHelper } from '../../../hooks/affine/use-language-helper'; diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/delete-leave-workspace/delete/index.tsx b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/delete-leave-workspace/delete/index.tsx index c076117d2c..6632476fa2 100644 --- a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/delete-leave-workspace/delete/index.tsx +++ b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/delete-leave-workspace/delete/index.tsx @@ -1,12 +1,12 @@ import { Input } from '@affine/component'; +import { + ConfirmModal, + type ConfirmModalProps, +} from '@affine/component/ui/modal'; import type { AffineOfficialWorkspace } from '@affine/env/workspace'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { - ConfirmModal, - type ConfirmModalProps, -} from '@toeverything/components/modal'; import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name'; import { useCallback, useState } from 'react'; diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/delete-leave-workspace/index.tsx b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/delete-leave-workspace/index.tsx index 86cd3ce6ac..ad27044b7d 100644 --- a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/delete-leave-workspace/index.tsx +++ b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/delete-leave-workspace/index.tsx @@ -1,9 +1,9 @@ import { SettingRow } from '@affine/component/setting-components'; +import { ConfirmModal } from '@affine/component/ui/modal'; import type { AffineOfficialWorkspace } from '@affine/env/workspace'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { ArrowRightSmallIcon } from '@blocksuite/icons'; -import { ConfirmModal } from '@toeverything/components/modal'; import { useCallback, useState } from 'react'; import type { WorkspaceSettingDetailProps } from '../types'; diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/export.tsx b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/export.tsx index 592ccf09c0..a74908f90e 100644 --- a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/export.tsx +++ b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/export.tsx @@ -1,8 +1,8 @@ import { pushNotificationAtom } from '@affine/component/notification-center'; import { SettingRow } from '@affine/component/setting-components'; +import { Button } from '@affine/component/ui/button'; import type { AffineOfficialWorkspace } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Button } from '@toeverything/components/button'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import type { SaveDBFileResult } from '@toeverything/infra/type'; import { useSetAtom } from 'jotai'; diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/members.tsx b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/members.tsx index 5901dce957..f1d5a0adde 100644 --- a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/members.tsx +++ b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/members.tsx @@ -8,16 +8,16 @@ import { } from '@affine/component/member-components'; import { pushNotificationAtom } from '@affine/component/notification-center'; import { SettingRow } from '@affine/component/setting-components'; +import { Avatar } from '@affine/component/ui/avatar'; +import { Button, IconButton } from '@affine/component/ui/button'; +import { Loading } from '@affine/component/ui/loading'; +import { Menu, MenuItem } from '@affine/component/ui/menu'; +import { Tooltip } from '@affine/component/ui/tooltip'; import type { AffineOfficialWorkspace } from '@affine/env/workspace'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { Permission, SubscriptionPlan } from '@affine/graphql'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { ArrowRightBigIcon, MoreVerticalIcon } from '@blocksuite/icons'; -import { Avatar } from '@toeverything/components/avatar'; -import { Button, IconButton } from '@toeverything/components/button'; -import { Loading } from '@toeverything/components/loading'; -import { Menu, MenuItem } from '@toeverything/components/menu'; -import { Tooltip } from '@toeverything/components/tooltip'; import clsx from 'clsx'; import { useSetAtom } from 'jotai'; import type { ReactElement } from 'react'; diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/profile.tsx b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/profile.tsx index ef9722ab58..59a06c6ed3 100644 --- a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/profile.tsx +++ b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/profile.tsx @@ -1,10 +1,10 @@ import { FlexWrapper, Input, Wrapper } from '@affine/component'; import { pushNotificationAtom } from '@affine/component/notification-center'; +import { Avatar } from '@affine/component/ui/avatar'; +import { Button } from '@affine/component/ui/button'; import type { AffineOfficialWorkspace } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { CameraIcon } from '@blocksuite/icons'; -import { Avatar } from '@toeverything/components/avatar'; -import { Button } from '@toeverything/components/button'; import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-block-suite-workspace-avatar-url'; import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name'; import { useSetAtom } from 'jotai'; diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/publish.tsx b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/publish.tsx index 95652cdd91..f7869905fd 100644 --- a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/publish.tsx +++ b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/publish.tsx @@ -1,5 +1,7 @@ import { FlexWrapper, Input, Switch } from '@affine/component'; import { SettingRow } from '@affine/component/setting-components'; +import { Button } from '@affine/component/ui/button'; +import { Tooltip } from '@affine/component/ui/tooltip'; import { Unreachable } from '@affine/env/constant'; import type { AffineCloudWorkspace, @@ -9,8 +11,6 @@ import type { import type { AffineOfficialWorkspace } from '@affine/env/workspace'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Button } from '@toeverything/components/button'; -import { Tooltip } from '@toeverything/components/tooltip'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name'; import { noop } from 'foxact/noop'; diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/storage.tsx b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/storage.tsx index afe7b918f0..9c6ac08873 100644 --- a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/storage.tsx +++ b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/storage.tsx @@ -1,9 +1,9 @@ import { FlexWrapper, toast } from '@affine/component'; import { SettingRow } from '@affine/component/setting-components'; +import { Button } from '@affine/component/ui/button'; +import { Tooltip } from '@affine/component/ui/tooltip'; import type { AffineOfficialWorkspace } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Button } from '@toeverything/components/button'; -import { Tooltip } from '@toeverything/components/tooltip'; import type { MoveDBFileResult } from '@toeverything/infra/type'; import { useMemo } from 'react'; import { useCallback, useEffect, useState } from 'react'; diff --git a/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx b/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx index 8eb7150e82..c78d856020 100644 --- a/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx +++ b/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx @@ -3,12 +3,12 @@ import { BlockSuiteEditor, EditorLoading, } from '@affine/component/block-suite-editor'; +import { Button } from '@affine/component/ui/button'; +import { ConfirmModal, Modal } from '@affine/component/ui/modal'; import type { PageMode } from '@affine/core/atoms'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import type { Workspace } from '@blocksuite/store'; import type { DialogContentProps } from '@radix-ui/react-dialog'; -import { Button } from '@toeverything/components/button'; -import { ConfirmModal, Modal } from '@toeverything/components/modal'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { useAtom, useAtomValue } from 'jotai'; import { diff --git a/packages/frontend/core/src/components/affine/payment-disable/index.tsx b/packages/frontend/core/src/components/affine/payment-disable/index.tsx index 58a29da3c5..869837c7d7 100644 --- a/packages/frontend/core/src/components/affine/payment-disable/index.tsx +++ b/packages/frontend/core/src/components/affine/payment-disable/index.tsx @@ -1,5 +1,5 @@ +import { ConfirmModal } from '@affine/component/ui/modal'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { ConfirmModal } from '@toeverything/components/modal'; import { useAtom } from 'jotai'; import { useCallback } from 'react'; diff --git a/packages/frontend/core/src/components/affine/setting-modal/account-setting/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/account-setting/index.tsx index 4d82b3c5d8..651dc953d3 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/account-setting/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/account-setting/index.tsx @@ -5,6 +5,8 @@ import { SettingRow, StorageProgress, } from '@affine/component/setting-components'; +import { Avatar } from '@affine/component/ui/avatar'; +import { Button } from '@affine/component/ui/button'; import { allBlobSizesQuery, removeAvatarMutation, @@ -14,8 +16,6 @@ import { import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useMutation, useQuery } from '@affine/workspace/affine/gql'; import { ArrowRightSmallIcon, CameraIcon } from '@blocksuite/icons'; -import { Avatar } from '@toeverything/components/avatar'; -import { Button } from '@toeverything/components/button'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { validateAndReduceImage } from '@toeverything/hooks/use-block-suite-workspace-avatar-url'; import bytes from 'bytes'; diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/about/update-check-section.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/about/update-check-section.tsx index 64f615169f..ceb942128e 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/about/update-check-section.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/about/update-check-section.tsx @@ -1,19 +1,12 @@ +import { Loading } from '@affine/component'; import { SettingRow } from '@affine/component/setting-components'; +import { Button } from '@affine/component/ui/button'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Button } from '@toeverything/components/button'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; -import { - downloadProgressAtom, - isCheckingForUpdatesAtom, - updateAvailableAtom, - updateReadyAtom, - useAppUpdater, -} from '@toeverything/hooks/use-app-updater'; +import { useAppUpdater } from '@toeverything/hooks/use-app-updater'; import clsx from 'clsx'; -import { useAtomValue } from 'jotai'; import { useCallback, useMemo, useState } from 'react'; -import { Loading } from '../../../../pure/workspace-slider-bar/workspace-card/loading-icon'; import * as styles from './style.css'; enum CheckUpdateStatus { @@ -25,18 +18,16 @@ enum CheckUpdateStatus { const useUpdateStatusLabels = (checkUpdateStatus: CheckUpdateStatus) => { const t = useAFFiNEI18N(); - const isCheckingForUpdates = useAtomValue(isCheckingForUpdatesAtom); - const updateAvailable = useAtomValue(updateAvailableAtom); - const updateReady = useAtomValue(updateReadyAtom); - const downloadProgress = useAtomValue(downloadProgressAtom); + const { updateAvailable, downloadProgress, updateReady, checkingForUpdates } = + useAppUpdater(); const buttonLabel = useMemo(() => { - if (updateAvailable && downloadProgress === null) { - return t['com.affine.aboutAFFiNE.checkUpdate.button.download'](); - } if (updateReady) { return t['com.affine.aboutAFFiNE.checkUpdate.button.restart'](); } + if (updateAvailable && downloadProgress === null) { + return t['com.affine.aboutAFFiNE.checkUpdate.button.download'](); + } if ( checkUpdateStatus === CheckUpdateStatus.LATEST || checkUpdateStatus === CheckUpdateStatus.ERROR @@ -47,16 +38,16 @@ const useUpdateStatusLabels = (checkUpdateStatus: CheckUpdateStatus) => { }, [checkUpdateStatus, downloadProgress, t, updateAvailable, updateReady]); const subtitleLabel = useMemo(() => { - if (updateAvailable && downloadProgress === null) { + if (updateReady) { + return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.restart'](); + } else if (updateAvailable && downloadProgress === null) { return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.update-available']({ version: updateAvailable.version, }); - } else if (isCheckingForUpdates) { + } else if (checkingForUpdates) { return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.checking'](); } else if (updateAvailable && downloadProgress !== null) { return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.downloading'](); - } else if (updateReady) { - return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.restart'](); } else if (checkUpdateStatus === CheckUpdateStatus.ERROR) { return t['com.affine.aboutAFFiNE.checkUpdate.subtitle.error'](); } else if (checkUpdateStatus === CheckUpdateStatus.LATEST) { @@ -66,7 +57,7 @@ const useUpdateStatusLabels = (checkUpdateStatus: CheckUpdateStatus) => { }, [ checkUpdateStatus, downloadProgress, - isCheckingForUpdates, + checkingForUpdates, t, updateAvailable, updateReady, @@ -83,14 +74,14 @@ const useUpdateStatusLabels = (checkUpdateStatus: CheckUpdateStatus) => { error: checkUpdateStatus === CheckUpdateStatus.ERROR, })} > - {isCheckingForUpdates ? : null} + {checkingForUpdates ? : null} {subtitleLabel} ); }, [ checkUpdateStatus, downloadProgress, - isCheckingForUpdates, + checkingForUpdates, subtitleLabel, updateAvailable, updateReady, @@ -101,10 +92,14 @@ const useUpdateStatusLabels = (checkUpdateStatus: CheckUpdateStatus) => { export const UpdateCheckSection = () => { const t = useAFFiNEI18N(); - const { checkForUpdates, downloadUpdate, quitAndInstall } = useAppUpdater(); - const updateAvailable = useAtomValue(updateAvailableAtom); - const updateReady = useAtomValue(updateReadyAtom); - const downloadProgress = useAtomValue(downloadProgressAtom); + const { + checkForUpdates, + downloadUpdate, + quitAndInstall, + updateAvailable, + downloadProgress, + updateReady, + } = useAppUpdater(); const [checkUpdateStatus, setCheckUpdateStatus] = useState( CheckUpdateStatus.UNCHECK ); diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/appearance/date-format-setting.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/appearance/date-format-setting.tsx index 1b790e86c7..06ee1aa6d4 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/appearance/date-format-setting.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/appearance/date-format-setting.tsx @@ -1,11 +1,8 @@ -import { Menu, MenuItem, MenuTrigger } from '@toeverything/components/menu'; +import { Menu, MenuItem, MenuTrigger } from '@affine/component/ui/menu'; +import { dateFormatOptions, type DateFormats } from '@toeverything/infra/atom'; import dayjs from 'dayjs'; import { useCallback } from 'react'; -import { - dateFormatOptions, - type DateFormats, -} from '../../../../../atoms/settings'; import { useAppSettingHelper } from '../../../../../hooks/affine/use-app-setting-helper'; interface DateFormatMenuContentProps { diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/appearance/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/appearance/index.tsx index 076a0fad14..5988c8e15b 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/appearance/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/appearance/index.tsx @@ -3,14 +3,14 @@ import { SettingHeader } from '@affine/component/setting-components'; import { SettingRow } from '@affine/component/setting-components'; import { SettingWrapper } from '@affine/component/setting-components'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { useTheme } from 'next-themes'; -import { useCallback } from 'react'; - import { type AppSetting, fontStyleOptions, windowFrameStyleOptions, -} from '../../../../../atoms/settings'; +} from '@toeverything/infra/atom'; +import { useTheme } from 'next-themes'; +import { useCallback } from 'react'; + import { useAppSettingHelper } from '../../../../../hooks/affine/use-app-setting-helper'; import { LanguageMenu } from '../../../language-menu'; import { DateFormatSetting } from './date-format-setting'; diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/billing/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/billing/index.tsx index 48b10075f3..8fe1fdda44 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/billing/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/billing/index.tsx @@ -5,6 +5,8 @@ import { SettingRow, SettingWrapper, } from '@affine/component/setting-components'; +import { Button, IconButton } from '@affine/component/ui/button'; +import { Loading } from '@affine/component/ui/loading'; import { createCustomerPortalMutation, getInvoicesCountQuery, @@ -20,8 +22,6 @@ import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useMutation, useQuery } from '@affine/workspace/affine/gql'; import { ArrowRightSmallIcon } from '@blocksuite/icons'; -import { Button, IconButton } from '@toeverything/components/button'; -import { Loading } from '@toeverything/components/loading'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { useSetAtom } from 'jotai'; import { Suspense, useCallback, useMemo, useState } from 'react'; diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/modals.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/modals.tsx index cd3531ab0f..816d364e82 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/modals.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/modals.tsx @@ -1,11 +1,11 @@ -import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { DialogTrigger } from '@radix-ui/react-dialog'; -import { Button } from '@toeverything/components/button'; +import { Button } from '@affine/component/ui/button'; import { ConfirmModal, type ConfirmModalProps, Modal, -} from '@toeverything/components/modal'; +} from '@affine/component/ui/modal'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { DialogTrigger } from '@radix-ui/react-dialog'; import { type ReactNode, useEffect, useRef } from 'react'; import * as styles from './style.css'; diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/plan-card.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/plan-card.tsx index f8f415ee69..a160b5da35 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/plan-card.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/plan-card.tsx @@ -1,3 +1,5 @@ +import { Button } from '@affine/component/ui/button'; +import { Tooltip } from '@affine/component/ui/tooltip'; import type { Subscription, SubscriptionMutator, @@ -13,8 +15,6 @@ import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useMutation } from '@affine/workspace/affine/gql'; import { DoneIcon } from '@blocksuite/icons'; -import { Button } from '@toeverything/components/button'; -import { Tooltip } from '@toeverything/components/tooltip'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { useSetAtom } from 'jotai'; import { useAtom } from 'jotai'; diff --git a/packages/frontend/core/src/components/affine/setting-modal/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/index.tsx index 075a78b321..9651d5a0fd 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/index.tsx @@ -1,7 +1,7 @@ import { WorkspaceDetailSkeleton } from '@affine/component/setting-components'; +import { Modal, type ModalProps } from '@affine/component/ui/modal'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { ContactWithUsIcon } from '@blocksuite/icons'; -import { Modal, type ModalProps } from '@toeverything/components/modal'; import { debounce } from 'lodash-es'; import { Suspense, useCallback, useLayoutEffect, useRef } from 'react'; diff --git a/packages/frontend/core/src/components/affine/setting-modal/setting-sidebar/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/setting-sidebar/index.tsx index 030b08fcc4..a39509437d 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/setting-sidebar/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/setting-sidebar/index.tsx @@ -2,13 +2,13 @@ import { WorkspaceListItemSkeleton, WorkspaceListSkeleton, } from '@affine/component/setting-components'; +import { Avatar } from '@affine/component/ui/avatar'; +import { Tooltip } from '@affine/component/ui/tooltip'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import type { RootWorkspaceMetadata } from '@affine/workspace/atom'; import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom'; import { Logo1Icon } from '@blocksuite/icons'; -import { Avatar } from '@toeverything/components/avatar'; -import { Tooltip } from '@toeverything/components/tooltip'; import { useBlockSuiteWorkspaceAvatarUrl } from '@toeverything/hooks/use-block-suite-workspace-avatar-url'; import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name'; import { getBlockSuiteWorkspaceAtom } from '@toeverything/infra/__internal__/workspace'; diff --git a/packages/frontend/core/src/components/affine/share-page-modal/index.tsx b/packages/frontend/core/src/components/affine/share-page-modal/index.tsx index 1e4395ce46..353f9cd7ca 100644 --- a/packages/frontend/core/src/components/affine/share-page-modal/index.tsx +++ b/packages/frontend/core/src/components/affine/share-page-modal/index.tsx @@ -14,7 +14,7 @@ type SharePageModalProps = { page: Page; }; -export const SharePageModal = ({ workspace, page }: SharePageModalProps) => { +export const SharePageButton = ({ workspace, page }: SharePageModalProps) => { const onTransformWorkspace = useOnTransformWorkspace(); const [open, setOpen] = useState(false); diff --git a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-export.tsx b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-export.tsx index e8e081221c..2f5895e201 100644 --- a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-export.tsx +++ b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-export.tsx @@ -1,9 +1,9 @@ import { ExportMenuItems } from '@affine/component/page-list'; +import { Button } from '@affine/component/ui/button'; +import { Divider } from '@affine/component/ui/divider'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { LinkIcon } from '@blocksuite/icons'; -import { Button } from '@toeverything/components/button'; -import { Divider } from '@toeverything/components/divider'; import { useExportPage } from '../../../../hooks/affine/use-export-page'; import * as styles from './index.css'; diff --git a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-menu.tsx b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-menu.tsx index 9b364cc04a..b4492cfec6 100644 --- a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-menu.tsx +++ b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-menu.tsx @@ -1,3 +1,6 @@ +import { Button } from '@affine/component/ui/button'; +import { Divider } from '@affine/component/ui/divider'; +import { Menu } from '@affine/component/ui/menu'; import { type AffineCloudWorkspace, type AffineOfficialWorkspace, @@ -8,9 +11,6 @@ import { import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { WebIcon } from '@blocksuite/icons'; import type { Page } from '@blocksuite/store'; -import { Button } from '@toeverything/components/button'; -import { Divider } from '@toeverything/components/divider'; -import { Menu } from '@toeverything/components/menu'; import { useIsSharedPage } from '../../../../hooks/affine/use-is-shared-page'; import * as styles from './index.css'; @@ -60,7 +60,7 @@ const LocalShareMenu = (props: ShareMenuProps) => { modal: false, }} > - @@ -73,10 +73,7 @@ const CloudShareMenu = (props: ShareMenuProps) => { workspace: { id: workspaceId }, currentPage, } = props; - const { isSharedPage } = useIsSharedPage( - workspaceId, - currentPage.spaceDoc.guid - ); + const { isSharedPage } = useIsSharedPage(workspaceId, currentPage.id); return ( { modal: false, }} > - + ); +}; diff --git a/packages/frontend/core/src/components/cloud/share-header-right-item/styles.css.ts b/packages/frontend/core/src/components/cloud/share-header-right-item/styles.css.ts index 3000c5d257..389f92c4f7 100644 --- a/packages/frontend/core/src/components/cloud/share-header-right-item/styles.css.ts +++ b/packages/frontend/core/src/components/cloud/share-header-right-item/styles.css.ts @@ -13,3 +13,13 @@ export const iconWrapper = style({ }, }, }); +export const rightItemContainer = style({ + display: 'flex', + alignItems: 'center', + gap: '8px', + padding: '0 8px', +}); + +export const presentButton = style({ + gap: '4px', +}); diff --git a/packages/frontend/core/src/components/page-detail-editor.css.ts b/packages/frontend/core/src/components/page-detail-editor.css.ts index 8665a39e39..d0c78166a2 100644 --- a/packages/frontend/core/src/components/page-detail-editor.css.ts +++ b/packages/frontend/core/src/components/page-detail-editor.css.ts @@ -1,21 +1,8 @@ import { globalStyle, style } from '@vanilla-extract/css'; -/** - * Editor container element layer should be lower than header and after auto - * The zIndex of header is 2, defined in packages/frontend/core/src/components/pure/header/style.css.tsx - */ -export const editorContainer = style({ - position: 'relative', - zIndex: 0, // it will create stacking context to limit layer of child elements and be lower than after auto zIndex -}); - -export const pluginContainer = style({ - height: '100%', - width: '100%', -}); - export const editor = style({ - height: '100%', + flex: 1, + overflow: 'auto', selectors: { '&.full-screen': { vars: { diff --git a/packages/frontend/core/src/components/page-detail-editor.tsx b/packages/frontend/core/src/components/page-detail-editor.tsx index ca3dc916d9..1d0a13615a 100644 --- a/packages/frontend/core/src/components/page-detail-editor.tsx +++ b/packages/frontend/core/src/components/page-detail-editor.tsx @@ -1,45 +1,25 @@ import './page-detail-editor.css'; import { PageNotFoundError } from '@affine/env/constant'; -import type { LayoutNode } from '@affine/sdk/entry'; -import { rootBlockHubAtom } from '@affine/workspace/atom'; -import type { BlockHub } from '@blocksuite/blocks'; -import type { EditorContainer } from '@blocksuite/editor'; import { assertExists, DisposableGroup } from '@blocksuite/global/utils'; +import type { EditorContainer } from '@blocksuite/presets'; import type { Page, Workspace } from '@blocksuite/store'; import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useBlockSuiteWorkspacePage } from '@toeverything/hooks/use-block-suite-workspace-page'; -import { - addCleanup, - pluginEditorAtom, - pluginWindowAtom, -} from '@toeverything/infra/__internal__/plugin'; -import { contentLayoutAtom, getCurrentStore } from '@toeverything/infra/atom'; +import { pluginEditorAtom } from '@toeverything/infra/__internal__/plugin'; +import { getCurrentStore } from '@toeverything/infra/atom'; +import { fontStyleOptions } from '@toeverything/infra/atom'; import clsx from 'clsx'; -import { useAtomValue, useSetAtom } from 'jotai'; -import type { CSSProperties, ReactElement } from 'react'; -import { - memo, - startTransition, - Suspense, - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; -import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'; +import { useAtomValue } from 'jotai'; +import type { CSSProperties } from 'react'; +import { memo, Suspense, useCallback, useMemo, useState } from 'react'; import { useLocation } from 'react-router-dom'; import { type PageMode, pageSettingFamily } from '../atoms'; -import { fontStyleOptions } from '../atoms/settings'; import { useAppSettingHelper } from '../hooks/affine/use-app-setting-helper'; import { useBlockSuiteMetaHelper } from '../hooks/affine/use-block-suite-meta-helper'; import { BlockSuiteEditor as Editor } from './blocksuite/block-suite-editor'; -import { Bookmark } from './bookmark'; import * as styles from './page-detail-editor.css'; -import { editorContainer, pluginContainer } from './page-detail-editor.css'; -import { TrashButtonGroup } from './pure/trash-button-group'; declare global { // eslint-disable-next-line no-var @@ -60,44 +40,14 @@ function useRouterHash() { return useLocation().hash.substring(1); } -function useCreateAndSetRootBlockHub( - editor?: EditorContainer, - showBlockHub?: boolean -) { - const setBlockHub = useSetAtom(rootBlockHubAtom); - useEffect(() => { - let canceled = false; - let blockHub: BlockHub | undefined; - if (editor && showBlockHub) { - editor - .createBlockHub() - .then(bh => { - if (canceled) { - return; - } - blockHub = bh; - setBlockHub(blockHub); - }) - .catch(console.error); - } - return () => { - canceled = true; - blockHub?.remove(); - }; - }, [editor, showBlockHub, setBlockHub]); -} - -const EditorWrapper = memo(function EditorWrapper({ +const PageDetailEditorMain = memo(function PageDetailEditorMain({ workspace, + page, pageId, onLoad, isPublic, publishMode, -}: PageDetailEditorProps) { - const page = useBlockSuiteWorkspacePage(workspace, pageId); - if (!page) { - throw new PageNotFoundError(workspace, pageId); - } +}: PageDetailEditorProps & { page: Page }) { const meta = useBlockSuitePageMeta(workspace).find( meta => meta.id === pageId ); @@ -143,11 +93,9 @@ const EditorWrapper = memo(function EditorWrapper({ [isPublic, switchToEdgelessMode, pageId, switchToPageMode] ); - const [editor, setEditor] = useState(); + const [, setEditor] = useState(); const blockId = useRouterHash(); - useCreateAndSetRootBlockHub(editor, !meta.trash); - const onLoadEditor = useCallback( (editor: EditorContainer) => { // debug current detail editor @@ -165,6 +113,9 @@ const EditorWrapper = memo(function EditorWrapper({ if (onLoad) { disposableGroup.add(onLoad(page, editor)); } + + // todo: remove the following + // for now this is required for the image-preview plugin to work const rootStore = getCurrentStore(); const editorItems = rootStore.get(pluginEditorAtom); let disposes: (() => void)[] = []; @@ -194,133 +145,23 @@ const EditorWrapper = memo(function EditorWrapper({ ); return ( - <> - - {meta.trash && } - - - ); -}); - -interface PluginContentAdapterProps { - windowItem: (div: HTMLDivElement) => () => void; - pluginName: string; -} - -const PluginContentAdapter = memo( - function PluginContentAdapter({ windowItem, pluginName }) { - const rootRef = useRef(null); - useEffect(() => { - const abortController = new AbortController(); - const root = rootRef.current; - if (root) { - startTransition(() => { - if (abortController.signal.aborted) { - return; - } - const div = document.createElement('div'); - const cleanup = windowItem(div); - root.append(div); - if (abortController.signal.aborted) { - cleanup(); - div.remove(); - } else { - const cl = () => { - cleanup(); - div.remove(); - }; - const dispose = addCleanup(pluginName, cl); - abortController.signal.addEventListener('abort', () => { - window.setTimeout(() => { - dispose(); - cl(); - }); - }); - } - }); - return () => { - abortController.abort(); - }; + ; - } -); - -interface LayoutPanelProps { - node: LayoutNode; - editorProps: PageDetailEditorProps; - depth: number; -} - -const LayoutPanel = memo(function LayoutPanel( - props: LayoutPanelProps -): ReactElement { - const { node, depth, editorProps } = props; - const windowItems = useAtomValue(pluginWindowAtom); - if (typeof node === 'string') { - if (node === 'editor') { - return ; - } else { - const windowItem = windowItems[node]; - return ; - } - } else { - return ( - - - - - - - - - - - - - - ); - } + mode={mode} + page={page} + onModeChange={setEditorMode} + defaultSelectedBlockId={blockId} + onLoadEditor={onLoadEditor} + /> + ); }); export const PageDetailEditor = (props: PageDetailEditorProps) => { @@ -330,27 +171,9 @@ export const PageDetailEditor = (props: PageDetailEditorProps) => { throw new PageNotFoundError(workspace, pageId); } - const layout = useAtomValue(contentLayoutAtom); - - if (layout === 'editor') { - return ( - - - - - - - - ); - } - return ( - + ); }; diff --git a/packages/frontend/core/src/components/pure/file-upload/index.tsx b/packages/frontend/core/src/components/pure/file-upload/index.tsx index 76ca15ed0d..8d09b530a5 100644 --- a/packages/frontend/core/src/components/pure/file-upload/index.tsx +++ b/packages/frontend/core/src/components/pure/file-upload/index.tsx @@ -1,6 +1,6 @@ import { styled } from '@affine/component'; +import { Button } from '@affine/component/ui/button'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Button } from '@toeverything/components/button'; import type { ChangeEvent, PropsWithChildren } from 'react'; import { useRef } from 'react'; diff --git a/packages/frontend/core/src/components/pure/header-drop-down-button/index.tsx b/packages/frontend/core/src/components/pure/header-drop-down-button/index.tsx index dc9e1b8c94..8cb02f804f 100644 --- a/packages/frontend/core/src/components/pure/header-drop-down-button/index.tsx +++ b/packages/frontend/core/src/components/pure/header-drop-down-button/index.tsx @@ -1,8 +1,5 @@ +import { IconButton, type IconButtonProps } from '@affine/component/ui/button'; import { ArrowDownSmallIcon } from '@blocksuite/icons'; -import { - IconButton, - type IconButtonProps, -} from '@toeverything/components/button'; import { forwardRef } from 'react'; import { headerMenuTrigger } from './styles.css'; diff --git a/packages/frontend/core/src/components/pure/header/index.tsx b/packages/frontend/core/src/components/pure/header/index.tsx index ac1633582c..69a91f66af 100644 --- a/packages/frontend/core/src/components/pure/header/index.tsx +++ b/packages/frontend/core/src/components/pure/header/index.tsx @@ -5,56 +5,51 @@ import { } from '@affine/component/app-sidebar'; import { useIsTinyScreen } from '@toeverything/hooks/use-is-tiny-screen'; import clsx from 'clsx'; -import { type Atom, useAtomValue } from 'jotai'; +import { useAtomValue } from 'jotai'; import type { ReactNode } from 'react'; -import { forwardRef, useRef } from 'react'; +import { useCallback, useRef, useState } from 'react'; import * as style from './style.css'; -import { WindowsAppControls } from './windows-app-controls'; interface HeaderPros { left?: ReactNode; right?: ReactNode; center?: ReactNode; - mainContainerAtom: Atom; bottomBorder?: boolean; } // The Header component is used to solve the following problems // 1. Manage layout issues independently of page or business logic // 2. Dynamic centered middle element (relative to the main-container), when the middle element is detected to collide with the two elements, the line wrapping process is performed -export const Header = forwardRef(function Header( - { left, center, right, mainContainerAtom, bottomBorder }, - ref -) { +export const Header = ({ left, center, right, bottomBorder }: HeaderPros) => { const sidebarSwitchRef = useRef(null); const leftSlotRef = useRef(null); const centerSlotRef = useRef(null); const rightSlotRef = useRef(null); - const windowControlsRef = useRef(null); - const mainContainer = useAtomValue(mainContainerAtom); + const [headerRoot, setHeaderRoot] = useState(null); + + const onSetHeaderRoot = useCallback((node: HTMLDivElement | null) => { + setHeaderRoot(node); + }, []); const isTinyScreen = useIsTinyScreen({ - mainContainer, + container: headerRoot, leftStatic: sidebarSwitchRef, leftSlot: [leftSlotRef], centerDom: centerSlotRef, rightSlot: [rightSlotRef], - rightStatic: windowControlsRef, }); - const isWindowsDesktop = environment.isDesktop && environment.isWindows; const open = useAtomValue(appSidebarOpenAtom); const appSidebarFloating = useAtomValue(appSidebarFloatingAtom); return (
    (function Header(
    @@ -90,17 +84,16 @@ export const Header = forwardRef(function Header( block: isTinyScreen, })} > -
    -
    - {isWindowsDesktop ? : null} -
    -
    -
    -
    {right}
    +
    + {right}
    ); -}); +}; Header.displayName = 'Header'; + +export const HeaderDivider = () => { + return
    ; +}; diff --git a/packages/frontend/core/src/components/pure/header/style.css.tsx b/packages/frontend/core/src/components/pure/header/style.css.tsx index 9295b1500c..90ed34dced 100644 --- a/packages/frontend/core/src/components/pure/header/style.css.tsx +++ b/packages/frontend/core/src/components/pure/header/style.css.tsx @@ -62,9 +62,6 @@ export const headerCenter = style({ left: '50%', zIndex: 1, selectors: { - '&.is-window': { - maxWidth: '50%', - }, '&.shadow': { position: 'static', visibility: 'hidden', @@ -89,9 +86,6 @@ export const headerSideContainer = style({ export const windowAppControlsWrapper = style({ display: 'flex', - marginLeft: '20px', - // header padding right - transform: 'translateX(16px)', }); export const windowAppControl = style({ @@ -118,3 +112,10 @@ export const windowAppControl = style({ }, }, } as ComplexStyleRule); + +export const headerDivider = style({ + height: '20px', + width: '1px', + background: 'var(--affine-border-color)', + margin: '0 12px', +}); diff --git a/packages/frontend/core/src/components/pure/help-island/index.tsx b/packages/frontend/core/src/components/pure/help-island/index.tsx index 714ce1514b..87c954c8e2 100644 --- a/packages/frontend/core/src/components/pure/help-island/index.tsx +++ b/packages/frontend/core/src/components/pure/help-island/index.tsx @@ -1,9 +1,10 @@ +import { Tooltip } from '@affine/component/ui/tooltip'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { CloseIcon, NewIcon, UserGuideIcon } from '@blocksuite/icons'; -import { Tooltip } from '@toeverything/components/tooltip'; import { useSetAtom } from 'jotai/react'; import { useAtomValue } from 'jotai/react'; import { useCallback, useState } from 'react'; +import { useParams } from 'react-router-dom'; import { openOnboardingModalAtom, openSettingModalAtom } from '../../../atoms'; import { currentModeAtom } from '../../../atoms/mode'; @@ -22,19 +23,16 @@ const DEFAULT_SHOW_LIST: IslandItemNames[] = [ 'shortcuts', ]; const DESKTOP_SHOW_LIST: IslandItemNames[] = [...DEFAULT_SHOW_LIST, 'guide']; -export type IslandItemNames = 'whatNew' | 'contact' | 'shortcuts' | 'guide'; +type IslandItemNames = 'whatNew' | 'contact' | 'shortcuts' | 'guide'; -export const HelpIsland = ({ - showList = environment.isDesktop ? DESKTOP_SHOW_LIST : DEFAULT_SHOW_LIST, -}: { - showList?: IslandItemNames[]; -}) => { +const showList = environment.isDesktop ? DESKTOP_SHOW_LIST : DEFAULT_SHOW_LIST; + +export const HelpIsland = () => { const mode = useAtomValue(currentModeAtom); const setOpenOnboarding = useSetAtom(openOnboardingModalAtom); const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom); const [spread, setShowSpread] = useState(false); const t = useAFFiNEI18N(); - const openSettingModal = useCallback( (tab: SettingProps['activeTab']) => { setShowSpread(false); @@ -56,6 +54,8 @@ export const HelpIsland = ({ [openSettingModal] ); + const { pageId } = useParams(); + return ( { setShowSpread(!spread); }} - inEdgelessPage={mode === 'edgeless'} + inEdgelessPage={!!pageId && mode === 'edgeless'} > (({ spread, inEdgelessPage }) => { return { - transition: 'box-shadow 0.2s', width: '44px', position: 'relative', boxShadow: spread diff --git a/packages/frontend/core/src/components/pure/trash-button-group/index.tsx b/packages/frontend/core/src/components/pure/trash-button-group/index.tsx deleted file mode 100644 index f4592d4ea4..0000000000 --- a/packages/frontend/core/src/components/pure/trash-button-group/index.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import { WorkspaceSubPath } from '@affine/env/workspace'; -import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { assertExists } from '@blocksuite/global/utils'; -import { DeleteIcon, ResetIcon } from '@blocksuite/icons'; -import { Button } from '@toeverything/components/button'; -import { ConfirmModal } from '@toeverything/components/modal'; -import { Tooltip } from '@toeverything/components/tooltip'; -import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; -import { currentPageIdAtom } from '@toeverything/infra/atom'; -import { useAtomValue } from 'jotai'; -import { useCallback, useEffect, useRef, useState } from 'react'; - -import { useAppSettingHelper } from '../../../hooks/affine/use-app-setting-helper'; -import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper'; -import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace'; -import { useNavigateHelper } from '../../../hooks/use-navigate-helper'; -import { toast } from '../../../utils'; -import * as styles from './styles.css'; - -export const TrashButtonGroup = () => { - // fixme(himself65): remove these hooks ASAP - const [workspace] = useCurrentWorkspace(); - const pageId = useAtomValue(currentPageIdAtom); - assertExists(workspace); - assertExists(pageId); - const blockSuiteWorkspace = workspace.blockSuiteWorkspace; - const pageMeta = useBlockSuitePageMeta(blockSuiteWorkspace).find( - meta => meta.id === pageId - ); - assertExists(pageMeta); - const t = useAFFiNEI18N(); - const { appSettings } = useAppSettingHelper(); - const { jumpToSubPath } = useNavigateHelper(); - const { restoreFromTrash } = useBlockSuiteMetaHelper(blockSuiteWorkspace); - const restoreRef = useRef(null); - const deleteRef = useRef(null); - const hintTextRef = useRef(null); - const wrapperRef = useRef(null); - const [open, setOpen] = useState(false); - const [width, setWidth] = useState(0); - const hintText = - 'This page has been moved to the trash, you can either restore or permanently delete it.'; - useEffect(() => { - const currentRef = wrapperRef.current; - - if (!currentRef) { - return; - } - - const handleResize = () => { - if (!currentRef) { - return; - } - - const wrapperWidth = currentRef?.offsetWidth || 0; - setWidth(wrapperWidth); - }; - - const resizeObserver = new ResizeObserver(handleResize); - resizeObserver.observe(currentRef); - - return () => { - resizeObserver.unobserve(currentRef); - }; - }, []); - - return ( -
    -
    - -
    - {hintText} -
    -
    - -
    - - - - - - -
    - { - jumpToSubPath(workspace.id, WorkspaceSubPath.ALL); - blockSuiteWorkspace.removePage(pageId); - toast(t['com.affine.toastMessage.permanentlyDeleted']()); - }, [blockSuiteWorkspace, jumpToSubPath, pageId, workspace.id, t])} - onOpenChange={setOpen} - /> -
    -
    - ); -}; - -export default TrashButtonGroup; diff --git a/packages/frontend/core/src/components/pure/trash-page-footer/index.tsx b/packages/frontend/core/src/components/pure/trash-page-footer/index.tsx new file mode 100644 index 0000000000..57ac8fcc0a --- /dev/null +++ b/packages/frontend/core/src/components/pure/trash-page-footer/index.tsx @@ -0,0 +1,99 @@ +import { Button } from '@affine/component/ui/button'; +import { ConfirmModal } from '@affine/component/ui/modal'; +import { Tooltip } from '@affine/component/ui/tooltip'; +import { WorkspaceSubPath } from '@affine/env/workspace'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { assertExists } from '@blocksuite/global/utils'; +import { DeleteIcon, ResetIcon } from '@blocksuite/icons'; +import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; +import { useCallback, useState } from 'react'; + +import { useAppSettingHelper } from '../../../hooks/affine/use-app-setting-helper'; +import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper'; +import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace'; +import { useNavigateHelper } from '../../../hooks/use-navigate-helper'; +import { toast } from '../../../utils'; +import * as styles from './styles.css'; + +export const TrashPageFooter = ({ pageId }: { pageId: string }) => { + // fixme(himself65): remove these hooks ASAP + const [workspace] = useCurrentWorkspace(); + assertExists(workspace); + const blockSuiteWorkspace = workspace.blockSuiteWorkspace; + const pageMeta = useBlockSuitePageMeta(blockSuiteWorkspace).find( + meta => meta.id === pageId + ); + assertExists(pageMeta); + const t = useAFFiNEI18N(); + const { appSettings } = useAppSettingHelper(); + const { jumpToSubPath } = useNavigateHelper(); + const { restoreFromTrash } = useBlockSuiteMetaHelper(blockSuiteWorkspace); + const [open, setOpen] = useState(false); + const hintText = t['com.affine.cmdk.affine.editor.trash-footer-hint'](); + + const onRestore = useCallback(() => { + restoreFromTrash(pageId); + toast( + t['com.affine.toastMessage.restored']({ + title: pageMeta.title || 'Untitled', + }) + ); + }, [pageId, pageMeta.title, restoreFromTrash, t]); + + const onConfirmDelete = useCallback(() => { + jumpToSubPath(workspace.id, WorkspaceSubPath.ALL); + blockSuiteWorkspace.removePage(pageId); + toast(t['com.affine.toastMessage.permanentlyDeleted']()); + }, [blockSuiteWorkspace, jumpToSubPath, pageId, workspace.id, t]); + + const onDelete = useCallback(() => { + setOpen(true); + }, []); + + return ( +
    +
    {hintText}
    +
    + + + + + + +
    + +
    + ); +}; diff --git a/packages/frontend/core/src/components/pure/trash-button-group/styles.css.ts b/packages/frontend/core/src/components/pure/trash-page-footer/styles.css.ts similarity index 96% rename from packages/frontend/core/src/components/pure/trash-button-group/styles.css.ts rename to packages/frontend/core/src/components/pure/trash-page-footer/styles.css.ts index 71b07c64f0..8895b4118d 100644 --- a/packages/frontend/core/src/components/pure/trash-button-group/styles.css.ts +++ b/packages/frontend/core/src/components/pure/trash-page-footer/styles.css.ts @@ -6,12 +6,13 @@ export const group = style({ justifyContent: 'center', }); export const deleteHintContainer = style({ - position: 'fixed', + position: 'relative', zIndex: 2, padding: '14px 20px', display: 'flex', justifyContent: 'space-between', alignItems: 'center', + flexShrink: 0, bottom: '0', gap: '16px', backgroundColor: 'var(--affine-background-primary-color)', @@ -32,7 +33,6 @@ export const deleteHintText = style({ whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden', - cursor: 'pointer', }); export const buttonContainer = style({ color: 'var(--affine-pure-white)', diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/add-collection-button.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/add-collection-button.tsx index 115784314b..9dfc4ee386 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/add-collection-button.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/add-collection-button.tsx @@ -3,9 +3,9 @@ import { useCollectionManager, useEditCollectionName, } from '@affine/component/page-list'; +import { IconButton } from '@affine/component/ui/button'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { PlusIcon } from '@blocksuite/icons'; -import { IconButton } from '@toeverything/components/button'; import { nanoid } from 'nanoid'; import { useCallback } from 'react'; diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/collections-list.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/collections-list.tsx index 1dfa9edf4b..277531876e 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/collections-list.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/collections-list.tsx @@ -10,6 +10,7 @@ import { useCollectionManager, useSavedCollections, } from '@affine/component/page-list'; +import { IconButton } from '@affine/component/ui/button'; import type { Collection, DeleteCollectionInfo } from '@affine/env/filter'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { InformationIcon, MoreHorizontalIcon } from '@blocksuite/icons'; @@ -17,7 +18,6 @@ import type { PageMeta, Workspace } from '@blocksuite/store'; import type { DragEndEvent } from '@dnd-kit/core'; import { useDroppable } from '@dnd-kit/core'; import * as Collapsible from '@radix-ui/react-collapsible'; -import { IconButton } from '@toeverything/components/button'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useMemo, useState } from 'react'; diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/page.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/page.tsx index b4f701580b..e50458dfd3 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/page.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/page.tsx @@ -1,4 +1,11 @@ import { MenuItem as CollectionItem } from '@affine/component/app-sidebar'; +import { IconButton } from '@affine/component/ui/button'; +import { + Menu, + MenuIcon, + MenuItem, + type MenuItemProps, +} from '@affine/component/ui/menu'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { DeleteIcon, @@ -9,13 +16,6 @@ import { } from '@blocksuite/icons'; import type { PageMeta, Workspace } from '@blocksuite/store'; import * as Collapsible from '@radix-ui/react-collapsible'; -import { IconButton } from '@toeverything/components/button'; -import { - Menu, - MenuIcon, - MenuItem, - type MenuItemProps, -} from '@toeverything/components/menu'; import { useBlockSuitePageReferences } from '@toeverything/hooks/use-block-suite-page-references'; import { useAtomValue } from 'jotai/index'; import type { ReactElement } from 'react'; diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/components/reference-page.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/components/reference-page.tsx index 12f7233c1e..7ffe6c1e0c 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/components/reference-page.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/components/reference-page.tsx @@ -1,7 +1,7 @@ import { MenuLinkItem } from '@affine/component/app-sidebar'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { EdgelessIcon, PageIcon } from '@blocksuite/icons'; -import type { PageMeta, Workspace } from '@blocksuite/store'; +import { type PageMeta, type Workspace } from '@blocksuite/store'; import * as Collapsible from '@radix-ui/react-collapsible'; import { useBlockSuitePageReferences } from '@toeverything/hooks/use-block-suite-page-references'; import { useAtomValue } from 'jotai/react'; @@ -9,6 +9,7 @@ import { useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; import { pageSettingFamily } from '../../../../atoms'; +import { AddFavouriteButton } from '../favorite/add-favourite-button'; import * as styles from '../favorite/styles.css'; interface ReferencePageProps { workspace: Workspace; @@ -54,6 +55,7 @@ export const ReferencePage = ({ icon={icon} collapsed={collapsible ? collapsed : undefined} onCollapsedChange={setCollapsed} + postfix={} > {metaMapping[pageId]?.title || t['Untitled']()} diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/add-favourite-button.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/add-favourite-button.tsx index 92e04e86b8..05bb8dd869 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/add-favourite-button.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/add-favourite-button.tsx @@ -1,6 +1,6 @@ +import { IconButton } from '@affine/component/ui/button'; import { PlusIcon } from '@blocksuite/icons'; import type { Workspace } from '@blocksuite/store'; -import { IconButton } from '@toeverything/components/button'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { usePageMetaHelper } from '@toeverything/hooks/use-block-suite-page-meta'; @@ -8,16 +8,48 @@ import { usePageHelper } from '../../../blocksuite/block-suite-page-list/utils'; type AddFavouriteButtonProps = { workspace: Workspace; + pageId?: string; }; -export const AddFavouriteButton = ({ workspace }: AddFavouriteButtonProps) => { +export const AddFavouriteButton = ({ + workspace, + pageId, +}: AddFavouriteButtonProps) => { const { createPage } = usePageHelper(workspace); const { setPageMeta } = usePageMetaHelper(workspace); - const handleAddFavorite = useAsyncCallback(async () => { - const page = createPage(); - await page.waitForLoaded(); - setPageMeta(page.id, { favorite: true }); - }, [createPage, setPageMeta]); + const handleAddFavorite = useAsyncCallback( + async e => { + if (pageId) { + e.stopPropagation(); + e.preventDefault(); + const page = createPage(); + await page.load(); + const parentPage = workspace.getPage(pageId); + if (parentPage) { + await parentPage.load(); + const text = parentPage.Text.fromDelta([ + { + insert: ' ', + attributes: { + reference: { + type: 'LinkedPage', + pageId: page.id, + }, + }, + }, + ]); + const [frame] = parentPage.getBlockByFlavour('affine:note'); + frame && parentPage.addBlock('affine:paragraph', { text }, frame.id); + setPageMeta(page.id, {}); + } + } else { + const page = createPage(); + await page.load(); + setPageMeta(page.id, { favorite: true }); + } + }, + [createPage, setPageMeta, workspace, pageId] + ); return ( { - return ( - - - - - ); -}; diff --git a/packages/frontend/core/src/components/root-app-sidebar/index.tsx b/packages/frontend/core/src/components/root-app-sidebar/index.tsx index e08826e098..5956f64a1f 100644 --- a/packages/frontend/core/src/components/root-app-sidebar/index.tsx +++ b/packages/frontend/core/src/components/root-app-sidebar/index.tsx @@ -13,18 +13,13 @@ import { SidebarScrollableContainer, } from '@affine/component/app-sidebar'; import { MoveToTrash } from '@affine/component/page-list'; +import { Menu } from '@affine/component/ui/menu'; import { WorkspaceSubPath } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { FolderIcon, SettingsIcon } from '@blocksuite/icons'; import type { Page } from '@blocksuite/store'; import { useDroppable } from '@dnd-kit/core'; -import { Menu } from '@toeverything/components/menu'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; -import { - isAutoCheckUpdateAtom, - isAutoDownloadUpdateAtom, - useAppUpdater, -} from '@toeverything/hooks/use-app-updater'; import { useAtom, useAtomValue } from 'jotai'; import type { HTMLAttributes, ReactElement } from 'react'; import { forwardRef, useCallback, useEffect, useMemo } from 'react'; @@ -107,10 +102,6 @@ export const RootAppSidebar = ({ }: RootAppSidebarProps): ReactElement => { const currentWorkspaceId = currentWorkspace.id; const { appSettings } = useAppSettingHelper(); - const { toggleAutoCheck, toggleAutoDownload } = useAppUpdater(); - const { autoCheckUpdate, autoDownloadUpdate } = appSettings; - const isAutoDownload = useAtomValue(isAutoDownloadUpdateAtom); - const isAutoCheck = useAtomValue(isAutoCheckUpdateAtom); const blockSuiteWorkspace = currentWorkspace.blockSuiteWorkspace; const t = useAFFiNEI18N(); const [openUserWorkspaceList, setOpenUserWorkspaceList] = useAtom( @@ -159,26 +150,6 @@ export const RootAppSidebar = ({ } }, [sidebarOpen]); - useEffect(() => { - if (!environment.isDesktop) { - return; - } - - if (isAutoCheck !== autoCheckUpdate) { - toggleAutoCheck(autoCheckUpdate); - } - if (isAutoDownload !== autoDownloadUpdate) { - toggleAutoDownload(autoDownloadUpdate); - } - }, [ - autoCheckUpdate, - autoDownloadUpdate, - isAutoCheck, - isAutoDownload, - toggleAutoCheck, - toggleAutoDownload, - ]); - const [history, setHistory] = useHistoryAtom(); const router = useMemo(() => { return { diff --git a/packages/frontend/core/src/components/share-header.tsx b/packages/frontend/core/src/components/share-header.tsx deleted file mode 100644 index 20f6c6ab64..0000000000 --- a/packages/frontend/core/src/components/share-header.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import type { Workspace } from '@blocksuite/store'; -import { useSetAtom } from 'jotai/react'; - -import type { PageMode } from '../atoms'; -import { appHeaderAtom, mainContainerAtom } from '../atoms/element'; -import { useWorkspace } from '../hooks/use-workspace'; -import { BlockSuiteHeaderTitle } from './blocksuite/block-suite-header-title'; -import ShareHeaderLeftItem from './cloud/share-header-left-item'; -import ShareHeaderRightItem from './cloud/share-header-right-item'; -import { Header } from './pure/header'; - -export function ShareHeader({ - workspace, - pageId, - publishMode, -}: { - workspace: Workspace; - pageId: string; - publishMode: PageMode; -}) { - const setAppHeader = useSetAtom(appHeaderAtom); - - const currentWorkspace = useWorkspace(workspace.id); - - return ( -
    } - center={ - - } - right={ - - } - bottomBorder - /> - ); -} diff --git a/packages/frontend/core/src/components/workspace-header.tsx b/packages/frontend/core/src/components/workspace-header.tsx deleted file mode 100644 index 18d15026e1..0000000000 --- a/packages/frontend/core/src/components/workspace-header.tsx +++ /dev/null @@ -1,189 +0,0 @@ -import { - CollectionList, - FilterList, - SaveAsCollectionButton, - useCollectionManager, -} from '@affine/component/page-list'; -import { Unreachable } from '@affine/env/constant'; -import type { Collection, Filter } from '@affine/env/filter'; -import type { - WorkspaceFlavour, - WorkspaceHeaderProps, -} from '@affine/env/workspace'; -import { WorkspaceSubPath } from '@affine/env/workspace'; -import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { DeleteIcon } from '@blocksuite/icons'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; -import { useSetAtom } from 'jotai/react'; -import { useCallback } from 'react'; - -import { collectionsCRUDAtom } from '../atoms/collections'; -import { appHeaderAtom, mainContainerAtom } from '../atoms/element'; -import { useAllPageListConfig } from '../hooks/affine/use-all-page-list-config'; -import { useDeleteCollectionInfo } from '../hooks/affine/use-delete-collection-info'; -import { useNavigateHelper } from '../hooks/use-navigate-helper'; -import { useWorkspace } from '../hooks/use-workspace'; -import { SharePageModal } from './affine/share-page-modal'; -import { BlockSuiteHeaderTitle } from './blocksuite/block-suite-header-title'; -import { filterContainerStyle } from './filter-container.css'; -import { Header } from './pure/header'; -import { PluginHeader } from './pure/plugin-header'; -import { WorkspaceModeFilterTab } from './pure/workspace-mode-filter-tab'; -import { TopTip } from './top-tip'; -import * as styles from './workspace-header.css'; - -const FilterContainer = ({ workspaceId }: { workspaceId: string }) => { - const currentWorkspace = useWorkspace(workspaceId); - const navigateHelper = useNavigateHelper(); - const setting = useCollectionManager(collectionsCRUDAtom); - const saveToCollection = useCallback( - async (collection: Collection) => { - await setting.createCollection({ - ...collection, - filterList: setting.currentCollection.filterList, - }); - navigateHelper.jumpToCollection(workspaceId, collection.id); - }, - [setting, navigateHelper, workspaceId] - ); - - const onFilterChange = useAsyncCallback( - async (filterList: Filter[]) => { - await setting.updateCollection({ - ...setting.currentCollection, - filterList, - }); - }, - [setting] - ); - - if (!setting.isDefault || !setting.currentCollection.filterList.length) { - return null; - } - - return ( -
    -
    - -
    -
    - {setting.currentCollection.filterList.length > 0 ? ( - - ) : null} -
    -
    - ); -}; - -export function WorkspaceHeader({ - currentWorkspaceId, - currentEntry, - rightSlot, -}: WorkspaceHeaderProps) { - const setAppHeader = useSetAtom(appHeaderAtom); - - const currentWorkspace = useWorkspace(currentWorkspaceId); - const workspace = currentWorkspace.blockSuiteWorkspace; - const setting = useCollectionManager(collectionsCRUDAtom); - const config = useAllPageListConfig(); - const userInfo = useDeleteCollectionInfo(); - - const t = useAFFiNEI18N(); - - // route in all page - if ( - 'subPath' in currentEntry && - currentEntry.subPath === WorkspaceSubPath.ALL - ) { - return ( - <> -
    - } - right={rightSlot} - center={} - /> - - - ); - } - - // route in shared - if ( - 'subPath' in currentEntry && - currentEntry.subPath === WorkspaceSubPath.SHARED - ) { - return ( -
    } - /> - ); - } - - // route in trash - if ( - 'subPath' in currentEntry && - currentEntry.subPath === WorkspaceSubPath.TRASH - ) { - return ( -
    - - {t['com.affine.workspaceSubPath.trash']()} -
    - } - /> - ); - } - - // route in edit page - if ('pageId' in currentEntry) { - const currentPage = workspace.getPage(currentEntry.pageId); - const sharePageModal = currentPage ? ( - - ) : null; - return ( - <> -
    - } - right={ -
    - {sharePageModal} - -
    - } - bottomBorder - /> - - - ); - } - - throw new Unreachable(); -} diff --git a/packages/frontend/core/src/components/workspace-upgrade/upgrade.tsx b/packages/frontend/core/src/components/workspace-upgrade/upgrade.tsx index e132926baa..62fd5dcf98 100644 --- a/packages/frontend/core/src/components/workspace-upgrade/upgrade.tsx +++ b/packages/frontend/core/src/components/workspace-upgrade/upgrade.tsx @@ -1,6 +1,6 @@ import { AffineShapeIcon } from '@affine/component/page-list'; // TODO: import from page-list temporarily, need to defined common svg icon/images management. +import { Button } from '@affine/component/ui/button'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Button } from '@toeverything/components/button'; import type { MigrationPoint } from '@toeverything/infra/blocksuite'; import { useCallback, useMemo } from 'react'; diff --git a/packages/frontend/core/src/hooks/affine/use-app-setting-helper.ts b/packages/frontend/core/src/hooks/affine/use-app-setting-helper.ts index f918f8be83..1be32c0b8f 100644 --- a/packages/frontend/core/src/hooks/affine/use-app-setting-helper.ts +++ b/packages/frontend/core/src/hooks/affine/use-app-setting-helper.ts @@ -1,8 +1,7 @@ +import { type AppSetting, appSettingAtom } from '@toeverything/infra/atom'; import { useAtom } from 'jotai'; import { useCallback, useMemo } from 'react'; -import { type AppSetting, appSettingAtom } from '../../atoms/settings'; - export function useAppSettingHelper() { const [appSettings, setAppSettings] = useAtom(appSettingAtom); diff --git a/packages/frontend/core/src/hooks/affine/use-current-user.ts b/packages/frontend/core/src/hooks/affine/use-current-user.ts index 94b4b967a2..35cbe9bbc3 100644 --- a/packages/frontend/core/src/hooks/affine/use-current-user.ts +++ b/packages/frontend/core/src/hooks/affine/use-current-user.ts @@ -106,5 +106,6 @@ export function useCurrentUser(): CheckedUser { hasPassword: user?.hasPassword ?? false, update, }; - }, [user, update]); + // spread the user object to make sure the hook will not be re-rendered when user ref changed but the properties not. + }, [user.id, user.name, user.email, user.image, user.hasPassword, update]); } diff --git a/packages/frontend/core/src/hooks/current/use-current-page.ts b/packages/frontend/core/src/hooks/current/use-current-page.ts new file mode 100644 index 0000000000..0e5a35ada8 --- /dev/null +++ b/packages/frontend/core/src/hooks/current/use-current-page.ts @@ -0,0 +1,12 @@ +import { + currentPageIdAtom, + currentWorkspaceAtom, +} from '@toeverything/infra/atom'; +import { useAtomValue } from 'jotai'; + +export const useCurrentPage = () => { + const currentPageId = useAtomValue(currentPageIdAtom); + const currentWorkspace = useAtomValue(currentWorkspaceAtom); + + return currentPageId ? currentWorkspace.getPage(currentPageId) : null; +}; diff --git a/packages/frontend/core/src/index.tsx b/packages/frontend/core/src/index.tsx index 3667442d56..b7cef0c072 100644 --- a/packages/frontend/core/src/index.tsx +++ b/packages/frontend/core/src/index.tsx @@ -1,5 +1,6 @@ import './polyfill/ses-lockdown'; import './polyfill/intl-segmenter'; +import './polyfill/request-idle-callback'; import { WorkspaceFallback } from '@affine/component/workspace'; import { assertExists } from '@blocksuite/global/utils'; diff --git a/packages/frontend/core/src/layouts/workspace-layout.tsx b/packages/frontend/core/src/layouts/workspace-layout.tsx index 3c4a8d3498..ab848eeb00 100644 --- a/packages/frontend/core/src/layouts/workspace-layout.tsx +++ b/packages/frontend/core/src/layouts/workspace-layout.tsx @@ -2,16 +2,11 @@ import { AppSidebarFallback, appSidebarResizingAtom, } from '@affine/component/app-sidebar'; -import { RootBlockHub } from '@affine/component/block-hub'; import { type DraggableTitleCellData, PageListDragOverlay, } from '@affine/component/page-list'; -import { - MainContainer, - ToolContainer, - WorkspaceFallback, -} from '@affine/component/workspace'; +import { MainContainer, WorkspaceFallback } from '@affine/component/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom'; import { getBlobEngine } from '@affine/workspace/manager'; @@ -36,12 +31,10 @@ import { useLocation, useParams } from 'react-router-dom'; import { Map as YMap } from 'yjs'; import { openQuickSearchModalAtom, openSettingModalAtom } from '../atoms'; -import { mainContainerAtom } from '../atoms/element'; import { AdapterProviderWrapper } from '../components/adapter-worksapce-wrapper'; import { AppContainer } from '../components/affine/app-container'; +import { SyncAwareness } from '../components/affine/awareness'; import { usePageHelper } from '../components/blocksuite/block-suite-page-list/utils'; -import type { IslandItemNames } from '../components/pure/help-island'; -import { HelpIsland } from '../components/pure/help-island'; import { processCollectionsDrag } from '../components/pure/workspace-slider-bar/collections'; import { DROPPABLE_SIDEBAR_TRASH, @@ -91,10 +84,6 @@ export const QuickSearch = () => { ); }; -const showList: IslandItemNames[] = environment.isDesktop - ? ['whatNew', 'contact', 'guide'] - : ['whatNew', 'contact']; - export const CurrentWorkspaceContext = ({ children, }: PropsWithChildren): ReactNode => { @@ -259,12 +248,6 @@ export const WorkspaceLayoutInner = ({ const { appSettings } = useAppSettingHelper(); const location = useLocation(); - const { pageId } = useParams(); - const pageMeta = useBlockSuitePageMeta( - currentWorkspace.blockSuiteWorkspace - ).find(meta => meta.id === pageId); - const inTrashPage = pageMeta?.trash ?? false; - const setMainContainer = useSetAtom(mainContainerAtom); return ( <> @@ -293,27 +276,20 @@ export const WorkspaceLayoutInner = ({ paths={pathGenerator} /> - }> - + }> + {migration ? ( ) : ( children )} - - - - + ); }; diff --git a/packages/frontend/core/src/pages/expired.tsx b/packages/frontend/core/src/pages/expired.tsx index 5d4cd67ae8..6c6efbcd6d 100644 --- a/packages/frontend/core/src/pages/expired.tsx +++ b/packages/frontend/core/src/pages/expired.tsx @@ -1,6 +1,6 @@ import { AuthPageContainer } from '@affine/component/auth-components'; +import { Button } from '@affine/component/ui/button'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Button } from '@toeverything/components/button'; import { useCallback } from 'react'; import { RouteLogic, useNavigateHelper } from '../hooks/use-navigate-helper'; diff --git a/packages/frontend/core/src/pages/index.tsx b/packages/frontend/core/src/pages/index.tsx index a980a5879d..0d98c9e85c 100644 --- a/packages/frontend/core/src/pages/index.tsx +++ b/packages/frontend/core/src/pages/index.tsx @@ -1,6 +1,6 @@ +import { Menu } from '@affine/component/ui/menu'; import { DebugLogger } from '@affine/debug'; import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom'; -import { Menu } from '@toeverything/components/menu'; import { getWorkspace } from '@toeverything/infra/__internal__/workspace'; import { getCurrentStore } from '@toeverything/infra/atom'; import { lazy } from 'react'; diff --git a/packages/frontend/core/src/pages/open-app.tsx b/packages/frontend/core/src/pages/open-app.tsx index b26da450ff..6941a45289 100644 --- a/packages/frontend/core/src/pages/open-app.tsx +++ b/packages/frontend/core/src/pages/open-app.tsx @@ -1,9 +1,9 @@ +import { Button } from '@affine/component/ui/button'; import { type GetCurrentUserQuery, getCurrentUserQuery } from '@affine/graphql'; import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { fetcher } from '@affine/workspace/affine/gql'; import { Logo1Icon } from '@blocksuite/icons'; -import { Button } from '@toeverything/components/button'; import { useCallback, useMemo } from 'react'; import { type LoaderFunction, diff --git a/packages/frontend/core/src/pages/share/detail-page.tsx b/packages/frontend/core/src/pages/share/share-detail-page.tsx similarity index 96% rename from packages/frontend/core/src/pages/share/detail-page.tsx rename to packages/frontend/core/src/pages/share/share-detail-page.tsx index 0776f83237..e255212a63 100644 --- a/packages/frontend/core/src/pages/share/detail-page.tsx +++ b/packages/frontend/core/src/pages/share/share-detail-page.tsx @@ -18,11 +18,11 @@ import { } from 'react-router-dom'; import { applyUpdate } from 'yjs'; -import { PageDetailEditor } from '../../adapters/shared'; import type { PageMode } from '../../atoms'; import { AppContainer } from '../../components/affine/app-container'; -import { ShareHeader } from '../../components/share-header'; +import { PageDetailEditor } from '../../components/page-detail-editor'; import { SharePageNotFoundError } from '../../components/share-page-not-found-error'; +import { ShareHeader } from './share-header'; type LoaderData = { page: Page; diff --git a/packages/frontend/core/src/pages/share/share-header.tsx b/packages/frontend/core/src/pages/share/share-header.tsx new file mode 100644 index 0000000000..bbcf38f4be --- /dev/null +++ b/packages/frontend/core/src/pages/share/share-header.tsx @@ -0,0 +1,42 @@ +import type { Workspace } from '@blocksuite/store'; + +import type { PageMode } from '../../atoms'; +import { BlockSuiteHeaderTitle } from '../../components/blocksuite/block-suite-header-title'; +import ShareHeaderLeftItem from '../../components/cloud/share-header-left-item'; +import ShareHeaderRightItem from '../../components/cloud/share-header-right-item'; +import { Header } from '../../components/pure/header'; +import { useWorkspace } from '../../hooks/use-workspace'; + +export function ShareHeader({ + workspace, + pageId, + publishMode, +}: { + workspace: Workspace; + pageId: string; + publishMode: PageMode; +}) { + const currentWorkspace = useWorkspace(workspace.id); + + return ( +
    } + center={ + + } + right={ + + } + bottomBorder + /> + ); +} diff --git a/packages/frontend/core/src/pages/upgrade-success.css.ts b/packages/frontend/core/src/pages/upgrade-success.css.ts index 4183c5ae7f..654f56d424 100644 --- a/packages/frontend/core/src/pages/upgrade-success.css.ts +++ b/packages/frontend/core/src/pages/upgrade-success.css.ts @@ -1,87 +1,9 @@ import { style } from '@vanilla-extract/css'; - -export const root = style({ - height: '100vh', - width: '100vw', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - fontSize: 'var(--affine-font-base)', - position: 'relative', -}); - -export const affineLogo = style({ - color: 'inherit', -}); - -export const topNav = style({ - position: 'absolute', - top: 0, - left: 0, - right: 0, - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - padding: '16px 120px', -}); - -export const topNavLinks = style({ - display: 'flex', - columnGap: 4, -}); - -export const topNavLink = style({ - color: 'var(--affine-text-primary-color)', - fontSize: 'var(--affine-font-sm)', - fontWeight: 500, - textDecoration: 'none', - padding: '4px 18px', -}); - -export const tryAgainLink = style({ - color: 'var(--affine-link-color)', - fontWeight: 500, - textDecoration: 'none', - fontSize: 'var(--affine-font-sm)', -}); - -export const centerContent = style({ - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - marginTop: 40, -}); - -export const prompt = style({ - marginTop: 20, - marginBottom: 12, -}); - -export const body = style({ - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - width: '100%', - flexWrap: 'wrap', - gap: '48px', - padding: '0 20px', -}); - -export const leftContainer = style({ - display: 'flex', - flexDirection: 'column', - width: '548px', - gap: '28px', -}); -export const leftContentTitle = style({ - fontSize: 'var(--affine-font-title)', - fontWeight: 700, - minHeight: '44px', -}); export const leftContentText = style({ fontSize: 'var(--affine-font-base)', fontWeight: 400, lineHeight: '1.6', + maxWidth: '548px', }); export const mail = style({ diff --git a/packages/frontend/core/src/pages/upgrade-success.tsx b/packages/frontend/core/src/pages/upgrade-success.tsx index a8af870ead..ce67a89711 100644 --- a/packages/frontend/core/src/pages/upgrade-success.tsx +++ b/packages/frontend/core/src/pages/upgrade-success.tsx @@ -1,8 +1,7 @@ -import { Empty } from '@affine/component'; +import { AuthPageContainer } from '@affine/component/auth-components'; +import { Button } from '@affine/component/ui/button'; import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { Logo1Icon } from '@blocksuite/icons'; -import { Button } from '@toeverything/components/button'; import { useCallback } from 'react'; import { useNavigateHelper } from '../hooks/use-navigate-helper'; @@ -11,93 +10,40 @@ import * as styles from './upgrade-success.css'; export const UpgradeSuccess = () => { const t = useAFFiNEI18N(); - const openDownloadLink = useCallback(() => { - const url = `https://affine.pro/download`; - open(url, '_blank'); - }, []); - const { jumpToIndex } = useNavigateHelper(); const openAffine = useCallback(() => { jumpToIndex(); }, [jumpToIndex]); - return ( -
    - -
    -
    -
    - {t['com.affine.payment.upgrade-success-page.title']()} -
    -
    - {t['com.affine.payment.upgrade-success-page.text']()} -
    - - ), - }} + const subtitle = ( +
    + {t['com.affine.payment.upgrade-success-page.text']()} +
    + -
    -
    -
    - -
    -
    - + ), + }} + />
    ); + + return ( + + + + ); }; export const Component = () => { diff --git a/packages/frontend/core/src/pages/workspace/all-page/all-page-filter.tsx b/packages/frontend/core/src/pages/workspace/all-page/all-page-filter.tsx new file mode 100644 index 0000000000..2c88292722 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/all-page/all-page-filter.tsx @@ -0,0 +1,60 @@ +import { + FilterList, + SaveAsCollectionButton, + useCollectionManager, +} from '@affine/component/page-list'; +import type { Collection, Filter } from '@affine/env/filter'; +import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; +import { useCallback } from 'react'; + +import { collectionsCRUDAtom } from '../../../atoms/collections'; +import { filterContainerStyle } from '../../../components/filter-container.css'; +import { useNavigateHelper } from '../../../hooks/use-navigate-helper'; +import { useWorkspace } from '../../../hooks/use-workspace'; + +export const FilterContainer = ({ workspaceId }: { workspaceId: string }) => { + const currentWorkspace = useWorkspace(workspaceId); + const navigateHelper = useNavigateHelper(); + const setting = useCollectionManager(collectionsCRUDAtom); + const saveToCollection = useCallback( + async (collection: Collection) => { + await setting.createCollection({ + ...collection, + filterList: setting.currentCollection.filterList, + }); + navigateHelper.jumpToCollection(workspaceId, collection.id); + }, + [setting, navigateHelper, workspaceId] + ); + + const onFilterChange = useAsyncCallback( + async (filterList: Filter[]) => { + await setting.updateCollection({ + ...setting.currentCollection, + filterList, + }); + }, + [setting] + ); + + if (!setting.isDefault || !setting.currentCollection.filterList.length) { + return null; + } + + return ( +
    +
    + +
    +
    + {setting.currentCollection.filterList.length > 0 ? ( + + ) : null} +
    +
    + ); +}; diff --git a/packages/frontend/core/src/pages/workspace/all-page.css.ts b/packages/frontend/core/src/pages/workspace/all-page/all-page.css.ts similarity index 91% rename from packages/frontend/core/src/pages/workspace/all-page.css.ts rename to packages/frontend/core/src/pages/workspace/all-page/all-page.css.ts index 32ecc9f4c4..70db74ebc4 100644 --- a/packages/frontend/core/src/pages/workspace/all-page.css.ts +++ b/packages/frontend/core/src/pages/workspace/all-page/all-page.css.ts @@ -67,3 +67,10 @@ export const headerCreateNewButtonHidden = style({ opacity: 0, pointerEvents: 'none', }); + +export const headerRightWindows = style({ + display: 'flex', + alignItems: 'center', + gap: 8, + transform: 'translateX(16px)', +}); diff --git a/packages/frontend/core/src/pages/workspace/all-page.tsx b/packages/frontend/core/src/pages/workspace/all-page/all-page.tsx similarity index 78% rename from packages/frontend/core/src/pages/workspace/all-page.tsx rename to packages/frontend/core/src/pages/workspace/all-page/all-page.tsx index f4a44e1e40..d07a8fb2d3 100644 --- a/packages/frontend/core/src/pages/workspace/all-page.tsx +++ b/packages/frontend/core/src/pages/workspace/all-page/all-page.tsx @@ -1,5 +1,6 @@ import { toast } from '@affine/component'; import { + CollectionList, currentCollectionAtom, FloatingToolbar, NewPageButton as PureNewPageButton, @@ -8,7 +9,7 @@ import { useCollectionManager, VirtualizedPageList, } from '@affine/component/page-list'; -import { WorkspaceFlavour, WorkspaceSubPath } from '@affine/env/workspace'; +import { WorkspaceFlavour } from '@affine/env/workspace'; import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { assertExists } from '@blocksuite/global/utils'; @@ -18,7 +19,7 @@ import { PlusIcon, ViewLayersIcon, } from '@blocksuite/icons'; -import type { PageMeta } from '@blocksuite/store'; +import type { PageMeta, Workspace } from '@blocksuite/store'; import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { getBlockSuiteWorkspaceAtom } from '@toeverything/infra/__internal__/workspace'; import { getCurrentStore } from '@toeverything/infra/atom'; @@ -34,16 +35,22 @@ import type { LoaderFunction } from 'react-router-dom'; import { redirect } from 'react-router-dom'; import { NIL } from 'uuid'; -import { collectionsCRUDAtom } from '../../atoms/collections'; -import { usePageHelper } from '../../components/blocksuite/block-suite-page-list/utils'; -import { WorkspaceHeader } from '../../components/workspace-header'; -import { useBlockSuiteMetaHelper } from '../../hooks/affine/use-block-suite-meta-helper'; -import { useTrashModalHelper } from '../../hooks/affine/use-trash-modal-helper'; -import { useCurrentWorkspace } from '../../hooks/current/use-current-workspace'; -import { performanceRenderLogger } from '../../shared'; +import { collectionsCRUDAtom } from '../../../atoms/collections'; +import { HubIsland } from '../../../components/affine/hub-island'; +import { usePageHelper } from '../../../components/blocksuite/block-suite-page-list/utils'; +import { Header } from '../../../components/pure/header'; +import { WindowsAppControls } from '../../../components/pure/header/windows-app-controls'; +import { WorkspaceModeFilterTab } from '../../../components/pure/workspace-mode-filter-tab'; +import { useAllPageListConfig } from '../../../hooks/affine/use-all-page-list-config'; +import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper'; +import { useDeleteCollectionInfo } from '../../../hooks/affine/use-delete-collection-info'; +import { useTrashModalHelper } from '../../../hooks/affine/use-trash-modal-helper'; +import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace'; +import { performanceRenderLogger } from '../../../shared'; +import { EmptyPageList } from '../page-list-empty'; +import { useFilteredPageMetas } from '../pages'; import * as styles from './all-page.css'; -import { EmptyPageList } from './page-list-empty'; -import { useFilteredPageMetas } from './pages'; +import { FilterContainer } from './all-page-filter'; export const loader: LoaderFunction = async args => { const rootStore = getCurrentStore(); @@ -217,6 +224,50 @@ const NewPageButton = ({ ); }; +const AllPageHeader = ({ + workspace, + showCreateNew, +}: { + workspace: Workspace; + showCreateNew: boolean; +}) => { + const setting = useCollectionManager(collectionsCRUDAtom); + const config = useAllPageListConfig(); + const userInfo = useDeleteCollectionInfo(); + const isWindowsDesktop = environment.isDesktop && environment.isWindows; + + return ( + <> +
    + } + right={ +
    + + + + {isWindowsDesktop ? : null} +
    + } + center={} + /> + + + ); +}; + // even though it is called all page, it is also being used for collection route as well export const AllPage = () => { const [currentWorkspace] = useCurrentWorkspace(); @@ -250,22 +301,9 @@ export const AllPage = () => { return (
    {currentWorkspace.flavour !== WorkspaceFlavour.AFFINE_PUBLIC ? ( - - - - } + ) : null} {filteredPageMetas.length > 0 ? ( @@ -295,9 +333,11 @@ export const AllPage = () => { ) : ( } blockSuiteWorkspace={currentWorkspace.blockSuiteWorkspace} /> )} +
    ); }; diff --git a/packages/frontend/core/src/pages/workspace/collection.tsx b/packages/frontend/core/src/pages/workspace/collection.tsx index f66bb5fe75..3db8be5986 100644 --- a/packages/frontend/core/src/pages/workspace/collection.tsx +++ b/packages/frontend/core/src/pages/workspace/collection.tsx @@ -30,7 +30,7 @@ import { useCurrentWorkspace } from '../../hooks/current/use-current-workspace'; import { useNavigateHelper } from '../../hooks/use-navigate-helper'; import { WorkspaceSubPath } from '../../shared'; import { getWorkspaceSetting } from '../../utils/workspace-setting'; -import { AllPage } from './all-page'; +import { AllPage } from './all-page/all-page'; import * as styles from './collection.css'; export const loader: LoaderFunction = async args => { diff --git a/packages/frontend/core/src/pages/workspace/detail-page.tsx b/packages/frontend/core/src/pages/workspace/detail-page.tsx deleted file mode 100644 index bc8cd4996c..0000000000 --- a/packages/frontend/core/src/pages/workspace/detail-page.tsx +++ /dev/null @@ -1,203 +0,0 @@ -import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton'; -import { - createTagFilter, - useCollectionManager, -} from '@affine/component/page-list'; -import { WorkspaceSubPath } from '@affine/env/workspace'; -import { globalBlockSuiteSchema } from '@affine/workspace/manager'; -import { SyncEngineStep } from '@affine/workspace/providers'; -import type { EditorContainer } from '@blocksuite/editor'; -import { assertExists } from '@blocksuite/global/utils'; -import type { Page } from '@blocksuite/store'; -import { - contentLayoutAtom, - currentPageIdAtom, - currentWorkspaceIdAtom, -} from '@toeverything/infra/atom'; -import { useAtomValue, useSetAtom } from 'jotai'; -import { type ReactElement, useCallback, useEffect, useState } from 'react'; -import { type LoaderFunction, useParams } from 'react-router-dom'; -import type { Map as YMap } from 'yjs'; - -import { getUIAdapter } from '../../adapters/workspace'; -import { setPageModeAtom } from '../../atoms'; -import { collectionsCRUDAtom } from '../../atoms/collections'; -import { currentModeAtom } from '../../atoms/mode'; -import { AffineErrorBoundary } from '../../components/affine/affine-error-boundary'; -import { GlobalPageHistoryModal } from '../../components/affine/page-history-modal'; -import { WorkspaceHeader } from '../../components/workspace-header'; -import { useRegisterBlocksuiteEditorCommands } from '../../hooks/affine/use-register-blocksuite-editor-commands'; -import { useCurrentSyncEngineStatus } from '../../hooks/current/use-current-sync-engine'; -import { useCurrentWorkspace } from '../../hooks/current/use-current-workspace'; -import { useNavigateHelper } from '../../hooks/use-navigate-helper'; -import { performanceRenderLogger } from '../../shared'; - -const DetailPageImpl = (): ReactElement => { - const { openPage, jumpToSubPath } = useNavigateHelper(); - const currentPageId = useAtomValue(currentPageIdAtom); - const [currentWorkspace] = useCurrentWorkspace(); - assertExists(currentWorkspace); - assertExists(currentPageId); - const blockSuiteWorkspace = currentWorkspace.blockSuiteWorkspace; - const { setTemporaryFilter } = useCollectionManager(collectionsCRUDAtom); - const mode = useAtomValue(currentModeAtom); - const setPageMode = useSetAtom(setPageModeAtom); - useRegisterBlocksuiteEditorCommands(currentPageId, mode); - const onLoad = useCallback( - (page: Page, editor: EditorContainer) => { - try { - const surfaceBlock = page.getBlockByFlavour('affine:surface')[0]; - // hotfix for old page - if ( - surfaceBlock && - (surfaceBlock.yBlock.get('prop:elements') as YMap).get( - 'type' - ) !== '$blocksuite:internal:native$' - ) { - globalBlockSuiteSchema.upgradePage( - 0, - { - 'affine:surface': 3, - }, - page.spaceDoc - ); - } - } catch {} - setPageMode(currentPageId, mode); - const dispose = editor.slots.pageLinkClicked.on(({ pageId }) => { - return openPage(blockSuiteWorkspace.id, pageId); - }); - const disposeTagClick = editor.slots.tagClicked.on(async ({ tagId }) => { - jumpToSubPath(currentWorkspace.id, WorkspaceSubPath.ALL); - setTemporaryFilter([createTagFilter(tagId)]); - }); - return () => { - dispose.dispose(); - disposeTagClick.dispose(); - }; - }, - [ - blockSuiteWorkspace.id, - currentPageId, - currentWorkspace.id, - jumpToSubPath, - mode, - openPage, - setPageMode, - setTemporaryFilter, - ] - ); - - const { PageDetail } = getUIAdapter(currentWorkspace.flavour); - return ( - <> - - - - - - ); -}; - -export const DetailPage = (): ReactElement => { - const [currentWorkspace] = useCurrentWorkspace(); - const currentSyncEngineStatus = useCurrentSyncEngineStatus(); - const currentPageId = useAtomValue(currentPageIdAtom); - const [page, setPage] = useState(null); - - // load page by current page id - useEffect(() => { - if (!currentPageId) { - setPage(null); - return; - } - - const exists = currentWorkspace.blockSuiteWorkspace.getPage(currentPageId); - - if (exists) { - setPage(exists); - return; - } - - const dispose = currentWorkspace.blockSuiteWorkspace.slots.pagesUpdated.on( - () => { - const exists = - currentWorkspace.blockSuiteWorkspace.getPage(currentPageId); - - if (exists) { - setPage(exists); - } - } - ); - - return dispose.dispose; - }, [currentPageId, currentWorkspace]); - - const navigate = useNavigateHelper(); - - // if sync engine has been synced and the page is null, wait 1s and jump to 404 page. - useEffect(() => { - if (currentSyncEngineStatus?.step === SyncEngineStep.Synced && !page) { - const timeout = setTimeout(() => { - navigate.jumpTo404(); - }, 1000); - return () => { - clearTimeout(timeout); - }; - } - return; - }, [currentSyncEngineStatus, navigate, page]); - - if (!currentPageId || !page) { - return ; - } - - if (page.meta.jumpOnce) { - currentWorkspace.blockSuiteWorkspace.setPageMeta(page.id, { - jumpOnce: false, - }); - } - - return ; -}; - -export const loader: LoaderFunction = async () => { - return null; -}; - -export const Component = () => { - performanceRenderLogger.info('DetailPage'); - - const setContentLayout = useSetAtom(contentLayoutAtom); - const setCurrentWorkspaceId = useSetAtom(currentWorkspaceIdAtom); - const setCurrentPageId = useSetAtom(currentPageIdAtom); - const params = useParams(); - - useEffect(() => { - setContentLayout('editor'); - if (params.workspaceId) { - localStorage.setItem('last_workspace_id', params.workspaceId); - setCurrentWorkspaceId(params.workspaceId); - } - if (params.pageId) { - localStorage.setItem('last_page_id', params.pageId); - setCurrentPageId(params.pageId); - } - }, [params, setContentLayout, setCurrentPageId, setCurrentWorkspaceId]); - - // Add a key to force rerender when page changed, to avoid error boundary persisting. - return ( - - - - ); -}; diff --git a/packages/frontend/core/src/pages/workspace/detail-page/detail-page-header.css.ts b/packages/frontend/core/src/pages/workspace/detail-page/detail-page-header.css.ts new file mode 100644 index 0000000000..73b4f6052c --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/detail-page/detail-page-header.css.ts @@ -0,0 +1,59 @@ +import type { ComplexStyleRule } from '@vanilla-extract/css'; +import { style } from '@vanilla-extract/css'; + +export const header = style({ + display: 'flex', + height: '52px', + width: '100%', + alignItems: 'center', + flexShrink: 0, + background: 'var(--affine-background-primary-color)', + borderBottom: '1px solid var(--affine-border-color)', + selectors: { + '&[data-sidebar-floating="false"]': { + WebkitAppRegion: 'drag', + }, + }, + '@media': { + print: { + display: 'none', + }, + }, + ':has([data-popper-placement])': { + WebkitAppRegion: 'no-drag', + }, +} as ComplexStyleRule); + +export const mainHeader = style([ + header, + { + padding: '0 16px', + }, +]); + +export const sidebarHeader = style([ + header, + { + padding: '0 14px', + gap: '12px', + }, +]); + +export const mainHeaderRight = style({ + display: 'flex', + alignItems: 'center', + gap: '8px', +}); + +export const spacer = style({ + flexGrow: 1, +}); + +export const standaloneExtensionSwitcherWrapper = style({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + flexShrink: 0, + height: '52px', + position: 'relative', +}); diff --git a/packages/frontend/core/src/pages/workspace/detail-page/detail-page-header.tsx b/packages/frontend/core/src/pages/workspace/detail-page/detail-page-header.tsx new file mode 100644 index 0000000000..6628da77e8 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/detail-page/detail-page-header.tsx @@ -0,0 +1,159 @@ +import { IconButton } from '@affine/component'; +import { + appSidebarFloatingAtom, + appSidebarOpenAtom, + SidebarSwitch, +} from '@affine/component/app-sidebar'; +import type { AllWorkspace } from '@affine/core/shared'; +import { RightSidebarIcon } from '@blocksuite/icons'; +import type { Page } from '@blocksuite/store'; +import { useAtomValue, useSetAtom } from 'jotai'; + +import { SharePageButton } from '../../../components/affine/share-page-modal'; +import { BlockSuiteHeaderTitle } from '../../../components/blocksuite/block-suite-header-title'; +import { HeaderDivider } from '../../../components/pure/header'; +import { WindowsAppControls } from '../../../components/pure/header/windows-app-controls'; +import * as styles from './detail-page-header.css'; +import { ExtensionTabs } from './editor-sidebar'; +import { + editorSidebarOpenAtom, + editorSidebarToggleAtom, +} from './editor-sidebar/atoms'; + +interface PageHeaderRightProps { + showSidebarSwitch?: boolean; +} + +const ToggleSidebarButton = () => { + const toggle = useSetAtom(editorSidebarToggleAtom); + return ( + + + + ); +}; +const isWindowsDesktop = environment.isDesktop && environment.isWindows; + +const WindowsMainPageHeaderRight = ({ + showSidebarSwitch, +}: PageHeaderRightProps) => { + const editorSidebarOpen = useAtomValue(editorSidebarOpenAtom); + + if (editorSidebarOpen) { + return null; + } + + return ( + <> + +
    + {showSidebarSwitch ? : null} + +
    + + ); +}; + +const NonWindowsMainPageHeaderRight = ({ + showSidebarSwitch, +}: PageHeaderRightProps) => { + const editorSidebarOpen = useAtomValue(editorSidebarOpenAtom); + + if (editorSidebarOpen || !showSidebarSwitch) { + return null; + } + + return ( + <> + +
    + +
    + + ); +}; + +function Header({ + children, + style, + className, +}: { + children: React.ReactNode; + className?: string; + style?: React.CSSProperties; +}) { + const appSidebarFloating = useAtomValue(appSidebarFloatingAtom); + return ( +
    + {children} +
    + ); +} + +export function DetailPageHeader({ + page, + workspace, + showSidebarSwitch = true, +}: { + page: Page; + workspace: AllWorkspace; + showSidebarSwitch?: boolean; +}) { + const leftSidebarOpen = useAtomValue(appSidebarOpenAtom); + const RightHeader = isWindowsDesktop + ? WindowsMainPageHeaderRight + : NonWindowsMainPageHeaderRight; + return ( +
    + + {!leftSidebarOpen ? : null} + +
    + {page ? : null} + +
    + ); +} + +function WindowsSidebarHeader() { + return ( + <> +
    +
    + + +
    +
    + +
    + + ); +} + +function NonWindowsSidebarHeader() { + return ( +
    + +
    + +
    + ); +} + +export function RightSidebarHeader() { + return isWindowsDesktop ? ( + + ) : ( + + ); +} diff --git a/packages/frontend/core/src/pages/workspace/detail-page/detail-page.css.ts b/packages/frontend/core/src/pages/workspace/detail-page/detail-page.css.ts new file mode 100644 index 0000000000..4c01eab62b --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/detail-page/detail-page.css.ts @@ -0,0 +1,95 @@ +import { style } from '@vanilla-extract/css'; + +export const root = style({ + display: 'flex', + height: '100%', + overflow: 'hidden', + width: '100%', +}); + +export const mainContainer = style({ + display: 'flex', + flex: 1, + height: '100%', + position: 'relative', + flexDirection: 'column', + width: '100%', + selectors: { + [`${root}[data-client-border] &`]: { + borderRadius: '4px', + }, + }, +}); + +export const editorContainer = style({ + position: 'relative', + display: 'flex', + flexDirection: 'column', + flex: 1, + overflow: 'hidden', + zIndex: 0, // it will create stacking context to limit layer of child elements and be lower than after auto zIndex +}); + +export const resizeHandle = style({ + width: '1px', + position: 'relative', + backgroundColor: 'var(--affine-border-color)', + selectors: { + '&[data-collapsed=true]': { + display: 'none', + }, + [`${root}[data-client-border] &`]: { + width: '8px', + backgroundColor: 'transparent', + }, + }, +}); + +export const resizeHandleInner = style({ + height: '100%', + width: '10px', // this is the real hit box + position: 'absolute', + transform: 'translateX(-50%)', + zIndex: 10, + transition: 'all 0.2s ease-in-out', + display: 'flex', + justifyContent: 'center', + '::before': { + content: '""', + width: '0px', + height: '100%', + borderRadius: '2px', + transition: 'all 0.2s ease-in-out', + }, + selectors: { + [`${root}[data-client-border] &`]: { + transform: 'translateX(-1px)', + }, + [`:is(${resizeHandle}:hover, ${resizeHandle}[data-resize-handle-active]) &::before`]: + { + width: '2px', + backgroundColor: 'var(--affine-primary-color)', + }, + [`${resizeHandle}[data-resize-handle-active] &::before`]: { + width: '4px', + borderRadius: '4px', + }, + }, +}); + +export const sidebarContainer = style({ + transition: 'flex 0.2s ease-in-out', + display: 'flex', + flexDirection: 'column', + selectors: { + [`${resizeHandle}[data-resize-handle-active] + &`]: { + transition: 'none', + }, + [`${root}[data-disable-animation] &`]: { + transition: 'none', + }, + [`${root}[data-client-border] &`]: { + borderRadius: '4px', + }, + }, +}); diff --git a/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx b/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx new file mode 100644 index 0000000000..62fd0b0838 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx @@ -0,0 +1,363 @@ +import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton'; +import { + createTagFilter, + useCollectionManager, +} from '@affine/component/page-list'; +import { WorkspaceSubPath } from '@affine/env/workspace'; +import { globalBlockSuiteSchema } from '@affine/workspace/manager'; +import { SyncEngineStep } from '@affine/workspace/providers'; +import { assertExists } from '@blocksuite/global/utils'; +import type { EditorContainer } from '@blocksuite/presets'; +import type { Page, Workspace } from '@blocksuite/store'; +import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; +import { + appSettingAtom, + currentPageIdAtom, + currentWorkspaceIdAtom, +} from '@toeverything/infra/atom'; +import { useAtomValue, useSetAtom } from 'jotai'; +import { + type ReactElement, + type ReactNode, + useCallback, + useEffect, + useRef, + useState, +} from 'react'; +import type { PanelOnResize } from 'react-resizable-panels'; +import { + type ImperativePanelHandle, + Panel, + PanelGroup, + PanelResizeHandle, +} from 'react-resizable-panels'; +import { type LoaderFunction, useParams } from 'react-router-dom'; +import type { Map as YMap } from 'yjs'; + +import { setPageModeAtom } from '../../../atoms'; +import { collectionsCRUDAtom } from '../../../atoms/collections'; +import { currentModeAtom } from '../../../atoms/mode'; +import { AffineErrorBoundary } from '../../../components/affine/affine-error-boundary'; +import { HubIsland } from '../../../components/affine/hub-island'; +import { GlobalPageHistoryModal } from '../../../components/affine/page-history-modal'; +import { PageDetailEditor } from '../../../components/page-detail-editor'; +import { TrashPageFooter } from '../../../components/pure/trash-page-footer'; +import { TopTip } from '../../../components/top-tip'; +import { useRegisterBlocksuiteEditorCommands } from '../../../hooks/affine/use-register-blocksuite-editor-commands'; +import { + useCurrentSyncEngine, + useCurrentSyncEngineStatus, +} from '../../../hooks/current/use-current-sync-engine'; +import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace'; +import { useNavigateHelper } from '../../../hooks/use-navigate-helper'; +import { performanceRenderLogger } from '../../../shared'; +import * as styles from './detail-page.css'; +import { DetailPageHeader, RightSidebarHeader } from './detail-page-header'; +import { + EditorSidebar, + editorSidebarOpenAtom, + editorSidebarStateAtom, + editorSidebarWidthAtom, +} from './editor-sidebar'; + +interface DetailPageLayoutProps { + main: ReactNode; + header: ReactNode; + footer: ReactNode; + sidebar: ReactNode; +} + +// disable animation to avoid UI flash +function useEnableAnimation() { + const [enable, setEnable] = useState(false); + useEffect(() => { + window.setTimeout(() => { + setEnable(true); + }, 500); + }, []); + return enable; +} + +// todo: consider move to a shared place if we also want to reuse the layout for other routes +const DetailPageLayout = ({ + main, + header, + footer, + sidebar, +}: DetailPageLayoutProps): ReactElement => { + const sidebarState = useAtomValue(editorSidebarStateAtom); + const setSidebarWidth = useSetAtom(editorSidebarWidthAtom); + const setSidebarOpen = useSetAtom(editorSidebarOpenAtom); + const { clientBorder } = useAtomValue(appSettingAtom); + + const sidebarRef = useRef(null); + + const onExpandSidebar = useCallback(() => { + setSidebarOpen(true); + }, [setSidebarOpen]); + + const onCollapseSidebar = useCallback(() => { + setSidebarOpen(false); + }, [setSidebarOpen]); + + const onResize: PanelOnResize = useCallback( + e => { + if (e.sizePixels > 0) { + setSidebarWidth(e.sizePixels); + } + }, + [setSidebarWidth] + ); + + useEffect(() => { + const panelHandle = sidebarRef.current; + if (!panelHandle) { + return; + } + + if (sidebarState.isOpen) { + panelHandle.expand(); + } else { + panelHandle.collapse(); + } + }, [sidebarState.isOpen]); + + const enableAnimation = useEnableAnimation(); + + return ( + + + {header} + {main} + {footer} + + {sidebar ? ( + <> + +
    + + + {sidebar} + + + ) : null} + + ); +}; + +const DetailPageImpl = ({ page }: { page: Page }) => { + const currentPageId = page.id; + const { openPage, jumpToSubPath } = useNavigateHelper(); + const [currentWorkspace] = useCurrentWorkspace(); + assertExists( + currentWorkspace, + 'current workspace is null when rendering detail' + ); + const blockSuiteWorkspace = currentWorkspace.blockSuiteWorkspace; + + const pageMeta = useBlockSuitePageMeta(blockSuiteWorkspace).find( + meta => meta.id === page.id + ); + + const isInTrash = pageMeta?.trash; + + const { setTemporaryFilter } = useCollectionManager(collectionsCRUDAtom); + const mode = useAtomValue(currentModeAtom); + const setPageMode = useSetAtom(setPageModeAtom); + useRegisterBlocksuiteEditorCommands(currentPageId, mode); + + const onLoad = useCallback( + (page: Page, editor: EditorContainer) => { + try { + // todo(joooye34): improve the following migration code + const surfaceBlock = page.getBlockByFlavour('affine:surface')[0]; + // hotfix for old page + if ( + surfaceBlock && + (surfaceBlock.yBlock.get('prop:elements') as YMap).get( + 'type' + ) !== '$blocksuite:internal:native$' + ) { + globalBlockSuiteSchema.upgradePage( + 0, + { + 'affine:surface': 3, + }, + page.spaceDoc + ); + } + } catch {} + setPageMode(currentPageId, mode); + const dispose = editor.slots.pageLinkClicked.on(({ pageId }) => { + return openPage(blockSuiteWorkspace.id, pageId); + }); + const disposeTagClick = editor.slots.tagClicked.on(async ({ tagId }) => { + jumpToSubPath(currentWorkspace.id, WorkspaceSubPath.ALL); + setTemporaryFilter([createTagFilter(tagId)]); + }); + return () => { + dispose.dispose(); + disposeTagClick.dispose(); + }; + }, + [ + blockSuiteWorkspace.id, + currentPageId, + currentWorkspace.id, + jumpToSubPath, + mode, + openPage, + setPageMode, + setTemporaryFilter, + ] + ); + + return ( + <> + + + + + } + main={ +
    + + +
    + } + footer={isInTrash ? : null} + sidebar={ + !isInTrash ? ( + <> + + + + ) : null + } + /> + + + ); +}; + +const useForceUpdate = () => { + const [, setCount] = useState(0); + return useCallback(() => setCount(count => count + 1), []); +}; +const useSafePage = (workspace: Workspace, pageId: string) => { + const forceUpdate = useForceUpdate(); + useEffect(() => { + const disposable = workspace.slots.pagesUpdated.on(() => { + forceUpdate(); + }); + return disposable.dispose; + }, [pageId, workspace.slots.pagesUpdated, forceUpdate]); + + return workspace.getPage(pageId); +}; + +export const DetailPage = ({ pageId }: { pageId: string }): ReactElement => { + const [currentWorkspace] = useCurrentWorkspace(); + const currentSyncEngineStatus = useCurrentSyncEngineStatus(); + const currentSyncEngine = useCurrentSyncEngine(); + + // set sync engine priority target + useEffect(() => { + currentSyncEngine?.setPriorityRule(id => id.endsWith(pageId)); + }, [pageId, currentSyncEngine, currentWorkspace]); + + const page = useSafePage(currentWorkspace?.blockSuiteWorkspace, pageId); + + const navigate = useNavigateHelper(); + + // if sync engine has been synced and the page is null, wait 1s and jump to 404 page. + useEffect(() => { + if (currentSyncEngineStatus?.step === SyncEngineStep.Synced && !page) { + const timeout = setTimeout(() => { + navigate.jumpTo404(); + }, 1000); + return () => { + clearTimeout(timeout); + }; + } + return; + }, [currentSyncEngineStatus, navigate, page]); + + if (!page) { + return ; + } + + if (page.meta.jumpOnce) { + currentWorkspace.blockSuiteWorkspace.setPageMeta(page.id, { + jumpOnce: false, + }); + } + + return ; +}; + +export const loader: LoaderFunction = async () => { + return null; +}; + +export const Component = () => { + performanceRenderLogger.info('DetailPage'); + + const setCurrentWorkspaceId = useSetAtom(currentWorkspaceIdAtom); + const setCurrentPageId = useSetAtom(currentPageIdAtom); + const params = useParams(); + + useEffect(() => { + if (params.workspaceId) { + localStorage.setItem('last_workspace_id', params.workspaceId); + setCurrentWorkspaceId(params.workspaceId); + } + if (params.pageId) { + localStorage.setItem('last_page_id', params.pageId); + setCurrentPageId(params.pageId); + } + }, [params, setCurrentPageId, setCurrentWorkspaceId]); + + const pageId = params.pageId; + + // Add a key to force rerender when page changed, to avoid error boundary persisting. + return ( + + {pageId ? : null} + + ); +}; diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/atoms.ts b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/atoms.ts new file mode 100644 index 0000000000..0d0fcfb8d9 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/atoms.ts @@ -0,0 +1,79 @@ +// main editor sidebar states +import { assertExists } from '@blocksuite/global/utils'; +import { atom } from 'jotai'; +import { selectAtom } from 'jotai/utils'; + +import { outlineExtension } from './extensions/outline'; +import type { EditorExtension, EditorExtensionName } from './types'; + +// the list of all possible extensions in affine. +// order matters (determines the order of the tabs) +export const extensions: EditorExtension[] = [outlineExtension]; + +export interface EditorSidebarState { + isOpen: boolean; + width: number; + activeExtension?: EditorExtension; + extensions: EditorExtension[]; +} + +const baseStateAtom = atom({ + isOpen: false, + width: 300, // todo: should be resizable + activeExtension: extensions[0], + extensions: extensions, // todo: maybe should be dynamic (by feature flag?) +}); + +export const editorSidebarStateAtom = atom(get => get(baseStateAtom)); + +const isOpenAtom = selectAtom(baseStateAtom, state => state.isOpen); +const activeExtensionAtom = selectAtom( + baseStateAtom, + state => state.activeExtension +); +const widthAtom = selectAtom(baseStateAtom, state => state.width); + +export const editorExtensionsAtom = selectAtom( + baseStateAtom, + state => state.extensions +); + +// get/set sidebar open state +export const editorSidebarOpenAtom = atom( + get => get(isOpenAtom), + (_, set, isOpen: boolean) => { + set(baseStateAtom, prev => { + return { ...prev, isOpen }; + }); + } +); + +// get/set active extension +export const editorSidebarActiveExtensionAtom = atom( + get => get(activeExtensionAtom), + (_, set, extension: EditorExtensionName) => { + set(baseStateAtom, prev => { + const extensions = prev.extensions; + const newExtension = extensions.find(e => e.name === extension); + assertExists(newExtension, `extension ${extension} not found`); + return { ...prev, activeExtension: newExtension }; + }); + } +); + +// toggle sidebar (write only) +export const editorSidebarToggleAtom = atom(null, (_, set) => { + set(baseStateAtom, prev => { + return { ...prev, isOpen: !prev.isOpen }; + }); +}); + +// get/set sidebar width +export const editorSidebarWidthAtom = atom( + get => get(widthAtom), + (_, set, width: number) => { + set(baseStateAtom, prev => { + return { ...prev, width }; + }); + } +); diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/editor-sidebar.css.ts b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/editor-sidebar.css.ts new file mode 100644 index 0000000000..5c9fd6fa13 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/editor-sidebar.css.ts @@ -0,0 +1,10 @@ +import { style } from '@vanilla-extract/css'; + +export const root = style({ + display: 'flex', + flexDirection: 'column', + flex: 1, + overflow: 'auto', + width: '100%', + minWidth: '300px', +}); diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/editor-sidebar.tsx b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/editor-sidebar.tsx new file mode 100644 index 0000000000..9d0427f2cf --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/editor-sidebar.tsx @@ -0,0 +1,16 @@ +import { useAtomValue } from 'jotai'; + +import { editorSidebarStateAtom } from './atoms'; +import * as styles from './editor-sidebar.css'; + +export const EditorSidebar = () => { + const sidebarState = useAtomValue(editorSidebarStateAtom); + const Component = sidebarState.activeExtension?.Component; + + // do we need this? + if (!sidebarState.isOpen) { + return null; + } + + return
    {Component ? : null}
    ; +}; diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/extensions.css.ts b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/extensions.css.ts new file mode 100644 index 0000000000..f5231a4e1a --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/extensions.css.ts @@ -0,0 +1,45 @@ +import { createVar, style } from '@vanilla-extract/css'; + +export const activeIdx = createVar(); + +export const switchRoot = style({ + vars: { + [activeIdx]: '0', + }, + display: 'flex', + alignItems: 'center', + gap: '8px', + height: '32px', + borderRadius: '12px', + padding: '4px', + position: 'relative', + background: 'var(--affine-background-secondary-color)', + + '::after': { + content: '""', + display: 'block', + width: '24px', + height: '24px', + background: 'var(--affine-background-primary-color)', + boxShadow: 'var(--affine-shadow-1)', + borderRadius: '8px', + position: 'absolute', + transform: `translateX(calc(${activeIdx} * 32px))`, + transition: 'all .15s', + }, +}); + +export const button = style({ + width: '24px', + height: '24px', + borderRadius: '8px', + color: 'var(--affine-icon-color)', + position: 'relative', + zIndex: 1, + + selectors: { + '&[data-active=true]': { + color: 'var(--affine-primary-color)', + }, + }, +}); diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/extensions.tsx b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/extensions.tsx new file mode 100644 index 0000000000..01c0a07221 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/extensions.tsx @@ -0,0 +1,37 @@ +import { IconButton } from '@affine/component'; +import { assignInlineVars } from '@vanilla-extract/dynamic'; +import { useAtom, useAtomValue } from 'jotai'; + +import { + editorExtensionsAtom, + editorSidebarActiveExtensionAtom, +} from '../atoms'; +import * as styles from './extensions.css'; + +// provide a switcher for active extensions +// will be used in global top header (MacOS) or sidebar (Windows) +export const ExtensionTabs = () => { + const exts = useAtomValue(editorExtensionsAtom); + const [selected, setSelected] = useAtom(editorSidebarActiveExtensionAtom); + const vars = assignInlineVars({ + [styles.activeIdx]: String( + exts.findIndex(ext => ext.name === selected?.name) ?? 0 + ), + }); + return ( +
    + {exts.map(extension => { + return ( + setSelected(extension.name)} + key={extension.name} + data-active={selected?.name === extension.name} + className={styles.button} + > + {extension.icon} + + ); + })} +
    + ); +}; diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/frame.css.ts b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/frame.css.ts new file mode 100644 index 0000000000..0a2dbb4421 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/frame.css.ts @@ -0,0 +1,7 @@ +import { style } from '@vanilla-extract/css'; + +export const root = style({ + display: 'flex', + height: '100%', + width: '100%', +}); diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/frame.tsx b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/frame.tsx new file mode 100644 index 0000000000..9ac1705446 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/frame.tsx @@ -0,0 +1,41 @@ +import { TOCNotesPanel } from '@blocksuite/blocks'; +import { assertExists } from '@blocksuite/global/utils'; +import { FrameIcon } from '@blocksuite/icons'; +import { useCallback, useRef } from 'react'; + +import { useCurrentPage } from '../../../../../hooks/current/use-current-page'; +import type { EditorExtension } from '../types'; +import * as styles from './frame.css'; + +// A wrapper for TOCNotesPanel +const EditorOutline = () => { + const tocPanelRef = useRef(null); + const currentPage = useCurrentPage(); + + const onRefChange = useCallback((container: HTMLDivElement | null) => { + if (container) { + assertExists(tocPanelRef.current, 'toc panel should be initialized'); + container.append(tocPanelRef.current); + } + }, []); + + if (!currentPage) { + return; + } + + if (!tocPanelRef.current) { + tocPanelRef.current = new TOCNotesPanel(); + } + + if (currentPage !== tocPanelRef.current?.page) { + (tocPanelRef.current as TOCNotesPanel).page = currentPage; + } + + return
    ; +}; + +export const frameExtension: EditorExtension = { + name: 'frame', + icon: , + Component: EditorOutline, +}; diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/index.tsx b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/index.tsx new file mode 100644 index 0000000000..4509f5ccb0 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/index.tsx @@ -0,0 +1 @@ +export * from './extensions'; diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/outline.css.ts b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/outline.css.ts new file mode 100644 index 0000000000..0a2dbb4421 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/outline.css.ts @@ -0,0 +1,7 @@ +import { style } from '@vanilla-extract/css'; + +export const root = style({ + display: 'flex', + height: '100%', + width: '100%', +}); diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/outline.tsx b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/outline.tsx new file mode 100644 index 0000000000..eaa2f3cac1 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/outline.tsx @@ -0,0 +1,41 @@ +import { TOCNotesPanel } from '@blocksuite/blocks'; +import { assertExists } from '@blocksuite/global/utils'; +import { TocIcon } from '@blocksuite/icons'; +import { useCallback, useRef } from 'react'; + +import { useCurrentPage } from '../../../../../hooks/current/use-current-page'; +import type { EditorExtension } from '../types'; +import * as styles from './outline.css'; + +// A wrapper for TOCNotesPanel +const EditorOutline = () => { + const tocPanelRef = useRef(null); + const currentPage = useCurrentPage(); + + const onRefChange = useCallback((container: HTMLDivElement | null) => { + if (container) { + assertExists(tocPanelRef.current, 'toc panel should be initialized'); + container.append(tocPanelRef.current); + } + }, []); + + if (!currentPage) { + return; + } + + if (!tocPanelRef.current) { + tocPanelRef.current = new TOCNotesPanel(); + } + + if (currentPage !== tocPanelRef.current?.page) { + (tocPanelRef.current as TOCNotesPanel).page = currentPage; + } + + return
    ; +}; + +export const outlineExtension: EditorExtension = { + name: 'outline', + icon: , + Component: EditorOutline, +}; diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/index.ts b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/index.ts new file mode 100644 index 0000000000..3ee1e3b1b2 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/index.ts @@ -0,0 +1,4 @@ +export * from './atoms'; +export * from './editor-sidebar'; +export * from './extensions'; +export * from './types'; diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/types.ts b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/types.ts new file mode 100644 index 0000000000..ab7ab4d043 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/types.ts @@ -0,0 +1,7 @@ +export type EditorExtensionName = 'outline' | 'frame'; + +export interface EditorExtension { + name: EditorExtensionName; + icon: React.ReactNode; + Component: React.ComponentType; +} diff --git a/packages/frontend/core/src/pages/workspace/detail-page/index.ts b/packages/frontend/core/src/pages/workspace/detail-page/index.ts new file mode 100644 index 0000000000..dbf539bf24 --- /dev/null +++ b/packages/frontend/core/src/pages/workspace/detail-page/index.ts @@ -0,0 +1 @@ +export * from './detail-page'; diff --git a/packages/frontend/core/src/pages/workspace/index.tsx b/packages/frontend/core/src/pages/workspace/index.tsx index a9b20a7f45..bd70b0e2b3 100644 --- a/packages/frontend/core/src/pages/workspace/index.tsx +++ b/packages/frontend/core/src/pages/workspace/index.tsx @@ -45,9 +45,13 @@ export const loader: LoaderFunction = async args => { return redirect('/404'); } - if (!args.params.pageId) { + if (args.params.pageId) { + localStorage.setItem('last_page_id', args.params.pageId); + rootStore.set(currentPageIdAtom, args.params.pageId); + } else { rootStore.set(currentPageIdAtom, null); } + const [workspaceAtom] = getBlockSuiteWorkspaceAtom(currentMetadata.id); workspaceLoaderLogger.info('get cloud workspace atom'); diff --git a/packages/frontend/core/src/pages/workspace/page-list-empty.tsx b/packages/frontend/core/src/pages/workspace/page-list-empty.tsx index 4cf41ea70e..dc813941a5 100644 --- a/packages/frontend/core/src/pages/workspace/page-list-empty.tsx +++ b/packages/frontend/core/src/pages/workspace/page-list-empty.tsx @@ -2,7 +2,7 @@ import { Empty } from '@affine/component'; import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import type { Workspace } from '@blocksuite/store'; -import { useCallback } from 'react'; +import { type ReactNode, useCallback } from 'react'; import { usePageHelper } from '../../components/blocksuite/block-suite-page-list/utils'; import * as styles from './page-list-empty.css'; @@ -10,9 +10,11 @@ import * as styles from './page-list-empty.css'; export const EmptyPageList = ({ type, blockSuiteWorkspace, + heading, }: { type: 'all' | 'trash' | 'shared' | 'public'; blockSuiteWorkspace: Workspace; + heading?: ReactNode; }) => { const { createPage } = usePageHelper(blockSuiteWorkspace); const t = useAFFiNEI18N(); @@ -56,6 +58,7 @@ export const EmptyPageList = ({ return (
    + {heading &&
    {heading}
    } { + const t = useAFFiNEI18N(); + return ( +
    + + {t['com.affine.workspaceSubPath.trash']()} +
    + } + right={ + isWindowsDesktop ? ( +
    + +
    + ) : null + } + /> + ); +}; export const TrashPage = () => { const [currentWorkspace] = useCurrentWorkspace(); @@ -60,12 +83,7 @@ export const TrashPage = () => { ); return (
    - + {filteredPageMetas.length > 0 ? ( import('./pages/workspace/all-page'), + lazy: () => import('./pages/workspace/all-page/all-page'), }, { path: 'collection/:collectionId', @@ -25,13 +25,13 @@ export const routes = [ }, { path: ':pageId', - lazy: () => import('./pages/workspace/detail-page'), + lazy: () => import('./pages/workspace/detail-page/detail-page'), }, ], }, { path: '/share/:workspaceId/:pageId', - lazy: () => import('./pages/share/detail-page'), + lazy: () => import('./pages/share/share-detail-page'), }, { path: '/404', diff --git a/packages/frontend/core/src/utils/toast.ts b/packages/frontend/core/src/utils/toast.ts index 4e951259c1..b65e99d96f 100644 --- a/packages/frontend/core/src/utils/toast.ts +++ b/packages/frontend/core/src/utils/toast.ts @@ -1,21 +1,16 @@ import type { ToastOptions } from '@affine/component'; import { toast as basicToast } from '@affine/component'; -import { assertEquals, assertExists } from '@blocksuite/global/utils'; -import { getCurrentStore } from '@toeverything/infra/atom'; - -import { mainContainerAtom } from '../atoms/element'; +import { assertEquals } from '@blocksuite/global/utils'; export const toast = (message: string, options?: ToastOptions) => { - const mainContainer = getCurrentStore().get(mainContainerAtom); const modal = document.querySelector( '[role=presentation]' ) as HTMLDivElement | null; - assertExists(mainContainer, 'main container should exist'); if (modal) { assertEquals(modal.constructor, HTMLDivElement, 'modal should be div'); } return basicToast(message, { - portal: modal || mainContainer || document.body, + portal: modal || document.body, ...options, }); }; diff --git a/packages/frontend/electron/package.json b/packages/frontend/electron/package.json index 47a3a543ff..f191dd0430 100644 --- a/packages/frontend/electron/package.json +++ b/packages/frontend/electron/package.json @@ -11,14 +11,14 @@ "homepage": "https://github.com/toeverything/AFFiNE", "scripts": { "start": "electron .", - "dev": "DEV_SERVER_URL=http://localhost:8080 yarn ts-node ./scripts/dev.ts", - "dev:prod": "yarn ts-node scripts/dev.ts", - "build": "NODE_ENV=production ts-node scripts/build-layers.ts", - "build:dev": "NODE_ENV=development ts-node scripts/build-layers.ts", - "generate-assets": "ts-node scripts/generate-assets.ts", - "package": "cross-env NODE_OPTIONS=\"--loader ts-node/esm\" electron-forge package", - "make": "cross-env NODE_OPTIONS=\"--loader ts-node/esm\" electron-forge make", - "make-squirrel": "yarn ts-node scripts/make-squirrel.ts" + "dev": "DEV_SERVER_URL=http://localhost:8080 node --loader ts-node/esm/transpile-only ./scripts/dev.ts", + "dev:prod": "yarn node --loader ts-node/esm/transpile-only scripts/dev.ts", + "build": "NODE_ENV=production node --loader ts-node/esm/transpile-only scripts/build-layers.ts", + "build:dev": "NODE_ENV=development node --loader ts-node/esm/transpile-only scripts/build-layers.ts", + "generate-assets": "node --loader ts-node/esm/transpile-only scripts/generate-assets.ts", + "package": "cross-env NODE_OPTIONS=\"--loader ts-node/esm/transpile-only\" electron-forge package", + "make": "cross-env NODE_OPTIONS=\"--loader ts-node/esm/transpile-only\" electron-forge make", + "make-squirrel": "node --loader ts-node/esm/transpile-only scripts/make-squirrel.ts" }, "main": "./dist/main.js", "devDependencies": { @@ -32,10 +32,10 @@ "@affine/sdk": "workspace:*", "@affine/templates": "workspace:*", "@affine/vue-hello-world-plugin": "workspace:*", - "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/lit": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/blocks": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/lit": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/presets": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/store": "0.11.0-nightly-202312070955-2b5bb47", "@electron-forge/cli": "^7.1.0", "@electron-forge/core": "^7.1.0", "@electron-forge/core-utils": "^7.1.0", @@ -64,7 +64,7 @@ "semver": "^7.5.4", "tinykeys": "^2.1.0", "ts-node": "^10.9.1", - "undici": "^5.27.2", + "undici": "^6.0.0", "uuid": "^9.0.1", "vitest": "0.34.6", "which": "^4.0.0", diff --git a/packages/frontend/electron/src/helper/db/migration.ts b/packages/frontend/electron/src/helper/db/migration.ts index d6cb90f199..393b0f76b4 100644 --- a/packages/frontend/electron/src/helper/db/migration.ts +++ b/packages/frontend/electron/src/helper/db/migration.ts @@ -1,4 +1,3 @@ -import { equal } from 'node:assert'; import { resolve } from 'node:path'; import { SqliteConnection } from '@affine/native'; @@ -78,19 +77,23 @@ export const migrateToLatest = async ( }; await downloadBinary(rootDoc, true); const result = await forceUpgradePages(rootDoc, schema); - equal(result, true, 'migrateWorkspace should return boolean value'); - const uploadBinary = async (doc: YDoc, isRoot: boolean) => { - await connection.replaceUpdates(doc.guid, [ - { docId: isRoot ? undefined : doc.guid, data: encodeStateAsUpdate(doc) }, - ]); - // connection..applyUpdate(encodeStateAsUpdate(doc), 'self', doc.guid) - await Promise.all( - [...doc.subdocs].map(subdoc => { - return uploadBinary(subdoc, false); - }) - ); - }; - await uploadBinary(rootDoc, true); + if (result) { + const uploadBinary = async (doc: YDoc, isRoot: boolean) => { + await connection.replaceUpdates(doc.guid, [ + { + docId: isRoot ? undefined : doc.guid, + data: encodeStateAsUpdate(doc), + }, + ]); + // connection..applyUpdate(encodeStateAsUpdate(doc), 'self', doc.guid) + await Promise.all( + [...doc.subdocs].map(subdoc => { + return uploadBinary(subdoc, false); + }) + ); + }; + await uploadBinary(rootDoc, true); + } await connection.close(); }; diff --git a/packages/frontend/electron/src/main/application-menu/create.ts b/packages/frontend/electron/src/main/application-menu/create.ts index c57ee266f9..dd16bc3b6c 100644 --- a/packages/frontend/electron/src/main/application-menu/create.ts +++ b/packages/frontend/electron/src/main/application-menu/create.ts @@ -125,7 +125,7 @@ export function createApplicationMenu() { { label: 'Check for Updates', click: async () => { - await checkForUpdates(true); + await checkForUpdates(); }, }, ], diff --git a/packages/frontend/electron/src/main/deep-link.ts b/packages/frontend/electron/src/main/deep-link.ts index d006c55bb5..bc5edb05f6 100644 --- a/packages/frontend/electron/src/main/deep-link.ts +++ b/packages/frontend/electron/src/main/deep-link.ts @@ -5,10 +5,10 @@ import { type App, type BrowserWindow, ipcMain } from 'electron'; import { buildType, CLOUD_BASE_URL, isDev } from './config'; import { logger } from './logger'; import { + getOrCreateWindow, handleOpenUrlInHiddenWindow, mainWindowOrigin, removeCookie, - restoreOrCreateWindow, setCookie, } from './main-window'; @@ -39,8 +39,9 @@ export function setupDeepLink(app: App) { // on windows & linux, we need to listen for the second-instance event app.on('second-instance', (event, commandLine) => { - restoreOrCreateWindow() - .then(() => { + getOrCreateWindow() + .then(window => { + window.show(); const url = commandLine.pop(); if (url?.startsWith(`${protocol}://`)) { event.preventDefault(); @@ -67,7 +68,7 @@ async function handleAffineUrl(url: string) { async function handleOauthJwt(url: string) { if (url) { try { - const mainWindow = await restoreOrCreateWindow(); + const mainWindow = await getOrCreateWindow(); mainWindow.show(); const urlObj = new URL(url); const token = urlObj.searchParams.get('token'); diff --git a/packages/frontend/electron/src/main/helper-process.ts b/packages/frontend/electron/src/main/helper-process.ts index 23b6da1653..d2d3d05fce 100644 --- a/packages/frontend/electron/src/main/helper-process.ts +++ b/packages/frontend/electron/src/main/helper-process.ts @@ -20,6 +20,8 @@ import { logger } from './logger'; const HELPER_PROCESS_PATH = path.join(__dirname, './helper.js'); +const isDev = process.env.NODE_ENV === 'development'; + function pickAndBind( obj: T, keys: U[] @@ -48,12 +50,16 @@ class HelperProcessManager { } private constructor() { - const helperProcess = utilityProcess.fork(HELPER_PROCESS_PATH); + const helperProcess = utilityProcess.fork(HELPER_PROCESS_PATH, [], { + // todo: port number should not being used + execArgv: isDev ? ['--inspect=40894'] : [], + }); this.#process = helperProcess; this.ready = new Promise((resolve, reject) => { helperProcess.once('spawn', () => { try { this.#connectMain(); + logger.info('[helper] forked', helperProcess.pid); resolve(); } catch (err) { logger.error('[helper] connectMain error', err); diff --git a/packages/frontend/electron/src/main/index.ts b/packages/frontend/electron/src/main/index.ts index ccaed0b076..3a38caa3f0 100644 --- a/packages/frontend/electron/src/main/index.ts +++ b/packages/frontend/electron/src/main/index.ts @@ -11,7 +11,7 @@ import { registerEvents } from './events'; import { registerHandlers } from './handlers'; import { ensureHelperProcess } from './helper-process'; import { logger } from './logger'; -import { restoreOrCreateWindow } from './main-window'; +import { getOrCreateWindow } from './main-window'; import { registerProtocol } from './protocol'; import { registerUpdater } from './updater'; @@ -56,7 +56,7 @@ app.on('window-all-closed', () => { * @see https://www.electronjs.org/docs/v14-x-y/api/app#event-activate-macos Event: 'activate' */ app.on('activate', () => { - restoreOrCreateWindow().catch(e => + getOrCreateWindow().catch(e => console.error('Failed to restore or create window:', e) ); }); @@ -72,7 +72,7 @@ app .then(registerHandlers) .then(registerEvents) .then(ensureHelperProcess) - .then(restoreOrCreateWindow) + .then(getOrCreateWindow) .then(createApplicationMenu) .then(registerUpdater) .catch(e => console.error('Failed create window:', e)); diff --git a/packages/frontend/electron/src/main/main-window.ts b/packages/frontend/electron/src/main/main-window.ts index 9aeeb5e3e7..c4234adfa8 100644 --- a/packages/frontend/electron/src/main/main-window.ts +++ b/packages/frontend/electron/src/main/main-window.ts @@ -148,16 +148,11 @@ let browserWindow$: Promise | undefined; /** * Restore existing BrowserWindow or Create new BrowserWindow */ -export async function restoreOrCreateWindow() { +export async function getOrCreateWindow() { if (!browserWindow$ || (await browserWindow$.then(w => w.isDestroyed()))) { browserWindow$ = createWindow(); } const mainWindow = await browserWindow$; - - if (mainWindow.isMinimized()) { - mainWindow.restore(); - logger.info('restore main window'); - } return mainWindow; } @@ -188,7 +183,11 @@ export async function setCookie( arg0: CookiesSetDetails | string, arg1?: string ) { - const window = await restoreOrCreateWindow(); + const window = await browserWindow$; + if (!window) { + // do nothing if window is not ready + return; + } const details = typeof arg1 === 'string' && typeof arg0 === 'string' ? parseCookie(arg0, arg1) @@ -204,12 +203,20 @@ export async function setCookie( } export async function removeCookie(url: string, name: string): Promise { - const window = await restoreOrCreateWindow(); + const window = await browserWindow$; + if (!window) { + // do nothing if window is not ready + return; + } await window.webContents.session.cookies.remove(url, name); } export async function getCookie(url?: string, name?: string) { - const window = await restoreOrCreateWindow(); + const window = await browserWindow$; + if (!window) { + // do nothing if window is not ready + return; + } const cookies = await window.webContents.session.cookies.get({ url, name, diff --git a/packages/frontend/electron/src/main/protocol.ts b/packages/frontend/electron/src/main/protocol.ts index 9b622a0cf4..e874ff6cab 100644 --- a/packages/frontend/electron/src/main/protocol.ts +++ b/packages/frontend/electron/src/main/protocol.ts @@ -119,8 +119,12 @@ export function registerProtocol() { // if sending request to the cloud, attach the session cookie if (isNetworkResource(pathname)) { const cookie = await getCookie(CLOUD_BASE_URL); - const cookieString = cookie.map(c => `${c.name}=${c.value}`).join('; '); - details.requestHeaders['cookie'] = cookieString; + if (cookie) { + const cookieString = cookie + .map(c => `${c.name}=${c.value}`) + .join('; '); + details.requestHeaders['cookie'] = cookieString; + } } callback({ cancel: false, diff --git a/packages/frontend/electron/src/main/updater/electron-updater.ts b/packages/frontend/electron/src/main/updater/electron-updater.ts index 7594664107..0cbba4d980 100644 --- a/packages/frontend/electron/src/main/updater/electron-updater.ts +++ b/packages/frontend/electron/src/main/updater/electron-updater.ts @@ -17,9 +17,9 @@ export const quitAndInstall = async () => { autoUpdater.quitAndInstall(); }; -let lastCheckTime = 0; - let downloading = false; +let configured = false; +let checkingUpdate = false; export type UpdaterConfig = { autoCheckUpdate: boolean; @@ -36,29 +36,39 @@ export const getConfig = (): UpdaterConfig => { }; export const setConfig = (newConfig: Partial = {}): void => { + configured = true; + Object.assign(config, newConfig); + + logger.info('Updater configured!', config); + + // if config.autoCheckUpdate is true, trigger a check + if (config.autoCheckUpdate) { + checkForUpdates().catch(err => { + logger.error('Error checking for updates', err); + }); + } }; -export const checkForUpdates = async (force = false) => { - if (disabled) { +export const checkForUpdates = async () => { + if (disabled || checkingUpdate) { return; } - - if ( - force || - (config.autoCheckUpdate && lastCheckTime + 1000 * 1800 < Date.now()) - ) { - lastCheckTime = Date.now(); - return await autoUpdater.checkForUpdates(); + checkingUpdate = true; + try { + const info = await autoUpdater.checkForUpdates(); + return info; + } finally { + checkingUpdate = false; } - return; }; export const downloadUpdate = async () => { - if (disabled) { + if (disabled || downloading) { return; } downloading = true; + updaterSubjects.downloadProgress.next(0); autoUpdater.downloadUpdate().catch(e => { downloading = false; logger.error('Failed to download update', e); @@ -96,19 +106,16 @@ export const registerUpdater = async () => { autoUpdater.setFeedURL(feedUrl); - // register events for checkForUpdatesAndNotify + // register events for checkForUpdates autoUpdater.on('checking-for-update', () => { logger.info('Checking for update'); }); autoUpdater.on('update-available', info => { logger.info('Update available', info); - if (config.autoDownloadUpdate && allowAutoUpdate && !downloading) { - downloading = true; - autoUpdater?.downloadUpdate().catch(e => { - downloading = false; - logger.error('Failed to download update', e); + if (config.autoDownloadUpdate && allowAutoUpdate) { + downloadUpdate().catch(err => { + console.error(err); }); - logger.info('Update available, downloading...', info); } updaterSubjects.updateAvailable.next({ version: info.version, @@ -137,9 +144,20 @@ export const registerUpdater = async () => { }); autoUpdater.forceDevUpdateConfig = isDev; + // check update whenever the window is activated + let lastCheckTime = 0; app.on('activate', () => { - checkForUpdates(false).catch(err => { - console.error(err); + (async () => { + if ( + configured && + config.autoCheckUpdate && + lastCheckTime + 1000 * 1800 < Date.now() + ) { + lastCheckTime = Date.now(); + await checkForUpdates(); + } + })().catch(err => { + logger.error('Error checking for updates', err); }); }); }; diff --git a/packages/frontend/electron/src/main/updater/index.ts b/packages/frontend/electron/src/main/updater/index.ts index 19d24aaaf3..0265db1e71 100644 --- a/packages/frontend/electron/src/main/updater/index.ts +++ b/packages/frontend/electron/src/main/updater/index.ts @@ -29,8 +29,8 @@ export const updaterHandlers = { ): Promise => { return setConfig(newConfig); }, - checkForUpdatesAndNotify: async () => { - const res = await checkForUpdates(true); + checkForUpdates: async () => { + const res = await checkForUpdates(); if (res) { const { updateInfo } = res; return { diff --git a/packages/frontend/graphql/src/graphql/create-workspace.gql b/packages/frontend/graphql/src/graphql/create-workspace.gql index 315768c16f..2b849ad97d 100644 --- a/packages/frontend/graphql/src/graphql/create-workspace.gql +++ b/packages/frontend/graphql/src/graphql/create-workspace.gql @@ -1,5 +1,5 @@ -mutation createWorkspace($init: Upload!) { - createWorkspace(init: $init) { +mutation createWorkspace { + createWorkspace { id public createdAt diff --git a/packages/frontend/graphql/src/graphql/index.ts b/packages/frontend/graphql/src/graphql/index.ts index 9f543e0569..bc2d22c79b 100644 --- a/packages/frontend/graphql/src/graphql/index.ts +++ b/packages/frontend/graphql/src/graphql/index.ts @@ -153,10 +153,10 @@ export const createWorkspaceMutation = { id: 'createWorkspaceMutation' as const, operationName: 'createWorkspace', definitionName: 'createWorkspace', - containsFile: true, + containsFile: false, query: ` -mutation createWorkspace($init: Upload!) { - createWorkspace(init: $init) { +mutation createWorkspace { + createWorkspace { id public createdAt diff --git a/packages/frontend/graphql/src/schema.ts b/packages/frontend/graphql/src/schema.ts index b0243ca909..7010647b6c 100644 --- a/packages/frontend/graphql/src/schema.ts +++ b/packages/frontend/graphql/src/schema.ts @@ -199,9 +199,7 @@ export type CreateCustomerPortalMutation = { createCustomerPortal: string; }; -export type CreateWorkspaceMutationVariables = Exact<{ - init: Scalars['Upload']['input']; -}>; +export type CreateWorkspaceMutationVariables = Exact<{ [key: string]: never }>; export type CreateWorkspaceMutation = { __typename?: 'Mutation'; diff --git a/packages/frontend/hooks/package.json b/packages/frontend/hooks/package.json index c38eda2dd3..5c3b674149 100644 --- a/packages/frontend/hooks/package.json +++ b/packages/frontend/hooks/package.json @@ -9,8 +9,9 @@ "foxact": "^0.2.20", "image-blob-reduce": "^4.1.0", "jotai": "^2.5.1", + "jotai-effect": "^0.2.3", "lodash.debounce": "^4.0.8", - "p-queue": "^7.4.1", + "p-queue": "^8.0.0", "react": "18.2.0", "rxjs": "^7.8.1", "swr": "2.2.4", @@ -19,13 +20,14 @@ "devDependencies": { "@affine/debug": "workspace:*", "@affine/env": "workspace:*", - "@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/lit": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/block-std": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/blocks": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/global": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/lit": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/presets": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/store": "0.11.0-nightly-202312070955-2b5bb47", "@testing-library/react": "^14.0.0", + "@toeverything/infra": "workspace:*", "@types/image-blob-reduce": "^4.1.3", "@types/lodash.debounce": "^4.0.7", "fake-indexeddb": "^5.0.0", @@ -35,8 +37,8 @@ "peerDependencies": { "@blocksuite/block-std": "*", "@blocksuite/blocks": "*", - "@blocksuite/editor": "*", "@blocksuite/global": "*", + "@blocksuite/presets": "*", "@blocksuite/store": "*", "y-provider": "workspace:*" }, @@ -50,10 +52,10 @@ "@blocksuite/blocks": { "optional": true }, - "@blocksuite/editor": { + "@blocksuite/global": { "optional": true }, - "@blocksuite/global": { + "@blocksuite/presets": { "optional": true }, "@blocksuite/store": { diff --git a/packages/frontend/hooks/src/use-app-updater.ts b/packages/frontend/hooks/src/use-app-updater.ts index adf0c8e7f0..d4e28c1dc6 100644 --- a/packages/frontend/hooks/src/use-app-updater.ts +++ b/packages/frontend/hooks/src/use-app-updater.ts @@ -1,10 +1,13 @@ import { isBrowser } from '@affine/env/constant'; +import { appSettingAtom } from '@toeverything/infra/atom'; import type { UpdateMeta } from '@toeverything/infra/type'; -import { atom, useAtomValue, useSetAtom } from 'jotai'; +import { atom, useAtom, useAtomValue } from 'jotai'; import { atomWithObservable, atomWithStorage } from 'jotai/utils'; import { useCallback, useState } from 'react'; import { Observable } from 'rxjs'; +import { useAsyncCallback } from './affine-async-hooks'; + function rpcToObservable< T, H extends () => Promise, @@ -41,25 +44,21 @@ function rpcToObservable< }); } +// download complete, ready to install export const updateReadyAtom = atomWithObservable(() => { return rpcToObservable(null as UpdateMeta | null, { event: window.events?.updater.onUpdateReady, }); }); -export const updateAvailableStateAtom = atom(null); - -export const updateAvailableAtom = atomWithObservable(get => { - return rpcToObservable(get(updateAvailableStateAtom), { +// update available, but not downloaded yet +export const updateAvailableAtom = atomWithObservable(() => { + return rpcToObservable(null as UpdateMeta | null, { event: window.events?.updater.onUpdateAvailable, - onSubscribe: () => { - window.apis?.updater.checkForUpdatesAndNotify().catch(err => { - console.error(err); - }); - }, }); }); +// downloading new update export const downloadProgressAtom = atomWithObservable(() => { return rpcToObservable(null as number | null, { event: window.events?.updater.onDownloadProgress, @@ -71,6 +70,8 @@ export const changelogCheckedAtom = atomWithStorage>( {} ); +export const checkingForUpdatesAtom = atom(false); + export const currentVersionAtom = atom(async () => { if (!isBrowser) { return null; @@ -79,29 +80,43 @@ export const currentVersionAtom = atom(async () => { return currentVersion; }); -export const currentChangelogUnreadAtom = atom(async get => { - if (!isBrowser) { +const currentChangelogUnreadAtom = atom( + async get => { + if (!isBrowser) { + return false; + } + const mapping = get(changelogCheckedAtom); + const currentVersion = await get(currentVersionAtom); + if (currentVersion) { + return !mapping[currentVersion]; + } return false; + }, + async (get, set, v: boolean) => { + const currentVersion = await get(currentVersionAtom); + if (currentVersion) { + set(changelogCheckedAtom, mapping => { + return { + ...mapping, + [currentVersion]: v, + }; + }); + } } - const mapping = get(changelogCheckedAtom); - const currentVersion = await get(currentVersionAtom); - if (currentVersion) { - return !mapping[currentVersion]; - } - return false; -}); - -export const isCheckingForUpdatesAtom = atom(false); -export const isAutoDownloadUpdateAtom = atom(true); -export const isAutoCheckUpdateAtom = atom(true); +); export const useAppUpdater = () => { const [appQuitting, setAppQuitting] = useState(false); const updateReady = useAtomValue(updateReadyAtom); - const setUpdateAvailableState = useSetAtom(updateAvailableStateAtom); - const setIsCheckingForUpdates = useSetAtom(isCheckingForUpdatesAtom); - const setIsAutoCheckUpdate = useSetAtom(isAutoCheckUpdateAtom); - const setIsAutoDownloadUpdate = useSetAtom(isAutoDownloadUpdateAtom); + const [setting, setSetting] = useAtom(appSettingAtom); + const downloadProgress = useAtomValue(downloadProgressAtom); + const [changelogUnread, setChangelogUnread] = useAtom( + currentChangelogUnreadAtom + ); + + const [checkingForUpdates, setCheckingForUpdates] = useAtom( + checkingForUpdatesAtom + ); const quitAndInstall = useCallback(() => { if (updateReady) { @@ -114,73 +129,64 @@ export const useAppUpdater = () => { }, [updateReady]); const checkForUpdates = useCallback(async () => { - setIsCheckingForUpdates(true); + if (checkingForUpdates) { + return; + } + setCheckingForUpdates(true); try { - const updateInfo = await window.apis?.updater.checkForUpdatesAndNotify(); - setIsCheckingForUpdates(false); - if (updateInfo) { - const updateMeta: UpdateMeta = { - version: updateInfo.version, - allowAutoUpdate: false, - }; - setUpdateAvailableState(updateMeta); - return updateInfo.version; - } - return false; + const updateInfo = await window.apis?.updater.checkForUpdates(); + return updateInfo?.version ?? false; } catch (err) { - setIsCheckingForUpdates(false); console.error('Error checking for updates:', err); return null; + } finally { + setCheckingForUpdates(false); } - }, [setIsCheckingForUpdates, setUpdateAvailableState]); + }, [checkingForUpdates, setCheckingForUpdates]); const downloadUpdate = useCallback(() => { - window.apis?.updater - .downloadUpdate() - .then(() => {}) - .catch(err => { - console.error('Error downloading update:', err); - }); + window.apis?.updater.downloadUpdate().catch(err => { + console.error('Error downloading update:', err); + }); }, []); const toggleAutoDownload = useCallback( (enable: boolean) => { - window.apis?.updater - .setConfig({ - autoDownloadUpdate: enable, - }) - .then(() => { - setIsAutoDownloadUpdate(enable); - }) - .catch(err => { - console.error('Error setting auto download:', err); - }); + setSetting({ + autoDownloadUpdate: enable, + }); }, - [setIsAutoDownloadUpdate] + [setSetting] ); const toggleAutoCheck = useCallback( (enable: boolean) => { - window.apis?.updater - .setConfig({ - autoCheckUpdate: enable, - }) - .then(() => { - setIsAutoCheckUpdate(enable); - }) - .catch(err => { - console.error('Error setting auto check:', err); - }); + setSetting({ + autoCheckUpdate: enable, + }); }, - [setIsAutoCheckUpdate] + [setSetting] ); + const readChangelog = useAsyncCallback(async () => { + await setChangelogUnread(true); + }, [setChangelogUnread]); + return { quitAndInstall, - appQuitting, checkForUpdates, downloadUpdate, toggleAutoDownload, toggleAutoCheck, + appQuitting, + checkingForUpdates, + autoCheck: setting.autoCheckUpdate, + autoDownload: setting.autoDownloadUpdate, + changelogUnread, + readChangelog, + updateReady, + updateAvailable: useAtomValue(updateAvailableAtom), + downloadProgress, + currentVersion: useAtomValue(currentVersionAtom), }; }; diff --git a/packages/frontend/hooks/src/use-block-suite-page-preview.ts b/packages/frontend/hooks/src/use-block-suite-page-preview.ts index 869e56b555..dc4837f845 100644 --- a/packages/frontend/hooks/src/use-block-suite-page-preview.ts +++ b/packages/frontend/hooks/src/use-block-suite-page-preview.ts @@ -60,7 +60,7 @@ export function useBlockSuitePagePreview(page: Page | null): Atom { page.slots.ready.on(() => { set(getPagePreviewText(page)); }), - page.slots.yUpdated.on(() => { + page.slots.blockUpdated.on(() => { set(getPagePreviewText(page)); }), ]; diff --git a/packages/frontend/hooks/src/use-block-suite-page-references.ts b/packages/frontend/hooks/src/use-block-suite-page-references.ts index 6b78117748..1c81d420db 100644 --- a/packages/frontend/hooks/src/use-block-suite-page-references.ts +++ b/packages/frontend/hooks/src/use-block-suite-page-references.ts @@ -5,12 +5,9 @@ import { useBlockSuiteWorkspacePage } from './use-block-suite-workspace-page'; const weakMap = new WeakMap>(); function getPageReferences(page: Page): string[] { - // todo: is there a way to use page indexer to get all references? - return ['affine:paragraph', 'affine:list', 'affine:database'] - .flatMap(f => page.getBlockByFlavour(f)) - .flatMap(b => b.text?.toDelta()) - .map(v => v?.attributes?.reference?.pageId) - .filter(Boolean); + return Object.values( + page.workspace.indexer.backlink.linkIndexMap[page.id] ?? {} + ).flatMap(linkNodes => linkNodes.map(linkNode => linkNode.pageId)); } const getPageReferencesAtom = (page: Page | null) => { @@ -25,7 +22,7 @@ const getPageReferencesAtom = (page: Page | null) => { page.slots.ready.on(() => { set(getPageReferences(page)); }), - page.slots.yUpdated.on(() => { + page.workspace.indexer.backlink.slots.indexUpdated.on(() => { set(getPageReferences(page)); }), ]; diff --git a/packages/frontend/hooks/src/use-is-tiny-screen.ts b/packages/frontend/hooks/src/use-is-tiny-screen.ts index 506416a983..e87971da79 100644 --- a/packages/frontend/hooks/src/use-is-tiny-screen.ts +++ b/packages/frontend/hooks/src/use-is-tiny-screen.ts @@ -4,24 +4,22 @@ import debounce from 'lodash.debounce'; import { type RefObject, useEffect, useState } from 'react'; export function useIsTinyScreen({ - mainContainer, + container, leftStatic, leftSlot, centerDom, - rightStatic, rightSlot, }: { - mainContainer: HTMLElement | null; + container: HTMLElement | null; leftStatic: RefObject; leftSlot: RefObject[]; centerDom: RefObject; - rightStatic: RefObject; rightSlot: RefObject[]; }) { const [isTinyScreen, setIsTinyScreen] = useState(false); useEffect(() => { - if (!mainContainer) { + if (!container) { return; } const handleResize = debounce(() => { @@ -33,8 +31,6 @@ export function useIsTinyScreen({ return accWidth + (dom.current?.clientWidth || 0); }, 0); - const rightStaticWidth = rightStatic.current?.clientWidth || 0; - const rightSlotWidth = rightSlot.reduce((accWidth, dom) => { return accWidth + (dom.current?.clientWidth || 0); }, 0); @@ -46,14 +42,13 @@ export function useIsTinyScreen({ return; } - const containerRect = mainContainer.getBoundingClientRect(); + const containerRect = container.getBoundingClientRect(); const centerRect = centerDom.current.getBoundingClientRect(); if ( leftStaticWidth + leftSlotWidth + containerRect.left >= centerRect.left || - containerRect.right - centerRect.right <= - rightSlotWidth + rightStaticWidth + containerRect.right - centerRect.right <= rightSlotWidth ) { setIsTinyScreen(true); } else { @@ -67,20 +62,12 @@ export function useIsTinyScreen({ handleResize(); }); - resizeObserver.observe(mainContainer); + resizeObserver.observe(container); return () => { - resizeObserver.unobserve(mainContainer); + resizeObserver.unobserve(container); }; - }, [ - centerDom, - isTinyScreen, - leftSlot, - leftStatic, - mainContainer, - rightSlot, - rightStatic, - ]); + }, [centerDom, isTinyScreen, leftSlot, leftStatic, container, rightSlot]); return isTinyScreen; } diff --git a/packages/frontend/hooks/tsconfig.json b/packages/frontend/hooks/tsconfig.json index f0040e1558..9b142bb210 100644 --- a/packages/frontend/hooks/tsconfig.json +++ b/packages/frontend/hooks/tsconfig.json @@ -9,6 +9,7 @@ "references": [ { "path": "../../common/env" }, { "path": "../../common/y-indexeddb" }, - { "path": "../../common/debug" } + { "path": "../../common/debug" }, + { "path": "../../common/infra" } ] } diff --git a/packages/frontend/i18n/package.json b/packages/frontend/i18n/package.json index f643be3134..acc174f2c2 100644 --- a/packages/frontend/i18n/package.json +++ b/packages/frontend/i18n/package.json @@ -17,9 +17,9 @@ "scripts": { "build": "node build.mjs", "dev": "node dev.mjs", - "sync-languages": "ts-node -P ./tsconfig.node.json src/scripts/sync.ts", + "sync-languages": "node --loader ts-node/esm/transpile-only src/scripts/sync.ts", "sync-languages:check": "yarn run sync-languages --check", - "download-resources": "ts-node -P ./tsconfig.node.json src/scripts/download.ts" + "download-resources": "node --loader ts-node/esm/transpile-only src/scripts/download.ts" }, "keywords": [], "repository": { diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index 22f5d6072f..8d2e58c3fc 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -373,6 +373,7 @@ "com.affine.appUpdater.downloading": "Downloading", "com.affine.appUpdater.installUpdate": "Restart to install update", "com.affine.appUpdater.openDownloadPage": "Open download page", + "com.affine.appUpdater.downloadUpdate": "Download update", "com.affine.appUpdater.updateAvailable": "Update available", "com.affine.appUpdater.whatsNew": "Discover what's new!", "com.affine.appearanceSettings.clientBorder.description": "Customise the appearance of the client.", @@ -480,6 +481,7 @@ "com.affine.auth.toast.title.signed-in": "Signed in", "com.affine.backButton": "Back", "com.affine.banner.content": "This demo is limited. <1>Download the AFFiNE Client for the latest features and Performance.", + "com.affine.banner.local-warning": "Your local data is stored in the browser and may be lost. Don't risk it - enable cloud now!", "com.affine.brand.affineCloud": "AFFiNE Cloud", "com.affine.cloudTempDisable.description": "We are upgrading the AFFiNE Cloud service and it is temporarily unavailable on the client side. If you wish to stay updated on the progress and be notified on availability, you can fill out the <1>AFFiNE Cloud Signup.", "com.affine.cloudTempDisable.title": "AFFiNE Cloud is upgrading now.", @@ -509,6 +511,7 @@ "com.affine.cmdk.affine.editor.edgeless.presentation-start": "Start Presentation", "com.affine.cmdk.affine.editor.remove-from-favourites": "Remove from Favourites", "com.affine.cmdk.affine.editor.restore-from-trash": "Restore from Trash", + "com.affine.cmdk.affine.editor.trash-footer-hint": "This page has been moved to the trash, you can either restore or permanently delete it.", "com.affine.cmdk.affine.font-style.to": "Change Font Style to", "com.affine.cmdk.affine.full-width-layout.to": "Change Full Width Layout to", "com.affine.cmdk.affine.getting-started": "Getting Started", @@ -606,6 +609,7 @@ "com.affine.filter": "Filter", "com.affine.filter.after": "after", "com.affine.filter.before": "before", + "com.affine.filter.last": "last", "com.affine.filter.contains all": "contains all", "com.affine.filter.contains one of": "contains one of", "com.affine.filter.does not contains all": "does not contains all", @@ -1000,5 +1004,6 @@ "com.affine.history.empty-prompt.title": "Empty", "com.affine.history.empty-prompt.description": "This document is such a spring chicken, it hasn't sprouted a single historical sprig yet!", "com.affine.history.confirm-restore-modal.restore": "Restore", - "com.affine.history.confirm-restore-modal.hint": "You are about to restore the current version of the page to the latest version available. This action will overwrite any changes made prior to the latest version." + "com.affine.history.confirm-restore-modal.hint": "You are about to restore the current version of the page to the latest version available. This action will overwrite any changes made prior to the latest version.", + "com.affine.share-page.header.present": "Present" } diff --git a/packages/frontend/i18n/src/resources/zh-Hans.json b/packages/frontend/i18n/src/resources/zh-Hans.json index 47833ee09c..54a6b9c7a7 100644 --- a/packages/frontend/i18n/src/resources/zh-Hans.json +++ b/packages/frontend/i18n/src/resources/zh-Hans.json @@ -593,6 +593,7 @@ "com.affine.filter": "᭛选", "com.affine.filter.after": "晚äēŽ", "com.affine.filter.before": "æ—ŠäēŽ", + "com.affine.filter.last": "最čŋ‘", "com.affine.filter.contains all": "包åĢäģĨ下所有", "com.affine.filter.contains one of": "包åĢäģĨ下之一", "com.affine.filter.does not contains all": "不包åĢäģĨ下所有", diff --git a/packages/frontend/native/index.d.ts b/packages/frontend/native/index.d.ts index 47c9a16d5a..3446100ebc 100644 --- a/packages/frontend/native/index.d.ts +++ b/packages/frontend/native/index.d.ts @@ -1,32 +1,6 @@ -/* tslint:disable */ +/* auto-generated by NAPI-RS */ /* eslint-disable */ -/* auto-generated by NAPI-RS */ - -export interface BlobRow { - key: string - data: Buffer - timestamp: Date -} -export interface UpdateRow { - id: number - timestamp: Date - data: Buffer - docId?: string -} -export interface InsertRow { - docId?: string - data: Uint8Array -} -export enum ValidationResult { - MissingTables = 0, - MissingDocIdColumn = 1, - MissingVersionColumn = 2, - GeneralError = 3, - Valid = 4 -} -export function verifyChallengeResponse(response: string, bits: number, resource: string): Promise -export function mintChallengeResponse(resource: string, bits?: number | undefined | null): Promise export class SqliteConnection { constructor(path: string) connect(): Promise @@ -47,3 +21,34 @@ export class SqliteConnection { static validate(path: string): Promise migrateAddDocId(): Promise } + +export interface BlobRow { + key: string + data: Buffer + timestamp: Date +} + +export interface InsertRow { + docId?: string + data: Uint8Array +} + +export function mintChallengeResponse(resource: string, bits?: number | undefined | null): Promise + +export interface UpdateRow { + id: number + timestamp: Date + data: Buffer + docId?: string +} + +export enum ValidationResult { + MissingTables = 0, + MissingDocIdColumn = 1, + MissingVersionColumn = 2, + GeneralError = 3, + Valid = 4 +} + +export function verifyChallengeResponse(response: string, bits: number, resource: string): Promise + diff --git a/packages/frontend/native/index.js b/packages/frontend/native/index.js index 5773485893..0f5779e9f2 100644 --- a/packages/frontend/native/index.js +++ b/packages/frontend/native/index.js @@ -1,7 +1,5 @@ -/* tslint:disable */ +// prettier-ignore /* eslint-disable */ -/* prettier-ignore */ - /* auto-generated by NAPI-RS */ const { existsSync, readFileSync } = require('fs') @@ -13,18 +11,52 @@ let nativeBinding = null let localFileExisted = false let loadError = null -function isMusl() { - // For Node 10 - if (!process.report || typeof process.report.getReport !== 'function') { - try { - const lddPath = require('child_process').execSync('which ldd').toString().trim() - return readFileSync(lddPath, 'utf8').includes('musl') - } catch (e) { +const isMusl = () => { + let musl = false + if (process.platform === 'linux') { + musl = isMuslFromFilesystem() + if (musl === null) { + musl = isMuslFromReport() + } + if (musl === null) { + musl = isMuslFromChildProcess() + } + } + return musl +} + +const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-') + +const isMuslFromFilesystem = () => { + try { + return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl') + } catch { + return null + } +} + +const isMuslFromReport = () => { + const report = typeof process.report.getReport === 'function' ? process.report.getReport() : null + if (!report) { + return null + } + if (report.header && report.header.glibcVersionRuntime) { + return false + } + if (Array.isArray(report.sharedObjects)) { + if (report.sharedObjects.some(isFileMusl)) { return true } - } else { - const { glibcVersionRuntime } = process.report.getReport().header - return !glibcVersionRuntime + } + return false +} + +const isMuslFromChildProcess = () => { + try { + return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl') + } catch (e) { + // If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false + return false } } @@ -252,9 +284,7 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { SqliteConnection, ValidationResult, verifyChallengeResponse, mintChallengeResponse } = nativeBinding - -module.exports.SqliteConnection = SqliteConnection -module.exports.ValidationResult = ValidationResult -module.exports.verifyChallengeResponse = verifyChallengeResponse -module.exports.mintChallengeResponse = mintChallengeResponse +module.exports.SqliteConnection = nativeBinding.SqliteConnection +module.exports.mintChallengeResponse = nativeBinding.mintChallengeResponse +module.exports.ValidationResult = nativeBinding.ValidationResult +module.exports.verifyChallengeResponse = nativeBinding.verifyChallengeResponse diff --git a/packages/frontend/native/package.json b/packages/frontend/native/package.json index 090ef3b741..831dedce3d 100644 --- a/packages/frontend/native/package.json +++ b/packages/frontend/native/package.json @@ -4,7 +4,7 @@ "main": "index.js", "types": "index.d.ts", "napi": { - "name": "affine", + "binaryName": "affine", "triples": { "additional": [ "aarch64-apple-darwin", @@ -35,10 +35,10 @@ } }, "devDependencies": { - "@napi-rs/cli": "^2.16.5", + "@napi-rs/cli": "3.0.0-alpha.15", "@types/node": "^20.9.3", "@types/uuid": "^9.0.7", - "ava": "^5.3.1", + "ava": "^6.0.0", "cross-env": "^7.0.3", "nx": "^17.1.3", "nx-cloud": "^16.5.2", @@ -53,7 +53,7 @@ "scripts": { "artifacts": "napi artifacts", "build": "napi build --platform --release --no-const-enum", - "build:debug": "napi build --platform --no-const-enum", + "build:debug": "napi build --platform", "universal": "napi universal", "test": "ava", "version": "napi version" diff --git a/packages/frontend/workspace/package.json b/packages/frontend/workspace/package.json index 92db92e83f..b36fb053cd 100644 --- a/packages/frontend/workspace/package.json +++ b/packages/frontend/workspace/package.json @@ -22,7 +22,7 @@ "@toeverything/hooks": "workspace:*", "@toeverything/y-indexeddb": "workspace:*", "async-call-rpc": "^6.3.1", - "idb": "^7.1.1", + "idb": "^8.0.0", "idb-keyval": "^6.2.1", "is-svg": "^5.0.0", "jotai": "^2.5.1", diff --git a/packages/frontend/workspace/src/affine/crud.ts b/packages/frontend/workspace/src/affine/crud.ts index e4a656c409..434cd729b2 100644 --- a/packages/frontend/workspace/src/affine/crud.ts +++ b/packages/frontend/workspace/src/affine/crud.ts @@ -40,13 +40,8 @@ export const CRUD: WorkspaceCRUD = { } const { createWorkspace } = await fetcher({ query: createWorkspaceMutation, - variables: { - init: new File( - [Y.encodeStateAsUpdate(upstreamWorkspace.doc)], - 'initBinary.yDoc' - ), - }, }); + createdWorkspaces.push(upstreamWorkspace.id); const newBlockSuiteWorkspace = getOrCreateWorkspace( createWorkspace.id, diff --git a/packages/frontend/workspace/src/atom.ts b/packages/frontend/workspace/src/atom.ts index 4a63b6e2d9..881726723d 100644 --- a/packages/frontend/workspace/src/atom.ts +++ b/packages/frontend/workspace/src/atom.ts @@ -1,7 +1,6 @@ import { DebugLogger } from '@affine/debug'; import type { WorkspaceAdapter } from '@affine/env/workspace'; import { WorkspaceFlavour } from '@affine/env/workspace'; -import type { BlockHub } from '@blocksuite/blocks'; import { assertEquals, assertExists } from '@blocksuite/global/utils'; import { currentPageIdAtom, @@ -259,7 +258,4 @@ export const refreshRootMetadataAtom = atom(null, (get, set) => { set(rootWorkspacesMetadataPrimitiveAtom, fetchMetadata(get)); }); -// blocksuite atoms, -// each app should have only one block-hub in the same time -export const rootBlockHubAtom = atom | null>(null); //#endregion diff --git a/packages/frontend/workspace/src/blob/storage/affine-cloud.ts b/packages/frontend/workspace/src/blob/storage/affine-cloud.ts index 91ffa92a4f..a1c23f54bd 100644 --- a/packages/frontend/workspace/src/blob/storage/affine-cloud.ts +++ b/packages/frontend/workspace/src/blob/storage/affine-cloud.ts @@ -8,6 +8,7 @@ import { import { fetcher } from '@affine/workspace/affine/gql'; import type { BlobStorage } from '../engine'; +import { bufferToBlob } from '../util'; export const createAffineCloudBlobStorage = ( workspaceId: string @@ -26,7 +27,7 @@ export const createAffineCloudBlobStorage = ( // status not in the range 200-299 return undefined; } - return await res.blob(); + return bufferToBlob(await res.arrayBuffer()); } ); }, diff --git a/packages/frontend/workspace/src/blob/storage/indexeddb.ts b/packages/frontend/workspace/src/blob/storage/indexeddb.ts index 66973bd28a..ce5fb35bf6 100644 --- a/packages/frontend/workspace/src/blob/storage/indexeddb.ts +++ b/packages/frontend/workspace/src/blob/storage/indexeddb.ts @@ -1,6 +1,7 @@ import { createStore, del, get, keys, set } from 'idb-keyval'; import type { BlobStorage } from '../engine'; +import { bufferToBlob } from '../util'; export const createIndexeddbBlobStorage = ( workspaceId: string @@ -13,7 +14,7 @@ export const createIndexeddbBlobStorage = ( get: async (key: string) => { const res = await get(key, db); if (res) { - return new Blob([res], { type: await get(key, mimeTypeDb) }); + return bufferToBlob(res); } return undefined; }, diff --git a/packages/frontend/workspace/src/manager/index.ts b/packages/frontend/workspace/src/manager/index.ts index 0c0daec087..bd1266e439 100644 --- a/packages/frontend/workspace/src/manager/index.ts +++ b/packages/frontend/workspace/src/manager/index.ts @@ -1,4 +1,3 @@ -import { isBrowser } from '@affine/env/constant'; import type { BlockSuiteFeatureFlags } from '@affine/env/global'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { createAffinePublicProviders } from '@affine/workspace/providers'; @@ -25,10 +24,6 @@ function setEditorFlags(workspace: Workspace) { value ); }); - workspace.awarenessStore.setFlag( - 'enable_bookmark_operation', - environment.isDesktop - ); } type UpdateCallback = ( @@ -116,7 +111,6 @@ export function getOrCreateWorkspace( const workspace = new Workspace({ id, - isSSR: !isBrowser, providerCreators: typeof window === 'undefined' ? [] : providerCreators, blobStorages: [ () => ({ diff --git a/packages/frontend/workspace/src/providers/sync/__tests__/engine.spec.ts b/packages/frontend/workspace/src/providers/sync/__tests__/engine.spec.ts index 6e9028caa5..3436d8e896 100644 --- a/packages/frontend/workspace/src/providers/sync/__tests__/engine.spec.ts +++ b/packages/frontend/workspace/src/providers/sync/__tests__/engine.spec.ts @@ -25,7 +25,7 @@ describe('SyncEngine', () => { { const workspace = new Workspace({ id: 'test', - isSSR: true, + schema, }); @@ -57,7 +57,7 @@ describe('SyncEngine', () => { { const workspace = new Workspace({ id: 'test', - isSSR: true, + schema, }); const syncEngine = new SyncEngine( @@ -76,7 +76,7 @@ describe('SyncEngine', () => { { const workspace = new Workspace({ id: 'test', - isSSR: true, + schema, }); const syncEngine = new SyncEngine( @@ -95,7 +95,7 @@ describe('SyncEngine', () => { { const workspace = new Workspace({ id: 'test', - isSSR: true, + schema, }); const syncEngine = new SyncEngine( diff --git a/packages/frontend/workspace/src/providers/sync/__tests__/peer.spec.ts b/packages/frontend/workspace/src/providers/sync/__tests__/peer.spec.ts index db605a89a4..7711657c23 100644 --- a/packages/frontend/workspace/src/providers/sync/__tests__/peer.spec.ts +++ b/packages/frontend/workspace/src/providers/sync/__tests__/peer.spec.ts @@ -21,7 +21,7 @@ describe('SyncPeer', () => { { const workspace = new Workspace({ id: 'test', - isSSR: true, + schema, }); @@ -49,7 +49,7 @@ describe('SyncPeer', () => { { const workspace = new Workspace({ id: 'test', - isSSR: true, + schema, }); const syncPeer = new SyncPeer( @@ -67,7 +67,7 @@ describe('SyncPeer', () => { test('status', async () => { const workspace = new Workspace({ id: 'test - status', - isSSR: true, + schema, }); diff --git a/packages/frontend/workspace/src/providers/sync/engine.ts b/packages/frontend/workspace/src/providers/sync/engine.ts index fca3e2c74a..6acfee83f0 100644 --- a/packages/frontend/workspace/src/providers/sync/engine.ts +++ b/packages/frontend/workspace/src/providers/sync/engine.ts @@ -3,6 +3,7 @@ import { Slot } from '@blocksuite/global/utils'; import type { Doc } from 'yjs'; import type { Storage } from '../storage'; +import { SharedPriorityTarget } from '../utils/async-queue'; import { MANUALLY_STOP, SyncEngineStep } from './consts'; import { SyncPeer, type SyncPeerStatus, SyncPeerStep } from './peer'; @@ -56,6 +57,8 @@ export class SyncEngine { this.onStatusChange.emit(s); } + priorityTarget = new SharedPriorityTarget(); + get status() { return this._status; } @@ -110,7 +113,11 @@ export class SyncEngine { const cleanUp: (() => void)[] = []; try { // Step 1: start local sync peer - state.localPeer = new SyncPeer(this.rootDoc, this.local); + state.localPeer = new SyncPeer( + this.rootDoc, + this.local, + this.priorityTarget + ); cleanUp.push( state.localPeer.onStatusChange.on(() => { @@ -126,7 +133,7 @@ export class SyncEngine { // Step 3: start remote sync peer state.remotePeers = this.remotes.map(remote => { - const peer = new SyncPeer(this.rootDoc, remote); + const peer = new SyncPeer(this.rootDoc, remote, this.priorityTarget); cleanUp.push( peer.onStatusChange.on(() => { if (!signal.aborted) @@ -237,4 +244,8 @@ export class SyncEngine { ]); } } + + setPriorityRule(target: ((id: string) => boolean) | null) { + this.priorityTarget.priorityRule = target; + } } diff --git a/packages/frontend/workspace/src/providers/sync/peer.ts b/packages/frontend/workspace/src/providers/sync/peer.ts index 5597bb8469..6a1955c4d7 100644 --- a/packages/frontend/workspace/src/providers/sync/peer.ts +++ b/packages/frontend/workspace/src/providers/sync/peer.ts @@ -5,7 +5,7 @@ import type { Doc } from 'yjs'; import { applyUpdate, encodeStateAsUpdate, encodeStateVector } from 'yjs'; import { mergeUpdates, type Storage } from '../storage'; -import { AsyncQueue } from '../utils/async-queue'; +import { PriorityAsyncQueue, SharedPriorityTarget } from '../utils/async-queue'; import { throwIfAborted } from '../utils/throw-if-aborted'; import { MANUALLY_STOP } from './consts'; @@ -70,7 +70,8 @@ export class SyncPeer { constructor( private readonly rootDoc: Doc, - private readonly storage: Storage + private readonly storage: Storage, + private readonly priorityTarget = new SharedPriorityTarget() ) { this.logger.debug('peer start'); @@ -152,24 +153,24 @@ export class SyncPeer { private readonly state: { connectedDocs: Map; - pushUpdatesQueue: AsyncQueue<{ - docId: string; + pushUpdatesQueue: PriorityAsyncQueue<{ + id: string; data: Uint8Array[]; }>; pushingUpdate: boolean; - pullUpdatesQueue: AsyncQueue<{ - docId: string; + pullUpdatesQueue: PriorityAsyncQueue<{ + id: string; data: Uint8Array; }>; subdocLoading: boolean; - subdocsLoadQueue: AsyncQueue; + subdocsLoadQueue: PriorityAsyncQueue<{ id: string; doc: Doc }>; } = { connectedDocs: new Map(), - pushUpdatesQueue: new AsyncQueue(), + pushUpdatesQueue: new PriorityAsyncQueue([], this.priorityTarget), pushingUpdate: false, - pullUpdatesQueue: new AsyncQueue(), + pullUpdatesQueue: new PriorityAsyncQueue([], this.priorityTarget), subdocLoading: false, - subdocsLoadQueue: new AsyncQueue(), + subdocsLoadQueue: new PriorityAsyncQueue([], this.priorityTarget), }; initState() { @@ -211,7 +212,10 @@ export class SyncPeer { // Step 2: load subdocs this.state.subdocsLoadQueue.push( - ...Array.from(this.rootDoc.getSubdocs()) + ...Array.from(this.rootDoc.getSubdocs()).map(doc => ({ + id: doc.guid, + doc, + })) ); this.reportSyncStatus(); @@ -227,7 +231,7 @@ export class SyncPeer { ); this.state.subdocLoading = true; this.reportSyncStatus(); - await this.connectDoc(subdoc, abortInner.signal); + await this.connectDoc(subdoc.doc, abortInner.signal); this.state.subdocLoading = false; this.reportSyncStatus(); } @@ -235,7 +239,7 @@ export class SyncPeer { // pull updates (async () => { while (throwIfAborted(abortInner.signal)) { - const { docId, data } = await this.state.pullUpdatesQueue.next( + const { id, data } = await this.state.pullUpdatesQueue.next( abortInner.signal ); // don't apply empty data or Uint8Array([0, 0]) @@ -245,7 +249,7 @@ export class SyncPeer { (data.byteLength === 2 && data[0] === 0 && data[1] === 0) ) ) { - const subdoc = this.state.connectedDocs.get(docId); + const subdoc = this.state.connectedDocs.get(id); if (subdoc) { applyUpdate(subdoc, data, this.name); } @@ -256,7 +260,7 @@ export class SyncPeer { // push updates (async () => { while (throwIfAborted(abortInner.signal)) { - const { docId, data } = await this.state.pushUpdatesQueue.next( + const { id, data } = await this.state.pushUpdatesQueue.next( abortInner.signal ); this.state.pushingUpdate = true; @@ -271,7 +275,7 @@ export class SyncPeer { (merged.byteLength === 2 && merged[0] === 0 && merged[1] === 0) ) ) { - await this.storage.push(docId, merged); + await this.storage.push(id, merged); } this.state.pushingUpdate = false; @@ -299,7 +303,7 @@ export class SyncPeer { // diff root doc and in-storage, save updates to pendingUpdates this.state.pushUpdatesQueue.push({ - docId: doc.guid, + id: doc.guid, data: [encodeStateAsUpdate(doc, inStorageState)], }); @@ -327,14 +331,12 @@ export class SyncPeer { return; } - const exist = this.state.pushUpdatesQueue.find( - ({ docId }) => docId === doc.guid - ); + const exist = this.state.pushUpdatesQueue.find(({ id }) => id === doc.guid); if (exist) { exist.data.push(update); } else { this.state.pushUpdatesQueue.push({ - docId: doc.guid, + id: doc.guid, data: [update], }); } @@ -351,20 +353,20 @@ export class SyncPeer { removed: Set; }) => { for (const subdoc of added) { - this.state.subdocsLoadQueue.push(subdoc); + this.state.subdocsLoadQueue.push({ id: subdoc.guid, doc: subdoc }); } for (const subdoc of removed) { this.disconnectDoc(subdoc); - this.state.subdocsLoadQueue.remove(doc => doc === subdoc); + this.state.subdocsLoadQueue.remove(doc => doc.doc === subdoc); } this.reportSyncStatus(); }; // handle updates from storage - handleStorageUpdates = (docId: string, data: Uint8Array) => { + handleStorageUpdates = (id: string, data: Uint8Array) => { this.state.pullUpdatesQueue.push({ - docId, + id, data, }); this.reportSyncStatus(); diff --git a/packages/frontend/workspace/src/providers/utils/async-queue.ts b/packages/frontend/workspace/src/providers/utils/async-queue.ts index db29b8d43e..e7f994a39b 100644 --- a/packages/frontend/workspace/src/providers/utils/async-queue.ts +++ b/packages/frontend/workspace/src/providers/utils/async-queue.ts @@ -12,8 +12,11 @@ export class AsyncQueue { return this._queue.length; } - async next(abort?: AbortSignal): Promise { - const update = this._queue.shift(); + async next( + abort?: AbortSignal, + dequeue: (arr: T[]) => T | undefined = a => a.shift() + ): Promise { + const update = dequeue(this._queue); if (update) { return update; } else { @@ -35,7 +38,7 @@ export class AsyncQueue { }), ]); - return this.next(abort); + return this.next(abort, dequeue); } } @@ -64,3 +67,35 @@ export class AsyncQueue { this._queue = []; } } + +export class PriorityAsyncQueue< + T extends { id: string }, +> extends AsyncQueue { + constructor( + init: T[] = [], + public readonly priorityTarget: SharedPriorityTarget = new SharedPriorityTarget() + ) { + super(init); + } + + override next(abort?: AbortSignal | undefined): Promise { + return super.next(abort, arr => { + if (this.priorityTarget.priorityRule !== null) { + const index = arr.findIndex( + update => this.priorityTarget.priorityRule?.(update.id) + ); + if (index !== -1) { + return arr.splice(index, 1)[0]; + } + } + return arr.shift(); + }); + } +} + +/** + * Shared priority target can be shared by multiple queues. + */ +export class SharedPriorityTarget { + public priorityRule: ((id: string) => boolean) | null = null; +} diff --git a/packages/plugins/copilot/package.json b/packages/plugins/copilot/package.json index 71e90548bc..9f074c95c6 100644 --- a/packages/plugins/copilot/package.json +++ b/packages/plugins/copilot/package.json @@ -17,10 +17,9 @@ "@affine/component": "workspace:*", "@affine/sdk": "workspace:*", "@blocksuite/icons": "2.1.36", - "@toeverything/components": "^0.0.46", "@vanilla-extract/css": "^1.13.0", "clsx": "^2.0.0", - "idb": "^7.1.1", + "idb": "^8.0.0", "langchain": "^0.0.166", "marked": "^9.1.2", "marked-gfm-heading-id": "^3.1.0", diff --git a/packages/plugins/copilot/src/UI/debug-content.tsx b/packages/plugins/copilot/src/UI/debug-content.tsx index 1df626ca8b..9cc0fb822a 100644 --- a/packages/plugins/copilot/src/UI/debug-content.tsx +++ b/packages/plugins/copilot/src/UI/debug-content.tsx @@ -1,5 +1,5 @@ import { FlexWrapper, Input } from '@affine/component'; -import { Button } from '@toeverything/components/button'; +import { Button } from '@affine/component/ui/button'; import { useAtom } from 'jotai'; import { type ReactElement, useCallback } from 'react'; diff --git a/packages/plugins/copilot/src/UI/detail-content.tsx b/packages/plugins/copilot/src/UI/detail-content.tsx index 5a39732fab..78855ff672 100644 --- a/packages/plugins/copilot/src/UI/detail-content.tsx +++ b/packages/plugins/copilot/src/UI/detail-content.tsx @@ -1,5 +1,5 @@ +import { IconButton } from '@affine/component/ui/button'; import { SendIcon } from '@blocksuite/icons'; -import { IconButton } from '@toeverything/components/button'; import { useAtomValue, useSetAtom } from 'jotai'; import type { ReactElement } from 'react'; import { Suspense, useCallback, useState } from 'react'; diff --git a/packages/plugins/copilot/src/UI/header-item.tsx b/packages/plugins/copilot/src/UI/header-item.tsx index 8dc7ea7ac7..9f72c344fc 100644 --- a/packages/plugins/copilot/src/UI/header-item.tsx +++ b/packages/plugins/copilot/src/UI/header-item.tsx @@ -1,7 +1,7 @@ +import { IconButton } from '@affine/component/ui/button'; +import { Tooltip } from '@affine/component/ui/tooltip'; import { deleteLayoutAtom, pushLayoutAtom } from '@affine/sdk/entry'; import { AiIcon } from '@blocksuite/icons'; -import { IconButton } from '@toeverything/components/button'; -import { Tooltip } from '@toeverything/components/tooltip'; import { useSetAtom } from 'jotai'; import type { ComponentType, PropsWithChildren, ReactElement } from 'react'; import { useCallback, useState } from 'react'; diff --git a/packages/plugins/copilot/src/core/components/conversation/index.tsx b/packages/plugins/copilot/src/core/components/conversation/index.tsx index f4bbf19ca5..8c4e5f517a 100644 --- a/packages/plugins/copilot/src/core/components/conversation/index.tsx +++ b/packages/plugins/copilot/src/core/components/conversation/index.tsx @@ -1,5 +1,5 @@ +import { Button } from '@affine/component/ui/button'; import { PlusIcon, ResetIcon } from '@blocksuite/icons'; -import { Button } from '@toeverything/components/button'; import { clsx } from 'clsx'; import type { MessageType } from 'langchain/schema'; import { marked } from 'marked'; diff --git a/packages/plugins/hello-world/package.json b/packages/plugins/hello-world/package.json index 64694abf49..0cfc6591fd 100644 --- a/packages/plugins/hello-world/package.json +++ b/packages/plugins/hello-world/package.json @@ -17,8 +17,7 @@ "dependencies": { "@affine/component": "workspace:*", "@affine/sdk": "workspace:*", - "@blocksuite/icons": "2.1.36", - "@toeverything/components": "^0.0.46" + "@blocksuite/icons": "2.1.36" }, "devDependencies": { "@affine/plugin-cli": "workspace:*" diff --git a/packages/plugins/hello-world/src/app.tsx b/packages/plugins/hello-world/src/app.tsx index 8639b900a8..d79927f078 100644 --- a/packages/plugins/hello-world/src/app.tsx +++ b/packages/plugins/hello-world/src/app.tsx @@ -1,6 +1,6 @@ +import { IconButton } from '@affine/component/ui/button'; +import { Tooltip } from '@affine/component/ui/tooltip'; import { Logo1Icon } from '@blocksuite/icons'; -import { IconButton } from '@toeverything/components/button'; -import { Tooltip } from '@toeverything/components/tooltip'; import { useCallback } from 'react'; export const HeaderItem = () => { diff --git a/packages/plugins/image-preview/package.json b/packages/plugins/image-preview/package.json index 0473339a5c..5cfc52c54b 100644 --- a/packages/plugins/image-preview/package.json +++ b/packages/plugins/image-preview/package.json @@ -17,7 +17,6 @@ "@affine/component": "workspace:*", "@affine/sdk": "workspace:*", "@blocksuite/icons": "2.1.36", - "@toeverything/components": "^0.0.46", "@toeverything/theme": "^0.7.24", "clsx": "^2.0.0", "foxact": "^0.2.20", diff --git a/packages/plugins/image-preview/src/component/index.tsx b/packages/plugins/image-preview/src/component/index.tsx index 853ddd52a2..b0ff6ef467 100644 --- a/packages/plugins/image-preview/src/component/index.tsx +++ b/packages/plugins/image-preview/src/component/index.tsx @@ -1,3 +1,5 @@ +import { Button, IconButton } from '@affine/component/ui/button'; +import { Tooltip } from '@affine/component/ui/tooltip'; import type { ImageBlockModel } from '@blocksuite/blocks'; import { assertExists } from '@blocksuite/global/utils'; import { @@ -11,8 +13,6 @@ import { ViewBarIcon, } from '@blocksuite/icons'; import type { Workspace } from '@blocksuite/store'; -import { Button, IconButton } from '@toeverything/components/button'; -import { Tooltip } from '@toeverything/components/tooltip'; import clsx from 'clsx'; import { useErrorBoundary } from 'foxact/use-error-boundary'; import { useAtom } from 'jotai'; diff --git a/packages/plugins/outline/package.json b/packages/plugins/outline/package.json index 6a63b7bb25..503ebad48a 100644 --- a/packages/plugins/outline/package.json +++ b/packages/plugins/outline/package.json @@ -17,8 +17,7 @@ "dependencies": { "@affine/component": "workspace:*", "@affine/sdk": "workspace:*", - "@blocksuite/icons": "2.1.36", - "@toeverything/components": "^0.0.46" + "@blocksuite/icons": "2.1.36" }, "devDependencies": { "@affine/plugin-cli": "workspace:*", diff --git a/packages/plugins/outline/src/app.tsx b/packages/plugins/outline/src/app.tsx index caeb0eaa1a..c22b0a2c61 100644 --- a/packages/plugins/outline/src/app.tsx +++ b/packages/plugins/outline/src/app.tsx @@ -1,3 +1,5 @@ +import { IconButton } from '@affine/component/ui/button'; +import { Tooltip } from '@affine/component/ui/tooltip'; import { currentPageIdAtom, currentWorkspaceAtom, @@ -7,8 +9,6 @@ import { import { TOCNotesPanel } from '@blocksuite/blocks'; import { assertExists } from '@blocksuite/global/utils'; import { RightSidebarIcon } from '@blocksuite/icons'; -import { IconButton } from '@toeverything/components/button'; -import { Tooltip } from '@toeverything/components/tooltip'; import { useAtomValue, useSetAtom } from 'jotai'; import type { ComponentType, PropsWithChildren } from 'react'; import { useCallback, useRef, useState } from 'react'; diff --git a/scripts/bump-blocksuite.sh b/scripts/bump-blocksuite.sh index eaf6d275e3..c76ee4feae 100755 --- a/scripts/bump-blocksuite.sh +++ b/scripts/bump-blocksuite.sh @@ -1,11 +1,12 @@ #!/bin/bash -LATEST_NIGHTLY=$(npm view @blocksuite/editor@nightly version | cut -d ':' -f 2 | tr -d '[:space:]') +LATEST_NIGHTLY=$(npm view @blocksuite/presets@nightly version | cut -d ':' -f 2 | tr -d '[:space:]') echo "Bump to latest BlockSuite nightly version: $LATEST_NIGHTLY" yarn up "@blocksuite/store@${LATEST_NIGHTLY}" yarn up "@blocksuite/blocks@${LATEST_NIGHTLY}" -yarn up "@blocksuite/editor@${LATEST_NIGHTLY}" +yarn up "@blocksuite/presets@${LATEST_NIGHTLY}" yarn up "@blocksuite/global@${LATEST_NIGHTLY}" yarn up "@blocksuite/block-std@${LATEST_NIGHTLY}" +yarn up "@blocksuite/lit@${LATEST_NIGHTLY}" yarn up "@blocksuite/virgo@${LATEST_NIGHTLY}" diff --git a/scripts/set-version.sh b/scripts/set-version.sh index bf421bb821..448b638143 100755 --- a/scripts/set-version.sh +++ b/scripts/set-version.sh @@ -6,3 +6,35 @@ for DIR in $(yarn workspaces list --json | jq -r '.location'); do jq ".version = \"$1\"" "$DIR"/package.json > tmp.json && mv tmp.json "$DIR"/package.json fi done + +update_app_version_in_helm_charts() { + local file_path=$1 + local new_version=$2 + + # Check if file exists + if [ ! -f "$file_path" ]; then + echo "Error: File does not exist at $file_path." + return 1 + fi + + echo "Updating $file_path with appVersion $new_version" + + # Use sed to replace the appVersion value with the new version. + sed -i.bak -E "s/^appVersion:[[:space:]]+[\"']?.*[\"']?$/appVersion: \"$new_version\"/" "$file_path" + + # Check if sed command succeeded + if [ $? -ne 0 ]; then + echo "Error: Failed to update the appVersion." + return 1 + fi + + echo "appVersion in $file_path updated to $new_version" + + rm "$file_path".bak +} + +new_version=$1 + +update_app_version_in_helm_charts ".github/helm/affine/Chart.yaml" "$new_version" +update_app_version_in_helm_charts ".github/helm/affine/charts/graphql/Chart.yaml" "$new_version" +update_app_version_in_helm_charts ".github/helm/affine/charts/sync/Chart.yaml" "$new_version" diff --git a/scripts/setup/lottie-web.ts b/scripts/setup/lottie-web.ts index f4a5b8ebbb..4c68a3719b 100644 --- a/scripts/setup/lottie-web.ts +++ b/scripts/setup/lottie-web.ts @@ -4,7 +4,7 @@ vi.mock('lottie-web', () => ({ default: {}, })); -vi.mock('@blocksuite/editor', () => ({ +vi.mock('@blocksuite/presets', () => ({ EditorContainer: vi.fn(), })); diff --git a/tests/affine-cloud/e2e/basic.spec.ts b/tests/affine-cloud/e2e/basic.spec.ts deleted file mode 100644 index 291fa07de5..0000000000 --- a/tests/affine-cloud/e2e/basic.spec.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { readFile } from 'node:fs/promises'; -import { resolve } from 'node:path'; - -import { test } from '@affine-test/kit/playwright'; -import { - createRandomUser, - deleteUser, - enableCloudWorkspace, - getLoginCookie, - loginUser, - runPrisma, -} from '@affine-test/kit/utils/cloud'; -import { clickEdgelessModeButton } from '@affine-test/kit/utils/editor'; -import { coreUrl } from '@affine-test/kit/utils/load-page'; -import { - clickNewPageButton, - waitForEditorLoad, -} from '@affine-test/kit/utils/page-logic'; -import { clickSideBarSettingButton } from '@affine-test/kit/utils/sidebar'; -import { createLocalWorkspace } from '@affine-test/kit/utils/workspace'; -import { expect } from '@playwright/test'; - -let user: { - id: string; - name: string; - email: string; - password: string; -}; - -test.beforeEach(async () => { - user = await createRandomUser(); -}); - -test.beforeEach(async ({ page, context }) => { - await loginUser(page, user.email, { - beforeLogin: async () => { - expect(await getLoginCookie(context)).toBeUndefined(); - }, - afterLogin: async () => { - expect(await getLoginCookie(context)).toBeTruthy(); - await page.reload(); - await waitForEditorLoad(page); - expect(await getLoginCookie(context)).toBeTruthy(); - }, - }); -}); - -test.afterEach(async () => { - // if you want to keep the user in the database for debugging, - // comment this line - await deleteUser(user.email); -}); - -test.describe('basic', () => { - test('migration', async ({ page, browser }) => { - let workspaceId: string; - { - // create the old cloud workspace in another browser - const context = await browser.newContext(); - const page = await context.newPage(); - await loginUser(page, user.email); - await page.reload(); - await createLocalWorkspace( - { - name: 'test', - }, - page - ); - await enableCloudWorkspace(page); - await clickNewPageButton(page); - await waitForEditorLoad(page); - // http://localhost:8080/workspace/2bc0b6c8-f68d-4dd3-98a8-be746754f9e1/xxx - workspaceId = page.url().split('/')[4]; - await runPrisma(async client => { - const sqls = ( - await readFile( - resolve(__dirname, 'fixtures', '0.9.0-canary.9-snapshots.sql'), - 'utf-8' - ) - ) - .replaceAll('2bc0b6c8-f68d-4dd3-98a8-be746754f9e1', workspaceId) - .split('\n'); - await client.snapshot.deleteMany({ - where: { - workspaceId, - }, - }); - - for (const sql of sqls) { - await client.$executeRawUnsafe(sql); - } - }); - await page.close(); - } - await page.reload(); - await page.waitForTimeout(1000); - await page.goto(`${coreUrl}/workspace/${workspaceId}/all`); - await page.getByTestId('upgrade-workspace-button').click(); - await expect(page.getByText('Refresh Current Page')).toBeVisible({ - timeout: 60000, - }); - await page.goto( - // page 'F1SX6cgNxy' has edgeless mode - `${coreUrl}/workspace/${workspaceId}/F1SX6cgNxy` - ); - await page.waitForTimeout(5000); - await page.reload(); - await waitForEditorLoad(page); - await clickEdgelessModeButton(page); - await expect(page.locator('affine-edgeless-page')).toBeVisible({ - timeout: 1000, - }); - }); - - test('can see and change email and password in setting panel', async ({ - page, - }) => { - const newName = 'test name'; - { - await clickSideBarSettingButton(page); - const locator = page.getByTestId('user-info-card'); - expect(locator.getByText(user.email)).toBeTruthy(); - expect(locator.getByText(user.name)).toBeTruthy(); - await locator.click({ - delay: 50, - }); - const nameInput = page.getByPlaceholder('Input account name'); - await nameInput.clear(); - await nameInput.pressSequentially(newName, { - delay: 50, - }); - await page.getByTestId('save-user-name').click({ - delay: 50, - }); - } - await page.reload(); - { - await clickSideBarSettingButton(page); - const locator = page.getByTestId('user-info-card'); - expect(locator.getByText(user.email)).toBeTruthy(); - expect(locator.getByText(newName)).toBeTruthy(); - } - }); -}); diff --git a/tests/affine-cloud/e2e/collaboration.spec.ts b/tests/affine-cloud/e2e/collaboration.spec.ts index a592bc494c..349efeec2f 100644 --- a/tests/affine-cloud/e2e/collaboration.spec.ts +++ b/tests/affine-cloud/e2e/collaboration.spec.ts @@ -6,25 +6,17 @@ import { enableCloudWorkspaceFromShareButton, loginUser, } from '@affine-test/kit/utils/cloud'; -import { dropFile } from '@affine-test/kit/utils/drop-file'; import { clickEdgelessModeButton } from '@affine-test/kit/utils/editor'; import { clickNewPageButton, getBlockSuiteEditorTitle, waitForEditorLoad, } from '@affine-test/kit/utils/page-logic'; -import { - clickUserInfoCard, - openSettingModal, - openWorkspaceSettingPanel, -} from '@affine-test/kit/utils/setting'; -import { - clickSideBarAllPageButton, - clickSideBarCurrentWorkspaceBanner, - clickSideBarSettingButton, -} from '@affine-test/kit/utils/sidebar'; +import { clickUserInfoCard } from '@affine-test/kit/utils/setting'; +import { clickSideBarSettingButton } from '@affine-test/kit/utils/sidebar'; import { createLocalWorkspace } from '@affine-test/kit/utils/workspace'; import { expect } from '@playwright/test'; +import { resolve } from 'path'; let user: { id: string; @@ -41,298 +33,184 @@ test.beforeEach(async ({ page }) => { await loginUser(page, user.email); }); -test.describe('collaboration', () => { - test('can enable share page', async ({ page, browser }) => { - await page.reload(); - await waitForEditorLoad(page); - await createLocalWorkspace( - { - name: 'test', - }, - page - ); - await enableCloudWorkspaceFromShareButton(page); - const title = getBlockSuiteEditorTitle(page); - await title.pressSequentially('TEST TITLE', { - delay: 50, - }); - await page.keyboard.press('Enter', { delay: 50 }); - await page.keyboard.type('TEST CONTENT', { delay: 50 }); - await page.getByTestId('cloud-share-menu-button').click(); - await page.getByTestId('share-menu-create-link-button').click(); - await page.getByTestId('share-menu-copy-link-button').click(); - - // check share page is accessible +test('can enable share page', async ({ page, browser }) => { + await page.reload(); + await waitForEditorLoad(page); + await createLocalWorkspace( { - const context = await browser.newContext(); - const url: string = await page.evaluate(() => - navigator.clipboard.readText() - ); - const page2 = await context.newPage(); - await page2.goto(url); - await waitForEditorLoad(page2); - const title = getBlockSuiteEditorTitle(page2); - expect(await title.innerText()).toBe('TEST TITLE'); - expect(await page2.textContent('affine-paragraph')).toContain( - 'TEST CONTENT' - ); - } + name: 'test', + }, + page + ); + await enableCloudWorkspaceFromShareButton(page); + const title = getBlockSuiteEditorTitle(page); + await title.pressSequentially('TEST TITLE', { + delay: 50, }); + await page.keyboard.press('Enter', { delay: 50 }); + await page.keyboard.type('TEST CONTENT', { delay: 50 }); + await page.getByTestId('cloud-share-menu-button').click(); + await page.getByTestId('share-menu-create-link-button').click(); + await page.getByTestId('share-menu-copy-link-button').click(); - test('share page with default edgeless', async ({ page, browser }) => { - await page.reload(); - await waitForEditorLoad(page); - await createLocalWorkspace( - { - name: 'test', - }, - page + // check share page is accessible + { + const context = await browser.newContext(); + const url: string = await page.evaluate(() => + navigator.clipboard.readText() ); - await enableCloudWorkspaceFromShareButton(page); - const title = getBlockSuiteEditorTitle(page); - await title.pressSequentially('TEST TITLE', { - delay: 50, - }); - await page.keyboard.press('Enter', { delay: 50 }); - await page.keyboard.type('TEST CONTENT', { delay: 50 }); - await clickEdgelessModeButton(page); + const page2 = await context.newPage(); + await page2.goto(url); + await waitForEditorLoad(page2); + const title = getBlockSuiteEditorTitle(page2); + expect(await title.innerText()).toBe('TEST TITLE'); + expect(await page2.textContent('affine-paragraph')).toContain( + 'TEST CONTENT' + ); + } +}); + +test('share page with default edgeless', async ({ page, browser }) => { + await page.reload(); + await waitForEditorLoad(page); + await createLocalWorkspace( + { + name: 'test', + }, + page + ); + await enableCloudWorkspaceFromShareButton(page); + const title = getBlockSuiteEditorTitle(page); + await title.pressSequentially('TEST TITLE', { + delay: 50, + }); + await page.keyboard.press('Enter', { delay: 50 }); + await page.keyboard.type('TEST CONTENT', { delay: 50 }); + await clickEdgelessModeButton(page); + await expect(page.locator('affine-edgeless-page')).toBeVisible({ + timeout: 1000, + }); + await page.getByTestId('cloud-share-menu-button').click(); + await page.getByTestId('share-menu-create-link-button').click(); + await page.getByTestId('share-menu-copy-link-button').click(); + + // check share page is accessible + { + const context = await browser.newContext(); + const url: string = await page.evaluate(() => + navigator.clipboard.readText() + ); + const page2 = await context.newPage(); + await page2.goto(url); + await waitForEditorLoad(page2); await expect(page.locator('affine-edgeless-page')).toBeVisible({ timeout: 1000, }); - await page.getByTestId('cloud-share-menu-button').click(); - await page.getByTestId('share-menu-create-link-button').click(); - await page.getByTestId('share-menu-copy-link-button').click(); - - // check share page is accessible - { - const context = await browser.newContext(); - const url: string = await page.evaluate(() => - navigator.clipboard.readText() - ); - const page2 = await context.newPage(); - await page2.goto(url); - await waitForEditorLoad(page2); - await expect(page.locator('affine-edgeless-page')).toBeVisible({ - timeout: 1000, - }); - expect(await page2.textContent('affine-paragraph')).toContain( - 'TEST CONTENT' - ); - const logo = page2.getByTestId('share-page-logo'); - const editButton = page2.getByTestId('share-page-edit-button'); - await expect(editButton).not.toBeVisible(); - await expect(logo).toBeVisible(); - } - }); - - test('can collaborate with other user and name should display when editing', async ({ - page, - browser, - }) => { - await page.reload(); - await waitForEditorLoad(page); - await createLocalWorkspace( - { - name: 'test', - }, - page + expect(await page2.textContent('affine-paragraph')).toContain( + 'TEST CONTENT' ); - await enableCloudWorkspace(page); - await clickNewPageButton(page); - const currentUrl = page.url(); - // format: http://localhost:8080/workspace/${workspaceId}/xxx - const workspaceId = currentUrl.split('/')[4]; - const userB = await createRandomUser(); + const logo = page2.getByTestId('share-page-logo'); + const editButton = page2.getByTestId('share-page-edit-button'); + await expect(editButton).not.toBeVisible(); + await expect(logo).toBeVisible(); + } +}); + +test('can collaborate with other user and name should display when editing', async ({ + page, + browser, +}) => { + await page.reload(); + await waitForEditorLoad(page); + await createLocalWorkspace( + { + name: 'test', + }, + page + ); + await enableCloudWorkspace(page); + await clickNewPageButton(page); + const currentUrl = page.url(); + // format: http://localhost:8080/workspace/${workspaceId}/xxx + const workspaceId = currentUrl.split('/')[4]; + const userB = await createRandomUser(); + const context = await browser.newContext(); + const page2 = await context.newPage(); + await loginUser(page2, userB.email); + await addUserToWorkspace(workspaceId, userB.id, 1 /* READ */); + await page2.reload(); + await waitForEditorLoad(page2); + await page2.goto(currentUrl); + { + const title = getBlockSuiteEditorTitle(page); + await title.pressSequentially('TEST TITLE', { + delay: 50, + }); + } + await page2.waitForTimeout(200); + { + const title = getBlockSuiteEditorTitle(page2); + expect(await title.innerText()).toBe('TEST TITLE'); + const typingPromise = Promise.all([ + page.keyboard.press('Enter', { delay: 50 }), + page.keyboard.type('TEST CONTENT', { delay: 50 }), + ]); + // username should be visible when editing + await expect(page2.getByText(user.name)).toBeVisible(); + await typingPromise; + } + + // change username + await clickSideBarSettingButton(page); + await clickUserInfoCard(page); + const input = page.getByTestId('user-name-input'); + await input.clear(); + await input.pressSequentially('TEST USER', { + delay: 50, + }); + await page.getByTestId('save-user-name').click({ + delay: 50, + }); + await page.keyboard.press('Escape', { + delay: 50, + }); + const title = getBlockSuiteEditorTitle(page); + await title.focus(); + + { + await expect(page2.getByText('TEST USER')).toBeVisible({ + timeout: 2000, + }); + } +}); + +test('can sync collections between different browser', async ({ + page, + browser, +}) => { + await page.reload(); + await waitForEditorLoad(page); + await createLocalWorkspace( + { + name: 'test', + }, + page + ); + await enableCloudWorkspace(page); + await page.getByTestId('slider-bar-add-collection-button').click(); + const title = page.getByTestId('input-collection-title'); + await title.isVisible(); + await title.fill('test collection'); + await page.getByTestId('save-collection').click(); + + { const context = await browser.newContext(); const page2 = await context.newPage(); - await loginUser(page2, userB.email); - await addUserToWorkspace(workspaceId, userB.id, 1 /* READ */); - await page2.reload(); - await waitForEditorLoad(page2); - await page2.goto(currentUrl); - { - const title = getBlockSuiteEditorTitle(page); - await title.pressSequentially('TEST TITLE', { - delay: 50, - }); - } - await page2.waitForTimeout(200); - { - const title = getBlockSuiteEditorTitle(page2); - expect(await title.innerText()).toBe('TEST TITLE'); - const typingPromise = Promise.all([ - page.keyboard.press('Enter', { delay: 50 }), - page.keyboard.type('TEST CONTENT', { delay: 50 }), - ]); - // username should be visible when editing - await expect(page2.getByText(user.name)).toBeVisible(); - await typingPromise; - } - - // change username - await clickSideBarSettingButton(page); - await clickUserInfoCard(page); - const input = page.getByTestId('user-name-input'); - await input.clear(); - await input.pressSequentially('TEST USER', { - delay: 50, - }); - await page.getByTestId('save-user-name').click({ - delay: 50, - }); - await page.keyboard.press('Escape', { - delay: 50, - }); - const title = getBlockSuiteEditorTitle(page); - await title.focus(); - - { - await expect(page2.getByText('TEST USER')).toBeVisible({ - timeout: 2000, - }); - } - }); - - test('can sync collections between different browser', async ({ - page, - browser, - }) => { - await page.reload(); - await waitForEditorLoad(page); - await createLocalWorkspace( - { - name: 'test', - }, - page - ); - await enableCloudWorkspace(page); - await page.getByTestId('slider-bar-add-collection-button').click(); - const title = page.getByTestId('input-collection-title'); - await title.isVisible(); - await title.fill('test collection'); - await page.getByTestId('save-collection').click(); - - { - const context = await browser.newContext(); - const page2 = await context.newPage(); - await loginUser(page2, user.email); - await page2.goto(page.url()); - const collections = page2.getByTestId('collections'); - await expect(collections.getByText('test collection')).toBeVisible(); - } - }); - - test('exit successfully and re-login', async ({ page }) => { - await page.reload(); - await clickSideBarAllPageButton(page); - await page.waitForTimeout(200); - const url = page.url(); - await createLocalWorkspace( - { - name: 'test', - }, - page - ); - await enableCloudWorkspace(page); - await clickSideBarSettingButton(page); - await clickUserInfoCard(page); - await page.getByTestId('sign-out-button').click(); - await page.getByTestId('confirm-sign-out-button').click(); - await page.waitForTimeout(5000); - expect(page.url()).toBe(url); - }); -}); - -test.describe('collaboration members', () => { - test('should have pagination in member list', async ({ page }) => { - await page.reload(); - await waitForEditorLoad(page); - await createLocalWorkspace( - { - name: 'test', - }, - page - ); - await enableCloudWorkspace(page); - await clickNewPageButton(page); - const currentUrl = page.url(); - // format: http://localhost:8080/workspace/${workspaceId}/xxx - const workspaceId = currentUrl.split('/')[4]; - - // create 10 user and add to workspace - const createUserAndAddToWorkspace = async () => { - const userB = await createRandomUser(); - await addUserToWorkspace(workspaceId, userB.id, 1 /* READ */); - }; - await Promise.all( - Array.from({ length: 10 }) - .fill(1) - .map(() => createUserAndAddToWorkspace()) - ); - - await openSettingModal(page); - await openWorkspaceSettingPanel(page, 'test'); - - await page.waitForTimeout(1000); - - const firstPageMemberItemCount = await page - .locator('[data-testid="member-item"]') - .count(); - - expect(firstPageMemberItemCount).toBe(8); - - const navigationItems = await page - .getByRole('navigation') - .getByRole('button') - .all(); - - // make sure the first member is the owner - await expect(page.getByTestId('member-item').first()).toContainText( - 'Workspace Owner' - ); - - // There have four pagination items: < 1 2 > - expect(navigationItems.length).toBe(4); - // Click second page - await navigationItems[2].click(); - await page.waitForTimeout(500); - // There should have other three members in second page - const secondPageMemberItemCount = await page - .locator('[data-testid="member-item"]') - .count(); - expect(secondPageMemberItemCount).toBe(3); - // Click left arrow to back to first page - await navigationItems[0].click(); - await page.waitForTimeout(500); - expect(await page.locator('[data-testid="member-item"]').count()).toBe(8); - // Click right arrow to second page - await navigationItems[3].click(); - await page.waitForTimeout(500); - expect(await page.locator('[data-testid="member-item"]').count()).toBe(3); - }); -}); - -test.describe('sign out', () => { - test('can sign out', async ({ page }) => { - await page.reload(); - await waitForEditorLoad(page); - await createLocalWorkspace( - { - name: 'test', - }, - page - ); - await clickSideBarAllPageButton(page); - const currentUrl = page.url(); - await clickSideBarCurrentWorkspaceBanner(page); - await page.getByTestId('workspace-modal-account-option').click(); - await page.getByTestId('workspace-modal-sign-out-option').click(); - await page.getByTestId('confirm-sign-out-button').click(); - await clickSideBarCurrentWorkspaceBanner(page); - const signInButton = page.getByTestId('cloud-signin-button'); - await expect(signInButton).toBeVisible(); - expect(page.url()).toBe(currentUrl); - }); + await loginUser(page2, user.email); + await page2.goto(page.url()); + const collections = page2.getByTestId('collections'); + await expect(collections.getByText('test collection')).toBeVisible(); + } }); test('can sync svg between different browsers', async ({ page, browser }) => { @@ -348,12 +226,45 @@ test('can sync svg between different browsers', async ({ page, browser }) => { await clickNewPageButton(page); await waitForEditorLoad(page); - // drop an svg file - const svg = ` - - `; + // upload local svg - await dropFile(page, 'affine-paragraph', svg, 'test.svg', 'image/svg+xml'); + const slashMenu = page.locator(`.slash-menu`); + const image = page.locator('affine-image'); + + page.evaluate(async () => { + window.showOpenFilePicker = undefined; + }); + + const title = getBlockSuiteEditorTitle(page); + await title.pressSequentially('TEST TITLE', { + delay: 50, + }); + await page.keyboard.press('Enter', { delay: 50 }); + await page.waitForTimeout(100); + await page.keyboard.type('/', { delay: 50 }); + await expect(slashMenu).toBeVisible(); + await page.keyboard.type('image', { delay: 100 }); + await expect(slashMenu).toBeVisible(); + await page.keyboard.press('Enter', { delay: 50 }); + await page.setInputFiles( + "input[type='file']", + resolve(__dirname, 'logo.svg') + ); + await expect(image).toBeVisible(); + + // the user should see the svg + // get the image src under "affine-image img" + const src1 = await page.locator('affine-image img').getAttribute('src'); + expect(src1).not.toBeNull(); + + // fetch the actual src1 resource in the browser + const svg1 = await page.evaluate( + src => + fetch(src!) + .then(res => res.blob()) + .then(blob => blob.text()), + src1 + ); { const context = await browser.newContext(); @@ -361,20 +272,20 @@ test('can sync svg between different browsers', async ({ page, browser }) => { await loginUser(page2, user.email); await page2.goto(page.url()); - // the user should see the svg + // second user should see the svg // get the image src under "affine-image img" - const src = await page2.locator('affine-image img').getAttribute('src'); + const src2 = await page2.locator('affine-image img').getAttribute('src'); + expect(src2).not.toBeNull(); - expect(src).not.toBeNull(); + // fetch the actual src2 resource in the browser + const svg2 = await page2.evaluate( + src => + fetch(src!) + .then(res => res.blob()) + .then(blob => blob.text()), + src2 + ); - // fetch the src resource in the browser - const svg2 = await page2.evaluate(src => { - return fetch(src!) - .then(res => res.blob()) - .then(blob => blob.text()); - }, src); - - // turn the blob into string and check if it contains the svg - expect(svg2).toContain(svg); + expect(svg2).toEqual(svg1); } }); diff --git a/tests/affine-cloud/e2e/login.spec.ts b/tests/affine-cloud/e2e/login.spec.ts index 0cc007a477..6bfca65460 100644 --- a/tests/affine-cloud/e2e/login.spec.ts +++ b/tests/affine-cloud/e2e/login.spec.ts @@ -1,17 +1,111 @@ import { test } from '@affine-test/kit/playwright'; +import { + createRandomUser, + enableCloudWorkspace, + loginUser, +} from '@affine-test/kit/utils/cloud'; import { openHomePage } from '@affine-test/kit/utils/load-page'; import { waitForEditorLoad } from '@affine-test/kit/utils/page-logic'; -import { clickSideBarCurrentWorkspaceBanner } from '@affine-test/kit/utils/sidebar'; +import { clickUserInfoCard } from '@affine-test/kit/utils/setting'; +import { + clickSideBarAllPageButton, + clickSideBarCurrentWorkspaceBanner, + clickSideBarSettingButton, +} from '@affine-test/kit/utils/sidebar'; +import { createLocalWorkspace } from '@affine-test/kit/utils/workspace'; import { expect } from '@playwright/test'; -test.describe('login', () => { - test('can open login modal in workspace list', async ({ page }) => { - await openHomePage(page); +test('can open login modal in workspace list', async ({ page }) => { + await openHomePage(page); + await waitForEditorLoad(page); + await clickSideBarCurrentWorkspaceBanner(page); + await page.getByTestId('cloud-signin-button').click({ + delay: 200, + }); + await expect(page.getByTestId('auth-modal')).toBeVisible(); +}); + +test.describe('login first', () => { + let user: { + id: string; + name: string; + email: string; + password: string; + }; + + test.beforeEach(async ({ page }) => { + user = await createRandomUser(); + await loginUser(page, user.email); + }); + + test('exit successfully and re-login', async ({ page }) => { + await page.reload(); + await clickSideBarAllPageButton(page); + await page.waitForTimeout(200); + const url = page.url(); + await createLocalWorkspace( + { + name: 'test', + }, + page + ); + await enableCloudWorkspace(page); + await clickSideBarSettingButton(page); + await clickUserInfoCard(page); + await page.getByTestId('sign-out-button').click(); + await page.getByTestId('confirm-sign-out-button').click(); + await page.waitForTimeout(5000); + expect(page.url()).toBe(url); + }); + + test('can sign out', async ({ page }) => { + await page.reload(); await waitForEditorLoad(page); + await createLocalWorkspace( + { + name: 'test', + }, + page + ); + await clickSideBarAllPageButton(page); + const currentUrl = page.url(); await clickSideBarCurrentWorkspaceBanner(page); - await page.getByTestId('cloud-signin-button').click({ - delay: 200, - }); - await expect(page.getByTestId('auth-modal')).toBeVisible(); + await page.getByTestId('workspace-modal-account-option').click(); + await page.getByTestId('workspace-modal-sign-out-option').click(); + await page.getByTestId('confirm-sign-out-button').click(); + await clickSideBarCurrentWorkspaceBanner(page); + const signInButton = page.getByTestId('cloud-signin-button'); + await expect(signInButton).toBeVisible(); + expect(page.url()).toBe(currentUrl); + }); + + test('can see and change email and password in setting panel', async ({ + page, + }) => { + const newName = 'test name'; + { + await clickSideBarSettingButton(page); + const locator = page.getByTestId('user-info-card'); + expect(locator.getByText(user.email)).toBeTruthy(); + expect(locator.getByText(user.name)).toBeTruthy(); + await locator.click({ + delay: 50, + }); + const nameInput = page.getByPlaceholder('Input account name'); + await nameInput.clear(); + await nameInput.pressSequentially(newName, { + delay: 50, + }); + await page.getByTestId('save-user-name').click({ + delay: 50, + }); + } + await page.reload(); + { + await clickSideBarSettingButton(page); + const locator = page.getByTestId('user-info-card'); + expect(locator.getByText(user.email)).toBeTruthy(); + expect(locator.getByText(newName)).toBeTruthy(); + } }); }); diff --git a/tests/affine-cloud/e2e/logo.svg b/tests/affine-cloud/e2e/logo.svg new file mode 100644 index 0000000000..0fda3514a3 --- /dev/null +++ b/tests/affine-cloud/e2e/logo.svg @@ -0,0 +1 @@ + diff --git a/tests/affine-cloud/e2e/migration.spec.ts b/tests/affine-cloud/e2e/migration.spec.ts new file mode 100644 index 0000000000..b0a42db203 --- /dev/null +++ b/tests/affine-cloud/e2e/migration.spec.ts @@ -0,0 +1,113 @@ +import { readFile } from 'node:fs/promises'; +import { resolve } from 'node:path'; + +import { test } from '@affine-test/kit/playwright'; +import { + createRandomUser, + deleteUser, + enableCloudWorkspace, + getLoginCookie, + loginUser, + runPrisma, +} from '@affine-test/kit/utils/cloud'; +import { coreUrl } from '@affine-test/kit/utils/load-page'; +import { + clickNewPageButton, + waitForEditorLoad, +} from '@affine-test/kit/utils/page-logic'; +import { createLocalWorkspace } from '@affine-test/kit/utils/workspace'; +import { expect } from '@playwright/test'; + +let user: { + id: string; + name: string; + email: string; + password: string; +}; + +test.beforeEach(async () => { + user = await createRandomUser(); +}); + +test.beforeEach(async ({ page, context }) => { + await loginUser(page, user.email, { + beforeLogin: async () => { + expect(await getLoginCookie(context)).toBeUndefined(); + }, + afterLogin: async () => { + expect(await getLoginCookie(context)).toBeTruthy(); + await page.reload(); + await waitForEditorLoad(page); + expect(await getLoginCookie(context)).toBeTruthy(); + }, + }); +}); + +test.afterEach(async () => { + // if you want to keep the user in the database for debugging, + // comment this line + await deleteUser(user.email); +}); + +// TODO: @joooye34 mock migration from server data after page level upgrade implemented. +test.skip('migration', async ({ page, browser }) => { + let workspaceId: string; + { + // create the old cloud workspace in another browser + const context = await browser.newContext(); + const page = await context.newPage(); + await loginUser(page, user.email); + await page.reload(); + await createLocalWorkspace( + { + name: 'test', + }, + page + ); + await enableCloudWorkspace(page); + await clickNewPageButton(page); + await waitForEditorLoad(page); + // http://localhost:8080/workspace/2bc0b6c8-f68d-4dd3-98a8-be746754f9e1/xxx + workspaceId = page.url().split('/')[4]; + await runPrisma(async client => { + const sqls = ( + await readFile( + resolve(__dirname, 'fixtures', '0.9.0-canary.9-snapshots.sql'), + 'utf-8' + ) + ) + .replaceAll('2bc0b6c8-f68d-4dd3-98a8-be746754f9e1', workspaceId) + .split('\n'); + await client.snapshot.deleteMany({ + where: { + workspaceId, + }, + }); + + for (const sql of sqls) { + await client.$executeRawUnsafe(sql); + } + }); + await page.close(); + } + await page.reload(); + await page.waitForTimeout(1000); + await page.goto(`${coreUrl}/workspace/${workspaceId}/all`); + await page.getByTestId('upgrade-workspace-button').click(); + await expect(page.getByText('Refresh Current Page')).toBeVisible({ + timeout: 60000, + }); + await page.goto( + // page 'F1SX6cgNxy' has edgeless mode + `${coreUrl}/workspace/${workspaceId}/F1SX6cgNxy` + ); + await page.waitForTimeout(5000); + await page.reload(); + await waitForEditorLoad(page); + + // click edgeless switch button is flaky in e2e + // await clickEdgelessModeButton(page); + // await expect(page.locator('affine-edgeless-page')).toBeVisible({ + // timeout: 1000, + // }); +}); diff --git a/tests/affine-cloud/e2e/workspace.spec.ts b/tests/affine-cloud/e2e/workspace.spec.ts new file mode 100644 index 0000000000..31d00f1769 --- /dev/null +++ b/tests/affine-cloud/e2e/workspace.spec.ts @@ -0,0 +1,96 @@ +import { test } from '@affine-test/kit/playwright'; +import { + addUserToWorkspace, + createRandomUser, + enableCloudWorkspace, + loginUser, +} from '@affine-test/kit/utils/cloud'; +import { + clickNewPageButton, + waitForEditorLoad, +} from '@affine-test/kit/utils/page-logic'; +import { + openSettingModal, + openWorkspaceSettingPanel, +} from '@affine-test/kit/utils/setting'; +import { createLocalWorkspace } from '@affine-test/kit/utils/workspace'; +import { expect } from '@playwright/test'; + +let user: { + id: string; + name: string; + email: string; + password: string; +}; + +test.beforeEach(async ({ page }) => { + user = await createRandomUser(); + await loginUser(page, user.email); +}); + +test('should have pagination in member list', async ({ page }) => { + await page.reload(); + await waitForEditorLoad(page); + await createLocalWorkspace( + { + name: 'test', + }, + page + ); + await enableCloudWorkspace(page); + await clickNewPageButton(page); + const currentUrl = page.url(); + // format: http://localhost:8080/workspace/${workspaceId}/xxx + const workspaceId = currentUrl.split('/')[4]; + + // create 10 user and add to workspace + const createUserAndAddToWorkspace = async () => { + const userB = await createRandomUser(); + await addUserToWorkspace(workspaceId, userB.id, 1 /* READ */); + }; + await Promise.all( + Array.from({ length: 10 }) + .fill(1) + .map(() => createUserAndAddToWorkspace()) + ); + + await openSettingModal(page); + await openWorkspaceSettingPanel(page, 'test'); + + await page.waitForTimeout(1000); + + const firstPageMemberItemCount = await page + .locator('[data-testid="member-item"]') + .count(); + + expect(firstPageMemberItemCount).toBe(8); + + const navigationItems = await page + .getByRole('navigation') + .getByRole('button') + .all(); + + // make sure the first member is the owner + await expect(page.getByTestId('member-item').first()).toContainText( + 'Workspace Owner' + ); + + // There have four pagination items: < 1 2 > + expect(navigationItems.length).toBe(4); + // Click second page + await navigationItems[2].click(); + await page.waitForTimeout(500); + // There should have other three members in second page + const secondPageMemberItemCount = await page + .locator('[data-testid="member-item"]') + .count(); + expect(secondPageMemberItemCount).toBe(3); + // Click left arrow to back to first page + await navigationItems[0].click(); + await page.waitForTimeout(500); + expect(await page.locator('[data-testid="member-item"]').count()).toBe(8); + // Click right arrow to second page + await navigationItems[3].click(); + await page.waitForTimeout(500); + expect(await page.locator('[data-testid="member-item"]').count()).toBe(3); +}); diff --git a/tests/affine-cloud/playwright.config.ts b/tests/affine-cloud/playwright.config.ts index 772dfecf37..fcd64a0b00 100644 --- a/tests/affine-cloud/playwright.config.ts +++ b/tests/affine-cloud/playwright.config.ts @@ -6,7 +6,7 @@ import type { const config: PlaywrightTestConfig = { testDir: './e2e', fullyParallel: !process.env.CI, - timeout: process.env.CI ? 120_000 : 30_000, + timeout: 120_000, use: { baseURL: 'http://localhost:8081/', browserName: diff --git a/tests/affine-desktop/e2e/basic.spec.ts b/tests/affine-desktop/e2e/basic.spec.ts index 5ff31caf87..1089934aca 100644 --- a/tests/affine-desktop/e2e/basic.spec.ts +++ b/tests/affine-desktop/e2e/basic.spec.ts @@ -160,9 +160,9 @@ test('affine onboarding button', async ({ page }) => { test('windows only check', async ({ page }) => { const windowOnlyUI = page.locator('[data-platform-target=win32]'); if (process.platform === 'win32') { - await expect(windowOnlyUI).toBeVisible(); + await expect(windowOnlyUI.first()).toBeVisible(); } else { - await expect(windowOnlyUI).not.toBeVisible(); + await expect(windowOnlyUI.first()).not.toBeVisible(); } }); diff --git a/tests/affine-legacy/0.6.1-beta.1/package.json b/tests/affine-legacy/0.6.1-beta.1/package.json index e74e1b2c46..f50e2ace49 100644 --- a/tests/affine-legacy/0.6.1-beta.1/package.json +++ b/tests/affine-legacy/0.6.1-beta.1/package.json @@ -9,10 +9,10 @@ "devDependencies": { "@affine-test/fixtures": "workspace:*", "@affine-test/kit": "workspace:*", - "@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/block-std": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/blocks": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/global": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/store": "0.11.0-nightly-202312070955-2b5bb47", "@playwright/test": "^1.39.0", "express": "^4.18.2", "http-proxy-middleware": "^3.0.0-beta.1", diff --git a/tests/affine-legacy/0.7.0-canary.18/package.json b/tests/affine-legacy/0.7.0-canary.18/package.json index 181931c4ee..98d2c0b826 100644 --- a/tests/affine-legacy/0.7.0-canary.18/package.json +++ b/tests/affine-legacy/0.7.0-canary.18/package.json @@ -9,10 +9,10 @@ "devDependencies": { "@affine-test/fixtures": "workspace:*", "@affine-test/kit": "workspace:*", - "@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/block-std": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/blocks": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/global": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/store": "0.11.0-nightly-202312070955-2b5bb47", "@playwright/test": "^1.39.0", "express": "^4.18.2", "http-proxy-middleware": "^3.0.0-beta.1", diff --git a/tests/affine-legacy/0.8.0-canary.7/package.json b/tests/affine-legacy/0.8.0-canary.7/package.json index 8228a31148..1f7141d169 100644 --- a/tests/affine-legacy/0.8.0-canary.7/package.json +++ b/tests/affine-legacy/0.8.0-canary.7/package.json @@ -9,10 +9,10 @@ "devDependencies": { "@affine-test/fixtures": "workspace:*", "@affine-test/kit": "workspace:*", - "@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/block-std": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/blocks": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/global": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/store": "0.11.0-nightly-202312070955-2b5bb47", "@playwright/test": "^1.39.0", "express": "^4.18.2", "http-proxy-middleware": "^3.0.0-beta.1", diff --git a/tests/affine-legacy/0.8.4/package.json b/tests/affine-legacy/0.8.4/package.json index 5603164f29..8dd6712eae 100644 --- a/tests/affine-legacy/0.8.4/package.json +++ b/tests/affine-legacy/0.8.4/package.json @@ -9,10 +9,10 @@ "devDependencies": { "@affine-test/fixtures": "workspace:*", "@affine-test/kit": "workspace:*", - "@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/block-std": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/blocks": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/global": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/store": "0.11.0-nightly-202312070955-2b5bb47", "@playwright/test": "^1.39.0", "express": "^4.18.2", "http-proxy-middleware": "^3.0.0-beta.1", diff --git a/tests/affine-local/e2e/blocksuite/block-hub.spec.ts b/tests/affine-local/e2e/blocksuite/block-hub.spec.ts deleted file mode 100644 index b4c13a0760..0000000000 --- a/tests/affine-local/e2e/blocksuite/block-hub.spec.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { test } from '@affine-test/kit/playwright'; -import { checkBlockHub } from '@affine-test/kit/utils/editor'; -import { openHomePage } from '@affine-test/kit/utils/load-page'; -import { waitForEditorLoad } from '@affine-test/kit/utils/page-logic'; - -test('block-hub should work', async ({ page }) => { - await openHomePage(page); - await waitForEditorLoad(page); - await checkBlockHub(page); -}); diff --git a/tests/affine-local/e2e/local-first-avatar.spec.ts b/tests/affine-local/e2e/local-first-avatar.spec.ts index b352686406..deb1445a84 100644 --- a/tests/affine-local/e2e/local-first-avatar.spec.ts +++ b/tests/affine-local/e2e/local-first-avatar.spec.ts @@ -16,14 +16,11 @@ test('should create a page with a local first avatar and remove it', async ({ await waitForEditorLoad(page); await clickNewPageButton(page); await page.getByTestId('workspace-name').click(); - await page.getByTestId('new-workspace').click({ delay: 50 }); - await page - .getByTestId('create-workspace-input') - .pressSequentially('Test Workspace 1', { delay: 50 }); + await page.getByTestId('new-workspace').click(); + await page.getByTestId('create-workspace-input').fill('Test Workspace 1'); await page.getByTestId('create-workspace-create-button').click(); - await page.getByTestId('workspace-name').click({ - delay: 50, - }); + await page.waitForTimeout(1000); + await page.getByTestId('workspace-name').click(); await page.getByTestId('workspace-card').nth(1).click(); await page.getByTestId('settings-modal-trigger').click(); await page.getByTestId('current-workspace-label').click(); @@ -31,14 +28,10 @@ test('should create a page with a local first avatar and remove it', async ({ .getByTestId('upload-avatar') .setInputFiles(resolve(rootDir, 'tests', 'fixtures', 'smile.png')); await page.mouse.click(0, 0); - await page.getByTestId('workspace-name').click({ - delay: 50, - }); + await page.getByTestId('workspace-name').click(); await page.getByTestId('workspace-card').nth(0).click(); await page.waitForTimeout(1000); - await page.getByTestId('workspace-name').click({ - delay: 50, - }); + await page.getByTestId('workspace-name').click(); await page.getByTestId('workspace-card').nth(1).click(); const blobUrl = await page .getByTestId('workspace-avatar') diff --git a/tests/affine-local/e2e/local-first-favorites-items.spec.ts b/tests/affine-local/e2e/local-first-favorites-items.spec.ts index 769eb66841..296b47e640 100644 --- a/tests/affine-local/e2e/local-first-favorites-items.spec.ts +++ b/tests/affine-local/e2e/local-first-favorites-items.spec.ts @@ -130,7 +130,7 @@ test("Deleted page's reference will not be shown in sidebar", async ({ test('Add new favorite page via sidebar', async ({ page }) => { await openHomePage(page); await waitForEditorLoad(page); - await page.getByTestId('slider-bar-add-favorite-button').click(); + await page.getByTestId('slider-bar-add-favorite-button').first().click(); await waitForEditorLoad(page); // enter random page title diff --git a/tests/affine-local/e2e/local-first-trash-page.spec.ts b/tests/affine-local/e2e/local-first-trash-page.spec.ts index ba9248db61..6e8797b222 100644 --- a/tests/affine-local/e2e/local-first-trash-page.spec.ts +++ b/tests/affine-local/e2e/local-first-trash-page.spec.ts @@ -2,7 +2,6 @@ import { test } from '@affine-test/kit/playwright'; import { openHomePage } from '@affine-test/kit/utils/load-page'; import { clickNewPageButton, - clickPageMoreActions, getBlockSuiteEditorTitle, getPageOperationButton, waitForEditorLoad, @@ -41,21 +40,3 @@ test('New a page , then delete it in all pages, finally find it in trash', async expect(currentWorkspace.flavour).toContain('local'); }); - -test('New a page , then delete it in page, blockHub and option menu will not appear ', async ({ - page, -}) => { - await openHomePage(page); - await waitForEditorLoad(page); - await clickNewPageButton(page); - const title = getBlockSuiteEditorTitle(page); - await title.pressSequentially('test'); - await clickPageMoreActions(page); - await page.getByTestId('editor-option-menu-delete').click(); - await page.getByTestId('confirm-delete-page').click(); - await expect(page.getByTestId('header-dropDownButton')).not.toBeVisible(); - await expect(page.getByTestId('block-hub')).not.toBeVisible(); - await page.getByTestId('page-restore-button').click(); - await expect(page.getByTestId('header-dropDownButton')).toBeVisible(); - await expect(page.getByTestId('block-hub')).toBeVisible(); -}); diff --git a/tests/affine-migration/e2e/basic.spec.ts b/tests/affine-migration/e2e/basic.spec.ts index 83d302db27..9e26655cd5 100644 --- a/tests/affine-migration/e2e/basic.spec.ts +++ b/tests/affine-migration/e2e/basic.spec.ts @@ -89,9 +89,11 @@ test('v3 to v4, surface migration', async ({ page }) => { await page.getByTestId('upgrade-workspace-button').click(); await waitForEditorLoad(page); + await page.waitForTimeout(500); + // check edgeless mode is correct await clickEdgelessModeButton(page); - await expect(page.locator('edgeless-toolbar')).toBeVisible(); + await expect(page.locator('.edgeless-toolbar-container')).toBeVisible(); await expect(page.locator('affine-edgeless-page')).toBeVisible(); }); @@ -121,6 +123,6 @@ test('v0 to v4, subdoc migration', async ({ page }) => { // check edgeless mode is correct await clickEdgelessModeButton(page); - await expect(page.locator('edgeless-toolbar')).toBeVisible(); + await expect(page.locator('.edgeless-toolbar-container')).toBeVisible(); await expect(page.locator('affine-edgeless-page')).toBeVisible(); }); diff --git a/tests/affine-migration/package.json b/tests/affine-migration/package.json index 5964e964dc..3373a1ad1b 100644 --- a/tests/affine-migration/package.json +++ b/tests/affine-migration/package.json @@ -7,10 +7,10 @@ "devDependencies": { "@affine-test/fixtures": "workspace:*", "@affine-test/kit": "workspace:*", - "@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/block-std": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/blocks": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/global": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/store": "0.11.0-nightly-202312070955-2b5bb47", "@playwright/test": "^1.39.0" }, "version": "0.10.3" diff --git a/tests/kit/utils/editor.ts b/tests/kit/utils/editor.ts index 4a2425605d..d10c73e2cc 100644 --- a/tests/kit/utils/editor.ts +++ b/tests/kit/utils/editor.ts @@ -1,15 +1,4 @@ import type { Page } from '@playwright/test'; -import { expect } from '@playwright/test'; - -export async function checkBlockHub(page: Page) { - const box = await page.locator('affine-block-hub').boundingBox(); - if (!box) throw new Error('block-hub not found'); - await page.getByTestId('block-hub').click(); - await page.waitForTimeout(500); - const box2 = await page.locator('affine-block-hub').boundingBox(); - if (!box2) throw new Error('block-hub not found'); - expect(box2.height).toBeGreaterThan(box.height); -} export async function clickEdgelessModeButton(page: Page) { await page.getByTestId('switch-edgeless-mode-button').click({ diff --git a/tests/storybook/.storybook/preview.tsx b/tests/storybook/.storybook/preview.tsx index fc0d9253a6..82f655b9ac 100644 --- a/tests/storybook/.storybook/preview.tsx +++ b/tests/storybook/.storybook/preview.tsx @@ -1,7 +1,6 @@ import 'ses'; import '@affine/component/theme/global.css'; import '@affine/component/theme/theme.css'; -import '@toeverything/components/style.css'; import { createI18n } from '@affine/i18n'; import MockSessionContext, { mockAuthStates, diff --git a/tests/storybook/package.json b/tests/storybook/package.json index f0e80c4e14..85a329e3e9 100644 --- a/tests/storybook/package.json +++ b/tests/storybook/package.json @@ -31,14 +31,14 @@ "wait-on": "^7.2.0" }, "devDependencies": { - "@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/block-std": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/blocks": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/global": "0.11.0-nightly-202312070955-2b5bb47", "@blocksuite/icons": "2.1.36", - "@blocksuite/lit": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", - "@blocksuite/virgo": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/lit": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/presets": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/store": "0.11.0-nightly-202312070955-2b5bb47", + "@blocksuite/virgo": "0.11.0-nightly-202312070955-2b5bb47", "@dnd-kit/sortable": "^8.0.0", "@tomfreudenberg/next-auth-mock": "^0.5.6", "chromatic": "^9.1.0", @@ -51,9 +51,9 @@ }, "peerDependencies": { "@blocksuite/blocks": "*", - "@blocksuite/editor": "*", "@blocksuite/global": "*", "@blocksuite/icons": "2.1.34", + "@blocksuite/presets": "*", "@blocksuite/store": "*" }, "version": "0.10.3" diff --git a/tests/storybook/src/stories/card.stories.tsx b/tests/storybook/src/stories/card.stories.tsx index 305554f2a3..296383629d 100644 --- a/tests/storybook/src/stories/card.stories.tsx +++ b/tests/storybook/src/stories/card.stories.tsx @@ -1,6 +1,7 @@ import { toast } from '@affine/component'; import { BlockCard } from '@affine/component/card/block-card'; import { WorkspaceCard } from '@affine/component/card/workspace-card'; +import { Tooltip } from '@affine/component/ui/tooltip'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { getOrCreateWorkspace } from '@affine/workspace/manager'; import { @@ -10,7 +11,6 @@ import { PageIcon, } from '@blocksuite/icons'; import type { Meta } from '@storybook/react'; -import { Tooltip } from '@toeverything/components/tooltip'; export default { title: 'AFFiNE/Card', diff --git a/tests/storybook/src/stories/image-preview-modal.stories.tsx b/tests/storybook/src/stories/image-preview-modal.stories.tsx index 26f798495c..c914129996 100644 --- a/tests/storybook/src/stories/image-preview-modal.stories.tsx +++ b/tests/storybook/src/stories/image-preview-modal.stories.tsx @@ -1,4 +1,3 @@ -import { RootBlockHub } from '@affine/component/block-hub'; import { BlockSuiteEditor } from '@affine/component/block-suite-editor'; import { WorkspaceFlavour } from '@affine/env/workspace'; // eslint-disable-next-line @typescript-eslint/no-restricted-imports @@ -44,21 +43,18 @@ fetch(new URL('@affine-test/fixtures/large-image.png', import.meta.url)) export const Default = () => { return ( - <> -
    - - {createPortal( - , - document.body - )} -
    - - +
    + + {createPortal( + , + document.body + )} +
    ); }; diff --git a/tests/storybook/src/stories/quick-search/quick-search-modal.stories.tsx b/tests/storybook/src/stories/quick-search/quick-search-modal.stories.tsx index 2b026b617d..368df7e97e 100644 --- a/tests/storybook/src/stories/quick-search/quick-search-modal.stories.tsx +++ b/tests/storybook/src/stories/quick-search/quick-search-modal.stories.tsx @@ -1,6 +1,6 @@ +import { Button } from '@affine/component/ui/button'; import { CMDKContainer, CMDKModal } from '@affine/core/components/pure/cmdk'; import type { Meta, StoryFn } from '@storybook/react'; -import { Button } from '@toeverything/components/button'; import { useState } from 'react'; export default { diff --git a/tools/cli/package.json b/tools/cli/package.json index 9f8b5cc527..f907f05673 100644 --- a/tools/cli/package.json +++ b/tools/cli/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "dotenv": "^16.3.1", - "vite": "^4.4.11" + "vite": "^5.0.6" }, "peerDependencies": { "ts-node": "*" diff --git a/tools/commitlint/package.json b/tools/commitlint/package.json index 6a47516f4b..08d92fd448 100644 --- a/tools/commitlint/package.json +++ b/tools/commitlint/package.json @@ -1,6 +1,6 @@ { "name": "@affine/commitlint-config", - "version": "0.0.0", + "version": "0.10.3-canary.2", "private": true, "devDependencies": { "@commitlint/cli": "^18.4.3", diff --git a/tools/plugin-cli/package.json b/tools/plugin-cli/package.json index a0eb829084..a679505fc9 100644 --- a/tools/plugin-cli/package.json +++ b/tools/plugin-cli/package.json @@ -16,7 +16,7 @@ "@toeverything/infra": "workspace:^", "@vanilla-extract/rollup-plugin": "^1.3.0", "@vitejs/plugin-vue": "^4.4.0", - "rollup": "^3.29.4", + "rollup": "^4.0.0", "rollup-plugin-swc3": "^0.10.2", "ts-node": "^10.9.1", "vue": "^3.3.4" diff --git a/tsconfig.json b/tsconfig.json index 0439ba2331..b03ccd2b6c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,6 @@ "compilerOptions": { "verbatimModuleSyntax": true, // Classification follows https://www.typescriptlang.org/tsconfig - // Type Checking "strict": true, "exactOptionalPropertyTypes": false, @@ -16,7 +15,6 @@ "noPropertyAccessFromIndexSignature": false, "noUncheckedIndexedAccess": false, "useUnknownInCatchVariables": true, - // Modules "module": "ESNext", "moduleResolution": "bundler", @@ -28,16 +26,13 @@ "sourceMap": true, // skip type emit for @internal types // "stripInternal": true, - // JavaScript Support "allowJs": false, "checkJs": false, - // Interop Constraints "forceConsistentCasingInFileNames": true, "allowSyntheticDefaultImports": true, "isolatedModules": true, - // Language and Environment "jsx": "preserve", "jsxImportSource": "@emotion/react", @@ -46,11 +41,9 @@ "useDefineForClassFields": false, "experimentalDecorators": true, "emitDecoratorMetadata": true, - // Projects "composite": true, "incremental": true, - // Completeness "skipLibCheck": true, // skip all type checks for .d.ts files "paths": { @@ -81,7 +74,6 @@ "@affine/native": ["./packages/frontend/native/index.d.ts"], "@affine/native/*": ["./packages/frontend/native/*"], "@affine/storage": ["./packages/backend/storage/index.d.ts"], - // Development only "@affine/electron/*": ["./packages/frontend/electron/src/*"] } diff --git a/yarn.lock b/yarn.lock index 2bc8715170..e3055874a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13,9 +13,9 @@ __metadata: linkType: hard "@adobe/css-tools@npm:^4.3.1": - version: 4.3.1 - resolution: "@adobe/css-tools@npm:4.3.1" - checksum: 039a42ffdd41ecf3abcaf09c9fef0ffd634ccbe81c04002fc989e74564eba99bb19169a8f48dadf6442aa2c5c9f0925a7b27ec5c36a1ed1a3515fe77d6930996 + version: 4.3.2 + resolution: "@adobe/css-tools@npm:4.3.2" + checksum: 973dcb7ba5141f57ec726ddec2e94e8947361bb0c5f0e8ebd1e8aa3a84b28e66db4ad843908825f99730d59784ff3c43868b014a7268676a65950cdb850c42cc languageName: node linkType: hard @@ -25,10 +25,10 @@ __metadata: dependencies: "@affine-test/fixtures": "workspace:*" "@affine-test/kit": "workspace:*" - "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/block-std": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/blocks": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47" "@playwright/test": "npm:^1.39.0" express: "npm:^4.18.2" http-proxy-middleware: "npm:^3.0.0-beta.1" @@ -42,10 +42,10 @@ __metadata: dependencies: "@affine-test/fixtures": "workspace:*" "@affine-test/kit": "workspace:*" - "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/block-std": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/blocks": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47" "@playwright/test": "npm:^1.39.0" express: "npm:^4.18.2" http-proxy-middleware: "npm:^3.0.0-beta.1" @@ -59,10 +59,10 @@ __metadata: dependencies: "@affine-test/fixtures": "workspace:*" "@affine-test/kit": "workspace:*" - "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/block-std": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/blocks": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47" "@playwright/test": "npm:^1.39.0" express: "npm:^4.18.2" http-proxy-middleware: "npm:^3.0.0-beta.1" @@ -76,10 +76,10 @@ __metadata: dependencies: "@affine-test/fixtures": "workspace:*" "@affine-test/kit": "workspace:*" - "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/block-std": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/blocks": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47" "@playwright/test": "npm:^1.39.0" express: "npm:^4.18.2" http-proxy-middleware: "npm:^3.0.0-beta.1" @@ -138,10 +138,10 @@ __metadata: dependencies: "@affine-test/fixtures": "workspace:*" "@affine-test/kit": "workspace:*" - "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/block-std": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/blocks": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47" "@playwright/test": "npm:^1.39.0" languageName: unknown linkType: soft @@ -186,7 +186,7 @@ __metadata: "@magic-works/i18n-codegen": "npm:^0.5.0" dotenv: "npm:^16.3.1" ts-node: "npm:^10.9.1" - vite: "npm:^4.4.11" + vite: "npm:^5.0.6" peerDependencies: ts-node: "*" bin: @@ -222,14 +222,14 @@ __metadata: "@affine/graphql": "workspace:*" "@affine/i18n": "workspace:*" "@affine/workspace": "workspace:*" - "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/blocks": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" "@blocksuite/icons": "npm:2.1.36" - "@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/lit": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/presets": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47" "@dnd-kit/core": "npm:^6.0.8" - "@dnd-kit/modifiers": "npm:^6.0.1" + "@dnd-kit/modifiers": "npm:^7.0.0" "@dnd-kit/sortable": "npm:^8.0.0" "@emotion/cache": "npm:^11.11.0" "@emotion/react": "npm:^11.11.1" @@ -238,12 +238,26 @@ __metadata: "@popperjs/core": "npm:^2.11.8" "@radix-ui/react-avatar": "npm:^1.0.4" "@radix-ui/react-collapsible": "npm:^1.0.3" + "@radix-ui/react-dialog": "npm:^1.0.5" + "@radix-ui/react-dropdown-menu": "npm:^2.0.6" "@radix-ui/react-popover": "npm:^1.0.7" "@radix-ui/react-radio-group": "npm:^1.1.3" "@radix-ui/react-scroll-area": "npm:^1.0.5" "@radix-ui/react-toast": "npm:^1.1.5" "@radix-ui/react-toolbar": "npm:^1.0.4" + "@radix-ui/react-tooltip": "npm:^1.0.7" + "@storybook/addon-actions": "npm:^7.5.3" + "@storybook/addon-essentials": "npm:^7.5.3" + "@storybook/addon-interactions": "npm:^7.5.3" + "@storybook/addon-links": "npm:^7.5.3" + "@storybook/addon-mdx-gfm": "npm:^7.5.3" + "@storybook/addon-storysource": "npm:^7.5.3" + "@storybook/blocks": "npm:^7.5.3" + "@storybook/builder-vite": "npm:^7.5.3" "@storybook/jest": "npm:^0.2.3" + "@storybook/react": "npm:^7.5.3" + "@storybook/react-vite": "npm:^7.5.3" + "@storybook/test-runner": "npm:^0.15.2" "@storybook/testing-library": "npm:^0.2.2" "@testing-library/react": "npm:^14.0.0" "@toeverything/hooks": "workspace:*" @@ -280,16 +294,18 @@ __metadata: react-router-dom: "npm:^6.16.0" react-virtuoso: "npm:^4.6.2" rxjs: "npm:^7.8.1" + storybook: "npm:^7.5.3" + storybook-dark-mode: "npm:^3.0.1" typescript: "npm:^5.3.2" uuid: "npm:^9.0.1" - vite: "npm:^4.4.11" + vite: "npm:^5.0.6" vitest: "npm:0.34.6" yjs: "npm:^13.6.10" peerDependencies: "@blocksuite/blocks": "*" - "@blocksuite/editor": "*" "@blocksuite/global": "*" "@blocksuite/icons": 2.1.34 + "@blocksuite/presets": "*" "@blocksuite/store": "*" languageName: unknown linkType: soft @@ -302,11 +318,10 @@ __metadata: "@affine/plugin-cli": "workspace:*" "@affine/sdk": "workspace:*" "@blocksuite/icons": "npm:2.1.36" - "@toeverything/components": "npm:^0.0.46" "@types/marked": "npm:^6.0.0" "@vanilla-extract/css": "npm:^1.13.0" clsx: "npm:^2.0.0" - idb: "npm:^7.1.1" + idb: "npm:^8.0.0" jotai: "npm:^2.5.1" langchain: "npm:^0.0.166" marked: "npm:^9.1.2" @@ -335,14 +350,14 @@ __metadata: "@affine/templates": "workspace:*" "@affine/workspace": "workspace:*" "@aws-sdk/client-s3": "npm:3.433.0" - "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/block-std": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/blocks": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" "@blocksuite/icons": "npm:2.1.36" - "@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/virgo": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/lit": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/presets": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/virgo": "npm:0.11.0-nightly-202312070955-2b5bb47" "@dnd-kit/core": "npm:^6.0.8" "@dnd-kit/sortable": "npm:^8.0.0" "@emotion/cache": "npm:^11.11.0" @@ -363,7 +378,6 @@ __metadata: "@svgr/webpack": "npm:^8.1.0" "@swc/core": "npm:^1.3.93" "@testing-library/react": "npm:^14.0.0" - "@toeverything/components": "npm:^0.0.46" "@toeverything/theme": "npm:^0.7.20" "@types/bytes": "npm:^3.1.3" "@types/lodash-es": "npm:^4.17.9" @@ -384,10 +398,11 @@ __metadata: foxact: "npm:^0.2.20" graphql: "npm:^16.8.1" html-webpack-plugin: "npm:^5.5.3" - idb: "npm:^7.1.1" + idb: "npm:^8.0.0" intl-segmenter-polyfill-rs: "npm:^0.1.6" jotai: "npm:^2.5.1" jotai-devtools: "npm:^0.7.0" + jotai-effect: "npm:^0.2.3" lit: "npm:^3.0.2" lodash-es: "npm:^4.17.21" lottie-web: "npm:^5.12.2" @@ -449,10 +464,10 @@ __metadata: "@affine/sdk": "workspace:*" "@affine/templates": "workspace:*" "@affine/vue-hello-world-plugin": "workspace:*" - "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/blocks": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/lit": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/presets": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47" "@electron-forge/cli": "npm:^7.1.0" "@electron-forge/core": "npm:^7.1.0" "@electron-forge/core-utils": "npm:^7.1.0" @@ -485,7 +500,7 @@ __metadata: semver: "npm:^7.5.4" tinykeys: "npm:^2.1.0" ts-node: "npm:^10.9.1" - undici: "npm:^5.27.2" + undici: "npm:^6.0.0" uuid: "npm:^9.0.1" vitest: "npm:0.34.6" which: "npm:^4.0.0" @@ -500,8 +515,8 @@ __metadata: version: 0.0.0-use.local resolution: "@affine/env@workspace:packages/common/env" dependencies: - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47" lit: "npm:^3.0.2" react: "npm:18.2.0" react-dom: "npm:18.2.0" @@ -510,7 +525,6 @@ __metadata: peerDependencies: "@affine/templates": "workspace:*" "@blocksuite/global": 0.0.0-20230409084303-221991d4-nightly - "@toeverything/infra": "workspace:*" languageName: unknown linkType: soft @@ -541,7 +555,6 @@ __metadata: "@affine/plugin-cli": "workspace:*" "@affine/sdk": "workspace:*" "@blocksuite/icons": "npm:2.1.36" - "@toeverything/components": "npm:^0.0.46" languageName: unknown linkType: soft @@ -566,7 +579,6 @@ __metadata: "@affine/plugin-cli": "workspace:*" "@affine/sdk": "workspace:*" "@blocksuite/icons": "npm:2.1.36" - "@toeverything/components": "npm:^0.0.46" "@toeverything/theme": "npm:^0.7.24" clsx: "npm:^2.0.0" foxact: "npm:^0.2.20" @@ -630,7 +642,7 @@ __metadata: string-width: "npm:^7.0.0" ts-node: "npm:^10.9.1" typescript: "npm:^5.3.2" - vite: "npm:^5.0.1" + vite: "npm:^5.0.6" vite-plugin-istanbul: "npm:^5.0.0" vite-plugin-static-copy: "npm:^0.17.1" vite-tsconfig-paths: "npm:^4.2.1" @@ -644,10 +656,10 @@ __metadata: version: 0.0.0-use.local resolution: "@affine/native@workspace:packages/frontend/native" dependencies: - "@napi-rs/cli": "npm:^2.16.5" + "@napi-rs/cli": "npm:3.0.0-alpha.15" "@types/node": "npm:^20.9.3" "@types/uuid": "npm:^9.0.7" - ava: "npm:^5.3.1" + ava: "npm:^6.0.0" cross-env: "npm:^7.0.3" nx: "npm:^17.1.3" nx-cloud: "npm:^16.5.2" @@ -666,7 +678,6 @@ __metadata: "@affine/plugin-cli": "workspace:*" "@affine/sdk": "workspace:*" "@blocksuite/icons": "npm:2.1.36" - "@toeverything/components": "npm:^0.0.46" jotai: "npm:^2.5.1" react: "npm:18.2.0" react-dom: "npm:18.2.0" @@ -683,7 +694,7 @@ __metadata: "@toeverything/infra": "workspace:^" "@vanilla-extract/rollup-plugin": "npm:^1.3.0" "@vitejs/plugin-vue": "npm:^4.4.0" - rollup: "npm:^3.29.4" + rollup: "npm:^4.0.0" rollup-plugin-swc3: "npm:^0.10.2" ts-node: "npm:^10.9.1" vue: "npm:^3.3.4" @@ -696,13 +707,13 @@ __metadata: version: 0.0.0-use.local resolution: "@affine/sdk@workspace:packages/common/sdk" dependencies: - "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/block-std": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/blocks": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/presets": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47" jotai: "npm:^2.5.1" - vite: "npm:^4.4.11" + vite: "npm:^5.0.6" vite-plugin-dts: "npm:3.6.0" zod: "npm:^3.22.4" languageName: unknown @@ -739,11 +750,11 @@ __metadata: "@opentelemetry/core": "npm:^1.18.1" "@opentelemetry/exporter-prometheus": "npm:^0.45.1" "@opentelemetry/exporter-zipkin": "npm:^1.18.1" - "@opentelemetry/host-metrics": "npm:^0.33.2" + "@opentelemetry/host-metrics": "npm:^0.34.0" "@opentelemetry/instrumentation": "npm:^0.45.1" "@opentelemetry/instrumentation-graphql": "npm:^0.36.0" "@opentelemetry/instrumentation-http": "npm:^0.45.1" - "@opentelemetry/instrumentation-ioredis": "npm:^0.35.3" + "@opentelemetry/instrumentation-ioredis": "npm:^0.36.0" "@opentelemetry/instrumentation-nestjs-core": "npm:^0.33.3" "@opentelemetry/instrumentation-socket.io": "npm:^0.34.3" "@opentelemetry/resources": "npm:^1.18.1" @@ -766,7 +777,7 @@ __metadata: "@types/sinon": "npm:^17.0.2" "@types/supertest": "npm:^2.0.16" "@types/ws": "npm:^8.5.10" - ava: "npm:^5.3.1" + ava: "npm:^6.0.0" c8: "npm:^8.0.1" cookie-parser: "npm:^1.4.6" dotenv: "npm:^16.3.1" @@ -810,7 +821,7 @@ __metadata: version: 0.0.0-use.local resolution: "@affine/storage@workspace:packages/backend/storage" dependencies: - "@napi-rs/cli": "npm:^2.16.5" + "@napi-rs/cli": "npm:3.0.0-alpha.15" lib0: "npm:^0.2.87" nx: "npm:^17.1.3" nx-cloud: "npm:^16.5.2" @@ -824,14 +835,14 @@ __metadata: dependencies: "@affine/component": "workspace:*" "@affine/i18n": "workspace:*" - "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/block-std": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/blocks": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" "@blocksuite/icons": "npm:2.1.36" - "@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/virgo": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/lit": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/presets": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/virgo": "npm:0.11.0-nightly-202312070955-2b5bb47" "@dnd-kit/sortable": "npm:^8.0.0" "@storybook/addon-actions": "npm:^7.5.3" "@storybook/addon-essentials": "npm:^7.5.3" @@ -863,9 +874,9 @@ __metadata: wait-on: "npm:^7.2.0" peerDependencies: "@blocksuite/blocks": "*" - "@blocksuite/editor": "*" "@blocksuite/global": "*" "@blocksuite/icons": 2.1.34 + "@blocksuite/presets": "*" "@blocksuite/store": "*" languageName: unknown linkType: soft @@ -910,7 +921,7 @@ __metadata: "@types/ws": "npm:^8.5.7" async-call-rpc: "npm:^6.3.1" fake-indexeddb: "npm:^5.0.0" - idb: "npm:^7.1.1" + idb: "npm:^8.0.0" idb-keyval: "npm:^6.2.1" is-svg: "npm:^5.0.0" jotai: "npm:^2.5.1" @@ -4072,74 +4083,76 @@ __metadata: languageName: node linkType: hard -"@blocksuite/block-std@npm:0.0.0-20231124123613-7c06e95d-nightly": - version: 0.0.0-20231124123613-7c06e95d-nightly - resolution: "@blocksuite/block-std@npm:0.0.0-20231124123613-7c06e95d-nightly" +"@blocksuite/block-std@npm:0.11.0-nightly-202312070955-2b5bb47": + version: 0.11.0-nightly-202312070955-2b5bb47 + resolution: "@blocksuite/block-std@npm:0.11.0-nightly-202312070955-2b5bb47" dependencies: - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" lz-string: "npm:^1.5.0" w3c-keyname: "npm:^2.2.8" zod: "npm:^3.22.4" peerDependencies: - "@blocksuite/store": 0.0.0-20231124123613-7c06e95d-nightly - checksum: c2fbfdc60c8a6e2c38b8b572522d70543ae89b66283d8e246b8058eb6c8de83ede739dcfd5df204434147eefadbf0aeabc0bd26eb064f2fb771b8bfc32fc5443 + "@blocksuite/store": 0.11.0-nightly-202312070955-2b5bb47 + checksum: 386721e0232cc1b2c566ca931b5d36b24d99ca9dc365df2b15115033b4bf1647490b87adfcd1ae8cffbf6c2fe65ae17e193ab349e80a040a536424bca515d58d languageName: node linkType: hard -"@blocksuite/blocks@npm:0.0.0-20231124123613-7c06e95d-nightly": - version: 0.0.0-20231124123613-7c06e95d-nightly - resolution: "@blocksuite/blocks@npm:0.0.0-20231124123613-7c06e95d-nightly" +"@blocksuite/blocks@npm:0.11.0-nightly-202312070955-2b5bb47": + version: 0.11.0-nightly-202312070955-2b5bb47 + resolution: "@blocksuite/blocks@npm:0.11.0-nightly-202312070955-2b5bb47" dependencies: - "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/virgo": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/block-std": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/lit": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/virgo": "npm:0.11.0-nightly-202312070955-2b5bb47" "@floating-ui/dom": "npm:^1.5.3" - "@toeverything/theme": "npm:^0.7.26" - "@types/webfontloader": "npm:^1.6.36" + "@toeverything/theme": "npm:^0.7.27" + "@types/hast": "npm:^3.0.3" + "@types/mdast": "npm:^4.0.3" + "@types/webfontloader": "npm:^1.6.38" buffer: "npm:^6.0.3" date-fns: "npm:^2.30.0" file-type: "npm:^16.5.4" fractional-indexing: "npm:^3.2.0" html2canvas: "npm:^1.4.1" jszip: "npm:^3.10.1" - lit: "npm:^3.0.2" + lit: "npm:^3.1.0" marked: "npm:^4.3.0" - nanoid: "npm:^5.0.3" + mdast-util-gfm-autolink-literal: "npm:^2.0.0" + mdast-util-gfm-strikethrough: "npm:^2.0.0" + mdast-util-gfm-table: "npm:^2.0.0" + mdast-util-gfm-task-list-item: "npm:^2.0.0" + micromark-extension-gfm-autolink-literal: "npm:^2.0.0" + micromark-extension-gfm-strikethrough: "npm:^2.0.0" + micromark-extension-gfm-table: "npm:^2.0.0" + micromark-extension-gfm-task-list-item: "npm:^2.0.1" + micromark-util-combine-extensions: "npm:^2.0.0" + nanoid: "npm:^5.0.4" pdf-lib: "npm:^1.17.1" + rehype-parse: "npm:^9.0.0" + rehype-stringify: "npm:^10.0.0" + remark-parse: "npm:^11.0.0" + remark-stringify: "npm:^11.0.0" shiki: "npm:^0.14.5" - sortablejs: "npm:^1.15.0" + sortablejs: "npm:^1.15.1" + unified: "npm:^11.0.4" webfontloader: "npm:^1.6.28" zod: "npm:^3.22.4" - checksum: d08b8fae9fd7d5b64d1c0a7f2278ae71608848c9df16aa52be69818868e71ff0d1abf9bd082395e6ec3ccb97ea32e8e3f9483bfec46941105366125a4c2751f3 + checksum: 5a5e4d186c79436b0169d6b2de8e19e8d2da2246e873e87b49ce53d82e6168fb684a7b3d36533ce2ca2914aa87b6eb1795842e5b3e686396c3760f08c50ddd2a languageName: node linkType: hard -"@blocksuite/editor@npm:0.0.0-20231124123613-7c06e95d-nightly": - version: 0.0.0-20231124123613-7c06e95d-nightly - resolution: "@blocksuite/editor@npm:0.0.0-20231124123613-7c06e95d-nightly" - dependencies: - "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@toeverything/theme": "npm:^0.7.26" - lit: "npm:^3.0.2" - checksum: 9da41777250311c926c65a4d8812dc9b43709b3822859617542c6ea6afc5092df1a28d37c228e16c8d0089ff2565aadc3ff234f51429f9366e93b8c21bb4aaf3 - languageName: node - linkType: hard - -"@blocksuite/global@npm:0.0.0-20231124123613-7c06e95d-nightly": - version: 0.0.0-20231124123613-7c06e95d-nightly - resolution: "@blocksuite/global@npm:0.0.0-20231124123613-7c06e95d-nightly" +"@blocksuite/global@npm:0.11.0-nightly-202312070955-2b5bb47": + version: 0.11.0-nightly-202312070955-2b5bb47 + resolution: "@blocksuite/global@npm:0.11.0-nightly-202312070955-2b5bb47" dependencies: zod: "npm:^3.22.4" - checksum: 739e00a63011212a9b74c6af913b6d6d73b3cf2b75fd8abd2680635bb295515f1634ae535334f10d10a8c670e641e5098040c8b5f9d77abdc900bcc058232d03 + checksum: de06d91f72a9387a882003f189493c5d532a72d321744d6a478d243c9f464e1d71b652cfe7628bf87ff2c200e0595695bc20883993bb78b41edacb617aff8734 languageName: node linkType: hard -"@blocksuite/icons@npm:2.1.36, @blocksuite/icons@npm:^2.1.33": +"@blocksuite/icons@npm:2.1.36": version: 2.1.36 resolution: "@blocksuite/icons@npm:2.1.36" peerDependencies: @@ -4149,57 +4162,66 @@ __metadata: languageName: node linkType: hard -"@blocksuite/lit@npm:0.0.0-20231124123613-7c06e95d-nightly": - version: 0.0.0-20231124123613-7c06e95d-nightly - resolution: "@blocksuite/lit@npm:0.0.0-20231124123613-7c06e95d-nightly" +"@blocksuite/lit@npm:0.11.0-nightly-202312070955-2b5bb47": + version: 0.11.0-nightly-202312070955-2b5bb47 + resolution: "@blocksuite/lit@npm:0.11.0-nightly-202312070955-2b5bb47" dependencies: - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/virgo": "npm:0.0.0-20231124123613-7c06e95d-nightly" - lit: "npm:^3.0.2" + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/virgo": "npm:0.11.0-nightly-202312070955-2b5bb47" + lit: "npm:^3.1.0" peerDependencies: - "@blocksuite/block-std": 0.0.0-20231124123613-7c06e95d-nightly - "@blocksuite/store": 0.0.0-20231124123613-7c06e95d-nightly - checksum: 8ce5fa1918cf1a39cb8922ba39a4c4248690350a258bf6fdae9f098de7ca9fd45762ee994781626bdea76a02697c2bb972044a02b33ab45f1aecec6ce94260e0 + "@blocksuite/block-std": 0.11.0-nightly-202312070955-2b5bb47 + "@blocksuite/store": 0.11.0-nightly-202312070955-2b5bb47 + checksum: 9e2f12a824a3121953c1b31d68005a34a5ab9b2069f7e4b296aaf8fcc0acf10a1165298eb2baa8527f3493f2aba23e862fd8398fa4d659fb8ace68de13ba3cf2 languageName: node linkType: hard -"@blocksuite/store@npm:0.0.0-20231124123613-7c06e95d-nightly": - version: 0.0.0-20231124123613-7c06e95d-nightly - resolution: "@blocksuite/store@npm:0.0.0-20231124123613-7c06e95d-nightly" +"@blocksuite/presets@npm:0.11.0-nightly-202312070955-2b5bb47": + version: 0.11.0-nightly-202312070955-2b5bb47 + resolution: "@blocksuite/presets@npm:0.11.0-nightly-202312070955-2b5bb47" dependencies: - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/virgo": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/blocks": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/lit": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@toeverything/theme": "npm:^0.7.27" + lit: "npm:^3.1.0" + checksum: 3e607f11dd709cc8d1f824bb087449c0f32350d306dff3b0948ef97b84da9c223da353ddd538ee148c0449d424f46c8f0b13cacf6804ae3ea77e4197474e0fbd + languageName: node + linkType: hard + +"@blocksuite/store@npm:0.11.0-nightly-202312070955-2b5bb47": + version: 0.11.0-nightly-202312070955-2b5bb47 + resolution: "@blocksuite/store@npm:0.11.0-nightly-202312070955-2b5bb47" + dependencies: + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/virgo": "npm:0.11.0-nightly-202312070955-2b5bb47" "@types/flexsearch": "npm:^0.7.3" - "@types/mdast": "npm:^4.0.2" flexsearch: "npm:0.7.21" idb-keyval: "npm:^6.2.1" - lib0: "npm:^0.2.87" + lib0: "npm:^0.2.88" merge: "npm:^2.1.1" minimatch: "npm:^9.0.3" - nanoid: "npm:^5.0.3" - remark-gfm: "npm:^4.0.0" - remark-parse: "npm:^11.0.0" - remark-stringify: "npm:^11.0.0" - unified: "npm:^11.0.4" + nanoid: "npm:^5.0.4" y-protocols: "npm:^1.0.6" zod: "npm:^3.22.4" peerDependencies: async-call-rpc: ^6 yjs: ^13 - checksum: fb993b2db0f3720c972a341923a6215fb86d2cb48812cc8fc7aecdb04ba2b904236271d18d796e9a7a5eb1a303dc516f91bc78e76d8fa94af1cee3016fb68f73 + checksum: e4ce26662ac86dafcf03f9ed542a355567c785ca1f6cebf3ca63a51e9a9c5db00153a26a133990202b297a36be393ac56d15033c7c578048413257e07807d1bf languageName: node linkType: hard -"@blocksuite/virgo@npm:0.0.0-20231124123613-7c06e95d-nightly": - version: 0.0.0-20231124123613-7c06e95d-nightly - resolution: "@blocksuite/virgo@npm:0.0.0-20231124123613-7c06e95d-nightly" +"@blocksuite/virgo@npm:0.11.0-nightly-202312070955-2b5bb47": + version: 0.11.0-nightly-202312070955-2b5bb47 + resolution: "@blocksuite/virgo@npm:0.11.0-nightly-202312070955-2b5bb47" dependencies: - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" zod: "npm:^3.22.4" peerDependencies: lit: ^3.0.2 yjs: ^13 - checksum: 02732e5ff5c4206e23f473c467c5b786f646af6b67c44bb38f2faf70525513e9571c9b640ff8a20fba221968c0dfd5d05391761d57b297970b9d77eb3aa21b14 + checksum: a279e498b96c1b44e90bcd8216fe680e895d1aea5e22eb06cf2d4ed23227eaf42db699ce8bd34777dc821e2f727f04db2505f9b92642768268d4b2cd9abcffb5 languageName: node linkType: hard @@ -4546,16 +4568,16 @@ __metadata: languageName: node linkType: hard -"@dnd-kit/modifiers@npm:^6.0.1": - version: 6.0.1 - resolution: "@dnd-kit/modifiers@npm:6.0.1" +"@dnd-kit/modifiers@npm:^7.0.0": + version: 7.0.0 + resolution: "@dnd-kit/modifiers@npm:7.0.0" dependencies: - "@dnd-kit/utilities": "npm:^3.2.1" + "@dnd-kit/utilities": "npm:^3.2.2" tslib: "npm:^2.0.0" peerDependencies: - "@dnd-kit/core": ^6.0.6 + "@dnd-kit/core": ^6.1.0 react: ">=16.8.0" - checksum: fd9b444a6777494bf631fab1fa902172066509c7b060dad2ef53f893590c8a70da9b50c4e5bdba551fe614ff4828cf9f51c8d94f922ff4f275175994f25d0ea2 + checksum: 9ee0b7b86c23c15f6820d76ec398724597abc9d9e31cf58836e7f0b9935e33f9136a60ee9600eb27818447623f07786d4fed3f1d685d9cc6d860d8f6c5354ae3 languageName: node linkType: hard @@ -4572,7 +4594,7 @@ __metadata: languageName: node linkType: hard -"@dnd-kit/utilities@npm:^3.2.1, @dnd-kit/utilities@npm:^3.2.2": +"@dnd-kit/utilities@npm:^3.2.2": version: 3.2.2 resolution: "@dnd-kit/utilities@npm:3.2.2" dependencies: @@ -5228,9 +5250,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/android-arm64@npm:0.19.7" +"@esbuild/android-arm64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/android-arm64@npm:0.19.8" conditions: os=android & cpu=arm64 languageName: node linkType: hard @@ -5256,9 +5278,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/android-arm@npm:0.19.7" +"@esbuild/android-arm@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/android-arm@npm:0.19.8" conditions: os=android & cpu=arm languageName: node linkType: hard @@ -5284,9 +5306,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-x64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/android-x64@npm:0.19.7" +"@esbuild/android-x64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/android-x64@npm:0.19.8" conditions: os=android & cpu=x64 languageName: node linkType: hard @@ -5312,9 +5334,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/darwin-arm64@npm:0.19.7" +"@esbuild/darwin-arm64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/darwin-arm64@npm:0.19.8" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -5340,9 +5362,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/darwin-x64@npm:0.19.7" +"@esbuild/darwin-x64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/darwin-x64@npm:0.19.8" conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -5368,9 +5390,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/freebsd-arm64@npm:0.19.7" +"@esbuild/freebsd-arm64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/freebsd-arm64@npm:0.19.8" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard @@ -5396,9 +5418,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/freebsd-x64@npm:0.19.7" +"@esbuild/freebsd-x64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/freebsd-x64@npm:0.19.8" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard @@ -5424,9 +5446,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-arm64@npm:0.19.7" +"@esbuild/linux-arm64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-arm64@npm:0.19.8" conditions: os=linux & cpu=arm64 languageName: node linkType: hard @@ -5452,9 +5474,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-arm@npm:0.19.7" +"@esbuild/linux-arm@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-arm@npm:0.19.8" conditions: os=linux & cpu=arm languageName: node linkType: hard @@ -5480,9 +5502,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-ia32@npm:0.19.7" +"@esbuild/linux-ia32@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-ia32@npm:0.19.8" conditions: os=linux & cpu=ia32 languageName: node linkType: hard @@ -5508,9 +5530,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-loong64@npm:0.19.7" +"@esbuild/linux-loong64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-loong64@npm:0.19.8" conditions: os=linux & cpu=loong64 languageName: node linkType: hard @@ -5536,9 +5558,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-mips64el@npm:0.19.7" +"@esbuild/linux-mips64el@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-mips64el@npm:0.19.8" conditions: os=linux & cpu=mips64el languageName: node linkType: hard @@ -5564,9 +5586,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-ppc64@npm:0.19.7" +"@esbuild/linux-ppc64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-ppc64@npm:0.19.8" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard @@ -5592,9 +5614,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-riscv64@npm:0.19.7" +"@esbuild/linux-riscv64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-riscv64@npm:0.19.8" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard @@ -5620,9 +5642,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-s390x@npm:0.19.7" +"@esbuild/linux-s390x@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-s390x@npm:0.19.8" conditions: os=linux & cpu=s390x languageName: node linkType: hard @@ -5648,9 +5670,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-x64@npm:0.19.7" +"@esbuild/linux-x64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-x64@npm:0.19.8" conditions: os=linux & cpu=x64 languageName: node linkType: hard @@ -5676,9 +5698,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/netbsd-x64@npm:0.19.7" +"@esbuild/netbsd-x64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/netbsd-x64@npm:0.19.8" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard @@ -5704,9 +5726,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/openbsd-x64@npm:0.19.7" +"@esbuild/openbsd-x64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/openbsd-x64@npm:0.19.8" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard @@ -5732,9 +5754,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/sunos-x64@npm:0.19.7" +"@esbuild/sunos-x64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/sunos-x64@npm:0.19.8" conditions: os=sunos & cpu=x64 languageName: node linkType: hard @@ -5760,9 +5782,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/win32-arm64@npm:0.19.7" +"@esbuild/win32-arm64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/win32-arm64@npm:0.19.8" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard @@ -5788,9 +5810,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/win32-ia32@npm:0.19.7" +"@esbuild/win32-ia32@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/win32-ia32@npm:0.19.8" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard @@ -5816,9 +5838,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/win32-x64@npm:0.19.7" +"@esbuild/win32-x64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/win32-x64@npm:0.19.8" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -7161,6 +7183,15 @@ __metadata: languageName: node linkType: hard +"@ljharb/through@npm:^2.3.11": + version: 2.3.11 + resolution: "@ljharb/through@npm:2.3.11" + dependencies: + call-bind: "npm:^1.0.2" + checksum: 45bcc0681b89bbaf4c814473f3dcb89ba8c6d259becce36d21aafde8959c2fb0e3e640bd735fa00ac68b13e2947b165225bf56a70609f697b20c91a6982bfbd1 + languageName: node + linkType: hard + "@lukeed/csprng@npm:^1.0.0": version: 1.1.0 resolution: "@lukeed/csprng@npm:1.1.0" @@ -7266,6 +7297,25 @@ __metadata: languageName: node linkType: hard +"@mapbox/node-pre-gyp@npm:^1.0.5": + version: 1.0.11 + resolution: "@mapbox/node-pre-gyp@npm:1.0.11" + dependencies: + detect-libc: "npm:^2.0.0" + https-proxy-agent: "npm:^5.0.0" + make-dir: "npm:^3.1.0" + node-fetch: "npm:^2.6.7" + nopt: "npm:^5.0.0" + npmlog: "npm:^5.0.1" + rimraf: "npm:^3.0.2" + semver: "npm:^7.3.5" + tar: "npm:^6.1.11" + bin: + node-pre-gyp: bin/node-pre-gyp + checksum: 59529a2444e44fddb63057152452b00705aa58059079191126c79ac1388ae4565625afa84ed4dd1bf017d1111ab6e47907f7c5192e06d83c9496f2f3e708680a + languageName: node + linkType: hard + "@marsidev/react-turnstile@npm:^0.3.1": version: 0.3.2 resolution: "@marsidev/react-turnstile@npm:0.3.2" @@ -7361,12 +7411,68 @@ __metadata: languageName: node linkType: hard -"@napi-rs/cli@npm:^2.16.5": - version: 2.16.5 - resolution: "@napi-rs/cli@npm:2.16.5" +"@napi-rs/cli@npm:3.0.0-alpha.15": + version: 3.0.0-alpha.15 + resolution: "@napi-rs/cli@npm:3.0.0-alpha.15" + dependencies: + "@napi-rs/cross-toolchain": "npm:^0.0.12" + "@octokit/rest": "npm:^20.0.2" + "@tybys/wasm-util": "npm:0.8.0" + clipanion: "npm:^3.2.1" + colorette: "npm:^2.0.20" + debug: "npm:^4.3.4" + emnapi: "npm:0.44.0" + inquirer: "npm:^9.2.12" + js-yaml: "npm:^4.1.0" + lodash-es: "npm:^4.17.21" + toml: "npm:^3.0.0" + typanion: "npm:^3.14.0" + peerDependencies: + "@emnapi/runtime": 0.44.0 + "@tybys/wasm-util": 0.8.0 + emnapi: 0.44.0 + peerDependenciesMeta: + "@emnapi/runtime": + optional: true + "@tybys/wasm-util": + optional: true + emnapi: + optional: true bin: - napi: scripts/index.js - checksum: 37c16d900887970d080e5a3dd5656463d27e5d4c78f3c50d8382af0b4c51752c705e4713c18b46a83ff54a3c58d5d146aabc82ac7b7bbda8134adb7325bb9fa1 + napi: dist/cli.js + napi-raw: cli.mjs + checksum: e4223d226bf4412241a130a559867c1d0ed74b2ad2068853db31eb385520b2b4ebaf886900741713d8c5a840c50765d3069e2361296a819ffdfcab0a020ef700 + languageName: node + linkType: hard + +"@napi-rs/cross-toolchain@npm:^0.0.12": + version: 0.0.12 + resolution: "@napi-rs/cross-toolchain@npm:0.0.12" + dependencies: + "@napi-rs/lzma": "npm:^1.2.1" + "@napi-rs/tar": "npm:^0.0.1" + debug: "npm:^4.3.4" + peerDependencies: + "@napi-rs/cross-toolchain-arm64-target-aarch64": ^0.0.12 + "@napi-rs/cross-toolchain-arm64-target-armv7": ^0.0.12 + "@napi-rs/cross-toolchain-arm64-target-x86_64": ^0.0.12 + "@napi-rs/cross-toolchain-x64-target-aarch64": ^0.0.12 + "@napi-rs/cross-toolchain-x64-target-armv7": ^0.0.12 + "@napi-rs/cross-toolchain-x64-target-x86_64": ^0.0.12 + peerDependenciesMeta: + "@napi-rs/cross-toolchain-arm64-target-aarch64": + optional: true + "@napi-rs/cross-toolchain-arm64-target-armv7": + optional: true + "@napi-rs/cross-toolchain-arm64-target-x86_64": + optional: true + "@napi-rs/cross-toolchain-x64-target-aarch64": + optional: true + "@napi-rs/cross-toolchain-x64-target-armv7": + optional: true + "@napi-rs/cross-toolchain-x64-target-x86_64": + optional: true + checksum: 7631a0d72f4264ab46b8792519284aa9f8dd6bf922a27dd598c4eec398c83730950454a5f36ceb502fe7c018e89d99f2186ed16416d21e35f6d120c2a6ea48fc languageName: node linkType: hard @@ -7499,6 +7605,145 @@ __metadata: languageName: node linkType: hard +"@napi-rs/lzma-android-arm-eabi@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-android-arm-eabi@npm:1.2.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/lzma-android-arm64@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-android-arm64@npm:1.2.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/lzma-darwin-arm64@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-darwin-arm64@npm:1.2.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/lzma-darwin-x64@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-darwin-x64@npm:1.2.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/lzma-freebsd-x64@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-freebsd-x64@npm:1.2.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-arm-gnueabihf@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-linux-arm-gnueabihf@npm:1.2.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-arm64-gnu@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-linux-arm64-gnu@npm:1.2.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-arm64-musl@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-linux-arm64-musl@npm:1.2.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-x64-gnu@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-linux-x64-gnu@npm:1.2.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-x64-musl@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-linux-x64-musl@npm:1.2.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/lzma-win32-arm64-msvc@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-win32-arm64-msvc@npm:1.2.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/lzma-win32-ia32-msvc@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-win32-ia32-msvc@npm:1.2.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@napi-rs/lzma-win32-x64-msvc@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-win32-x64-msvc@npm:1.2.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/lzma@npm:^1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma@npm:1.2.1" + dependencies: + "@napi-rs/lzma-android-arm-eabi": "npm:1.2.1" + "@napi-rs/lzma-android-arm64": "npm:1.2.1" + "@napi-rs/lzma-darwin-arm64": "npm:1.2.1" + "@napi-rs/lzma-darwin-x64": "npm:1.2.1" + "@napi-rs/lzma-freebsd-x64": "npm:1.2.1" + "@napi-rs/lzma-linux-arm-gnueabihf": "npm:1.2.1" + "@napi-rs/lzma-linux-arm64-gnu": "npm:1.2.1" + "@napi-rs/lzma-linux-arm64-musl": "npm:1.2.1" + "@napi-rs/lzma-linux-x64-gnu": "npm:1.2.1" + "@napi-rs/lzma-linux-x64-musl": "npm:1.2.1" + "@napi-rs/lzma-win32-arm64-msvc": "npm:1.2.1" + "@napi-rs/lzma-win32-ia32-msvc": "npm:1.2.1" + "@napi-rs/lzma-win32-x64-msvc": "npm:1.2.1" + dependenciesMeta: + "@napi-rs/lzma-android-arm-eabi": + optional: true + "@napi-rs/lzma-android-arm64": + optional: true + "@napi-rs/lzma-darwin-arm64": + optional: true + "@napi-rs/lzma-darwin-x64": + optional: true + "@napi-rs/lzma-freebsd-x64": + optional: true + "@napi-rs/lzma-linux-arm-gnueabihf": + optional: true + "@napi-rs/lzma-linux-arm64-gnu": + optional: true + "@napi-rs/lzma-linux-arm64-musl": + optional: true + "@napi-rs/lzma-linux-x64-gnu": + optional: true + "@napi-rs/lzma-linux-x64-musl": + optional: true + "@napi-rs/lzma-win32-arm64-msvc": + optional: true + "@napi-rs/lzma-win32-ia32-msvc": + optional: true + "@napi-rs/lzma-win32-x64-msvc": + optional: true + checksum: 5e6d9bee5e359227f7bb3616b3b2a8b064bde0f5aebf4b885df23954cc2f35b1ae44595405733e54826ab9896c02309eac5ff2b340592b698ef2aa135a44288c + languageName: node + linkType: hard + "@napi-rs/magic-string-android-arm-eabi@npm:0.3.4": version: 0.3.4 resolution: "@napi-rs/magic-string-android-arm-eabi@npm:0.3.4" @@ -7638,6 +7883,145 @@ __metadata: languageName: node linkType: hard +"@napi-rs/tar-android-arm-eabi@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-android-arm-eabi@npm:0.0.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/tar-android-arm64@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-android-arm64@npm:0.0.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/tar-darwin-arm64@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-darwin-arm64@npm:0.0.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/tar-darwin-x64@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-darwin-x64@npm:0.0.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/tar-freebsd-x64@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-freebsd-x64@npm:0.0.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/tar-linux-arm-gnueabihf@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-linux-arm-gnueabihf@npm:0.0.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/tar-linux-arm64-gnu@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-linux-arm64-gnu@npm:0.0.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/tar-linux-arm64-musl@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-linux-arm64-musl@npm:0.0.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/tar-linux-x64-gnu@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-linux-x64-gnu@npm:0.0.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/tar-linux-x64-musl@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-linux-x64-musl@npm:0.0.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/tar-win32-arm64-msvc@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-win32-arm64-msvc@npm:0.0.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/tar-win32-ia32-msvc@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-win32-ia32-msvc@npm:0.0.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@napi-rs/tar-win32-x64-msvc@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-win32-x64-msvc@npm:0.0.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/tar@npm:^0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar@npm:0.0.1" + dependencies: + "@napi-rs/tar-android-arm-eabi": "npm:0.0.1" + "@napi-rs/tar-android-arm64": "npm:0.0.1" + "@napi-rs/tar-darwin-arm64": "npm:0.0.1" + "@napi-rs/tar-darwin-x64": "npm:0.0.1" + "@napi-rs/tar-freebsd-x64": "npm:0.0.1" + "@napi-rs/tar-linux-arm-gnueabihf": "npm:0.0.1" + "@napi-rs/tar-linux-arm64-gnu": "npm:0.0.1" + "@napi-rs/tar-linux-arm64-musl": "npm:0.0.1" + "@napi-rs/tar-linux-x64-gnu": "npm:0.0.1" + "@napi-rs/tar-linux-x64-musl": "npm:0.0.1" + "@napi-rs/tar-win32-arm64-msvc": "npm:0.0.1" + "@napi-rs/tar-win32-ia32-msvc": "npm:0.0.1" + "@napi-rs/tar-win32-x64-msvc": "npm:0.0.1" + dependenciesMeta: + "@napi-rs/tar-android-arm-eabi": + optional: true + "@napi-rs/tar-android-arm64": + optional: true + "@napi-rs/tar-darwin-arm64": + optional: true + "@napi-rs/tar-darwin-x64": + optional: true + "@napi-rs/tar-freebsd-x64": + optional: true + "@napi-rs/tar-linux-arm-gnueabihf": + optional: true + "@napi-rs/tar-linux-arm64-gnu": + optional: true + "@napi-rs/tar-linux-arm64-musl": + optional: true + "@napi-rs/tar-linux-x64-gnu": + optional: true + "@napi-rs/tar-linux-x64-musl": + optional: true + "@napi-rs/tar-win32-arm64-msvc": + optional: true + "@napi-rs/tar-win32-ia32-msvc": + optional: true + "@napi-rs/tar-win32-x64-msvc": + optional: true + checksum: b0a80a08641c05a2fa035cc8e8e642fad0c25e781f292fc541daecedde3921aa96c6ae7785aea733ab6a08983e740dff1b7eefc36546ad939fdaa23e0fab5d70 + languageName: node + linkType: hard + "@napi-rs/xattr-android-arm-eabi@npm:1.0.1": version: 1.0.1 resolution: "@napi-rs/xattr-android-arm-eabi@npm:1.0.1" @@ -8685,6 +9069,131 @@ __metadata: languageName: node linkType: hard +"@octokit/auth-token@npm:^4.0.0": + version: 4.0.0 + resolution: "@octokit/auth-token@npm:4.0.0" + checksum: 60e42701e341d700f73c518c7a35675d36d79fa9d5e838cc3ade96d147e49f5ba74db2e07b2337c2b95aaa540aa42088116df2122daa25633f9e70a2c8785c44 + languageName: node + linkType: hard + +"@octokit/core@npm:^5.0.0": + version: 5.0.2 + resolution: "@octokit/core@npm:5.0.2" + dependencies: + "@octokit/auth-token": "npm:^4.0.0" + "@octokit/graphql": "npm:^7.0.0" + "@octokit/request": "npm:^8.0.2" + "@octokit/request-error": "npm:^5.0.0" + "@octokit/types": "npm:^12.0.0" + before-after-hook: "npm:^2.2.0" + universal-user-agent: "npm:^6.0.0" + checksum: bb991f88793fab043c4c09f9441432596fe0e6448caf42cd2209f52c1f26807418be488ad2cea7a8293e58e79e5c0019f38dda46e8cf96af5e89e43cca37ec3e + languageName: node + linkType: hard + +"@octokit/endpoint@npm:^9.0.0": + version: 9.0.4 + resolution: "@octokit/endpoint@npm:9.0.4" + dependencies: + "@octokit/types": "npm:^12.0.0" + universal-user-agent: "npm:^6.0.0" + checksum: 7df35c96f2b5628fe5b3f44a72614be9b439779c06b4dd1bb72283b3cb2ea53e59e1f9a108798efe5404b6856f4380a4c5be12d93255d854f0683cd6e22f3a27 + languageName: node + linkType: hard + +"@octokit/graphql@npm:^7.0.0": + version: 7.0.2 + resolution: "@octokit/graphql@npm:7.0.2" + dependencies: + "@octokit/request": "npm:^8.0.1" + "@octokit/types": "npm:^12.0.0" + universal-user-agent: "npm:^6.0.0" + checksum: f5dcc51fed5304f65dab83fcea4c2a569107d3b71e8d084199dc44f0d0cfc852c9e1f341b06ae66601f9da4af3aad416b0c62dcd0567ac7568f072d8d90d502e + languageName: node + linkType: hard + +"@octokit/openapi-types@npm:^19.0.2": + version: 19.1.0 + resolution: "@octokit/openapi-types@npm:19.1.0" + checksum: 3abedc09baa91bb4de00a2b82bf519401c2b6388913b7caa98541002c9e9612eba8256926323b1e40782ac700309a3373bb8c13520af325cef1accc40cb4566b + languageName: node + linkType: hard + +"@octokit/plugin-paginate-rest@npm:^9.0.0": + version: 9.1.4 + resolution: "@octokit/plugin-paginate-rest@npm:9.1.4" + dependencies: + "@octokit/types": "npm:^12.3.0" + peerDependencies: + "@octokit/core": ">=5" + checksum: 1573934e0c2a99e3512cd21a0dbb17f6ca1d5faffdffb499cb80519b1219da4d56f814a30c68c0961fcccf152895bdced478709195f53a6e4c32e71a3235f888 + languageName: node + linkType: hard + +"@octokit/plugin-request-log@npm:^4.0.0": + version: 4.0.0 + resolution: "@octokit/plugin-request-log@npm:4.0.0" + peerDependencies: + "@octokit/core": ">=5" + checksum: 2a8a6619640942092009a9248ceeb163ce01c978e2d7b2a7eb8686bd09a04b783c4cd9071eebb16652d233587abcde449a02ce4feabc652f0a171615fb3e9946 + languageName: node + linkType: hard + +"@octokit/plugin-rest-endpoint-methods@npm:^10.0.0": + version: 10.2.0 + resolution: "@octokit/plugin-rest-endpoint-methods@npm:10.2.0" + dependencies: + "@octokit/types": "npm:^12.3.0" + peerDependencies: + "@octokit/core": ">=5" + checksum: 0f8ca73b3e582b366b400278f19df6309f263efa3809a9d6ba613063e7a26f16d6f8d69c413bf9b23c2431ad4c795e4e06a43717b6acc1367186fb55347cfb69 + languageName: node + linkType: hard + +"@octokit/request-error@npm:^5.0.0": + version: 5.0.1 + resolution: "@octokit/request-error@npm:5.0.1" + dependencies: + "@octokit/types": "npm:^12.0.0" + deprecation: "npm:^2.0.0" + once: "npm:^1.4.0" + checksum: a21a4614c46cb173e4ba73fa048576204f1ddc541dee3e7c938ef36088566e3b25e04ca1f96f375ec2e3cc29b7ba970b3b078a89a20bc50cdcdbed879db94573 + languageName: node + linkType: hard + +"@octokit/request@npm:^8.0.1, @octokit/request@npm:^8.0.2": + version: 8.1.6 + resolution: "@octokit/request@npm:8.1.6" + dependencies: + "@octokit/endpoint": "npm:^9.0.0" + "@octokit/request-error": "npm:^5.0.0" + "@octokit/types": "npm:^12.0.0" + universal-user-agent: "npm:^6.0.0" + checksum: aebea1c33d607d23c70f663cd5f8279a8bd932ab77b4ca5cca3b33968a347b4adb47476c886086f3a9aa1acefab3b79adac78ee7aa2dacd67eb1f2a05e272618 + languageName: node + linkType: hard + +"@octokit/rest@npm:^20.0.2": + version: 20.0.2 + resolution: "@octokit/rest@npm:20.0.2" + dependencies: + "@octokit/core": "npm:^5.0.0" + "@octokit/plugin-paginate-rest": "npm:^9.0.0" + "@octokit/plugin-request-log": "npm:^4.0.0" + "@octokit/plugin-rest-endpoint-methods": "npm:^10.0.0" + checksum: 527e1806ca274209a2a7daa485010dafb2ebb6c9b0b44c1d33a8f1f16f10e54a96386a4f642dc416160842a4b367d3953d27f8b827b9a94600709d2ac5e95d21 + languageName: node + linkType: hard + +"@octokit/types@npm:^12.0.0, @octokit/types@npm:^12.3.0": + version: 12.3.0 + resolution: "@octokit/types@npm:12.3.0" + dependencies: + "@octokit/openapi-types": "npm:^19.0.2" + checksum: ab78fd25490f995f79e341b00c375bade64eedb44d4c76fa3da85961003ba1efcac3cf168ea221bf4f9f5761afe91738412737acf30f1f41f3f2dbad14b872e4 + languageName: node + linkType: hard + "@open-draft/deferred-promise@npm:^2.2.0": version: 2.2.0 resolution: "@open-draft/deferred-promise@npm:2.2.0" @@ -8830,15 +9339,15 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/host-metrics@npm:^0.33.2": - version: 0.33.2 - resolution: "@opentelemetry/host-metrics@npm:0.33.2" +"@opentelemetry/host-metrics@npm:^0.34.0": + version: 0.34.0 + resolution: "@opentelemetry/host-metrics@npm:0.34.0" dependencies: "@opentelemetry/sdk-metrics": "npm:^1.8.0" systeminformation: "npm:^5.0.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 35140ecb2c4e97ab49873411a9e81a23238cf392112212f936f238091a6572c0802d6364ccea428cb7f0595cb28f4cbafbe6fa5c23510ebb1243cf7f398871e5 + checksum: 54f6a6c2f2ab8f236dc5f0d32d71771339f211c8e492afad45394e15067178491181823aa66ebe43c4d832fa9f6785b7fdd5fad515c92a6785c6ee1f81df2a38 languageName: node linkType: hard @@ -8867,9 +9376,9 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-ioredis@npm:^0.35.3": - version: 0.35.3 - resolution: "@opentelemetry/instrumentation-ioredis@npm:0.35.3" +"@opentelemetry/instrumentation-ioredis@npm:^0.36.0": + version: 0.36.0 + resolution: "@opentelemetry/instrumentation-ioredis@npm:0.36.0" dependencies: "@opentelemetry/instrumentation": "npm:^0.45.1" "@opentelemetry/redis-common": "npm:^0.36.1" @@ -8877,7 +9386,7 @@ __metadata: "@types/ioredis4": "npm:@types/ioredis@^4.28.10" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 25cb843ef0315751cae577fa6cf275d0eaae1d7bffa2c723e25ad140e9a505cfef0297db12069b5ed685e6907392e3b048c016691e48a9c49a66001ff16cd9b8 + checksum: c9f07ad9d51eb5aabadb3b190b6810ad28ab58a15ec371ca6e910739ae922c86658bc90127b466c038858f3dd206f6a5e2f51a025d10f92a7f28ee81055b6390 languageName: node linkType: hard @@ -9726,7 +10235,7 @@ __metadata: languageName: node linkType: hard -"@radix-ui/react-dialog@npm:^1.0.4": +"@radix-ui/react-dialog@npm:^1.0.4, @radix-ui/react-dialog@npm:^1.0.5": version: 1.0.5 resolution: "@radix-ui/react-dialog@npm:1.0.5" dependencies: @@ -9833,7 +10342,7 @@ __metadata: languageName: node linkType: hard -"@radix-ui/react-dropdown-menu@npm:^2.0.5": +"@radix-ui/react-dropdown-menu@npm:^2.0.6": version: 2.0.6 resolution: "@radix-ui/react-dropdown-menu@npm:2.0.6" dependencies: @@ -9971,7 +10480,7 @@ __metadata: languageName: node linkType: hard -"@radix-ui/react-popover@npm:^1.0.6, @radix-ui/react-popover@npm:^1.0.7": +"@radix-ui/react-popover@npm:^1.0.7": version: 1.0.7 resolution: "@radix-ui/react-popover@npm:1.0.7" dependencies: @@ -10510,7 +11019,7 @@ __metadata: languageName: node linkType: hard -"@radix-ui/react-tooltip@npm:^1.0.6": +"@radix-ui/react-tooltip@npm:^1.0.7": version: 1.0.7 resolution: "@radix-ui/react-tooltip@npm:1.0.7" dependencies: @@ -10783,6 +11292,16 @@ __metadata: languageName: node linkType: hard +"@rollup/pluginutils@npm:^4.0.0": + version: 4.2.1 + resolution: "@rollup/pluginutils@npm:4.2.1" + dependencies: + estree-walker: "npm:^2.0.1" + picomatch: "npm:^2.2.2" + checksum: 503a6f0a449e11a2873ac66cfdfb9a3a0b77ffa84c5cad631f5e4bc1063c850710e8d5cd5dab52477c0d66cda2ec719865726dbe753318cd640bab3fff7ca476 + languageName: node + linkType: hard + "@rollup/pluginutils@npm:^5.0.2, @rollup/pluginutils@npm:^5.0.5": version: 5.0.5 resolution: "@rollup/pluginutils@npm:5.0.5" @@ -10799,6 +11318,90 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm-eabi@npm:4.6.1": + version: 4.6.1 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.6.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-android-arm64@npm:4.6.1": + version: 4.6.1 + resolution: "@rollup/rollup-android-arm64@npm:4.6.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-arm64@npm:4.6.1": + version: 4.6.1 + resolution: "@rollup/rollup-darwin-arm64@npm:4.6.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-x64@npm:4.6.1": + version: 4.6.1 + resolution: "@rollup/rollup-darwin-x64@npm:4.6.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-gnueabihf@npm:4.6.1": + version: 4.6.1 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.6.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-gnu@npm:4.6.1": + version: 4.6.1 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.6.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-musl@npm:4.6.1": + version: 4.6.1 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.6.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-gnu@npm:4.6.1": + version: 4.6.1 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.6.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-musl@npm:4.6.1": + version: 4.6.1 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.6.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-win32-arm64-msvc@npm:4.6.1": + version: 4.6.1 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.6.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-ia32-msvc@npm:4.6.1": + version: 4.6.1 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.6.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-msvc@npm:4.6.1": + version: 4.6.1 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.6.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@rushstack/node-core-library@npm:3.61.0": version: 3.61.0 resolution: "@rushstack/node-core-library@npm:3.61.0" @@ -11067,6 +11670,13 @@ __metadata: languageName: node linkType: hard +"@sindresorhus/merge-streams@npm:^1.0.0": + version: 1.0.0 + resolution: "@sindresorhus/merge-streams@npm:1.0.0" + checksum: 453c2a28164113a5ec4fd23ba636e291a4112f6ee9e91cd5476b9a96e0fc9ee5ff40d405fe81bbf284c9773b7ed718a3a0f31df7895a0efd413b1f9775d154fe + languageName: node + linkType: hard + "@sinonjs/commons@npm:^2.0.0": version: 2.0.0 resolution: "@sinonjs/commons@npm:2.0.0" @@ -11900,6 +12510,17 @@ __metadata: languageName: node linkType: hard +"@storybook/addon-mdx-gfm@npm:^7.5.3": + version: 7.5.3 + resolution: "@storybook/addon-mdx-gfm@npm:7.5.3" + dependencies: + "@storybook/node-logger": "npm:7.5.3" + remark-gfm: "npm:^3.0.1" + ts-dedent: "npm:^2.0.0" + checksum: 6814b9c042047e441353a0f7b35138bc83de8e8be0cbc78740ff926ae1128c546b5143c39b11872fbe7e018dd3dfd838bfa198f32f63e22d72655a7b1e3091fa + languageName: node + linkType: hard + "@storybook/addon-measure@npm:7.5.3": version: 7.5.3 resolution: "@storybook/addon-measure@npm:7.5.3" @@ -13115,45 +13736,29 @@ __metadata: languageName: node linkType: hard -"@toeverything/components@npm:^0.0.46": - version: 0.0.46 - resolution: "@toeverything/components@npm:0.0.46" - dependencies: - "@blocksuite/icons": "npm:^2.1.33" - "@radix-ui/react-dialog": "npm:^1.0.4" - "@radix-ui/react-dropdown-menu": "npm:^2.0.5" - "@radix-ui/react-popover": "npm:^1.0.6" - "@radix-ui/react-tooltip": "npm:^1.0.6" - peerDependencies: - "@radix-ui/react-avatar": ^1 - clsx: ^2 - react: ^18 - react-dom: ^18 - checksum: 1e74a620d82bc6f6c318ccac35a2f4f86d5e3b97761e798d0ad3f3b31a74f377e402338ad00a2af54c62dda7d88c231cc07b53f2aa9c9bff1de5088659e1c712 - languageName: node - linkType: hard - "@toeverything/hooks@workspace:*, @toeverything/hooks@workspace:packages/frontend/hooks": version: 0.0.0-use.local resolution: "@toeverything/hooks@workspace:packages/frontend/hooks" dependencies: "@affine/debug": "workspace:*" "@affine/env": "workspace:*" - "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/block-std": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/blocks": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/lit": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/presets": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47" "@testing-library/react": "npm:^14.0.0" + "@toeverything/infra": "workspace:*" "@types/image-blob-reduce": "npm:^4.1.3" "@types/lodash.debounce": "npm:^4.0.7" fake-indexeddb: "npm:^5.0.0" foxact: "npm:^0.2.20" image-blob-reduce: "npm:^4.1.0" jotai: "npm:^2.5.1" + jotai-effect: "npm:^0.2.3" lodash.debounce: "npm:^4.0.8" - p-queue: "npm:^7.4.1" + p-queue: "npm:^8.0.0" react: "npm:18.2.0" rxjs: "npm:^7.8.1" swr: "npm:2.2.4" @@ -13163,8 +13768,8 @@ __metadata: peerDependencies: "@blocksuite/block-std": "*" "@blocksuite/blocks": "*" - "@blocksuite/editor": "*" "@blocksuite/global": "*" + "@blocksuite/presets": "*" "@blocksuite/store": "*" y-provider: "workspace:*" peerDependenciesMeta: @@ -13174,10 +13779,10 @@ __metadata: optional: true "@blocksuite/blocks": optional: true - "@blocksuite/editor": - optional: true "@blocksuite/global": optional: true + "@blocksuite/presets": + optional: true "@blocksuite/store": optional: true y-provider: @@ -13190,13 +13795,15 @@ __metadata: resolution: "@toeverything/infra@workspace:packages/common/infra" dependencies: "@affine-test/fixtures": "workspace:*" + "@affine/debug": "workspace:*" + "@affine/env": "workspace:*" "@affine/sdk": "workspace:*" "@affine/templates": "workspace:*" - "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/blocks": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/global": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/lit": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/presets": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47" "@testing-library/react": "npm:^14.0.0" async-call-rpc: "npm:^6.3.1" electron: "link:../../frontend/electron/node_modules/electron" @@ -13206,14 +13813,14 @@ __metadata: react: "npm:^18.2.0" rxjs: "npm:^7.8.1" tinykeys: "npm:^2.1.0" - vite: "npm:^4.4.11" + vite: "npm:^5.0.6" vite-plugin-dts: "npm:3.6.0" vitest: "npm:0.34.6" yjs: "npm:^13.6.10" zod: "npm:^3.22.4" peerDependencies: "@affine/templates": "*" - "@blocksuite/editor": "*" + "@blocksuite/presets": "*" async-call-rpc: "*" electron: "*" react: "*" @@ -13221,7 +13828,7 @@ __metadata: peerDependenciesMeta: "@affine/templates": optional: true - "@blocksuite/editor": + "@blocksuite/presets": optional: true async-call-rpc: optional: true @@ -13234,7 +13841,7 @@ __metadata: languageName: unknown linkType: soft -"@toeverything/theme@npm:^0.7.20, @toeverything/theme@npm:^0.7.24, @toeverything/theme@npm:^0.7.26": +"@toeverything/theme@npm:^0.7.20, @toeverything/theme@npm:^0.7.24, @toeverything/theme@npm:^0.7.27": version: 0.7.27 resolution: "@toeverything/theme@npm:0.7.27" checksum: 9349d30256d33b5528441dbca09387819225449f97549585b92b33e354d7fbb3d0e5e35001c89b8a7e3033d8f51d3aee75ef9edaaf14fb7dd717dd66bfe96009 @@ -13245,12 +13852,12 @@ __metadata: version: 0.0.0-use.local resolution: "@toeverything/y-indexeddb@workspace:packages/common/y-indexeddb" dependencies: - "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" - "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/blocks": "npm:0.11.0-nightly-202312070955-2b5bb47" + "@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47" fake-indexeddb: "npm:^5.0.0" - idb: "npm:^7.1.1" + idb: "npm:^8.0.0" nanoid: "npm:^5.0.3" - vite: "npm:^4.4.11" + vite: "npm:^5.0.6" vite-plugin-dts: "npm:3.6.0" vitest: "npm:0.34.6" y-indexeddb: "npm:^9.0.11" @@ -13320,6 +13927,15 @@ __metadata: languageName: node linkType: hard +"@tybys/wasm-util@npm:0.8.0": + version: 0.8.0 + resolution: "@tybys/wasm-util@npm:0.8.0" + dependencies: + tslib: "npm:^2.4.0" + checksum: 7fa650acfc3fdcaf103f0f7acdf6f4cf67125632eb0f91c92d826375e262547d69de28838cf7a4b69fb665d2fb6f153558528fa1fddd5615a5aa838a87ed7d16 + languageName: node + linkType: hard + "@types/accepts@npm:*": version: 1.3.7 resolution: "@types/accepts@npm:1.3.7" @@ -13734,6 +14350,15 @@ __metadata: languageName: node linkType: hard +"@types/hast@npm:^3.0.0, @types/hast@npm:^3.0.3": + version: 3.0.3 + resolution: "@types/hast@npm:3.0.3" + dependencies: + "@types/unist": "npm:*" + checksum: cf380cb351215847a598b06c10c0139a694fbb9a5cca27e7a836df4c9d616873ff5b01326530907b5a95b6a4b8fc928bcecb46424cc6f9bd1f53ba377f190d86 + languageName: node + linkType: hard + "@types/html-minifier-terser@npm:^6.0.0": version: 6.1.0 resolution: "@types/html-minifier-terser@npm:6.1.0" @@ -13959,7 +14584,16 @@ __metadata: languageName: node linkType: hard -"@types/mdast@npm:^4.0.0, @types/mdast@npm:^4.0.2": +"@types/mdast@npm:^3.0.0": + version: 3.0.15 + resolution: "@types/mdast@npm:3.0.15" + dependencies: + "@types/unist": "npm:^2" + checksum: 050a5c1383928b2688dd145382a22535e2af87dc3fd592c843abb7851bcc99893a1ee0f63be19fc4e89779387ec26a57486cfb425b016c0b2a98a17fc4a1e8b3 + languageName: node + linkType: hard + +"@types/mdast@npm:^4.0.0, @types/mdast@npm:^4.0.3": version: 4.0.3 resolution: "@types/mdast@npm:4.0.3" dependencies: @@ -14361,7 +14995,7 @@ __metadata: languageName: node linkType: hard -"@types/webfontloader@npm:^1.6.36": +"@types/webfontloader@npm:^1.6.38": version: 1.6.38 resolution: "@types/webfontloader@npm:1.6.38" checksum: 2be3d1e43837ddeea8ea0390d0952fc735abc6d713b2b87843ad0a6d4acd6628b2ce8f0280ec81d48144a23e358d1bd22c16e4717cc8d67f75c1cd1ddc2d0f27 @@ -14540,7 +15174,7 @@ __metadata: languageName: node linkType: hard -"@ungap/structured-clone@npm:^1.2.0": +"@ungap/structured-clone@npm:^1.0.0, @ungap/structured-clone@npm:^1.2.0": version: 1.2.0 resolution: "@ungap/structured-clone@npm:1.2.0" checksum: c6fe89a505e513a7592e1438280db1c075764793a2397877ff1351721fe8792a966a5359769e30242b3cd023f2efb9e63ca2ca88019d73b564488cc20e3eab12 @@ -14651,6 +15285,27 @@ __metadata: languageName: node linkType: hard +"@vercel/nft@npm:^0.24.4": + version: 0.24.4 + resolution: "@vercel/nft@npm:0.24.4" + dependencies: + "@mapbox/node-pre-gyp": "npm:^1.0.5" + "@rollup/pluginutils": "npm:^4.0.0" + acorn: "npm:^8.6.0" + async-sema: "npm:^3.1.1" + bindings: "npm:^1.4.0" + estree-walker: "npm:2.0.2" + glob: "npm:^7.1.3" + graceful-fs: "npm:^4.2.9" + micromatch: "npm:^4.0.2" + node-gyp-build: "npm:^4.2.2" + resolve-from: "npm:^5.0.0" + bin: + nft: out/cli.js + checksum: e6614ff91854fd3989e1116dbedca46c9937f1f2086fc4acf8d52d42e1cc13c192fb81d9f6efbdbe19f706565c3a09952e47de96df999be96d6b8a9ff088c41c + languageName: node + linkType: hard + "@vitejs/plugin-react-swc@npm:^3.5.0": version: 3.5.0 resolution: "@vitejs/plugin-react-swc@npm:3.5.0" @@ -15389,7 +16044,7 @@ __metadata: languageName: node linkType: hard -"acorn-walk@npm:^8.1.1, acorn-walk@npm:^8.2.0": +"acorn-walk@npm:^8.1.1, acorn-walk@npm:^8.2.0, acorn-walk@npm:^8.3.0": version: 8.3.0 resolution: "acorn-walk@npm:8.3.0" checksum: 7673f342db939adc16ac3596c374a56be33e6ef84e01dfb3a0b50cc87cf9b8e46d84c337dcd7d5644f75bf219ad5a36bf33795e9f1af15298e6bceacf46c5f1f @@ -15405,7 +16060,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.10.0, acorn@npm:^8.11.2, acorn@npm:^8.4.1, acorn@npm:^8.7.1, acorn@npm:^8.8.0, acorn@npm:^8.8.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": +"acorn@npm:^8.10.0, acorn@npm:^8.11.2, acorn@npm:^8.4.1, acorn@npm:^8.6.0, acorn@npm:^8.7.1, acorn@npm:^8.8.0, acorn@npm:^8.8.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": version: 8.11.2 resolution: "acorn@npm:8.11.2" bin: @@ -15465,16 +16120,6 @@ __metadata: languageName: node linkType: hard -"aggregate-error@npm:^4.0.0": - version: 4.0.1 - resolution: "aggregate-error@npm:4.0.1" - dependencies: - clean-stack: "npm:^4.0.0" - indent-string: "npm:^5.0.0" - checksum: bb3ffdfd13447800fff237c2cba752c59868ee669104bb995dfbbe0b8320e967d679e683dabb640feb32e4882d60258165cde0baafc4cd467cc7d275a13ad6b5 - languageName: node - linkType: hard - "ajv-formats@npm:^2.1.1": version: 2.1.1 resolution: "ajv-formats@npm:2.1.1" @@ -15561,7 +16206,7 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0": +"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0, ansi-escapes@npm:^4.3.2": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" dependencies: @@ -15725,6 +16370,16 @@ __metadata: languageName: node linkType: hard +"are-we-there-yet@npm:^2.0.0": + version: 2.0.0 + resolution: "are-we-there-yet@npm:2.0.0" + dependencies: + delegates: "npm:^1.0.0" + readable-stream: "npm:^3.6.0" + checksum: ea6f47d14fc33ae9cbea3e686eeca021d9d7b9db83a306010dd04ad5f2c8b7675291b127d3fcbfcbd8fec26e47b3324ad5b469a6cc3733a582f2fe4e12fc6756 + languageName: node + linkType: hard + "are-we-there-yet@npm:^3.0.0": version: 3.0.1 resolution: "are-we-there-yet@npm:3.0.1" @@ -16004,6 +16659,13 @@ __metadata: languageName: node linkType: hard +"async-sema@npm:^3.1.1": + version: 3.1.1 + resolution: "async-sema@npm:3.1.1" + checksum: ee0225c2e7b72ae76d66157499f61a881a050824019edc54fa6ec789313076790729557556fbbe237af0083173c66fb2edf1c9cc45c522c5f846b66c0a94ddb3 + languageName: node + linkType: hard + "async-validator@npm:^4.2.5": version: 4.2.5 resolution: "async-validator@npm:4.2.5" @@ -16053,49 +16715,46 @@ __metadata: languageName: node linkType: hard -"ava@npm:^5.3.1": - version: 5.3.1 - resolution: "ava@npm:5.3.1" +"ava@npm:^6.0.0": + version: 6.0.0 + resolution: "ava@npm:6.0.0" dependencies: - acorn: "npm:^8.8.2" - acorn-walk: "npm:^8.2.0" + "@vercel/nft": "npm:^0.24.4" + acorn: "npm:^8.11.2" + acorn-walk: "npm:^8.3.0" ansi-styles: "npm:^6.2.1" arrgv: "npm:^1.0.2" arrify: "npm:^3.0.0" - callsites: "npm:^4.0.0" - cbor: "npm:^8.1.0" - chalk: "npm:^5.2.0" - chokidar: "npm:^3.5.3" + callsites: "npm:^4.1.0" + cbor: "npm:^9.0.1" + chalk: "npm:^5.3.0" chunkd: "npm:^2.0.1" - ci-info: "npm:^3.8.0" + ci-info: "npm:^4.0.0" ci-parallel-vars: "npm:^1.0.1" - clean-yaml-object: "npm:^0.1.0" - cli-truncate: "npm:^3.1.0" + cli-truncate: "npm:^4.0.0" code-excerpt: "npm:^4.0.0" common-path-prefix: "npm:^3.0.0" concordance: "npm:^5.0.4" currently-unhandled: "npm:^0.4.1" debug: "npm:^4.3.4" emittery: "npm:^1.0.1" - figures: "npm:^5.0.0" - globby: "npm:^13.1.4" + figures: "npm:^6.0.1" + globby: "npm:^14.0.0" ignore-by-default: "npm:^2.1.0" indent-string: "npm:^5.0.0" - is-error: "npm:^2.2.2" is-plain-object: "npm:^5.0.0" is-promise: "npm:^4.0.0" matcher: "npm:^5.0.0" - mem: "npm:^9.0.2" + memoize: "npm:^10.0.0" ms: "npm:^2.1.3" - p-event: "npm:^5.0.1" - p-map: "npm:^5.5.0" - picomatch: "npm:^2.3.1" - pkg-conf: "npm:^4.0.0" + p-map: "npm:^6.0.0" + package-config: "npm:^5.0.0" + picomatch: "npm:^3.0.1" plur: "npm:^5.1.0" pretty-ms: "npm:^8.0.0" resolve-cwd: "npm:^3.0.0" stack-utils: "npm:^2.0.6" - strip-ansi: "npm:^7.0.1" + strip-ansi: "npm:^7.1.0" supertap: "npm:^3.0.1" temp-dir: "npm:^3.0.0" write-file-atomic: "npm:^5.0.1" @@ -16107,7 +16766,7 @@ __metadata: optional: true bin: ava: entrypoints/cli.mjs - checksum: 4b59259471b5c9b5e8952db65c5231ce0d906f339d4afa9c9f19927a2e9db1e1b7d4e414082381a3554e6607c36df60762f7cc26ff501a215bc6bac939dc9e77 + checksum: 95b85827b1df666f9159371b22a09706776cd66de67ed2f33bbfd830ce5431fead87ecedae979036049d122aa6de9556b8bd0354d051b3dbae39b684e1fe8a0c languageName: node linkType: hard @@ -16423,6 +17082,13 @@ __metadata: languageName: node linkType: hard +"before-after-hook@npm:^2.2.0": + version: 2.2.3 + resolution: "before-after-hook@npm:2.2.3" + checksum: e676f769dbc4abcf4b3317db2fd2badb4a92c0710e0a7da12cf14b59c3482d4febf835ad7de7874499060fd4e13adf0191628e504728b3c5bb4ec7a878c09940 + languageName: node + linkType: hard + "better-opn@npm:^3.0.2": version: 3.0.2 resolution: "better-opn@npm:3.0.2" @@ -16467,6 +17133,15 @@ __metadata: languageName: node linkType: hard +"bindings@npm:^1.4.0": + version: 1.5.0 + resolution: "bindings@npm:1.5.0" + dependencies: + file-uri-to-path: "npm:1.0.0" + checksum: 593d5ae975ffba15fbbb4788fe5abd1e125afbab849ab967ab43691d27d6483751805d98cb92f7ac24a2439a8a8678cd0131c535d5d63de84e383b0ce2786133 + languageName: node + linkType: hard + "bintrees@npm:1.0.2": version: 1.0.2 resolution: "bintrees@npm:1.0.2" @@ -16911,7 +17586,7 @@ __metadata: languageName: node linkType: hard -"callsites@npm:^4.0.0": +"callsites@npm:^4.1.0": version: 4.1.0 resolution: "callsites@npm:4.1.0" checksum: 4ad31de7b7615fa25bdab9c2373865209d2d5190f895cdf2e2f518bd1dafa7ebcda2e6e9cc9640f2dfde6b3893d82fa4359a78ffc27baad2503227553c6882fa @@ -17009,12 +17684,12 @@ __metadata: languageName: node linkType: hard -"cbor@npm:^8.1.0": - version: 8.1.0 - resolution: "cbor@npm:8.1.0" +"cbor@npm:^9.0.1": + version: 9.0.1 + resolution: "cbor@npm:9.0.1" dependencies: nofilter: "npm:^3.1.0" - checksum: fc6c6d4f8d14def3a0f2ef111f4fc14b3b0bc91d22ed8fd0eb005095c4699c723a45721e515d713571148d0d965ceeb771f4ad422953cb4e9658b379991b52c9 + checksum: fa1bdf233b7d8b95b991c7d3861b6bf300b0d62fcebda34e4cca53605d32585021e80ee00b52378f492da011ebde6b21d704ac5117c2c6cce30de0b6419d2372 languageName: node linkType: hard @@ -17056,7 +17731,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:5.3.0, chalk@npm:^5.0.1, chalk@npm:^5.2.0": +"chalk@npm:5.3.0, chalk@npm:^5.0.1, chalk@npm:^5.2.0, chalk@npm:^5.3.0": version: 5.3.0 resolution: "chalk@npm:5.3.0" checksum: 6373caaab21bd64c405bfc4bd9672b145647fc9482657b5ea1d549b3b2765054e9d3d928870cdf764fb4aad67555f5061538ff247b8310f110c5c888d92397ea @@ -17146,6 +17821,13 @@ __metadata: languageName: node linkType: hard +"character-entities-html4@npm:^2.0.0": + version: 2.1.0 + resolution: "character-entities-html4@npm:2.1.0" + checksum: 7034aa7c7fa90309667f6dd50499c8a760c3d3a6fb159adb4e0bada0107d194551cdbad0714302f62d06ce4ed68565c8c2e15fdef2e8f8764eb63fa92b34b11d + languageName: node + linkType: hard + "character-entities-legacy@npm:^1.0.0": version: 1.1.4 resolution: "character-entities-legacy@npm:1.1.4" @@ -17153,6 +17835,13 @@ __metadata: languageName: node linkType: hard +"character-entities-legacy@npm:^3.0.0": + version: 3.0.0 + resolution: "character-entities-legacy@npm:3.0.0" + checksum: 7582af055cb488b626d364b7d7a4e46b06abd526fb63c0e4eb35bcb9c9799cc4f76b39f34fdccef2d1174ac95e53e9ab355aae83227c1a2505877893fce77731 + languageName: node + linkType: hard + "character-entities@npm:^1.0.0": version: 1.2.4 resolution: "character-entities@npm:1.2.4" @@ -17306,6 +17995,13 @@ __metadata: languageName: node linkType: hard +"ci-info@npm:^4.0.0": + version: 4.0.0 + resolution: "ci-info@npm:4.0.0" + checksum: c983bb7ff1b06648f4a47432201abbd58291147d8ab5043dbb5c03e1a0e3fb2347f40d29b66a3044f28ffeb5dade01ac35aa6bd4e7464a44d9a49a3d7532415a + languageName: node + linkType: hard + "ci-parallel-vars@npm:^1.0.1": version: 1.0.1 resolution: "ci-parallel-vars@npm:1.0.1" @@ -17352,22 +18048,6 @@ __metadata: languageName: node linkType: hard -"clean-stack@npm:^4.0.0": - version: 4.2.0 - resolution: "clean-stack@npm:4.2.0" - dependencies: - escape-string-regexp: "npm:5.0.0" - checksum: 373f656a31face5c615c0839213b9b542a0a48057abfb1df66900eab4dc2a5c6097628e4a0b5aa559cdfc4e66f8a14ea47be9681773165a44470ef5fb8ccc172 - languageName: node - linkType: hard - -"clean-yaml-object@npm:^0.1.0": - version: 0.1.0 - resolution: "clean-yaml-object@npm:0.1.0" - checksum: 0374ad2f1fbd4984ecf56ebc62200092f6372b9ccf1b7971bb979c328fb12fe76e759fb1e8adc491c80b7b1861f9f00c7f19813dd2a0f49c88231422c70451f4 - languageName: node - linkType: hard - "cli-boxes@npm:^3.0.0": version: 3.0.0 resolution: "cli-boxes@npm:3.0.0" @@ -17440,6 +18120,16 @@ __metadata: languageName: node linkType: hard +"cli-truncate@npm:^4.0.0": + version: 4.0.0 + resolution: "cli-truncate@npm:4.0.0" + dependencies: + slice-ansi: "npm:^5.0.0" + string-width: "npm:^7.0.0" + checksum: d5149175fd25ca985731bdeec46a55ec237475cf74c1a5e103baea696aceb45e372ac4acbaabf1316f06bd62e348123060f8191ffadfeedebd2a70a2a7fb199d + languageName: node + linkType: hard + "cli-width@npm:^3.0.0": version: 3.0.0 resolution: "cli-width@npm:3.0.0" @@ -17447,6 +18137,13 @@ __metadata: languageName: node linkType: hard +"cli-width@npm:^4.1.0": + version: 4.1.0 + resolution: "cli-width@npm:4.1.0" + checksum: b58876fbf0310a8a35c79b72ecfcf579b354e18ad04e6b20588724ea2b522799a758507a37dfe132fafaf93a9922cafd9514d9e1598e6b2cd46694853aed099f + languageName: node + linkType: hard + "client-only@npm:^0.0.1": version: 0.0.1 resolution: "client-only@npm:0.0.1" @@ -17454,7 +18151,7 @@ __metadata: languageName: node linkType: hard -"clipanion@npm:^3.1.0": +"clipanion@npm:^3.1.0, clipanion@npm:^3.2.1": version: 3.2.1 resolution: "clipanion@npm:3.2.1" dependencies: @@ -17629,7 +18326,7 @@ __metadata: languageName: node linkType: hard -"color-support@npm:^1.1.3": +"color-support@npm:^1.1.2, color-support@npm:^1.1.3": version: 1.1.3 resolution: "color-support@npm:1.1.3" bin: @@ -17695,6 +18392,13 @@ __metadata: languageName: node linkType: hard +"comma-separated-tokens@npm:^2.0.0": + version: 2.0.3 + resolution: "comma-separated-tokens@npm:2.0.3" + checksum: e3bf9e0332a5c45f49b90e79bcdb4a7a85f28d6a6f0876a94f1bb9b2bfbdbbb9292aac50e1e742d8c0db1e62a0229a106f57917e2d067fca951d81737651700d + languageName: node + linkType: hard + "commander@npm:11.0.0": version: 11.0.0 resolution: "commander@npm:11.0.0" @@ -17936,7 +18640,7 @@ __metadata: languageName: node linkType: hard -"console-control-strings@npm:^1.1.0": +"console-control-strings@npm:^1.0.0, console-control-strings@npm:^1.1.0": version: 1.1.0 resolution: "console-control-strings@npm:1.1.0" checksum: 27b5fa302bc8e9ae9e98c03c66d76ca289ad0c61ce2fe20ab288d288bee875d217512d2edb2363fc83165e88f1c405180cf3f5413a46e51b4fe1a004840c6cdb @@ -18865,6 +19569,13 @@ __metadata: languageName: node linkType: hard +"deprecation@npm:^2.0.0": + version: 2.3.1 + resolution: "deprecation@npm:2.3.1" + checksum: f56a05e182c2c195071385455956b0c4106fe14e36245b00c689ceef8e8ab639235176a96977ba7c74afb173317fac2e0ec6ec7a1c6d1e6eaa401c586c714132 + languageName: node + linkType: hard + "dequal@npm:2.0.3, dequal@npm:^2.0.0, dequal@npm:^2.0.2, dequal@npm:^2.0.3": version: 2.0.3 resolution: "dequal@npm:2.0.3" @@ -18895,7 +19606,7 @@ __metadata: languageName: node linkType: hard -"detect-libc@npm:^2.0.1": +"detect-libc@npm:^2.0.0, detect-libc@npm:^2.0.1": version: 2.0.2 resolution: "detect-libc@npm:2.0.2" checksum: 6118f30c0c425b1e56b9d2609f29bec50d35a6af0b762b6ad127271478f3bbfda7319ce869230cf1a351f2b219f39332cde290858553336d652c77b970f15de8 @@ -18992,7 +19703,7 @@ __metadata: languageName: node linkType: hard -"diff@npm:^5.1.0": +"diff@npm:^5.0.0, diff@npm:^5.1.0": version: 5.1.0 resolution: "diff@npm:5.1.0" checksum: f4557032a98b2967fe27b1a91dfcf8ebb6b9a24b1afe616b5c2312465100b861e9b8d4da374be535f2d6b967ce2f53826d7f6edc2a0d32b2ab55abc96acc2f9d @@ -19498,6 +20209,18 @@ __metadata: languageName: node linkType: hard +"emnapi@npm:0.44.0": + version: 0.44.0 + resolution: "emnapi@npm:0.44.0" + peerDependencies: + node-addon-api: ">= 6.1.0" + peerDependenciesMeta: + node-addon-api: + optional: true + checksum: c0177b08bbca8c815e5b15c544d431cd270975bc5831bad03691eba271de5ad7cff78be431ae1b5e3816a622951a9ed525b83a1a3707b9f9c70ecc8aece7344d + languageName: node + linkType: hard + "emoji-regex@npm:^10.3.0": version: 10.3.0 resolution: "emoji-regex@npm:10.3.0" @@ -19890,7 +20613,7 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.18.0, esbuild@npm:^0.18.10": +"esbuild@npm:^0.18.0": version: 0.18.20 resolution: "esbuild@npm:0.18.20" dependencies: @@ -19967,32 +20690,32 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.19.7": - version: 0.19.7 - resolution: "esbuild@npm:0.19.7" +"esbuild@npm:^0.19.3, esbuild@npm:^0.19.7": + version: 0.19.8 + resolution: "esbuild@npm:0.19.8" dependencies: - "@esbuild/android-arm": "npm:0.19.7" - "@esbuild/android-arm64": "npm:0.19.7" - "@esbuild/android-x64": "npm:0.19.7" - "@esbuild/darwin-arm64": "npm:0.19.7" - "@esbuild/darwin-x64": "npm:0.19.7" - "@esbuild/freebsd-arm64": "npm:0.19.7" - "@esbuild/freebsd-x64": "npm:0.19.7" - "@esbuild/linux-arm": "npm:0.19.7" - "@esbuild/linux-arm64": "npm:0.19.7" - "@esbuild/linux-ia32": "npm:0.19.7" - "@esbuild/linux-loong64": "npm:0.19.7" - "@esbuild/linux-mips64el": "npm:0.19.7" - "@esbuild/linux-ppc64": "npm:0.19.7" - "@esbuild/linux-riscv64": "npm:0.19.7" - "@esbuild/linux-s390x": "npm:0.19.7" - "@esbuild/linux-x64": "npm:0.19.7" - "@esbuild/netbsd-x64": "npm:0.19.7" - "@esbuild/openbsd-x64": "npm:0.19.7" - "@esbuild/sunos-x64": "npm:0.19.7" - "@esbuild/win32-arm64": "npm:0.19.7" - "@esbuild/win32-ia32": "npm:0.19.7" - "@esbuild/win32-x64": "npm:0.19.7" + "@esbuild/android-arm": "npm:0.19.8" + "@esbuild/android-arm64": "npm:0.19.8" + "@esbuild/android-x64": "npm:0.19.8" + "@esbuild/darwin-arm64": "npm:0.19.8" + "@esbuild/darwin-x64": "npm:0.19.8" + "@esbuild/freebsd-arm64": "npm:0.19.8" + "@esbuild/freebsd-x64": "npm:0.19.8" + "@esbuild/linux-arm": "npm:0.19.8" + "@esbuild/linux-arm64": "npm:0.19.8" + "@esbuild/linux-ia32": "npm:0.19.8" + "@esbuild/linux-loong64": "npm:0.19.8" + "@esbuild/linux-mips64el": "npm:0.19.8" + "@esbuild/linux-ppc64": "npm:0.19.8" + "@esbuild/linux-riscv64": "npm:0.19.8" + "@esbuild/linux-s390x": "npm:0.19.8" + "@esbuild/linux-x64": "npm:0.19.8" + "@esbuild/netbsd-x64": "npm:0.19.8" + "@esbuild/openbsd-x64": "npm:0.19.8" + "@esbuild/sunos-x64": "npm:0.19.8" + "@esbuild/win32-arm64": "npm:0.19.8" + "@esbuild/win32-ia32": "npm:0.19.8" + "@esbuild/win32-x64": "npm:0.19.8" dependenciesMeta: "@esbuild/android-arm": optional: true @@ -20040,7 +20763,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 326b9d98a77c5f2fb9a535b292bdc67c88bdfb4a19d29a221d65fd69f4800faea1f34947e8e6bc25ca3bd5db01f61c6968fec91f8c335e21e29b50330d90bd89 + checksum: 8c440db4689948626dbc4122a03575c378e86e630e5de3789464504cd474bf3a1fd7c9402ed79eb8aa2f83e5cfd75872c8607d6255a05e540065b42750e89afe languageName: node linkType: hard @@ -20058,13 +20781,6 @@ __metadata: languageName: node linkType: hard -"escape-string-regexp@npm:5.0.0, escape-string-regexp@npm:^5.0.0": - version: 5.0.0 - resolution: "escape-string-regexp@npm:5.0.0" - checksum: 20daabe197f3cb198ec28546deebcf24b3dbb1a5a269184381b3116d12f0532e06007f4bc8da25669d6a7f8efb68db0758df4cd981f57bc5b57f521a3e12c59e - languageName: node - linkType: hard - "escape-string-regexp@npm:^1.0.2, escape-string-regexp@npm:^1.0.5": version: 1.0.5 resolution: "escape-string-regexp@npm:1.0.5" @@ -20086,6 +20802,13 @@ __metadata: languageName: node linkType: hard +"escape-string-regexp@npm:^5.0.0": + version: 5.0.0 + resolution: "escape-string-regexp@npm:5.0.0" + checksum: 20daabe197f3cb198ec28546deebcf24b3dbb1a5a269184381b3116d12f0532e06007f4bc8da25669d6a7f8efb68db0758df4cd981f57bc5b57f521a3e12c59e + languageName: node + linkType: hard + "escodegen@npm:^2.1.0": version: 2.1.0 resolution: "escodegen@npm:2.1.0" @@ -20401,6 +21124,13 @@ __metadata: languageName: node linkType: hard +"estree-walker@npm:2.0.2, estree-walker@npm:^2.0.1, estree-walker@npm:^2.0.2": + version: 2.0.2 + resolution: "estree-walker@npm:2.0.2" + checksum: b02109c5d46bc2ed47de4990eef770f7457b1159a229f0999a09224d2b85ffeed2d7679cffcff90aeb4448e94b0168feb5265b209cdec29aad50a3d6e93d21e2 + languageName: node + linkType: hard + "estree-walker@npm:^0.6.1": version: 0.6.1 resolution: "estree-walker@npm:0.6.1" @@ -20408,13 +21138,6 @@ __metadata: languageName: node linkType: hard -"estree-walker@npm:^2.0.2": - version: 2.0.2 - resolution: "estree-walker@npm:2.0.2" - checksum: b02109c5d46bc2ed47de4990eef770f7457b1159a229f0999a09224d2b85ffeed2d7679cffcff90aeb4448e94b0168feb5265b209cdec29aad50a3d6e93d21e2 - languageName: node - linkType: hard - "esutils@npm:^2.0.2, esutils@npm:^2.0.3": version: 2.0.3 resolution: "esutils@npm:2.0.3" @@ -20642,7 +21365,7 @@ __metadata: languageName: node linkType: hard -"external-editor@npm:^3.0.3": +"external-editor@npm:^3.0.3, external-editor@npm:^3.1.0": version: 3.1.0 resolution: "external-editor@npm:3.1.0" dependencies: @@ -20732,7 +21455,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:3.3.2, fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.7, fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0": +"fast-glob@npm:3.3.2, fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.7, fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0, fast-glob@npm:^3.3.2": version: 3.3.2 resolution: "fast-glob@npm:3.3.2" dependencies: @@ -20930,6 +21653,15 @@ __metadata: languageName: node linkType: hard +"figures@npm:^6.0.1": + version: 6.0.1 + resolution: "figures@npm:6.0.1" + dependencies: + is-unicode-supported: "npm:^2.0.0" + checksum: 2fb988f01bed5ae6915a0593342f083bd1b09d0a6bf9aa58d0882c446cf13b59059f8a967acd676763278107b87b762231430d610d8f3be87c90ce87984a32a1 + languageName: node + linkType: hard + "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" @@ -20971,6 +21703,13 @@ __metadata: languageName: node linkType: hard +"file-uri-to-path@npm:1.0.0": + version: 1.0.0 + resolution: "file-uri-to-path@npm:1.0.0" + checksum: b648580bdd893a008c92c7ecc96c3ee57a5e7b6c4c18a9a09b44fb5d36d79146f8e442578bc0e173dc027adf3987e254ba1dfd6e3ec998b7c282873010502144 + languageName: node + linkType: hard + "filelist@npm:^1.0.4": version: 1.0.4 resolution: "filelist@npm:1.0.4" @@ -21090,6 +21829,13 @@ __metadata: languageName: node linkType: hard +"find-up-simple@npm:^1.0.0": + version: 1.0.0 + resolution: "find-up-simple@npm:1.0.0" + checksum: 91c3d51c1111b5eb4e6e6d71d21438f6571a37a69dc288d4222b98996756e2f472fa5393a4dddb5e1a84929405d87e86f4bdce798ba84ee513b79854960ec140 + languageName: node + linkType: hard + "find-up@npm:5.0.0, find-up@npm:^5.0.0": version: 5.0.0 resolution: "find-up@npm:5.0.0" @@ -21128,16 +21874,6 @@ __metadata: languageName: node linkType: hard -"find-up@npm:^6.0.0": - version: 6.3.0 - resolution: "find-up@npm:6.3.0" - dependencies: - locate-path: "npm:^7.1.0" - path-exists: "npm:^5.0.0" - checksum: 4f3bdc30d41778c647e53f4923e72de5e5fb055157031f34501c5b36c2eb59f77b997edf9cb00165c6060cda7eaa2e3da82cb6be2e61d68ad3e07c4bc4cce67e - languageName: node - linkType: hard - "flat-cache@npm:^3.0.4": version: 3.2.0 resolution: "flat-cache@npm:3.2.0" @@ -21494,7 +22230,7 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:^2.3.2, fsevents@npm:~2.3.2": +"fsevents@npm:^2.3.2, fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": version: 2.3.3 resolution: "fsevents@npm:2.3.3" dependencies: @@ -21513,7 +22249,7 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": +"fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": version: 2.3.3 resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" dependencies: @@ -21547,6 +22283,23 @@ __metadata: languageName: node linkType: hard +"gauge@npm:^3.0.0": + version: 3.0.2 + resolution: "gauge@npm:3.0.2" + dependencies: + aproba: "npm:^1.0.3 || ^2.0.0" + color-support: "npm:^1.1.2" + console-control-strings: "npm:^1.0.0" + has-unicode: "npm:^2.0.1" + object-assign: "npm:^4.1.1" + signal-exit: "npm:^3.0.0" + string-width: "npm:^4.2.3" + strip-ansi: "npm:^6.0.1" + wide-align: "npm:^1.1.2" + checksum: 46df086451672a5fecd58f7ec86da74542c795f8e00153fbef2884286ce0e86653c3eb23be2d0abb0c4a82b9b2a9dec3b09b6a1cf31c28085fa0376599a26589 + languageName: node + linkType: hard + "gauge@npm:^4.0.3": version: 4.0.4 resolution: "gauge@npm:4.0.4" @@ -22036,7 +22789,7 @@ __metadata: languageName: node linkType: hard -"globby@npm:^13.1.1, globby@npm:^13.1.4": +"globby@npm:^13.1.1": version: 13.2.2 resolution: "globby@npm:13.2.2" dependencies: @@ -22049,6 +22802,20 @@ __metadata: languageName: node linkType: hard +"globby@npm:^14.0.0": + version: 14.0.0 + resolution: "globby@npm:14.0.0" + dependencies: + "@sindresorhus/merge-streams": "npm:^1.0.0" + fast-glob: "npm:^3.3.2" + ignore: "npm:^5.2.4" + path-type: "npm:^5.0.0" + slash: "npm:^5.1.0" + unicorn-magic: "npm:^0.1.0" + checksum: 6e7d84bbc69d8d21a07507af090998c6546c385702a350ff14f6fb08207f90ed40bd41c7b19c11a23851c3b86666e79503373e0f8b400a91a29b13952b1e960c + languageName: node + linkType: hard + "globrex@npm:^0.1.2": version: 0.1.2 resolution: "globrex@npm:0.1.2" @@ -22396,6 +23163,36 @@ __metadata: languageName: node linkType: hard +"hast-util-from-html@npm:^2.0.0": + version: 2.0.1 + resolution: "hast-util-from-html@npm:2.0.1" + dependencies: + "@types/hast": "npm:^3.0.0" + devlop: "npm:^1.1.0" + hast-util-from-parse5: "npm:^8.0.0" + parse5: "npm:^7.0.0" + vfile: "npm:^6.0.0" + vfile-message: "npm:^4.0.0" + checksum: 5e8111c28fa4aa7cbb2d57b41bf4ff771641332c6e0704900367acbe0b3f5e5c432a82df80815d4fe91ac8137c264f72c8632fd7704992ebbddc6c0712b78582 + languageName: node + linkType: hard + +"hast-util-from-parse5@npm:^8.0.0": + version: 8.0.1 + resolution: "hast-util-from-parse5@npm:8.0.1" + dependencies: + "@types/hast": "npm:^3.0.0" + "@types/unist": "npm:^3.0.0" + devlop: "npm:^1.0.0" + hastscript: "npm:^8.0.0" + property-information: "npm:^6.0.0" + vfile: "npm:^6.0.0" + vfile-location: "npm:^5.0.0" + web-namespaces: "npm:^2.0.0" + checksum: d4105af849bebceac0a641a5f4611a43eeb4b94f9d3958ce6cbbb069dd177edefb9cd31a210689bc9cca9a30db984d622bdf898aed44a2ea99560d81023b0e2d + languageName: node + linkType: hard + "hast-util-parse-selector@npm:^2.0.0": version: 2.2.5 resolution: "hast-util-parse-selector@npm:2.2.5" @@ -22403,6 +23200,80 @@ __metadata: languageName: node linkType: hard +"hast-util-parse-selector@npm:^4.0.0": + version: 4.0.0 + resolution: "hast-util-parse-selector@npm:4.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + checksum: 76087670d3b0b50b23a6cb70bca53a6176d6608307ccdbb3ed18b650b82e7c3513bfc40348f1389dc0c5ae872b9a768851f4335f44654abd7deafd6974c52402 + languageName: node + linkType: hard + +"hast-util-raw@npm:^9.0.0": + version: 9.0.1 + resolution: "hast-util-raw@npm:9.0.1" + dependencies: + "@types/hast": "npm:^3.0.0" + "@types/unist": "npm:^3.0.0" + "@ungap/structured-clone": "npm:^1.0.0" + hast-util-from-parse5: "npm:^8.0.0" + hast-util-to-parse5: "npm:^8.0.0" + html-void-elements: "npm:^3.0.0" + mdast-util-to-hast: "npm:^13.0.0" + parse5: "npm:^7.0.0" + unist-util-position: "npm:^5.0.0" + unist-util-visit: "npm:^5.0.0" + vfile: "npm:^6.0.0" + web-namespaces: "npm:^2.0.0" + zwitch: "npm:^2.0.0" + checksum: b89a198ec3a3786cef08beac500d27f948124d0f2795e079f775f16c38506719157b9b5cc9a0c781c705b6eff7f66d692f55f0aa5e88530d4ba81e21ca653248 + languageName: node + linkType: hard + +"hast-util-to-html@npm:^9.0.0": + version: 9.0.0 + resolution: "hast-util-to-html@npm:9.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + "@types/unist": "npm:^3.0.0" + ccount: "npm:^2.0.0" + comma-separated-tokens: "npm:^2.0.0" + hast-util-raw: "npm:^9.0.0" + hast-util-whitespace: "npm:^3.0.0" + html-void-elements: "npm:^3.0.0" + mdast-util-to-hast: "npm:^13.0.0" + property-information: "npm:^6.0.0" + space-separated-tokens: "npm:^2.0.0" + stringify-entities: "npm:^4.0.0" + zwitch: "npm:^2.0.4" + checksum: 4bfa78b681135b9303743b34d7139328ff5dc412a1f6bd372e83192413fd86a5d7e8d55eab4eeb2cd561878218eec07a57df1f92cf0f3272756830738611708a + languageName: node + linkType: hard + +"hast-util-to-parse5@npm:^8.0.0": + version: 8.0.0 + resolution: "hast-util-to-parse5@npm:8.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + comma-separated-tokens: "npm:^2.0.0" + devlop: "npm:^1.0.0" + property-information: "npm:^6.0.0" + space-separated-tokens: "npm:^2.0.0" + web-namespaces: "npm:^2.0.0" + zwitch: "npm:^2.0.0" + checksum: ba59d0913ba7e914d8b0a50955c06806a6868445c56796ac9129d58185e86d7ff24037246767aba2ea904d9dee8c09b8ff303630bcd854431fdc1bbee2164c36 + languageName: node + linkType: hard + +"hast-util-whitespace@npm:^3.0.0": + version: 3.0.0 + resolution: "hast-util-whitespace@npm:3.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + checksum: 8c7e9eeb8131fc18702f3a42623eb6b0b09d470347aa8badacac70e6d91f79657ab8c6b57c4c6fee3658cff405fac30e816d1cdfb3ed1fbf6045d0a4555cf4d4 + languageName: node + linkType: hard + "hastscript@npm:^6.0.0": version: 6.0.0 resolution: "hastscript@npm:6.0.0" @@ -22416,6 +23287,19 @@ __metadata: languageName: node linkType: hard +"hastscript@npm:^8.0.0": + version: 8.0.0 + resolution: "hastscript@npm:8.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + comma-separated-tokens: "npm:^2.0.0" + hast-util-parse-selector: "npm:^4.0.0" + property-information: "npm:^6.0.0" + space-separated-tokens: "npm:^2.0.0" + checksum: cdc3477968ee0161c39615a650203e592d03bbd9a2a0e1d78d37f544fcf8c30f55fcf9e6d27c4372a89fdebeae756452f19c7f5b655a162d54524b39b2dfe0fe + languageName: node + linkType: hard + "he@npm:^1.2.0": version: 1.2.0 resolution: "he@npm:1.2.0" @@ -22582,6 +23466,13 @@ __metadata: languageName: node linkType: hard +"html-void-elements@npm:^3.0.0": + version: 3.0.0 + resolution: "html-void-elements@npm:3.0.0" + checksum: 59be397525465a7489028afa064c55763d9cccd1d7d9f630cca47137317f0e897a9ca26cef7e745e7cff1abc44260cfa407742b243a54261dfacd42230e94fce + languageName: node + linkType: hard + "html-webpack-plugin@npm:^5.5.3": version: 5.5.3 resolution: "html-webpack-plugin@npm:5.5.3" @@ -22879,10 +23770,10 @@ __metadata: languageName: node linkType: hard -"idb@npm:^7.1.1": - version: 7.1.1 - resolution: "idb@npm:7.1.1" - checksum: 8e33eaebf21055129864acb89932e0739b8c96788e559df24c253ce114d8c6deb977a3b30ea47a9bb8a2ae8a55964861c3df65f360d95745e341cee40d5c17f4 +"idb@npm:^8.0.0": + version: 8.0.0 + resolution: "idb@npm:8.0.0" + checksum: 6ff47b46ead4bf02ac3a9145bb2f48bac707b950cf33282a72eec8efcda610ffb0a09aab6eea58450f6c87373060bf50998d307085830a302e9e231eee6cd37d languageName: node linkType: hard @@ -23090,6 +23981,29 @@ __metadata: languageName: node linkType: hard +"inquirer@npm:^9.2.12": + version: 9.2.12 + resolution: "inquirer@npm:9.2.12" + dependencies: + "@ljharb/through": "npm:^2.3.11" + ansi-escapes: "npm:^4.3.2" + chalk: "npm:^5.3.0" + cli-cursor: "npm:^3.1.0" + cli-width: "npm:^4.1.0" + external-editor: "npm:^3.1.0" + figures: "npm:^5.0.0" + lodash: "npm:^4.17.21" + mute-stream: "npm:1.0.0" + ora: "npm:^5.4.1" + run-async: "npm:^3.0.0" + rxjs: "npm:^7.8.1" + string-width: "npm:^4.2.3" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^6.2.0" + checksum: 02b259c641fd6c6b88c0c530aced23389d586bd5360799bab0ae11d2a965ac5ce9c587402faefad70f08b8b56ccae56fb973da3a8deb6e17ba4577f803d427c5 + languageName: node + linkType: hard + "interpret@npm:^3.1.1": version: 3.1.1 resolution: "interpret@npm:3.1.1" @@ -23229,6 +24143,13 @@ __metadata: languageName: node linkType: hard +"is-buffer@npm:^2.0.0": + version: 2.0.5 + resolution: "is-buffer@npm:2.0.5" + checksum: 3261a8b858edcc6c9566ba1694bf829e126faa88911d1c0a747ea658c5d81b14b6955e3a702d59dabadd58fdd440c01f321aa71d6547105fd21d03f94d0597e7 + languageName: node + linkType: hard + "is-buffer@npm:~1.1.6": version: 1.1.6 resolution: "is-buffer@npm:1.1.6" @@ -23277,13 +24198,6 @@ __metadata: languageName: node linkType: hard -"is-error@npm:^2.2.2": - version: 2.2.2 - resolution: "is-error@npm:2.2.2" - checksum: a97b39587150f0d38f9f93f64699807fe3020fe5edbd63548f234dc2ba96fd7c776d66c062bf031dfeb93c7f48db563ff6bde588418ca041da37c659a416f055 - languageName: node - linkType: hard - "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -23561,7 +24475,7 @@ __metadata: languageName: node linkType: hard -"is-unicode-supported@npm:*": +"is-unicode-supported@npm:*, is-unicode-supported@npm:^2.0.0": version: 2.0.0 resolution: "is-unicode-supported@npm:2.0.0" checksum: 000b80639dedaf59a385f1c0a57f97a4d1435e0723716f24cc19ad94253a7a0a9f838bdc9ac49b10a29ac93b01f52ae9b2ed358a8876caf1eb74d73b4ede92b2 @@ -24851,6 +25765,13 @@ __metadata: languageName: node linkType: hard +"kleur@npm:^4.0.3": + version: 4.1.5 + resolution: "kleur@npm:4.1.5" + checksum: 44d84cc4eedd4311099402ef6d4acd9b2d16e08e499d6ef3bb92389bd4692d7ef09e35248c26e27f98acac532122acb12a1bfee645994ae3af4f0a37996da7df + languageName: node + linkType: hard + "kolorist@npm:^1.8.0": version: 1.8.0 resolution: "kolorist@npm:1.8.0" @@ -25234,7 +26155,7 @@ __metadata: languageName: node linkType: hard -"lib0@npm:^0.2.74, lib0@npm:^0.2.85, lib0@npm:^0.2.86, lib0@npm:^0.2.87": +"lib0@npm:^0.2.74, lib0@npm:^0.2.85, lib0@npm:^0.2.86, lib0@npm:^0.2.87, lib0@npm:^0.2.88": version: 0.2.88 resolution: "lib0@npm:0.2.88" dependencies: @@ -25400,7 +26321,7 @@ __metadata: languageName: node linkType: hard -"lit@npm:^3.0.2": +"lit@npm:^3.0.2, lit@npm:^3.1.0": version: 3.1.0 resolution: "lit@npm:3.1.0" dependencies: @@ -25423,7 +26344,7 @@ __metadata: languageName: node linkType: hard -"load-json-file@npm:^7.0.0": +"load-json-file@npm:^7.0.1": version: 7.0.1 resolution: "load-json-file@npm:7.0.1" checksum: a560288da6891778321ef993e4bdbdf05374a4f3a3aeedd5ba6b64672798c830d748cfc59a2ec9891a3db30e78b3d04172e0dcb0d4828168289a393147ca0e74 @@ -25502,15 +26423,6 @@ __metadata: languageName: node linkType: hard -"locate-path@npm:^7.1.0": - version: 7.2.0 - resolution: "locate-path@npm:7.2.0" - dependencies: - p-locate: "npm:^6.0.0" - checksum: 1c6d269d4efec555937081be964e8a9b4a136319c79ca1d45ac6382212a8466113c75bd89e44521ca8ecd1c47fb08523b56eee5c0712bc7d14fec5f729deeb42 - languageName: node - linkType: hard - "lodash-es@npm:^4.17.21": version: 4.17.21 resolution: "lodash-es@npm:4.17.21" @@ -25945,7 +26857,7 @@ __metadata: languageName: node linkType: hard -"make-dir@npm:^3.0.0, make-dir@npm:^3.0.2": +"make-dir@npm:^3.0.0, make-dir@npm:^3.0.2, make-dir@npm:^3.1.0": version: 3.1.0 resolution: "make-dir@npm:3.1.0" dependencies: @@ -26022,7 +26934,7 @@ __metadata: languageName: node linkType: hard -"map-age-cleaner@npm:^0.1.1, map-age-cleaner@npm:^0.1.3": +"map-age-cleaner@npm:^0.1.1": version: 0.1.3 resolution: "map-age-cleaner@npm:0.1.3" dependencies: @@ -26169,6 +27081,18 @@ __metadata: languageName: node linkType: hard +"mdast-util-find-and-replace@npm:^2.0.0": + version: 2.2.2 + resolution: "mdast-util-find-and-replace@npm:2.2.2" + dependencies: + "@types/mdast": "npm:^3.0.0" + escape-string-regexp: "npm:^5.0.0" + unist-util-is: "npm:^5.0.0" + unist-util-visit-parents: "npm:^5.0.0" + checksum: 59e11e853b74d8f6083950327df39e27287b383930ff836298a5100aeda5568282bb45046c27886d2156ea101580bb0689b890c29623cefa5adc74e95d9ca9ff + languageName: node + linkType: hard + "mdast-util-find-and-replace@npm:^3.0.0": version: 3.0.1 resolution: "mdast-util-find-and-replace@npm:3.0.1" @@ -26181,6 +27105,26 @@ __metadata: languageName: node linkType: hard +"mdast-util-from-markdown@npm:^1.0.0": + version: 1.3.1 + resolution: "mdast-util-from-markdown@npm:1.3.1" + dependencies: + "@types/mdast": "npm:^3.0.0" + "@types/unist": "npm:^2.0.0" + decode-named-character-reference: "npm:^1.0.0" + mdast-util-to-string: "npm:^3.1.0" + micromark: "npm:^3.0.0" + micromark-util-decode-numeric-character-reference: "npm:^1.0.0" + micromark-util-decode-string: "npm:^1.0.0" + micromark-util-normalize-identifier: "npm:^1.0.0" + micromark-util-symbol: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.0" + unist-util-stringify-position: "npm:^3.0.0" + uvu: "npm:^0.5.0" + checksum: 1d334a54ddd6481ec4acf64c2c537b6463bc5113ba5a408f65c228dcc302d46837352814f11307af0f8b51dd7e4a0b887ce692e4d30ff31ff9d578b8ca82810b + languageName: node + linkType: hard + "mdast-util-from-markdown@npm:^2.0.0": version: 2.0.0 resolution: "mdast-util-from-markdown@npm:2.0.0" @@ -26201,6 +27145,18 @@ __metadata: languageName: node linkType: hard +"mdast-util-gfm-autolink-literal@npm:^1.0.0": + version: 1.0.3 + resolution: "mdast-util-gfm-autolink-literal@npm:1.0.3" + dependencies: + "@types/mdast": "npm:^3.0.0" + ccount: "npm:^2.0.0" + mdast-util-find-and-replace: "npm:^2.0.0" + micromark-util-character: "npm:^1.0.0" + checksum: 272d075cdc7937bec0179af4052bd9032a6fbb05608b387b1b075b0491c73ce012f3ff1c718cdb5fb0ed1032c1fa7570d955b59c0ab3c3c72609928754774529 + languageName: node + linkType: hard + "mdast-util-gfm-autolink-literal@npm:^2.0.0": version: 2.0.0 resolution: "mdast-util-gfm-autolink-literal@npm:2.0.0" @@ -26214,16 +27170,24 @@ __metadata: languageName: node linkType: hard -"mdast-util-gfm-footnote@npm:^2.0.0": - version: 2.0.0 - resolution: "mdast-util-gfm-footnote@npm:2.0.0" +"mdast-util-gfm-footnote@npm:^1.0.0": + version: 1.0.2 + resolution: "mdast-util-gfm-footnote@npm:1.0.2" dependencies: - "@types/mdast": "npm:^4.0.0" - devlop: "npm:^1.1.0" - mdast-util-from-markdown: "npm:^2.0.0" - mdast-util-to-markdown: "npm:^2.0.0" - micromark-util-normalize-identifier: "npm:^2.0.0" - checksum: 9a820ce66575f1dc5bcc1e3269f27777a96f462f84651e72a74319d313f8fe4043fe329169bcc80ec2f210dabb84c832c77fa386ab9b4d23c31379d9bf0f8ff6 + "@types/mdast": "npm:^3.0.0" + mdast-util-to-markdown: "npm:^1.3.0" + micromark-util-normalize-identifier: "npm:^1.0.0" + checksum: 825f207afc98fd1daa0acc8adcb5754d1f0d577ccb1749245289bee7c892557668d8ee3a5ab618f42e710646cf018dcda84f3c0c608ae11718e9014e5bf4f9dc + languageName: node + linkType: hard + +"mdast-util-gfm-strikethrough@npm:^1.0.0": + version: 1.0.3 + resolution: "mdast-util-gfm-strikethrough@npm:1.0.3" + dependencies: + "@types/mdast": "npm:^3.0.0" + mdast-util-to-markdown: "npm:^1.3.0" + checksum: a9c2dc3ef46be7952d13b7063a16171bba8aa266bffe6b1e7267df02a60b4fa3734115cca311e9127db8cfcbbcd68fdd92aa26152bcd0c14372c79b254e4df2f languageName: node linkType: hard @@ -26238,6 +27202,18 @@ __metadata: languageName: node linkType: hard +"mdast-util-gfm-table@npm:^1.0.0": + version: 1.0.7 + resolution: "mdast-util-gfm-table@npm:1.0.7" + dependencies: + "@types/mdast": "npm:^3.0.0" + markdown-table: "npm:^3.0.0" + mdast-util-from-markdown: "npm:^1.0.0" + mdast-util-to-markdown: "npm:^1.3.0" + checksum: 167f7f7a9dc17ce852f4f9bd155d7be179588e2ccf4ce3c4f23b12c1c9db5de904cdacc6f41b2d635cb84eb09a7ff5a33497585f2664a7f1e6bd6f7ab7e1197a + languageName: node + linkType: hard + "mdast-util-gfm-table@npm:^2.0.0": version: 2.0.0 resolution: "mdast-util-gfm-table@npm:2.0.0" @@ -26251,6 +27227,16 @@ __metadata: languageName: node linkType: hard +"mdast-util-gfm-task-list-item@npm:^1.0.0": + version: 1.0.2 + resolution: "mdast-util-gfm-task-list-item@npm:1.0.2" + dependencies: + "@types/mdast": "npm:^3.0.0" + mdast-util-to-markdown: "npm:^1.3.0" + checksum: 958417a7d7690728b44d65127ab9189c7feaa17aea924dd56a888c781ab3abaa4eb0c209f05c4dbf203da3d0c4df8fdace4c9471b644268bfc7fc792a018a171 + languageName: node + linkType: hard + "mdast-util-gfm-task-list-item@npm:^2.0.0": version: 2.0.0 resolution: "mdast-util-gfm-task-list-item@npm:2.0.0" @@ -26263,18 +27249,28 @@ __metadata: languageName: node linkType: hard -"mdast-util-gfm@npm:^3.0.0": - version: 3.0.0 - resolution: "mdast-util-gfm@npm:3.0.0" +"mdast-util-gfm@npm:^2.0.0": + version: 2.0.2 + resolution: "mdast-util-gfm@npm:2.0.2" dependencies: - mdast-util-from-markdown: "npm:^2.0.0" - mdast-util-gfm-autolink-literal: "npm:^2.0.0" - mdast-util-gfm-footnote: "npm:^2.0.0" - mdast-util-gfm-strikethrough: "npm:^2.0.0" - mdast-util-gfm-table: "npm:^2.0.0" - mdast-util-gfm-task-list-item: "npm:^2.0.0" - mdast-util-to-markdown: "npm:^2.0.0" - checksum: 3e0c8e9982d3df6e9235d862cb4a2a02cf54d11e9e65f9d139d217e9b7973bb49ef4b8ee49ec05d29bdd9fe3e5f7efe1c3ebdf40a950e9f553dfc25235ebbcc2 + mdast-util-from-markdown: "npm:^1.0.0" + mdast-util-gfm-autolink-literal: "npm:^1.0.0" + mdast-util-gfm-footnote: "npm:^1.0.0" + mdast-util-gfm-strikethrough: "npm:^1.0.0" + mdast-util-gfm-table: "npm:^1.0.0" + mdast-util-gfm-task-list-item: "npm:^1.0.0" + mdast-util-to-markdown: "npm:^1.0.0" + checksum: 70e6cd32af94181d409f171f984f83fc18b3efe316844c62f31816f5c1612a92517b8ed766340f23e0a6d6cb0f27a8b07d288bab6619cbdbb0c5341006bcdc4d + languageName: node + linkType: hard + +"mdast-util-phrasing@npm:^3.0.0": + version: 3.0.1 + resolution: "mdast-util-phrasing@npm:3.0.1" + dependencies: + "@types/mdast": "npm:^3.0.0" + unist-util-is: "npm:^5.0.0" + checksum: c5b616d9b1eb76a6b351d195d94318494722525a12a89d9c8a3b091af7db3dd1fc55d294f9d29266d8159a8267b0df4a7a133bda8a3909d5331c383e1e1ff328 languageName: node linkType: hard @@ -26288,6 +27284,38 @@ __metadata: languageName: node linkType: hard +"mdast-util-to-hast@npm:^13.0.0": + version: 13.0.2 + resolution: "mdast-util-to-hast@npm:13.0.2" + dependencies: + "@types/hast": "npm:^3.0.0" + "@types/mdast": "npm:^4.0.0" + "@ungap/structured-clone": "npm:^1.0.0" + devlop: "npm:^1.0.0" + micromark-util-sanitize-uri: "npm:^2.0.0" + trim-lines: "npm:^3.0.0" + unist-util-position: "npm:^5.0.0" + unist-util-visit: "npm:^5.0.0" + checksum: 6f91926ca59bc1b048a0f82c21ba6355f7352c3793442c43e3f93ac895af0b9f85881b7a461d23aeed0fbe16d695b419106a48075c79e3b6008fef75ca43a571 + languageName: node + linkType: hard + +"mdast-util-to-markdown@npm:^1.0.0, mdast-util-to-markdown@npm:^1.3.0": + version: 1.5.0 + resolution: "mdast-util-to-markdown@npm:1.5.0" + dependencies: + "@types/mdast": "npm:^3.0.0" + "@types/unist": "npm:^2.0.0" + longest-streak: "npm:^3.0.0" + mdast-util-phrasing: "npm:^3.0.0" + mdast-util-to-string: "npm:^3.0.0" + micromark-util-decode-string: "npm:^1.0.0" + unist-util-visit: "npm:^4.0.0" + zwitch: "npm:^2.0.0" + checksum: 713f674588a01969a2ce524a69985bd57e507377eea2c4ba69800fb305414468b30144ae9b837fbdde8c609877673140e4f56f6cabe9e0e2bc1487291e3c5144 + languageName: node + linkType: hard + "mdast-util-to-markdown@npm:^2.0.0": version: 2.1.0 resolution: "mdast-util-to-markdown@npm:2.1.0" @@ -26311,6 +27339,15 @@ __metadata: languageName: node linkType: hard +"mdast-util-to-string@npm:^3.0.0, mdast-util-to-string@npm:^3.1.0": + version: 3.2.0 + resolution: "mdast-util-to-string@npm:3.2.0" + dependencies: + "@types/mdast": "npm:^3.0.0" + checksum: fafe201c12a0d412a875fe8540bf70b4360f3775fb7f0d19403ba7b59e50f74f730e3b405c72ad940bc8a3ec1ba311f76dfca61c4ce585dce1ccda2168ec244f + languageName: node + linkType: hard + "mdast-util-to-string@npm:^4.0.0": version: 4.0.0 resolution: "mdast-util-to-string@npm:4.0.0" @@ -26361,16 +27398,6 @@ __metadata: languageName: node linkType: hard -"mem@npm:^9.0.2": - version: 9.0.2 - resolution: "mem@npm:9.0.2" - dependencies: - map-age-cleaner: "npm:^0.1.3" - mimic-fn: "npm:^4.0.0" - checksum: 82f899e73212509c8bccb26399f03c61193e15b796c9e8e10332db6a3eb1cad65edbe4ab144554e88a0db3dc53af3b9e960a2576cb3521fa968ba1ca5a0b6719 - languageName: node - linkType: hard - "memfs@npm:^3.4.3": version: 3.5.3 resolution: "memfs@npm:3.5.3" @@ -26387,6 +27414,15 @@ __metadata: languageName: node linkType: hard +"memoize@npm:^10.0.0": + version: 10.0.0 + resolution: "memoize@npm:10.0.0" + dependencies: + mimic-function: "npm:^5.0.0" + checksum: 2239451cc0b26f9e99e6107c2a24f069b8ccd98877b4fe4f28fe3a1e977521fe23a53fa7fb5e7ad485577e0f30ab61aed97cf29facbc701b88facf27b8f12ce3 + languageName: node + linkType: hard + "memoizerific@npm:^1.11.3": version: 1.11.3 resolution: "memoizerific@npm:1.11.3" @@ -26469,6 +27505,30 @@ __metadata: languageName: node linkType: hard +"micromark-core-commonmark@npm:^1.0.0, micromark-core-commonmark@npm:^1.0.1": + version: 1.1.0 + resolution: "micromark-core-commonmark@npm:1.1.0" + dependencies: + decode-named-character-reference: "npm:^1.0.0" + micromark-factory-destination: "npm:^1.0.0" + micromark-factory-label: "npm:^1.0.0" + micromark-factory-space: "npm:^1.0.0" + micromark-factory-title: "npm:^1.0.0" + micromark-factory-whitespace: "npm:^1.0.0" + micromark-util-character: "npm:^1.0.0" + micromark-util-chunked: "npm:^1.0.0" + micromark-util-classify-character: "npm:^1.0.0" + micromark-util-html-tag-name: "npm:^1.0.0" + micromark-util-normalize-identifier: "npm:^1.0.0" + micromark-util-resolve-all: "npm:^1.0.0" + micromark-util-subtokenize: "npm:^1.0.0" + micromark-util-symbol: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.1" + uvu: "npm:^0.5.0" + checksum: a73694d223ac8baad8ff00597a3c39d61f5b32bfd56fe4bcf295d75b2a4e8e67fb2edbfc7cc287b362b9d7f6d24fce08b6a7e8b5b155d79bcc1e4d9b2756ffb2 + languageName: node + linkType: hard + "micromark-core-commonmark@npm:^2.0.0": version: 2.0.0 resolution: "micromark-core-commonmark@npm:2.0.0" @@ -26493,6 +27553,18 @@ __metadata: languageName: node linkType: hard +"micromark-extension-gfm-autolink-literal@npm:^1.0.0": + version: 1.0.5 + resolution: "micromark-extension-gfm-autolink-literal@npm:1.0.5" + dependencies: + micromark-util-character: "npm:^1.0.0" + micromark-util-sanitize-uri: "npm:^1.0.0" + micromark-util-symbol: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.0" + checksum: 1e0ccc758baef3cd0478ba84ff86fa1ec2b389042421c7cade9485b775456c1a9c3bd797393002b2c6f6abd9bdf829cb114874557bbcb8e43d16d06a464811c0 + languageName: node + linkType: hard + "micromark-extension-gfm-autolink-literal@npm:^2.0.0": version: 2.0.0 resolution: "micromark-extension-gfm-autolink-literal@npm:2.0.0" @@ -26505,19 +27577,33 @@ __metadata: languageName: node linkType: hard -"micromark-extension-gfm-footnote@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-extension-gfm-footnote@npm:2.0.0" +"micromark-extension-gfm-footnote@npm:^1.0.0": + version: 1.1.2 + resolution: "micromark-extension-gfm-footnote@npm:1.1.2" dependencies: - devlop: "npm:^1.0.0" - micromark-core-commonmark: "npm:^2.0.0" - micromark-factory-space: "npm:^2.0.0" - micromark-util-character: "npm:^2.0.0" - micromark-util-normalize-identifier: "npm:^2.0.0" - micromark-util-sanitize-uri: "npm:^2.0.0" - micromark-util-symbol: "npm:^2.0.0" - micromark-util-types: "npm:^2.0.0" - checksum: 7813d226b862f84d417ff890f263961c1fdceaf4b02d543bf754e21b46b834bf524962acc9bb058af26edc65c838c194735fd858079c6340a0f217d031e0932d + micromark-core-commonmark: "npm:^1.0.0" + micromark-factory-space: "npm:^1.0.0" + micromark-util-character: "npm:^1.0.0" + micromark-util-normalize-identifier: "npm:^1.0.0" + micromark-util-sanitize-uri: "npm:^1.0.0" + micromark-util-symbol: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.0" + uvu: "npm:^0.5.0" + checksum: 8777073fb76d2fd01f6b2405106af6c349c1e25660c4d37cadcc61c187d71c8444870f73cefaaa67f12884d5e45c78ee3c5583561a0b330bd91c6d997113584a + languageName: node + linkType: hard + +"micromark-extension-gfm-strikethrough@npm:^1.0.0": + version: 1.0.7 + resolution: "micromark-extension-gfm-strikethrough@npm:1.0.7" + dependencies: + micromark-util-chunked: "npm:^1.0.0" + micromark-util-classify-character: "npm:^1.0.0" + micromark-util-resolve-all: "npm:^1.0.0" + micromark-util-symbol: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.0" + uvu: "npm:^0.5.0" + checksum: 8411ef1aa5dc83f662e8b45b085f70ddff29deb3c4259269e8a1ff656397abb755d8ea841a14be23e8585a31d3c0a5de1bd2c05f3453b66670e499d4a0004f5e languageName: node linkType: hard @@ -26535,6 +27621,19 @@ __metadata: languageName: node linkType: hard +"micromark-extension-gfm-table@npm:^1.0.0": + version: 1.0.7 + resolution: "micromark-extension-gfm-table@npm:1.0.7" + dependencies: + micromark-factory-space: "npm:^1.0.0" + micromark-util-character: "npm:^1.0.0" + micromark-util-symbol: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.0" + uvu: "npm:^0.5.0" + checksum: f05d86a099c941a2a309d60bf4839d16a00a93cb880cda4ab8faeb831647763fff6e03197ec15b80e1f195002afcca6afe2b95c3622b049b82d7ff8ef1c1c776 + languageName: node + linkType: hard + "micromark-extension-gfm-table@npm:^2.0.0": version: 2.0.0 resolution: "micromark-extension-gfm-table@npm:2.0.0" @@ -26548,16 +27647,29 @@ __metadata: languageName: node linkType: hard -"micromark-extension-gfm-tagfilter@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-extension-gfm-tagfilter@npm:2.0.0" +"micromark-extension-gfm-tagfilter@npm:^1.0.0": + version: 1.0.2 + resolution: "micromark-extension-gfm-tagfilter@npm:1.0.2" dependencies: - micromark-util-types: "npm:^2.0.0" - checksum: c5e3f8cdf22e184de3f55968e6b010876a100dff31f509b7d2975f2b981a7fdda6c2d9e452238b9fe54dc51f5d7b069e86de509d421d4efbdfc9194749b3f132 + micromark-util-types: "npm:^1.0.0" + checksum: 55c7d9019d6a39efaaed2c2e40b0aaa137d2c4f9c94cac82e93f509a806c3a775e4c815b5d8e986617450b68861a19776e4b886307e83db452b393f15a837b39 languageName: node linkType: hard -"micromark-extension-gfm-task-list-item@npm:^2.0.0": +"micromark-extension-gfm-task-list-item@npm:^1.0.0": + version: 1.0.5 + resolution: "micromark-extension-gfm-task-list-item@npm:1.0.5" + dependencies: + micromark-factory-space: "npm:^1.0.0" + micromark-util-character: "npm:^1.0.0" + micromark-util-symbol: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.0" + uvu: "npm:^0.5.0" + checksum: 46bb1baa10bfb785a2e3e2f975e5509260b9995d5c3aeddf77051957d218ce1af4ea737bcb6a56a930e62d42b05307b20632a400eff25cdb290789ff3170cad5 + languageName: node + linkType: hard + +"micromark-extension-gfm-task-list-item@npm:^2.0.1": version: 2.0.1 resolution: "micromark-extension-gfm-task-list-item@npm:2.0.1" dependencies: @@ -26570,19 +27682,30 @@ __metadata: languageName: node linkType: hard -"micromark-extension-gfm@npm:^3.0.0": - version: 3.0.0 - resolution: "micromark-extension-gfm@npm:3.0.0" +"micromark-extension-gfm@npm:^2.0.0": + version: 2.0.3 + resolution: "micromark-extension-gfm@npm:2.0.3" dependencies: - micromark-extension-gfm-autolink-literal: "npm:^2.0.0" - micromark-extension-gfm-footnote: "npm:^2.0.0" - micromark-extension-gfm-strikethrough: "npm:^2.0.0" - micromark-extension-gfm-table: "npm:^2.0.0" - micromark-extension-gfm-tagfilter: "npm:^2.0.0" - micromark-extension-gfm-task-list-item: "npm:^2.0.0" - micromark-util-combine-extensions: "npm:^2.0.0" - micromark-util-types: "npm:^2.0.0" - checksum: 8493d1041756bf21f9421fa6d357056bff6112aeccebc20595604686cdd908a6816765de297206457ae4c00f85fc58672bdbcbbc36820c25d561b1737af89055 + micromark-extension-gfm-autolink-literal: "npm:^1.0.0" + micromark-extension-gfm-footnote: "npm:^1.0.0" + micromark-extension-gfm-strikethrough: "npm:^1.0.0" + micromark-extension-gfm-table: "npm:^1.0.0" + micromark-extension-gfm-tagfilter: "npm:^1.0.0" + micromark-extension-gfm-task-list-item: "npm:^1.0.0" + micromark-util-combine-extensions: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.0" + checksum: 3ffd06ced4314abd0f0c72ec227f034f38dd47facbb62439ef3216d42f32433f3901d14675cf806e8d73689802a11849958b330bb5b55dd4fd5cdc64ebaf345c + languageName: node + linkType: hard + +"micromark-factory-destination@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-factory-destination@npm:1.1.0" + dependencies: + micromark-util-character: "npm:^1.0.0" + micromark-util-symbol: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.0" + checksum: 9e2b5fb5fedbf622b687e20d51eb3d56ae90c0e7ecc19b37bd5285ec392c1e56f6e21aa7cfcb3c01eda88df88fe528f3acb91a5f57d7f4cba310bc3cd7f824fa languageName: node linkType: hard @@ -26597,6 +27720,18 @@ __metadata: languageName: node linkType: hard +"micromark-factory-label@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-factory-label@npm:1.1.0" + dependencies: + micromark-util-character: "npm:^1.0.0" + micromark-util-symbol: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.0" + uvu: "npm:^0.5.0" + checksum: fcda48f1287d9b148c562c627418a2ab759cdeae9c8e017910a0cba94bb759a96611e1fc6df33182e97d28fbf191475237298983bb89ef07d5b02464b1ad28d5 + languageName: node + linkType: hard + "micromark-factory-label@npm:^2.0.0": version: 2.0.0 resolution: "micromark-factory-label@npm:2.0.0" @@ -26609,6 +27744,16 @@ __metadata: languageName: node linkType: hard +"micromark-factory-space@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-factory-space@npm:1.1.0" + dependencies: + micromark-util-character: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.0" + checksum: b58435076b998a7e244259a4694eb83c78915581206b6e7fc07b34c6abd36a1726ade63df8972fbf6c8fa38eecb9074f4e17be8d53f942e3b3d23d1a0ecaa941 + languageName: node + linkType: hard + "micromark-factory-space@npm:^2.0.0": version: 2.0.0 resolution: "micromark-factory-space@npm:2.0.0" @@ -26619,6 +27764,18 @@ __metadata: languageName: node linkType: hard +"micromark-factory-title@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-factory-title@npm:1.1.0" + dependencies: + micromark-factory-space: "npm:^1.0.0" + micromark-util-character: "npm:^1.0.0" + micromark-util-symbol: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.0" + checksum: 4432d3dbc828c81f483c5901b0c6591a85d65a9e33f7d96ba7c3ae821617a0b3237ff5faf53a9152d00aaf9afb3a9f185b205590f40ed754f1d9232e0e9157b1 + languageName: node + linkType: hard + "micromark-factory-title@npm:^2.0.0": version: 2.0.0 resolution: "micromark-factory-title@npm:2.0.0" @@ -26631,6 +27788,18 @@ __metadata: languageName: node linkType: hard +"micromark-factory-whitespace@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-factory-whitespace@npm:1.1.0" + dependencies: + micromark-factory-space: "npm:^1.0.0" + micromark-util-character: "npm:^1.0.0" + micromark-util-symbol: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.0" + checksum: ef0fa682c7d593d85a514ee329809dee27d10bc2a2b65217d8ef81173e33b8e83c549049764b1ad851adfe0a204dec5450d9d20a4ca8598f6c94533a73f73fcd + languageName: node + linkType: hard + "micromark-factory-whitespace@npm:^2.0.0": version: 2.0.0 resolution: "micromark-factory-whitespace@npm:2.0.0" @@ -26643,6 +27812,16 @@ __metadata: languageName: node linkType: hard +"micromark-util-character@npm:^1.0.0": + version: 1.2.0 + resolution: "micromark-util-character@npm:1.2.0" + dependencies: + micromark-util-symbol: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.0" + checksum: 88cf80f9b4c95266f24814ef587fb4180454668dcc3be4ac829e1227188cf349c8981bfca29e3eab1682f324c2c47544c0b0b799a26fbf9df5f156c6a84c970c + languageName: node + linkType: hard + "micromark-util-character@npm:^2.0.0": version: 2.0.1 resolution: "micromark-util-character@npm:2.0.1" @@ -26653,6 +27832,15 @@ __metadata: languageName: node linkType: hard +"micromark-util-chunked@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-util-chunked@npm:1.1.0" + dependencies: + micromark-util-symbol: "npm:^1.0.0" + checksum: c435bde9110cb595e3c61b7f54c2dc28ee03e6a57fa0fc1e67e498ad8bac61ee5a7457a2b6a73022ddc585676ede4b912d28dcf57eb3bd6951e54015e14dc20b + languageName: node + linkType: hard + "micromark-util-chunked@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-chunked@npm:2.0.0" @@ -26662,6 +27850,17 @@ __metadata: languageName: node linkType: hard +"micromark-util-classify-character@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-util-classify-character@npm:1.1.0" + dependencies: + micromark-util-character: "npm:^1.0.0" + micromark-util-symbol: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.0" + checksum: 8499cb0bb1f7fb946f5896285fcca65cd742f66cd3e79ba7744792bd413ec46834f932a286de650349914d02e822946df3b55d03e6a8e1d245d1ddbd5102e5b0 + languageName: node + linkType: hard + "micromark-util-classify-character@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-classify-character@npm:2.0.0" @@ -26673,6 +27872,16 @@ __metadata: languageName: node linkType: hard +"micromark-util-combine-extensions@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-util-combine-extensions@npm:1.1.0" + dependencies: + micromark-util-chunked: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.0" + checksum: ee78464f5d4b61ccb437850cd2d7da4d690b260bca4ca7a79c4bb70291b84f83988159e373b167181b6716cb197e309bc6e6c96a68cc3ba9d50c13652774aba9 + languageName: node + linkType: hard + "micromark-util-combine-extensions@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-combine-extensions@npm:2.0.0" @@ -26683,6 +27892,15 @@ __metadata: languageName: node linkType: hard +"micromark-util-decode-numeric-character-reference@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-util-decode-numeric-character-reference@npm:1.1.0" + dependencies: + micromark-util-symbol: "npm:^1.0.0" + checksum: 4733fe75146e37611243f055fc6847137b66f0cde74d080e33bd26d0408c1d6f44cabc984063eee5968b133cb46855e729d555b9ff8d744652262b7b51feec73 + languageName: node + linkType: hard + "micromark-util-decode-numeric-character-reference@npm:^2.0.0": version: 2.0.1 resolution: "micromark-util-decode-numeric-character-reference@npm:2.0.1" @@ -26692,6 +27910,18 @@ __metadata: languageName: node linkType: hard +"micromark-util-decode-string@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-util-decode-string@npm:1.1.0" + dependencies: + decode-named-character-reference: "npm:^1.0.0" + micromark-util-character: "npm:^1.0.0" + micromark-util-decode-numeric-character-reference: "npm:^1.0.0" + micromark-util-symbol: "npm:^1.0.0" + checksum: f1625155db452f15aa472918499689ba086b9c49d1322a08b22bfbcabe918c61b230a3002c8bc3ea9b1f52ca7a9bb1c3dd43ccb548c7f5f8b16c24a1ae77a813 + languageName: node + linkType: hard + "micromark-util-decode-string@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-decode-string@npm:2.0.0" @@ -26704,6 +27934,13 @@ __metadata: languageName: node linkType: hard +"micromark-util-encode@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-util-encode@npm:1.1.0" + checksum: 4ef29d02b12336918cea6782fa87c8c578c67463925221d4e42183a706bde07f4b8b5f9a5e1c7ce8c73bb5a98b261acd3238fecd152e6dd1cdfa2d1ae11b60a0 + languageName: node + linkType: hard + "micromark-util-encode@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-encode@npm:2.0.0" @@ -26711,6 +27948,13 @@ __metadata: languageName: node linkType: hard +"micromark-util-html-tag-name@npm:^1.0.0": + version: 1.2.0 + resolution: "micromark-util-html-tag-name@npm:1.2.0" + checksum: ccf0fa99b5c58676dc5192c74665a3bfd1b536fafaf94723bd7f31f96979d589992df6fcf2862eba290ef18e6a8efb30ec8e1e910d9f3fc74f208871e9f84750 + languageName: node + linkType: hard + "micromark-util-html-tag-name@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-html-tag-name@npm:2.0.0" @@ -26718,6 +27962,15 @@ __metadata: languageName: node linkType: hard +"micromark-util-normalize-identifier@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-util-normalize-identifier@npm:1.1.0" + dependencies: + micromark-util-symbol: "npm:^1.0.0" + checksum: 8655bea41ffa4333e03fc22462cb42d631bbef9c3c07b625fd852b7eb442a110f9d2e5902a42e65188d85498279569502bf92f3434a1180fc06f7c37edfbaee2 + languageName: node + linkType: hard + "micromark-util-normalize-identifier@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-normalize-identifier@npm:2.0.0" @@ -26727,6 +27980,15 @@ __metadata: languageName: node linkType: hard +"micromark-util-resolve-all@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-util-resolve-all@npm:1.1.0" + dependencies: + micromark-util-types: "npm:^1.0.0" + checksum: 1ce6c0237cd3ca061e76fae6602cf95014e764a91be1b9f10d36cb0f21ca88f9a07de8d49ab8101efd0b140a4fbfda6a1efb72027ab3f4d5b54c9543271dc52c + languageName: node + linkType: hard + "micromark-util-resolve-all@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-resolve-all@npm:2.0.0" @@ -26736,6 +27998,17 @@ __metadata: languageName: node linkType: hard +"micromark-util-sanitize-uri@npm:^1.0.0": + version: 1.2.0 + resolution: "micromark-util-sanitize-uri@npm:1.2.0" + dependencies: + micromark-util-character: "npm:^1.0.0" + micromark-util-encode: "npm:^1.0.0" + micromark-util-symbol: "npm:^1.0.0" + checksum: 0d024100d95ffb88bf75f3360e305b545c1eb745430959b8633f7aa93f37ec401fc7094c90c97298409a9e30d94d53b895bae224e1bb966bea114976cfa0fd48 + languageName: node + linkType: hard + "micromark-util-sanitize-uri@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-sanitize-uri@npm:2.0.0" @@ -26747,6 +28020,18 @@ __metadata: languageName: node linkType: hard +"micromark-util-subtokenize@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-util-subtokenize@npm:1.1.0" + dependencies: + micromark-util-chunked: "npm:^1.0.0" + micromark-util-symbol: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.0" + uvu: "npm:^0.5.0" + checksum: 075a1db6ea586d65827d3eead33dbfc520c4e43659c93fcd8fd82f44a7b75cfe61dcde967a3dfcc2ffd999347440ba5aa6698e65a04f3fc627e13e9f12a1a910 + languageName: node + linkType: hard + "micromark-util-subtokenize@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-subtokenize@npm:2.0.0" @@ -26759,6 +28044,13 @@ __metadata: languageName: node linkType: hard +"micromark-util-symbol@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-util-symbol@npm:1.1.0" + checksum: a26b6b1efd77a715a4d9bbe0a5338eaf3d04ea5e85733e34fee56dfeabf64495c0afc5438fe5220316884cd3a5eae1f17768e0ff4e117827ea4a653897466f86 + languageName: node + linkType: hard + "micromark-util-symbol@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-symbol@npm:2.0.0" @@ -26766,6 +28058,13 @@ __metadata: languageName: node linkType: hard +"micromark-util-types@npm:^1.0.0, micromark-util-types@npm:^1.0.1": + version: 1.1.0 + resolution: "micromark-util-types@npm:1.1.0" + checksum: 287ac5de4a3802bb6f6c3842197c294997a488db1c0486e03c7a8e674d9eb7720c17dda1bcb814814b8343b338c4826fcbc0555f3e75463712a60dcdb53a028e + languageName: node + linkType: hard + "micromark-util-types@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-types@npm:2.0.0" @@ -26773,6 +28072,31 @@ __metadata: languageName: node linkType: hard +"micromark@npm:^3.0.0": + version: 3.2.0 + resolution: "micromark@npm:3.2.0" + dependencies: + "@types/debug": "npm:^4.0.0" + debug: "npm:^4.0.0" + decode-named-character-reference: "npm:^1.0.0" + micromark-core-commonmark: "npm:^1.0.1" + micromark-factory-space: "npm:^1.0.0" + micromark-util-character: "npm:^1.0.0" + micromark-util-chunked: "npm:^1.0.0" + micromark-util-combine-extensions: "npm:^1.0.0" + micromark-util-decode-numeric-character-reference: "npm:^1.0.0" + micromark-util-encode: "npm:^1.0.0" + micromark-util-normalize-identifier: "npm:^1.0.0" + micromark-util-resolve-all: "npm:^1.0.0" + micromark-util-sanitize-uri: "npm:^1.0.0" + micromark-util-subtokenize: "npm:^1.0.0" + micromark-util-symbol: "npm:^1.0.0" + micromark-util-types: "npm:^1.0.1" + uvu: "npm:^0.5.0" + checksum: 560a4a501efc3859d622461aaa9345fb95b99a2f34d3d3f2a775ab04de1dd857cb0f642083a6b28ab01bd817f5f0741a1be9857fd702f45e04a3752927a66719 + languageName: node + linkType: hard + "micromark@npm:^4.0.0": version: 4.0.0 resolution: "micromark@npm:4.0.0" @@ -26881,6 +28205,13 @@ __metadata: languageName: node linkType: hard +"mimic-function@npm:^5.0.0": + version: 5.0.0 + resolution: "mimic-function@npm:5.0.0" + checksum: 1cb53bc250e4824544b89322f047ef37b2f70327cac67a9e5d64a192ac2b810dabc6a6e76e528751aae8558adf618de91fa0b69cec8514f96ee3cf1db81c4508 + languageName: node + linkType: hard + "mimic-response@npm:^1.0.0": version: 1.0.1 resolution: "mimic-response@npm:1.0.1" @@ -27219,7 +28550,7 @@ __metadata: languageName: node linkType: hard -"mri@npm:^1.2.0": +"mri@npm:^1.1.0, mri@npm:^1.2.0": version: 1.2.0 resolution: "mri@npm:1.2.0" checksum: 6775a1d2228bb9d191ead4efc220bd6be64f943ad3afd4dcb3b3ac8fc7b87034443f666e38805df38e8d047b29f910c3cc7810da0109af83e42c82c73bd3f6bc @@ -27371,7 +28702,14 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.3, nanoid@npm:^3.3.6": +"mute-stream@npm:1.0.0": + version: 1.0.0 + resolution: "mute-stream@npm:1.0.0" + checksum: 36fc968b0e9c9c63029d4f9dc63911950a3bdf55c9a87f58d3a266289b67180201cade911e7699f8b2fa596b34c9db43dad37649e3f7fdd13c3bb9edb0017ee7 + languageName: node + linkType: hard + +"nanoid@npm:^3.3.3, nanoid@npm:^3.3.7": version: 3.3.7 resolution: "nanoid@npm:3.3.7" bin: @@ -27380,12 +28718,12 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^5.0.1, nanoid@npm:^5.0.3": - version: 5.0.3 - resolution: "nanoid@npm:5.0.3" +"nanoid@npm:^5.0.1, nanoid@npm:^5.0.3, nanoid@npm:^5.0.4": + version: 5.0.4 + resolution: "nanoid@npm:5.0.4" bin: nanoid: bin/nanoid.js - checksum: 419f083464f91e148fa8f313d898e3271b2c148aa711d37c6251e5689b119d2e3ac766d5fa26e53214896969e9e9c6ccbce7af679c966d24490b1b5df94ffb5d + checksum: cf09cca3774f3147100948f7478f75f4c9ee97a4af65c328dd9abbd83b12f8bb35cf9f89a21c330f3b759d667a4cd0140ed84aa5fdd522c61e0d341aeaa7fb6f languageName: node linkType: hard @@ -27625,6 +28963,17 @@ __metadata: languageName: node linkType: hard +"node-gyp-build@npm:^4.2.2": + version: 4.7.1 + resolution: "node-gyp-build@npm:4.7.1" + bin: + node-gyp-build: bin.js + node-gyp-build-optional: optional.js + node-gyp-build-test: build-test.js + checksum: 3f6780a24dc7f6c47870ee1095a3f88aca9ca9c156dfdc390aee8f320fe94ebf8b91a361edd62aff7bf2eae469e25800378ed97533134d8580a8b9bdae75994c + languageName: node + linkType: hard + "node-gyp@npm:^9.0.0": version: 9.4.1 resolution: "node-gyp@npm:9.4.1" @@ -27730,6 +29079,17 @@ __metadata: languageName: node linkType: hard +"nopt@npm:^5.0.0": + version: 5.0.0 + resolution: "nopt@npm:5.0.0" + dependencies: + abbrev: "npm:1" + bin: + nopt: bin/nopt.js + checksum: 00f9bb2d16449469ba8ffcf9b8f0eae6bae285ec74b135fec533e5883563d2400c0cd70902d0a7759e47ac031ccf206ace4e86556da08ed3f1c66dda206e9ccd + languageName: node + linkType: hard + "nopt@npm:^6.0.0": version: 6.0.0 resolution: "nopt@npm:6.0.0" @@ -27863,6 +29223,18 @@ __metadata: languageName: node linkType: hard +"npmlog@npm:^5.0.1": + version: 5.0.1 + resolution: "npmlog@npm:5.0.1" + dependencies: + are-we-there-yet: "npm:^2.0.0" + console-control-strings: "npm:^1.1.0" + gauge: "npm:^3.0.0" + set-blocking: "npm:^2.0.0" + checksum: f42c7b9584cdd26a13c41a21930b6f5912896b6419ab15be88cc5721fc792f1c3dd30eb602b26ae08575694628ba70afdcf3675d86e4f450fc544757e52726ec + languageName: node + linkType: hard + "npmlog@npm:^6.0.0": version: 6.0.2 resolution: "npmlog@npm:6.0.2" @@ -28368,15 +29740,6 @@ __metadata: languageName: node linkType: hard -"p-event@npm:^5.0.1": - version: 5.0.1 - resolution: "p-event@npm:5.0.1" - dependencies: - p-timeout: "npm:^5.0.2" - checksum: 755a737e3d4fe912772daaa7262f7f3a4b45e3dbcfb0212a3a913c2db47b0981ddc2e9b1c5ec5fbbfb0cb622ce5b67bc04751ec8ced7e340398107e536d5aab2 - languageName: node - linkType: hard - "p-finally@npm:^1.0.0": version: 1.0.0 resolution: "p-finally@npm:1.0.0" @@ -28463,15 +29826,6 @@ __metadata: languageName: node linkType: hard -"p-locate@npm:^6.0.0": - version: 6.0.0 - resolution: "p-locate@npm:6.0.0" - dependencies: - p-limit: "npm:^4.0.0" - checksum: 2bfe5234efa5e7a4e74b30a5479a193fdd9236f8f6b4d2f3f69e3d286d9a7d7ab0c118a2a50142efcf4e41625def635bd9332d6cbf9cc65d85eb0718c579ab38 - languageName: node - linkType: hard - "p-map@npm:^3.0.0": version: 3.0.0 resolution: "p-map@npm:3.0.0" @@ -28490,12 +29844,10 @@ __metadata: languageName: node linkType: hard -"p-map@npm:^5.5.0": - version: 5.5.0 - resolution: "p-map@npm:5.5.0" - dependencies: - aggregate-error: "npm:^4.0.0" - checksum: 089a709d2525208a965b7907cc8e58af950542629b538198fc142c40e7f36b3b492dd6a46a1279515ccab58bb6f047e04593c0ab5ef4539d312adf7f761edf55 +"p-map@npm:^6.0.0": + version: 6.0.0 + resolution: "p-map@npm:6.0.0" + checksum: 1fd59257b3828a4c4def676ef64acb0edb7809b161ada25efd9a0c8db312ad81c66bcaa9e5d8fd982fd20d412609aabcb8da9b090e81f6c449bc1203752ba0eb languageName: node linkType: hard @@ -28509,13 +29861,13 @@ __metadata: languageName: node linkType: hard -"p-queue@npm:^7.4.1": - version: 7.4.1 - resolution: "p-queue@npm:7.4.1" +"p-queue@npm:^8.0.0": + version: 8.0.0 + resolution: "p-queue@npm:8.0.0" dependencies: eventemitter3: "npm:^5.0.1" - p-timeout: "npm:^5.0.2" - checksum: 82934551f20a38cc19b31cda7200f2db93ca99b8c642d3ac861d12a7a9160eb32235738a8cd53f1a7ea0c7b52d6c0bb27644b6461e9a51e6a59f1e8d65904b78 + p-timeout: "npm:^6.1.2" + checksum: 3d11784d48e5713989f712ccf33899157e88860a84c0e694fb8a1477f01ef1594a7633698ec1a4aebd38020f452a87adbe07733137c069f2fa5c392fe8e3fe99 languageName: node linkType: hard @@ -28538,10 +29890,10 @@ __metadata: languageName: node linkType: hard -"p-timeout@npm:^5.0.2": - version: 5.1.0 - resolution: "p-timeout@npm:5.1.0" - checksum: f5cd4e17301ff1ff1d8dbf2817df0ad88c6bba99349fc24d8d181827176ad4f8aca649190b8a5b1a428dfd6ddc091af4606835d3e0cb0656e04045da5c9e270c +"p-timeout@npm:^6.1.2": + version: 6.1.2 + resolution: "p-timeout@npm:6.1.2" + checksum: ca3ede368d792bd86fcfa4e133220536382225d31e5f62e2cedb8280df267b25f6684aa0056b22e8aa538cc85014b310058d8fdddeb0a1ff363093d56e87ac3a languageName: node linkType: hard @@ -28559,6 +29911,16 @@ __metadata: languageName: node linkType: hard +"package-config@npm:^5.0.0": + version: 5.0.0 + resolution: "package-config@npm:5.0.0" + dependencies: + find-up-simple: "npm:^1.0.0" + load-json-file: "npm:^7.0.1" + checksum: dfff5264c51a0dad7af9a55b02e3b8b6e457075e9c4f02d0ffacfeee9af4dd5db2b566dae41486412161292b8741483cd89d5a8404a5742fc54d718dadacac4a + languageName: node + linkType: hard + "package-hash@npm:^4.0.0": version: 4.0.0 resolution: "package-hash@npm:4.0.0" @@ -28756,13 +30118,6 @@ __metadata: languageName: node linkType: hard -"path-exists@npm:^5.0.0": - version: 5.0.0 - resolution: "path-exists@npm:5.0.0" - checksum: 8ca842868cab09423994596eb2c5ec2a971c17d1a3cb36dbf060592c730c725cd524b9067d7d2a1e031fef9ba7bd2ac6dc5ec9fb92aa693265f7be3987045254 - languageName: node - linkType: hard - "path-is-absolute@npm:^1.0.0": version: 1.0.1 resolution: "path-is-absolute@npm:1.0.1" @@ -28884,6 +30239,13 @@ __metadata: languageName: node linkType: hard +"path-type@npm:^5.0.0": + version: 5.0.0 + resolution: "path-type@npm:5.0.0" + checksum: 15ec24050e8932c2c98d085b72cfa0d6b4eeb4cbde151a0a05726d8afae85784fc5544f733d8dfc68536587d5143d29c0bd793623fad03d7e61cc00067291cd5 + languageName: node + linkType: hard + "pathe@npm:^1.1.0, pathe@npm:^1.1.1": version: 1.1.1 resolution: "pathe@npm:1.1.1" @@ -28961,13 +30323,20 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.3, picomatch@npm:^2.3.0, picomatch@npm:^2.3.1": +"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.2, picomatch@npm:^2.2.3, picomatch@npm:^2.3.0, picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" checksum: 60c2595003b05e4535394d1da94850f5372c9427ca4413b71210f437f7b2ca091dbd611c45e8b37d10036fa8eade25c1b8951654f9d3973bfa66a2ff4d3b08bc languageName: node linkType: hard +"picomatch@npm:^3.0.1": + version: 3.0.1 + resolution: "picomatch@npm:3.0.1" + checksum: 65ac837fedbd0640586f7c214f6c7481e1e12f41cdcd22a95eb6a2914d1773707ed0f0b5bd2d1e39b5ec7860b43a4c9150152332a3884cd8dd1d419b2a2fa5b5 + languageName: node + linkType: hard + "pidtree@npm:0.6.0": version: 0.6.0 resolution: "pidtree@npm:0.6.0" @@ -28998,16 +30367,6 @@ __metadata: languageName: node linkType: hard -"pkg-conf@npm:^4.0.0": - version: 4.0.0 - resolution: "pkg-conf@npm:4.0.0" - dependencies: - find-up: "npm:^6.0.0" - load-json-file: "npm:^7.0.0" - checksum: 6da0c064a74f6c7ae80d7d68c5853e14f7e762a2a80c6ca9e0aa827002b90b69c86fefe3bac830b10a6f1739e7f96a1f728637f2a141e50b0fdafe92a2c3eab6 - languageName: node - linkType: hard - "pkg-dir@npm:^3.0.0": version: 3.0.0 resolution: "pkg-dir@npm:3.0.0" @@ -29503,14 +30862,14 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.3.6, postcss@npm:^8.4.21, postcss@npm:^8.4.27, postcss@npm:^8.4.31": - version: 8.4.31 - resolution: "postcss@npm:8.4.31" +"postcss@npm:^8.3.6, postcss@npm:^8.4.21, postcss@npm:^8.4.31, postcss@npm:^8.4.32": + version: 8.4.32 + resolution: "postcss@npm:8.4.32" dependencies: - nanoid: "npm:^3.3.6" + nanoid: "npm:^3.3.7" picocolors: "npm:^1.0.0" source-map-js: "npm:^1.0.2" - checksum: 1a6653e72105907377f9d4f2cd341d8d90e3fde823a5ddea1e2237aaa56933ea07853f0f2758c28892a1d70c53bbaca200eb8b80f8ed55f13093003dbec5afa0 + checksum: 28084864122f29148e1f632261c408444f5ead0e0b9ea9bd9729d0468818ebe73fe5dc0075acd50c1365dbe639b46a79cba27d355ec857723a24bc9af0f18525 languageName: node linkType: hard @@ -29793,6 +31152,13 @@ __metadata: languageName: node linkType: hard +"property-information@npm:^6.0.0": + version: 6.4.0 + resolution: "property-information@npm:6.4.0" + checksum: 853302c207586fa26b11c104d0cf1f832d079adda52985fae901eee8c0c1f3d1c3105f3306f5655614f5017f34d0a46664573f5e9d97b108629b1b8f1bf7f110 + languageName: node + linkType: hard + "protobufjs@npm:^7.0.0, protobufjs@npm:^7.2.3, protobufjs@npm:^7.2.4": version: 7.2.5 resolution: "protobufjs@npm:7.2.5" @@ -30788,6 +32154,28 @@ __metadata: languageName: node linkType: hard +"rehype-parse@npm:^9.0.0": + version: 9.0.0 + resolution: "rehype-parse@npm:9.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + hast-util-from-html: "npm:^2.0.0" + unified: "npm:^11.0.0" + checksum: fe35d7745d7266269f230eed81474e92bda43bd367a01d900926201bb1005e775bcbed9bbba117bd125cab49cf7ac922258927890abffabc96348ce681266372 + languageName: node + linkType: hard + +"rehype-stringify@npm:^10.0.0": + version: 10.0.0 + resolution: "rehype-stringify@npm:10.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + hast-util-to-html: "npm:^9.0.0" + unified: "npm:^11.0.0" + checksum: d5cf4e0951f5fa6a65c69479fbe974f1b75298170de2c4618e0e91cceba6a52c6094926e26c9501252120572f3daba02fb6d09436cc483282b0b647f1d970b66 + languageName: node + linkType: hard + "relateurl@npm:^0.2.7": version: 0.2.7 resolution: "relateurl@npm:0.2.7" @@ -30828,17 +32216,15 @@ __metadata: languageName: node linkType: hard -"remark-gfm@npm:^4.0.0": - version: 4.0.0 - resolution: "remark-gfm@npm:4.0.0" +"remark-gfm@npm:^3.0.1": + version: 3.0.1 + resolution: "remark-gfm@npm:3.0.1" dependencies: - "@types/mdast": "npm:^4.0.0" - mdast-util-gfm: "npm:^3.0.0" - micromark-extension-gfm: "npm:^3.0.0" - remark-parse: "npm:^11.0.0" - remark-stringify: "npm:^11.0.0" - unified: "npm:^11.0.0" - checksum: 9f7b17aae0e9dc79ba9c989c2a679baff7161e1831a87307cfa2e0e9b0c492bd8c1900cdf7305855b898a2a9fab9aa8e586d71ce49cbc1ea90f68b714c249c0d + "@types/mdast": "npm:^3.0.0" + mdast-util-gfm: "npm:^2.0.0" + micromark-extension-gfm: "npm:^2.0.0" + unified: "npm:^10.0.0" + checksum: 8ec301f5fb1f52c548b5a6d7ca6a3422d55db73cd703f147c979d16dca003f065181f55404d6f3f49d33f1faca3fe56ae731ed7fe0acc00cd945a8e605f155f2 languageName: node linkType: hard @@ -31277,7 +32663,7 @@ __metadata: languageName: node linkType: hard -"rollup@npm:^2.25.0 || ^3.3.0, rollup@npm:^3.27.1, rollup@npm:^3.29.4": +"rollup@npm:^2.25.0 || ^3.3.0": version: 3.29.4 resolution: "rollup@npm:3.29.4" dependencies: @@ -31291,6 +32677,56 @@ __metadata: languageName: node linkType: hard +"rollup@npm:^4.0.0, rollup@npm:^4.2.0": + version: 4.6.1 + resolution: "rollup@npm:4.6.1" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.6.1" + "@rollup/rollup-android-arm64": "npm:4.6.1" + "@rollup/rollup-darwin-arm64": "npm:4.6.1" + "@rollup/rollup-darwin-x64": "npm:4.6.1" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.6.1" + "@rollup/rollup-linux-arm64-gnu": "npm:4.6.1" + "@rollup/rollup-linux-arm64-musl": "npm:4.6.1" + "@rollup/rollup-linux-x64-gnu": "npm:4.6.1" + "@rollup/rollup-linux-x64-musl": "npm:4.6.1" + "@rollup/rollup-win32-arm64-msvc": "npm:4.6.1" + "@rollup/rollup-win32-ia32-msvc": "npm:4.6.1" + "@rollup/rollup-win32-x64-msvc": "npm:4.6.1" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 32fcbb3954597c27fe493d8dcebc24c3ddff8eab2150829cfb2161761038a9bd64873f51a90a6bfce522a70201318d764371e78ed294fc7aa019804f1dac7f08 + languageName: node + linkType: hard + "run-async@npm:^2.4.0": version: 2.4.1 resolution: "run-async@npm:2.4.1" @@ -31298,6 +32734,13 @@ __metadata: languageName: node linkType: hard +"run-async@npm:^3.0.0": + version: 3.0.0 + resolution: "run-async@npm:3.0.0" + checksum: 97fb8747f7765b77ebcd311d3a33548099336f04c6434e0763039b98c1de0f1b4421000695aff8751f309c0b995d8dfd620c1f1e4c35572da38c101488165305 + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -31325,6 +32768,15 @@ __metadata: languageName: node linkType: hard +"sade@npm:^1.7.3": + version: 1.8.1 + resolution: "sade@npm:1.8.1" + dependencies: + mri: "npm:^1.1.0" + checksum: 1c67ba03c94083e0ae307ff5564ecb86c2104c0f558042fdaa40ea0054f91a63a9783f14069870f2f784336adabb70f90f22a84dc457b5a25e859aaadefe0910 + languageName: node + linkType: hard + "safe-buffer@npm:5.1.2, safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": version: 5.1.2 resolution: "safe-buffer@npm:5.1.2" @@ -31815,7 +33267,7 @@ __metadata: languageName: node linkType: hard -"slash@npm:^5.0.0": +"slash@npm:^5.0.0, slash@npm:^5.1.0": version: 5.1.0 resolution: "slash@npm:5.1.0" checksum: 2c41ec6fb1414cd9bba0fa6b1dd00e8be739e3fe85d079c69d4b09ca5f2f86eafd18d9ce611c0c0f686428638a36c272a6ac14799146a8295f259c10cc45cde4 @@ -31960,10 +33412,10 @@ __metadata: languageName: node linkType: hard -"sortablejs@npm:^1.15.0": - version: 1.15.0 - resolution: "sortablejs@npm:1.15.0" - checksum: f93a8e2f34b9fced858d09056fe9da55b022d51401cddee652b19449be99ce7b4e42f3bfad926d7e2a34ac4ca681296c5ba129f33ddbd4fc1f69c04af741049d +"sortablejs@npm:^1.15.1": + version: 1.15.1 + resolution: "sortablejs@npm:1.15.1" + checksum: c4ccbc60e7936321eee0e07448e04b1f4481fed727ed1ace86866f49f9929e3acda2b7b825894e7ece5b8bc41f3c135eb4d00d09a287de1ee896d8256a0a84df languageName: node linkType: hard @@ -32052,6 +33504,13 @@ __metadata: languageName: node linkType: hard +"space-separated-tokens@npm:^2.0.0": + version: 2.0.2 + resolution: "space-separated-tokens@npm:2.0.2" + checksum: 202e97d7ca1ba0758a0aa4fe226ff98142073bcceeff2da3aad037968878552c3bbce3b3231970025375bbba5aee00c5b8206eda408da837ab2dc9c0f26be990 + languageName: node + linkType: hard + "spawn-command@npm:0.0.2": version: 0.0.2 resolution: "spawn-command@npm:0.0.2" @@ -32483,6 +33942,16 @@ __metadata: languageName: node linkType: hard +"stringify-entities@npm:^4.0.0": + version: 4.0.3 + resolution: "stringify-entities@npm:4.0.3" + dependencies: + character-entities-html4: "npm:^2.0.0" + character-entities-legacy: "npm:^3.0.0" + checksum: 3dc827fbcc9b5feb252d942a21caca89297272d857260448174ca264018726308b48e02ad492f89a2b5faebf7241be56f5a4d9cbf050cfaf5db607d6e5ceb9e7 + languageName: node + linkType: hard + "strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" @@ -33291,6 +34760,13 @@ __metadata: languageName: node linkType: hard +"toml@npm:^3.0.0": + version: 3.0.0 + resolution: "toml@npm:3.0.0" + checksum: cfef0966868d552bd02e741f30945a611f70841b7cddb07ea2b17441fe32543985bc0a7c0dcf7971af26fcaf8a17712a485d911f46bfe28644536e9a71a2bd09 + languageName: node + linkType: hard + "totalist@npm:^3.0.0": version: 3.0.1 resolution: "totalist@npm:3.0.1" @@ -33325,6 +34801,13 @@ __metadata: languageName: node linkType: hard +"trim-lines@npm:^3.0.0": + version: 3.0.1 + resolution: "trim-lines@npm:3.0.1" + checksum: 7a1325e4ce8ff7e9e52007600e9c9862a166d0db1f1cf0c9357e359e410acab1278fcd91cc279dfa5123fc37b69f080de02f471e91dbbc61b155b9ca92597929 + languageName: node + linkType: hard + "trim-newlines@npm:^3.0.0": version: 3.0.1 resolution: "trim-newlines@npm:3.0.1" @@ -33467,7 +34950,7 @@ __metadata: languageName: node linkType: hard -"typanion@npm:^3.8.0": +"typanion@npm:^3.14.0, typanion@npm:^3.8.0": version: 3.14.0 resolution: "typanion@npm:3.14.0" checksum: 5e88d9e6121ff0ec543f572152fdd1b70e9cca35406d79013ec8e08defa8ef96de5fec9e98da3afbd1eb4426b9e8e8fe423163d0b482e34a40103cab1ef29abd @@ -33686,7 +35169,7 @@ __metadata: languageName: node linkType: hard -"undici@npm:^5.22.1, undici@npm:^5.27.2": +"undici@npm:^5.22.1": version: 5.28.0 resolution: "undici@npm:5.28.0" dependencies: @@ -33695,6 +35178,15 @@ __metadata: languageName: node linkType: hard +"undici@npm:^6.0.0": + version: 6.0.1 + resolution: "undici@npm:6.0.1" + dependencies: + "@fastify/busboy": "npm:^2.0.0" + checksum: 48d7f3c9aa9e1ca3fb2488c8aa25838e9163d3a7f1e41d695ed110006abc26648cf97c15d76abfd896fe16128e34136c8a7e0723e0583451ed9f34aaa37513ad + languageName: node + linkType: hard + "unicode-canonical-property-names-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" @@ -33726,6 +35218,28 @@ __metadata: languageName: node linkType: hard +"unicorn-magic@npm:^0.1.0": + version: 0.1.0 + resolution: "unicorn-magic@npm:0.1.0" + checksum: 9b4d0e9809807823dc91d0920a4a4c0cff2de3ebc54ee87ac1ee9bc75eafd609b09d1f14495e0173aef26e01118706196b6ab06a75fe0841028b3983a8af313f + languageName: node + linkType: hard + +"unified@npm:^10.0.0": + version: 10.1.2 + resolution: "unified@npm:10.1.2" + dependencies: + "@types/unist": "npm:^2.0.0" + bail: "npm:^2.0.0" + extend: "npm:^3.0.0" + is-buffer: "npm:^2.0.0" + is-plain-obj: "npm:^4.0.0" + trough: "npm:^2.0.0" + vfile: "npm:^5.0.0" + checksum: 6cffebcefc3290be26d25a58ba714cda943142782baf320fddf374ca3a319bdaabb006f96df4be17b8b367f5e6f6e113b1027c52ef66154846a7a110550f6688 + languageName: node + linkType: hard + "unified@npm:^11.0.0, unified@npm:^11.0.4": version: 11.0.4 resolution: "unified@npm:11.0.4" @@ -33793,6 +35307,15 @@ __metadata: languageName: node linkType: hard +"unist-util-is@npm:^5.0.0": + version: 5.2.1 + resolution: "unist-util-is@npm:5.2.1" + dependencies: + "@types/unist": "npm:^2.0.0" + checksum: c10f6c07aad4f4830ffa8ea82b42a2c8d5cd36c7555e27889e5fee953040af321e4e6f4e52c4edb606604de75d7230a5f4bc7b71b8ac3e874a26ab595c2057e4 + languageName: node + linkType: hard + "unist-util-is@npm:^6.0.0": version: 6.0.0 resolution: "unist-util-is@npm:6.0.0" @@ -33802,6 +35325,24 @@ __metadata: languageName: node linkType: hard +"unist-util-position@npm:^5.0.0": + version: 5.0.0 + resolution: "unist-util-position@npm:5.0.0" + dependencies: + "@types/unist": "npm:^3.0.0" + checksum: 89d4da00e74618d7562ac7ac288961df9bcd4ccca6df3b5a90650f018eceb6b95de6e771e88bdbef46cc9d96861d456abe57b7ad1108921e0feb67c6292aa29d + languageName: node + linkType: hard + +"unist-util-stringify-position@npm:^3.0.0": + version: 3.0.3 + resolution: "unist-util-stringify-position@npm:3.0.3" + dependencies: + "@types/unist": "npm:^2.0.0" + checksum: 07913e4fd77fe57d95f8b2f771354f97a29082229c1ad14ceedce6bbc77b2d784ca8296563335471cdca97915e548204bd6f098ea5b808b822b4b54087662cfb + languageName: node + linkType: hard + "unist-util-stringify-position@npm:^4.0.0": version: 4.0.0 resolution: "unist-util-stringify-position@npm:4.0.0" @@ -33821,6 +35362,16 @@ __metadata: languageName: node linkType: hard +"unist-util-visit-parents@npm:^5.0.0, unist-util-visit-parents@npm:^5.1.1": + version: 5.1.3 + resolution: "unist-util-visit-parents@npm:5.1.3" + dependencies: + "@types/unist": "npm:^2.0.0" + unist-util-is: "npm:^5.0.0" + checksum: 5381fc57a129d478d983b988d86b72a1266d6f91fc608562b00bfa76596128d6e4d1c2b26ced64d96e55eb5d27d620081b4ee9703979bab63e1210789e781372 + languageName: node + linkType: hard + "unist-util-visit-parents@npm:^6.0.0": version: 6.0.1 resolution: "unist-util-visit-parents@npm:6.0.1" @@ -33842,6 +35393,17 @@ __metadata: languageName: node linkType: hard +"unist-util-visit@npm:^4.0.0": + version: 4.1.2 + resolution: "unist-util-visit@npm:4.1.2" + dependencies: + "@types/unist": "npm:^2.0.0" + unist-util-is: "npm:^5.0.0" + unist-util-visit-parents: "npm:^5.1.1" + checksum: e3b20c6b1f5ae1b7b40bbf9be49103a342d98fad98bdf958110c20d72e5923bd3f12966b6702459bc61ab832facb5af418a79af87cefa7a8a41b892369678b13 + languageName: node + linkType: hard + "unist-util-visit@npm:^5.0.0": version: 5.0.0 resolution: "unist-util-visit@npm:5.0.0" @@ -33853,6 +35415,13 @@ __metadata: languageName: node linkType: hard +"universal-user-agent@npm:^6.0.0": + version: 6.0.1 + resolution: "universal-user-agent@npm:6.0.1" + checksum: fdc8e1ae48a05decfc7ded09b62071f571c7fe0bd793d700704c80cea316101d4eac15cc27ed2bb64f4ce166d2684777c3198b9ab16034f547abea0d3aa1c93c + languageName: node + linkType: hard + "universalify@npm:^0.1.0": version: 0.1.2 resolution: "universalify@npm:0.1.2" @@ -34161,6 +35730,20 @@ __metadata: languageName: node linkType: hard +"uvu@npm:^0.5.0": + version: 0.5.6 + resolution: "uvu@npm:0.5.6" + dependencies: + dequal: "npm:^2.0.0" + diff: "npm:^5.0.0" + kleur: "npm:^4.0.3" + sade: "npm:^1.7.3" + bin: + uvu: bin.js + checksum: 66ba25afc6732249877f9f4f8b6146f3aaa97538c51cf498f55825d602c33dbb903e02c7e1547cbca6bdfbb609e07eb7ea758b5156002ac2dd5072f00606f8d9 + languageName: node + linkType: hard + "v8-compile-cache-lib@npm:^3.0.1": version: 3.0.1 resolution: "v8-compile-cache-lib@npm:3.0.1" @@ -34245,6 +35828,26 @@ __metadata: languageName: node linkType: hard +"vfile-location@npm:^5.0.0": + version: 5.0.2 + resolution: "vfile-location@npm:5.0.2" + dependencies: + "@types/unist": "npm:^3.0.0" + vfile: "npm:^6.0.0" + checksum: b61c048cedad3555b4f007f390412c6503f58a6a130b58badf4ee340c87e0d7421e9c86bbc1494c57dedfccadb60f5176cc60ba3098209d99fb3a3d8804e4c38 + languageName: node + linkType: hard + +"vfile-message@npm:^3.0.0": + version: 3.1.4 + resolution: "vfile-message@npm:3.1.4" + dependencies: + "@types/unist": "npm:^2.0.0" + unist-util-stringify-position: "npm:^3.0.0" + checksum: 423ca87f4427a403e4688d7ec663a2e6add694eefac47c945746463377428c7553bc613058841f1da83e18b68af886d3dd11cb96d582b5cc3c98e11efb7e55e9 + languageName: node + linkType: hard + "vfile-message@npm:^4.0.0": version: 4.0.2 resolution: "vfile-message@npm:4.0.2" @@ -34255,6 +35858,18 @@ __metadata: languageName: node linkType: hard +"vfile@npm:^5.0.0": + version: 5.3.7 + resolution: "vfile@npm:5.3.7" + dependencies: + "@types/unist": "npm:^2.0.0" + is-buffer: "npm:^2.0.0" + unist-util-stringify-position: "npm:^3.0.0" + vfile-message: "npm:^3.0.0" + checksum: d8f59b419d4c83b3ed24f500cf02393149b728f8803f88519c18fe0733f62544fa9ab0d8425a8bc7835181d848b9ce29c014168dc45af72f416074bbe475f643 + languageName: node + linkType: hard + "vfile@npm:^6.0.0": version: 6.0.1 resolution: "vfile@npm:6.0.1" @@ -34365,16 +35980,16 @@ __metadata: languageName: node linkType: hard -"vite@npm:^4.4.11": - version: 4.5.0 - resolution: "vite@npm:4.5.0" +"vite@npm:^5.0.6": + version: 5.0.6 + resolution: "vite@npm:5.0.6" dependencies: - esbuild: "npm:^0.18.10" - fsevents: "npm:~2.3.2" - postcss: "npm:^8.4.27" - rollup: "npm:^3.27.1" + esbuild: "npm:^0.19.3" + fsevents: "npm:~2.3.3" + postcss: "npm:^8.4.32" + rollup: "npm:^4.2.0" peerDependencies: - "@types/node": ">= 14" + "@types/node": ^18.0.0 || >=20.0.0 less: "*" lightningcss: ^1.21.0 sass: "*" @@ -34401,7 +36016,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: b262ea4880ba7de8a77b0a665c771561ae3cb7f0d6c5b90e65298039755192550bf90cb96a2910d564506e2d499aa20e9becd330b835c34d414249083ac6e40c + checksum: c4647591c76ad5a3b818efef37fb23d793512be832b7daa6c54719f24b0dbadfe383b18cf0896e2c4eaf9d1acab031b3caeaa36570b79ce9da2be6a328d27a1e languageName: node linkType: hard @@ -34695,6 +36310,13 @@ __metadata: languageName: node linkType: hard +"web-namespaces@npm:^2.0.0": + version: 2.0.1 + resolution: "web-namespaces@npm:2.0.1" + checksum: b6d9f02f1a43d0ef0848a812d89c83801d5bbad57d8bb61f02eb6d7eb794c3736f6cc2e1191664bb26136594c8218ac609f4069722c6f56d9fc2d808fa9271c6 + languageName: node + linkType: hard + "web-streams-polyfill@npm:4.0.0-beta.3": version: 4.0.0-beta.3 resolution: "web-streams-polyfill@npm:4.0.0-beta.3" @@ -35025,7 +36647,7 @@ __metadata: languageName: node linkType: hard -"wide-align@npm:^1.1.5": +"wide-align@npm:^1.1.2, wide-align@npm:^1.1.5": version: 1.1.5 resolution: "wide-align@npm:1.1.5" dependencies: @@ -35345,8 +36967,8 @@ __metadata: version: 0.0.0-use.local resolution: "y-provider@workspace:packages/common/y-provider" dependencies: - "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" - vite: "npm:^4.4.11" + "@blocksuite/store": "npm:0.11.0-nightly-202312070955-2b5bb47" + vite: "npm:^5.0.6" vite-plugin-dts: "npm:3.6.0" vitest: "npm:0.34.6" yjs: "npm:^13.6.10" @@ -35574,7 +37196,7 @@ __metadata: languageName: node linkType: hard -"zwitch@npm:^2.0.0": +"zwitch@npm:^2.0.0, zwitch@npm:^2.0.4": version: 2.0.4 resolution: "zwitch@npm:2.0.4" checksum: f22ec5fc2d5f02c423c93d35cdfa83573a3a3bd98c66b927c368ea4d0e7252a500df2a90a6b45522be536a96a73404393c958e945fdba95e6832c200791702b6