diff --git a/.eslintignore b/.eslintignore index c661e0c283..1b4baef2f6 100644 --- a/.eslintignore +++ b/.eslintignore @@ -11,6 +11,4 @@ e2e-dist-* static web-static public -packages/common/sdk/src/*.d.ts -packages/common/sdk/src/*.js packages/frontend/i18n/src/i18n-generated.ts diff --git a/.eslintrc.js b/.eslintrc.js index 5e5566f176..240f89f93f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -61,7 +61,6 @@ const allPackages = [ 'packages/frontend/core', 'packages/frontend/electron', 'packages/frontend/graphql', - 'packages/frontend/hooks', 'packages/frontend/i18n', 'packages/frontend/native', 'packages/frontend/templates', @@ -69,10 +68,8 @@ const allPackages = [ 'packages/common/debug', 'packages/common/env', 'packages/common/infra', - 'packages/common/sdk', 'packages/common/theme', 'packages/common/y-indexeddb', - 'packages/plugins/copilot', 'tools/cli', 'tests/storybook', ]; diff --git a/.github/CLA.md b/.github/CLA.md index 6c40ce67cc..530146a257 100644 --- a/.github/CLA.md +++ b/.github/CLA.md @@ -33,32 +33,6 @@ You accept and agree to the following terms and conditions for your past, presen 9. This Agreement will be governed by the laws of Republic of Singapore without reference to conflict of laws principles. -## List of Contributors +## How To Sign -The below-signed are contributors to a code repository that is part of the project named "AFFiNE". Each below-signed contributor has read, understand and agrees to the terms above in the section within this document entitled "AFFiNE Contributor License Agreement" as of the date beside their real name (or entity name) and GitHub account name. - ---- - - - -- Dark Sky, @darkskygit, 2022/07/22 -- Lin Onetwo, @linonetwo, 2022/02/14 -- zqran, @zqran, 2023/02/17 -- Alessio Gravili, @AlessioGr, 2023/03/04 -- Victor Nanka, @victornanka, 2023/03/09 -- Aditya Sharma, @adityash1, 2023/03/21 -- Fangdun Tsai, @fundon, 2023/03/21 -- Zhilin Liu, @lzlme, 2023/04/09 -- Skye Sun, @skyesun, 2023/04/14 -- Jordy Delgado, @Jdelgad8, 2023/04/17 -- Howard Do, @howarddo2208, 2023/04/20 -- 三咲智子 Kevin Deng, @sxzz, 2023/04/21 -- Moeyua, @moeyua, 2023/04/22 -- Shishu, @shishudesu, 2023/05/19 -- Kushagra Singh, @kush002, 2023/06/28 -- Sarvesh Kumar, @sarvesh521 2023/08/25 -- 微扰理论 Qinghao Huang, @wfnuser 2023/09/29 +Visit https://cla-assistant.io/toeverything/AFFiNE and sign it. diff --git a/.github/actions/deploy/deploy.mjs b/.github/actions/deploy/deploy.mjs index 204d8471ad..ea8ab78e13 100644 --- a/.github/actions/deploy/deploy.mjs +++ b/.github/actions/deploy/deploy.mjs @@ -13,7 +13,6 @@ const { R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, - R2_BUCKET, ENABLE_CAPTCHA, CAPTCHA_TURNSTILE_SECRET, OAUTH_EMAIL_SENDER, @@ -96,7 +95,6 @@ const createHelmCommand = ({ isDryRun }) => { `--set-string graphql.app.objectStorage.r2.accountId="${R2_ACCOUNT_ID}"`, `--set-string graphql.app.objectStorage.r2.accessKeyId="${R2_ACCESS_KEY_ID}"`, `--set-string graphql.app.objectStorage.r2.secretAccessKey="${R2_SECRET_ACCESS_KEY}"`, - `--set-string graphql.app.objectStorage.r2.bucket="${R2_BUCKET}"`, `--set-string graphql.app.oauth.email.sender="${OAUTH_EMAIL_SENDER}"`, `--set-string graphql.app.oauth.email.login="${OAUTH_EMAIL_LOGIN}"`, `--set-string graphql.app.oauth.email.password="${OAUTH_EMAIL_PASSWORD}"`, diff --git a/.github/actions/download-core/action.yml b/.github/actions/download-core/action.yml index c70fcfbe9b..2746bd8455 100644 --- a/.github/actions/download-core/action.yml +++ b/.github/actions/download-core/action.yml @@ -9,7 +9,7 @@ runs: using: 'composite' steps: - name: Download tar.gz - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: core path: . diff --git a/.github/actions/setup-node/action.yml b/.github/actions/setup-node/action.yml index 79d2602c77..addc1ef70c 100644 --- a/.github/actions/setup-node/action.yml +++ b/.github/actions/setup-node/action.yml @@ -21,14 +21,6 @@ inputs: description: 'set nmMode to hardlinks-local in .yarnrc.yml' required: false default: 'true' - build-infra: - description: 'Build infra' - required: false - default: 'true' - build-plugins: - description: 'Build plugins' - required: false - default: 'true' nmHoistingLimits: description: 'Set nmHoistingLimits in .yarnrc.yml' required: false @@ -190,13 +182,3 @@ runs: run: node ./node_modules/electron/install.js env: electron_config_cache: ./node_modules/.cache/electron - - - name: Build Infra - shell: bash - if: inputs.build-infra == 'true' - run: yarn run build:infra - - - name: Build Plugins - if: inputs.build-plugins == 'true' - shell: bash - run: yarn run build:plugins diff --git a/.github/deployment/front/Dockerfile b/.github/deployment/front/Dockerfile index 8bff27af71..2aa4d6913d 100644 --- a/.github/deployment/front/Dockerfile +++ b/.github/deployment/front/Dockerfile @@ -1,4 +1,4 @@ -FROM openresty/openresty:1.21.4.1-0-buster +FROM openresty/openresty:1.21.4.3-0-buster WORKDIR /app COPY ./packages/frontend/core/dist ./dist COPY ./.github/deployment/front/nginx.conf /usr/local/openresty/nginx/conf/nginx.conf diff --git a/.github/helm/affine-cloud/.gitignore b/.github/helm/affine-cloud/.gitignore deleted file mode 100644 index 711a39c541..0000000000 --- a/.github/helm/affine-cloud/.gitignore +++ /dev/null @@ -1 +0,0 @@ -charts/ \ No newline at end of file diff --git a/.github/helm/affine-cloud/.helmignore b/.github/helm/affine-cloud/.helmignore deleted file mode 100644 index 691fa13d6a..0000000000 --- a/.github/helm/affine-cloud/.helmignore +++ /dev/null @@ -1,23 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ \ No newline at end of file diff --git a/.github/helm/affine-cloud/Chart.lock b/.github/helm/affine-cloud/Chart.lock deleted file mode 100644 index 211ccfd106..0000000000 --- a/.github/helm/affine-cloud/Chart.lock +++ /dev/null @@ -1,6 +0,0 @@ -dependencies: -- name: postgresql - repository: https://charts.bitnami.com/bitnami - 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 deleted file mode 100644 index 18465fef65..0000000000 --- a/.github/helm/affine-cloud/Chart.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v2 -name: affine-cloud -description: A Helm chart for AFFiNE Cloud - -type: application -version: 0.6.1 -appVersion: '0.6.1' - -dependencies: - - name: postgresql - version: 13.2.23 - repository: https://charts.bitnami.com/bitnami diff --git a/.github/helm/affine-cloud/readme.md b/.github/helm/affine-cloud/readme.md deleted file mode 100644 index 0cac15cf57..0000000000 --- a/.github/helm/affine-cloud/readme.md +++ /dev/null @@ -1,30 +0,0 @@ -# Helm Chart Configuration - -The following table lists the configurable parameters of this Helm chart and their default values. - -## AFFiNE Cloud Server parameters - -| Parameter | Description | Default | -| ------------------------------ | -------------------------------------------------- | ------------------ | -| `affineCloud.tag` | The Docker tag of the AffineCloud image to be used | `'nightly-latest'` | -| `affineCloud.resources.cpu` | The CPU resources allocated for AffineCloud | `'250m'` | -| `affineCloud.resources.memory` | The memory resources allocated for AffineCloud | `'0.5Gi'` | -| `affineCloud.signKey` | The key used to sign the JWT tokens | `'c2VjcmV0'` | -| `affineCloud.service.type` | The type of the Kubernetes service | `'ClusterIP'` | -| `affineCloud.service.port` | The port of the Kubernetes service | `'http'` | -| `affineCloud.mail.account` | The email account used to send emails | `''` | -| `affineCloud.mail.password` | The password of the email account | `''` | - -## PostgreSQL parameters - -| Parameter | Description | Default | -| -------------------------------------------- | ------------------------------------------------------------------------------------- | ------------ | -| `postgresql.auth.username` | Username for the PostgreSQL database | `'affine'` | -| `postgresql.auth.password` | Password for the PostgreSQL database. Please change this for production environments. | `'password'` | -| `postgresql.auth.database` | The name of the default database that will be created on image startup | `'affine'` | -| `postgresql.primary.resources.limits.cpu` | The CPU resources allocated for the PostgreSQL primary node | `'500m'` | -| `postgresql.primary.resources.limits.memory` | The memory resources allocated for the PostgreSQL primary node | `'0.5Gi'` | - -For more postgres parameters, please refer to: https://artifacthub.io/packages/helm/bitnami/postgresql - -Please note that for the `postgresql.auth.password`, you should provide your own password for production environments. The default value is provided only for demonstration purposes. diff --git a/.github/helm/affine-cloud/templates/_helper.tpl b/.github/helm/affine-cloud/templates/_helper.tpl deleted file mode 100644 index 06a02feb84..0000000000 --- a/.github/helm/affine-cloud/templates/_helper.tpl +++ /dev/null @@ -1,51 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "affine-cloud.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "affine-cloud.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "affine-cloud.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "affine-cloud.labels" -}} -helm.sh/chart: {{ include "affine-cloud.chart" . }} -{{ include "affine-cloud.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "affine-cloud.selectorLabels" -}} -app.kubernetes.io/name: {{ include "affine-cloud.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} diff --git a/.github/helm/affine-cloud/templates/deployment.yaml b/.github/helm/affine-cloud/templates/deployment.yaml deleted file mode 100644 index c8d8d6fbe7..0000000000 --- a/.github/helm/affine-cloud/templates/deployment.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: "{{ include "affine-cloud.fullname" . }}" - labels: - {{- include "affine-cloud.labels" . | nindent 4 }} -spec: - replicas: 1 - selector: - matchLabels: - {{- include "affine-cloud.selectorLabels" . | nindent 6 }} - strategy: - type: RollingUpdate - rollingUpdate: - maxUnavailable: 2 - template: - metadata: - labels: - {{- include "affine-cloud.selectorLabels" . | nindent 8 }} - spec: - restartPolicy: Always - containers: - - name: affine-cloud - image: "ghcr.io/toeverything/cloud-self-hosted:{{ .Values.affineCloud.tag | default .Chart.AppVersion }}" - env: - - name: PG_USER - value: "{{ .Values.postgresql.auth.username }}" - - name: PG_PASS - value: "{{ .Values.postgresql.auth.password }}" - - name: PG_DATABASE - value: "{{ .Values.postgresql.auth.database }}" - - name: PG_HOST - value: "{{ .Values.postgresql.fullnameOverride | default (printf "%s-postgresql" .Release.Name) }}" - - name: DATABASE_URL - value: "{{ .Values.affineCloud.databaseUrl | default "postgresql://$(PG_USER):$(PG_PASS)@$(PG_HOST)/$(PG_DATABASE)" }}" - envFrom: - - secretRef: - name: affine-cloud-secret - ports: - - containerPort: 3000 - livenessProbe: - httpGet: - path: /api/healthz - port: 3000 - failureThreshold: 1 - initialDelaySeconds: 10 - periodSeconds: 10 - resources: - limits: - cpu: "{{ .Values.affineCloud.resources.cpu }}" - memory: "{{ .Values.affineCloud.resources.memory }}" diff --git a/.github/helm/affine-cloud/templates/secret.yaml b/.github/helm/affine-cloud/templates/secret.yaml deleted file mode 100644 index f6879af5e8..0000000000 --- a/.github/helm/affine-cloud/templates/secret.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: affine-cloud-secret -type: Opaque -data: - SIGN_KEY: "{{ .Values.affineCloud.signKey }}" - MAIL_ACCOUNT: "{{ .Values.affineCloud.mail.account }}" - MAIL_PASSWORD: "{{ .Values.affineCloud.mail.password }}" diff --git a/.github/helm/affine-cloud/templates/services.yaml b/.github/helm/affine-cloud/templates/services.yaml deleted file mode 100644 index 2529d4569d..0000000000 --- a/.github/helm/affine-cloud/templates/services.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: "{{ include "affine-cloud.fullname" . }}" - labels: - {{- include "affine-cloud.labels" . | nindent 4 }} -spec: - type: "{{ .Values.affineCloud.service.type }}" - ports: - - name: http - protocol: TCP - port: {{ .Values.affineCloud.service.port }} - targetPort: 3000 - selector: - {{- include "affine-cloud.selectorLabels" . | nindent 4 }} diff --git a/.github/helm/affine-cloud/values.yaml b/.github/helm/affine-cloud/values.yaml deleted file mode 100644 index 514f3513ab..0000000000 --- a/.github/helm/affine-cloud/values.yaml +++ /dev/null @@ -1,30 +0,0 @@ -affineCloud: - tag: 'canary-5e0d5e0cc65ea46f326fdde12658bfac59b38c9f-0949' - # databaseUrl: 'postgresql://affine:password@affine-cloud-postgresql:5432/affine' - signKey: TUFtdFdzQTJhdGJuem01TA== - mail: - account: '' - password: '' - service: - type: ClusterIP - port: 80 - resources: - cpu: '250m' - memory: 0.5Gi -postgresql: - fullnameOverride: tcp-postgresql - auth: - # only for demo, please modify it at prod env - username: affine - password: password - database: affine - primary: - initdb: - scripts: - 01-init.sql: | - CREATE DATABASE affine_binary; - GRANT ALL PRIVILEGES ON DATABASE affine_binary TO affine; - resources: - limits: - cpu: '500m' - memory: 0.5Gi diff --git a/.github/helm/affine/charts/graphql/templates/deployment.yaml b/.github/helm/affine/charts/graphql/templates/deployment.yaml index be14730988..38e53c3427 100644 --- a/.github/helm/affine/charts/graphql/templates/deployment.yaml +++ b/.github/helm/affine/charts/graphql/templates/deployment.yaml @@ -136,11 +136,6 @@ spec: secretKeyRef: name: "{{ .Values.app.objectStorage.r2.secretName }}" key: secretAccessKey - - name: R2_OBJECT_STORAGE_BUCKET - valueFrom: - secretKeyRef: - name: "{{ .Values.app.objectStorage.r2.secretName }}" - key: bucket {{ end }} {{ if .Values.app.captcha.enabled }} - name: CAPTCHA_TURNSTILE_SECRET diff --git a/.github/helm/affine/charts/graphql/templates/migration.yaml b/.github/helm/affine/charts/graphql/templates/migration.yaml index 658a7f3ad8..8d95457ff5 100644 --- a/.github/helm/affine/charts/graphql/templates/migration.yaml +++ b/.github/helm/affine/charts/graphql/templates/migration.yaml @@ -35,6 +35,23 @@ spec: - name: DATABASE_URL value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.gcloud.cloudSqlInternal }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }} {{ end }} + {{ if .Values.app.objectStorage.r2.enabled }} + - name: R2_OBJECT_STORAGE_ACCOUNT_ID + valueFrom: + secretKeyRef: + name: "{{ .Values.app.objectStorage.r2.secretName }}" + key: accountId + - name: R2_OBJECT_STORAGE_ACCESS_KEY_ID + valueFrom: + secretKeyRef: + name: "{{ .Values.app.objectStorage.r2.secretName }}" + key: accessKeyId + - name: R2_OBJECT_STORAGE_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: "{{ .Values.app.objectStorage.r2.secretName }}" + key: secretAccessKey + {{ end }} resources: requests: cpu: '100m' diff --git a/.github/helm/affine/charts/graphql/templates/r2-secret.yaml b/.github/helm/affine/charts/graphql/templates/r2-secret.yaml index d4a655b947..a521c27131 100644 --- a/.github/helm/affine/charts/graphql/templates/r2-secret.yaml +++ b/.github/helm/affine/charts/graphql/templates/r2-secret.yaml @@ -8,5 +8,4 @@ data: accountId: {{ .Values.app.objectStorage.r2.accountId | b64enc }} accessKeyId: {{ .Values.app.objectStorage.r2.accessKeyId | b64enc }} secretAccessKey: {{ .Values.app.objectStorage.r2.secretAccessKey | b64enc }} - bucket: {{ .Values.app.objectStorage.r2.bucket | b64enc }} {{- end }} diff --git a/.github/helm/affine/charts/graphql/values.yaml b/.github/helm/affine/charts/graphql/values.yaml index 2353c3939b..629997382d 100644 --- a/.github/helm/affine/charts/graphql/values.yaml +++ b/.github/helm/affine/charts/graphql/values.yaml @@ -34,7 +34,6 @@ app: accountId: '' accessKeyId: '' secretAccessKey: '' - bucket: '' oauth: email: secretName: 'oauth-email' diff --git a/.github/helm/deployment_guide.md b/.github/helm/deployment_guide.md deleted file mode 100644 index 67830faab5..0000000000 --- a/.github/helm/deployment_guide.md +++ /dev/null @@ -1,60 +0,0 @@ -# Cluster Deployment Guide - -This document provides a step-by-step guide for developers on how to deploy services in a Kubernetes cluster. The following content assumes that the reader already has a basic understanding of Kubernetes concepts and operations. - -### 1. Configure Service Mesh (Optional) - -In the Kubernetes cluster, we optionally use Service Mesh (like Istio and Anthos Service Mesh) to manage the network interactions of microservices. If Service Mesh is already deployed on your cluster or do not need to use the service network, you can skip this step. In this step, we assume that you are using Google Kubernetes Engine (GKE) and have already installed Anthos Service Mesh on your cluster, if you wish to use another Ingress Controller, please refer to the relevant documentation. - -To configure your kubectl context to interact with your Kubernetes cluster using the gcloud tool, you need to execute the following commands: - -```sh -export CLUSTER_NAME=your_cluster_name -export REGION=your_cluster_region -export PROJECT=your_project_id -gcloud container clusters get-credentials $CLUSTER_NAME --region $REGION --project $PROJECT -``` - -In this command, you should replace `CLUSTER_NAME`, `REGION` and `PROJECT` with the actual name, region and project id of your Kubernetes cluster. This command retrieves the access credentials for your Kubernetes cluster and automatically configures kubectl to use these credentials. - -Now, to inject Service Mesh for a specific Namespace, first, set the environment variable `NAMESPACE` that should correspond to your target Kubernetes Namespace. In this example, we use `prod` as the target Namespace: - -```sh -export NAMESPACE=prod -``` - -Then, we label the Namespace which will enable Istio to automatically inject the sidecar container for all new Pods under this Namespace: - -```sh -kubectl label namespace $NAMESPACE istio-injection- istio.io/rev=asm-managed --overwrite -``` - -Finally, we trigger the Kubernetes Deployment restart mechanism to allow existing Pods to also obtain sidecar container injection: - -```sh -kubectl rollout restart deployment -n $NAMESPACE -``` - -### 2. Deploying the Application - -Next, we will deploy our application in the Kubernetes cluster through Helm. First, set relevant environment variables: - -```sh -export NAMESPACE=prod -export RELEASE=affine-cloud-prod -export PATH=.github/helm/affine-cloud -``` - -- `NAMESPACE` should be consistent with the first step, indicating your target Kubernetes Namespace. -- `RELEASE` is the name of your Helm release. -- `PATH` is the location of your Helm chart in your file system. - -Finally, use the `helm upgrade --install` command to deploy or upgrade your application: - -```sh -helm upgrade --namespace $NAMESPACE --create-namespace --install $RELEASE $PATH -``` - -This command creates (if it doesn't already exist) and deploys your Helm chart in the specified Namespace. If the release already exists, it will be upgraded. - -The above are the complete steps for deploying an application in a Kubernetes cluster. Make sure all prerequisites are met before deploying, and also ensure that you have the correct permissions for operations in Kubernetes. diff --git a/.github/labeler.yml b/.github/labeler.yml index 41ed2112f8..79c9e0c192 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -19,26 +19,11 @@ mod:dev: - 'tools/cli/**/*' - 'packages/common/debug/**/*' -mod:plugin: - - changed-files: - - any-glob-to-any-file: - - 'packages/plugins/**/*' - -plugin:copilot: - - changed-files: - - any-glob-to-any-file: - - 'packages/plugins/copilot/**/*' - mod:infra: - changed-files: - any-glob-to-any-file: - 'packages/common/infra/**/*' -mod:sdk: - - changed-files: - - any-glob-to-any-file: - - 'packages/common/sdk/**/*' - mod:plugin-cli: - changed-files: - any-glob-to-any-file: @@ -47,7 +32,12 @@ mod:plugin-cli: mod:workspace: - changed-files: - any-glob-to-any-file: - - 'packages/frontend/workspace/**/*' + - 'packages/common/workspace/**/*' + +mod:workspace-impl: + - changed-files: + - any-glob-to-any-file: + - 'packages/frontend/workspace-impl/**/*' mod:i18n: - changed-files: @@ -59,11 +49,6 @@ mod:env: - any-glob-to-any-file: - 'packages/common/env/**/*' -mod:hooks: - - changed-files: - - any-glob-to-any-file: - - 'packages/frontend/hooks/**/*' - mod:component: - changed-files: - any-glob-to-any-file: diff --git a/.github/renovate.json b/.github/renovate.json index 74771bc859..c42ac66181 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -1,27 +1,35 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:base", - "group:allNonMajor", - ":preserveSemverRanges", - ":disablePeerDependencies" - ], + "extends": ["config:recommended", ":disablePeerDependencies"], "labels": ["dependencies"], + "ignorePaths": [ + "**/node_modules/**", + "**/bower_components/**", + "**/vendor/**", + "**/examples/**", + "**/__tests__/**", + "**/test/**", + "**/__fixtures__/**" + ], "packageRules": [ { "matchPackageNames": ["napi", "napi-build", "napi-derive"], + "rangeStrategy": "replace", "groupName": "napi-rs" }, { "matchPackagePatterns": ["^eslint", "^@typescript-eslint"], + "rangeStrategy": "replace", "groupName": "linter" }, { "matchPackagePatterns": ["^@nestjs"], + "rangeStrategy": "replace", "groupName": "nestjs" }, { "matchPackagePatterns": ["^@opentelemetry"], + "rangeStrategy": "replace", "groupName": "opentelemetry" }, { @@ -30,16 +38,32 @@ "@prisma/instrumentation", "prisma" ], + "rangeStrategy": "replace", "groupName": "prisma" }, { "matchPackagePatterns": ["^@electron-forge"], + "rangeStrategy": "replace", "groupName": "electron-forge" }, { + "groupName": "blocksuite-nightly", "matchPackagePatterns": ["^@blocksuite"], "excludePackageNames": ["@blocksuite/icons"], + "rangeStrategy": "replace", "followTag": "nightly" + }, + { + "groupName": "all non-major dependencies", + "groupSlug": "all-minor-patch", + "matchPackagePatterns": ["*"], + "excludePackagePatterns": ["^@blocksuite/"], + "matchUpdateTypes": ["minor", "patch"] + }, + { + "matchPackagePatterns": ["*"], + "rangeStrategy": "replace", + "excludePackagePatterns": ["^@blocksuite/"] } ], "commitMessagePrefix": "chore: ", diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index d95161ad8f..8c81d96fc3 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -108,44 +108,6 @@ jobs: 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 @@ -169,7 +131,7 @@ jobs: - name: Upload test results if: ${{ failure() }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-results-e2e-${{ matrix.shard }} path: ./test-results @@ -194,7 +156,7 @@ jobs: - name: Upload test results if: ${{ failure() }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-results-e2e-migration path: ./tests/affine-migration/test-results @@ -216,7 +178,7 @@ jobs: full-cache: true - name: Download affine.linux-x64-gnu.node - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: affine.linux-x64-gnu.node path: ./packages/frontend/native @@ -254,8 +216,6 @@ jobs: with: extra-flags: workspaces focus @affine/native electron-install: false - build-infra: false - build-plugins: false - name: Setup filename id: filename shell: bash @@ -269,7 +229,7 @@ jobs: package: '@affine/native' nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - name: Upload ${{ steps.filename.outputs.filename }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ steps.filename.outputs.filename }} path: ./packages/frontend/native/${{ steps.filename.outputs.filename }} @@ -287,8 +247,6 @@ jobs: 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: @@ -296,7 +254,7 @@ jobs: package: '@affine/storage' nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - name: Upload storage.node - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: storage.node path: ./packages/backend/storage/storage.node @@ -312,7 +270,6 @@ jobs: 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 @@ -320,7 +277,7 @@ jobs: - name: zip core run: tar -czf dist.tar.gz --directory=packages/frontend/core/dist . - name: Upload core artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: core path: dist.tar.gz @@ -358,6 +315,12 @@ jobs: electron-install: false full-cache: true + - name: Download storage.node + uses: actions/download-artifact@v4 + with: + name: storage.node + path: ./packages/backend/server + - name: Initialize database run: | psql -h localhost -U postgres -c "CREATE DATABASE affine;" @@ -380,12 +343,6 @@ jobs: 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: @@ -450,6 +407,18 @@ jobs: playwright-install: true hard-link-nm: false + - name: Download storage.node + uses: actions/download-artifact@v4 + with: + name: storage.node + path: ./packages/backend/server + + - name: Download affine.linux-x64-gnu.node + uses: actions/download-artifact@v4 + with: + name: affine.linux-x64-gnu.node + path: ./packages/frontend/native + - name: Initialize database run: | psql -h localhost -U postgres -c "CREATE DATABASE affine;" @@ -469,17 +438,6 @@ jobs: run: | yarn workspace @affine/server data-migration 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: | @@ -490,7 +448,7 @@ jobs: - name: Upload test results if: ${{ failure() }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-results-e2e-server path: ./tests/affine-cloud/test-results @@ -554,7 +512,7 @@ jobs: echo "filename=affine.$PLATFORM_ARCH_ABI.node" >> "$GITHUB_OUTPUT" - name: Download ${{ steps.filename.outputs.filename }} - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ steps.filename.outputs.filename }} path: ./packages/frontend/native @@ -562,8 +520,7 @@ jobs: - name: Run unit tests if: ${{ matrix.spec.test }} shell: bash - run: yarn vitest - working-directory: packages/frontend/electron + run: yarn workspace @affine/electron vitest - name: Download core artifact uses: ./.github/actions/download-core @@ -596,7 +553,7 @@ jobs: - name: Upload test results if: ${{ failure() }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-results-e2e-${{ matrix.spec.os }}-${{ matrix.spec.arch }} path: ./test-results diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 57827411bf..39185531b9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -32,7 +32,7 @@ jobs: - name: Build Server run: yarn workspace @affine/server build - name: Upload server dist - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: server-dist path: ./packages/backend/server/dist @@ -48,8 +48,6 @@ jobs: uses: ./.github/actions/setup-version - name: Setup Node.js uses: ./.github/actions/setup-node - - name: Build Plugins - run: yarn run build:plugins - name: Build Core run: yarn nx build @affine/core --skip-nx-cache env: @@ -65,7 +63,7 @@ jobs: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_DSN: ${{ secrets.SENTRY_DSN }} - name: Upload core artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: core path: ./packages/frontend/core/dist @@ -89,7 +87,7 @@ jobs: package: '@affine/storage' nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - name: Upload storage.node - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: storage.node path: ./packages/backend/storage/storage.node @@ -113,7 +111,7 @@ jobs: package: '@affine/storage' nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - name: Upload storage.node - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: storage.arm64.node path: ./packages/backend/storage/storage.node @@ -130,22 +128,22 @@ jobs: steps: - uses: actions/checkout@v4 - name: Download core artifact - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: core path: ./packages/frontend/core/dist - name: Download server dist - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: server-dist path: ./packages/backend/server/dist - name: Download storage.node - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: storage.node path: ./packages/backend/server - name: Download storage.node arm64 - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: storage.arm64.node path: ./packages/backend/storage @@ -242,7 +240,6 @@ jobs: R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }} R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} - R2_BUCKET: ${{ secrets.R2_BUCKET }} ENABLE_CAPTCHA: true CAPTCHA_TURNSTILE_SECRET: ${{ secrets.CAPTCHA_TURNSTILE_SECRET }} OAUTH_EMAIL_SENDER: ${{ secrets.OAUTH_EMAIL_SENDER }} diff --git a/.github/workflows/publish-storybook.yml b/.github/workflows/publish-storybook.yml index 166e795196..5ad11dc9c6 100644 --- a/.github/workflows/publish-storybook.yml +++ b/.github/workflows/publish-storybook.yml @@ -32,8 +32,6 @@ jobs: uses: ./.github/actions/setup-node with: electron-install: false - - name: Build Plugins - run: yarn run build:plugins - uses: chromaui/action-next@v1 with: workingDir: tests/storybook @@ -44,7 +42,7 @@ jobs: env: CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} NODE_OPTIONS: ${{ env.NODE_OPTIONS }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: always() with: name: chromatic-build-artifacts-${{ github.run_id }} diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 12beff675d..da59b42a4e 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -61,7 +61,7 @@ jobs: SKIP_NX_CACHE: 'true' - name: Upload core artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: core path: packages/frontend/electron/resources/web-static @@ -102,7 +102,6 @@ jobs: with: extra-flags: workspaces focus @affine/electron @affine/monorepo hard-link-nm: false - build-plugins: false nmHoistingLimits: workspaces enableScripts: false - name: Build AFFiNE native @@ -111,7 +110,7 @@ jobs: target: ${{ matrix.spec.target }} package: '@affine/native' nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: core path: packages/frontend/electron/resources/web-static @@ -147,7 +146,7 @@ jobs: mv packages/frontend/electron/out/*/make/AppImage/x64/*.AppImage ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.AppImage - name: Upload Artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: affine-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}-builds path: builds @@ -179,7 +178,6 @@ jobs: with: extra-flags: workspaces focus @affine/electron @affine/monorepo hard-link-nm: false - build-plugins: false nmHoistingLimits: workspaces - name: Build AFFiNE native uses: ./.github/actions/build-rust @@ -187,14 +185,11 @@ jobs: target: ${{ matrix.spec.target }} package: '@affine/native' nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: core path: packages/frontend/electron/resources/web-static - - name: Build Plugins - run: yarn run build:plugins - - name: Build Desktop Layers run: yarn workspace @affine/electron build @@ -216,7 +211,7 @@ jobs: run: Compress-Archive -CompressionLevel Fastest -Path packages/frontend/electron/out/* -DestinationPath archive.zip - name: Save packaged artifacts for signing - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: packaged-${{ matrix.spec.platform }}-${{ matrix.spec.arch }} path: | @@ -250,7 +245,7 @@ jobs: timeout-minutes: 10 uses: ./.github/actions/setup-node - name: Download and overwrite packaged artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: signed-packaged-${{ matrix.spec.platform }}-${{ matrix.spec.arch }} path: . @@ -271,7 +266,7 @@ jobs: echo $FILES_TO_BE_SIGNED - name: Save installer for signing - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: installer-${{ matrix.spec.platform }}-${{ matrix.spec.arch }} path: archive.zip @@ -297,7 +292,7 @@ jobs: runs-on: ${{ matrix.spec.runner }} steps: - name: Download and overwrite installer artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: signed-installer-${{ matrix.spec.platform }}-${{ matrix.spec.arch }} path: . @@ -312,7 +307,7 @@ jobs: mv packages/frontend/electron/out/*/make/squirrel.windows/x64/*.msi ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.msi - name: Upload Artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: affine-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}-builds path: builds @@ -323,29 +318,29 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: core path: web-static - name: Zip web-static run: zip -r web-static.zip web-static - name: Download Artifacts (macos-x64) - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: affine-darwin-x64-builds path: ./ - name: Download Artifacts (macos-arm64) - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: affine-darwin-arm64-builds path: ./ - name: Download Artifacts (windows-x64) - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: affine-win32-x64-builds path: ./ - name: Download Artifacts (linux-x64) - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: affine-linux-x64-builds path: ./ @@ -358,6 +353,7 @@ jobs: env: RELEASE_VERSION: ${{ needs.before-make.outputs.RELEASE_VERSION }} - name: Create Release Draft + if: ${{ github.ref_type == 'tag' }} uses: softprops/action-gh-release@v1 with: name: ${{ needs.before-make.outputs.RELEASE_VERSION }} @@ -372,3 +368,25 @@ jobs: ./*.AppImage ./*.apk ./*.yml + - name: Create Nightly Release Draft + if: ${{ github.ref_type == 'branch' }} + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + with: + # Temporarily, treat release from branch as nightly release, artifact saved to AFFiNE-Releases. + # Need to improve internal build and nightly release logic. + repository: 'toeverything/AFFiNE-Releases' + name: ${{ needs.before-make.outputs.RELEASE_VERSION }} + tag_name: ${{ needs.before-make.outputs.RELEASE_VERSION }} + body: '' + draft: false + prerelease: true + files: | + ./VERSION + ./*.zip + ./*.dmg + ./*.exe + ./*.AppImage + ./*.apk + ./*.yml diff --git a/.github/workflows/windows-signer.yml b/.github/workflows/windows-signer.yml index 36229ff390..b5c8c36fde 100644 --- a/.github/workflows/windows-signer.yml +++ b/.github/workflows/windows-signer.yml @@ -14,7 +14,7 @@ jobs: env: ARCHIVE_DIR: ${{ github.run_id }}-${{ github.run_attempt }}-${{ inputs.artifact-name }} steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: ${{ inputs.artifact-name }} path: ${{ env.ARCHIVE_DIR }} @@ -36,7 +36,7 @@ jobs: cd ${{ env.ARCHIVE_DIR }} 7za a signed.zip .\out\* - name: upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: signed-${{ inputs.artifact-name }} path: ${{ env.ARCHIVE_DIR }}/signed.zip diff --git a/.github/workflows/workers.yml b/.github/workflows/workers.yml index f079df253a..a00ca341d4 100644 --- a/.github/workflows/workers.yml +++ b/.github/workflows/workers.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Publish - uses: cloudflare/wrangler-action@v3.3.2 + uses: cloudflare/wrangler-action@v3.4.0 with: apiToken: ${{ secrets.CF_API_TOKEN }} accountId: ${{ secrets.CF_ACCOUNT_ID }} diff --git a/.prettierignore b/.prettierignore index 89117d2064..c516bb9871 100644 --- a/.prettierignore +++ b/.prettierignore @@ -16,6 +16,8 @@ packages/frontend/i18n/src/i18n-generated.ts packages/frontend/graphql/src/graphql/index.ts tests/affine-legacy/**/static .yarnrc.yml +packages/frontend/templates/templates.gen.ts +packages/frontend/templates/onboarding # auto-generated by NAPI-RS # fixme(@joooye34): need script to check and generate ignore list here diff --git a/Cargo.lock b/Cargo.lock index 2313e7e1c2..ff081424ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2024,16 +2024,14 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.2" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ab43bb47d23c1a631b4b680199a45255dce26fa9ab2fa902581f624ff13e6a8" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" dependencies = [ - "byteorder", "const-oid", "digest", "num-bigint-dig", "num-integer", - "num-iter", "num-traits", "pkcs1", "pkcs8", @@ -2462,9 +2460,9 @@ dependencies = [ [[package]] name = "spki" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", diff --git a/README.md b/README.md index c73b6c911a..60ce267a89 100644 --- a/README.md +++ b/README.md @@ -113,21 +113,6 @@ If you have questions, you are welcome to contact us. One of the best places to | [@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 - -> Plugins are a way to extend the functionality of AFFiNE. You can use plugins to add new blocks, new features, and even new ways to edit content. -> -> (Currently, the plugin system is under heavy development. You will see the plugin system in the canary release.) - -- [@affine/sdk](./packages/common/sdk) - SDK for developing plugins -- [@affine/plugin-cli](./tools/plugin-cli) - CLI for developing plugins - -| Official Plugin | Description | Status | -| ---------------------------------------------------------------- | ----------------------------------------- | ------ | -| [@affine/copilot-plugin](./packages/plugins/copilot) | AI Copilot that help you document writing | 🚧 | -| [@affine/image-preview-plugin](./packages/plugins/image-preview) | Component for previewing an image | ✅ | -| [@affine/outline](./packages/plugins/outline) | Outline for your document | ✅ | - ## Upstreams We would also like to give thanks to open-source projects that make AFFiNE possible: @@ -227,7 +212,7 @@ See [LICENSE] for details. [jobs available]: ./docs/jobs.md [latest packages]: https://github.com/toeverything/AFFiNE/pkgs/container/affine-self-hosted [contributor license agreement]: https://github.com/toeverything/affine/edit/canary/.github/CLA.md -[rust-version-icon]: https://img.shields.io/badge/Rust-1.74.1-dea584 +[rust-version-icon]: https://img.shields.io/badge/Rust-1.75.0-dea584 [stars-icon]: https://img.shields.io/github/stars/toeverything/AFFiNE.svg?style=flat&logo=github&colorB=red&label=stars [codecov]: https://codecov.io/gh/toeverything/affine/branch/canary/graphs/badge.svg?branch=canary [node-version-icon]: https://img.shields.io/badge/node-%3E=18.16.1-success diff --git a/docs/BUILDING.md b/docs/BUILDING.md index d90d609654..b28b8f3954 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -57,6 +57,29 @@ corepack prepare yarn@stable --activate yarn install ``` +### Clone repository + +#### Linux & MacOS + +```sh +git clone https://github.com/toeverything/AFFiNE +``` + +#### Windows + +In our codebase, we use symbolic links. Due to the security design of Windows, the creation of symbolic links requires administrator privileges. This is part of the security policy settings of Windows, and more information can be found at [Security Policy Settings for Creating Symbolic Links](https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links). + +For detailed guidance on enabling this feature, please refer to the official documentation: [Enable Developer Mode on Windows](https://learn.microsoft.com/en-us/windows/apps/get-started/enable-your-device-for-development). + +Once Developer Mode is enabled, execute the following command with administrator privileges: + +```sh +# Enable symbolic links +git config --global core.symlinks true +# Clone the repository, also need to be run with administrator privileges +git clone https://github.com/toeverything/AFFiNE +``` + ### Build Native Dependencies Run the following script. It will build the native module at [`/packages/frontend/native`](/packages/frontend/native) and build Node.js binding using [NAPI.rs](https://napi.rs/). @@ -67,18 +90,6 @@ Note: use `strip` from system instead of `binutils` if you are running MacOS. [s yarn workspace @affine/native build ``` -### Build Infra - -```sh -yarn run build:infra -``` - -### Build Plugins - -```sh -yarn run build:plugins -``` - ### Build Server Dependencies ```sh @@ -102,7 +113,7 @@ yarn test ### E2E Test ```shell -# there are `affine-local`, `affine-migration`, `affine-local`, `affine-plugin`, `affine-prototype` e2e tests, +# there are `affine-local`, `affine-migration`, `affine-local`, `affine-prototype` e2e tests, # which are run under different situations. cd tests/affine-local yarn e2e diff --git a/docs/contributing/tutorial.md b/docs/contributing/tutorial.md index 07bd19ad1d..39be0c964d 100644 --- a/docs/contributing/tutorial.md +++ b/docs/contributing/tutorial.md @@ -17,7 +17,6 @@ The codebase is organized as follows: - `packages/` contains all code running in production. - `backend/` contains backend code, more information from . - `frontend/` contains frontend code, including the web app, the electron app and business libraries. - - `plugins/` contains all build-in plugins. - `common` contains the isomorphic code or basic libraries without business. - `tools/` contains tools to help developing or CI, not used in production. - `tests/` contains testings across different libraries, including e2e testings and integration testings. diff --git a/docs/reference/package.json b/docs/reference/package.json new file mode 100644 index 0000000000..0b83a40942 --- /dev/null +++ b/docs/reference/package.json @@ -0,0 +1,23 @@ +{ + "name": "@affine/docs", + "type": "module", + "private": true, + "scripts": { + "build": "typedoc --options ../../typedoc.json", + "dev": "nodemon --exec 'typedoc --options ../../typedoc.json' & serve dist/" + }, + "devDependencies": { + "nodemon": "^3.0.1", + "serve": "^14.2.1", + "typedoc": "^0.25.4" + }, + "nodemonConfig": { + "watch": [ + "./readme.md", + "../../packages/*/*/src/*.ts", + "../../**/typedoc{.base,}.json" + ], + "ext": "ts,md,json" + }, + "version": "0.10.3-canary.2" +} diff --git a/docs/reference/readme.md b/docs/reference/readme.md new file mode 100644 index 0000000000..e0333b9cbd --- /dev/null +++ b/docs/reference/readme.md @@ -0,0 +1,7 @@ +Welcome to AFFiNE development reference. + +This document is intended for developers who want to contribute to AFFiNE. It contains information about the architecture of AFFiNE, how to build it, and how to contribute to it. + +### The Infrastructure of AFFiNE + +see {@link @toeverything/infra!} diff --git a/package.json b/package.json index 171dfa6190..ec60f92b62 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,9 @@ ".", "packages/*/*", "tools/*", - "!tools/@types", + "docs/reference", "tools/@types/*", "tests/*", - "!tests/affine-legacy", "tests/affine-legacy/*" ], "engines": { @@ -23,8 +22,6 @@ "build": "yarn nx build @affine/core", "build:electron": "yarn nx build @affine/electron", "build:storage": "yarn nx run-many -t build -p @affine/storage", - "build:infra": "yarn nx run-many -t build --projects=tag:infra", - "build:plugins": "yarn nx run-many -t build --projects=tag:plugin", "build:storybook": "yarn nx build @affine/storybook", "start:web-static": "yarn workspace @affine/core static-server", "start:storybook": "yarn exec serve tests/storybook/storybook-static -l 6006", @@ -33,7 +30,7 @@ "lint:eslint:fix": "yarn lint:eslint --fix", "lint:prettier": "prettier --ignore-unknown --cache --check .", "lint:prettier:fix": "prettier --ignore-unknown --cache --write .", - "lint:ox": "oxlint --deny-warnings -D correctness -D nursery -D prefer-array-some -D no-useless-promise-resolve-reject -A no-undef -A consistent-type-exports -A default -A named -A ban-ts-comment", + "lint:ox": "oxlint --import-plugin --deny-warnings -D correctness -D nursery -D prefer-array-some -D no-useless-promise-resolve-reject -D perf -A no-undef -A consistent-type-exports -A default -A named -A ban-ts-comment -A export", "lint": "yarn lint:eslint && yarn lint:prettier", "lint:fix": "yarn lint:eslint:fix && yarn lint:prettier:fix", "test": "vitest --run", @@ -58,13 +55,12 @@ "devDependencies": { "@affine-test/kit": "workspace:*", "@affine/cli": "workspace:*", - "@affine/plugin-cli": "workspace:*", "@commitlint/cli": "^18.4.3", "@commitlint/config-conventional": "^18.4.3", "@faker-js/faker": "^8.3.1", "@istanbuljs/schema": "^0.1.3", "@magic-works/i18n-codegen": "^0.5.0", - "@nx/vite": "17.1.3", + "@nx/vite": "17.2.8", "@perfsee/sdk": "^1.9.0", "@playwright/test": "^1.40.0", "@taplo/cli": "^0.5.2", @@ -78,8 +74,8 @@ "@vanilla-extract/vite-plugin": "^3.9.2", "@vanilla-extract/webpack-plugin": "^2.3.1", "@vitejs/plugin-react-swc": "^3.5.0", - "@vitest/coverage-istanbul": "1.0.4", - "@vitest/ui": "1.0.4", + "@vitest/coverage-istanbul": "1.1.3", + "@vitest/ui": "1.1.3", "electron": "^27.1.0", "eslint": "^8.54.0", "eslint-config-prettier": "^9.0.0", @@ -91,7 +87,7 @@ "eslint-plugin-unicorn": "^50.0.0", "eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-vue": "^9.18.1", - "fake-indexeddb": "5.0.1", + "fake-indexeddb": "5.0.2", "happy-dom": "^12.10.3", "husky": "^8.0.3", "lint-staged": "^15.1.0", @@ -100,7 +96,7 @@ "nx": "^17.1.3", "nx-cloud": "^16.5.2", "nyc": "^15.1.0", - "oxlint": "0.0.21", + "oxlint": "0.0.22", "prettier": "^3.1.0", "semver": "^7.5.4", "serve": "^14.2.1", @@ -111,7 +107,7 @@ "vite-plugin-istanbul": "^5.0.0", "vite-plugin-static-copy": "^1.0.0", "vite-tsconfig-paths": "^4.2.1", - "vitest": "1.0.4", + "vitest": "1.1.3", "vitest-fetch-mock": "^0.2.2", "vitest-mock-extended": "^1.3.1" }, diff --git a/packages/backend/server/migrations/20240103092238_add_workspace_features/migration.sql b/packages/backend/server/migrations/20240103092238_add_workspace_features/migration.sql new file mode 100644 index 0000000000..212f0c9d67 --- /dev/null +++ b/packages/backend/server/migrations/20240103092238_add_workspace_features/migration.sql @@ -0,0 +1,18 @@ +-- CreateTable +CREATE TABLE "workspace_features" ( + "id" SERIAL NOT NULL, + "workspace_id" VARCHAR(36) NOT NULL, + "feature_id" INTEGER NOT NULL, + "reason" VARCHAR NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "expired_at" TIMESTAMPTZ(6), + "activated" BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT "workspace_features_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "workspace_features" ADD CONSTRAINT "workspace_features_feature_id_fkey" FOREIGN KEY ("feature_id") REFERENCES "features"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "workspace_features" ADD CONSTRAINT "workspace_features_workspace_id_fkey" FOREIGN KEY ("workspace_id") REFERENCES "workspaces"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/backend/server/package.json b/packages/backend/server/package.json index ea2ddcf98f..7856cd5a7c 100644 --- a/packages/backend/server/package.json +++ b/packages/backend/server/package.json @@ -9,12 +9,12 @@ }, "scripts": { "build": "tsc", - "start": "node --loader ts-node/esm.mjs --es-module-specifier-resolution node ./src/index.ts", + "start": "node --loader ts-node/esm/transpile-only.mjs --es-module-specifier-resolution node ./src/index.ts", "dev": "nodemon ./src/index.ts", "test": "ava --concurrency 1 --serial", "test:coverage": "c8 ava --concurrency 1 --serial", "postinstall": "prisma generate", - "data-migration": "node --loader ts-node/esm.mjs --es-module-specifier-resolution node ./src/data/app.ts", + "data-migration": "node --loader ts-node/esm/transpile-only.mjs --es-module-specifier-resolution node ./src/data/app.ts", "predeploy": "yarn prisma migrate deploy && node --es-module-specifier-resolution node ./dist/data/app.js run" }, "dependencies": { @@ -36,29 +36,29 @@ "@nestjs/websockets": "^10.2.10", "@node-rs/argon2": "^1.5.2", "@node-rs/crc32": "^1.7.2", - "@node-rs/jsonwebtoken": "^0.2.3", + "@node-rs/jsonwebtoken": "^0.3.0", "@opentelemetry/api": "^1.7.0", - "@opentelemetry/core": "^1.18.1", - "@opentelemetry/exporter-prometheus": "^0.45.1", - "@opentelemetry/exporter-zipkin": "^1.18.1", + "@opentelemetry/core": "^1.19.0", + "@opentelemetry/exporter-prometheus": "^0.46.0", + "@opentelemetry/exporter-zipkin": "^1.19.0", "@opentelemetry/host-metrics": "^0.34.0", - "@opentelemetry/instrumentation": "^0.45.1", + "@opentelemetry/instrumentation": "^0.46.0", "@opentelemetry/instrumentation-graphql": "^0.36.0", - "@opentelemetry/instrumentation-http": "^0.45.1", + "@opentelemetry/instrumentation-http": "^0.46.0", "@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", - "@opentelemetry/sdk-metrics": "^1.18.1", - "@opentelemetry/sdk-node": "^0.45.1", - "@opentelemetry/sdk-trace-node": "^1.18.1", - "@prisma/client": "^5.6.0", - "@prisma/instrumentation": "^5.6.0", + "@opentelemetry/instrumentation-socket.io": "^0.35.0", + "@opentelemetry/resources": "^1.19.0", + "@opentelemetry/sdk-metrics": "^1.19.0", + "@opentelemetry/sdk-node": "^0.46.0", + "@opentelemetry/sdk-trace-node": "^1.19.0", + "@prisma/client": "^5.7.1", + "@prisma/instrumentation": "^5.7.1", "@socket.io/redis-adapter": "^8.2.1", "cookie-parser": "^1.4.6", "dotenv": "^16.3.1", "express": "^4.18.2", - "file-type": "^18.7.0", + "file-type": "^19.0.0", "get-stream": "^8.0.1", "graphql": "^16.8.1", "graphql-type-json": "^0.3.2", @@ -74,9 +74,9 @@ "on-headers": "^1.0.2", "parse-duration": "^1.1.0", "pretty-time": "^1.1.0", - "prisma": "^5.6.0", + "prisma": "^5.7.1", "prom-client": "^15.0.0", - "reflect-metadata": "^0.1.13", + "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", "semver": "^7.5.4", "socket.io": "^4.7.2", @@ -101,10 +101,10 @@ "@types/on-headers": "^1.0.3", "@types/pretty-time": "^1.1.5", "@types/sinon": "^17.0.2", - "@types/supertest": "^2.0.16", + "@types/supertest": "^6.0.0", "@types/ws": "^8.5.10", "ava": "^6.0.0", - "c8": "^8.0.1", + "c8": "^9.0.0", "nodemon": "^3.0.1", "sinon": "^17.0.1", "supertest": "^6.3.3", diff --git a/packages/backend/server/schema.prisma b/packages/backend/server/schema.prisma index fae3ff0b10..f0b17cd978 100644 --- a/packages/backend/server/schema.prisma +++ b/packages/backend/server/schema.prisma @@ -1,7 +1,7 @@ generator client { provider = "prisma-client-js" binaryTargets = ["native", "debian-openssl-3.0.x", "linux-arm64-openssl-3.0.x"] - previewFeatures = ["metrics", "tracing"] + previewFeatures = ["metrics", "tracing", "relationJoins", "nativeDistinct"] } datasource db { @@ -40,6 +40,7 @@ model Workspace { pages WorkspacePage[] permissions WorkspaceUserPermission[] pagePermissions WorkspacePageUserPermission[] + features WorkspaceFeatures[] @@map("workspaces") } @@ -135,12 +136,39 @@ model UserFeatures { // - if we switch the user to another plan, we will set the old plan to deactivated, but dont delete it activated Boolean @default(false) - user User @relation(fields: [userId], references: [id], onDelete: Cascade) feature Features @relation(fields: [featureId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("user_features") } +// feature gates is a way to enable/disable features for a workspace +// for example: +// - copilet is a feature that allow some users in a workspace to access the copilet feature +model WorkspaceFeatures { + id Int @id @default(autoincrement()) + workspaceId String @map("workspace_id") @db.VarChar(36) + featureId Int @map("feature_id") @db.Integer + + // we will record the reason why the feature is enabled/disabled + // for example: + // - copilet_v1: "owner buy the copilet feature package" + reason String @db.VarChar + // record the feature enabled time + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + // record the quota expired time, pay plan is a subscription, so it will expired + expiredAt DateTime? @map("expired_at") @db.Timestamptz(6) + // whether the feature is activated + // for example: + // - if owner unsubscribe a feature package, we will set the feature to deactivated, but dont delete it + activated Boolean @default(false) + + feature Features @relation(fields: [featureId], references: [id], onDelete: Cascade) + workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) + + @@map("workspace_features") +} + model Features { id Int @id @default(autoincrement()) feature String @db.VarChar @@ -151,7 +179,8 @@ model Features { configs Json @db.Json createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) - UserFeatureGates UserFeatures[] + UserFeatureGates UserFeatures[] + WorkspaceFeatures WorkspaceFeatures[] @@unique([feature, version]) @@map("features") @@ -196,6 +225,7 @@ model VerificationToken { @@map("verificationtokens") } +// deprecated, use [ObjectStorage] model Blob { id Int @id @default(autoincrement()) @db.Integer hash String @db.VarChar @@ -210,6 +240,7 @@ model Blob { @@map("blobs") } +// deprecated, use [ObjectStorage] model OptimizedBlob { id Int @id @default(autoincrement()) @db.Integer hash String @db.VarChar diff --git a/packages/backend/server/src/affine.config.ts b/packages/backend/server/src/affine.config.ts new file mode 100644 index 0000000000..43e9a83fac --- /dev/null +++ b/packages/backend/server/src/affine.config.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +// Custom configurations + +const env = process.env; +const node = AFFiNE.node; + +// TODO: may be separate config overring in `affine.[env].config`? +if (node.prod && env.R2_OBJECT_STORAGE_ACCOUNT_ID) { + AFFiNE.storage.providers.r2 = { + accountId: env.R2_OBJECT_STORAGE_ACCOUNT_ID, + credentials: { + accessKeyId: env.R2_OBJECT_STORAGE_ACCESS_KEY_ID!, + secretAccessKey: env.R2_OBJECT_STORAGE_SECRET_ACCESS_KEY!, + }, + }; + AFFiNE.storage.storages.avatar.provider = 'r2'; + AFFiNE.storage.storages.avatar.bucket = 'account-avatar'; + AFFiNE.storage.storages.avatar.publicLinkFactory = key => + `https://avatar.affineassets.com/${key}`; + + AFFiNE.storage.storages.blob.provider = 'r2'; + AFFiNE.storage.storages.blob.bucket = `workspace-blobs-${ + AFFiNE.affine.canary ? 'canary' : 'prod' + }`; +} diff --git a/packages/backend/server/src/affine.ts b/packages/backend/server/src/affine.ts new file mode 100644 index 0000000000..ceb37536da --- /dev/null +++ b/packages/backend/server/src/affine.ts @@ -0,0 +1,3 @@ +import { getDefaultAFFiNEConfig } from './config/default'; + +globalThis.AFFiNE = getDefaultAFFiNEConfig(); diff --git a/packages/backend/server/src/app.ts b/packages/backend/server/src/app.ts index a1f068f026..89a4019800 100644 --- a/packages/backend/server/src/app.ts +++ b/packages/backend/server/src/app.ts @@ -9,7 +9,6 @@ import { BusinessModules } from './modules'; import { AuthModule } from './modules/auth'; import { PrismaModule } from './prisma'; import { SessionModule } from './session'; -import { StorageModule } from './storage'; import { RateLimiterModule } from './throttler'; const BasicModules = [ @@ -17,7 +16,6 @@ const BasicModules = [ ConfigModule.forRoot(), CacheModule, EventModule, - StorageModule.forRoot(), SessionModule, RateLimiterModule, AuthModule, diff --git a/packages/backend/server/src/config/def.ts b/packages/backend/server/src/config/def.ts index 145e1b3b5d..a3f9c2288f 100644 --- a/packages/backend/server/src/config/def.ts +++ b/packages/backend/server/src/config/def.ts @@ -1,6 +1,7 @@ import type { ApolloDriverConfig } from '@nestjs/apollo'; import type { LeafPaths } from '../utils/types'; +import type { AFFiNEStorageConfig } from './storage'; declare global { // eslint-disable-next-line @typescript-eslint/no-namespace @@ -165,30 +166,11 @@ export interface AFFiNEConfig { featureFlags: { earlyAccessPreview: boolean; }; + /** - * object storage Config - * - * all artifacts and logs will be stored on instance disk, - * and can not shared between instances if not configured + * Configuration for Object Storage, which defines how blobs and avatar assets are stored. */ - objectStorage: { - /** - * whether use remote object storage - */ - r2: { - enabled: boolean; - accountId: string; - bucket: string; - accessKeyId: string; - secretAccessKey: string; - }; - /** - * Only used when `enable` is `false` - */ - fs: { - path: string; - }; - }; + storage: AFFiNEStorageConfig; /** * Rate limiter config diff --git a/packages/backend/server/src/config/default.ts b/packages/backend/server/src/config/default.ts index aa2f0ba76f..ec3233cfbf 100644 --- a/packages/backend/server/src/config/default.ts +++ b/packages/backend/server/src/config/default.ts @@ -1,14 +1,13 @@ /// import { createPrivateKey, createPublicKey } from 'node:crypto'; -import { homedir } from 'node:os'; -import { join } from 'node:path'; import parse from 'parse-duration'; import pkg from '../../package.json' assert { type: 'json' }; import type { AFFiNEConfig, ServerFlavor } from './def'; import { applyEnvToConfig } from './env'; +import { getDefaultAFFiNEStorageConfig } from './storage'; export const SERVER_FLAVOR = (process.env.SERVER_FLAVOR ?? 'allinone') as ServerFlavor; @@ -59,11 +58,6 @@ export const getDefaultAFFiNEConfig: () => AFFiNEConfig = () => { AFFINE_SERVER_SUB_PATH: 'path', AFFINE_ENV: 'affineEnv', DATABASE_URL: 'db.url', - ENABLE_R2_OBJECT_STORAGE: ['objectStorage.r2.enabled', 'boolean'], - R2_OBJECT_STORAGE_ACCOUNT_ID: 'objectStorage.r2.accountId', - R2_OBJECT_STORAGE_ACCESS_KEY_ID: 'objectStorage.r2.accessKeyId', - R2_OBJECT_STORAGE_SECRET_ACCESS_KEY: 'objectStorage.r2.secretAccessKey', - R2_OBJECT_STORAGE_BUCKET: 'objectStorage.r2.bucket', ENABLE_CAPTCHA: ['auth.captcha.enable', 'boolean'], CAPTCHA_TURNSTILE_SECRET: ['auth.captcha.turnstile.secret', 'string'], OAUTH_GOOGLE_ENABLED: ['auth.oauthProviders.google.enabled', 'boolean'], @@ -180,18 +174,7 @@ export const getDefaultAFFiNEConfig: () => AFFiNEConfig = () => { password: '', }, }, - objectStorage: { - r2: { - enabled: false, - bucket: '', - accountId: '', - accessKeyId: '', - secretAccessKey: '', - }, - fs: { - path: join(homedir(), '.affine-storage'), - }, - }, + storage: getDefaultAFFiNEStorageConfig(), rateLimiter: { ttl: 60, limit: 60, diff --git a/packages/backend/server/src/config/index.ts b/packages/backend/server/src/config/index.ts index cdd76a91bb..722dee3e93 100644 --- a/packages/backend/server/src/config/index.ts +++ b/packages/backend/server/src/config/index.ts @@ -74,3 +74,4 @@ export class ConfigModule { export type { AFFiNEConfig } from './def'; export { SERVER_FLAVOR } from './default'; +export * from './storage'; diff --git a/packages/backend/server/src/config/storage/index.ts b/packages/backend/server/src/config/storage/index.ts new file mode 100644 index 0000000000..aa90ab76fb --- /dev/null +++ b/packages/backend/server/src/config/storage/index.ts @@ -0,0 +1,59 @@ +import { homedir } from 'node:os'; +import { join } from 'node:path'; + +import { S3ClientConfigType } from '@aws-sdk/client-s3'; + +export type StorageProviderType = 'fs' | 'r2' | 's3'; +export interface FsStorageConfig { + path: string; +} +export type R2StorageConfig = S3ClientConfigType & { + accountId: string; +}; +export type S3StorageConfig = S3ClientConfigType; + +export type StorageTargetConfig = { + provider: StorageProviderType; + bucket: string; +} & Ext; + +export interface AFFiNEStorageConfig { + /** + * All providers for object storage + * + * Support different providers for different usage at the same time. + */ + providers: { + fs?: FsStorageConfig; + s3?: S3StorageConfig; + r2?: R2StorageConfig; + }; + storages: { + avatar: StorageTargetConfig<{ publicLinkFactory: (key: string) => string }>; + blob: StorageTargetConfig; + }; +} + +export type StorageProviders = AFFiNEStorageConfig['providers']; +export type Storages = keyof AFFiNEStorageConfig['storages']; + +export function getDefaultAFFiNEStorageConfig(): AFFiNEStorageConfig { + return { + providers: { + fs: { + path: join(homedir(), '.affine/storage'), + }, + }, + storages: { + avatar: { + provider: 'fs', + bucket: 'avatars', + publicLinkFactory: key => `/api/avatars/${key}`, + }, + blob: { + provider: 'fs', + bucket: 'blobs', + }, + }, + }; +} diff --git a/packages/backend/server/src/data/app.ts b/packages/backend/server/src/data/app.ts index 1369bad627..5287e74fd9 100644 --- a/packages/backend/server/src/data/app.ts +++ b/packages/backend/server/src/data/app.ts @@ -1,18 +1,32 @@ import { Logger, Module } from '@nestjs/common'; import { CommandFactory } from 'nest-commander'; -import { PrismaModule } from '../prisma'; +import { AppModule as BusinessAppModule } from '../app'; +import { ConfigModule } from '../config'; import { CreateCommand, NameQuestion } from './commands/create'; import { RevertCommand, RunCommand } from './commands/run'; @Module({ - imports: [PrismaModule], + imports: [ + ConfigModule.forRoot({ + doc: { + manager: { + enableUpdateAutoMerging: false, + }, + }, + }), + BusinessAppModule, + ], providers: [NameQuestion, CreateCommand, RunCommand, RevertCommand], }) class AppModule {} async function bootstrap() { - await CommandFactory.run(AppModule, new Logger()); + await CommandFactory.run(AppModule, new Logger()).catch(e => { + console.error(e); + process.exit(1); + }); + process.exit(0); } await bootstrap(); diff --git a/packages/backend/server/src/data/commands/run.ts b/packages/backend/server/src/data/commands/run.ts index 97b3c3098d..b8123cd8d9 100644 --- a/packages/backend/server/src/data/commands/run.ts +++ b/packages/backend/server/src/data/commands/run.ts @@ -3,6 +3,7 @@ import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { Logger } from '@nestjs/common'; +import { ModuleRef } from '@nestjs/core'; import { Command, CommandRunner } from 'nest-commander'; import { PrismaService } from '../../prisma'; @@ -10,8 +11,8 @@ import { PrismaService } from '../../prisma'; interface Migration { file: string; name: string; - up: (db: PrismaService) => Promise; - down: (db: PrismaService) => Promise; + up: (db: PrismaService, injector: ModuleRef) => Promise; + down: (db: PrismaService, injector: ModuleRef) => Promise; } export async function collectMigrations(): Promise { @@ -46,7 +47,10 @@ export async function collectMigrations(): Promise { }) export class RunCommand extends CommandRunner { logger = new Logger(RunCommand.name); - constructor(private readonly db: PrismaService) { + constructor( + private readonly db: PrismaService, + private readonly injector: ModuleRef + ) { super(); } @@ -103,14 +107,14 @@ export class RunCommand extends CommandRunner { }); try { - await migration.up(this.db); + await migration.up(this.db, this.injector); } catch (e) { await this.db.dataMigration.delete({ where: { id: record.id, }, }); - await migration.down(this.db); + await migration.down(this.db, this.injector); this.logger.error('Failed to run data migration', e); process.exit(1); } @@ -134,7 +138,10 @@ export class RunCommand extends CommandRunner { export class RevertCommand extends CommandRunner { logger = new Logger(RevertCommand.name); - constructor(private readonly db: PrismaService) { + constructor( + private readonly db: PrismaService, + private readonly injector: ModuleRef + ) { super(); } @@ -168,7 +175,7 @@ export class RevertCommand extends CommandRunner { try { this.logger.log(`Reverting ${name}...`); - await migration.down(this.db); + await migration.down(this.db, this.injector); this.logger.log('Done reverting'); } catch (e) { this.logger.error(`Failed to revert data migration ${name}`, e); diff --git a/packages/backend/server/src/data/migrations/1698652531198-user-features-init.ts b/packages/backend/server/src/data/migrations/1698652531198-user-features-init.ts index b877eb18b3..786d467718 100644 --- a/packages/backend/server/src/data/migrations/1698652531198-user-features-init.ts +++ b/packages/backend/server/src/data/migrations/1698652531198-user-features-init.ts @@ -1,13 +1,7 @@ -import { Prisma } from '@prisma/client'; - -import { - CommonFeature, - FeatureKind, - Features, - FeatureType, -} from '../../modules/features'; +import { Features } from '../../modules/features'; import { Quotas } from '../../modules/quota/schema'; import { PrismaService } from '../../prisma'; +import { migrateNewFeatureTable, upsertFeature } from './utils/user-features'; export class UserFeaturesInit1698652531198 { // do the migration @@ -28,95 +22,3 @@ export class UserFeaturesInit1698652531198 { // TODO: revert the migration } } - -// upgrade features from lower version to higher version -async function upsertFeature( - db: PrismaService, - feature: CommonFeature -): Promise { - const hasEqualOrGreaterVersion = - (await db.features.count({ - where: { - feature: feature.feature, - version: { - gte: feature.version, - }, - }, - })) > 0; - // will not update exists version - if (!hasEqualOrGreaterVersion) { - await db.features.create({ - data: { - feature: feature.feature, - type: feature.type, - version: feature.version, - configs: feature.configs as Prisma.InputJsonValue, - }, - }); - } -} - -async function migrateNewFeatureTable(prisma: PrismaService) { - const waitingList = await prisma.newFeaturesWaitingList.findMany(); - for (const oldUser of waitingList) { - const user = await prisma.user.findFirst({ - where: { - email: oldUser.email, - }, - }); - if (user) { - const hasEarlyAccess = await prisma.userFeatures.count({ - where: { - userId: user.id, - feature: { - feature: FeatureType.EarlyAccess, - }, - activated: true, - }, - }); - if (hasEarlyAccess === 0) { - await prisma.$transaction(async tx => { - const latestFlag = await tx.userFeatures.findFirst({ - where: { - userId: user.id, - feature: { - feature: FeatureType.EarlyAccess, - type: FeatureKind.Feature, - }, - activated: true, - }, - orderBy: { - createdAt: 'desc', - }, - }); - if (latestFlag) { - return latestFlag.id; - } else { - return tx.userFeatures - .create({ - data: { - reason: 'Early access user', - activated: true, - user: { - connect: { - id: user.id, - }, - }, - feature: { - connect: { - feature_version: { - feature: FeatureType.EarlyAccess, - version: 1, - }, - type: FeatureKind.Feature, - }, - }, - }, - }) - .then(r => r.id); - } - }); - } - } - } -} diff --git a/packages/backend/server/src/data/migrations/1703756315970-unamed-account.ts b/packages/backend/server/src/data/migrations/1703756315970-unamed-account.ts new file mode 100644 index 0000000000..bd8b7b8d4c --- /dev/null +++ b/packages/backend/server/src/data/migrations/1703756315970-unamed-account.ts @@ -0,0 +1,31 @@ +import type { UserType } from '../../modules/users'; +import { PrismaService } from '../../prisma'; + +export class UnamedAccount1703756315970 { + // do the migration + static async up(db: PrismaService) { + await db.$transaction(async tx => { + // only find users with empty names + const users = await db.$queryRaw< + UserType[] + >`SELECT * FROM users WHERE name ~ E'^[\\s\\u2000-\\u200F]*$';`; + console.log( + `renaming ${users.map(({ email }) => email).join('|')} users` + ); + + await Promise.all( + users.map(({ id, email }) => + tx.user.update({ + where: { id }, + data: { + name: email.split('@')[0], + }, + }) + ) + ); + }); + } + + // revert the migration + static async down(_db: PrismaService) {} +} diff --git a/packages/backend/server/src/data/migrations/1703828796699-workspace-blobs.ts b/packages/backend/server/src/data/migrations/1703828796699-workspace-blobs.ts new file mode 100644 index 0000000000..6bbeff1c3e --- /dev/null +++ b/packages/backend/server/src/data/migrations/1703828796699-workspace-blobs.ts @@ -0,0 +1,38 @@ +import { ModuleRef } from '@nestjs/core'; + +import { WorkspaceBlobStorage } from '../../modules/storage'; +import { PrismaService } from '../../prisma'; + +export class WorkspaceBlobs1703828796699 { + // do the migration + static async up(db: PrismaService, injector: ModuleRef) { + const blobStorage = injector.get(WorkspaceBlobStorage, { strict: false }); + let hasMore = true; + let turn = 0; + const eachTurnCount = 50; + + while (hasMore) { + const blobs = await db.blob.findMany({ + skip: turn * eachTurnCount, + take: eachTurnCount, + orderBy: { + createdAt: 'asc', + }, + }); + + hasMore = blobs.length === eachTurnCount; + turn += 1; + + await Promise.all( + blobs.map(async ({ workspaceId, hash, blob }) => + blobStorage.put(workspaceId, hash, blob) + ) + ); + } + } + + // revert the migration + static async down(_db: PrismaService) { + // old data kept, no need to downgrade the migration + } +} diff --git a/packages/backend/server/src/data/migrations/1704352562369-refresh-user-features.ts b/packages/backend/server/src/data/migrations/1704352562369-refresh-user-features.ts new file mode 100644 index 0000000000..407aa22986 --- /dev/null +++ b/packages/backend/server/src/data/migrations/1704352562369-refresh-user-features.ts @@ -0,0 +1,16 @@ +import { Features } from '../../modules/features'; +import { PrismaService } from '../../prisma'; +import { upsertFeature } from './utils/user-features'; + +export class RefreshUserFeatures1704352562369 { + // do the migration + static async up(db: PrismaService) { + // add early access v2 & copilot feature + for (const feature of Features) { + await upsertFeature(db, feature); + } + } + + // revert the migration + static async down(_db: PrismaService) {} +} diff --git a/packages/backend/server/src/data/migrations/utils/user-features.ts b/packages/backend/server/src/data/migrations/utils/user-features.ts new file mode 100644 index 0000000000..0e93ce2328 --- /dev/null +++ b/packages/backend/server/src/data/migrations/utils/user-features.ts @@ -0,0 +1,100 @@ +import { Prisma } from '@prisma/client'; + +import { + CommonFeature, + FeatureKind, + FeatureType, +} from '../../../modules/features'; +import { PrismaService } from '../../../prisma'; + +// upgrade features from lower version to higher version +export async function upsertFeature( + db: PrismaService, + feature: CommonFeature +): Promise { + const hasEqualOrGreaterVersion = + (await db.features.count({ + where: { + feature: feature.feature, + version: { + gte: feature.version, + }, + }, + })) > 0; + // will not update exists version + if (!hasEqualOrGreaterVersion) { + await db.features.create({ + data: { + feature: feature.feature, + type: feature.type, + version: feature.version, + configs: feature.configs as Prisma.InputJsonValue, + }, + }); + } +} + +export async function migrateNewFeatureTable(prisma: PrismaService) { + const waitingList = await prisma.newFeaturesWaitingList.findMany(); + for (const oldUser of waitingList) { + const user = await prisma.user.findFirst({ + where: { + email: oldUser.email, + }, + }); + if (user) { + const hasEarlyAccess = await prisma.userFeatures.count({ + where: { + userId: user.id, + feature: { + feature: FeatureType.EarlyAccess, + }, + activated: true, + }, + }); + if (hasEarlyAccess === 0) { + await prisma.$transaction(async tx => { + const latestFlag = await tx.userFeatures.findFirst({ + where: { + userId: user.id, + feature: { + feature: FeatureType.EarlyAccess, + type: FeatureKind.Feature, + }, + activated: true, + }, + orderBy: { + createdAt: 'desc', + }, + }); + if (latestFlag) { + return latestFlag.id; + } else { + return tx.userFeatures + .create({ + data: { + reason: 'Early access user', + activated: true, + user: { + connect: { + id: user.id, + }, + }, + feature: { + connect: { + feature_version: { + feature: FeatureType.EarlyAccess, + version: 1, + }, + type: FeatureKind.Feature, + }, + }, + }, + }) + .then(r => r.id); + } + }); + } + } + } +} diff --git a/packages/backend/server/src/event/events.ts b/packages/backend/server/src/event/events.ts index 025d664663..f1e4f1e466 100644 --- a/packages/backend/server/src/event/events.ts +++ b/packages/backend/server/src/event/events.ts @@ -1,10 +1,16 @@ -import type { Snapshot, Workspace } from '@prisma/client'; +import type { Snapshot, User, Workspace } from '@prisma/client'; import { Flatten, Payload } from './types'; interface EventDefinitions { workspace: { deleted: Payload; + blob: { + deleted: Payload<{ + workspaceId: Workspace['id']; + name: string; + }>; + }; }; snapshot: { @@ -15,6 +21,10 @@ interface EventDefinitions { >; deleted: Payload>; }; + + user: { + deleted: Payload; + }; } export type EventKV = Flatten; diff --git a/packages/backend/server/src/index.ts b/packages/backend/server/src/index.ts index 1f42851a94..187dd1f920 100644 --- a/packages/backend/server/src/index.ts +++ b/packages/backend/server/src/index.ts @@ -5,7 +5,6 @@ startAutoMetrics(); import { NestFactory } from '@nestjs/core'; import type { NestExpressApplication } from '@nestjs/platform-express'; import cookieParser from 'cookie-parser'; -import { static as staticMiddleware } from 'express'; import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs'; import { AppModule } from './app'; @@ -43,10 +42,6 @@ const config = app.get(Config); const host = config.node.prod ? '0.0.0.0' : 'localhost'; const port = config.port ?? 3010; -if (!config.objectStorage.r2.enabled) { - app.use('/assets', staticMiddleware(config.objectStorage.fs.path)); -} - if (config.redis.enabled) { const redisIoAdapter = new RedisIoAdapter(app); await redisIoAdapter.connectToRedis( diff --git a/packages/backend/server/src/metrics/opentelemetry.ts b/packages/backend/server/src/metrics/opentelemetry.ts index 5b084c8cfc..38355a389d 100644 --- a/packages/backend/server/src/metrics/opentelemetry.ts +++ b/packages/backend/server/src/metrics/opentelemetry.ts @@ -29,7 +29,9 @@ import { SpanExporter, TraceIdRatioBasedSampler, } from '@opentelemetry/sdk-trace-node'; -import { PrismaInstrumentation } from '@prisma/instrumentation'; +import prismaInstrument from '@prisma/instrumentation'; + +const { PrismaInstrumentation } = prismaInstrument; import { PrismaMetricProducer } from './prisma'; diff --git a/packages/backend/server/src/modules/auth/service.ts b/packages/backend/server/src/modules/auth/service.ts index d544c41654..59239cb799 100644 --- a/packages/backend/server/src/modules/auth/service.ts +++ b/packages/backend/server/src/modules/auth/service.ts @@ -51,7 +51,7 @@ export class AuthService { exp: now + this.config.auth.accessTokenExpiresIn, iss: this.config.serverId, sub: user.id, - aud: user.name, + aud: 'https://affine.pro', jti: randomUUID({ disableEntropyCache: true, }), @@ -80,7 +80,7 @@ export class AuthService { iat: now, iss: this.config.serverId, sub: user.id, - aud: user.name, + aud: 'https://affine.pro', jti: randomUUID({ disableEntropyCache: true, }), @@ -100,6 +100,7 @@ export class AuthService { iss: [this.config.serverId], leeway: this.config.auth.leeway, requiredSpecClaims: ['exp', 'iat', 'iss', 'sub'], + aud: ['https://affine.pro'], }) ).data as UserClaim; diff --git a/packages/backend/server/src/modules/auth/utils/jwt.ts b/packages/backend/server/src/modules/auth/utils/jwt.ts index b666636011..82b9bd42c9 100644 --- a/packages/backend/server/src/modules/auth/utils/jwt.ts +++ b/packages/backend/server/src/modules/auth/utils/jwt.ts @@ -38,7 +38,7 @@ export const jwtEncode = async ( exp: now + (maxAge ?? config.auth.accessTokenExpiresIn), iss: config.serverId, sub: user.id, - aud: user.name, + aud: 'https://affine.pro', jti: randomUUID({ disableEntropyCache: true, }), diff --git a/packages/backend/server/src/modules/features/feature.ts b/packages/backend/server/src/modules/features/feature.ts index f0bd2a763c..9f585ed99d 100644 --- a/packages/backend/server/src/modules/features/feature.ts +++ b/packages/backend/server/src/modules/features/feature.ts @@ -19,7 +19,20 @@ class FeatureConfig { } } +export class CopilotFeatureConfig extends FeatureConfig { + override config!: Feature & { feature: FeatureType.Copilot }; + constructor(data: any) { + super(data); + + if (this.config.feature !== FeatureType.Copilot) { + throw new Error('Invalid feature config: type is not Copilot'); + } + } +} + export class EarlyAccessFeatureConfig extends FeatureConfig { + override config!: Feature & { feature: FeatureType.EarlyAccess }; + constructor(data: any) { super(data); @@ -27,25 +40,18 @@ export class EarlyAccessFeatureConfig extends FeatureConfig { throw new Error('Invalid feature config: type is not EarlyAccess'); } } - - checkWhiteList(email: string) { - for (const domain in this.config.configs.whitelist) { - if (email.endsWith(domain)) { - return true; - } - } - return false; - } } const FeatureConfigMap = { + [FeatureType.Copilot]: CopilotFeatureConfig, [FeatureType.EarlyAccess]: EarlyAccessFeatureConfig, }; -const FeatureCache = new Map< - number, - InstanceType<(typeof FeatureConfigMap)[FeatureType]> ->(); +export type FeatureConfigType = InstanceType< + (typeof FeatureConfigMap)[F] +>; + +const FeatureCache = new Map>(); export async function getFeature(prisma: PrismaService, featureId: number) { const cachedQuota = FeatureCache.get(featureId); diff --git a/packages/backend/server/src/modules/features/management.ts b/packages/backend/server/src/modules/features/management.ts index d7e274525c..57766fc519 100644 --- a/packages/backend/server/src/modules/features/management.ts +++ b/packages/backend/server/src/modules/features/management.ts @@ -1,35 +1,32 @@ -import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import { Config } from '../../config'; import { PrismaService } from '../../prisma'; -import { EarlyAccessFeatureConfig } from './feature'; import { FeatureService } from './service'; import { FeatureType } from './types'; -enum NewFeaturesKind { - EarlyAccess, -} +const STAFF = ['@toeverything.info']; @Injectable() -export class FeatureManagementService implements OnModuleInit { +export class FeatureManagementService { protected logger = new Logger(FeatureManagementService.name); - private earlyAccessFeature?: EarlyAccessFeatureConfig; + constructor( private readonly feature: FeatureService, private readonly prisma: PrismaService, private readonly config: Config ) {} - async onModuleInit() { - this.earlyAccessFeature = await this.feature.getFeature( - FeatureType.EarlyAccess - ); - } // ======== Admin ======== // todo(@darkskygit): replace this with abac isStaff(email: string) { - return this.earlyAccessFeature?.checkWhiteList(email) ?? false; + for (const domain of STAFF) { + if (email.endsWith(domain)) { + return true; + } + } + return false; } // ======== Early Access ======== @@ -38,7 +35,7 @@ export class FeatureManagementService implements OnModuleInit { return this.feature.addUserFeature( userId, FeatureType.EarlyAccess, - 1, + 2, 'Early access user' ); } @@ -61,29 +58,54 @@ export class FeatureManagementService implements OnModuleInit { }); if (user) { const canEarlyAccess = await this.feature - .hasFeature(user.id, FeatureType.EarlyAccess) + .hasUserFeature(user.id, FeatureType.EarlyAccess) .catch(() => false); - if (canEarlyAccess) { - return true; - } - // TODO: Outdated, switch to feature gates - const oldCanEarlyAccess = await this.prisma.newFeaturesWaitingList - .findUnique({ - where: { email, type: NewFeaturesKind.EarlyAccess }, - }) - .then(x => !!x) - .catch(() => false); - if (oldCanEarlyAccess) { - this.logger.warn( - `User ${email} has early access in old table but not in new table` - ); - } - return oldCanEarlyAccess; + return canEarlyAccess; } return false; } else { return true; } } + + // ======== Workspace Feature ======== + async addWorkspaceFeatures( + workspaceId: string, + feature: FeatureType, + version?: number, + reason?: string + ) { + const latestVersions = await this.feature.getFeaturesVersion(); + // use latest version if not specified + const latestVersion = version || latestVersions[feature]; + if (!Number.isInteger(latestVersion)) { + throw new Error(`Version of feature ${feature} not found`); + } + return this.feature.addWorkspaceFeature( + workspaceId, + feature, + latestVersion, + reason || 'add feature by api' + ); + } + + async getWorkspaceFeatures(workspaceId: string) { + const features = await this.feature.getWorkspaceFeatures(workspaceId); + return features.filter(f => f.activated).map(f => f.feature.name); + } + + async hasWorkspaceFeature(workspaceId: string, feature: FeatureType) { + return this.feature.hasWorkspaceFeature(workspaceId, feature); + } + + async removeWorkspaceFeature(workspaceId: string, feature: FeatureType) { + return this.feature + .removeWorkspaceFeature(workspaceId, feature) + .then(c => c > 0); + } + + async listFeatureWorkspaces(feature: FeatureType) { + return this.feature.listFeatureWorkspaces(feature); + } } diff --git a/packages/backend/server/src/modules/features/service.ts b/packages/backend/server/src/modules/features/service.ts index beed9dc4fb..b7bf53c023 100644 --- a/packages/backend/server/src/modules/features/service.ts +++ b/packages/backend/server/src/modules/features/service.ts @@ -2,7 +2,8 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '../../prisma'; import { UserType } from '../users/types'; -import { getFeature } from './feature'; +import { WorkspaceType } from '../workspaces/types'; +import { FeatureConfigType, getFeature } from './feature'; import { FeatureKind, FeatureType } from './types'; @Injectable() @@ -21,14 +22,23 @@ export class FeatureService { }); return features.reduce( (acc, feature) => { - acc[feature.feature] = feature.version; + // only keep the latest version + if (acc[feature.feature]) { + if (acc[feature.feature] < feature.version) { + acc[feature.feature] = feature.version; + } + } else { + acc[feature.feature] = feature.version; + } return acc; }, {} as Record ); } - async getFeature(feature: FeatureType) { + async getFeature( + feature: F + ): Promise | undefined> { const data = await this.prisma.features.findFirst({ where: { feature, @@ -40,11 +50,13 @@ export class FeatureService { }, }); if (data) { - return getFeature(this.prisma, data.id); + return getFeature(this.prisma, data.id) as FeatureConfigType; } return undefined; } + // ======== User Features ======== + async addUserFeature( userId: string, feature: FeatureType, @@ -114,6 +126,11 @@ export class FeatureService { .then(r => r.count); } + /** + * get user's features, will included inactivated features + * @param userId user id + * @returns list of features + */ async getUserFeatures(userId: string) { const features = await this.prisma.userFeatures.findMany({ where: { @@ -167,7 +184,7 @@ export class FeatureService { .then(users => users.map(user => user.user)); } - async hasFeature(userId: string, feature: FeatureType) { + async hasUserFeature(userId: string, feature: FeatureType) { return this.prisma.userFeatures .count({ where: { @@ -181,4 +198,145 @@ export class FeatureService { }) .then(count => count > 0); } + + // ======== Workspace Features ======== + + async addWorkspaceFeature( + workspaceId: string, + feature: FeatureType, + version: number, + reason: string, + expiredAt?: Date | string + ) { + return this.prisma.$transaction(async tx => { + const latestFlag = await tx.workspaceFeatures.findFirst({ + where: { + workspaceId, + feature: { + feature, + type: FeatureKind.Feature, + }, + activated: true, + }, + orderBy: { + createdAt: 'desc', + }, + }); + if (latestFlag) { + return latestFlag.id; + } else { + return tx.workspaceFeatures + .create({ + data: { + reason, + expiredAt, + activated: true, + workspace: { + connect: { + id: workspaceId, + }, + }, + feature: { + connect: { + feature_version: { + feature, + version, + }, + type: FeatureKind.Feature, + }, + }, + }, + }) + .then(r => r.id); + } + }); + } + + async removeWorkspaceFeature(workspaceId: string, feature: FeatureType) { + return this.prisma.workspaceFeatures + .updateMany({ + where: { + workspaceId, + feature: { + feature, + type: FeatureKind.Feature, + }, + activated: true, + }, + data: { + activated: false, + }, + }) + .then(r => r.count); + } + + /** + * get workspace's features, will included inactivated features + * @param workspaceId workspace id + * @returns list of features + */ + async getWorkspaceFeatures(workspaceId: string) { + const features = await this.prisma.workspaceFeatures.findMany({ + where: { + workspace: { id: workspaceId }, + feature: { + type: FeatureKind.Feature, + }, + }, + select: { + activated: true, + reason: true, + createdAt: true, + expiredAt: true, + featureId: true, + }, + }); + + const configs = await Promise.all( + features.map(async feature => ({ + ...feature, + feature: await getFeature(this.prisma, feature.featureId), + })) + ); + + return configs.filter(feature => !!feature.feature); + } + + async listFeatureWorkspaces(feature: FeatureType): Promise { + return this.prisma.workspaceFeatures + .findMany({ + where: { + activated: true, + feature: { + feature: feature, + type: FeatureKind.Feature, + }, + }, + select: { + workspace: { + select: { + id: true, + public: true, + createdAt: true, + }, + }, + }, + }) + .then(wss => wss.map(ws => ws.workspace as WorkspaceType)); + } + + async hasWorkspaceFeature(workspaceId: string, feature: FeatureType) { + return this.prisma.workspaceFeatures + .count({ + where: { + workspaceId, + activated: true, + feature: { + feature, + type: FeatureKind.Feature, + }, + }, + }) + .then(count => count > 0); + } } diff --git a/packages/backend/server/src/modules/features/types/common.ts b/packages/backend/server/src/modules/features/types/common.ts new file mode 100644 index 0000000000..913705567b --- /dev/null +++ b/packages/backend/server/src/modules/features/types/common.ts @@ -0,0 +1,11 @@ +import { registerEnumType } from '@nestjs/graphql'; + +export enum FeatureType { + Copilot = 'copilot', + EarlyAccess = 'early_access', +} + +registerEnumType(FeatureType, { + name: 'FeatureType', + description: 'The type of workspace feature', +}); diff --git a/packages/backend/server/src/modules/features/types/copilot.ts b/packages/backend/server/src/modules/features/types/copilot.ts new file mode 100644 index 0000000000..0a58096354 --- /dev/null +++ b/packages/backend/server/src/modules/features/types/copilot.ts @@ -0,0 +1,8 @@ +import { z } from 'zod'; + +import { FeatureType } from './common'; + +export const featureCopilot = z.object({ + feature: z.literal(FeatureType.Copilot), + configs: z.object({}), +}); diff --git a/packages/backend/server/src/modules/features/types/early-access.ts b/packages/backend/server/src/modules/features/types/early-access.ts new file mode 100644 index 0000000000..abf3b238f5 --- /dev/null +++ b/packages/backend/server/src/modules/features/types/early-access.ts @@ -0,0 +1,8 @@ +import { z } from 'zod'; + +import { FeatureType } from './common'; + +export const featureEarlyAccess = z.object({ + feature: z.literal(FeatureType.EarlyAccess), + configs: z.object({}), +}); diff --git a/packages/backend/server/src/modules/features/types.ts b/packages/backend/server/src/modules/features/types/index.ts similarity index 58% rename from packages/backend/server/src/modules/features/types.ts rename to packages/backend/server/src/modules/features/types/index.ts index 007463b07d..0fd8b62323 100644 --- a/packages/backend/server/src/modules/features/types.ts +++ b/packages/backend/server/src/modules/features/types/index.ts @@ -1,7 +1,9 @@ -import { URL } from 'node:url'; - import { z } from 'zod'; +import { FeatureType } from './common'; +import { featureCopilot } from './copilot'; +import { featureEarlyAccess } from './early-access'; + /// ======== common schema ======== export enum FeatureKind { @@ -20,30 +22,13 @@ export type CommonFeature = z.infer; /// ======== feature define ======== -export enum FeatureType { - EarlyAccess = 'early_access', -} - -function checkHostname(host: string) { - try { - return new URL(`https://${host}`).hostname === host; - } catch (_) { - return false; - } -} - -const featureEarlyAccess = z.object({ - feature: z.literal(FeatureType.EarlyAccess), - configs: z.object({ - whitelist: z - .string() - .startsWith('@') - .refine(domain => checkHostname(domain.slice(1))) - .array(), - }), -}); - export const Features: Feature[] = [ + { + feature: FeatureType.Copilot, + type: FeatureKind.Feature, + version: 1, + configs: {}, + }, { feature: FeatureType.EarlyAccess, type: FeatureKind.Feature, @@ -52,6 +37,12 @@ export const Features: Feature[] = [ whitelist: ['@toeverything.info'], }, }, + { + feature: FeatureType.EarlyAccess, + type: FeatureKind.Feature, + version: 2, + configs: {}, + }, ]; /// ======== schema infer ======== @@ -60,6 +51,8 @@ export const FeatureSchema = commonFeatureSchema .extend({ type: z.literal(FeatureKind.Feature), }) - .and(z.discriminatedUnion('feature', [featureEarlyAccess])); + .and(z.discriminatedUnion('feature', [featureCopilot, featureEarlyAccess])); export type Feature = z.infer; + +export { FeatureType }; diff --git a/packages/backend/server/src/modules/index.ts b/packages/backend/server/src/modules/index.ts index 0b22eeb069..c2edffda85 100644 --- a/packages/backend/server/src/modules/index.ts +++ b/packages/backend/server/src/modules/index.ts @@ -8,6 +8,7 @@ import { DocModule } from './doc'; import { PaymentModule } from './payment'; import { QuotaModule } from './quota'; import { SelfHostedModule } from './self-hosted'; +import { StorageModule } from './storage'; import { SyncModule } from './sync'; import { UsersModule } from './users'; import { WorkspaceModule } from './workspaces'; @@ -27,7 +28,8 @@ switch (SERVER_FLAVOR) { WorkspaceModule, UsersModule, SyncModule, - DocModule + DocModule, + StorageModule ); break; case 'graphql': @@ -39,7 +41,8 @@ switch (SERVER_FLAVOR) { UsersModule, DocModule, PaymentModule, - QuotaModule + QuotaModule, + StorageModule ); break; case 'allinone': @@ -53,7 +56,8 @@ switch (SERVER_FLAVOR) { QuotaModule, SyncModule, DocModule, - PaymentModule + PaymentModule, + StorageModule ); break; } diff --git a/packages/backend/server/src/modules/quota/index.ts b/packages/backend/server/src/modules/quota/index.ts index 59ac207e37..2fc8c8edd5 100644 --- a/packages/backend/server/src/modules/quota/index.ts +++ b/packages/backend/server/src/modules/quota/index.ts @@ -1,5 +1,6 @@ import { Module } from '@nestjs/common'; +import { StorageModule } from '../storage'; import { PermissionService } from '../workspaces/permission'; import { QuotaService } from './service'; import { QuotaManagementService } from './storage'; @@ -11,6 +12,7 @@ import { QuotaManagementService } from './storage'; * - quota statistics */ @Module({ + imports: [StorageModule], providers: [PermissionService, QuotaService, QuotaManagementService], exports: [QuotaService, QuotaManagementService], }) diff --git a/packages/backend/server/src/modules/quota/storage.ts b/packages/backend/server/src/modules/quota/storage.ts index fff2dd50df..f754c6576c 100644 --- a/packages/backend/server/src/modules/quota/storage.ts +++ b/packages/backend/server/src/modules/quota/storage.ts @@ -1,7 +1,6 @@ -import type { Storage } from '@affine/storage'; -import { Inject, Injectable, NotFoundException } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; -import { StorageProvide } from '../../storage'; +import { WorkspaceBlobStorage } from '../storage'; import { PermissionService } from '../workspaces/permission'; import { QuotaService } from './service'; @@ -10,7 +9,7 @@ export class QuotaManagementService { constructor( private readonly quota: QuotaService, private readonly permissions: PermissionService, - @Inject(StorageProvide) private readonly storage: Storage + private readonly storage: WorkspaceBlobStorage ) {} async getUserQuota(userId: string) { @@ -29,7 +28,12 @@ export class QuotaManagementService { // TODO: lazy calc, need to be optimized with cache async getUserUsage(userId: string) { const workspaces = await this.permissions.getOwnedWorkspaces(userId); - return this.storage.blobsSize(workspaces); + + const sizes = await Promise.all( + workspaces.map(workspace => this.storage.totalSize(workspace)) + ); + + return sizes.reduce((total, size) => total + size, 0); } // get workspace's owner quota and total size of used diff --git a/packages/backend/server/src/modules/storage/__tests__/fs.spec.ts b/packages/backend/server/src/modules/storage/__tests__/fs.spec.ts new file mode 100644 index 0000000000..242c7d5664 --- /dev/null +++ b/packages/backend/server/src/modules/storage/__tests__/fs.spec.ts @@ -0,0 +1,113 @@ +import { promises as fs } from 'node:fs'; +import { join } from 'node:path'; + +import test from 'ava'; +import { getStreamAsBuffer } from 'get-stream'; + +import { ListObjectsMetadata } from '../providers'; +import { FsStorageProvider } from '../providers/fs'; + +const config = { + path: join(process.cwd(), 'node_modules', '.cache/affine-test-storage'), +}; + +function createProvider() { + return new FsStorageProvider( + config, + 'test' + Math.random().toString(16).substring(2, 8) + ); +} + +function keys(list: ListObjectsMetadata[]) { + return list.map(i => i.key); +} + +async function randomPut( + provider: FsStorageProvider, + prefix = '' +): Promise { + const key = prefix + 'test-key-' + Math.random().toString(16).substring(2, 8); + const body = Buffer.from(key); + provider.put(key, body); + return key; +} + +test.after.always(() => { + fs.rm(config.path, { recursive: true }); +}); + +test('put & get', async t => { + const provider = createProvider(); + const key = 'testKey'; + const body = Buffer.from('testBody'); + await provider.put(key, body); + + const result = await provider.get(key); + + t.deepEqual(await getStreamAsBuffer(result.body!), body); + t.is(result.metadata?.contentLength, body.length); +}); + +test('list - one level', async t => { + const provider = createProvider(); + const list = await Promise.all( + Array.from({ length: 100 }).map(() => randomPut(provider)) + ); + list.sort(); + // random order, use set + const result = await provider.list(); + t.deepEqual(keys(result), list); + + const result2 = await provider.list('test-key'); + t.deepEqual(keys(result2), list); + + const result3 = await provider.list('testKey'); + t.is(result3.length, 0); +}); + +test('list recursively', async t => { + const provider = createProvider(); + + await Promise.all([ + Promise.all(Array.from({ length: 10 }).map(() => randomPut(provider))), + Promise.all( + Array.from({ length: 10 }).map(() => randomPut(provider, 'a/')) + ), + Promise.all( + Array.from({ length: 10 }).map(() => randomPut(provider, 'a/b/')) + ), + Promise.all( + Array.from({ length: 10 }).map(() => randomPut(provider, 'a/b/t/')) + ), + ]); + + const r1 = await provider.list(); + t.is(r1.length, 40); + + // contains all `a/xxx` and `a/b/xxx` and `a/b/c/xxx` + const r2 = await provider.list('a'); + t.is(r2.length, 30); + + // contains only `a/b/xxx` + const r3 = await provider.list('a/b'); + const r4 = await provider.list('a/b/'); + t.is(r3.length, 20); + t.deepEqual(r3, r4); + + // prefix is not ended with '/', it's open to all files and sub dirs + // contains all `a/b/t/xxx` and `a/b/t{xxxx}` + const r5 = await provider.list('a/b/t'); + + t.is(r5.length, 20); +}); + +test.only('delete', async t => { + const provider = createProvider(); + const key = 'testKey'; + const body = Buffer.from('testBody'); + await provider.put(key, body); + + await provider.delete(key); + + await t.throwsAsync(() => fs.access(join(config.path, provider.bucket, key))); +}); diff --git a/packages/backend/server/src/modules/storage/fs.ts b/packages/backend/server/src/modules/storage/fs.ts deleted file mode 100644 index af82527d8a..0000000000 --- a/packages/backend/server/src/modules/storage/fs.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { randomUUID } from 'node:crypto'; -import { createWriteStream } from 'node:fs'; -import { mkdir } from 'node:fs/promises'; -import { join } from 'node:path'; -import { pipeline } from 'node:stream/promises'; - -import { Injectable } from '@nestjs/common'; - -import { Config } from '../../config'; -import { FileUpload } from '../../types'; - -@Injectable() -export class FSService { - constructor(private readonly config: Config) {} - - async writeFile(key: string, file: FileUpload) { - const dest = this.config.objectStorage.fs.path; - const fileName = `${key}-${randomUUID()}`; - const prefix = this.config.node.dev - ? `${this.config.https ? 'https' : 'http'}://${this.config.host}:${ - this.config.port - }` - : ''; - await mkdir(dest, { recursive: true }); - const destFile = join(dest, fileName); - await pipeline(file.createReadStream(), createWriteStream(destFile)); - - return `${prefix}/assets/${fileName}`; - } -} diff --git a/packages/backend/server/src/modules/storage/index.ts b/packages/backend/server/src/modules/storage/index.ts index 1a21758549..071931e24f 100644 --- a/packages/backend/server/src/modules/storage/index.ts +++ b/packages/backend/server/src/modules/storage/index.ts @@ -1,11 +1,11 @@ import { Module } from '@nestjs/common'; -import { FSService } from './fs'; -import { S3 } from './s3'; -import { StorageService } from './storage.service'; +import { AvatarStorage, WorkspaceBlobStorage } from './wrappers'; @Module({ - providers: [S3, StorageService, FSService], - exports: [StorageService], + providers: [WorkspaceBlobStorage, AvatarStorage], + exports: [WorkspaceBlobStorage, AvatarStorage], }) export class StorageModule {} + +export { AvatarStorage, WorkspaceBlobStorage }; diff --git a/packages/backend/server/src/modules/storage/providers/fs.ts b/packages/backend/server/src/modules/storage/providers/fs.ts new file mode 100644 index 0000000000..3ad735d2e4 --- /dev/null +++ b/packages/backend/server/src/modules/storage/providers/fs.ts @@ -0,0 +1,258 @@ +import { + accessSync, + constants, + createReadStream, + Dirent, + mkdirSync, + readdirSync, + readFileSync, + rmSync, + statSync, + writeFileSync, +} from 'node:fs'; +import { join, parse, resolve } from 'node:path'; + +import { Logger } from '@nestjs/common'; +import { Readable } from 'stream'; + +import { FsStorageConfig } from '../../../config/storage'; +import { + BlobInputType, + GetObjectMetadata, + ListObjectsMetadata, + PutObjectMetadata, + StorageProvider, +} from './provider'; +import { autoMetadata, toBuffer } from './utils'; + +function escapeKey(key: string): string { + // avoid '../' and './' in key + return key.replace(/\.?\.[/\\]/g, '%'); +} + +export class FsStorageProvider implements StorageProvider { + private readonly path: string; + private readonly logger: Logger; + + readonly type = 'fs'; + + constructor( + config: FsStorageConfig, + public readonly bucket: string + ) { + this.path = resolve(config.path, bucket); + this.ensureAvailability(); + + this.logger = new Logger(`${FsStorageProvider.name}:${bucket}`); + } + + async put( + key: string, + body: BlobInputType, + metadata: PutObjectMetadata = {} + ): Promise { + key = escapeKey(key); + const blob = await toBuffer(body); + + // write object + this.writeObject(key, blob); + // write metadata + await this.writeMetadata(key, blob, metadata); + this.logger.verbose(`Object \`${key}\` put`); + } + + async get(key: string): Promise<{ + body?: Readable; + metadata?: GetObjectMetadata; + }> { + key = escapeKey(key); + + try { + const metadata = this.readMetadata(key); + const stream = this.readObject(this.join(key)); + this.logger.verbose(`Read object \`${key}\``); + return { + body: stream, + metadata, + }; + } catch (e) { + this.logger.error(`Failed to read object \`${key}\``, e); + return {}; + } + } + + async list(prefix?: string): Promise { + // prefix cases: + // - `undefined`: list all objects + // - `a/b`: list objects under dir `a` with prefix `b`, `b` might be a dir under `a` as well. + // - `a/b/` list objects under dir `a/b` + + // read dir recursively and filter out '.metadata.json' files + let dir = this.path; + if (prefix) { + prefix = escapeKey(prefix); + const parts = prefix.split(/[/\\]/); + // for prefix `a/b/c`, move `a/b` to dir and `c` to key prefix + if (parts.length > 1) { + dir = join(dir, ...parts.slice(0, -1)); + prefix = parts[parts.length - 1]; + } + } + + const results: ListObjectsMetadata[] = []; + async function getFiles(dir: string, prefix?: string): Promise { + try { + const entries: Dirent[] = readdirSync(dir, { withFileTypes: true }); + + for (const entry of entries) { + const res = join(dir, entry.name); + + if (entry.isDirectory()) { + if (!prefix || entry.name.startsWith(prefix)) { + await getFiles(res); + } + } else if ( + (!prefix || entry.name.startsWith(prefix)) && + !entry.name.endsWith('.metadata.json') + ) { + const stat = statSync(res); + results.push({ + key: res, + lastModified: stat.mtime, + size: stat.size, + }); + } + } + } catch (e) { + // failed to read dir, stop recursion + } + } + + await getFiles(dir, prefix); + + // trim path with `this.path` prefix + results.forEach(r => (r.key = r.key.slice(this.path.length + 1))); + + return results; + } + + delete(key: string): Promise { + key = escapeKey(key); + + try { + rmSync(this.join(key), { force: true }); + rmSync(this.join(`${key}.metadata.json`), { force: true }); + } catch (e) { + throw new Error(`Failed to delete object \`${key}\``, { + cause: e, + }); + } + + this.logger.verbose(`Object \`${key}\` deleted`); + + return Promise.resolve(); + } + + ensureAvailability() { + // check stats + const stats = statSync(this.path, { + throwIfNoEntry: false, + }); + + // not existing, create it + if (!stats) { + try { + mkdirSync(this.path, { recursive: true }); + } catch (e) { + throw new Error( + `Failed to create target directory for fs storage provider: ${this.path}`, + { + cause: e, + } + ); + } + } else if (stats.isDirectory()) { + // the target directory has already existed, check if it is readable & writable + try { + accessSync(this.path, constants.W_OK | constants.R_OK); + } catch (e) { + throw new Error( + `The target directory for fs storage provider has already existed, but it is not readable & writable: ${this.path}`, + { + cause: e, + } + ); + } + } else if (stats.isFile()) { + throw new Error( + `The target directory for fs storage provider is a file: ${this.path}` + ); + } + } + + private join(...paths: string[]) { + return join(this.path, ...paths); + } + + private readObject(file: string): Readable | undefined { + const state = statSync(file, { throwIfNoEntry: false }); + + if (state?.isFile()) { + return createReadStream(file); + } + + return undefined; + } + + private writeObject(key: string, blob: Buffer) { + const path = this.join(key); + mkdirSync(parse(path).dir, { recursive: true }); + writeFileSync(path, blob); + } + + private async writeMetadata( + key: string, + blob: Buffer, + raw: PutObjectMetadata + ) { + try { + const metadata = await autoMetadata(blob, raw); + + if (raw.checksumCRC32 && metadata.checksumCRC32 !== raw.checksumCRC32) { + throw new Error( + 'The checksum of the uploaded file is not matched with the one you provide, the file may be corrupted and the uploading will not be processed.' + ); + } + + writeFileSync( + this.join(`${key}.metadata.json`), + JSON.stringify({ + ...metadata, + lastModified: Date.now(), + }) + ); + } catch (e) { + this.logger.warn(`Failed to write metadata of object \`${key}\``, e); + } + } + + private readMetadata(key: string): GetObjectMetadata | undefined { + try { + const raw = JSON.parse( + readFileSync(this.join(`${key}.metadata.json`), { + encoding: 'utf-8', + }) + ); + + return { + ...raw, + lastModified: new Date(raw.lastModified), + expires: raw.expires ? new Date(raw.expires) : undefined, + }; + } catch (e) { + this.logger.warn(`Failed to read metadata of object \`${key}\``, e); + + return; + } + } +} diff --git a/packages/backend/server/src/modules/storage/providers/index.ts b/packages/backend/server/src/modules/storage/providers/index.ts new file mode 100644 index 0000000000..bfcc51cda2 --- /dev/null +++ b/packages/backend/server/src/modules/storage/providers/index.ts @@ -0,0 +1,34 @@ +import { AFFiNEStorageConfig, Storages } from '../../../config/storage'; +import { FsStorageProvider } from './fs'; +import type { StorageProvider } from './provider'; +import { R2StorageProvider } from './r2'; +import { S3StorageProvider } from './s3'; + +export function createStorageProvider( + config: AFFiNEStorageConfig, + storage: Storages +): StorageProvider { + const storageConfig = config.storages[storage]; + const providerConfig = config.providers[storageConfig.provider] as any; + if (!providerConfig) { + throw new Error( + `Failed to create ${storageConfig.provider} storage, configuration not correctly set` + ); + } + + if (storageConfig.provider === 's3') { + return new S3StorageProvider(providerConfig, storageConfig.bucket); + } + + if (storageConfig.provider === 'r2') { + return new R2StorageProvider(providerConfig, storageConfig.bucket); + } + + if (storageConfig.provider === 'fs') { + return new FsStorageProvider(providerConfig, storageConfig.bucket); + } + + throw new Error(`Unknown storage provider type: ${storageConfig.provider}`); +} + +export type * from './provider'; diff --git a/packages/backend/server/src/modules/storage/providers/provider.ts b/packages/backend/server/src/modules/storage/providers/provider.ts new file mode 100644 index 0000000000..6e83189184 --- /dev/null +++ b/packages/backend/server/src/modules/storage/providers/provider.ts @@ -0,0 +1,42 @@ +import type { Readable } from 'node:stream'; + +import { StorageProviderType } from '../../../config'; + +export interface GetObjectMetadata { + /** + * @default 'application/octet-stream' + */ + contentType: string; + contentLength: number; + lastModified: Date; + checksumCRC32?: string; +} + +export interface PutObjectMetadata { + contentType?: string; + contentLength?: number; + checksumCRC32?: string; +} + +export interface ListObjectsMetadata { + key: string; + lastModified: Date; + size: number; +} + +export type BlobInputType = Buffer | Readable | string; +export type BlobOutputType = Readable; + +export interface StorageProvider { + readonly type: StorageProviderType; + put( + key: string, + body: BlobInputType, + metadata?: PutObjectMetadata + ): Promise; + get( + key: string + ): Promise<{ body?: BlobOutputType; metadata?: GetObjectMetadata }>; + list(prefix?: string): Promise; + delete(key: string): Promise; +} diff --git a/packages/backend/server/src/modules/storage/providers/r2.ts b/packages/backend/server/src/modules/storage/providers/r2.ts new file mode 100644 index 0000000000..791e152002 --- /dev/null +++ b/packages/backend/server/src/modules/storage/providers/r2.ts @@ -0,0 +1,16 @@ +import { R2StorageConfig } from '../../../config/storage'; +import { S3StorageProvider } from './s3'; + +export class R2StorageProvider extends S3StorageProvider { + override readonly type = 'r2' as any /* cast 'r2' to 's3' */; + + constructor(config: R2StorageConfig, bucket: string) { + super( + { + ...config, + endpoint: `https://${config.accountId}.r2.cloudflarestorage.com`, + }, + bucket + ); + } +} diff --git a/packages/backend/server/src/modules/storage/providers/s3.ts b/packages/backend/server/src/modules/storage/providers/s3.ts new file mode 100644 index 0000000000..30c1f9ca8e --- /dev/null +++ b/packages/backend/server/src/modules/storage/providers/s3.ts @@ -0,0 +1,163 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { Readable } from 'node:stream'; + +import { + DeleteObjectCommand, + GetObjectCommand, + ListObjectsV2Command, + PutObjectCommand, + S3Client, +} from '@aws-sdk/client-s3'; +import { Logger } from '@nestjs/common'; + +import { S3StorageConfig } from '../../../config/storage'; +import { + BlobInputType, + GetObjectMetadata, + ListObjectsMetadata, + PutObjectMetadata, + StorageProvider, +} from './provider'; +import { autoMetadata, toBuffer } from './utils'; + +export class S3StorageProvider implements StorageProvider { + private readonly logger: Logger; + protected client: S3Client; + + readonly type = 's3'; + + constructor( + config: S3StorageConfig, + public readonly bucket: string + ) { + this.client = new S3Client({ region: 'auto', ...config }); + this.logger = new Logger(`${S3StorageProvider.name}:${bucket}`); + } + + async put( + key: string, + body: BlobInputType, + metadata: PutObjectMetadata = {} + ): Promise { + const blob = await toBuffer(body); + + metadata = await autoMetadata(blob, metadata); + + try { + await this.client.send( + new PutObjectCommand({ + Bucket: this.bucket, + Key: key, + Body: body, + + // metadata + ContentType: metadata.contentType, + ContentLength: metadata.contentLength, + // TODO: Cloudflare doesn't support CRC32, use md5 instead later. + // ChecksumCRC32: metadata.checksumCRC32, + }) + ); + + this.logger.verbose(`Object \`${key}\` put`); + } catch (e) { + throw new Error(`Failed to put object \`${key}\``, { + cause: e, + }); + } + } + + async get(key: string): Promise<{ + body?: Readable; + metadata?: GetObjectMetadata; + }> { + try { + const obj = await this.client.send( + new GetObjectCommand({ + Bucket: this.bucket, + Key: key, + }) + ); + + if (!obj.Body) { + this.logger.verbose(`Object \`${key}\` not found`); + return {}; + } + + this.logger.verbose(`Read object \`${key}\``); + return { + // @ts-expect-errors ignore browser response type `Blob` + body: obj.Body, + metadata: { + // always set when putting object + contentType: obj.ContentType!, + contentLength: obj.ContentLength!, + lastModified: obj.LastModified!, + checksumCRC32: obj.ChecksumCRC32, + }, + }; + } catch (e) { + throw new Error(`Failed to read object \`${key}\``, { + cause: e, + }); + } + } + + async list(prefix?: string): Promise { + // continuationToken should be `string | undefined`, + // but TypeScript will fail on type infer in the code below. + // Seems to be a bug in TypeScript + let continuationToken: any = undefined; + let hasMore = true; + let result: ListObjectsMetadata[] = []; + + try { + while (hasMore) { + const listResult = await this.client.send( + new ListObjectsV2Command({ + Bucket: this.bucket, + Prefix: prefix, + ContinuationToken: continuationToken, + }) + ); + + if (listResult.Contents?.length) { + result = result.concat( + listResult.Contents.map(r => ({ + key: r.Key!, + lastModified: r.LastModified!, + size: r.Size!, + })) + ); + } + + // has more items not listed + hasMore = !!listResult.IsTruncated; + continuationToken = listResult.NextContinuationToken; + } + + this.logger.verbose( + `List ${result.length} objects with prefix \`${prefix}\`` + ); + return result; + } catch (e) { + throw new Error(`Failed to list objects with prefix \`${prefix}\``, { + cause: e, + }); + } + } + + async delete(key: string): Promise { + try { + await this.client.send( + new DeleteObjectCommand({ + Bucket: this.bucket, + Key: key, + }) + ); + } catch (e) { + throw new Error(`Failed to delete object \`${key}\``, { + cause: e, + }); + } + } +} diff --git a/packages/backend/server/src/modules/storage/providers/utils.ts b/packages/backend/server/src/modules/storage/providers/utils.ts new file mode 100644 index 0000000000..c1b3355a3f --- /dev/null +++ b/packages/backend/server/src/modules/storage/providers/utils.ts @@ -0,0 +1,49 @@ +import { Readable } from 'node:stream'; + +import { crc32 } from '@node-rs/crc32'; +import { fileTypeFromBuffer } from 'file-type'; +import { getStreamAsBuffer } from 'get-stream'; + +import { BlobInputType, PutObjectMetadata } from './provider'; + +export async function toBuffer(input: BlobInputType): Promise { + return input instanceof Readable + ? await getStreamAsBuffer(input) + : input instanceof Buffer + ? input + : Buffer.from(input); +} + +export async function autoMetadata( + blob: Buffer, + raw: PutObjectMetadata +): Promise { + const metadata = { + ...raw, + }; + try { + // length + if (!metadata.contentLength) { + metadata.contentLength = blob.length; + } + + // checksum + if (!metadata.checksumCRC32) { + metadata.checksumCRC32 = crc32(blob).toString(16); + } + + // mime type + if (!metadata.contentType) { + try { + const typeResult = await fileTypeFromBuffer(blob); + metadata.contentType = typeResult?.mime ?? 'application/octet-stream'; + } catch { + // ignore + } + } + + return metadata; + } catch (e) { + return metadata; + } +} diff --git a/packages/backend/server/src/modules/storage/s3.ts b/packages/backend/server/src/modules/storage/s3.ts deleted file mode 100644 index c849c5aa26..0000000000 --- a/packages/backend/server/src/modules/storage/s3.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { S3Client } from '@aws-sdk/client-s3'; -import { FactoryProvider } from '@nestjs/common'; - -import { Config } from '../../config'; - -export const S3_SERVICE = Symbol('S3_SERVICE'); - -export const S3: FactoryProvider = { - provide: S3_SERVICE, - useFactory: (config: Config) => { - const s3 = new S3Client({ - region: 'auto', - endpoint: `https://${config.objectStorage.r2.accountId}.r2.cloudflarestorage.com`, - credentials: { - accessKeyId: config.objectStorage.r2.accessKeyId, - secretAccessKey: config.objectStorage.r2.secretAccessKey, - }, - }); - return s3; - }, - inject: [Config], -}; diff --git a/packages/backend/server/src/modules/storage/storage.service.ts b/packages/backend/server/src/modules/storage/storage.service.ts deleted file mode 100644 index bb714b00aa..0000000000 --- a/packages/backend/server/src/modules/storage/storage.service.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3'; -import { Inject, Injectable } from '@nestjs/common'; -import { crc32 } from '@node-rs/crc32'; -import { fileTypeFromBuffer } from 'file-type'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore - no types -import { getStreamAsBuffer } from 'get-stream'; - -import { Config } from '../../config'; -import { FileUpload } from '../../types'; -import { FSService } from './fs'; -import { S3_SERVICE } from './s3'; - -@Injectable() -export class StorageService { - constructor( - @Inject(S3_SERVICE) private readonly s3: S3Client, - private readonly fs: FSService, - private readonly config: Config - ) {} - - async uploadFile(key: string, file: FileUpload) { - if (this.config.objectStorage.r2.enabled) { - const readableFile = file.createReadStream(); - const fileBuffer = await getStreamAsBuffer(readableFile); - const mime = (await fileTypeFromBuffer(fileBuffer))?.mime; - const crc32Value = crc32(fileBuffer); - const keyWithCrc32 = `${crc32Value}-${key}`; - await this.s3.send( - new PutObjectCommand({ - Body: fileBuffer, - Bucket: this.config.objectStorage.r2.bucket, - Key: keyWithCrc32, - ContentLength: fileBuffer.length, - ContentType: mime, - }) - ); - return `https://avatar.affineassets.com/${keyWithCrc32}`; - } else { - return this.fs.writeFile(key, file); - } - } -} diff --git a/packages/backend/server/src/modules/storage/wrappers/avatar.ts b/packages/backend/server/src/modules/storage/wrappers/avatar.ts new file mode 100644 index 0000000000..6622631bd8 --- /dev/null +++ b/packages/backend/server/src/modules/storage/wrappers/avatar.ts @@ -0,0 +1,47 @@ +import { Injectable } from '@nestjs/common'; + +import { AFFiNEStorageConfig, Config } from '../../../config'; +import { type EventPayload, OnEvent } from '../../../event'; +import { + BlobInputType, + createStorageProvider, + PutObjectMetadata, + StorageProvider, +} from '../providers'; + +@Injectable() +export class AvatarStorage { + public readonly provider: StorageProvider; + private readonly storageConfig: AFFiNEStorageConfig['storages']['avatar']; + + constructor(private readonly config: Config) { + this.provider = createStorageProvider(this.config.storage, 'avatar'); + this.storageConfig = this.config.storage.storages.avatar; + } + + async put(key: string, blob: BlobInputType, metadata?: PutObjectMetadata) { + await this.provider.put(key, blob, metadata); + let link = this.storageConfig.publicLinkFactory(key); + + if (link.startsWith('/')) { + link = this.config.baseUrl + link; + } + + return link; + } + + get(key: string) { + return this.provider.get(key); + } + + delete(key: string) { + return this.provider.delete(key); + } + + @OnEvent('user.deleted') + async onUserDeleted(user: EventPayload<'user.deleted'>) { + if (user.avatarUrl) { + await this.delete(user.avatarUrl); + } + } +} diff --git a/packages/backend/server/src/modules/storage/wrappers/blob.ts b/packages/backend/server/src/modules/storage/wrappers/blob.ts new file mode 100644 index 0000000000..0eca2d4375 --- /dev/null +++ b/packages/backend/server/src/modules/storage/wrappers/blob.ts @@ -0,0 +1,114 @@ +import { Readable } from 'node:stream'; + +import type { Storage } from '@affine/storage'; +import { Injectable, OnModuleInit } from '@nestjs/common'; + +import { Config } from '../../../config'; +import { EventEmitter, type EventPayload, OnEvent } from '../../../event'; +import { OctoBaseStorageModule } from '../../../storage'; +import { + BlobInputType, + createStorageProvider, + StorageProvider, +} from '../providers'; +import { toBuffer } from '../providers/utils'; + +@Injectable() +export class WorkspaceBlobStorage implements OnModuleInit { + public readonly provider: StorageProvider; + + /** + * @deprecated for backwards compatibility, need to be removed in next stable release + */ + private octobase: Storage | null = null; + + constructor( + private readonly event: EventEmitter, + private readonly config: Config + ) { + this.provider = createStorageProvider(this.config.storage, 'blob'); + } + + async onModuleInit() { + if (!this.config.node.test) { + this.octobase = await OctoBaseStorageModule.Storage.connect( + this.config.db.url + ); + } + } + + async put(workspaceId: string, key: string, blob: BlobInputType) { + const buf = await toBuffer(blob); + await this.provider.put(`${workspaceId}/${key}`, buf); + if (this.octobase) { + await this.octobase.uploadBlob(workspaceId, buf); + } + } + + async get(workspaceId: string, key: string) { + const result = await this.provider.get(`${workspaceId}/${key}`); + if (!result.body && this.octobase) { + const blob = await this.octobase.getBlob(workspaceId, key); + + if (!blob) { + return result; + } + + return { + body: Readable.from(blob.data), + metadata: { + contentType: blob.contentType, + contentLength: blob.size, + lastModified: new Date(blob.lastModified), + }, + }; + } + + return result; + } + + async list(workspaceId: string) { + const blobs = await this.provider.list(workspaceId + '/'); + + blobs.forEach(item => { + // trim workspace prefix + item.key = item.key.slice(workspaceId.length + 1); + }); + + return blobs; + } + + /** + * we won't really delete the blobs until the doc blobs manager is implemented sounded + */ + async delete(_workspaceId: string, _key: string) { + // return this.provider.delete(`${workspaceId}/${key}`); + } + + async totalSize(workspaceId: string) { + const blobs = await this.list(workspaceId); + // how could we ignore the ones get soft-deleted? + return blobs.reduce((acc, item) => acc + item.size, 0); + } + + @OnEvent('workspace.deleted') + async onWorkspaceDeleted(workspaceId: EventPayload<'workspace.deleted'>) { + const blobs = await this.list(workspaceId); + + // to reduce cpu time holding + blobs.forEach(blob => { + this.event.emit('workspace.blob.deleted', { + workspaceId: workspaceId, + name: blob.key, + }); + }); + } + + @OnEvent('workspace.blob.deleted') + async onDeleteWorkspaceBlob({ + workspaceId, + name, + }: EventPayload<'workspace.blob.deleted'>) { + await this.delete(workspaceId, name); + } +} diff --git a/packages/backend/server/src/modules/storage/wrappers/index.ts b/packages/backend/server/src/modules/storage/wrappers/index.ts new file mode 100644 index 0000000000..250e9a8cc3 --- /dev/null +++ b/packages/backend/server/src/modules/storage/wrappers/index.ts @@ -0,0 +1,2 @@ +export { AvatarStorage } from './avatar'; +export { WorkspaceBlobStorage } from './blob'; diff --git a/packages/backend/server/src/modules/users/controller.ts b/packages/backend/server/src/modules/users/controller.ts new file mode 100644 index 0000000000..388ced7f9e --- /dev/null +++ b/packages/backend/server/src/modules/users/controller.ts @@ -0,0 +1,40 @@ +import { + Controller, + ForbiddenException, + Get, + NotFoundException, + Param, + Res, +} from '@nestjs/common'; +import type { Response } from 'express'; + +import { AvatarStorage } from '../storage'; + +@Controller('/api/avatars') +export class UserAvatarController { + constructor(private readonly storage: AvatarStorage) {} + + @Get('/:id') + async getAvatar(@Res() res: Response, @Param('id') id: string) { + if (this.storage.provider.type !== 'fs') { + throw new ForbiddenException( + 'Only available when avatar storage provider set to fs.' + ); + } + + const { body, metadata } = await this.storage.get(id); + + if (!body) { + throw new NotFoundException(`Avatar ${id} not found.`); + } + + // metadata should always exists if body is not null + if (metadata) { + res.setHeader('content-type', metadata.contentType); + res.setHeader('last-modified', metadata.lastModified.toISOString()); + res.setHeader('content-length', metadata.contentLength); + } + + body.pipe(res); + } +} diff --git a/packages/backend/server/src/modules/users/index.ts b/packages/backend/server/src/modules/users/index.ts index e48f44e7b7..f25792b026 100644 --- a/packages/backend/server/src/modules/users/index.ts +++ b/packages/backend/server/src/modules/users/index.ts @@ -3,12 +3,15 @@ import { Module } from '@nestjs/common'; import { FeatureModule } from '../features'; import { QuotaModule } from '../quota'; import { StorageModule } from '../storage'; +import { UserAvatarController } from './controller'; +import { UserManagementResolver } from './management'; import { UserResolver } from './resolver'; import { UsersService } from './users'; @Module({ imports: [StorageModule, FeatureModule, QuotaModule], - providers: [UserResolver, UsersService], + providers: [UserResolver, UserManagementResolver, UsersService], + controllers: [UserAvatarController], exports: [UsersService], }) export class UsersModule {} diff --git a/packages/backend/server/src/modules/users/management.ts b/packages/backend/server/src/modules/users/management.ts new file mode 100644 index 0000000000..a740466b98 --- /dev/null +++ b/packages/backend/server/src/modules/users/management.ts @@ -0,0 +1,91 @@ +import { + BadRequestException, + ForbiddenException, + UseGuards, +} from '@nestjs/common'; +import { Args, Context, Int, Mutation, Query, Resolver } from '@nestjs/graphql'; + +import { CloudThrottlerGuard, Throttle } from '../../throttler'; +import { Auth, CurrentUser } from '../auth/guard'; +import { AuthService } from '../auth/service'; +import { FeatureManagementService } from '../features'; +import { UserType } from './types'; +import { UsersService } from './users'; + +/** + * User resolver + * All op rate limit: 10 req/m + */ +@UseGuards(CloudThrottlerGuard) +@Auth() +@Resolver(() => UserType) +export class UserManagementResolver { + constructor( + private readonly auth: AuthService, + private readonly users: UsersService, + private readonly feature: FeatureManagementService + ) {} + + @Throttle({ + default: { + limit: 10, + ttl: 60, + }, + }) + @Mutation(() => Int) + async addToEarlyAccess( + @CurrentUser() currentUser: UserType, + @Args('email') email: string + ): Promise { + if (!this.feature.isStaff(currentUser.email)) { + throw new ForbiddenException('You are not allowed to do this'); + } + const user = await this.users.findUserByEmail(email); + if (user) { + return this.feature.addEarlyAccess(user.id); + } else { + const user = await this.auth.createAnonymousUser(email); + return this.feature.addEarlyAccess(user.id); + } + } + + @Throttle({ + default: { + limit: 10, + ttl: 60, + }, + }) + @Mutation(() => Int) + async removeEarlyAccess( + @CurrentUser() currentUser: UserType, + @Args('email') email: string + ): Promise { + if (!this.feature.isStaff(currentUser.email)) { + throw new ForbiddenException('You are not allowed to do this'); + } + const user = await this.users.findUserByEmail(email); + if (!user) { + throw new BadRequestException(`User ${email} not found`); + } + return this.feature.removeEarlyAccess(user.id); + } + + @Throttle({ + default: { + limit: 10, + ttl: 60, + }, + }) + @Query(() => [UserType]) + async earlyAccessUsers( + @Context() ctx: { isAdminQuery: boolean }, + @CurrentUser() user: UserType + ): Promise { + if (!this.feature.isStaff(user.email)) { + throw new ForbiddenException('You are not allowed to do this'); + } + // allow query other user's subscription + ctx.isAdminQuery = true; + return this.feature.listEarlyAccess(); + } +} diff --git a/packages/backend/server/src/modules/users/resolver.ts b/packages/backend/server/src/modules/users/resolver.ts index d816c111d2..94cbc2c81d 100644 --- a/packages/backend/server/src/modules/users/resolver.ts +++ b/packages/backend/server/src/modules/users/resolver.ts @@ -1,12 +1,6 @@ -import { - BadRequestException, - ForbiddenException, - HttpStatus, - UseGuards, -} from '@nestjs/common'; +import { BadRequestException, HttpStatus, UseGuards } from '@nestjs/common'; import { Args, - Context, Int, Mutation, Query, @@ -17,15 +11,21 @@ import type { User } from '@prisma/client'; import { GraphQLError } from 'graphql'; import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs'; +import { EventEmitter } from '../../event'; import { PrismaService } from '../../prisma/service'; import { CloudThrottlerGuard, Throttle } from '../../throttler'; import type { FileUpload } from '../../types'; import { Auth, CurrentUser, Public, Publicable } from '../auth/guard'; -import { AuthService } from '../auth/service'; import { FeatureManagementService } from '../features'; import { QuotaService } from '../quota'; -import { StorageService } from '../storage/storage.service'; -import { DeleteAccount, RemoveAvatar, UserQuotaType, UserType } from './types'; +import { AvatarStorage } from '../storage'; +import { + DeleteAccount, + RemoveAvatar, + UserOrLimitedUser, + UserQuotaType, + UserType, +} from './types'; import { UsersService } from './users'; /** @@ -37,12 +37,12 @@ import { UsersService } from './users'; @Resolver(() => UserType) export class UserResolver { constructor( - private readonly auth: AuthService, private readonly prisma: PrismaService, - private readonly storage: StorageService, + private readonly storage: AvatarStorage, private readonly users: UsersService, private readonly feature: FeatureManagementService, - private readonly quota: QuotaService + private readonly quota: QuotaService, + private readonly event: EventEmitter ) {} @Throttle({ @@ -83,14 +83,17 @@ export class UserResolver { ttl: 60, }, }) - @Query(() => UserType, { + @Query(() => UserOrLimitedUser, { name: 'user', description: 'Get user by email', nullable: true, }) @Public() - async user(@Args('email') email: string) { - if (!(await this.feature.canEarlyAccess(email))) { + async user( + @CurrentUser() currentUser?: UserType, + @Args('email') email?: string + ) { + if (!email || !(await this.feature.canEarlyAccess(email))) { return new GraphQLError( `You don't have early access permission\nVisit https://community.affine.pro/c/insider-general/ for more information`, { @@ -101,13 +104,16 @@ export class UserResolver { } ); } + // TODO: need to limit a user can only get another user witch is in the same workspace const user = await this.users.findUserByEmail(email); - if (user?.password) { - const userResponse: UserType = user; - userResponse.hasPassword = true; - } - return user; + if (currentUser) return user; + + // only return limited info when not logged in + return { + email: user?.email, + hasPassword: !!user?.password, + }; } @Throttle({ default: { limit: 10, ttl: 60 } }) @@ -147,10 +153,20 @@ export class UserResolver { if (!user) { throw new BadRequestException(`User not found`); } - const url = await this.storage.uploadFile(`${user.id}-avatar`, avatar); + + const link = await this.storage.put( + `${user.id}-avatar`, + avatar.createReadStream(), + { + contentType: avatar.mimetype, + } + ); + return this.prisma.user.update({ where: { id: user.id }, - data: { avatarUrl: url }, + data: { + avatarUrl: link, + }, }); } @@ -183,70 +199,8 @@ export class UserResolver { }) @Mutation(() => DeleteAccount) async deleteAccount(@CurrentUser() user: UserType): Promise { - await this.users.deleteUser(user.id); + const deletedUser = await this.users.deleteUser(user.id); + this.event.emit('user.deleted', deletedUser); return { success: true }; } - - @Throttle({ - default: { - limit: 10, - ttl: 60, - }, - }) - @Mutation(() => Int) - async addToEarlyAccess( - @CurrentUser() currentUser: UserType, - @Args('email') email: string - ): Promise { - if (!this.feature.isStaff(currentUser.email)) { - throw new ForbiddenException('You are not allowed to do this'); - } - const user = await this.users.findUserByEmail(email); - if (user) { - return this.feature.addEarlyAccess(user.id); - } else { - const user = await this.auth.createAnonymousUser(email); - return this.feature.addEarlyAccess(user.id); - } - } - - @Throttle({ - default: { - limit: 10, - ttl: 60, - }, - }) - @Mutation(() => Int) - async removeEarlyAccess( - @CurrentUser() currentUser: UserType, - @Args('email') email: string - ): Promise { - if (!this.feature.isStaff(currentUser.email)) { - throw new ForbiddenException('You are not allowed to do this'); - } - const user = await this.users.findUserByEmail(email); - if (!user) { - throw new BadRequestException(`User ${email} not found`); - } - return this.feature.removeEarlyAccess(user.id); - } - - @Throttle({ - default: { - limit: 10, - ttl: 60, - }, - }) - @Query(() => [UserType]) - async earlyAccessUsers( - @Context() ctx: { isAdminQuery: boolean }, - @CurrentUser() user: UserType - ): Promise { - if (!this.feature.isStaff(user.email)) { - throw new ForbiddenException('You are not allowed to do this'); - } - // allow query other user's subscription - ctx.isAdminQuery = true; - return this.feature.listEarlyAccess(); - } } diff --git a/packages/backend/server/src/modules/users/types.ts b/packages/backend/server/src/modules/users/types.ts index 8e95409f09..0da51645af 100644 --- a/packages/backend/server/src/modules/users/types.ts +++ b/packages/backend/server/src/modules/users/types.ts @@ -1,4 +1,4 @@ -import { Field, Float, ID, ObjectType } from '@nestjs/graphql'; +import { createUnionType, Field, Float, ID, ObjectType } from '@nestjs/graphql'; import type { User } from '@prisma/client'; @ObjectType('UserQuotaHumanReadable') @@ -67,6 +67,29 @@ export class UserType implements Partial { hasPassword?: boolean; } +@ObjectType() +export class LimitedUserType implements Partial { + @Field({ description: 'User email' }) + email!: string; + + @Field(() => Boolean, { + description: 'User password has been set', + nullable: true, + }) + hasPassword?: boolean; +} + +export const UserOrLimitedUser = createUnionType({ + name: 'UserOrLimitedUser', + types: () => [UserType, LimitedUserType] as const, + resolveType(value) { + if (value.id) { + return UserType; + } + return LimitedUserType; + }, +}); + @ObjectType() export class DeleteAccount { @Field() diff --git a/packages/backend/server/src/modules/workspaces/controller.ts b/packages/backend/server/src/modules/workspaces/controller.ts index 47b64cf4aa..e38ed68071 100644 --- a/packages/backend/server/src/modules/workspaces/controller.ts +++ b/packages/backend/server/src/modules/workspaces/controller.ts @@ -1,9 +1,8 @@ -import type { Storage } from '@affine/storage'; import { Controller, ForbiddenException, Get, - Inject, + Logger, NotFoundException, Param, Res, @@ -12,18 +11,19 @@ import type { Response } from 'express'; import { CallTimer } from '../../metrics'; import { PrismaService } from '../../prisma'; -import { StorageProvide } from '../../storage'; import { DocID } from '../../utils/doc'; import { Auth, CurrentUser, Publicable } from '../auth'; import { DocHistoryManager, DocManager } from '../doc'; +import { WorkspaceBlobStorage } from '../storage'; import { UserType } from '../users'; import { PermissionService, PublicPageMode } from './permission'; import { Permission } from './types'; @Controller('/api/workspaces') export class WorkspacesController { + logger = new Logger(WorkspacesController.name); constructor( - @Inject(StorageProvide) private readonly storage: Storage, + private readonly storage: WorkspaceBlobStorage, private readonly permission: PermissionService, private readonly docManager: DocManager, private readonly historyManager: DocHistoryManager, @@ -40,19 +40,25 @@ export class WorkspacesController { @Param('name') name: string, @Res() res: Response ) { - const blob = await this.storage.getBlob(workspaceId, name); + const { body, metadata } = await this.storage.get(workspaceId, name); - if (!blob) { + if (!body) { throw new NotFoundException( `Blob not found in workspace ${workspaceId}: ${name}` ); } - res.setHeader('content-type', blob.contentType); - res.setHeader('last-modified', blob.lastModified); - res.setHeader('content-length', blob.size); + // metadata should always exists if body is not null + if (metadata) { + res.setHeader('content-type', metadata.contentType); + res.setHeader('last-modified', metadata.lastModified.toISOString()); + res.setHeader('content-length', metadata.contentLength); + } else { + this.logger.warn(`Blob ${workspaceId}/${name} has no metadata`); + } - res.send(blob.data); + res.setHeader('cache-control', 'public, max-age=31536000, immutable'); + body.pipe(res); } // get doc binary diff --git a/packages/backend/server/src/modules/workspaces/index.ts b/packages/backend/server/src/modules/workspaces/index.ts index 50259cd5d2..36a822e7b3 100644 --- a/packages/backend/server/src/modules/workspaces/index.ts +++ b/packages/backend/server/src/modules/workspaces/index.ts @@ -1,24 +1,34 @@ import { Module } from '@nestjs/common'; import { DocModule } from '../doc'; +import { FeatureModule } from '../features'; import { QuotaModule } from '../quota'; +import { StorageModule } from '../storage'; import { UsersService } from '../users'; import { WorkspacesController } from './controller'; -import { DocHistoryResolver } from './history.resolver'; +import { WorkspaceManagementResolver } from './management'; import { PermissionService } from './permission'; -import { PagePermissionResolver, WorkspaceResolver } from './resolver'; +import { + DocHistoryResolver, + PagePermissionResolver, + WorkspaceBlobResolver, + WorkspaceResolver, +} from './resolvers'; @Module({ - imports: [DocModule, QuotaModule], + imports: [DocModule, FeatureModule, QuotaModule, StorageModule], controllers: [WorkspacesController], providers: [ WorkspaceResolver, + WorkspaceManagementResolver, PermissionService, UsersService, PagePermissionResolver, DocHistoryResolver, + WorkspaceBlobResolver, ], exports: [PermissionService], }) export class WorkspaceModule {} -export { InvitationType, WorkspaceType } from './resolver'; + +export type { InvitationType, WorkspaceType } from './types'; diff --git a/packages/backend/server/src/modules/workspaces/management.ts b/packages/backend/server/src/modules/workspaces/management.ts new file mode 100644 index 0000000000..b5a74f94ac --- /dev/null +++ b/packages/backend/server/src/modules/workspaces/management.ts @@ -0,0 +1,136 @@ +import { ForbiddenException, UseGuards } from '@nestjs/common'; +import { + Args, + Int, + Mutation, + Parent, + Query, + ResolveField, + Resolver, +} from '@nestjs/graphql'; + +import { CloudThrottlerGuard, Throttle } from '../../throttler'; +import { Auth, CurrentUser } from '../auth'; +import { FeatureManagementService, FeatureType } from '../features'; +import { UserType } from '../users'; +import { PermissionService } from './permission'; +import { WorkspaceType } from './types'; + +@UseGuards(CloudThrottlerGuard) +@Auth() +@Resolver(() => WorkspaceType) +export class WorkspaceManagementResolver { + constructor( + private readonly feature: FeatureManagementService, + private readonly permission: PermissionService + ) {} + + @Throttle({ + default: { + limit: 10, + ttl: 60, + }, + }) + @Mutation(() => Int) + async addWorkspaceFeature( + @CurrentUser() currentUser: UserType, + @Args('workspaceId') workspaceId: string, + @Args('feature', { type: () => FeatureType }) feature: FeatureType + ): Promise { + if (!this.feature.isStaff(currentUser.email)) { + throw new ForbiddenException('You are not allowed to do this'); + } + + return this.feature.addWorkspaceFeatures(workspaceId, feature); + } + + @Throttle({ + default: { + limit: 10, + ttl: 60, + }, + }) + @Mutation(() => Int) + async removeWorkspaceFeature( + @CurrentUser() currentUser: UserType, + @Args('workspaceId') workspaceId: string, + @Args('feature', { type: () => FeatureType }) feature: FeatureType + ): Promise { + if (!this.feature.isStaff(currentUser.email)) { + throw new ForbiddenException('You are not allowed to do this'); + } + + return this.feature.removeWorkspaceFeature(workspaceId, feature); + } + + @Throttle({ + default: { + limit: 10, + ttl: 60, + }, + }) + @Query(() => [WorkspaceType]) + async listWorkspaceFeatures( + @CurrentUser() user: UserType, + @Args('feature', { type: () => FeatureType }) feature: FeatureType + ): Promise { + if (!this.feature.isStaff(user.email)) { + throw new ForbiddenException('You are not allowed to do this'); + } + + return this.feature.listFeatureWorkspaces(feature); + } + + @Mutation(() => Boolean) + async setWorkspaceExperimentalFeature( + @CurrentUser() user: UserType, + @Args('workspaceId') workspaceId: string, + @Args('feature', { type: () => FeatureType }) feature: FeatureType, + @Args('enable') enable: boolean + ): Promise { + if (!(await this.feature.canEarlyAccess(user.email))) { + throw new ForbiddenException('You are not allowed to do this'); + } + + const owner = await this.permission.getWorkspaceOwner(workspaceId); + const availableFeatures = await this.availableFeatures(user); + if (owner.user.id !== user.id || !availableFeatures.includes(feature)) { + throw new ForbiddenException('You are not allowed to do this'); + } + + if (enable) { + return await this.feature + .addWorkspaceFeatures( + workspaceId, + feature, + undefined, + 'add by experimental feature api' + ) + .then(id => id > 0); + } else { + return await this.feature.removeWorkspaceFeature(workspaceId, feature); + } + } + + @ResolveField(() => [FeatureType], { + description: 'Available features of workspace', + complexity: 2, + }) + async availableFeatures( + @CurrentUser() user: UserType + ): Promise { + if (await this.feature.canEarlyAccess(user.email)) { + return [FeatureType.Copilot]; + } else { + return []; + } + } + + @ResolveField(() => [FeatureType], { + description: 'Enabled features of workspace', + complexity: 2, + }) + async features(@Parent() workspace: WorkspaceType): Promise { + return this.feature.getWorkspaceFeatures(workspace.id); + } +} diff --git a/packages/backend/server/src/modules/workspaces/permission.ts b/packages/backend/server/src/modules/workspaces/permission.ts index 524ab37e39..1248075b1c 100644 --- a/packages/backend/server/src/modules/workspaces/permission.ts +++ b/packages/backend/server/src/modules/workspaces/permission.ts @@ -34,6 +34,9 @@ export class PermissionService { accepted: true, type: Permission.Owner, }, + select: { + workspaceId: true, + }, }) .then(data => data.map(({ workspaceId }) => workspaceId)); } diff --git a/packages/backend/server/src/modules/workspaces/resolvers/blob.ts b/packages/backend/server/src/modules/workspaces/resolvers/blob.ts new file mode 100644 index 0000000000..d2a03994ac --- /dev/null +++ b/packages/backend/server/src/modules/workspaces/resolvers/blob.ts @@ -0,0 +1,174 @@ +import { ForbiddenException, Logger, UseGuards } from '@nestjs/common'; +import { + Args, + Float, + Int, + Mutation, + Parent, + Query, + ResolveField, + Resolver, +} from '@nestjs/graphql'; +import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs'; + +import { MakeCache, PreventCache } from '../../../cache'; +import { CloudThrottlerGuard } from '../../../throttler'; +import type { FileUpload } from '../../../types'; +import { Auth, CurrentUser } from '../../auth'; +import { QuotaManagementService } from '../../quota'; +import { WorkspaceBlobStorage } from '../../storage'; +import { UserType } from '../../users'; +import { PermissionService } from '../permission'; +import { Permission, WorkspaceBlobSizes, WorkspaceType } from '../types'; + +@UseGuards(CloudThrottlerGuard) +@Auth() +@Resolver(() => WorkspaceType) +export class WorkspaceBlobResolver { + logger = new Logger(WorkspaceBlobResolver.name); + constructor( + private readonly permissions: PermissionService, + private readonly quota: QuotaManagementService, + private readonly storage: WorkspaceBlobStorage + ) {} + + @ResolveField(() => Int, { + description: 'Blobs size of workspace', + complexity: 2, + }) + async blobsSize(@Parent() workspace: WorkspaceType) { + return this.storage.totalSize(workspace.id); + } + + /** + * @deprecated use `workspace.blobs` instead + */ + @Query(() => [String], { + description: 'List blobs of workspace', + deprecationReason: 'use `workspace.blobs` instead', + }) + @MakeCache(['blobs'], ['workspaceId']) + async listBlobs( + @CurrentUser() user: UserType, + @Args('workspaceId') workspaceId: string + ) { + await this.permissions.checkWorkspace(workspaceId, user.id); + + return this.storage + .list(workspaceId) + .then(list => list.map(item => item.key)); + } + + /** + * @deprecated use `user.storageUsage` instead + */ + @Query(() => WorkspaceBlobSizes, { + deprecationReason: 'use `user.storageUsage` instead', + }) + async collectAllBlobSizes(@CurrentUser() user: UserType) { + const size = await this.quota.getUserUsage(user.id); + return { size }; + } + + /** + * @deprecated mutation `setBlob` will check blob limit & quota usage + */ + @Query(() => WorkspaceBlobSizes, { + deprecationReason: 'no more needed', + }) + async checkBlobSize( + @CurrentUser() user: UserType, + @Args('workspaceId') workspaceId: string, + @Args('size', { type: () => Float }) blobSize: number + ) { + const canWrite = await this.permissions.tryCheckWorkspace( + workspaceId, + user.id, + Permission.Write + ); + if (canWrite) { + const size = await this.quota.checkBlobQuota(workspaceId, blobSize); + return { size }; + } + return false; + } + + @Mutation(() => String) + @PreventCache(['blobs'], ['workspaceId']) + async setBlob( + @CurrentUser() user: UserType, + @Args('workspaceId') workspaceId: string, + @Args({ name: 'blob', type: () => GraphQLUpload }) + blob: FileUpload + ) { + await this.permissions.checkWorkspace( + workspaceId, + user.id, + Permission.Write + ); + + const { quota, size } = await this.quota.getWorkspaceUsage(workspaceId); + + const checkExceeded = (recvSize: number) => { + if (!quota) { + throw new ForbiddenException('cannot find user quota'); + } + if (size + recvSize > quota) { + this.logger.log( + `storage size limit exceeded: ${size + recvSize} > ${quota}` + ); + return true; + } else { + return false; + } + }; + + if (checkExceeded(0)) { + throw new ForbiddenException('storage size limit exceeded'); + } + const buffer = await new Promise((resolve, reject) => { + const stream = blob.createReadStream(); + const chunks: Uint8Array[] = []; + stream.on('data', chunk => { + chunks.push(chunk); + + // check size after receive each chunk to avoid unnecessary memory usage + const bufferSize = chunks.reduce((acc, cur) => acc + cur.length, 0); + if (checkExceeded(bufferSize)) { + reject(new ForbiddenException('storage size limit exceeded')); + } + }); + stream.on('error', reject); + stream.on('end', () => { + const buffer = Buffer.concat(chunks); + + if (checkExceeded(buffer.length)) { + reject(new ForbiddenException('storage size limit exceeded')); + } else { + resolve(buffer); + } + }); + }); + + if (!(await this.quota.checkBlobQuota(workspaceId, buffer.length))) { + throw new ForbiddenException('blob size limit exceeded'); + } + + await this.storage.put(workspaceId, blob.filename, buffer); + return blob.filename; + } + + @Mutation(() => Boolean) + @PreventCache(['blobs'], ['workspaceId']) + async deleteBlob( + @CurrentUser() user: UserType, + @Args('workspaceId') workspaceId: string, + @Args('hash') name: string + ) { + await this.permissions.checkWorkspace(workspaceId, user.id); + + await this.storage.delete(workspaceId, name); + + return true; + } +} diff --git a/packages/backend/server/src/modules/workspaces/history.resolver.ts b/packages/backend/server/src/modules/workspaces/resolvers/history.ts similarity index 82% rename from packages/backend/server/src/modules/workspaces/history.resolver.ts rename to packages/backend/server/src/modules/workspaces/resolvers/history.ts index ad9483a542..e7940f9df1 100644 --- a/packages/backend/server/src/modules/workspaces/history.resolver.ts +++ b/packages/backend/server/src/modules/workspaces/resolvers/history.ts @@ -1,3 +1,4 @@ +import { UseGuards } from '@nestjs/common'; import { Args, Field, @@ -11,13 +12,13 @@ import { } from '@nestjs/graphql'; import type { SnapshotHistory } from '@prisma/client'; -import { DocID } from '../../utils/doc'; -import { Auth, CurrentUser } from '../auth'; -import { DocHistoryManager } from '../doc/history'; -import { UserType } from '../users'; -import { PermissionService } from './permission'; -import { WorkspaceType } from './resolver'; -import { Permission } from './types'; +import { CloudThrottlerGuard } from '../../../throttler'; +import { DocID } from '../../../utils/doc'; +import { Auth, CurrentUser } from '../../auth'; +import { DocHistoryManager } from '../../doc/history'; +import { UserType } from '../../users'; +import { PermissionService } from '../permission'; +import { Permission, WorkspaceType } from '../types'; @ObjectType() class DocHistoryType implements Partial { @@ -31,6 +32,7 @@ class DocHistoryType implements Partial { timestamp!: Date; } +@UseGuards(CloudThrottlerGuard) @Resolver(() => WorkspaceType) export class DocHistoryResolver { constructor( diff --git a/packages/backend/server/src/modules/workspaces/resolvers/index.ts b/packages/backend/server/src/modules/workspaces/resolvers/index.ts new file mode 100644 index 0000000000..d4e282afd0 --- /dev/null +++ b/packages/backend/server/src/modules/workspaces/resolvers/index.ts @@ -0,0 +1,4 @@ +export * from './blob'; +export * from './history'; +export * from './page'; +export * from './workspace'; diff --git a/packages/backend/server/src/modules/workspaces/resolvers/page.ts b/packages/backend/server/src/modules/workspaces/resolvers/page.ts new file mode 100644 index 0000000000..c37bc4049c --- /dev/null +++ b/packages/backend/server/src/modules/workspaces/resolvers/page.ts @@ -0,0 +1,163 @@ +import { ForbiddenException, UseGuards } from '@nestjs/common'; +import { + Args, + Field, + Mutation, + ObjectType, + Parent, + registerEnumType, + ResolveField, + Resolver, +} from '@nestjs/graphql'; +import type { WorkspacePage as PrismaWorkspacePage } from '@prisma/client'; + +import { PrismaService } from '../../../prisma'; +import { CloudThrottlerGuard } from '../../../throttler'; +import { DocID } from '../../../utils/doc'; +import { Auth, CurrentUser } from '../../auth'; +import { UserType } from '../../users'; +import { PermissionService, PublicPageMode } from '../permission'; +import { Permission, WorkspaceType } from '../types'; + +registerEnumType(PublicPageMode, { + name: 'PublicPageMode', + description: 'The mode which the public page default in', +}); + +@ObjectType() +class WorkspacePage implements Partial { + @Field(() => String, { name: 'id' }) + pageId!: string; + + @Field() + workspaceId!: string; + + @Field(() => PublicPageMode) + mode!: PublicPageMode; + + @Field() + public!: boolean; +} + +@UseGuards(CloudThrottlerGuard) +@Auth() +@Resolver(() => WorkspaceType) +export class PagePermissionResolver { + constructor( + private readonly prisma: PrismaService, + private readonly permission: PermissionService + ) {} + + /** + * @deprecated + */ + @ResolveField(() => [String], { + description: 'Shared pages of workspace', + complexity: 2, + deprecationReason: 'use WorkspaceType.publicPages', + }) + async sharedPages(@Parent() workspace: WorkspaceType) { + const data = await this.prisma.workspacePage.findMany({ + where: { + workspaceId: workspace.id, + public: true, + }, + }); + + return data.map(row => row.pageId); + } + + @ResolveField(() => [WorkspacePage], { + description: 'Public pages of a workspace', + complexity: 2, + }) + async publicPages(@Parent() workspace: WorkspaceType) { + return this.prisma.workspacePage.findMany({ + where: { + workspaceId: workspace.id, + public: true, + }, + }); + } + + /** + * @deprecated + */ + @Mutation(() => Boolean, { + name: 'sharePage', + deprecationReason: 'renamed to publicPage', + }) + async deprecatedSharePage( + @CurrentUser() user: UserType, + @Args('workspaceId') workspaceId: string, + @Args('pageId') pageId: string + ) { + await this.publishPage(user, workspaceId, pageId, PublicPageMode.Page); + return true; + } + + @Mutation(() => WorkspacePage) + async publishPage( + @CurrentUser() user: UserType, + @Args('workspaceId') workspaceId: string, + @Args('pageId') pageId: string, + @Args({ + name: 'mode', + type: () => PublicPageMode, + nullable: true, + defaultValue: PublicPageMode.Page, + }) + mode: PublicPageMode + ) { + const docId = new DocID(pageId, workspaceId); + + if (docId.isWorkspace) { + throw new ForbiddenException('Expect page not to be workspace'); + } + + await this.permission.checkWorkspace( + docId.workspace, + user.id, + Permission.Read + ); + + return this.permission.publishPage(docId.workspace, docId.guid, mode); + } + + /** + * @deprecated + */ + @Mutation(() => Boolean, { + name: 'revokePage', + deprecationReason: 'use revokePublicPage', + }) + async deprecatedRevokePage( + @CurrentUser() user: UserType, + @Args('workspaceId') workspaceId: string, + @Args('pageId') pageId: string + ) { + await this.revokePublicPage(user, workspaceId, pageId); + return true; + } + + @Mutation(() => WorkspacePage) + async revokePublicPage( + @CurrentUser() user: UserType, + @Args('workspaceId') workspaceId: string, + @Args('pageId') pageId: string + ) { + const docId = new DocID(pageId, workspaceId); + + if (docId.isWorkspace) { + throw new ForbiddenException('Expect page not to be workspace'); + } + + await this.permission.checkWorkspace( + docId.workspace, + user.id, + Permission.Read + ); + + return this.permission.revokePublicPage(docId.workspace, docId.guid); + } +} diff --git a/packages/backend/server/src/modules/workspaces/resolver.ts b/packages/backend/server/src/modules/workspaces/resolvers/workspace.ts similarity index 57% rename from packages/backend/server/src/modules/workspaces/resolver.ts rename to packages/backend/server/src/modules/workspaces/resolvers/workspace.ts index 1880ea935a..ad3208b555 100644 --- a/packages/backend/server/src/modules/workspaces/resolver.ts +++ b/packages/backend/server/src/modules/workspaces/resolvers/workspace.ts @@ -1,7 +1,5 @@ -import type { Storage } from '@affine/storage'; import { ForbiddenException, - Inject, InternalServerErrorException, Logger, NotFoundException, @@ -9,127 +7,36 @@ import { } from '@nestjs/common'; import { Args, - Field, - Float, - ID, - InputType, Int, Mutation, - ObjectType, - OmitType, Parent, - PartialType, - PickType, Query, - registerEnumType, ResolveField, Resolver, } from '@nestjs/graphql'; -import type { - User, - Workspace, - WorkspacePage as PrismaWorkspacePage, -} from '@prisma/client'; +import type { User } from '@prisma/client'; +import { getStreamAsBuffer } from 'get-stream'; import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs'; import { applyUpdate, Doc } from 'yjs'; -import { MakeCache, PreventCache } from '../../cache'; -import { EventEmitter } from '../../event'; -import { PrismaService } from '../../prisma'; -import { StorageProvide } from '../../storage'; -import { CloudThrottlerGuard, Throttle } from '../../throttler'; -import type { FileUpload } from '../../types'; -import { DocID } from '../../utils/doc'; -import { Auth, CurrentUser, Public } from '../auth'; -import { MailService } from '../auth/mailer'; -import { AuthService } from '../auth/service'; -import { QuotaManagementService } from '../quota'; -import { UsersService, UserType } from '../users'; -import { PermissionService, PublicPageMode } from './permission'; -import { Permission } from './types'; -import { defaultWorkspaceAvatar } from './utils'; - -registerEnumType(Permission, { - name: 'Permission', - description: 'User permission in workspace', -}); - -@ObjectType() -export class InviteUserType extends OmitType( - PartialType(UserType), - ['id'], - ObjectType -) { - @Field(() => ID) - id!: string; - - @Field(() => Permission, { description: 'User permission in workspace' }) - permission!: Permission; - - @Field({ description: 'Invite id' }) - inviteId!: string; - - @Field({ description: 'User accepted' }) - accepted!: boolean; -} - -@ObjectType() -export class WorkspaceType implements Partial { - @Field(() => ID) - id!: string; - - @Field({ description: 'is Public workspace' }) - public!: boolean; - - @Field({ description: 'Workspace created date' }) - createdAt!: Date; - - @Field(() => [InviteUserType], { - description: 'Members of workspace', - }) - members!: InviteUserType[]; -} - -@ObjectType() -export class InvitationWorkspaceType { - @Field(() => ID) - id!: string; - - @Field({ description: 'Workspace name' }) - name!: string; - - @Field(() => String, { - // nullable: true, - description: 'Base64 encoded avatar', - }) - avatar!: string; -} - -@ObjectType() -export class WorkspaceBlobSizes { - @Field(() => Float) - size!: number; -} - -@ObjectType() -export class InvitationType { - @Field({ description: 'Workspace information' }) - workspace!: InvitationWorkspaceType; - @Field({ description: 'User information' }) - user!: UserType; - @Field({ description: 'Invitee information' }) - invitee!: UserType; -} - -@InputType() -export class UpdateWorkspaceInput extends PickType( - PartialType(WorkspaceType), - ['public'], - InputType -) { - @Field(() => ID) - id!: string; -} +import { EventEmitter } from '../../../event'; +import { PrismaService } from '../../../prisma'; +import { CloudThrottlerGuard, Throttle } from '../../../throttler'; +import type { FileUpload } from '../../../types'; +import { Auth, CurrentUser, Public } from '../../auth'; +import { MailService } from '../../auth/mailer'; +import { AuthService } from '../../auth/service'; +import { WorkspaceBlobStorage } from '../../storage'; +import { UsersService, UserType } from '../../users'; +import { PermissionService } from '../permission'; +import { + InvitationType, + InviteUserType, + Permission, + UpdateWorkspaceInput, + WorkspaceType, +} from '../types'; +import { defaultWorkspaceAvatar } from '../utils'; /** * Workspace resolver @@ -140,7 +47,7 @@ export class UpdateWorkspaceInput extends PickType( @Auth() @Resolver(() => WorkspaceType) export class WorkspaceResolver { - private readonly logger = new Logger('WorkspaceResolver'); + private readonly logger = new Logger(WorkspaceResolver.name); constructor( private readonly auth: AuthService, @@ -149,8 +56,7 @@ export class WorkspaceResolver { private readonly permissions: PermissionService, private readonly users: UsersService, private readonly event: EventEmitter, - private readonly quota: QuotaManagementService, - @Inject(StorageProvide) private readonly storage: Storage + private readonly blobStorage: WorkspaceBlobStorage ) {} @ResolveField(() => Permission, { @@ -235,14 +141,6 @@ export class WorkspaceResolver { })); } - @ResolveField(() => Int, { - description: 'Blobs size of workspace', - complexity: 2, - }) - async blobsSize(@Parent() workspace: WorkspaceType) { - return this.storage.blobsSize([workspace.id]); - } - @Query(() => Boolean, { description: 'Get is owner of workspace', complexity: 2, @@ -565,11 +463,14 @@ export class WorkspaceResolver { let avatar = ''; if (metaJSON.avatar) { - const avatarBlob = await this.storage.getBlob( + const avatarBlob = await this.blobStorage.get( workspaceId, metaJSON.avatar ); - avatar = avatarBlob?.data.toString('base64') || ''; + + if (avatarBlob.body) { + avatar = (await getStreamAsBuffer(avatarBlob.body)).toString('base64'); + } } return { @@ -653,256 +554,4 @@ export class WorkspaceResolver { return this.permissions.revokeWorkspace(workspaceId, user.id); } - - @Query(() => [String], { - description: 'List blobs of workspace', - }) - @MakeCache(['blobs'], ['workspaceId']) - async listBlobs( - @CurrentUser() user: UserType, - @Args('workspaceId') workspaceId: string - ) { - await this.permissions.checkWorkspace(workspaceId, user.id); - - return this.storage.listBlobs(workspaceId); - } - - @Query(() => WorkspaceBlobSizes) - async collectAllBlobSizes(@CurrentUser() user: UserType) { - const size = await this.quota.getUserUsage(user.id); - return { size }; - } - - @Query(() => WorkspaceBlobSizes) - async checkBlobSize( - @CurrentUser() user: UserType, - @Args('workspaceId') workspaceId: string, - @Args('size', { type: () => Float }) blobSize: number - ) { - const canWrite = await this.permissions.tryCheckWorkspace( - workspaceId, - user.id, - Permission.Write - ); - if (canWrite) { - const size = await this.quota.checkBlobQuota(workspaceId, blobSize); - return { size }; - } - return false; - } - - @Mutation(() => String) - @PreventCache(['blobs'], ['workspaceId']) - async setBlob( - @CurrentUser() user: UserType, - @Args('workspaceId') workspaceId: string, - @Args({ name: 'blob', type: () => GraphQLUpload }) - blob: FileUpload - ) { - await this.permissions.checkWorkspace( - workspaceId, - user.id, - Permission.Write - ); - - const { quota, size } = await this.quota.getWorkspaceUsage(workspaceId); - - const checkExceeded = (recvSize: number) => { - if (!quota) { - throw new ForbiddenException('cannot find user quota'); - } - if (size + recvSize > quota) { - this.logger.log( - `storage size limit exceeded: ${size + recvSize} > ${quota}` - ); - return true; - } else { - return false; - } - }; - - if (checkExceeded(0)) { - throw new ForbiddenException('storage size limit exceeded'); - } - const buffer = await new Promise((resolve, reject) => { - const stream = blob.createReadStream(); - const chunks: Uint8Array[] = []; - stream.on('data', chunk => { - chunks.push(chunk); - - // check size after receive each chunk to avoid unnecessary memory usage - const bufferSize = chunks.reduce((acc, cur) => acc + cur.length, 0); - if (checkExceeded(bufferSize)) { - reject(new ForbiddenException('storage size limit exceeded')); - } - }); - stream.on('error', reject); - stream.on('end', () => { - const buffer = Buffer.concat(chunks); - - if (checkExceeded(buffer.length)) { - reject(new ForbiddenException('storage size limit exceeded')); - } else { - resolve(buffer); - } - }); - }); - - return this.storage.uploadBlob(workspaceId, buffer); - } - - @Mutation(() => Boolean) - @PreventCache(['blobs'], ['workspaceId']) - async deleteBlob( - @CurrentUser() user: UserType, - @Args('workspaceId') workspaceId: string, - @Args('hash') hash: string - ) { - await this.permissions.checkWorkspace(workspaceId, user.id); - - return this.storage.deleteBlob(workspaceId, hash); - } -} - -registerEnumType(PublicPageMode, { - name: 'PublicPageMode', - description: 'The mode which the public page default in', -}); - -@ObjectType() -class WorkspacePage implements Partial { - @Field(() => String, { name: 'id' }) - pageId!: string; - - @Field() - workspaceId!: string; - - @Field(() => PublicPageMode) - mode!: PublicPageMode; - - @Field() - public!: boolean; -} - -@UseGuards(CloudThrottlerGuard) -@Auth() -@Resolver(() => WorkspaceType) -export class PagePermissionResolver { - constructor( - private readonly prisma: PrismaService, - private readonly permission: PermissionService - ) {} - - /** - * @deprecated - */ - @ResolveField(() => [String], { - description: 'Shared pages of workspace', - complexity: 2, - deprecationReason: 'use WorkspaceType.publicPages', - }) - async sharedPages(@Parent() workspace: WorkspaceType) { - const data = await this.prisma.workspacePage.findMany({ - where: { - workspaceId: workspace.id, - public: true, - }, - }); - - return data.map(row => row.pageId); - } - - @ResolveField(() => [WorkspacePage], { - description: 'Public pages of a workspace', - complexity: 2, - }) - async publicPages(@Parent() workspace: WorkspaceType) { - return this.prisma.workspacePage.findMany({ - where: { - workspaceId: workspace.id, - public: true, - }, - }); - } - - /** - * @deprecated - */ - @Mutation(() => Boolean, { - name: 'sharePage', - deprecationReason: 'renamed to publicPage', - }) - async deprecatedSharePage( - @CurrentUser() user: UserType, - @Args('workspaceId') workspaceId: string, - @Args('pageId') pageId: string - ) { - await this.publishPage(user, workspaceId, pageId, PublicPageMode.Page); - return true; - } - - @Mutation(() => WorkspacePage) - async publishPage( - @CurrentUser() user: UserType, - @Args('workspaceId') workspaceId: string, - @Args('pageId') pageId: string, - @Args({ - name: 'mode', - type: () => PublicPageMode, - nullable: true, - defaultValue: PublicPageMode.Page, - }) - mode: PublicPageMode - ) { - const docId = new DocID(pageId, workspaceId); - - if (docId.isWorkspace) { - throw new ForbiddenException('Expect page not to be workspace'); - } - - await this.permission.checkWorkspace( - docId.workspace, - user.id, - Permission.Read - ); - - return this.permission.publishPage(docId.workspace, docId.guid, mode); - } - - /** - * @deprecated - */ - @Mutation(() => Boolean, { - name: 'revokePage', - deprecationReason: 'use revokePublicPage', - }) - async deprecatedRevokePage( - @CurrentUser() user: UserType, - @Args('workspaceId') workspaceId: string, - @Args('pageId') pageId: string - ) { - await this.revokePublicPage(user, workspaceId, pageId); - return true; - } - - @Mutation(() => WorkspacePage) - async revokePublicPage( - @CurrentUser() user: UserType, - @Args('workspaceId') workspaceId: string, - @Args('pageId') pageId: string - ) { - const docId = new DocID(pageId, workspaceId); - - if (docId.isWorkspace) { - throw new ForbiddenException('Expect page not to be workspace'); - } - - await this.permission.checkWorkspace( - docId.workspace, - user.id, - Permission.Read - ); - - return this.permission.revokePublicPage(docId.workspace, docId.guid); - } } diff --git a/packages/backend/server/src/modules/workspaces/types.ts b/packages/backend/server/src/modules/workspaces/types.ts index a86fcb6c2b..c3492f4230 100644 --- a/packages/backend/server/src/modules/workspaces/types.ts +++ b/packages/backend/server/src/modules/workspaces/types.ts @@ -1,6 +1,103 @@ +import { + Field, + Float, + ID, + InputType, + ObjectType, + OmitType, + PartialType, + PickType, + registerEnumType, +} from '@nestjs/graphql'; +import type { Workspace } from '@prisma/client'; + +import { UserType } from '../users/types'; + export enum Permission { Read = 0, Write = 1, Admin = 10, Owner = 99, } + +registerEnumType(Permission, { + name: 'Permission', + description: 'User permission in workspace', +}); + +@ObjectType() +export class InviteUserType extends OmitType( + PartialType(UserType), + ['id'], + ObjectType +) { + @Field(() => ID) + id!: string; + + @Field(() => Permission, { description: 'User permission in workspace' }) + permission!: Permission; + + @Field({ description: 'Invite id' }) + inviteId!: string; + + @Field({ description: 'User accepted' }) + accepted!: boolean; +} + +@ObjectType() +export class WorkspaceType implements Partial { + @Field(() => ID) + id!: string; + + @Field({ description: 'is Public workspace' }) + public!: boolean; + + @Field({ description: 'Workspace created date' }) + createdAt!: Date; + + @Field(() => [InviteUserType], { + description: 'Members of workspace', + }) + members!: InviteUserType[]; +} + +@ObjectType() +export class InvitationWorkspaceType { + @Field(() => ID) + id!: string; + + @Field({ description: 'Workspace name' }) + name!: string; + + @Field(() => String, { + // nullable: true, + description: 'Base64 encoded avatar', + }) + avatar!: string; +} + +@ObjectType() +export class WorkspaceBlobSizes { + @Field(() => Float) + size!: number; +} + +@ObjectType() +export class InvitationType { + @Field({ description: 'Workspace information' }) + workspace!: InvitationWorkspaceType; + @Field({ description: 'User information' }) + user!: UserType; + @Field({ description: 'Invitee information' }) + invitee!: UserType; +} + +@InputType() +export class UpdateWorkspaceInput extends PickType( + PartialType(WorkspaceType), + ['public'], + InputType +) { + @Field(() => ID) + id!: string; +} diff --git a/packages/backend/server/src/prelude.ts b/packages/backend/server/src/prelude.ts index 0c0fae35fc..bac73ad323 100644 --- a/packages/backend/server/src/prelude.ts +++ b/packages/backend/server/src/prelude.ts @@ -1,9 +1,7 @@ import 'reflect-metadata'; import 'dotenv/config'; - -import { getDefaultAFFiNEConfig } from './config/default'; - -globalThis.AFFiNE = getDefaultAFFiNEConfig(); +import './affine'; +import './affine.config'; if (process.env.NODE_ENV === 'development') { console.log('AFFiNE Config:', globalThis.AFFiNE); diff --git a/packages/backend/server/src/schema.gql b/packages/backend/server/src/schema.gql index e7a348b317..949bea6851 100644 --- a/packages/backend/server/src/schema.gql +++ b/packages/backend/server/src/schema.gql @@ -72,6 +72,113 @@ type RemoveAvatar { success: Boolean! } +type InviteUserType { + """User name""" + name: String + + """User email""" + email: String + + """User avatar url""" + avatarUrl: String + + """User email verified""" + emailVerified: DateTime + + """User created date""" + createdAt: DateTime + + """User password has been set""" + hasPassword: Boolean + id: ID! + + """User permission in workspace""" + permission: Permission! + + """Invite id""" + inviteId: String! + + """User accepted""" + accepted: Boolean! +} + +"""User permission in workspace""" +enum Permission { + Read + Write + Admin + Owner +} + +type WorkspaceType { + id: ID! + + """is Public workspace""" + public: Boolean! + + """Workspace created date""" + createdAt: DateTime! + + """Members of workspace""" + members(skip: Int, take: Int): [InviteUserType!]! + + """Permission of current signed in user in workspace""" + permission: Permission! + + """member count of workspace""" + memberCount: Int! + + """Owner of workspace""" + owner: UserType! + + """Available features of workspace""" + availableFeatures: [FeatureType!]! + + """Enabled features of workspace""" + features: [FeatureType!]! + + """Shared pages of workspace""" + sharedPages: [String!]! @deprecated(reason: "use WorkspaceType.publicPages") + + """Public pages of a workspace""" + publicPages: [WorkspacePage!]! + histories(guid: String!, before: DateTime, take: Int): [DocHistoryType!]! + + """Blobs size of workspace""" + blobsSize: Int! +} + +"""The type of workspace feature""" +enum FeatureType { + Copilot + EarlyAccess +} + +type InvitationWorkspaceType { + id: ID! + + """Workspace name""" + name: String! + + """Base64 encoded avatar""" + avatar: String! +} + +type WorkspaceBlobSizes { + size: Float! +} + +type InvitationType { + """Workspace information""" + workspace: InvitationWorkspaceType! + + """User information""" + user: UserType! + + """Invitee information""" + invitee: UserType! +} + type TokenType { token: String! refresh: String! @@ -147,99 +254,10 @@ enum InvoiceStatus { Uncollectible } -type InviteUserType { - """User name""" - name: String - - """User email""" - email: String - - """User avatar url""" - avatarUrl: String - - """User email verified""" - emailVerified: DateTime - - """User created date""" - createdAt: DateTime - - """User password has been set""" - hasPassword: Boolean - id: ID! - - """User permission in workspace""" - permission: Permission! - - """Invite id""" - inviteId: String! - - """User accepted""" - accepted: Boolean! -} - -"""User permission in workspace""" -enum Permission { - Read - Write - Admin - Owner -} - -type WorkspaceType { - id: ID! - - """is Public workspace""" - public: Boolean! - - """Workspace created date""" - createdAt: DateTime! - - """Members of workspace""" - members(skip: Int, take: Int): [InviteUserType!]! - - """Permission of current signed in user in workspace""" - permission: Permission! - - """member count of workspace""" - memberCount: Int! - - """Owner of workspace""" - owner: UserType! - - """Blobs size of workspace""" - blobsSize: Int! - - """Shared pages of workspace""" - sharedPages: [String!]! @deprecated(reason: "use WorkspaceType.publicPages") - - """Public pages of a workspace""" - publicPages: [WorkspacePage!]! - histories(guid: String!, before: DateTime, take: Int): [DocHistoryType!]! -} - -type InvitationWorkspaceType { - id: ID! - - """Workspace name""" - name: String! - - """Base64 encoded avatar""" - avatar: String! -} - -type WorkspaceBlobSizes { - size: Float! -} - -type InvitationType { - """Workspace information""" - workspace: InvitationWorkspaceType! - - """User information""" - user: UserType! - - """Invitee information""" - invitee: UserType! +type DocHistoryType { + workspaceId: String! + id: String! + timestamp: DateTime! } type WorkspacePage { @@ -255,12 +273,6 @@ enum PublicPageMode { Edgeless } -type DocHistoryType { - workspaceId: String! - id: String! - timestamp: DateTime! -} - type Query { """server config""" serverConfig: ServerConfigType! @@ -279,21 +291,32 @@ type Query { """Update workspace""" getInviteInfo(inviteId: String!): InvitationType! + listWorkspaceFeatures(feature: FeatureType!): [WorkspaceType!]! """List blobs of workspace""" - listBlobs(workspaceId: String!): [String!]! - collectAllBlobSizes: WorkspaceBlobSizes! - checkBlobSize(workspaceId: String!, size: Float!): WorkspaceBlobSizes! + listBlobs(workspaceId: String!): [String!]! @deprecated(reason: "use `workspace.blobs` instead") + collectAllBlobSizes: WorkspaceBlobSizes! @deprecated(reason: "use `user.storageUsage` instead") + checkBlobSize(workspaceId: String!, size: Float!): WorkspaceBlobSizes! @deprecated(reason: "no more needed") """Get current user""" currentUser: UserType """Get user by email""" - user(email: String!): UserType + user(email: String!): UserOrLimitedUser earlyAccessUsers: [UserType!]! prices: [SubscriptionPrice!]! } +union UserOrLimitedUser = UserType | LimitedUserType + +type LimitedUserType { + """User email""" + email: String! + + """User password has been set""" + hasPassword: Boolean +} + type Mutation { signUp(name: String!, email: String!, password: String!): UserType! signIn(email: String!, password: String!): UserType! @@ -314,13 +337,16 @@ type Mutation { revoke(workspaceId: String!, userId: String!): Boolean! acceptInviteById(workspaceId: String!, inviteId: String!, sendAcceptMail: Boolean): Boolean! leaveWorkspace(workspaceId: String!, workspaceName: String!, sendLeaveMail: Boolean): Boolean! - setBlob(workspaceId: String!, blob: Upload!): String! - deleteBlob(workspaceId: String!, hash: String!): Boolean! + addWorkspaceFeature(workspaceId: String!, feature: FeatureType!): Int! + removeWorkspaceFeature(workspaceId: String!, feature: FeatureType!): Int! + setWorkspaceExperimentalFeature(workspaceId: String!, feature: FeatureType!, enable: Boolean!): Boolean! sharePage(workspaceId: String!, pageId: String!): Boolean! @deprecated(reason: "renamed to publicPage") publishPage(workspaceId: String!, pageId: String!, mode: PublicPageMode = Page): WorkspacePage! revokePage(workspaceId: String!, pageId: String!): Boolean! @deprecated(reason: "use revokePublicPage") revokePublicPage(workspaceId: String!, pageId: String!): WorkspacePage! recoverDoc(workspaceId: String!, guid: String!, timestamp: DateTime!): DateTime! + setBlob(workspaceId: String!, blob: Upload!): String! + deleteBlob(workspaceId: String!, hash: String!): Boolean! """Upload user avatar""" uploadAvatar(avatar: Upload!): UserType! diff --git a/packages/backend/server/src/storage/index.ts b/packages/backend/server/src/storage/index.ts index 59299e27ca..ad121b5ad8 100644 --- a/packages/backend/server/src/storage/index.ts +++ b/packages/backend/server/src/storage/index.ts @@ -1,9 +1,5 @@ import { createRequire } from 'node:module'; -import { type DynamicModule, type FactoryProvider } from '@nestjs/common'; - -import { Config } from '../config'; - export const StorageProvide = Symbol('Storage'); let storageModule: typeof import('@affine/storage'); @@ -17,24 +13,7 @@ try { : require('../../storage.node'); } -export class StorageModule { - static forRoot(): DynamicModule { - const storageProvider: FactoryProvider = { - provide: StorageProvide, - useFactory: async (config: Config) => { - return storageModule.Storage.connect(config.db.url); - }, - inject: [Config], - }; - - return { - global: true, - module: StorageModule, - providers: [storageProvider], - exports: [storageProvider], - }; - } -} +export { storageModule as OctoBaseStorageModule }; export const mergeUpdatesInApplyWay = storageModule.mergeUpdatesInApplyWay; diff --git a/packages/backend/server/tests/app.e2e.ts b/packages/backend/server/tests/app.e2e.ts index 1578fe5f87..175f58b597 100644 --- a/packages/backend/server/tests/app.e2e.ts +++ b/packages/backend/server/tests/app.e2e.ts @@ -46,13 +46,6 @@ class FakePrisma { }, }; } - get newFeaturesWaitingList() { - return { - async findUnique() { - return null; - }, - }; - } } test.beforeEach(async t => { @@ -121,8 +114,12 @@ test('should find default user', async t => { query: ` query { user(email: "alex.yang@example.org") { - email - avatarUrl + ... on UserType { + email + } + ... on LimitedUserType { + email + } } } `, diff --git a/packages/backend/server/tests/doc.spec.ts b/packages/backend/server/tests/doc.spec.ts index f2a8e5ac7e..e004d697ec 100644 --- a/packages/backend/server/tests/doc.spec.ts +++ b/packages/backend/server/tests/doc.spec.ts @@ -22,8 +22,9 @@ import { import { EventModule } from '../src/event'; import { DocManager, DocModule } from '../src/modules/doc'; import { QuotaModule } from '../src/modules/quota'; +import { StorageModule } from '../src/modules/storage'; import { PrismaModule, PrismaService } from '../src/prisma'; -import { FakeStorageModule, flushDB } from './utils'; +import { flushDB } from './utils'; const createModule = () => { return Test.createTestingModule({ @@ -32,7 +33,7 @@ const createModule = () => { CacheModule, EventModule, QuotaModule, - FakeStorageModule.forRoot(), + StorageModule, ConfigModule.forRoot(), DocModule, RevertCommand, diff --git a/packages/backend/server/tests/feature.spec.ts b/packages/backend/server/tests/feature.spec.ts index ecc73db85c..50896d21d1 100644 --- a/packages/backend/server/tests/feature.spec.ts +++ b/packages/backend/server/tests/feature.spec.ts @@ -1,5 +1,6 @@ /// +import { Injectable } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { PrismaClient } from '@prisma/client'; import ava, { type TestFn } from 'ava'; @@ -14,14 +15,43 @@ import { FeatureService, FeatureType, } from '../src/modules/features'; -import { PrismaModule } from '../src/prisma'; +import { UserType } from '../src/modules/users/types'; +import { WorkspaceResolver } from '../src/modules/workspaces/resolvers'; +import { Permission } from '../src/modules/workspaces/types'; +import { PrismaModule, PrismaService } from '../src/prisma'; import { RateLimiterModule } from '../src/throttler'; import { initFeatureConfigs } from './utils'; +@Injectable() +class WorkspaceResolverMock { + constructor(private readonly prisma: PrismaService) {} + + async createWorkspace(user: UserType, _init: null) { + const workspace = await this.prisma.workspace.create({ + data: { + public: false, + permissions: { + create: { + type: Permission.Owner, + user: { + connect: { + id: user.id, + }, + }, + accepted: true, + }, + }, + }, + }); + return workspace; + } +} + const test = ava as TestFn<{ auth: AuthService; feature: FeatureService; - early_access: FeatureManagementService; + workspace: WorkspaceResolver; + management: FeatureManagementService; app: TestingModule; }>; @@ -30,6 +60,7 @@ test.beforeEach(async () => { const client = new PrismaClient(); await client.$connect(); await client.user.deleteMany({}); + await client.workspace.deleteMany({}); await client.$disconnect(); }); @@ -55,12 +86,17 @@ test.beforeEach(async t => { RevertCommand, RunCommand, ], - }).compile(); + providers: [WorkspaceResolver], + }) + .overrideProvider(WorkspaceResolver) + .useClass(WorkspaceResolverMock) + .compile(); t.context.app = module; t.context.auth = module.get(AuthService); t.context.feature = module.get(FeatureService); - t.context.early_access = module.get(FeatureManagementService); + t.context.workspace = module.get(WorkspaceResolver); + t.context.management = module.get(FeatureManagementService); // init features await initFeatureConfigs(module); @@ -70,7 +106,7 @@ test.afterEach.always(async t => { await t.context.app.close(); }); -test('should be able to set feature', async t => { +test('should be able to set user feature', async t => { const { auth, feature } = t.context; const u1 = await auth.signUp('DarkSky', 'darksky@example.org', '123456'); @@ -78,7 +114,7 @@ test('should be able to set feature', async t => { const f1 = await feature.getUserFeatures(u1.id); t.is(f1.length, 0, 'should be empty'); - await feature.addUserFeature(u1.id, FeatureType.EarlyAccess, 1, 'test'); + await feature.addUserFeature(u1.id, FeatureType.EarlyAccess, 2, 'test'); const f2 = await feature.getUserFeatures(u1.id); t.is(f2.length, 1, 'should have 1 feature'); @@ -86,14 +122,14 @@ test('should be able to set feature', async t => { }); test('should be able to check early access', async t => { - const { auth, feature, early_access } = t.context; + const { auth, feature, management } = t.context; const u1 = await auth.signUp('DarkSky', 'darksky@example.org', '123456'); - const f1 = await early_access.canEarlyAccess(u1.email); + const f1 = await management.canEarlyAccess(u1.email); t.false(f1, 'should not have early access'); - await early_access.addEarlyAccess(u1.id); - const f2 = await early_access.canEarlyAccess(u1.email); + await management.addEarlyAccess(u1.id); + const f2 = await management.canEarlyAccess(u1.email); t.true(f2, 'should have early access'); const f3 = await feature.listFeatureUsers(FeatureType.EarlyAccess); @@ -101,24 +137,24 @@ test('should be able to check early access', async t => { t.is(f3[0].id, u1.id, 'should be the same user'); }); -test('should be able revert quota', async t => { - const { auth, feature, early_access } = t.context; +test('should be able revert user feature', async t => { + const { auth, feature, management } = t.context; const u1 = await auth.signUp('DarkSky', 'darksky@example.org', '123456'); - const f1 = await early_access.canEarlyAccess(u1.email); + const f1 = await management.canEarlyAccess(u1.email); t.false(f1, 'should not have early access'); - await early_access.addEarlyAccess(u1.id); - const f2 = await early_access.canEarlyAccess(u1.email); + await management.addEarlyAccess(u1.id); + const f2 = await management.canEarlyAccess(u1.email); t.true(f2, 'should have early access'); - const q1 = await early_access.listEarlyAccess(); + const q1 = await management.listEarlyAccess(); t.is(q1.length, 1, 'should have 1 user'); t.is(q1[0].id, u1.id, 'should be the same user'); - await early_access.removeEarlyAccess(u1.id); - const f3 = await early_access.canEarlyAccess(u1.email); + await management.removeEarlyAccess(u1.id); + const f3 = await management.canEarlyAccess(u1.email); t.false(f3, 'should not have early access'); - const q2 = await early_access.listEarlyAccess(); + const q2 = await management.listEarlyAccess(); t.is(q2.length, 0, 'should have no user'); const q3 = await feature.getUserFeatures(u1.id); @@ -127,17 +163,72 @@ test('should be able revert quota', async t => { t.is(q3[0].activated, false, 'should be deactivated'); }); -test('should be same instance after reset the feature', async t => { - const { auth, feature, early_access } = t.context; +test('should be same instance after reset the user feature', async t => { + const { auth, feature, management } = t.context; const u1 = await auth.signUp('DarkSky', 'darksky@example.org', '123456'); - await early_access.addEarlyAccess(u1.id); + await management.addEarlyAccess(u1.id); const f1 = (await feature.getUserFeatures(u1.id))[0]; - await early_access.removeEarlyAccess(u1.id); + await management.removeEarlyAccess(u1.id); - await early_access.addEarlyAccess(u1.id); + await management.addEarlyAccess(u1.id); const f2 = (await feature.getUserFeatures(u1.id))[1]; t.is(f1.feature, f2.feature, 'should be same instance'); }); + +test('should be able to set workspace feature', async t => { + const { auth, feature, workspace } = t.context; + + const u1 = await auth.signUp('DarkSky', 'darksky@example.org', '123456'); + const w1 = await workspace.createWorkspace(u1, null); + + const f1 = await feature.getWorkspaceFeatures(w1.id); + t.is(f1.length, 0, 'should be empty'); + + await feature.addWorkspaceFeature(w1.id, FeatureType.Copilot, 1, 'test'); + + const f2 = await feature.getWorkspaceFeatures(w1.id); + t.is(f2.length, 1, 'should have 1 feature'); + t.is(f2[0].feature.name, FeatureType.Copilot, 'should be copilot'); +}); + +test('should be able to check workspace feature', async t => { + const { auth, feature, workspace, management } = t.context; + const u1 = await auth.signUp('DarkSky', 'darksky@example.org', '123456'); + const w1 = await workspace.createWorkspace(u1, null); + + const f1 = await management.hasWorkspaceFeature(w1.id, FeatureType.Copilot); + t.false(f1, 'should not have copilot'); + + await management.addWorkspaceFeatures(w1.id, FeatureType.Copilot, 1, 'test'); + const f2 = await management.hasWorkspaceFeature(w1.id, FeatureType.Copilot); + t.true(f2, 'should have copilot'); + + const f3 = await feature.listFeatureWorkspaces(FeatureType.Copilot); + t.is(f3.length, 1, 'should have 1 workspace'); + t.is(f3[0].id, w1.id, 'should be the same workspace'); +}); + +test('should be able revert workspace feature', async t => { + const { auth, feature, workspace, management } = t.context; + const u1 = await auth.signUp('DarkSky', 'darksky@example.org', '123456'); + const w1 = await workspace.createWorkspace(u1, null); + + const f1 = await management.hasWorkspaceFeature(w1.id, FeatureType.Copilot); + t.false(f1, 'should not have feature'); + + await management.addWorkspaceFeatures(w1.id, FeatureType.Copilot, 1, 'test'); + const f2 = await management.hasWorkspaceFeature(w1.id, FeatureType.Copilot); + t.true(f2, 'should have feature'); + + await management.removeWorkspaceFeature(w1.id, FeatureType.Copilot); + const f3 = await management.hasWorkspaceFeature(w1.id, FeatureType.Copilot); + t.false(f3, 'should not have feature'); + + const q3 = await feature.getWorkspaceFeatures(w1.id); + t.is(q3.length, 1, 'should have 1 feature'); + t.is(q3[0].feature.name, FeatureType.Copilot, 'should be copilot'); + t.is(q3[0].activated, false, 'should be deactivated'); +}); diff --git a/packages/backend/server/tests/history.spec.ts b/packages/backend/server/tests/history.spec.ts index d496816461..050f30de56 100644 --- a/packages/backend/server/tests/history.spec.ts +++ b/packages/backend/server/tests/history.spec.ts @@ -6,11 +6,12 @@ import test from 'ava'; import * as Sinon from 'sinon'; import { ConfigModule } from '../src/config'; -import type { EventPayload } from '../src/event'; +import { EventModule, type EventPayload } from '../src/event'; import { DocHistoryManager } from '../src/modules/doc'; import { QuotaModule } from '../src/modules/quota'; +import { StorageModule } from '../src/modules/storage'; import { PrismaModule, PrismaService } from '../src/prisma'; -import { FakeStorageModule, flushDB } from './utils'; +import { flushDB } from './utils'; let app: INestApplication; let m: TestingModule; @@ -24,7 +25,8 @@ test.beforeEach(async () => { imports: [ PrismaModule, QuotaModule, - FakeStorageModule.forRoot(), + EventModule, + StorageModule, ScheduleModule.forRoot(), ConfigModule.forRoot(), ], diff --git a/packages/backend/server/tests/quota.spec.ts b/packages/backend/server/tests/quota.spec.ts index f9d974929e..d819c21752 100644 --- a/packages/backend/server/tests/quota.spec.ts +++ b/packages/backend/server/tests/quota.spec.ts @@ -6,6 +6,7 @@ import ava, { type TestFn } from 'ava'; import { ConfigModule } from '../src/config'; import { RevertCommand, RunCommand } from '../src/data/commands/run'; +import { EventModule } from '../src/event'; import { AuthModule } from '../src/modules/auth'; import { AuthService } from '../src/modules/auth/service'; import { @@ -15,9 +16,10 @@ import { QuotaService, QuotaType, } from '../src/modules/quota'; +import { StorageModule } from '../src/modules/storage'; import { PrismaModule } from '../src/prisma'; import { RateLimiterModule } from '../src/throttler'; -import { FakeStorageModule, initFeatureConfigs } from './utils'; +import { initFeatureConfigs } from './utils'; const test = ava as TestFn<{ auth: AuthService; @@ -48,8 +50,9 @@ test.beforeEach(async t => { }), PrismaModule, AuthModule, + EventModule, QuotaModule, - FakeStorageModule.forRoot(), + StorageModule, RateLimiterModule, RevertCommand, RunCommand, diff --git a/packages/backend/server/tests/utils/blobs.ts b/packages/backend/server/tests/utils/blobs.ts index c026a2922f..5d13797634 100644 --- a/packages/backend/server/tests/utils/blobs.ts +++ b/packages/backend/server/tests/utils/blobs.ts @@ -106,7 +106,11 @@ export async function setBlob( }) ) .field('map', JSON.stringify({ '0': ['variables.blob'] })) - .attach('0', buffer, 'blob.data') + .attach( + '0', + buffer, + `blob-${Math.random().toString(16).substring(2, 10)}.data` + ) .expect(200); return res.body.data.setBlob; } diff --git a/packages/backend/server/tests/utils/utils.ts b/packages/backend/server/tests/utils/utils.ts index 0274e63daf..042f718c93 100644 --- a/packages/backend/server/tests/utils/utils.ts +++ b/packages/backend/server/tests/utils/utils.ts @@ -1,12 +1,10 @@ import { randomUUID } from 'node:crypto'; -import type { DynamicModule, FactoryProvider } from '@nestjs/common'; import { TestingModule } from '@nestjs/testing'; import { hashSync } from '@node-rs/argon2'; import { PrismaClient, type User } from '@prisma/client'; import { RevertCommand, RunCommand } from '../../src/data/commands/run'; -import { StorageProvide } from '../../src/storage'; export async function flushDB() { const client = new PrismaClient(); @@ -56,24 +54,6 @@ export class FakePrisma { } } -export class FakeStorageModule { - static forRoot(): DynamicModule { - const storageProvider: FactoryProvider = { - provide: StorageProvide, - useFactory: async () => { - return null; - }, - }; - - return { - global: true, - module: FakeStorageModule, - providers: [storageProvider], - exports: [storageProvider], - }; - } -} - export async function initFeatureConfigs(module: TestingModule) { const run = module.get(RunCommand); const revert = module.get(RevertCommand); diff --git a/packages/backend/server/tests/workspace-blobs.spec.ts b/packages/backend/server/tests/workspace-blobs.spec.ts index af0f683adc..ed86723e8a 100644 --- a/packages/backend/server/tests/workspace-blobs.spec.ts +++ b/packages/backend/server/tests/workspace-blobs.spec.ts @@ -99,8 +99,8 @@ test('should list blobs', async t => { const ret = await listBlobs(app, u1.token.token, workspace.id); t.is(ret.length, 2, 'failed to list blobs'); - t.is(ret[0], hash1, 'failed to list blobs'); - t.is(ret[1], hash2, 'failed to list blobs'); + // list blob result is not ordered + t.deepEqual(ret.sort(), [hash1, hash2].sort()); }); test('should calc blobs size', async t => { @@ -189,3 +189,13 @@ test('should be able calc quota after switch plan', async t => { ); t.is(size2, 0, 'failed to check pro plan blob size'); }); + +test('should reject blob exceeded limit', t => { + // TODO + t.true(true); +}); + +test('should reject blob exceeded quota', t => { + // TODO + t.true(true); +}); diff --git a/packages/backend/server/tests/workspace-usage.spec.ts b/packages/backend/server/tests/workspace-usage.spec.ts deleted file mode 100644 index ad5623b393..0000000000 --- a/packages/backend/server/tests/workspace-usage.spec.ts +++ /dev/null @@ -1,111 +0,0 @@ -import type { INestApplication } from '@nestjs/common'; -import { Test } from '@nestjs/testing'; -import ava, { type TestFn } from 'ava'; -import { stub } from 'sinon'; - -import { AppModule } from '../src/app'; -import { FeatureManagementService } from '../src/modules/features'; -import { Quotas } from '../src/modules/quota'; -import { UsersService } from '../src/modules/users'; -import { PermissionService } from '../src/modules/workspaces/permission'; -import { WorkspaceResolver } from '../src/modules/workspaces/resolver'; -import { PrismaService } from '../src/prisma'; -import { StorageProvide } from '../src/storage'; -import { FakePrisma } from './utils'; - -class FakePermission { - async tryCheckWorkspace() { - return true; - } - async getWorkspaceOwner() { - return { - user: new FakePrisma().fakeUser, - }; - } - async getOwnedWorkspaces() { - return ['']; - } -} - -const fakeUserService = { - getStorageQuotaById: stub(), -}; - -const test = ava as TestFn<{ - app: INestApplication; - resolver: WorkspaceResolver; -}>; - -test.beforeEach(async t => { - const module = await Test.createTestingModule({ - imports: [AppModule], - }) - .overrideProvider(PrismaService) - .useValue({ - workspaceUserPermission: { - async findMany() { - return []; - }, - }, - userFeatures: { - async count() { - return 1; - }, - async findFirst() { - return { - createdAt: new Date(), - expiredAt: new Date(), - reason: '', - feature: Quotas[0], - }; - }, - }, - features: { - async findFirst() { - return { - id: 0, - feature: 'free_plan_v1', - version: 1, - type: 1, - configs: { - name: 'Free', - blobLimit: 1, - storageQuota: 1, - historyPeriod: 1, - memberLimit: 3, - }, - }; - }, - }, - }) - .overrideProvider(PermissionService) - .useClass(FakePermission) - .overrideProvider(UsersService) - .useValue(fakeUserService) - .overrideProvider(StorageProvide) - .useValue({ - blobsSize() { - return 1024 * 10; - }, - }) - .overrideProvider(FeatureManagementService) - .useValue({}) - .compile(); - t.context.app = module.createNestApplication(); - t.context.resolver = t.context.app.get(WorkspaceResolver); - await t.context.app.init(); -}); - -test.afterEach.always(async t => { - await t.context.app.close(); -}); - -test('should get blob size limit', async t => { - const { resolver } = t.context; - fakeUserService.getStorageQuotaById.resolves(100 * 1024 * 1024 * 1024); - const res = await resolver.checkBlobSize(new FakePrisma().fakeUser, '', 100); - t.not(res, false); - // @ts-expect-error - t.is(typeof res.size, 'number'); - fakeUserService.getStorageQuotaById.reset(); -}); diff --git a/packages/backend/storage/package.json b/packages/backend/storage/package.json index e476113ebe..0159dcd8b7 100644 --- a/packages/backend/storage/package.json +++ b/packages/backend/storage/package.json @@ -35,7 +35,7 @@ "version": "napi version" }, "devDependencies": { - "@napi-rs/cli": "3.0.0-alpha.15", + "@napi-rs/cli": "3.0.0-alpha.33", "lib0": "^0.2.87", "nx": "^17.1.3", "nx-cloud": "^16.5.2", diff --git a/packages/common/cmdk/package.json b/packages/common/cmdk/package.json index e1defb7226..b7017dcd7e 100644 --- a/packages/common/cmdk/package.json +++ b/packages/common/cmdk/package.json @@ -8,5 +8,8 @@ "react": "18.2.0", "react-dom": "18.2.0" }, + "dependencies": { + "@radix-ui/react-dialog": "^1.0.5" + }, "version": "0.11.0" } diff --git a/packages/common/debug/package.json b/packages/common/debug/package.json index e8b4fb4f7e..8901a5e91e 100644 --- a/packages/common/debug/package.json +++ b/packages/common/debug/package.json @@ -7,7 +7,7 @@ }, "devDependencies": { "@types/debug": "^4.1.9", - "vitest": "1.0.4" + "vitest": "1.1.3" }, "version": "0.11.0" } diff --git a/packages/common/env/package.json b/packages/common/env/package.json index 46f924bb20..10ce0c5c7d 100644 --- a/packages/common/env/package.json +++ b/packages/common/env/package.json @@ -3,12 +3,11 @@ "private": true, "type": "module", "devDependencies": { - "@blocksuite/global": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/store": "0.11.0-nightly-202312220916-e3abcbb", + "@blocksuite/global": "0.11.0-nightly-202401020419-752a5b8", + "@blocksuite/store": "0.11.0-nightly-202401020419-752a5b8", "react": "18.2.0", "react-dom": "18.2.0", - "vitest": "1.0.4", - "zod": "^3.22.4" + "vitest": "1.1.3" }, "exports": { "./automation": "./src/automation.ts", @@ -21,10 +20,11 @@ }, "peerDependencies": { "@affine/templates": "workspace:*", - "@blocksuite/global": "0.0.0-20230409084303-221991d4-nightly" + "@blocksuite/global": "0.11.0-nightly-202401020419-752a5b8" }, "dependencies": { - "lit": "^3.0.2" + "lit": "^3.0.2", + "zod": "^3.22.4" }, "version": "0.11.0" } diff --git a/packages/common/env/src/global.ts b/packages/common/env/src/global.ts index 8709d98008..7e9527be4e 100644 --- a/packages/common/env/src/global.ts +++ b/packages/common/env/src/global.ts @@ -12,8 +12,6 @@ export const blockSuiteFeatureFlags = z.object({ }); export const runtimeFlagsSchema = z.object({ - enablePlugin: z.boolean(), - builtinPlugins: z.array(z.string()), enableTestProperties: z.boolean(), enableBroadcastChannelProvider: z.boolean(), enableDebugPage: z.boolean(), diff --git a/packages/common/env/src/workspace.ts b/packages/common/env/src/workspace.ts index 9f99f5c077..9381241e95 100644 --- a/packages/common/env/src/workspace.ts +++ b/packages/common/env/src/workspace.ts @@ -1,5 +1,3 @@ -import type { PropsWithChildren, ReactNode } from 'react'; - export enum WorkspaceSubPath { ALL = 'all', Collection = 'collection', @@ -8,18 +6,6 @@ export enum WorkspaceSubPath { SHARED = 'shared', } -export enum ReleaseType { - // if workspace is not released yet, we will not show it in the workspace list - UNRELEASED = 'unreleased', - STABLE = 'stable', -} - -export enum LoadPriority { - HIGH = 1, - MEDIUM = 2, - LOW = 3, -} - export enum WorkspaceFlavour { /** * New AFFiNE Cloud Workspace using Nest.js Server. @@ -27,41 +13,3 @@ export enum WorkspaceFlavour { AFFINE_CLOUD = 'affine-cloud', LOCAL = 'local', } - -export const settingPanel = { - General: 'general', - Collaboration: 'collaboration', - Publish: 'publish', - Export: 'export', - Sync: 'sync', -} as const; -export const settingPanelValues = Object.values(settingPanel); -export type SettingPanel = (typeof settingPanel)[keyof typeof settingPanel]; - -export type WorkspaceHeaderProps = { - rightSlot?: ReactNode; - currentEntry: - | { - subPath: WorkspaceSubPath; - } - | { - pageId: string; - }; -}; - -interface FC

{ - (props: P): ReactNode; -} - -export interface WorkspaceUISchema { - Provider: FC; - LoginCard?: FC; -} - -export interface WorkspaceAdapter { - releaseType: ReleaseType; - flavour: Flavour; - // The Adapter will be loaded according to the priority - loadPriority: LoadPriority; - UI: WorkspaceUISchema; -} diff --git a/packages/common/infra/package.json b/packages/common/infra/package.json index 982672f299..a3dfb401ba 100644 --- a/packages/common/infra/package.json +++ b/packages/common/infra/package.json @@ -1,90 +1,39 @@ { "name": "@toeverything/infra", "type": "module", - "module": "./dist/index.js", - "main": "./dist/index.cjs", - "types": "./dist/src/index.d.ts", + "private": true, "exports": { - ".": { - "types": "./dist/src/index.d.ts", - "import": "./dist/index.js", - "require": "./dist/index.cjs" - }, - "./blocksuite": { - "types": "./dist/src/blocksuite/index.d.ts", - "import": "./dist/blocksuite.js", - "require": "./dist/blocksuite.cjs" - }, - "./command": { - "types": "./dist/src/command/index.d.ts", - "import": "./dist/command.js", - "require": "./dist/command.cjs" - }, - "./core/*": { - "types": "./dist/src/core/*.d.ts", - "import": "./dist/core/*.js", - "require": "./dist/core/*.cjs" - }, - "./preload/*": { - "types": "./dist/src/preload/*.d.ts", - "import": "./dist/preload/*.js", - "require": "./dist/preload/*.cjs" - }, - "./atom": { - "type": "./dist/src/atom.d.ts", - "import": "./dist/atom.js", - "require": "./dist/atom.cjs" - }, - "./type": { - "type": "./dist/src/type.d.ts", - "import": "./dist/type.js", - "require": "./dist/type.cjs" - }, - "./app-config-storage": { - "type": "./dist/src/app-config-storage.d.ts", - "import": "./dist/app-config-storage.js", - "require": "./dist/app-config-storage.cjs" - }, - "./__internal__/*": { - "type": "./dist/src/__internal__/*.d.ts", - "import": "./dist/__internal__/*.js", - "require": "./dist/__internal__/*.cjs" - } - }, - "files": [ - "dist" - ], - "scripts": { - "build": "vite build", - "dev": "vite build --watch" + "./blocksuite": "./src/blocksuite/index.ts", + "./command": "./src/command/index.ts", + "./atom": "./src/atom/index.ts", + "./app-config-storage": "./src/app-config-storage.ts", + ".": "./src/index.ts" }, "dependencies": { "@affine/debug": "workspace:*", "@affine/env": "workspace:*", - "@affine/sdk": "workspace:*", - "@blocksuite/blocks": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/global": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/store": "0.11.0-nightly-202312220916-e3abcbb", + "@affine/templates": "workspace:*", + "@blocksuite/blocks": "0.11.0-nightly-202401020419-752a5b8", + "@blocksuite/global": "0.11.0-nightly-202401020419-752a5b8", + "@blocksuite/store": "0.11.0-nightly-202401020419-752a5b8", "jotai": "^2.5.1", "jotai-effect": "^0.2.3", + "nanoid": "^5.0.3", "tinykeys": "^2.1.0", + "yjs": "^13.6.10", "zod": "^3.22.4" }, "devDependencies": { "@affine-test/fixtures": "workspace:*", "@affine/templates": "workspace:*", - "@blocksuite/lit": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/presets": "0.11.0-nightly-202312220916-e3abcbb", - "@testing-library/react": "^14.0.0", + "@blocksuite/lit": "0.11.0-nightly-202401020419-752a5b8", + "@blocksuite/presets": "0.11.0-nightly-202401020419-752a5b8", "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": "^5.0.6", - "vite-plugin-dts": "3.6.0", - "vitest": "1.0.4", - "yjs": "^13.6.10" + "vite-plugin-dts": "3.7.0", + "vitest": "1.1.3" }, "peerDependencies": { "@affine/templates": "*", diff --git a/packages/common/infra/preload/electron.d.ts b/packages/common/infra/preload/electron.d.ts deleted file mode 100644 index 2910e840a5..0000000000 --- a/packages/common/infra/preload/electron.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -/* eslint-disable */ -// @ts-ignore -export * from '../dist/src/preload/electron'; diff --git a/packages/common/infra/preload/electron.js b/packages/common/infra/preload/electron.js deleted file mode 100644 index a83b2722bf..0000000000 --- a/packages/common/infra/preload/electron.js +++ /dev/null @@ -1,3 +0,0 @@ -/* eslint-disable */ -/// -export * from '../dist/preload/electron.js'; diff --git a/packages/common/infra/project.json b/packages/common/infra/project.json deleted file mode 100644 index e0eb1b94e0..0000000000 --- a/packages/common/infra/project.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "infra", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "library", - "sourceRoot": "packages/common/src", - "targets": { - "build": { - "executor": "nx:run-script", - "dependsOn": ["^build"], - "inputs": ["{projectRoot}/**/*"], - "options": { - "script": "build" - }, - "outputs": ["{projectRoot}/dist"] - } - }, - "tags": ["infra"] -} diff --git a/packages/common/infra/src/__internal__/plugin.ts b/packages/common/infra/src/__internal__/plugin.ts deleted file mode 100644 index 9e2840939a..0000000000 --- a/packages/common/infra/src/__internal__/plugin.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { CallbackMap } from '@affine/sdk/entry'; -import { assertExists } from '@blocksuite/global/utils'; -import { atomWithStorage } from 'jotai/utils'; -import { atom } from 'jotai/vanilla'; -import type { z } from 'zod'; - -import type { packageJsonOutputSchema } from '../type.js'; - -export const builtinPluginPaths = new Set(runtimeConfig.builtinPlugins); - -const pluginCleanupMap = new Map void>>(); - -export function addCleanup( - pluginName: string, - cleanup: () => void -): () => void { - if (!pluginCleanupMap.has(pluginName)) { - pluginCleanupMap.set(pluginName, new Set()); - } - const cleanupSet = pluginCleanupMap.get(pluginName); - assertExists(cleanupSet); - cleanupSet.add(cleanup); - return () => { - cleanupSet.delete(cleanup); - }; -} - -export function invokeCleanup(pluginName: string) { - pluginCleanupMap.get(pluginName)?.forEach(cleanup => cleanup()); - pluginCleanupMap.delete(pluginName); -} - -export const pluginPackageJson = atom< - z.infer[] ->([]); - -export const enabledPluginAtom = atomWithStorage('affine-enabled-plugin', [ - '@affine/image-preview-plugin', - '@affine/outline-plugin', -]); - -export const pluginHeaderItemAtom = atom< - Record ->({}); - -export const pluginSettingAtom = atom>( - {} -); - -export const pluginEditorAtom = atom>({}); - -export const pluginWindowAtom = atom< - Record () => void> ->({}); diff --git a/packages/common/infra/src/atom/index.ts b/packages/common/infra/src/atom/index.ts index 8b5f2a3104..12fc228d9d 100644 --- a/packages/common/infra/src/atom/index.ts +++ b/packages/common/infra/src/atom/index.ts @@ -1,8 +1,2 @@ -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/layout.ts b/packages/common/infra/src/atom/layout.ts deleted file mode 100644 index ed6ec20fe7..0000000000 --- a/packages/common/infra/src/atom/layout.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { ExpectedLayout } from '@affine/sdk/entry'; -import { atom } from 'jotai'; - -const contentLayoutBaseAtom = atom('editor'); - -type SetStateAction = Value | ((prev: Value) => Value); -export const contentLayoutAtom = atom< - ExpectedLayout, - [SetStateAction], - void ->( - get => get(contentLayoutBaseAtom), - (_, set, layout) => { - set(contentLayoutBaseAtom, prev => { - let setV: (prev: ExpectedLayout) => ExpectedLayout; - if (typeof layout !== 'function') { - setV = () => layout; - } else { - setV = layout; - } - const nextValue = setV(prev); - if (nextValue === 'editor') { - return nextValue; - } - if (nextValue.first !== 'editor') { - throw new Error('The first element of the layout should be editor.'); - } - if (nextValue.splitPercentage && nextValue.splitPercentage < 70) { - throw new Error('The split percentage should be greater than 70.'); - } - return nextValue; - }); - } -); diff --git a/packages/common/infra/src/atom/settings.ts b/packages/common/infra/src/atom/settings.ts index 229335ce48..ac66005dc3 100644 --- a/packages/common/infra/src/atom/settings.ts +++ b/packages/common/infra/src/atom/settings.ts @@ -72,12 +72,13 @@ const appSettingEffect = atomEffect(get => { // some values in settings should be synced into electron side if (environment.isDesktop) { console.log('set config', settings); - window.apis?.updater + // this api type in @affine/electron-api, but it is circular dependency this package, use any here + (window as any).apis?.updater .setConfig({ autoCheckUpdate: settings.autoCheckUpdate, autoDownloadUpdate: settings.autoDownloadUpdate, }) - .catch(err => { + .catch((err: any) => { console.error(err); }); } diff --git a/packages/common/infra/src/atom/workspace.ts b/packages/common/infra/src/atom/workspace.ts deleted file mode 100644 index 871333c835..0000000000 --- a/packages/common/infra/src/atom/workspace.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { atom } from 'jotai'; - -export const currentPageIdAtom = atom(null); diff --git a/packages/common/infra/src/blocksuite/initialization/index.ts b/packages/common/infra/src/blocksuite/initialization/index.ts index e596607b75..518afcf5ff 100644 --- a/packages/common/infra/src/blocksuite/initialization/index.ts +++ b/packages/common/infra/src/blocksuite/initialization/index.ts @@ -1,10 +1,17 @@ -import { assertExists } from '@blocksuite/global/utils'; -import type { Page, PageMeta, Workspace } from '@blocksuite/store'; +import type { + JobMiddleware, + Page, + PageMeta, + PageSnapshot, + Workspace, + WorkspaceInfoSnapshot, +} from '@blocksuite/store'; +import { Job } from '@blocksuite/store'; import type { createStore, WritableAtom } from 'jotai/vanilla'; -import { nanoid } from 'nanoid'; import { Map as YMap } from 'yjs'; import { getLatestVersions } from '../migration/blocksuite'; +import { replaceIdMiddleware } from './middleware'; export async function initEmptyPage(page: Page, title?: string) { await page.load(() => { @@ -22,7 +29,10 @@ export async function initEmptyPage(page: Page, title?: string) { */ export async function buildShowcaseWorkspace( workspace: Workspace, - options: { + { + store, + atoms, + }: { atoms: { pageMode: WritableAtom< undefined, @@ -33,239 +43,72 @@ export async function buildShowcaseWorkspace( store: ReturnType; } ) { - const prototypes = { - tags: { - options: [ - { - id: 'icg1n5UdkP', - value: 'Travel', - color: 'var(--affine-tag-gray)', - }, - { - id: 'Oe5dSe1DDJ', - value: 'Quick summary', - color: 'var(--affine-tag-green)', - }, - { - id: 'g1L5dXKctL', - value: 'OKR', - color: 'var(--affine-tag-purple)', - }, - { - id: 'q3mceOl_zi', - value: 'Streamline your workflow', - color: 'var(--affine-tag-teal)', - }, - { - id: 'ze07JVwBu4', - value: 'Plan', - color: 'var(--affine-tag-teal)', - }, - { - id: '8qcYPCTK0h', - value: 'Review', - color: 'var(--affine-tag-orange)', - }, - { - id: 'wg-fBtd2eI', - value: 'Engage', - color: 'var(--affine-tag-pink)', - }, - { - id: 'QYFD_HeQc-', - value: 'Create', - color: 'var(--affine-tag-blue)', - }, - { - id: 'ZHBa2NtdSo', - value: 'Learn', - color: 'var(--affine-tag-yellow)', - }, - ], - }, - }; - workspace.meta.setProperties(prototypes); - const edgelessPage1 = nanoid(); - const { store, atoms } = options; - store.set(atoms.pageMode, edgelessPage1, 'edgeless'); + const { onboarding } = await import('@affine/templates'); - const pageMetas = { - '9f6f3c04-cf32-470c-9648-479dc838f10e': { - createDate: 1691548231530, - tags: ['ZHBa2NtdSo', 'QYFD_HeQc-', 'wg-fBtd2eI'], - updatedDate: 1691676331623, - favorite: true, - jumpOnce: true, - }, - '0773e198-5de0-45d4-a35e-de22ea72b96b': { - createDate: 1691548220794, - tags: [], - updatedDate: 1691676775642, - favorite: false, - }, - '59b140eb-4449-488f-9eeb-42412dcc044e': { - createDate: 1691551731225, - tags: [], - updatedDate: 1691654611175, - favorite: false, - }, - '7217fbe2-61db-4a91-93c6-ad5c800e5a43': { - createDate: 1691552082822, - tags: [], - updatedDate: 1691654606912, - favorite: false, - }, - '6eb43ea8-8c11-456d-bb1d-5193937961ab': { - createDate: 1691552090989, - tags: [], - updatedDate: 1691646748171, - favorite: false, - }, - '3ddc8a4f-62c7-4fd4-8064-9ed9f61e437a': { - createDate: 1691564303138, - tags: [], - updatedDate: 1691646845195, - }, - '22163830-8252-43fe-b62d-fd9bbeaa4caa': { - createDate: 1691574859042, - tags: [], - updatedDate: 1691648159371, - }, - 'b7a9e1bc-e205-44aa-8dad-7e328269d00b': { - createDate: 1691575011078, - tags: ['8qcYPCTK0h'], - updatedDate: 1691645074511, - favorite: false, - }, - '646305d9-93e0-48df-bb92-d82944ceb5a3': { - createDate: 1691634722239, - tags: ['ze07JVwBu4'], - updatedDate: 1691647069662, - favorite: false, - }, - '0350509d-8702-4797-b4d7-168f5e9359c7': { - createDate: 1691635388447, - tags: ['Oe5dSe1DDJ'], - updatedDate: 1691645873930, - }, - 'aa02af3c-5c5c-4856-b7ce-947ad17331f3': { - createDate: 1691636192263, - tags: ['q3mceOl_zi', 'g1L5dXKctL'], - updatedDate: 1691645102104, - }, - } satisfies Record>; - const data = [ - [ - '9f6f3c04-cf32-470c-9648-479dc838f10e', - import('@affine/templates/v1/getting-started.json'), - nanoid(), - ], - [ - '0773e198-5de0-45d4-a35e-de22ea72b96b', - import('@affine/templates/v1/preloading.json'), - edgelessPage1, - ], - [ - '59b140eb-4449-488f-9eeb-42412dcc044e', - import('@affine/templates/v1/template-galleries.json'), - nanoid(), - ], - [ - '7217fbe2-61db-4a91-93c6-ad5c800e5a43', - import('@affine/templates/v1/personal-home.json'), - nanoid(), - ], - [ - '6eb43ea8-8c11-456d-bb1d-5193937961ab', - import('@affine/templates/v1/working-home.json'), - nanoid(), - ], - [ - '3ddc8a4f-62c7-4fd4-8064-9ed9f61e437a', - import('@affine/templates/v1/personal-project-management.json'), - nanoid(), - ], - [ - '22163830-8252-43fe-b62d-fd9bbeaa4caa', - import('@affine/templates/v1/personal-knowledge-management.json'), - nanoid(), - ], - [ - 'b7a9e1bc-e205-44aa-8dad-7e328269d00b', - import('@affine/templates/v1/annual-performance-review.json'), - nanoid(), - ], - [ - '646305d9-93e0-48df-bb92-d82944ceb5a3', - import('@affine/templates/v1/brief-event-planning.json'), - nanoid(), - ], - [ - '0350509d-8702-4797-b4d7-168f5e9359c7', - import('@affine/templates/v1/meeting-summary.json'), - nanoid(), - ], - [ - 'aa02af3c-5c5c-4856-b7ce-947ad17331f3', - import('@affine/templates/v1/okr-template.json'), - nanoid(), - ], - ] as const; - const idMap = await Promise.all(data).then(async data => { - return data.reduce>( - (record, currentValue) => { - const [oldId, _, newId] = currentValue; - record[oldId] = newId; - return record; - }, - {} as Record - ); + const info = onboarding['info.json'] as WorkspaceInfoSnapshot; + + const migrationMiddleware: JobMiddleware = ({ slots, workspace }) => { + slots.afterImport.on(payload => { + if (payload.type === 'page') { + workspace.schema.upgradePage( + info?.pageVersion ?? 0, + info?.blockVersions ?? {}, + payload.page.spaceDoc + ); + } + }); + }; + + const job = new Job({ + workspace, + middlewares: [replaceIdMiddleware, migrationMiddleware], }); - // Import page one by one to prevent workspace meta race condition problem. - for (const [id, promise, newId] of data) { - const { default: template } = await promise; - let json = JSON.stringify(template); - Object.entries(idMap).forEach(([oldId, newId]) => { - json = json.replaceAll(oldId, newId); - }); - json = JSON.parse(json); - await workspace - .importPageSnapshot(structuredClone(json), newId) - .catch(error => { - console.error('error importing page', id, error); - }); - const page = workspace.getPage(newId); - assertExists(page); - await page.load(); - workspace.schema.upgradePage( - 0, - { - 'affine:note': 1, - 'affine:bookmark': 1, - 'affine:database': 2, - 'affine:divider': 1, - 'affine:image': 1, - 'affine:list': 1, - 'affine:code': 1, - 'affine:page': 2, - 'affine:paragraph': 1, - 'affine:surface': 3, - }, - page.spaceDoc - ); - } + job.snapshotToWorkspaceInfo(info); + + // for now all onboarding assets are considered served via CDN + // hack assets so that every blob exists + // @ts-expect-error - rethinking API + job._assetsManager.writeToBlob = async () => {}; + + const pageSnapshots: PageSnapshot[] = Object.entries(onboarding) + .filter(([key]) => { + return key.endsWith('snapshot.json'); + }) + .map(([_, value]) => value as unknown as PageSnapshot); + + await Promise.all( + pageSnapshots.map(snapshot => { + return job.snapshotToPage(snapshot); + }) + ); - // The showcase building will create multiple pages once, and may skip the version writing. - // https://github.com/toeverything/blocksuite/blob/master/packages/store/src/workspace/page.ts#L662 - workspace.doc.getMap('meta').set('pageVersion', 2); const newVersions = getLatestVersions(workspace.schema); workspace.doc .getMap('meta') .set('blockVersions', new YMap(Object.entries(newVersions))); - Object.entries(pageMetas).forEach(([oldId, meta]) => { - const newId = idMap[oldId]; - workspace.setPageMeta(newId, meta); - }); + // todo: find better way to do the following + // perhaps put them into middleware? + { + // the "AFFiNE - not just a note-taking app" page should be set to edgeless mode + const edgelessPage1 = (workspace.meta.pages as PageMeta[])?.find( + p => p.title === 'AFFiNE - not just a note-taking app' + )?.id; + + if (edgelessPage1) { + store.set(atoms.pageMode, edgelessPage1, 'edgeless'); + } + + // should jump to "Getting Started" by default + const gettingStartedPage = (workspace.meta.pages as PageMeta[])?.find(p => + p.title.startsWith('Getting Started') + )?.id; + + if (gettingStartedPage) { + workspace.setPageMeta(gettingStartedPage, { + jumpOnce: true, + }); + } + } } diff --git a/packages/common/infra/src/blocksuite/initialization/middleware.ts b/packages/common/infra/src/blocksuite/initialization/middleware.ts new file mode 100644 index 0000000000..94cbd27024 --- /dev/null +++ b/packages/common/infra/src/blocksuite/initialization/middleware.ts @@ -0,0 +1,142 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +/* eslint-disable @typescript-eslint/no-restricted-imports */ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +// @ts-nocheck +// TODO: remove this file after blocksuite exposed it +import type { + DatabaseBlockModel, + ListBlockModel, + ParagraphBlockModel, +} from '@blocksuite/blocks/dist/models.js'; +import { assertExists } from '@blocksuite/global/utils'; +import type { DeltaOperation, JobMiddleware } from '@blocksuite/store'; + +export const replaceIdMiddleware: JobMiddleware = ({ slots, workspace }) => { + const idMap = new Map(); + slots.afterImport.on(payload => { + if ( + payload.type === 'block' && + payload.snapshot.flavour === 'affine:database' + ) { + const model = payload.model as DatabaseBlockModel; + Object.keys(model.cells).forEach(cellId => { + if (idMap.has(cellId)) { + model.cells[idMap.get(cellId)!] = model.cells[cellId]; + delete model.cells[cellId]; + } + }); + } + + // replace LinkedPage pageId with new id in paragraph blocks + if ( + payload.type === 'block' && + ['affine:paragraph', 'affine:list'].includes(payload.snapshot.flavour) + ) { + const model = payload.model as ParagraphBlockModel | ListBlockModel; + let prev = 0; + const delta: DeltaOperation[] = []; + for (const d of model.text.toDelta()) { + if (d.attributes?.reference?.pageId) { + if (prev > 0) { + delta.push({ retain: prev }); + } + delta.push({ + retain: d.insert.length, + attributes: { + reference: { + ...d.attributes.reference, + pageId: idMap.get(d.attributes.reference.pageId)!, + }, + }, + }); + prev = 0; + } else { + prev += d.insert.length; + } + } + if (delta.length > 0) { + model.text.applyDelta(delta); + } + } + }); + slots.beforeImport.on(payload => { + if (payload.type === 'page') { + const newId = workspace.idGenerator('page'); + idMap.set(payload.snapshot.meta.id, newId); + payload.snapshot.meta.id = newId; + return; + } + + if (payload.type === 'block') { + const { snapshot } = payload; + if (snapshot.flavour === 'affine:page') { + const index = snapshot.children.findIndex( + c => c.flavour === 'affine:surface' + ); + if (index !== -1) { + const [surface] = snapshot.children.splice(index, 1); + snapshot.children.push(surface); + } + } + + const original = snapshot.id; + let newId: string; + if (idMap.has(original)) { + newId = idMap.get(original)!; + } else { + newId = workspace.idGenerator('block'); + idMap.set(original, newId); + } + snapshot.id = newId; + + if (snapshot.flavour === 'affine:surface') { + // Generate new IDs for images and frames in advance. + snapshot.children.forEach(child => { + const original = child.id; + if (idMap.has(original)) { + newId = idMap.get(original)!; + } else { + newId = workspace.idGenerator('block'); + idMap.set(original, newId); + } + }); + + Object.entries( + snapshot.props.elements as Record> + ).forEach(([_, value]) => { + switch (value.type) { + case 'connector': { + let connection = value.source as Record; + if (idMap.has(connection.id)) { + const newId = idMap.get(connection.id); + assertExists(newId, 'reference id must exist'); + connection.id = newId; + } + connection = value.target as Record; + if (idMap.has(connection.id)) { + const newId = idMap.get(connection.id); + assertExists(newId, 'reference id must exist'); + connection.id = newId; + } + break; + } + case 'group': { + const json = value.children.json as Record; + Object.entries(json).forEach(([key, value]) => { + if (idMap.has(key)) { + delete json[key]; + const newKey = idMap.get(key); + assertExists(newKey, 'reference id must exist'); + json[newKey] = value; + } + }); + break; + } + default: + break; + } + }); + } + } + }); +}; diff --git a/packages/common/infra/src/core/event-emitter.ts b/packages/common/infra/src/core/event-emitter.ts deleted file mode 100644 index 2e019f6319..0000000000 --- a/packages/common/infra/src/core/event-emitter.ts +++ /dev/null @@ -1,74 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (c) 2018 Andy Wermke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -export type EventMap = { - [key: string]: (...args: any[]) => void; -}; - -/** - * Type-safe event emitter. - * - * Use it like this: - * - * ```typescript - * type MyEvents = { - * error: (error: Error) => void; - * message: (from: string, content: string) => void; - * } - * - * const myEmitter = new EventEmitter() as TypedEmitter; - * - * myEmitter.emit("error", "x") // <- Will catch this type error; - * ``` - * - * Lifecycle: - * invoke -> handle -> emit -> on/once - */ -export interface TypedEventEmitter { - addListener(event: E, listener: Events[E]): this; - on(event: E, listener: Events[E]): this; - once(event: E, listener: Events[E]): this; - - off(event: E, listener: Events[E]): this; - removeAllListeners(event?: E): this; - removeListener(event: E, listener: Events[E]): this; - - emit( - event: E, - ...args: Parameters - ): boolean; - // The sloppy `eventNames()` return type is to mitigate type incompatibilities - see #5 - eventNames(): (keyof Events | string | symbol)[]; - rawListeners(event: E): Events[E][]; - listeners(event: E): Events[E][]; - listenerCount(event: E): number; - - handle(event: E, handler: Events[E]): this; - invoke( - event: E, - ...args: Parameters - ): Promise>; - - getMaxListeners(): number; - setMaxListeners(maxListeners: number): this; -} diff --git a/packages/common/infra/src/handler.ts b/packages/common/infra/src/handler.ts deleted file mode 100644 index 722fc13890..0000000000 --- a/packages/common/infra/src/handler.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { - ClipboardHandlers, - ConfigStorageHandlers, - DBHandlers, - DebugHandlers, - DialogHandlers, - ExportHandlers, - UIHandlers, - UpdaterHandlers, - WorkspaceHandlers, -} from './type.js'; -import { HandlerManager } from './type.js'; - -export abstract class DBHandlerManager extends HandlerManager< - 'db', - DBHandlers -> {} - -export abstract class DebugHandlerManager extends HandlerManager< - 'debug', - DebugHandlers -> {} - -export abstract class DialogHandlerManager extends HandlerManager< - 'dialog', - DialogHandlers -> {} - -export abstract class UIHandlerManager extends HandlerManager< - 'ui', - UIHandlers -> {} - -export abstract class ClipboardHandlerManager extends HandlerManager< - 'clipboard', - ClipboardHandlers -> {} - -export abstract class ExportHandlerManager extends HandlerManager< - 'export', - ExportHandlers -> {} - -export abstract class UpdaterHandlerManager extends HandlerManager< - 'updater', - UpdaterHandlers -> {} - -export abstract class WorkspaceHandlerManager extends HandlerManager< - 'workspace', - WorkspaceHandlers -> {} - -export abstract class ConfigStorageHandlerManager extends HandlerManager< - 'configStorage', - ConfigStorageHandlers -> {} diff --git a/packages/common/infra/src/index.ts b/packages/common/infra/src/index.ts index d0d18480ad..6e94b1bdc7 100644 --- a/packages/common/infra/src/index.ts +++ b/packages/common/infra/src/index.ts @@ -1,2 +1,4 @@ -export * from './handler.js'; -export * from './type.js'; +export * from './app-config-storage'; +export * from './atom'; +export * from './blocksuite'; +export * from './command'; diff --git a/packages/common/infra/src/type.ts b/packages/common/infra/src/type.ts deleted file mode 100644 index cdda8bfe28..0000000000 --- a/packages/common/infra/src/type.ts +++ /dev/null @@ -1,295 +0,0 @@ -import type { ExpectedLayout } from '@affine/sdk/entry'; -import type Buffer from 'buffer'; -import type { WritableAtom } from 'jotai'; -import { z } from 'zod'; - -import type { AppConfigSchema } from './app-config-storage.js'; -import type { TypedEventEmitter } from './core/event-emitter.js'; - -type Buffer = Buffer.Buffer; - -export const packageJsonInputSchema = z.object({ - name: z.string(), - version: z.string(), - description: z.string(), - affinePlugin: z.object({ - release: z.union([z.boolean(), z.enum(['development'])]), - entry: z.object({ - core: z.string(), - }), - }), -}); - -export const packageJsonOutputSchema = z.object({ - name: z.string(), - version: z.string(), - description: z.string(), - affinePlugin: z.object({ - release: z.union([z.boolean(), z.enum(['development'])]), - entry: z.object({ - core: z.string(), - }), - assets: z.array(z.string()), - }), -}); - -type SetStateAction = Value | ((prev: Value) => Value); - -export type ContentLayoutAtom = WritableAtom< - ExpectedLayout, - [SetStateAction], - void ->; - -export abstract class HandlerManager< - Namespace extends string, - Handlers extends Record, -> { - static instance: HandlerManager>; - private readonly _app: App; - private readonly _namespace: Namespace; - private _handlers: Handlers; - - constructor() { - throw new Error('Method not implemented.'); - } - - private _initialized = false; - - registerHandlers(handlers: Handlers) { - if (this._initialized) { - throw new Error('Already initialized'); - } - this._handlers = handlers; - for (const [name, handler] of Object.entries(this._handlers)) { - this._app.handle(`${this._namespace}:${name}`, (async (...args: any[]) => - handler(...args)) as any); - } - this._initialized = true; - } - - invokeHandler( - name: K, - ...args: Parameters - ): Promise> { - return this._handlers[name](...args); - } - - static getInstance(): HandlerManager< - string, - Record - > { - throw new Error('Method not implemented.'); - } -} - -export interface WorkspaceMeta { - id: string; - mainDBPath: string; - secondaryDBPath?: string; // assume there will be only one -} - -export type PrimitiveHandlers = (...args: any[]) => Promise; - -export type DBHandlers = { - getDocAsUpdates: ( - workspaceId: string, - subdocId?: string - ) => Promise; - applyDocUpdate: ( - id: string, - update: Uint8Array, - subdocId?: string - ) => Promise; - addBlob: ( - workspaceId: string, - key: string, - data: Uint8Array - ) => Promise; - getBlob: (workspaceId: string, key: string) => Promise; - deleteBlob: (workspaceId: string, key: string) => Promise; - getBlobKeys: (workspaceId: string) => Promise; - getDefaultStorageLocation: () => Promise; -}; - -export type DebugHandlers = { - revealLogFile: () => Promise; - logFilePath: () => Promise; -}; - -export type ErrorMessage = - | 'DB_FILE_ALREADY_LOADED' - | 'DB_FILE_PATH_INVALID' - | 'DB_FILE_INVALID' - | 'DB_FILE_MIGRATION_FAILED' - | 'FILE_ALREADY_EXISTS' - | 'UNKNOWN_ERROR'; - -export interface LoadDBFileResult { - workspaceId?: string; - error?: ErrorMessage; - canceled?: boolean; -} - -export interface SaveDBFileResult { - filePath?: string; - canceled?: boolean; - error?: ErrorMessage; -} - -export interface SelectDBFileLocationResult { - filePath?: string; - error?: ErrorMessage; - canceled?: boolean; -} - -export interface MoveDBFileResult { - filePath?: string; - error?: ErrorMessage; - canceled?: boolean; -} - -// provide a backdoor to set dialog path for testing in playwright -export interface FakeDialogResult { - canceled?: boolean; - filePath?: string; - filePaths?: string[]; -} - -export type DialogHandlers = { - revealDBFile: (workspaceId: string) => Promise; - loadDBFile: () => Promise; - saveDBFileAs: (workspaceId: string) => Promise; - moveDBFile: ( - workspaceId: string, - dbFileLocation?: string - ) => Promise; - selectDBFileLocation: () => Promise; - setFakeDialogResult: (result: any) => Promise; -}; - -export type UIHandlers = { - handleThemeChange: (theme: 'system' | 'light' | 'dark') => Promise; - handleSidebarVisibilityChange: (visible: boolean) => Promise; - handleMinimizeApp: () => Promise; - handleMaximizeApp: () => Promise; - handleCloseApp: () => Promise; - getGoogleOauthCode: () => Promise; - getChallengeResponse: (resource: string) => Promise; - handleOpenMainApp: () => Promise; -}; - -export type ClipboardHandlers = { - copyAsImageFromString: (dataURL: string) => Promise; -}; - -export type ExportHandlers = { - savePDFFileAs: (title: string) => Promise; -}; - -export interface UpdateMeta { - version: string; - allowAutoUpdate: boolean; -} - -export type UpdaterConfig = { - autoCheckUpdate: boolean; - autoDownloadUpdate: boolean; -}; - -export type UpdaterHandlers = { - currentVersion: () => Promise; - quitAndInstall: () => Promise; - downloadUpdate: () => Promise; - getConfig: () => Promise; - setConfig: (newConfig: Partial) => Promise; - checkForUpdates: () => Promise<{ version: string } | null>; -}; - -export type WorkspaceHandlers = { - list: () => Promise<[workspaceId: string, meta: WorkspaceMeta][]>; - delete: (id: string) => Promise; - getMeta: (id: string) => Promise; - clone: (id: string, newId: string) => Promise; -}; - -export type ConfigStorageHandlers = { - set: (config: AppConfigSchema | Partial) => Promise; - get: () => Promise; -}; - -export type UnwrapManagerHandlerToServerSide< - ElectronEvent extends { - frameId: number; - processId: number; - }, - Manager extends HandlerManager>, -> = Manager extends HandlerManager - ? { - [K in keyof Handlers]: Handlers[K] extends ( - ...args: infer Args - ) => Promise - ? (event: ElectronEvent, ...args: Args) => Promise - : never; - } - : never; - -export type UnwrapManagerHandlerToClientSide< - Manager extends HandlerManager>, -> = Manager extends HandlerManager - ? { - [K in keyof Handlers]: Handlers[K] extends ( - ...args: infer Args - ) => Promise - ? (...args: Args) => Promise - : never; - } - : never; - -/** - * @internal - */ -export type App< - Namespace extends string, - Handlers extends Record, -> = TypedEventEmitter<{ - [K in keyof Handlers as `${Namespace}:${K & string}`]: Handlers[K]; -}>; - -export interface UpdaterEvents { - onUpdateAvailable: (fn: (versionMeta: UpdateMeta) => void) => () => void; - onUpdateReady: (fn: (versionMeta: UpdateMeta) => void) => () => void; - onDownloadProgress: (fn: (progress: number) => void) => () => void; -} - -export interface ApplicationMenuEvents { - onNewPageAction: (fn: () => void) => () => void; -} - -export interface DBEvents { - onExternalUpdate: ( - fn: (update: { - workspaceId: string; - update: Uint8Array; - docId?: string; - }) => void - ) => () => void; -} - -export interface WorkspaceEvents { - onMetaChange: ( - fn: (workspaceId: string, meta: WorkspaceMeta) => void - ) => () => void; -} - -export interface UIEvents { - onMaximized: (fn: (maximized: boolean) => void) => () => void; -} - -export interface EventMap { - updater: UpdaterEvents; - applicationMenu: ApplicationMenuEvents; - db: DBEvents; - ui: UIEvents; - workspace: WorkspaceEvents; -} diff --git a/packages/common/infra/tsconfig.json b/packages/common/infra/tsconfig.json index 1e81c00cd2..11279f55b9 100644 --- a/packages/common/infra/tsconfig.json +++ b/packages/common/infra/tsconfig.json @@ -4,13 +4,9 @@ "compilerOptions": { "composite": true, "noEmit": false, - "moduleResolution": "bundler", "outDir": "lib" }, "references": [ - { - "path": "../sdk" - }, { "path": "../env" }, diff --git a/packages/common/infra/typedoc.json b/packages/common/infra/typedoc.json new file mode 100644 index 0000000000..101e923dba --- /dev/null +++ b/packages/common/infra/typedoc.json @@ -0,0 +1,4 @@ +{ + "extends": ["../../../typedoc.base.json"], + "entryPoints": ["src/index.ts"] +} diff --git a/packages/common/infra/vite.config.ts b/packages/common/infra/vite.config.ts deleted file mode 100644 index 03baf6f2a7..0000000000 --- a/packages/common/infra/vite.config.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { resolve } from 'node:path'; - -import { fileURLToPath } from 'url'; -import { defineConfig } from 'vite'; -import dts from 'vite-plugin-dts'; - -const root = fileURLToPath(new URL('.', import.meta.url)); - -export default defineConfig({ - build: { - minify: false, - lib: { - entry: { - blocksuite: resolve(root, 'src/blocksuite/index.ts'), - index: resolve(root, 'src/index.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'), - 'preload/electron': resolve(root, 'src/preload/electron.ts'), - 'app-config-storage': resolve(root, 'src/app-config-storage.ts'), - '__internal__/plugin': resolve(root, 'src/__internal__/plugin.ts'), - }, - formats: ['es', 'cjs'], - name: 'AffineInfra', - }, - rollupOptions: { - external: [ - 'electron', - 'async-call-rpc', - 'rxjs', - 'zod', - 'react', - 'yjs', - 'nanoid', - /^jotai/, - /^@blocksuite/, - /^@affine\/templates/, - ], - }, - }, - plugins: [dts()], -}); diff --git a/packages/common/sdk/.gitignore b/packages/common/sdk/.gitignore deleted file mode 100644 index 3dca72d418..0000000000 --- a/packages/common/sdk/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -src/entry.d.ts -src/entry.d.ts.map -src/entry.js -src/entry.js.map diff --git a/packages/common/sdk/package.json b/packages/common/sdk/package.json deleted file mode 100644 index 09e13d00ec..0000000000 --- a/packages/common/sdk/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "@affine/sdk", - "version": "0.11.0", - "type": "module", - "scripts": { - "build": "vite build", - "dev": "vite build --watch" - }, - "exports": { - "./entry": { - "types": "./dist/src/entry.d.ts", - "import": "./dist/entry.js", - "require": "./dist/entry.cjs" - }, - "./server": { - "types": "./dist/src/server.d.ts", - "import": "./dist/server.js", - "require": "./dist/server.cjs" - } - }, - "files": [ - "dist" - ], - "dependencies": { - "@blocksuite/block-std": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/blocks": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/global": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/presets": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/store": "0.11.0-nightly-202312220916-e3abcbb", - "jotai": "^2.5.1", - "zod": "^3.22.4" - }, - "devDependencies": { - "vite": "^5.0.6", - "vite-plugin-dts": "3.6.0" - } -} diff --git a/packages/common/sdk/project.json b/packages/common/sdk/project.json deleted file mode 100644 index 749b2e7b53..0000000000 --- a/packages/common/sdk/project.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "sdk", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "library", - "sourceRoot": "packages/common/sdk/src", - "targets": { - "build": { - "executor": "@nx/vite:build", - "options": { - "outputPath": "packages/common/sdk/dist" - } - }, - "serve": { - "executor": "@nx/vite:build", - "options": { - "outputPath": "packages/common/sdk/dist", - "watch": true - } - } - }, - "tags": ["infra"] -} diff --git a/packages/common/sdk/src/entry.ts b/packages/common/sdk/src/entry.ts deleted file mode 100644 index 7421ebeb1e..0000000000 --- a/packages/common/sdk/src/entry.ts +++ /dev/null @@ -1,65 +0,0 @@ -import type { BaseSelection } from '@blocksuite/block-std'; -import type { AffineEditorContainer } from '@blocksuite/presets'; -import type { Page } from '@blocksuite/store'; -import type { Workspace } from '@blocksuite/store'; -import type { Atom, getDefaultStore } from 'jotai/vanilla'; -import type { WritableAtom } from 'jotai/vanilla/atom'; -import type { FunctionComponent } from 'react'; - -export type Part = 'headerItem' | 'editor' | 'setting' | 'formatBar'; - -export type CallbackMap = { - headerItem: (root: HTMLElement) => () => void; - editor: (root: HTMLElement, editor: AffineEditorContainer) => () => void; - setting: (root: HTMLElement) => () => void; - formatBar: ( - root: HTMLElement, - page: Page, - getBlockRange: () => BaseSelection[] - ) => () => void; -}; - -export interface PluginContext { - register: (part: T, callback: CallbackMap[T]) => void; - utils: { - PluginProvider: FunctionComponent; // make more clear - }; -} - -export type LayoutDirection = 'horizontal' | 'vertical'; -export type LayoutNode = LayoutParentNode | string; -export type LayoutParentNode = { - direction: LayoutDirection; - splitPercentage: number; // 0 - 100 - first: string; - second: LayoutNode; - maxWidth?: (number | undefined)[]; -}; - -export type ExpectedLayout = - | { - direction: 'horizontal'; - // the first element is always the editor - first: 'editor'; - second: LayoutNode; - // the percentage should be greater than 70 - splitPercentage: number; - } - | 'editor'; - -export declare const pushLayoutAtom: WritableAtom< - null, - | [ - string, - (div: HTMLDivElement) => () => void, - { - maxWidth: (number | undefined)[]; - }, - ] - | [string, (div: HTMLDivElement) => () => void], - void ->; -export declare const deleteLayoutAtom: WritableAtom; -export declare const currentPageIdAtom: Atom; -export declare const currentWorkspaceAtom: Atom>; -export declare const rootStore: ReturnType; diff --git a/packages/common/sdk/src/server.ts b/packages/common/sdk/src/server.ts deleted file mode 100644 index f7f88e3c48..0000000000 --- a/packages/common/sdk/src/server.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface ServerContext { - registerCommand: (command: string, fn: (...args: any[]) => any) => void; - unregisterCommand: (command: string) => void; -} diff --git a/packages/common/sdk/tsconfig.node.json b/packages/common/sdk/tsconfig.node.json deleted file mode 100644 index 097adbc4e3..0000000000 --- a/packages/common/sdk/tsconfig.node.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "../../../tsconfig.json", - "compilerOptions": { - "composite": true, - "module": "ESNext", - "moduleResolution": "Node", - "allowSyntheticDefaultImports": true, - "outDir": "lib", - "noEmit": false - }, - "include": ["vite.config.ts"] -} diff --git a/packages/common/sdk/vite.config.ts b/packages/common/sdk/vite.config.ts deleted file mode 100644 index d158eff203..0000000000 --- a/packages/common/sdk/vite.config.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { resolve } from 'node:path'; - -import { fileURLToPath } from 'url'; -import { defineConfig } from 'vite'; -import dts from 'vite-plugin-dts'; - -const root = fileURLToPath(new URL('.', import.meta.url)); - -export default defineConfig({ - build: { - minify: false, - lib: { - entry: { - entry: resolve(root, 'src/entry.ts'), - server: resolve(root, 'src/server.ts'), - }, - }, - rollupOptions: { - external: [/^jotai/, /^@blocksuite/, 'zod'], - }, - }, - plugins: [dts()], -}); diff --git a/packages/frontend/workspace/.gitignore b/packages/common/workspace/.gitignore similarity index 100% rename from packages/frontend/workspace/.gitignore rename to packages/common/workspace/.gitignore diff --git a/packages/common/workspace/package.json b/packages/common/workspace/package.json new file mode 100644 index 0000000000..2caa05aa64 --- /dev/null +++ b/packages/common/workspace/package.json @@ -0,0 +1,24 @@ +{ + "name": "@affine/workspace", + "private": true, + "main": "./src/index.ts", + "exports": { + ".": "./src/index.ts" + }, + "peerDependencies": { + "@blocksuite/blocks": "*", + "@blocksuite/global": "*", + "@blocksuite/store": "*" + }, + "dependencies": { + "@affine/debug": "workspace:*", + "@affine/env": "workspace:*", + "@toeverything/infra": "workspace:*", + "lodash-es": "^4.17.21", + "yjs": "^13.6.10" + }, + "devDependencies": { + "vitest": "1.1.3" + }, + "version": "0.11.0" +} diff --git a/packages/frontend/workspace/src/engine/awareness.ts b/packages/common/workspace/src/engine/awareness.ts similarity index 100% rename from packages/frontend/workspace/src/engine/awareness.ts rename to packages/common/workspace/src/engine/awareness.ts diff --git a/packages/frontend/workspace/src/engine/blob.ts b/packages/common/workspace/src/engine/blob.ts similarity index 100% rename from packages/frontend/workspace/src/engine/blob.ts rename to packages/common/workspace/src/engine/blob.ts diff --git a/packages/frontend/workspace/src/engine/index.ts b/packages/common/workspace/src/engine/index.ts similarity index 100% rename from packages/frontend/workspace/src/engine/index.ts rename to packages/common/workspace/src/engine/index.ts diff --git a/packages/frontend/workspace/src/engine/sync/consts.ts b/packages/common/workspace/src/engine/sync/consts.ts similarity index 100% rename from packages/frontend/workspace/src/engine/sync/consts.ts rename to packages/common/workspace/src/engine/sync/consts.ts diff --git a/packages/frontend/workspace/src/engine/sync/engine.ts b/packages/common/workspace/src/engine/sync/engine.ts similarity index 100% rename from packages/frontend/workspace/src/engine/sync/engine.ts rename to packages/common/workspace/src/engine/sync/engine.ts diff --git a/packages/frontend/workspace/src/engine/sync/index.ts b/packages/common/workspace/src/engine/sync/index.ts similarity index 100% rename from packages/frontend/workspace/src/engine/sync/index.ts rename to packages/common/workspace/src/engine/sync/index.ts diff --git a/packages/frontend/workspace/src/engine/sync/peer.ts b/packages/common/workspace/src/engine/sync/peer.ts similarity index 100% rename from packages/frontend/workspace/src/engine/sync/peer.ts rename to packages/common/workspace/src/engine/sync/peer.ts diff --git a/packages/frontend/workspace/src/engine/sync/storage.ts b/packages/common/workspace/src/engine/sync/storage.ts similarity index 100% rename from packages/frontend/workspace/src/engine/sync/storage.ts rename to packages/common/workspace/src/engine/sync/storage.ts diff --git a/packages/frontend/workspace/src/factory.ts b/packages/common/workspace/src/factory.ts similarity index 100% rename from packages/frontend/workspace/src/factory.ts rename to packages/common/workspace/src/factory.ts diff --git a/packages/frontend/workspace/src/global-schema.ts b/packages/common/workspace/src/global-schema.ts similarity index 100% rename from packages/frontend/workspace/src/global-schema.ts rename to packages/common/workspace/src/global-schema.ts diff --git a/packages/common/workspace/src/index.ts b/packages/common/workspace/src/index.ts new file mode 100644 index 0000000000..bd084ab359 --- /dev/null +++ b/packages/common/workspace/src/index.ts @@ -0,0 +1,7 @@ +export * from './engine'; +export * from './factory'; +export * from './global-schema'; +export * from './list'; +export * from './manager'; +export * from './metadata'; +export * from './workspace'; diff --git a/packages/frontend/workspace/src/list/cache.ts b/packages/common/workspace/src/list/cache.ts similarity index 100% rename from packages/frontend/workspace/src/list/cache.ts rename to packages/common/workspace/src/list/cache.ts diff --git a/packages/frontend/workspace/src/list/index.ts b/packages/common/workspace/src/list/index.ts similarity index 100% rename from packages/frontend/workspace/src/list/index.ts rename to packages/common/workspace/src/list/index.ts diff --git a/packages/frontend/workspace/src/list/information.ts b/packages/common/workspace/src/list/information.ts similarity index 100% rename from packages/frontend/workspace/src/list/information.ts rename to packages/common/workspace/src/list/information.ts diff --git a/packages/frontend/workspace/src/manager.ts b/packages/common/workspace/src/manager.ts similarity index 91% rename from packages/frontend/workspace/src/manager.ts rename to packages/common/workspace/src/manager.ts index 9b32ebff92..52e4bf98d8 100644 --- a/packages/frontend/workspace/src/manager.ts +++ b/packages/common/workspace/src/manager.ts @@ -7,7 +7,6 @@ import { applyUpdate, encodeStateAsUpdate } from 'yjs'; import type { BlobStorage } from '.'; import type { WorkspaceFactory } from './factory'; -import { LOCAL_WORKSPACE_LOCAL_STORAGE_KEY } from './impl/local/consts'; import type { WorkspaceList } from './list'; import type { WorkspaceMetadata } from './metadata'; import { WorkspacePool } from './pool'; @@ -151,21 +150,6 @@ export class WorkspaceManager { return factory.getWorkspaceBlob(metadata.id, blobKey); } - /** - * a hack for directly add local workspace to workspace list - * Used after copying sqlite database file to appdata folder - */ - _addLocalWorkspace(id: string) { - const allWorkspaceIDs: string[] = JSON.parse( - localStorage.getItem(LOCAL_WORKSPACE_LOCAL_STORAGE_KEY) ?? '[]' - ); - allWorkspaceIDs.push(id); - localStorage.setItem( - LOCAL_WORKSPACE_LOCAL_STORAGE_KEY, - JSON.stringify(allWorkspaceIDs) - ); - } - private open(metadata: WorkspaceMetadata) { logger.info(`open workspace [${metadata.flavour}] ${metadata.id} `); const factory = this.factories.find(x => x.name === metadata.flavour); diff --git a/packages/frontend/workspace/src/metadata.ts b/packages/common/workspace/src/metadata.ts similarity index 100% rename from packages/frontend/workspace/src/metadata.ts rename to packages/common/workspace/src/metadata.ts diff --git a/packages/frontend/workspace/src/pool.ts b/packages/common/workspace/src/pool.ts similarity index 100% rename from packages/frontend/workspace/src/pool.ts rename to packages/common/workspace/src/pool.ts diff --git a/packages/frontend/workspace/src/upgrade/index.ts b/packages/common/workspace/src/upgrade/index.ts similarity index 100% rename from packages/frontend/workspace/src/upgrade/index.ts rename to packages/common/workspace/src/upgrade/index.ts diff --git a/packages/frontend/workspace/src/utils/__tests__/async-queue.spec.ts b/packages/common/workspace/src/utils/__tests__/async-queue.spec.ts similarity index 100% rename from packages/frontend/workspace/src/utils/__tests__/async-queue.spec.ts rename to packages/common/workspace/src/utils/__tests__/async-queue.spec.ts diff --git a/packages/frontend/workspace/src/utils/__tests__/throw-if-aborted.spec.ts b/packages/common/workspace/src/utils/__tests__/throw-if-aborted.spec.ts similarity index 100% rename from packages/frontend/workspace/src/utils/__tests__/throw-if-aborted.spec.ts rename to packages/common/workspace/src/utils/__tests__/throw-if-aborted.spec.ts diff --git a/packages/frontend/workspace/src/utils/async-queue.ts b/packages/common/workspace/src/utils/async-queue.ts similarity index 100% rename from packages/frontend/workspace/src/utils/async-queue.ts rename to packages/common/workspace/src/utils/async-queue.ts diff --git a/packages/frontend/workspace/src/utils/merge-updates.ts b/packages/common/workspace/src/utils/merge-updates.ts similarity index 100% rename from packages/frontend/workspace/src/utils/merge-updates.ts rename to packages/common/workspace/src/utils/merge-updates.ts diff --git a/packages/frontend/workspace/src/utils/throw-if-aborted.ts b/packages/common/workspace/src/utils/throw-if-aborted.ts similarity index 100% rename from packages/frontend/workspace/src/utils/throw-if-aborted.ts rename to packages/common/workspace/src/utils/throw-if-aborted.ts diff --git a/packages/frontend/workspace/src/workspace.ts b/packages/common/workspace/src/workspace.ts similarity index 100% rename from packages/frontend/workspace/src/workspace.ts rename to packages/common/workspace/src/workspace.ts diff --git a/packages/frontend/workspace/tsconfig.json b/packages/common/workspace/tsconfig.json similarity index 78% rename from packages/frontend/workspace/tsconfig.json rename to packages/common/workspace/tsconfig.json index dc07b10505..15af0a0980 100644 --- a/packages/frontend/workspace/tsconfig.json +++ b/packages/common/workspace/tsconfig.json @@ -10,6 +10,7 @@ { "path": "../../common/env" }, { "path": "../../common/debug" }, { "path": "../../common/infra" }, - { "path": "../../frontend/graphql" } + { "path": "../../frontend/graphql" }, + { "path": "../../frontend/electron-api" } ] } diff --git a/packages/common/workspace/typedoc.json b/packages/common/workspace/typedoc.json new file mode 100644 index 0000000000..101e923dba --- /dev/null +++ b/packages/common/workspace/typedoc.json @@ -0,0 +1,4 @@ +{ + "extends": ["../../../typedoc.base.json"], + "entryPoints": ["src/index.ts"] +} diff --git a/packages/common/y-indexeddb/package.json b/packages/common/y-indexeddb/package.json index 7843f70381..9a2ec7dc7a 100644 --- a/packages/common/y-indexeddb/package.json +++ b/packages/common/y-indexeddb/package.json @@ -32,17 +32,18 @@ } }, "dependencies": { + "@blocksuite/global": "0.11.0-nightly-202401020419-752a5b8", "idb": "^8.0.0", "nanoid": "^5.0.3", "y-provider": "workspace:*" }, "devDependencies": { - "@blocksuite/blocks": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/store": "0.11.0-nightly-202312220916-e3abcbb", + "@blocksuite/blocks": "0.11.0-nightly-202401020419-752a5b8", + "@blocksuite/store": "0.11.0-nightly-202401020419-752a5b8", "fake-indexeddb": "^5.0.0", "vite": "^5.0.6", - "vite-plugin-dts": "3.6.0", - "vitest": "1.0.4", + "vite-plugin-dts": "3.7.0", + "vitest": "1.1.3", "y-indexeddb": "^9.0.11", "yjs": "^13.6.10" }, diff --git a/packages/common/y-provider/package.json b/packages/common/y-provider/package.json index 579af7a822..cb815e38d8 100644 --- a/packages/common/y-provider/package.json +++ b/packages/common/y-provider/package.json @@ -24,13 +24,14 @@ "build": "vite build" }, "devDependencies": { - "@blocksuite/store": "0.11.0-nightly-202312220916-e3abcbb", + "@blocksuite/store": "0.11.0-nightly-202401020419-752a5b8", "vite": "^5.0.6", - "vite-plugin-dts": "3.6.0", - "vitest": "1.0.4", + "vite-plugin-dts": "3.7.0", + "vitest": "1.1.3", "yjs": "^13.6.10" }, "peerDependencies": { + "@blocksuite/global": "*", "yjs": "^13" } } diff --git a/packages/frontend/component/package.json b/packages/frontend/component/package.json index 95f6d1b2a9..2df0a65915 100644 --- a/packages/frontend/component/package.json +++ b/packages/frontend/component/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@affine/debug": "workspace:*", + "@affine/electron-api": "workspace:*", "@affine/graphql": "workspace:*", "@affine/i18n": "workspace:*", "@affine/workspace": "workspace:*", @@ -41,8 +42,6 @@ "@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", "@vanilla-extract/dynamic": "^2.0.3", "bytes": "^3.1.2", @@ -72,12 +71,12 @@ "uuid": "^9.0.1" }, "devDependencies": { - "@blocksuite/blocks": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/global": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/icons": "2.1.36", - "@blocksuite/lit": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/presets": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/store": "0.11.0-nightly-202312220916-e3abcbb", + "@blocksuite/blocks": "0.11.0-nightly-202401020419-752a5b8", + "@blocksuite/global": "0.11.0-nightly-202401020419-752a5b8", + "@blocksuite/icons": "2.1.40", + "@blocksuite/lit": "0.11.0-nightly-202401020419-752a5b8", + "@blocksuite/presets": "0.11.0-nightly-202401020419-752a5b8", + "@blocksuite/store": "0.11.0-nightly-202401020419-752a5b8", "@storybook/addon-actions": "^7.5.3", "@storybook/addon-essentials": "^7.5.3", "@storybook/addon-interactions": "^7.5.3", @@ -89,7 +88,7 @@ "@storybook/jest": "^0.2.3", "@storybook/react": "^7.5.3", "@storybook/react-vite": "^7.5.3", - "@storybook/test-runner": "^0.15.2", + "@storybook/test-runner": "^0.16.0", "@storybook/testing-library": "^0.2.2", "@testing-library/react": "^14.0.0", "@types/bytes": "^3.1.3", @@ -103,7 +102,7 @@ "storybook-dark-mode": "^3.0.1", "typescript": "^5.3.2", "vite": "^5.0.6", - "vitest": "1.0.4", + "vitest": "1.1.3", "yjs": "^13.6.10" }, "version": "0.11.0" 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 bf9025d78f..d183c068c9 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,17 +1,18 @@ import { Unreachable } from '@affine/env/constant'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { CloseIcon, NewIcon, ResetIcon } from '@blocksuite/icons'; -import { useAppUpdater } from '@toeverything/hooks/use-app-updater'; import clsx from 'clsx'; import { useCallback, useMemo } from 'react'; import { Tooltip } from '../../../ui/tooltip'; import * as styles from './index.css'; -export interface AddPageButtonPureProps { - onClickUpdate: () => void; - onDismissCurrentChangelog: () => void; - currentChangelogUnread: boolean; +export interface AddPageButtonProps { + onQuitAndInstall: () => void; + onDownloadUpdate: () => void; + onDismissChangelog: () => void; + onOpenChangelog: () => void; + changelogUnread: boolean; updateReady: boolean; updateAvailable: { version: string; @@ -33,8 +34,8 @@ interface ButtonContentProps { autoDownload: boolean; downloadProgress: number | null; appQuitting: boolean; - currentChangelogUnread: boolean; - onDismissCurrentChangelog: () => void; + changelogUnread: boolean; + onDismissChangelog: () => void; } function DownloadUpdate({ updateAvailable }: ButtonContentProps) { @@ -114,14 +115,14 @@ function OpenDownloadPage({ updateAvailable }: ButtonContentProps) { ); } -function WhatsNew({ onDismissCurrentChangelog }: ButtonContentProps) { +function WhatsNew({ onDismissChangelog }: ButtonContentProps) { const t = useAFFiNEI18N(); const onClickClose: React.MouseEventHandler = useCallback( e => { - onDismissCurrentChangelog(); + onDismissChangelog(); e.stopPropagation(); }, - [onDismissCurrentChangelog] + [onDismissChangelog] ); return ( <> @@ -149,42 +150,76 @@ const getButtonContentRenderer = (props: ButtonContentProps) => { } } else if (props.updateAvailable && !props.updateAvailable?.allowAutoUpdate) { return OpenDownloadPage; - } else if (props.currentChangelogUnread) { + } else if (props.changelogUnread) { return WhatsNew; } return null; }; -export function AppUpdaterButtonPure({ +export function AppUpdaterButton({ updateReady, - onClickUpdate, - onDismissCurrentChangelog, - currentChangelogUnread, + changelogUnread, + onDismissChangelog, + onDownloadUpdate, + onQuitAndInstall, + onOpenChangelog, updateAvailable, autoDownload, downloadProgress, appQuitting, className, style, -}: AddPageButtonPureProps) { +}: AddPageButtonProps) { + const handleClick = useCallback(() => { + if (updateReady) { + onQuitAndInstall(); + } else if (updateAvailable) { + if (updateAvailable.allowAutoUpdate) { + if (autoDownload) { + // wait for download to finish + } else { + onDownloadUpdate(); + } + } else { + window.open( + `https://github.com/toeverything/AFFiNE/releases/tag/v${updateAvailable.version}`, + '_blank' + ); + } + } else if (changelogUnread) { + window.open(runtimeConfig.changelogUrl, '_blank'); + onOpenChangelog(); + } else { + throw new Unreachable(); + } + }, [ + updateReady, + updateAvailable, + changelogUnread, + onQuitAndInstall, + autoDownload, + onDownloadUpdate, + onOpenChangelog, + ]); + const contentProps = useMemo( () => ({ updateReady, updateAvailable, - currentChangelogUnread, + changelogUnread, autoDownload, downloadProgress, appQuitting, - onDismissCurrentChangelog, + onDismissChangelog, }), [ updateReady, updateAvailable, - currentChangelogUnread, + changelogUnread, autoDownload, downloadProgress, appQuitting, - onDismissCurrentChangelog, + onDismissChangelog, ] ); @@ -222,6 +257,10 @@ export function AppUpdaterButtonPure({ updateReady, ]); + if (!updateAvailable && !changelogUnread) { + return null; + } + return wrapWithTooltip( + - ))} - + {day} + + + {list.map((history, idx) => { + return ( + +
{ + e.stopPropagation(); + onVersionChange(history.timestamp); + }} + data-active={activeVersion === history.timestamp} + > + +
+ {idx > list.length - 1 ? ( +
+ ) : null} + + ); + })} + + ); })} {loadMore ? ( @@ -216,7 +338,7 @@ const PageHistoryList = ({ className={styles.historyItemLoadMore} onClick={loadMore} > - Load More + {t['com.affine.history.confirm-restore-modal.load-more']()} ) : null} @@ -306,7 +428,7 @@ const PageHistoryManager = ({ return workspace.getPage(pageId)?.spaceDoc.guid ?? pageId; }, [pageId, workspace]); - const snapshot = usePageHistory(workspaceId, pageDocId, activeVersion); + const snapshotPage = useSnapshotPage(workspaceId, pageDocId, activeVersion); const t = useAFFiNEI18N(); @@ -314,14 +436,15 @@ const PageHistoryManager = ({ const handleRestore = useMemo( () => async () => { - if (!activeVersion || !snapshot) { + if (!activeVersion || !snapshotPage) { return; } + const snapshot = encodeStateAsUpdate(snapshotPage.spaceDoc); await onRestore(activeVersion, new Uint8Array(snapshot)); // close the modal after restore onClose(); }, - [activeVersion, onClose, onRestore, snapshot] + [activeVersion, onClose, onRestore, snapshotPage] ); const defaultPreviewPageMode = useAtomValue(currentModeAtom); @@ -352,10 +475,8 @@ const PageHistoryManager = ({
{ icon: KeyboardIcon, testId: 'shortcuts-panel-trigger', }, - { - key: 'plugins', - title: 'Plugins', - icon: PluginIcon, - testId: 'plugins-panel-trigger', - }, { key: 'about', title: t['com.affine.aboutAFFiNE.title'](), @@ -96,8 +87,6 @@ export const GeneralSetting = ({ generalKey }: GeneralSettingProps) => { return ; case 'appearance': return ; - case 'plugins': - return ; case 'about': return ; case 'plans': diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/actions.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/actions.tsx index 4b8a7c0b1d..f71032a19b 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/actions.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/actions.tsx @@ -1,14 +1,14 @@ +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import type { SubscriptionMutator } from '@affine/core/hooks/use-subscription'; import { cancelSubscriptionMutation, resumeSubscriptionMutation, } from '@affine/graphql'; -import { useMutation } from '@affine/workspace/affine/gql'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { nanoid } from 'nanoid'; import type { PropsWithChildren } from 'react'; import { useState } from 'react'; +import { useMutation } from '../../../../../hooks/use-mutation'; import { ConfirmLoadingModal, DowngradeModal } from './modals'; /** diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/index.tsx index ff6f8555ba..a4c4958378 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plans/index.tsx @@ -7,13 +7,13 @@ import { } from '@affine/graphql'; import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { useQuery } from '@affine/workspace/affine/gql'; import { useSetAtom } from 'jotai'; import { Suspense, useEffect, useRef, useState } from 'react'; import type { FallbackProps } from 'react-error-boundary'; import { SWRErrorBoundary } from '../../../../../components/pure/swr-error-bundary'; import { useCurrentLoginStatus } from '../../../../../hooks/affine/use-current-login-status'; +import { useQuery } from '../../../../../hooks/use-query'; import { useUserSubscription } from '../../../../../hooks/use-subscription'; import { PlanLayout } from './layout'; import { type FixedPrice, getPlanDetail, PlanCard } from './plan-card'; 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 a160b5da35..440f49b74e 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,5 +1,6 @@ import { Button } from '@affine/component/ui/button'; import { Tooltip } from '@affine/component/ui/tooltip'; +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import type { Subscription, SubscriptionMutator, @@ -13,9 +14,7 @@ import { } from '@affine/graphql'; import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { useMutation } from '@affine/workspace/affine/gql'; import { DoneIcon } from '@blocksuite/icons'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { useSetAtom } from 'jotai'; import { useAtom } from 'jotai'; import { nanoid } from 'nanoid'; @@ -31,6 +30,7 @@ import { import { openPaymentDisableAtom } from '../../../../../atoms'; import { authAtom } from '../../../../../atoms/index'; import { useCurrentLoginStatus } from '../../../../../hooks/affine/use-current-login-status'; +import { useMutation } from '../../../../../hooks/use-mutation'; import { CancelAction, ResumeAction } from './actions'; import { BulledListIcon } from './icons/bulled-list'; import { ConfirmLoadingModal } from './modals'; diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plugins/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plugins/index.tsx deleted file mode 100644 index 02d29e954f..0000000000 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plugins/index.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { Switch } from '@affine/component'; -import { SettingHeader } from '@affine/component/setting-components'; -import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import type { CallbackMap } from '@affine/sdk/entry'; -import { - addCleanup, - enabledPluginAtom, - pluginPackageJson, - pluginSettingAtom, -} from '@toeverything/infra/__internal__/plugin'; -import { loadedPluginNameAtom } from '@toeverything/infra/atom'; -import type { packageJsonOutputSchema } from '@toeverything/infra/type'; -import { useAtom, useAtomValue } from 'jotai/react'; -import { startTransition, useCallback, useMemo } from 'react'; -import type { z } from 'zod'; - -import { pluginItemStyle } from './style.css'; - -type PluginItemProps = { - json: z.infer; -}; - -type PluginSettingDetailProps = { - pluginName: string; - create: CallbackMap['setting']; -}; - -const PluginSettingDetail = ({ - pluginName, - create, -}: PluginSettingDetailProps) => { - return ( -
{ - if (ref) { - const cleanup = create(ref); - addCleanup(pluginName, cleanup); - } - }, - [pluginName, create] - )} - /> - ); -}; - -const PluginItem = ({ json }: PluginItemProps) => { - const [plugins, setEnabledPlugins] = useAtom(enabledPluginAtom); - const checked = useMemo( - () => plugins.includes(json.name), - [json.name, plugins] - ); - const create = useAtomValue(pluginSettingAtom)[json.name]; - return ( -
-
- {json.name} - { - startTransition(() => { - setEnabledPlugins(plugins => { - if (checked) { - return [...plugins, json.name]; - } else { - return plugins.filter(plugin => plugin !== json.name); - } - }); - }); - }, - [json.name, setEnabledPlugins] - )} - /> -
-
{json.description}
- {create && } -
- ); -}; - -export const Plugins = () => { - const t = useAFFiNEI18N(); - const loadedPlugins = useAtomValue(loadedPluginNameAtom); - return ( - <> - - {useAtomValue(pluginPackageJson).map(json => ( - - ))} - - ); -}; diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plugins/style.css.ts b/packages/frontend/core/src/components/affine/setting-modal/general-setting/plugins/style.css.ts deleted file mode 100644 index 0f122181e4..0000000000 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/plugins/style.css.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { style } from '@vanilla-extract/css'; - -export const settingWrapperStyle = style({ - flexGrow: 1, - display: 'flex', - justifyContent: 'flex-end', - minWidth: '150px', - maxWidth: '250px', -}); - -export const pluginItemStyle = style({ - borderBottom: '1px solid var(--affine-border-color)', - transition: '0.3s', - padding: '24px 8px', - fontSize: 'var(--affine-font-sm)', -}); 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 8c0c3a8735..d29069803e 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 @@ -4,16 +4,16 @@ import { } from '@affine/component/setting-components'; import { Avatar } from '@affine/component/ui/avatar'; import { Tooltip } from '@affine/component/ui/tooltip'; -import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant'; -import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import type { WorkspaceMetadata } from '@affine/workspace'; +import { useWorkspaceBlobObjectUrl } from '@affine/core/hooks/use-workspace-blob'; +import { useWorkspaceInfo } from '@affine/core/hooks/use-workspace-info'; import { waitForCurrentWorkspaceAtom, workspaceListAtom, -} from '@affine/workspace/atom'; +} from '@affine/core/modules/workspace'; +import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import type { WorkspaceMetadata } from '@affine/workspace'; import { Logo1Icon } from '@blocksuite/icons'; -import { useWorkspaceBlobObjectUrl } from '@toeverything/hooks/use-workspace-blob'; -import { useWorkspaceInfo } from '@toeverything/hooks/use-workspace-info'; import clsx from 'clsx'; import { useAtom, useAtomValue } from 'jotai/react'; import { type ReactElement, Suspense, useCallback } from 'react'; @@ -127,9 +127,6 @@ export const SettingSidebar = ({
{generalSettingList.map(({ title, icon, key, testId }) => { - if (!runtimeConfig.enablePlugin && key === 'plugins') { - return null; - } return (
{ const isOwner = useIsWorkspaceOwner(workspaceMetadata); return ( - 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/setting-modal/workspace-setting/new-workspace-setting-detail/delete-leave-workspace/delete/index.tsx similarity index 97% rename from packages/frontend/core/src/components/affine/new-workspace-setting-detail/delete-leave-workspace/delete/index.tsx rename to packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/delete-leave-workspace/delete/index.tsx index 0a0239f71c..a07ee40be9 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/setting-modal/workspace-setting/new-workspace-setting-detail/delete-leave-workspace/delete/index.tsx @@ -3,12 +3,12 @@ import { ConfirmModal, type ConfirmModalProps, } from '@affine/component/ui/modal'; +import { useWorkspaceInfo } from '@affine/core/hooks/use-workspace-info'; import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import type { WorkspaceMetadata } from '@affine/workspace/metadata'; -import { useWorkspaceInfo } from '@toeverything/hooks/use-workspace-info'; import { useCallback, useState } from 'react'; import * as styles from './style.css'; diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/delete-leave-workspace/delete/style.css.ts b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/delete-leave-workspace/delete/style.css.ts similarity index 100% rename from packages/frontend/core/src/components/affine/new-workspace-setting-detail/delete-leave-workspace/delete/style.css.ts rename to packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/delete-leave-workspace/delete/style.css.ts 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/setting-modal/workspace-setting/new-workspace-setting-detail/delete-leave-workspace/index.tsx similarity index 94% rename from packages/frontend/core/src/components/affine/new-workspace-setting-detail/delete-leave-workspace/index.tsx rename to packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/delete-leave-workspace/index.tsx index 38d974b6b2..dd3964bdca 100644 --- a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/delete-leave-workspace/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/delete-leave-workspace/index.tsx @@ -1,23 +1,23 @@ import { pushNotificationAtom } from '@affine/component/notification-center'; import { SettingRow } from '@affine/component/setting-components'; import { ConfirmModal } from '@affine/component/ui/modal'; -import { WorkspaceSubPath } from '@affine/env/workspace'; -import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { currentWorkspaceAtom, workspaceListAtom, workspaceManagerAtom, -} from '@affine/workspace/atom'; +} from '@affine/core/modules/workspace'; +import { WorkspaceSubPath } from '@affine/env/workspace'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { ArrowRightSmallIcon } from '@blocksuite/icons'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { useAtomValue, useSetAtom } from 'jotai'; import { useCallback, useState } from 'react'; -import { openSettingModalAtom } from '../../../../atoms'; +import { openSettingModalAtom } from '../../../../../../atoms'; import { RouteLogic, useNavigateHelper, -} from '../../../../hooks/use-navigate-helper'; +} from '../../../../../../hooks/use-navigate-helper'; import type { WorkspaceSettingDetailProps } from '../types'; import { WorkspaceDeleteModal } from './delete'; diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/enable-cloud.tsx b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/enable-cloud.tsx similarity index 82% rename from packages/frontend/core/src/components/affine/new-workspace-setting-detail/enable-cloud.tsx rename to packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/enable-cloud.tsx index de246466c2..938eabdc80 100644 --- a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/enable-cloud.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/enable-cloud.tsx @@ -1,19 +1,19 @@ import { SettingRow } from '@affine/component/setting-components'; import { Button } from '@affine/component/ui/button'; +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; +import { useWorkspaceInfo } from '@affine/core/hooks/use-workspace-info'; +import { workspaceManagerAtom } from '@affine/core/modules/workspace'; import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant'; import { WorkspaceFlavour, WorkspaceSubPath } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import type { Workspace } from '@affine/workspace'; -import { workspaceManagerAtom } from '@affine/workspace/atom'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; -import { useWorkspaceInfo } from '@toeverything/hooks/use-workspace-info'; import { useAtomValue, useSetAtom } from 'jotai'; import { useState } from 'react'; -import { openSettingModalAtom } from '../../../atoms'; -import { useNavigateHelper } from '../../../hooks/use-navigate-helper'; -import { EnableAffineCloudModal } from '../enable-affine-cloud-modal'; -import { TmpDisableAffineCloudModal } from '../tmp-disable-affine-cloud-modal'; +import { openSettingModalAtom } from '../../../../../atoms'; +import { useNavigateHelper } from '../../../../../hooks/use-navigate-helper'; +import { EnableAffineCloudModal } from '../../../enable-affine-cloud-modal'; +import { TmpDisableAffineCloudModal } from '../../../tmp-disable-affine-cloud-modal'; import type { WorkspaceSettingDetailProps } from './types'; export interface PublishPanelProps extends WorkspaceSettingDetailProps { diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/export.tsx b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/export.tsx similarity index 87% rename from packages/frontend/core/src/components/affine/new-workspace-setting-detail/export.tsx rename to packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/export.tsx index 36ba065f2b..691ed8244c 100644 --- a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/export.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/export.tsx @@ -1,10 +1,10 @@ import { pushNotificationAtom } from '@affine/component/notification-center'; import { SettingRow } from '@affine/component/setting-components'; import { Button } from '@affine/component/ui/button'; +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; +import { apis } from '@affine/electron-api'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import type { Workspace, WorkspaceMetadata } from '@affine/workspace'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; -import type { SaveDBFileResult } from '@toeverything/infra/type'; import { useSetAtom } from 'jotai'; import { useState } from 'react'; @@ -30,8 +30,7 @@ export const ExportPanel = ({ try { await workspace.engine.sync.waitForSynced(); await workspace.engine.blob.sync(); - const result: SaveDBFileResult = - await window.apis?.dialog.saveDBFileAs(workspaceId); + const result = await apis?.dialog.saveDBFileAs(workspaceId); if (result?.error) { throw new Error(result.error); } else if (!result?.canceled) { diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/index.tsx similarity index 91% rename from packages/frontend/core/src/components/affine/new-workspace-setting-detail/index.tsx rename to packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/index.tsx index 6a1acc2299..8e9708f18f 100644 --- a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/index.tsx @@ -3,12 +3,12 @@ import { SettingRow, SettingWrapper, } from '@affine/component/setting-components'; +import { useSelfHosted } from '@affine/core/hooks/affine/use-server-config'; +import { useWorkspace } from '@affine/core/hooks/use-workspace'; +import { useWorkspaceInfo } from '@affine/core/hooks/use-workspace-info'; import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { useWorkspace } from '@toeverything/hooks/use-workspace'; -import { useWorkspaceInfo } from '@toeverything/hooks/use-workspace-info'; -import { useSelfHosted } from '../../../hooks/affine/use-server-config'; import { DeleteLeaveWorkspace } from './delete-leave-workspace'; import { EnableCloudPanel } from './enable-cloud'; import { ExportPanel } from './export'; diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/labels.tsx b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/labels.tsx similarity index 100% rename from packages/frontend/core/src/components/affine/new-workspace-setting-detail/labels.tsx rename to packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/labels.tsx diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/members.tsx b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/members.tsx similarity index 80% rename from packages/frontend/core/src/components/affine/new-workspace-setting-detail/members.tsx rename to packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/members.tsx index 95f8081681..afef7cb2e0 100644 --- a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/members.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/members.tsx @@ -1,6 +1,7 @@ import { InviteModal, type InviteModalProps, + MemberLimitModal, } from '@affine/component/member-components'; import { Pagination, @@ -13,8 +14,18 @@ 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 { openSettingModalAtom } from '@affine/core/atoms'; +import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary'; +import type { CheckedUser } from '@affine/core/hooks/affine/use-current-user'; +import { useCurrentUser } from '@affine/core/hooks/affine/use-current-user'; +import { useInviteMember } from '@affine/core/hooks/affine/use-invite-member'; +import { useMemberCount } from '@affine/core/hooks/affine/use-member-count'; +import { type Member, useMembers } from '@affine/core/hooks/affine/use-members'; +import { useRevokeMemberPermission } from '@affine/core/hooks/affine/use-revoke-member-permission'; +import { useUserQuota } from '@affine/core/hooks/use-quota'; +import { useUserSubscription } from '@affine/core/hooks/use-subscription'; import { WorkspaceFlavour } from '@affine/env/workspace'; -import { Permission } from '@affine/graphql'; +import { Permission, SubscriptionPlan } from '@affine/graphql'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { ArrowRightBigIcon, MoreVerticalIcon } from '@blocksuite/icons'; import clsx from 'clsx'; @@ -29,15 +40,6 @@ import { useState, } from 'react'; -import { openSettingModalAtom } from '../../../atoms'; -import type { CheckedUser } from '../../../hooks/affine/use-current-user'; -import { useCurrentUser } from '../../../hooks/affine/use-current-user'; -import { useInviteMember } from '../../../hooks/affine/use-invite-member'; -import { useMemberCount } from '../../../hooks/affine/use-member-count'; -import { type Member, useMembers } from '../../../hooks/affine/use-members'; -import { useRevokeMemberPermission } from '../../../hooks/affine/use-revoke-member-permission'; -import { useUserQuota } from '../../../hooks/use-quota'; -import { AffineErrorBoundary } from '../affine-error-boundary'; import * as style from './style.css'; import type { WorkspaceSettingDetailProps } from './types'; @@ -67,6 +69,19 @@ export const CloudWorkspaceMembersPanel = ({ const workspaceId = workspaceMetadata.id; const memberCount = useMemberCount(workspaceId); + const checkMemberCountLimit = useCallback( + (memberCount: number, memberLimit?: number) => { + if (memberLimit === undefined) return false; + return memberCount >= memberLimit; + }, + [] + ); + + const quota = useUserQuota(); + const [subscription] = useUserSubscription(); + const plan = subscription?.plan ?? SubscriptionPlan.Free; + const isLimited = checkMemberCountLimit(memberCount, quota?.memberLimit); + const t = useAFFiNEI18N(); const { invite, isMutating } = useInviteMember(workspaceId); const revokeMemberPermission = useRevokeMemberPermission(workspaceId); @@ -104,6 +119,14 @@ export const CloudWorkspaceMembersPanel = ({ [invite, pushNotification, t] ); + const setSettingModalAtom = useSetAtom(openSettingModalAtom); + const handleUpgradeConfirm = useCallback(() => { + setSettingModalAtom({ + open: true, + activeTab: 'plans', + }); + }, [setSettingModalAtom]); + const listContainerRef = useRef(null); const [memberListHeight, setMemberListHeight] = useState(null); @@ -131,16 +154,6 @@ export const CloudWorkspaceMembersPanel = ({ [pushNotification, revokeMemberPermission, t] ); - const setSettingModalAtom = useSetAtom(openSettingModalAtom); - const handleUpgrade = useCallback(() => { - setSettingModalAtom({ - open: true, - activeTab: 'plans', - }); - }, [setSettingModalAtom]); - - const quota = useUserQuota(); - const desc = useMemo(() => { if (!quota) return null; @@ -154,7 +167,10 @@ export const CloudWorkspaceMembersPanel = ({ {upgradable ? ( <> , -
+
{t['com.affine.payment.member.description.go-upgrade']()} @@ -164,7 +180,7 @@ export const CloudWorkspaceMembersPanel = ({ ) : null} ); - }, [handleUpgrade, quota, t, upgradable]); + }, [handleUpgradeConfirm, quota, t, upgradable]); return ( <> @@ -176,12 +192,23 @@ export const CloudWorkspaceMembersPanel = ({ {isOwner ? ( <> - + {isLimited ? ( + + ) : ( + + )} ) : null} diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/profile.tsx b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/profile.tsx similarity index 92% rename from packages/frontend/core/src/components/affine/new-workspace-setting-detail/profile.tsx rename to packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/profile.tsx index f9a7629cce..78aa8c3e82 100644 --- a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/profile.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/profile.tsx @@ -2,14 +2,16 @@ 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 { Upload } from '@affine/core/components/pure/file-upload'; +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; +import { useWorkspaceBlobObjectUrl } from '@affine/core/hooks/use-workspace-blob'; +import { useWorkspaceStatus } from '@affine/core/hooks/use-workspace-status'; +import { validateAndReduceImage } from '@affine/core/utils/reduce-image'; import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import type { Workspace } from '@affine/workspace'; import { SyncPeerStep } from '@affine/workspace'; import { CameraIcon } from '@blocksuite/icons'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; -import { useWorkspaceBlobObjectUrl } from '@toeverything/hooks/use-workspace-blob'; -import { useWorkspaceStatus } from '@toeverything/hooks/use-workspace-status'; import { useSetAtom } from 'jotai'; import { type KeyboardEvent, @@ -19,9 +21,7 @@ import { useState, } from 'react'; -import { validateAndReduceImage } from '../../../utils/reduce-image'; -import { Upload } from '../../pure/file-upload'; -import * as styles from './style.css'; +import * as style from './style.css'; import type { WorkspaceSettingDetailProps } from './types'; export interface ProfilePanelProps extends WorkspaceSettingDetailProps { @@ -165,7 +165,7 @@ export const ProfilePanel = ({ isOwner, workspace }: ProfilePanelProps) => { const canAdjustAvatar = !workspaceIsLoading && avatarUrl && isOwner; return ( -
+
{ -
{t['Workspace Name']()}
+
{t['Workspace Name']()}
{ const [path, setPath] = useState(undefined); useEffect(() => { - if (window.apis && window.events && environment.isDesktop) { - window.apis?.workspace + if (apis && events && environment.isDesktop) { + apis?.workspace .getMeta(workspaceId) .then(meta => { setPath(meta.secondaryDBPath); @@ -20,7 +20,7 @@ const useDBFileSecondaryPath = (workspaceId: string) => { .catch(err => { console.error(err); }); - return window.events.workspace.onMetaChange((newMeta: any) => { + return events.workspace.onMetaChange((newMeta: any) => { if (newMeta.workspaceId === workspaceId) { const meta = newMeta.meta; setPath(meta.secondaryDBPath); @@ -43,7 +43,7 @@ export const StoragePanel = ({ workspaceMetadata }: StoragePanelProps) => { const [moveToInProgress, setMoveToInProgress] = useState(false); const onRevealDBFile = useCallback(() => { - window.apis?.dialog.revealDBFile(workspaceId).catch(err => { + apis?.dialog.revealDBFile(workspaceId).catch(err => { console.error(err); }); }, [workspaceId]); @@ -53,9 +53,9 @@ export const StoragePanel = ({ workspaceMetadata }: StoragePanelProps) => { return; } setMoveToInProgress(true); - window.apis?.dialog + apis?.dialog .moveDBFile(workspaceId) - .then((result: MoveDBFileResult) => { + .then(result => { if (!result?.error && !result?.canceled) { toast(t['Move folder success']()); } else if (result?.error) { diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/style.css.ts b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/style.css.ts similarity index 100% rename from packages/frontend/core/src/components/affine/new-workspace-setting-detail/style.css.ts rename to packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/style.css.ts diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/types.ts b/packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/types.ts similarity index 100% rename from packages/frontend/core/src/components/affine/new-workspace-setting-detail/types.ts rename to packages/frontend/core/src/components/affine/setting-modal/workspace-setting/new-workspace-setting-detail/types.ts 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 f957f48567..2a7cca033e 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 @@ -1,8 +1,8 @@ +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; +import { workspaceManagerAtom } from '@affine/core/modules/workspace'; import { WorkspaceFlavour } from '@affine/env/workspace'; import type { Workspace } from '@affine/workspace'; -import { workspaceManagerAtom } from '@affine/workspace/atom'; import type { Page } from '@blocksuite/store'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { useAtomValue } from 'jotai'; import { useState } from 'react'; diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-header-title/index.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-header-title/index.tsx index a356605640..15d71090a6 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-header-title/index.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-header-title/index.tsx @@ -1,8 +1,8 @@ -import type { Workspace as BlockSuiteWorkspace } from '@blocksuite/store'; import { useBlockSuitePageMeta, usePageMetaHelper, -} from '@toeverything/hooks/use-block-suite-page-meta'; +} from '@affine/core/hooks/use-block-suite-page-meta'; +import type { Workspace as BlockSuiteWorkspace } from '@blocksuite/store'; import { type FocusEvent, type InputHTMLAttributes, diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-header-title/operation-menu.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-header-title/operation-menu.tsx index 7bb2979a48..dac01089c6 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-header-title/operation-menu.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-header-title/operation-menu.tsx @@ -5,9 +5,10 @@ import { MenuItem, MenuSeparator, } from '@affine/component/ui/menu'; +import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta'; +import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { waitForCurrentWorkspaceAtom } from '@affine/workspace/atom'; import { assertExists } from '@blocksuite/global/utils'; import { DuplicateIcon, @@ -19,7 +20,6 @@ import { ImportIcon, PageIcon, } from '@blocksuite/icons'; -import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useAtomValue } from 'jotai'; import { useCallback, useState } from 'react'; diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/index.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/index.tsx index 255880670a..dd6f3158f3 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/index.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/index.tsx @@ -1,6 +1,6 @@ import { Tooltip } from '@affine/component/ui/tooltip'; +import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useAtomValue } from 'jotai'; import type { CSSProperties } from 'react'; import { useCallback, useEffect } from 'react'; diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-page-list/utils.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-page-list/utils.tsx index 66ace961b1..e3ac93c4aa 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-page-list/utils.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-page-list/utils.tsx @@ -1,8 +1,8 @@ import { toast } from '@affine/component'; +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; +import { usePageMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta'; +import { useBlockSuiteWorkspaceHelper } from '@affine/core/hooks/use-block-suite-workspace-helper'; import { WorkspaceSubPath } from '@affine/env/workspace'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; -import { usePageMetaHelper } from '@toeverything/hooks/use-block-suite-page-meta'; -import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper'; import { initEmptyPage } from '@toeverything/infra/blocksuite'; import { useAtomValue, useSetAtom } from 'jotai'; import { useCallback, useMemo } from 'react'; diff --git a/packages/frontend/core/src/components/cloud/login-card.tsx b/packages/frontend/core/src/components/cloud/login-card.tsx deleted file mode 100644 index a871ee3396..0000000000 --- a/packages/frontend/core/src/components/cloud/login-card.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { Avatar } from '@affine/component/ui/avatar'; -import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { CloudWorkspaceIcon } from '@blocksuite/icons'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; - -import { useCurrentLoginStatus } from '../../hooks/affine/use-current-login-status'; -import { useCurrentUser } from '../../hooks/affine/use-current-user'; -import { signInCloud } from '../../utils/cloud-utils'; -import { StyledSignInButton } from '../pure/footer/styles'; - -export const LoginCard = () => { - const t = useAFFiNEI18N(); - const loginStatus = useCurrentLoginStatus(); - - const onSignInClick = useAsyncCallback(async () => { - await signInCloud(); - }, []); - - if (loginStatus === 'authenticated') { - return ; - } - return ( - -
- -
{' '} - {t['Sign in']()} -
- ); -}; - -const UserCard = () => { - const user = useCurrentUser(); - return ( -
- -
-
{user.name}
-
{user.email}
-
-
- ); -}; diff --git a/packages/frontend/core/src/components/cloud/share-header-left-item/user-avatar.tsx b/packages/frontend/core/src/components/cloud/share-header-left-item/user-avatar.tsx index e63da0f7ee..7b6716aae2 100644 --- a/packages/frontend/core/src/components/cloud/share-header-left-item/user-avatar.tsx +++ b/packages/frontend/core/src/components/cloud/share-header-left-item/user-avatar.tsx @@ -1,8 +1,8 @@ import { Avatar } from '@affine/component/ui/avatar'; import { Menu, MenuIcon, MenuItem } from '@affine/component/ui/menu'; +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { SignOutIcon } from '@blocksuite/icons'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { useMemo } from 'react'; import { useLocation } from 'react-router-dom'; diff --git a/packages/plugins/image-preview/src/component/hooks/use-zoom.tsx b/packages/frontend/core/src/components/image-preview/hooks/use-zoom.tsx similarity index 100% rename from packages/plugins/image-preview/src/component/hooks/use-zoom.tsx rename to packages/frontend/core/src/components/image-preview/hooks/use-zoom.tsx diff --git a/packages/plugins/image-preview/src/component/index.css.ts b/packages/frontend/core/src/components/image-preview/index.css.ts similarity index 100% rename from packages/plugins/image-preview/src/component/index.css.ts rename to packages/frontend/core/src/components/image-preview/index.css.ts diff --git a/packages/plugins/image-preview/src/component/index.jotai.ts b/packages/frontend/core/src/components/image-preview/index.jotai.ts similarity index 100% rename from packages/plugins/image-preview/src/component/index.jotai.ts rename to packages/frontend/core/src/components/image-preview/index.jotai.ts diff --git a/packages/plugins/image-preview/src/component/index.tsx b/packages/frontend/core/src/components/image-preview/index.tsx similarity index 98% rename from packages/plugins/image-preview/src/component/index.tsx rename to packages/frontend/core/src/components/image-preview/index.tsx index c34f0804cc..bc81976909 100644 --- a/packages/plugins/image-preview/src/component/index.tsx +++ b/packages/frontend/core/src/components/image-preview/index.tsx @@ -1,3 +1,4 @@ +import { toast } from '@affine/component'; import { Button, IconButton } from '@affine/component/ui/button'; import { Tooltip } from '@affine/component/ui/tooltip'; import type { ImageBlockModel } from '@blocksuite/blocks'; @@ -19,6 +20,7 @@ import { useAtom } from 'jotai'; import type { PropsWithChildren, ReactElement } from 'react'; import { Suspense, useCallback } from 'react'; import { useEffect, useRef, useState } from 'react'; +import ReactDOM from 'react-dom'; import type { FallbackProps } from 'react-error-boundary'; import { ErrorBoundary } from 'react-error-boundary'; import useSWR from 'swr'; @@ -41,7 +43,6 @@ import { unloaded, } from './index.css'; import { hasAnimationPlayedAtom, previewBlockIdAtom } from './index.jotai'; -import { toast } from './toast'; export type ImagePreviewModalProps = { workspace: Workspace; @@ -440,7 +441,7 @@ const ImagePreviewModalImpl = ( return; } const dataUrl = URL.createObjectURL(blob); - global.navigator.clipboard + navigator.clipboard .write([new ClipboardItem({ 'image/png': blob })]) .then(() => { console.log('Image copied to clipboard'); @@ -554,7 +555,7 @@ export const ImagePreviewModal = ( return null; } - return ( + return ReactDOM.createPortal(
-
+ , + document.body ); }; 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 ec702977ba..aebb2b74f6 100644 --- a/packages/frontend/core/src/components/page-detail-editor.css.ts +++ b/packages/frontend/core/src/components/page-detail-editor.css.ts @@ -20,6 +20,6 @@ globalStyle(`${editor} .affine-doc-viewport`, { paddingBottom: '150px', }); -globalStyle('.is-public-page affine-page-meta-data', { +globalStyle('.is-public-page page-meta-tags', { display: 'none', }); diff --git a/packages/frontend/core/src/components/page-detail-editor.tsx b/packages/frontend/core/src/components/page-detail-editor.tsx index 00a9a5538d..21b67a2854 100644 --- a/packages/frontend/core/src/components/page-detail-editor.tsx +++ b/packages/frontend/core/src/components/page-detail-editor.tsx @@ -1,16 +1,15 @@ import './page-detail-editor.css'; +import { useActiveBlocksuiteEditor } from '@affine/core/hooks/use-block-suite-editor'; +import { useBlockSuiteWorkspacePage } from '@affine/core/hooks/use-block-suite-workspace-page'; import { assertExists, DisposableGroup } from '@blocksuite/global/utils'; import type { AffineEditorContainer } from '@blocksuite/presets'; import type { Page, Workspace } from '@blocksuite/store'; -import { useBlockSuiteWorkspacePage } from '@toeverything/hooks/use-block-suite-workspace-page'; -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 } from 'jotai'; import type { CSSProperties } from 'react'; -import { memo, Suspense, useCallback, useMemo, useState } from 'react'; +import { memo, Suspense, useCallback, useMemo } from 'react'; import { useLocation } from 'react-router-dom'; import { type PageMode, pageSettingFamily } from '../atoms'; @@ -89,14 +88,14 @@ const PageDetailEditorMain = memo(function PageDetailEditorMain({ [isPublic, switchToEdgelessMode, pageId, switchToPageMode] ); - const [, setEditor] = useState(); + const [, setActiveBlocksuiteEditor] = useActiveBlocksuiteEditor(); const blockId = useRouterHash(); const onLoadEditor = useCallback( (editor: AffineEditorContainer) => { // debug current detail editor globalThis.currentEditor = editor; - setEditor(editor); + setActiveBlocksuiteEditor(editor); const disposableGroup = new DisposableGroup(); disposableGroup.add( page.slots.blockUpdated.once(() => { @@ -110,34 +109,11 @@ const PageDetailEditorMain = memo(function PageDetailEditorMain({ 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)[] = []; - const renderTimeout = window.setTimeout(() => { - disposes = Object.entries(editorItems).map(([id, editorItem]) => { - const div = document.createElement('div'); - div.setAttribute('plugin-id', id); - const cleanup = editorItem(div, editor); - assertExists(parent); - document.body.append(div); - return () => { - cleanup(); - div.remove(); - }; - }); - }); - return () => { disposableGroup.dispose(); - clearTimeout(renderTimeout); - window.setTimeout(() => { - disposes.forEach(dispose => dispose()); - }); }; }, - [onLoad, page] + [onLoad, page, setActiveBlocksuiteEditor] ); return ( diff --git a/packages/frontend/core/src/components/pure/cmdk/data.tsx b/packages/frontend/core/src/components/pure/cmdk/data.tsx index f3f0003d53..d37d08aeb7 100644 --- a/packages/frontend/core/src/components/pure/cmdk/data.tsx +++ b/packages/frontend/core/src/components/pure/cmdk/data.tsx @@ -1,18 +1,18 @@ import { commandScore } from '@affine/cmdk'; import { useCollectionManager } from '@affine/component/page-list'; -import type { Collection } from '@affine/env/filter'; -import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { - currentWorkspaceAtom, - waitForCurrentWorkspaceAtom, -} from '@affine/workspace/atom'; -import { EdgelessIcon, PageIcon, ViewLayersIcon } from '@blocksuite/icons'; -import type { Page, PageMeta } from '@blocksuite/store'; import { useBlockSuitePageMeta, usePageMetaHelper, -} from '@toeverything/hooks/use-block-suite-page-meta'; -import { currentPageIdAtom, getCurrentStore } from '@toeverything/infra/atom'; +} from '@affine/core/hooks/use-block-suite-page-meta'; +import { + currentWorkspaceAtom, + waitForCurrentWorkspaceAtom, +} from '@affine/core/modules/workspace'; +import type { Collection } from '@affine/env/filter'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { EdgelessIcon, PageIcon, ViewLayersIcon } from '@blocksuite/icons'; +import type { Page, PageMeta } from '@blocksuite/store'; +import { getCurrentStore } from '@toeverything/infra/atom'; import { type AffineCommand, AffineCommandRegistry, @@ -29,6 +29,7 @@ import { recentPageIdsBaseAtom, } from '../../../atoms'; import { collectionsCRUDAtom } from '../../../atoms/collections'; +import { currentPageIdAtom } from '../../../atoms/mode'; import { useNavigateHelper } from '../../../hooks/use-navigate-helper'; import { WorkspaceSubPath } from '../../../shared'; import { usePageHelper } from '../../blocksuite/block-suite-page-list/utils'; diff --git a/packages/frontend/core/src/components/pure/cmdk/main.tsx b/packages/frontend/core/src/components/pure/cmdk/main.tsx index f91e57849f..ced65792eb 100644 --- a/packages/frontend/core/src/components/pure/cmdk/main.tsx +++ b/packages/frontend/core/src/components/pure/cmdk/main.tsx @@ -1,9 +1,9 @@ import { Command } from '@affine/cmdk'; import { useCommandState } from '@affine/cmdk'; import { formatDate } from '@affine/component/page-list'; +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import type { PageMeta } from '@blocksuite/store'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import type { CommandCategory } from '@toeverything/infra/command'; import clsx from 'clsx'; import { useAtom, useAtomValue } from 'jotai'; diff --git a/packages/frontend/core/src/components/pure/header/index.tsx b/packages/frontend/core/src/components/pure/header/index.tsx index 4856a33975..b3478a9905 100644 --- a/packages/frontend/core/src/components/pure/header/index.tsx +++ b/packages/frontend/core/src/components/pure/header/index.tsx @@ -3,7 +3,7 @@ import { appSidebarOpenAtom, SidebarSwitch, } from '@affine/component/app-sidebar'; -import { useIsTinyScreen } from '@toeverything/hooks/use-is-tiny-screen'; +import { useIsTinyScreen } from '@affine/core/hooks/use-is-tiny-screen'; import clsx from 'clsx'; import { useAtomValue } from 'jotai'; import type { ReactNode } from 'react'; diff --git a/packages/frontend/core/src/components/pure/header/windows-app-controls.tsx b/packages/frontend/core/src/components/pure/header/windows-app-controls.tsx index 3e6ecaa4d0..b1dd042611 100644 --- a/packages/frontend/core/src/components/pure/header/windows-app-controls.tsx +++ b/packages/frontend/core/src/components/pure/header/windows-app-controls.tsx @@ -1,3 +1,4 @@ +import { apis, events } from '@affine/electron-api'; import { useAtomValue } from 'jotai'; import { atomWithObservable } from 'jotai/utils'; import { useCallback } from 'react'; @@ -8,7 +9,7 @@ import * as style from './style.css'; const maximizedAtom = atomWithObservable(() => { return new Observable(subscriber => { subscriber.next(false); - return window.events?.ui.onMaximized(maximized => { + return events?.ui.onMaximized(maximized => { return subscriber.next(maximized); }); }); @@ -76,17 +77,17 @@ const unmaximizedSVG = ( export const WindowsAppControls = () => { const handleMinimizeApp = useCallback(() => { - window.apis?.ui.handleMinimizeApp().catch(err => { + apis?.ui.handleMinimizeApp().catch(err => { console.error(err); }); }, []); const handleMaximizeApp = useCallback(() => { - window.apis?.ui.handleMaximizeApp().catch(err => { + apis?.ui.handleMaximizeApp().catch(err => { console.error(err); }); }, []); const handleCloseApp = useCallback(() => { - window.apis?.ui.handleCloseApp().catch(err => { + apis?.ui.handleCloseApp().catch(err => { console.error(err); }); }, []); diff --git a/packages/frontend/core/src/components/pure/plugin-header/index.tsx b/packages/frontend/core/src/components/pure/plugin-header/index.tsx deleted file mode 100644 index 6156691546..0000000000 --- a/packages/frontend/core/src/components/pure/plugin-header/index.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { - addCleanup, - pluginHeaderItemAtom, -} from '@toeverything/infra/__internal__/plugin'; -import { useAtomValue } from 'jotai'; -import { startTransition, useCallback, useRef } from 'react'; - -import * as styles from './styles.css'; -export const PluginHeader = () => { - const headerItem = useAtomValue(pluginHeaderItemAtom); - const pluginsRef = useRef([]); - - return ( -
{ - if (root) { - Object.entries(headerItem).forEach(([pluginName, create]) => { - if (pluginsRef.current.includes(pluginName)) { - return; - } - pluginsRef.current.push(pluginName); - const div = document.createElement('div'); - div.setAttribute('plugin-id', pluginName); - startTransition(() => { - const cleanup = create(div); - root.append(div); - addCleanup(pluginName, () => { - pluginsRef.current = pluginsRef.current.filter( - name => name !== pluginName - ); - div.remove(); - cleanup(); - }); - }); - }); - } - }, - [headerItem] - )} - /> - ); -}; diff --git a/packages/frontend/core/src/components/pure/plugin-header/styles.css.ts b/packages/frontend/core/src/components/pure/plugin-header/styles.css.ts deleted file mode 100644 index 254ef6618d..0000000000 --- a/packages/frontend/core/src/components/pure/plugin-header/styles.css.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { style } from '@vanilla-extract/css'; - -export const pluginHeaderItems = style({ - display: 'flex', - gap: '12px', - alignItems: 'center', - height: '100%', -}); 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 index 3828201064..739cc5f635 100644 --- a/packages/frontend/core/src/components/pure/trash-page-footer/index.tsx +++ b/packages/frontend/core/src/components/pure/trash-page-footer/index.tsx @@ -1,12 +1,12 @@ import { Button } from '@affine/component/ui/button'; import { ConfirmModal } from '@affine/component/ui/modal'; import { Tooltip } from '@affine/component/ui/tooltip'; +import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta'; +import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace'; import { WorkspaceSubPath } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { waitForCurrentWorkspaceAtom } from '@affine/workspace/atom'; import { assertExists } from '@blocksuite/global/utils'; import { DeleteIcon, ResetIcon } from '@blocksuite/icons'; -import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useAtomValue } from 'jotai'; import { useCallback, useState } 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 7e4d36607f..5f40e87fe7 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 @@ -9,13 +9,13 @@ import { } from '@affine/component/page-list'; import { RenameModal } from '@affine/component/rename-modal'; import { Button, IconButton } from '@affine/component/ui/button'; +import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta'; import type { Collection, DeleteCollectionInfo } from '@affine/env/filter'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { MoreHorizontalIcon, ViewLayersIcon } from '@blocksuite/icons'; import type { PageMeta, Workspace } from '@blocksuite/store'; import { useDroppable } from '@dnd-kit/core'; import * as Collapsible from '@radix-ui/react-collapsible'; -import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useCallback, useMemo, useState } from 'react'; import { useLocation } from 'react-router-dom'; 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 bf6239301c..169c8762ac 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,10 +1,10 @@ import { MenuItem as CollectionItem } from '@affine/component/app-sidebar'; +import { useBlockSuitePageReferences } from '@affine/core/hooks/use-block-suite-page-references'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { EdgelessIcon, PageIcon } from '@blocksuite/icons'; import type { PageMeta, Workspace } from '@blocksuite/store'; import { useDraggable } from '@dnd-kit/core'; import * as Collapsible from '@radix-ui/react-collapsible'; -import { useBlockSuitePageReferences } from '@toeverything/hooks/use-block-suite-page-references'; import { useAtomValue } from 'jotai/index'; import React, { useCallback, useMemo } from 'react'; import { useParams } from 'react-router-dom'; diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/styles.css.ts b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/styles.css.ts index 567f76f327..0243def103 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/styles.css.ts +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/styles.css.ts @@ -141,7 +141,7 @@ export const emptyCollectionIconWrapper = style({ alignItems: 'center', justifyContent: 'center', borderRadius: '50%', - backgroundColor: 'var(--affine-background-secondary-color)', + backgroundColor: 'var(--affine-hover-color)', }); export const emptyCollectionIcon = style({ @@ -152,7 +152,7 @@ export const emptyCollectionIcon = style({ export const emptyCollectionMessage = style({ fontSize: 'var(--affine-font-sm)', textAlign: 'center', - color: 'var(--affine-text-secondary-color)', + color: 'var(--affine-black-30)', }); export const emptyCollectionNewButton = style({ diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/components/postfix-item.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/components/postfix-item.tsx index fd2ed694c3..9f8198b764 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/components/postfix-item.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/components/postfix-item.tsx @@ -1,8 +1,8 @@ import { toast } from '@affine/component'; import { RenameModal } from '@affine/component/rename-modal'; +import { usePageMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import type { Workspace } from '@blocksuite/store'; -import { usePageMetaHelper } from '@toeverything/hooks/use-block-suite-page-meta'; import { useCallback, useState } from 'react'; import { AddFavouriteButton } from '../favorite/add-favourite-button'; 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 767909bd37..9d7e13756e 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,9 +1,9 @@ import { MenuLinkItem } from '@affine/component/app-sidebar'; +import { useBlockSuitePageReferences } from '@affine/core/hooks/use-block-suite-page-references'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { EdgelessIcon, PageIcon } from '@blocksuite/icons'; 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'; import { useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; 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 931e9c9108..2bebff6464 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,8 +1,8 @@ import { IconButton } from '@affine/component/ui/button'; +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; +import { usePageMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta'; import { PlusIcon } from '@blocksuite/icons'; import type { Workspace } from '@blocksuite/store'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; -import { usePageMetaHelper } from '@toeverything/hooks/use-block-suite-page-meta'; import { usePageHelper } from '../../../blocksuite/block-suite-page-list/utils'; diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favorite-list.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favorite-list.tsx index 6875ffb95f..04e7a3ece0 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favorite-list.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favorite-list.tsx @@ -1,6 +1,6 @@ +import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta'; import type { PageMeta } from '@blocksuite/store'; import { useDroppable } from '@dnd-kit/core'; -import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useMemo } from 'react'; import { getDropItemId } from '../../../../hooks/affine/use-sidebar-drag'; diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favourite-page.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favourite-page.tsx index bc32f778c5..f6b4d6d2a3 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favourite-page.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favourite-page.tsx @@ -1,9 +1,9 @@ import { MenuLinkItem } from '@affine/component/app-sidebar'; +import { useBlockSuitePageReferences } from '@affine/core/hooks/use-block-suite-page-references'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { EdgelessIcon, PageIcon } from '@blocksuite/icons'; import { useDraggable } from '@dnd-kit/core'; import * as Collapsible from '@radix-ui/react-collapsible'; -import { useBlockSuitePageReferences } from '@toeverything/hooks/use-block-suite-page-references'; import { useAtomValue } from 'jotai/index'; import { useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/styles.css.ts b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/styles.css.ts index eacbe22dbb..21b0cf7901 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/styles.css.ts +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/styles.css.ts @@ -159,7 +159,7 @@ export const emptyFavouritesIconWrapper = style({ alignItems: 'center', justifyContent: 'center', borderRadius: '50%', - backgroundColor: 'var(--affine-background-secondary-color)', + backgroundColor: 'var(--affine-hover-color)', }); export const emptyFavouritesIcon = style({ @@ -170,5 +170,5 @@ export const emptyFavouritesIcon = style({ export const emptyFavouritesMessage = style({ fontSize: 'var(--affine-font-sm)', textAlign: 'center', - color: 'var(--affine-text-secondary-color)', + color: 'var(--affine-black-30)', }); diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/index.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/index.tsx index 9d572cead3..e76bf53f15 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/index.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/index.tsx @@ -1,11 +1,11 @@ import { Divider } from '@affine/component/ui/divider'; import { MenuItem } from '@affine/component/ui/menu'; -import { Unreachable } from '@affine/env/constant'; -import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { workspaceListAtom, workspaceManagerAtom, -} from '@affine/workspace/atom'; +} from '@affine/core/modules/workspace'; +import { Unreachable } from '@affine/env/constant'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { Logo1Icon } from '@blocksuite/icons'; import { useAtomValue, useSetAtom } from 'jotai'; // eslint-disable-next-line @typescript-eslint/no-restricted-imports @@ -107,7 +107,7 @@ export const UserWithWorkspaceList = ({ )} - + {workspaces.length > 0 ? : null}
); @@ -83,18 +92,21 @@ const LocalWorkspaces = ({ onClick={onClickWorkspace} onSettingClick={onClickWorkspaceSetting} onDragEnd={onDragEnd} + useIsWorkspaceOwner={useIsWorkspaceOwner} + useWorkspaceName={useWorkspaceName} + useWorkspaceAvatar={useWorkspaceAvatar} />
); }; export const AFFiNEWorkspaceList = ({ - workspaces, onEventEnd, }: { - workspaces: WorkspaceMetadata[]; onEventEnd?: () => void; }) => { + const workspaces = useAtomValue(workspaceListAtom); + const setOpenCreateWorkspaceModal = useSetAtom(openCreateWorkspaceModalAtom); const { jumpToSubPath } = useNavigateHelper(); diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/workspace-card/index.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/workspace-card/index.tsx index ddaa00e087..4f9db66498 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/workspace-card/index.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/workspace-card/index.tsx @@ -1,10 +1,12 @@ import { Avatar } from '@affine/component/ui/avatar'; import { Loading } from '@affine/component/ui/loading'; import { Tooltip } from '@affine/component/ui/tooltip'; +import { useWorkspaceBlobObjectUrl } from '@affine/core/hooks/use-workspace-blob'; +import { useWorkspaceInfo } from '@affine/core/hooks/use-workspace-info'; +import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace'; import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { type SyncEngineStatus, SyncEngineStep } from '@affine/workspace'; -import { waitForCurrentWorkspaceAtom } from '@affine/workspace/atom'; import { CloudWorkspaceIcon, InformationFillDuotoneIcon, @@ -12,8 +14,6 @@ import { NoNetworkIcon, UnsyncIcon, } from '@blocksuite/icons'; -import { useWorkspaceBlobObjectUrl } from '@toeverything/hooks/use-workspace-blob'; -import { useWorkspaceInfo } from '@toeverything/hooks/use-workspace-info'; import { useAtomValue } from 'jotai'; import { debounce, mean } from 'lodash-es'; import { 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 151e3181cd..19dafd5f22 100644 --- a/packages/frontend/core/src/components/root-app-sidebar/index.tsx +++ b/packages/frontend/core/src/components/root-app-sidebar/index.tsx @@ -4,7 +4,6 @@ import { AppDownloadButton, AppSidebar, appSidebarOpenAtom, - AppUpdaterButton, CategoryDivider, MenuItem, MenuLinkItem, @@ -20,13 +19,14 @@ import { } from '@affine/component/page-list'; import { Menu } from '@affine/component/ui/menu'; import { collectionsCRUDAtom } from '@affine/core/atoms/collections'; +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; +import { apis, events } from '@affine/electron-api'; import { WorkspaceSubPath } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import type { Workspace } from '@affine/workspace'; import { FolderIcon, SettingsIcon } from '@blocksuite/icons'; import { type Page } from '@blocksuite/store'; import { useDroppable } from '@dnd-kit/core'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { useAtom, useAtomValue } from 'jotai'; import { nanoid } from 'nanoid'; import type { HTMLAttributes, ReactElement } from 'react'; @@ -48,6 +48,7 @@ import FavoriteList from '../pure/workspace-slider-bar/favorite/favorite-list'; import { UserWithWorkspaceList } from '../pure/workspace-slider-bar/user-with-workspace-list'; import { WorkspaceCard } from '../pure/workspace-slider-bar/workspace-card'; import ImportPage from './import-page'; +import { UpdaterButton } from './updater-button'; export type RootAppSidebarProps = { isPublicWorkspace: boolean; @@ -141,7 +142,7 @@ export const RootAppSidebar = ({ // Listen to the "New Page" action from the menu useEffect(() => { if (environment.isDesktop) { - return window.events?.applicationMenu.onNewPageAction(onClickNewPage); + return events?.applicationMenu.onNewPageAction(onClickNewPage); } return; }, [onClickNewPage]); @@ -149,7 +150,7 @@ export const RootAppSidebar = ({ const sidebarOpen = useAtomValue(appSidebarOpenAtom); useEffect(() => { if (environment.isDesktop) { - window.apis?.ui.handleSidebarVisibilityChange(sidebarOpen).catch(err => { + apis?.ui.handleSidebarVisibilityChange(sidebarOpen).catch(err => { console.error(err); }); } @@ -298,7 +299,7 @@ export const RootAppSidebar = ({ )} - {environment.isDesktop ? : } + {environment.isDesktop ? : }
diff --git a/packages/frontend/core/src/components/root-app-sidebar/updater-button.tsx b/packages/frontend/core/src/components/root-app-sidebar/updater-button.tsx new file mode 100644 index 0000000000..0c70996fe4 --- /dev/null +++ b/packages/frontend/core/src/components/root-app-sidebar/updater-button.tsx @@ -0,0 +1,21 @@ +import { AppUpdaterButton } from '@affine/component/app-sidebar/app-updater-button'; +import { useAppUpdater } from '@affine/core/hooks/use-app-updater'; + +export const UpdaterButton = () => { + const appUpdater = useAppUpdater(); + + return ( + + ); +}; diff --git a/packages/frontend/core/src/components/top-tip.tsx b/packages/frontend/core/src/components/top-tip.tsx index daaa2ecdeb..4caca6b19d 100644 --- a/packages/frontend/core/src/components/top-tip.tsx +++ b/packages/frontend/core/src/components/top-tip.tsx @@ -1,11 +1,11 @@ import { BrowserWarning } from '@affine/component/affine-banner'; import { LocalDemoTips } from '@affine/component/affine-banner'; +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; +import { workspaceManagerAtom } from '@affine/core/modules/workspace'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import type { Workspace } from '@affine/workspace'; -import { workspaceManagerAtom } from '@affine/workspace/atom'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { useAtomValue, useSetAtom } from 'jotai'; import { useCallback, useState } from 'react'; diff --git a/packages/frontend/core/src/components/workspace-upgrade/upgrade.tsx b/packages/frontend/core/src/components/workspace-upgrade/upgrade.tsx index d37051501d..a93fb73dce 100644 --- a/packages/frontend/core/src/components/workspace-upgrade/upgrade.tsx +++ b/packages/frontend/core/src/components/workspace-upgrade/upgrade.tsx @@ -1,17 +1,17 @@ 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 { WorkspaceSubPath } from '@affine/env/workspace'; -import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; +import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper'; +import { useWorkspaceStatus } from '@affine/core/hooks/use-workspace-status'; import { waitForCurrentWorkspaceAtom, workspaceManagerAtom, -} from '@affine/workspace/atom'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; -import { useWorkspaceStatus } from '@toeverything/hooks/use-workspace-status'; +} from '@affine/core/modules/workspace'; +import { WorkspaceSubPath } from '@affine/env/workspace'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAtomValue } from 'jotai'; import { useState } from 'react'; -import { useNavigateHelper } from '../../hooks/use-navigate-helper'; import * as styles from './upgrade.css'; import { ArrowCircleIcon, HeartBreakIcon } from './upgrade-icon'; diff --git a/packages/frontend/workspace/src/affine/__tests__/gql.spec.tsx b/packages/frontend/core/src/hooks/__tests__/gql.spec.tsx similarity index 97% rename from packages/frontend/workspace/src/affine/__tests__/gql.spec.tsx rename to packages/frontend/core/src/hooks/__tests__/gql.spec.tsx index 9bb659c1c8..3f5b9df57b 100644 --- a/packages/frontend/workspace/src/affine/__tests__/gql.spec.tsx +++ b/packages/frontend/core/src/hooks/__tests__/gql.spec.tsx @@ -6,7 +6,8 @@ import { render } from '@testing-library/react'; import type { Mock } from 'vitest'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { useMutation, useQuery } from '../gql'; +import { useMutation } from '../use-mutation'; +import { useQuery } from '../use-query'; let fetch: Mock; describe('GraphQL wrapper for SWR', () => { diff --git a/packages/frontend/hooks/src/__tests__/use-block-suite-workspace-helper.spec.ts b/packages/frontend/core/src/hooks/__tests__/use-block-suite-workspace-helper.spec.ts similarity index 100% rename from packages/frontend/hooks/src/__tests__/use-block-suite-workspace-helper.spec.ts rename to packages/frontend/core/src/hooks/__tests__/use-block-suite-workspace-helper.spec.ts diff --git a/packages/frontend/core/src/hooks/__tests__/use-block-suite-workspace-page-title.spec.ts b/packages/frontend/core/src/hooks/__tests__/use-block-suite-workspace-page-title.spec.ts new file mode 100644 index 0000000000..fc48a6b5e6 --- /dev/null +++ b/packages/frontend/core/src/hooks/__tests__/use-block-suite-workspace-page-title.spec.ts @@ -0,0 +1,49 @@ +/** + * @vitest-environment happy-dom + */ +import 'fake-indexeddb/auto'; + +import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models'; +import { assertExists } from '@blocksuite/global/utils'; +import type { Page } from '@blocksuite/store'; +import { Schema, Workspace as BlockSuiteWorkspace } from '@blocksuite/store'; +import { renderHook } from '@testing-library/react'; +import { describe, expect, test, vi } from 'vitest'; +import { beforeEach } from 'vitest'; + +import { useBlockSuiteWorkspacePageTitle } from '../use-block-suite-workspace-page-title'; + +let blockSuiteWorkspace: BlockSuiteWorkspace; + +const schema = new Schema(); +schema.register(AffineSchemas).register(__unstableSchemas); + +beforeEach(async () => { + vi.useFakeTimers({ toFake: ['requestIdleCallback'] }); + blockSuiteWorkspace = new BlockSuiteWorkspace({ id: 'test', schema }); + const initPage = async (page: Page) => { + await page.waitForLoaded(); + expect(page).not.toBeNull(); + assertExists(page); + const pageBlockId = page.addBlock('affine:page', { + title: new page.Text(''), + }); + const frameId = page.addBlock('affine:note', {}, pageBlockId); + page.addBlock('affine:paragraph', {}, frameId); + }; + await initPage(blockSuiteWorkspace.createPage({ id: 'page0' })); + await initPage(blockSuiteWorkspace.createPage({ id: 'page1' })); + await initPage(blockSuiteWorkspace.createPage({ id: 'page2' })); +}); + +describe('useBlockSuiteWorkspacePageTitle', () => { + test('basic', async () => { + const pageTitleHook = renderHook(() => + useBlockSuiteWorkspacePageTitle(blockSuiteWorkspace, 'page0') + ); + expect(pageTitleHook.result.current).toBe('Untitled'); + blockSuiteWorkspace.setPageMeta('page0', { title: '1' }); + pageTitleHook.rerender(); + expect(pageTitleHook.result.current).toBe('1'); + }); +}); diff --git a/packages/frontend/hooks/src/affine-async-hooks.ts b/packages/frontend/core/src/hooks/affine-async-hooks.ts similarity index 100% rename from packages/frontend/hooks/src/affine-async-hooks.ts rename to packages/frontend/core/src/hooks/affine-async-hooks.ts diff --git a/packages/frontend/core/src/hooks/affine/use-all-page-list-config.tsx b/packages/frontend/core/src/hooks/affine/use-all-page-list-config.tsx index e869cb9a16..803992507c 100644 --- a/packages/frontend/core/src/hooks/affine/use-all-page-list-config.tsx +++ b/packages/frontend/core/src/hooks/affine/use-all-page-list-config.tsx @@ -3,10 +3,10 @@ import { type AllPageListConfig, FavoriteTag, } from '@affine/component/page-list'; +import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta'; +import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { waitForCurrentWorkspaceAtom } from '@affine/workspace/atom'; import type { PageMeta } from '@blocksuite/store'; -import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useAtomValue } from 'jotai'; import { useCallback, useMemo } from 'react'; diff --git a/packages/frontend/core/src/hooks/affine/use-block-suite-meta-helper.ts b/packages/frontend/core/src/hooks/affine/use-block-suite-meta-helper.ts index 2898844a4e..9b6a4de9bb 100644 --- a/packages/frontend/core/src/hooks/affine/use-block-suite-meta-helper.ts +++ b/packages/frontend/core/src/hooks/affine/use-block-suite-meta-helper.ts @@ -1,9 +1,9 @@ -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { useBlockSuitePageMeta, usePageMetaHelper, -} from '@toeverything/hooks/use-block-suite-page-meta'; -import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper'; +} from '@affine/core/hooks/use-block-suite-page-meta'; +import { useBlockSuiteWorkspaceHelper } from '@affine/core/hooks/use-block-suite-workspace-helper'; import { useAtomValue, useSetAtom } from 'jotai'; import { useCallback } from 'react'; import { applyUpdate, encodeStateAsUpdate } from 'yjs'; diff --git a/packages/frontend/core/src/hooks/affine/use-export-page.ts b/packages/frontend/core/src/hooks/affine/use-export-page.ts index ecaa42937b..7ad1714c79 100644 --- a/packages/frontend/core/src/hooks/affine/use-export-page.ts +++ b/packages/frontend/core/src/hooks/affine/use-export-page.ts @@ -3,6 +3,7 @@ import { resolveGlobalLoadingEventAtom, } from '@affine/component/global-loading'; import { pushNotificationAtom } from '@affine/component/notification-center'; +import { apis } from '@affine/electron-api'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { HtmlTransformer, @@ -49,7 +50,7 @@ async function exportHandler({ page, type }: ExportHandlerOptions) { break; case 'pdf': if (environment.isDesktop && page.meta.mode === 'page') { - await window.apis?.export.savePDFFileAs( + await apis?.export.savePDFFileAs( (page.root as PageBlockModel).title.toString() ); } else { diff --git a/packages/frontend/core/src/hooks/affine/use-invite-member.ts b/packages/frontend/core/src/hooks/affine/use-invite-member.ts index 43bccc59e3..b56e5969fa 100644 --- a/packages/frontend/core/src/hooks/affine/use-invite-member.ts +++ b/packages/frontend/core/src/hooks/affine/use-invite-member.ts @@ -1,8 +1,8 @@ import type { Permission } from '@affine/graphql'; import { inviteByEmailMutation } from '@affine/graphql'; -import { useMutation } from '@affine/workspace/affine/gql'; import { useCallback } from 'react'; +import { useMutation } from '../use-mutation'; import { useMutateCloud } from './use-mutate-cloud'; export function useInviteMember(workspaceId: string) { diff --git a/packages/frontend/core/src/hooks/affine/use-is-shared-page.ts b/packages/frontend/core/src/hooks/affine/use-is-shared-page.ts index f581a87ded..bb0ad064a1 100644 --- a/packages/frontend/core/src/hooks/affine/use-is-shared-page.ts +++ b/packages/frontend/core/src/hooks/affine/use-is-shared-page.ts @@ -6,11 +6,12 @@ import { revokePublicPageMutation, } from '@affine/graphql'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { useMutation, useQuery } from '@affine/workspace/affine/gql'; import { useSetAtom } from 'jotai'; import { useCallback, useMemo } from 'react'; import type { PageMode } from '../../atoms'; +import { useMutation } from '../use-mutation'; +import { useQuery } from '../use-query'; type NoParametersKeys = { [K in keyof T]: T[K] extends () => any ? K : never; diff --git a/packages/frontend/core/src/hooks/affine/use-is-workspace-owner.ts b/packages/frontend/core/src/hooks/affine/use-is-workspace-owner.ts index 1b09ee3a54..4a8cd494b2 100644 --- a/packages/frontend/core/src/hooks/affine/use-is-workspace-owner.ts +++ b/packages/frontend/core/src/hooks/affine/use-is-workspace-owner.ts @@ -1,8 +1,9 @@ import { WorkspaceFlavour } from '@affine/env/workspace'; import { getIsOwnerQuery } from '@affine/graphql'; -import { useQueryImmutable } from '@affine/workspace/affine/gql'; import type { WorkspaceMetadata } from '@affine/workspace/metadata'; +import { useQueryImmutable } from '../use-query'; + export function useIsWorkspaceOwner(workspaceMetadata: WorkspaceMetadata) { const { data } = useQueryImmutable( workspaceMetadata.flavour !== WorkspaceFlavour.LOCAL diff --git a/packages/frontend/core/src/hooks/affine/use-language-helper.ts b/packages/frontend/core/src/hooks/affine/use-language-helper.ts index 9ddfca62f7..f179187ca9 100644 --- a/packages/frontend/core/src/hooks/affine/use-language-helper.ts +++ b/packages/frontend/core/src/hooks/affine/use-language-helper.ts @@ -1,5 +1,5 @@ +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { LOCALES, useI18N } from '@affine/i18n'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { useMemo } from 'react'; export function useLanguageHelper() { diff --git a/packages/frontend/core/src/hooks/affine/use-member-count.ts b/packages/frontend/core/src/hooks/affine/use-member-count.ts index 74fcfc4839..0bfb47d8f2 100644 --- a/packages/frontend/core/src/hooks/affine/use-member-count.ts +++ b/packages/frontend/core/src/hooks/affine/use-member-count.ts @@ -1,5 +1,6 @@ import { getMemberCountByWorkspaceIdQuery } from '@affine/graphql'; -import { useQuery } from '@affine/workspace/affine/gql'; + +import { useQuery } from '../use-query'; export function useMemberCount(workspaceId: string) { const { data } = useQuery({ diff --git a/packages/frontend/core/src/hooks/affine/use-members.ts b/packages/frontend/core/src/hooks/affine/use-members.ts index 5c1d54865d..0f6c78b576 100644 --- a/packages/frontend/core/src/hooks/affine/use-members.ts +++ b/packages/frontend/core/src/hooks/affine/use-members.ts @@ -2,7 +2,8 @@ import { type GetMembersByWorkspaceIdQuery, getMembersByWorkspaceIdQuery, } from '@affine/graphql'; -import { useQuery } from '@affine/workspace/affine/gql'; + +import { useQuery } from '../use-query'; export type Member = Omit< GetMembersByWorkspaceIdQuery['workspace']['members'][number], diff --git a/packages/frontend/core/src/hooks/affine/use-register-blocksuite-editor-commands.tsx b/packages/frontend/core/src/hooks/affine/use-register-blocksuite-editor-commands.tsx index 302f34ad00..afd030693b 100644 --- a/packages/frontend/core/src/hooks/affine/use-register-blocksuite-editor-commands.tsx +++ b/packages/frontend/core/src/hooks/affine/use-register-blocksuite-editor-commands.tsx @@ -1,10 +1,10 @@ import { toast } from '@affine/component'; +import { usePageMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta'; +import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { waitForCurrentWorkspaceAtom } from '@affine/workspace/atom'; import { assertExists } from '@blocksuite/global/utils'; import { EdgelessIcon, HistoryIcon, PageIcon } from '@blocksuite/icons'; -import { usePageMetaHelper } from '@toeverything/hooks/use-block-suite-page-meta'; import { PreconditionStrategy, registerAffineCommand, diff --git a/packages/frontend/core/src/hooks/affine/use-revoke-member-permission.ts b/packages/frontend/core/src/hooks/affine/use-revoke-member-permission.ts index 569b184eff..2dbd844432 100644 --- a/packages/frontend/core/src/hooks/affine/use-revoke-member-permission.ts +++ b/packages/frontend/core/src/hooks/affine/use-revoke-member-permission.ts @@ -1,7 +1,7 @@ import { revokeMemberPermissionMutation } from '@affine/graphql'; -import { useMutation } from '@affine/workspace/affine/gql'; import { useCallback } from 'react'; +import { useMutation } from '../use-mutation'; import { useMutateCloud } from './use-mutate-cloud'; export function useRevokeMemberPermission(workspaceId: string) { diff --git a/packages/frontend/core/src/hooks/affine/use-server-config.ts b/packages/frontend/core/src/hooks/affine/use-server-config.ts index 89f84b336c..8238c56c2e 100644 --- a/packages/frontend/core/src/hooks/affine/use-server-config.ts +++ b/packages/frontend/core/src/hooks/affine/use-server-config.ts @@ -1,7 +1,8 @@ import { serverConfigQuery } from '@affine/graphql'; -import { useQueryImmutable } from '@affine/workspace/affine/gql'; import type { BareFetcher, Middleware } from 'swr'; +import { useQueryImmutable } from '../use-query'; + const wrappedFetcher = (fetcher: BareFetcher | null, ...args: any[]) => fetcher?.(...args).catch(() => null); diff --git a/packages/frontend/core/src/hooks/affine/use-sidebar-drag.ts b/packages/frontend/core/src/hooks/affine/use-sidebar-drag.ts index 89b1d0eadf..0ad0231dc1 100644 --- a/packages/frontend/core/src/hooks/affine/use-sidebar-drag.ts +++ b/packages/frontend/core/src/hooks/affine/use-sidebar-drag.ts @@ -1,9 +1,9 @@ import { toast } from '@affine/component'; import type { DraggableTitleCellData } from '@affine/component/page-list'; +import { usePageMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta'; +import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { waitForCurrentWorkspaceAtom } from '@affine/workspace/atom'; import type { DragEndEvent, UniqueIdentifier } from '@dnd-kit/core'; -import { usePageMetaHelper } from '@toeverything/hooks/use-block-suite-page-meta'; import { useAtomValue } from 'jotai'; import { useCallback } from 'react'; diff --git a/packages/frontend/core/src/hooks/affine/use-toggle-cloud-public.ts b/packages/frontend/core/src/hooks/affine/use-toggle-cloud-public.ts index 57c88d3669..b98d91d56a 100644 --- a/packages/frontend/core/src/hooks/affine/use-toggle-cloud-public.ts +++ b/packages/frontend/core/src/hooks/affine/use-toggle-cloud-public.ts @@ -1,7 +1,7 @@ import { setWorkspacePublicByIdMutation } from '@affine/graphql'; -import { useMutation } from '@affine/workspace/affine/gql'; import { useCallback } from 'react'; +import { useMutation } from '../use-mutation'; import { useMutateCloud } from './use-mutate-cloud'; export function useToggleCloudPublic(workspaceId: string) { diff --git a/packages/frontend/core/src/hooks/current/use-current-page.ts b/packages/frontend/core/src/hooks/current/use-current-page.ts index 8b16a611c3..9d1ceb9b8c 100644 --- a/packages/frontend/core/src/hooks/current/use-current-page.ts +++ b/packages/frontend/core/src/hooks/current/use-current-page.ts @@ -1,8 +1,9 @@ -import { waitForCurrentWorkspaceAtom } from '@affine/workspace/atom'; -import { useBlockSuiteWorkspacePage } from '@toeverything/hooks/use-block-suite-workspace-page'; -import { currentPageIdAtom } from '@toeverything/infra/atom'; +import { useBlockSuiteWorkspacePage } from '@affine/core/hooks/use-block-suite-workspace-page'; +import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace'; import { useAtomValue } from 'jotai'; +import { currentPageIdAtom } from '../../atoms/mode'; + export const useCurrentPage = () => { const currentPageId = useAtomValue(currentPageIdAtom); const currentWorkspace = useAtomValue(waitForCurrentWorkspaceAtom); diff --git a/packages/frontend/core/src/hooks/use-app-config-storage.ts b/packages/frontend/core/src/hooks/use-app-config-storage.ts index 35b94fc292..6430a9b6d5 100644 --- a/packages/frontend/core/src/hooks/use-app-config-storage.ts +++ b/packages/frontend/core/src/hooks/use-app-config-storage.ts @@ -1,3 +1,5 @@ +import { apis } from '@affine/electron-api'; +import { assertExists } from '@blocksuite/global/utils'; import { type AppConfigSchema, AppConfigStorage, @@ -13,11 +15,13 @@ class AppConfigProxy { value: AppConfigSchema = defaultAppConfig; async getSync(): Promise { - return (this.value = await window.apis.configStorage.get()); + assertExists(apis); + return (this.value = await apis.configStorage.get()); } async setSync(): Promise { - await window.apis.configStorage.set(this.value); + assertExists(apis); + await apis.configStorage.set(this.value); } get(): AppConfigSchema { diff --git a/packages/frontend/hooks/src/use-app-updater.ts b/packages/frontend/core/src/hooks/use-app-updater.ts similarity index 86% rename from packages/frontend/hooks/src/use-app-updater.ts rename to packages/frontend/core/src/hooks/use-app-updater.ts index d4e28c1dc6..81604e6407 100644 --- a/packages/frontend/hooks/src/use-app-updater.ts +++ b/packages/frontend/core/src/hooks/use-app-updater.ts @@ -1,6 +1,6 @@ +import { apis, events, type UpdateMeta } from '@affine/electron-api'; import { isBrowser } from '@affine/env/constant'; import { appSettingAtom } from '@toeverything/infra/atom'; -import type { UpdateMeta } from '@toeverything/infra/type'; import { atom, useAtom, useAtomValue } from 'jotai'; import { atomWithObservable, atomWithStorage } from 'jotai/utils'; import { useCallback, useState } from 'react'; @@ -47,21 +47,21 @@ function rpcToObservable< // download complete, ready to install export const updateReadyAtom = atomWithObservable(() => { return rpcToObservable(null as UpdateMeta | null, { - event: window.events?.updater.onUpdateReady, + event: events?.updater.onUpdateReady, }); }); // update available, but not downloaded yet export const updateAvailableAtom = atomWithObservable(() => { return rpcToObservable(null as UpdateMeta | null, { - event: window.events?.updater.onUpdateAvailable, + event: events?.updater.onUpdateAvailable, }); }); // downloading new update export const downloadProgressAtom = atomWithObservable(() => { return rpcToObservable(null as number | null, { - event: window.events?.updater.onDownloadProgress, + event: events?.updater.onDownloadProgress, }); }); @@ -76,7 +76,7 @@ export const currentVersionAtom = atom(async () => { if (!isBrowser) { return null; } - const currentVersion = await window.apis?.updater.currentVersion(); + const currentVersion = await apis?.updater.currentVersion(); return currentVersion; }); @@ -121,7 +121,7 @@ export const useAppUpdater = () => { const quitAndInstall = useCallback(() => { if (updateReady) { setAppQuitting(true); - window.apis?.updater.quitAndInstall().catch(err => { + apis?.updater.quitAndInstall().catch(err => { // TODO: add error toast here console.error(err); }); @@ -134,7 +134,7 @@ export const useAppUpdater = () => { } setCheckingForUpdates(true); try { - const updateInfo = await window.apis?.updater.checkForUpdates(); + const updateInfo = await apis?.updater.checkForUpdates(); return updateInfo?.version ?? false; } catch (err) { console.error('Error checking for updates:', err); @@ -145,7 +145,7 @@ export const useAppUpdater = () => { }, [checkingForUpdates, setCheckingForUpdates]); const downloadUpdate = useCallback(() => { - window.apis?.updater.downloadUpdate().catch(err => { + apis?.updater.downloadUpdate().catch(err => { console.error('Error downloading update:', err); }); }, []); @@ -168,7 +168,12 @@ export const useAppUpdater = () => { [setSetting] ); - const readChangelog = useAsyncCallback(async () => { + const openChangelog = useAsyncCallback(async () => { + window.open(runtimeConfig.changelogUrl, '_blank'); + await setChangelogUnread(true); + }, [setChangelogUnread]); + + const dismissChangelog = useAsyncCallback(async () => { await setChangelogUnread(true); }, [setChangelogUnread]); @@ -183,7 +188,8 @@ export const useAppUpdater = () => { autoCheck: setting.autoCheckUpdate, autoDownload: setting.autoDownloadUpdate, changelogUnread, - readChangelog, + openChangelog, + dismissChangelog, updateReady, updateAvailable: useAtomValue(updateAvailableAtom), downloadProgress, diff --git a/packages/frontend/core/src/hooks/use-block-suite-editor.ts b/packages/frontend/core/src/hooks/use-block-suite-editor.ts new file mode 100644 index 0000000000..68d9ff82fb --- /dev/null +++ b/packages/frontend/core/src/hooks/use-block-suite-editor.ts @@ -0,0 +1,15 @@ +import type { AffineEditorContainer } from '@blocksuite/presets'; +import { atom, type SetStateAction, useAtom } from 'jotai'; + +const activeEditorContainerAtom = atom(null); + +export function useActiveBlocksuiteEditor(): [ + AffineEditorContainer | null, + React.Dispatch>, +] { + const [editorContainer, setEditorContainer] = useAtom( + activeEditorContainerAtom + ); + + return [editorContainer, setEditorContainer]; +} diff --git a/packages/frontend/hooks/src/use-block-suite-page-meta.ts b/packages/frontend/core/src/hooks/use-block-suite-page-meta.ts similarity index 100% rename from packages/frontend/hooks/src/use-block-suite-page-meta.ts rename to packages/frontend/core/src/hooks/use-block-suite-page-meta.ts diff --git a/packages/frontend/hooks/src/use-block-suite-page-references.ts b/packages/frontend/core/src/hooks/use-block-suite-page-references.ts similarity index 100% rename from packages/frontend/hooks/src/use-block-suite-page-references.ts rename to packages/frontend/core/src/hooks/use-block-suite-page-references.ts diff --git a/packages/frontend/hooks/src/use-block-suite-workspace-helper.ts b/packages/frontend/core/src/hooks/use-block-suite-workspace-helper.ts similarity index 100% rename from packages/frontend/hooks/src/use-block-suite-workspace-helper.ts rename to packages/frontend/core/src/hooks/use-block-suite-workspace-helper.ts diff --git a/packages/frontend/hooks/src/use-block-suite-workspace-page-title.ts b/packages/frontend/core/src/hooks/use-block-suite-workspace-page-title.ts similarity index 100% rename from packages/frontend/hooks/src/use-block-suite-workspace-page-title.ts rename to packages/frontend/core/src/hooks/use-block-suite-workspace-page-title.ts diff --git a/packages/frontend/core/src/hooks/use-block-suite-workspace-page.ts b/packages/frontend/core/src/hooks/use-block-suite-workspace-page.ts new file mode 100644 index 0000000000..1e6c069788 --- /dev/null +++ b/packages/frontend/core/src/hooks/use-block-suite-workspace-page.ts @@ -0,0 +1,46 @@ +import { DebugLogger } from '@affine/debug'; +import { DisposableGroup } from '@blocksuite/global/utils'; +import type { Page, Workspace } from '@blocksuite/store'; +import { useEffect, useState } from 'react'; + +const logger = new DebugLogger('use-block-suite-workspace-page'); + +export function useBlockSuiteWorkspacePage( + blockSuiteWorkspace: Workspace, + pageId: string | null +): Page | null { + const [page, setPage] = useState( + pageId ? blockSuiteWorkspace.getPage(pageId) : null + ); + + useEffect(() => { + const group = new DisposableGroup(); + group.add( + blockSuiteWorkspace.slots.pageAdded.on(id => { + if (pageId === id) { + setPage(blockSuiteWorkspace.getPage(id)); + } + }) + ); + group.add( + blockSuiteWorkspace.slots.pageRemoved.on(id => { + if (pageId === id) { + setPage(null); + } + }) + ); + return () => { + group.dispose(); + }; + }, [blockSuiteWorkspace, pageId]); + + useEffect(() => { + if (page && !page.loaded) { + page.load().catch(err => { + logger.error('Failed to load page', err); + }); + } + }, [page]); + + return page; +} diff --git a/packages/frontend/hooks/src/use-data-source-status.ts b/packages/frontend/core/src/hooks/use-data-source-status.ts similarity index 100% rename from packages/frontend/hooks/src/use-data-source-status.ts rename to packages/frontend/core/src/hooks/use-data-source-status.ts diff --git a/packages/frontend/core/src/hooks/use-get-page-info.ts b/packages/frontend/core/src/hooks/use-get-page-info.ts index 885a4336be..024957ad71 100644 --- a/packages/frontend/core/src/hooks/use-get-page-info.ts +++ b/packages/frontend/core/src/hooks/use-get-page-info.ts @@ -1,6 +1,6 @@ +import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta'; import type { GetPageInfoById } from '@affine/env/page-info'; import type { Workspace } from '@blocksuite/store'; -import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useAtomValue } from 'jotai'; import { useCallback, useMemo } from 'react'; diff --git a/packages/frontend/core/src/hooks/use-global-state.ts b/packages/frontend/core/src/hooks/use-global-state.ts new file mode 100644 index 0000000000..b42becafe9 --- /dev/null +++ b/packages/frontend/core/src/hooks/use-global-state.ts @@ -0,0 +1,16 @@ +import { noop } from 'lodash-es'; +import { useEffect } from 'react'; + +export function useDocumentTitle(newTitle?: string | null) { + useEffect(() => { + if (environment.isDesktop || !newTitle) { + return noop; + } + + const oldTitle = document.title; + document.title = newTitle; + return () => { + document.title = oldTitle; + }; + }, [newTitle]); +} diff --git a/packages/frontend/hooks/src/use-is-tiny-screen.ts b/packages/frontend/core/src/hooks/use-is-tiny-screen.ts similarity index 97% rename from packages/frontend/hooks/src/use-is-tiny-screen.ts rename to packages/frontend/core/src/hooks/use-is-tiny-screen.ts index e87971da79..4b0880991b 100644 --- a/packages/frontend/hooks/src/use-is-tiny-screen.ts +++ b/packages/frontend/core/src/hooks/use-is-tiny-screen.ts @@ -1,6 +1,6 @@ import 'foxact/use-debounced-state'; -import debounce from 'lodash.debounce'; +import { debounce } from 'lodash-es'; import { type RefObject, useEffect, useState } from 'react'; export function useIsTinyScreen({ diff --git a/packages/frontend/core/src/hooks/use-mutation.ts b/packages/frontend/core/src/hooks/use-mutation.ts new file mode 100644 index 0000000000..58860b2fe6 --- /dev/null +++ b/packages/frontend/core/src/hooks/use-mutation.ts @@ -0,0 +1,89 @@ +import type { + GraphQLQuery, + MutationOptions, + QueryResponse, + QueryVariables, + RecursiveMaybeFields, +} from '@affine/graphql'; +import { fetcher } from '@affine/graphql'; +import type { GraphQLError } from 'graphql'; +import { useMemo } from 'react'; +import type { Key } from 'swr'; +import { useSWRConfig } from 'swr'; +import type { + SWRMutationConfiguration, + SWRMutationResponse, +} from 'swr/mutation'; +import useSWRMutation from 'swr/mutation'; + +/** + * A useSWRMutation wrapper for sending graphql mutations + * + * @example + * + * ```ts + * import { someMutation } from '@affine/graphql' + * + * const { trigger } = useMutation({ + * mutation: someMutation, + * }) + * + * trigger({ name: 'John Doe' }) + */ +export function useMutation( + options: Omit, 'variables'>, + config?: Omit< + SWRMutationConfiguration< + QueryResponse, + GraphQLError | GraphQLError[], + K, + QueryVariables + >, + 'fetcher' + > +): SWRMutationResponse< + QueryResponse, + GraphQLError | GraphQLError[], + K, + QueryVariables +>; +export function useMutation( + options: Omit, 'variables'>, + config?: any +) { + return useSWRMutation( + () => ['cloud', options.mutation.id], + (_: unknown[], { arg }: { arg: any }) => + fetcher({ ...options, query: options.mutation, variables: arg }), + config + ); +} + +// use this to revalidate all queries that match the filter +export const useMutateQueryResource = () => { + const { mutate } = useSWRConfig(); + const revalidateResource = useMemo( + () => + ( + query: Q, + varsFilter: ( + vars: RecursiveMaybeFields> + ) => boolean = _vars => true + ) => { + return mutate(key => { + const res = + Array.isArray(key) && + key[0] === 'cloud' && + key[1] === query.id && + varsFilter(key[2]); + if (res) { + console.debug('revalidate resource', key); + } + return res; + }); + }, + [mutate] + ); + + return revalidateResource; +}; diff --git a/packages/frontend/workspace/src/affine/gql.ts b/packages/frontend/core/src/hooks/use-query.ts similarity index 56% rename from packages/frontend/workspace/src/affine/gql.ts rename to packages/frontend/core/src/hooks/use-query.ts index 91daf7c8ca..2906f5a28e 100644 --- a/packages/frontend/workspace/src/affine/gql.ts +++ b/packages/frontend/core/src/hooks/use-query.ts @@ -1,28 +1,15 @@ -import { setupGlobal } from '@affine/env/global'; import type { GraphQLQuery, - MutationOptions, QueryOptions, QueryResponse, - QueryVariables, - RecursiveMaybeFields, } from '@affine/graphql'; -import { getBaseUrl, gqlFetcherFactory } from '@affine/graphql'; +import { fetcher } from '@affine/graphql'; import type { GraphQLError } from 'graphql'; import { useCallback, useMemo } from 'react'; -import type { Key, SWRConfiguration, SWRResponse } from 'swr'; -import useSWR, { useSWRConfig } from 'swr'; +import type { SWRConfiguration, SWRResponse } from 'swr'; +import useSWR from 'swr'; import useSWRImutable from 'swr/immutable'; import useSWRInfinite from 'swr/infinite'; -import type { - SWRMutationConfiguration, - SWRMutationResponse, -} from 'swr/mutation'; -import useSWRMutation from 'swr/mutation'; - -setupGlobal(); - -export const fetcher = gqlFetcherFactory(getBaseUrl() + '/graphql'); /** * A `useSWR` wrapper for sending graphql queries @@ -140,77 +127,3 @@ export function useQueryInfinite( loadMore, }; } - -/** - * A useSWRMutation wrapper for sending graphql mutations - * - * @example - * - * ```ts - * import { someMutation } from '@affine/graphql' - * - * const { trigger } = useMutation({ - * mutation: someMutation, - * }) - * - * trigger({ name: 'John Doe' }) - */ -export function useMutation( - options: Omit, 'variables'>, - config?: Omit< - SWRMutationConfiguration< - QueryResponse, - GraphQLError | GraphQLError[], - K, - QueryVariables - >, - 'fetcher' - > -): SWRMutationResponse< - QueryResponse, - GraphQLError | GraphQLError[], - K, - QueryVariables ->; -export function useMutation( - options: Omit, 'variables'>, - config?: any -) { - return useSWRMutation( - () => ['cloud', options.mutation.id], - (_: unknown[], { arg }: { arg: any }) => - fetcher({ ...options, query: options.mutation, variables: arg }), - config - ); -} - -export const gql = fetcher; - -// use this to revalidate all queries that match the filter -export const useMutateQueryResource = () => { - const { mutate } = useSWRConfig(); - const revalidateResource = useMemo( - () => - ( - query: Q, - varsFilter: ( - vars: RecursiveMaybeFields> - ) => boolean = _vars => true - ) => { - return mutate(key => { - const res = - Array.isArray(key) && - key[0] === 'cloud' && - key[1] === query.id && - varsFilter(key[2]); - if (res) { - console.debug('revalidate resource', key); - } - return res; - }); - }, - [mutate] - ); - - return revalidateResource; -}; diff --git a/packages/frontend/core/src/hooks/use-quota.ts b/packages/frontend/core/src/hooks/use-quota.ts index 5d2d8db58a..30d8dc08c9 100644 --- a/packages/frontend/core/src/hooks/use-quota.ts +++ b/packages/frontend/core/src/hooks/use-quota.ts @@ -1,5 +1,6 @@ import { quotaQuery } from '@affine/graphql'; -import { useQuery } from '@affine/workspace/affine/gql'; + +import { useQuery } from './use-query'; export const useUserQuota = () => { const { data } = useQuery({ diff --git a/packages/frontend/core/src/hooks/use-register-workspace-commands.ts b/packages/frontend/core/src/hooks/use-register-workspace-commands.ts index b10fe3bf65..2a17abc569 100644 --- a/packages/frontend/core/src/hooks/use-register-workspace-commands.ts +++ b/packages/frontend/core/src/hooks/use-register-workspace-commands.ts @@ -1,5 +1,5 @@ +import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { waitForCurrentWorkspaceAtom } from '@affine/workspace/atom'; import { useAtom, useAtomValue, useStore } from 'jotai'; import { useTheme } from 'next-themes'; import { useEffect } from 'react'; @@ -15,6 +15,7 @@ import { } from '../commands'; import { usePageHelper } from '../components/blocksuite/block-suite-page-list/utils'; import { useLanguageHelper } from './affine/use-language-helper'; +import { useActiveBlocksuiteEditor } from './use-block-suite-editor'; import { useNavigateHelper } from './use-navigate-helper'; export function useRegisterWorkspaceCommands() { @@ -26,6 +27,7 @@ export function useRegisterWorkspaceCommands() { const pageHelper = usePageHelper(currentWorkspace.blockSuiteWorkspace); const navigationHelper = useNavigateHelper(); const [pageListMode, setPageListMode] = useAtom(allPageModeSelectAtom); + const [editor] = useActiveBlocksuiteEditor(); // register AffineUpdatesCommands useEffect(() => { @@ -69,12 +71,13 @@ export function useRegisterWorkspaceCommands() { t, theme, languageHelper, + editor, }); return () => { unsub(); }; - }, [store, t, theme, languageHelper]); + }, [store, t, theme, languageHelper, editor]); // register AffineLayoutCommands useEffect(() => { diff --git a/packages/frontend/core/src/hooks/use-subscription.ts b/packages/frontend/core/src/hooks/use-subscription.ts index f7095033ea..be6a1a0b78 100644 --- a/packages/frontend/core/src/hooks/use-subscription.ts +++ b/packages/frontend/core/src/hooks/use-subscription.ts @@ -1,8 +1,8 @@ +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { type SubscriptionQuery, subscriptionQuery } from '@affine/graphql'; -import { useQuery } from '@affine/workspace/affine/gql'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { useSelfHosted } from './affine/use-server-config'; +import { useQuery } from './use-query'; export type Subscription = NonNullable< NonNullable['subscription'] diff --git a/packages/frontend/hooks/src/use-workspace-blob.ts b/packages/frontend/core/src/hooks/use-workspace-blob.ts similarity index 93% rename from packages/frontend/hooks/src/use-workspace-blob.ts rename to packages/frontend/core/src/hooks/use-workspace-blob.ts index 9fe8fb5c0b..7f4db3e478 100644 --- a/packages/frontend/hooks/src/use-workspace-blob.ts +++ b/packages/frontend/core/src/hooks/use-workspace-blob.ts @@ -1,4 +1,4 @@ -import { workspaceManagerAtom } from '@affine/workspace/atom'; +import { workspaceManagerAtom } from '@affine/core/modules/workspace'; import type { WorkspaceMetadata } from '@affine/workspace/metadata'; import { useAtomValue } from 'jotai'; import { useEffect, useState } from 'react'; diff --git a/packages/frontend/core/src/hooks/use-workspace-info.ts b/packages/frontend/core/src/hooks/use-workspace-info.ts new file mode 100644 index 0000000000..b0d582bc3c --- /dev/null +++ b/packages/frontend/core/src/hooks/use-workspace-info.ts @@ -0,0 +1,38 @@ +import { workspaceManagerAtom } from '@affine/core/modules/workspace'; +import type { WorkspaceMetadata } from '@affine/workspace'; +import { useAtomValue } from 'jotai'; +import { useEffect, useState } from 'react'; + +import { useWorkspaceBlobObjectUrl } from './use-workspace-blob'; + +export function useWorkspaceInfo(meta: WorkspaceMetadata) { + const workspaceManager = useAtomValue(workspaceManagerAtom); + + const [information, setInformation] = useState( + () => workspaceManager.list.getInformation(meta).info + ); + + useEffect(() => { + const information = workspaceManager.list.getInformation(meta); + + setInformation(information.info); + return information.onUpdated.on(info => { + setInformation(info); + }).dispose; + }, [meta, workspaceManager]); + + return information; +} + +export function useWorkspaceName(meta: WorkspaceMetadata) { + const information = useWorkspaceInfo(meta); + + return information?.name; +} + +export function useWorkspaceAvatar(meta: WorkspaceMetadata) { + const information = useWorkspaceInfo(meta); + const avatar = useWorkspaceBlobObjectUrl(meta, information?.avatar); + + return avatar; +} diff --git a/packages/frontend/hooks/src/use-workspace-status.ts b/packages/frontend/core/src/hooks/use-workspace-status.ts similarity index 100% rename from packages/frontend/hooks/src/use-workspace-status.ts rename to packages/frontend/core/src/hooks/use-workspace-status.ts diff --git a/packages/frontend/hooks/src/use-workspace.ts b/packages/frontend/core/src/hooks/use-workspace.ts similarity index 91% rename from packages/frontend/hooks/src/use-workspace.ts rename to packages/frontend/core/src/hooks/use-workspace.ts index 5c6617b7ae..0c914b04c3 100644 --- a/packages/frontend/hooks/src/use-workspace.ts +++ b/packages/frontend/core/src/hooks/use-workspace.ts @@ -1,5 +1,5 @@ +import { workspaceManagerAtom } from '@affine/core/modules/workspace'; import type { Workspace } from '@affine/workspace'; -import { workspaceManagerAtom } from '@affine/workspace/atom'; import type { WorkspaceMetadata } from '@affine/workspace/metadata'; import { useAtomValue } from 'jotai'; import { useEffect, useState } from 'react'; diff --git a/packages/frontend/core/src/index.tsx b/packages/frontend/core/src/index.tsx index 57d68e38d1..1a1e95584b 100644 --- a/packages/frontend/core/src/index.tsx +++ b/packages/frontend/core/src/index.tsx @@ -1,14 +1,11 @@ -import 'ses'; import './polyfill/intl-segmenter'; import './polyfill/request-idle-callback'; import { assertExists } from '@blocksuite/global/utils'; -import { getCurrentStore } from '@toeverything/infra/atom'; import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import { App } from './app'; -import { bootstrapPluginSystem } from './bootstrap/register-plugins'; import { setup } from './bootstrap/setup'; import { performanceLogger } from './shared'; @@ -18,14 +15,9 @@ function main() { // skip bootstrap setup for desktop onboarding if (window.appInfo?.windowName !== 'onboarding') { - const rootStore = getCurrentStore(); performanceMainLogger.info('setup start'); setup(); performanceMainLogger.info('setup done'); - - bootstrapPluginSystem(rootStore).catch(err => { - console.error('Failed to bootstrap plugin system', err); - }); } mountApp(); diff --git a/packages/frontend/core/src/layouts/workspace-layout.tsx b/packages/frontend/core/src/layouts/workspace-layout.tsx index e903416643..17fe3ba50a 100644 --- a/packages/frontend/core/src/layouts/workspace-layout.tsx +++ b/packages/frontend/core/src/layouts/workspace-layout.tsx @@ -7,7 +7,9 @@ import { PageListDragOverlay, } from '@affine/component/page-list'; import { MainContainer, WorkspaceFallback } from '@affine/component/workspace'; -import { waitForCurrentWorkspaceAtom } from '@affine/workspace/atom'; +import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta'; +import { useWorkspaceStatus } from '@affine/core/hooks/use-workspace-status'; +import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace'; import { assertExists } from '@blocksuite/global/utils'; import { DndContext, @@ -18,8 +20,6 @@ import { useSensor, useSensors, } from '@dnd-kit/core'; -import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; -import { useWorkspaceStatus } from '@toeverything/hooks/use-workspace-status'; import { useAtom, useAtomValue, useSetAtom } from 'jotai'; import type { PropsWithChildren, ReactNode } from 'react'; import { lazy, Suspense, useCallback, useEffect, useState } from 'react'; @@ -27,7 +27,6 @@ import { useLocation, useParams } from 'react-router-dom'; import { Map as YMap } from 'yjs'; import { openQuickSearchModalAtom, openSettingModalAtom } from '../atoms'; -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'; @@ -41,6 +40,7 @@ import { AllWorkspaceModals, CurrentWorkspaceModals, } from '../providers/modal-provider'; +import { SWRConfigProvider } from '../providers/swr-config-provider'; import { pathGenerator } from '../shared'; const CMDKQuickSearchModal = lazy(() => @@ -78,7 +78,7 @@ export const WorkspaceLayout = function WorkspaceLayout({ children, }: PropsWithChildren) { return ( - + {/* load all workspaces is costly, do not block the whole UI */} @@ -87,7 +87,7 @@ export const WorkspaceLayout = function WorkspaceLayout({ }> {children} - + ); }; diff --git a/packages/frontend/workspace/src/atom.ts b/packages/frontend/core/src/modules/workspace/atoms.ts similarity index 93% rename from packages/frontend/workspace/src/atom.ts rename to packages/frontend/core/src/modules/workspace/atoms.ts index b5f967a4f4..1833db2178 100644 --- a/packages/frontend/workspace/src/atom.ts +++ b/packages/frontend/core/src/modules/workspace/atoms.ts @@ -1,10 +1,10 @@ import { DebugLogger } from '@affine/debug'; +import type { Workspace, WorkspaceMetadata } from '@affine/workspace'; +import { workspaceManager } from '@affine/workspace-impl'; import { atom } from 'jotai'; import { atomWithObservable } from 'jotai/utils'; import { Observable } from 'rxjs'; -import { type Workspace, workspaceManager, type WorkspaceMetadata } from '.'; - const logger = new DebugLogger('affine:workspace:atom'); // readonly atom for workspace manager, currently only one workspace manager is supported diff --git a/packages/frontend/core/src/modules/workspace/index.ts b/packages/frontend/core/src/modules/workspace/index.ts new file mode 100644 index 0000000000..2b93289c31 --- /dev/null +++ b/packages/frontend/core/src/modules/workspace/index.ts @@ -0,0 +1 @@ +export * from './atoms'; diff --git a/packages/frontend/core/src/pages/404.tsx b/packages/frontend/core/src/pages/404.tsx index f6f980ad12..280a33da82 100644 --- a/packages/frontend/core/src/pages/404.tsx +++ b/packages/frontend/core/src/pages/404.tsx @@ -1,5 +1,5 @@ import { NotFoundPage } from '@affine/component/not-found-page'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; // eslint-disable-next-line @typescript-eslint/no-restricted-imports import { useSession } from 'next-auth/react'; import type { ReactElement } from 'react'; diff --git a/packages/frontend/core/src/pages/auth.tsx b/packages/frontend/core/src/pages/auth.tsx index 7faca33bb6..8529109e33 100644 --- a/packages/frontend/core/src/pages/auth.tsx +++ b/packages/frontend/core/src/pages/auth.tsx @@ -13,8 +13,8 @@ import { changePasswordMutation, sendVerifyChangeEmailMutation, } from '@affine/graphql'; +import { fetcher } from '@affine/graphql'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { fetcher, useMutation } from '@affine/workspace/affine/gql'; import { useSetAtom } from 'jotai/react'; import type { ReactElement } from 'react'; import { useCallback } from 'react'; @@ -29,6 +29,7 @@ import { z } from 'zod'; import { SubscriptionRedirect } from '../components/affine/auth/subscription-redirect'; import { useCurrentLoginStatus } from '../hooks/affine/use-current-login-status'; import { useCurrentUser } from '../hooks/affine/use-current-user'; +import { useMutation } from '../hooks/use-mutation'; import { RouteLogic, useNavigateHelper } from '../hooks/use-navigate-helper'; const authTypeSchema = z.enum([ diff --git a/packages/frontend/core/src/pages/index.tsx b/packages/frontend/core/src/pages/index.tsx index 6e2d118260..1397c4c051 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 { WorkspaceFallback } from '@affine/component/workspace'; -import { workspaceListAtom } from '@affine/workspace/atom'; +import { workspaceListAtom } from '@affine/core/modules/workspace'; import { useAtomValue } from 'jotai'; import { lazy, useEffect, useLayoutEffect, useState } from 'react'; import { type LoaderFunction, redirect } from 'react-router-dom'; diff --git a/packages/frontend/core/src/pages/invite.tsx b/packages/frontend/core/src/pages/invite.tsx index e74995e056..39e5c4f424 100644 --- a/packages/frontend/core/src/pages/invite.tsx +++ b/packages/frontend/core/src/pages/invite.tsx @@ -5,7 +5,7 @@ import { type GetInviteInfoQuery, getInviteInfoQuery, } from '@affine/graphql'; -import { fetcher } from '@affine/workspace/affine/gql'; +import { fetcher } from '@affine/graphql'; import { useSetAtom } from 'jotai'; import { useCallback, useEffect } from 'react'; import { type LoaderFunction, redirect, useLoaderData } from 'react-router-dom'; diff --git a/packages/frontend/core/src/pages/onboarding.tsx b/packages/frontend/core/src/pages/onboarding.tsx index 1cdb210079..dc3b1cf809 100644 --- a/packages/frontend/core/src/pages/onboarding.tsx +++ b/packages/frontend/core/src/pages/onboarding.tsx @@ -1,3 +1,5 @@ +import { apis } from '@affine/electron-api'; +import { assertExists } from '@blocksuite/global/utils'; import { useCallback } from 'react'; import { redirect } from 'react-router-dom'; @@ -23,7 +25,8 @@ export const Component = () => { const openApp = useCallback(() => { if (environment.isDesktop) { - window.apis.ui.handleOpenMainApp().catch(err => { + assertExists(apis); + apis.ui.handleOpenMainApp().catch(err => { console.log('failed to open main app', err); }); } else { diff --git a/packages/frontend/core/src/pages/open-app.tsx b/packages/frontend/core/src/pages/open-app.tsx index a6e849cf32..839d3156a2 100644 --- a/packages/frontend/core/src/pages/open-app.tsx +++ b/packages/frontend/core/src/pages/open-app.tsx @@ -1,8 +1,8 @@ import { Button } from '@affine/component/ui/button'; import { type GetCurrentUserQuery, getCurrentUserQuery } from '@affine/graphql'; +import { fetcher } 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 { useCallback, useMemo } from 'react'; import { diff --git a/packages/frontend/core/src/pages/share/share-detail-page.tsx b/packages/frontend/core/src/pages/share/share-detail-page.tsx index 28a6e12854..fa667a19db 100644 --- a/packages/frontend/core/src/pages/share/share-detail-page.tsx +++ b/packages/frontend/core/src/pages/share/share-detail-page.tsx @@ -1,11 +1,11 @@ import { MainContainer } from '@affine/component/workspace'; import { DebugLogger } from '@affine/debug'; import { fetchWithTraceReport } from '@affine/graphql'; +import { globalBlockSuiteSchema } from '@affine/workspace'; import { createAffineCloudBlobStorage, createStaticBlobStorage, - globalBlockSuiteSchema, -} from '@affine/workspace'; +} from '@affine/workspace-impl'; import { assertExists } from '@blocksuite/global/utils'; import { type Page, Workspace } from '@blocksuite/store'; import { noop } from 'foxact/noop'; 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 index 7b77ff9281..e3d302cafd 100644 --- 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 @@ -3,8 +3,8 @@ import { SaveAsCollectionButton, useCollectionManager, } from '@affine/component/page-list'; +import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace'; import type { Collection, Filter } from '@affine/env/filter'; -import { waitForCurrentWorkspaceAtom } from '@affine/workspace/atom'; import { useAtomValue } from 'jotai'; import { useCallback } from 'react'; diff --git a/packages/frontend/core/src/pages/workspace/all-page/all-page.tsx b/packages/frontend/core/src/pages/workspace/all-page/all-page.tsx index 6e1cbda9c7..fed910d907 100644 --- a/packages/frontend/core/src/pages/workspace/all-page/all-page.tsx +++ b/packages/frontend/core/src/pages/workspace/all-page/all-page.tsx @@ -9,9 +9,10 @@ import { useCollectionManager, VirtualizedPageList, } from '@affine/component/page-list'; +import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta'; +import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace'; import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { waitForCurrentWorkspaceAtom } from '@affine/workspace/atom'; import { CloseIcon, DeleteIcon, @@ -19,7 +20,6 @@ import { ViewLayersIcon, } from '@blocksuite/icons'; import type { PageMeta, Workspace } from '@blocksuite/store'; -import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import clsx from 'clsx'; import { useAtomValue, useSetAtom } from 'jotai'; import { diff --git a/packages/frontend/core/src/pages/workspace/collection.tsx b/packages/frontend/core/src/pages/workspace/collection.tsx index 28d55d3337..903e06415a 100644 --- a/packages/frontend/core/src/pages/workspace/collection.tsx +++ b/packages/frontend/core/src/pages/workspace/collection.tsx @@ -10,17 +10,17 @@ import { useEditCollection, } from '@affine/component/page-list'; import { WindowsAppControls } from '@affine/core/components/pure/header/windows-app-controls'; +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; +import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace'; import type { Collection } from '@affine/env/filter'; import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { waitForCurrentWorkspaceAtom } from '@affine/workspace/atom'; import { CloseIcon, FilterIcon, PageIcon, ViewLayersIcon, } from '@blocksuite/icons'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { getCurrentStore } from '@toeverything/infra/atom'; import { useAtomValue } from 'jotai'; import { useSetAtom } from 'jotai'; 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 index 99216d5ad5..72e064940c 100644 --- a/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx +++ b/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx @@ -4,14 +4,14 @@ import { useCollectionManager, } from '@affine/component/page-list'; import { ResizePanel } from '@affine/component/resize-panel'; +import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta'; +import { useWorkspaceStatus } from '@affine/core/hooks/use-workspace-status'; +import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace'; import { WorkspaceSubPath } from '@affine/env/workspace'; import { globalBlockSuiteSchema, SyncEngineStep } from '@affine/workspace'; -import { waitForCurrentWorkspaceAtom } from '@affine/workspace/atom'; import type { AffineEditorContainer } from '@blocksuite/presets'; import type { Page, Workspace } from '@blocksuite/store'; -import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; -import { useWorkspaceStatus } from '@toeverything/hooks/use-workspace-status'; -import { appSettingAtom, currentPageIdAtom } from '@toeverything/infra/atom'; +import { appSettingAtom } from '@toeverything/infra/atom'; import { useAtom, useAtomValue, useSetAtom } from 'jotai'; import { memo, @@ -26,14 +26,16 @@ import type { Map as YMap } from 'yjs'; import { setPageModeAtom } from '../../../atoms'; import { collectionsCRUDAtom } from '../../../atoms/collections'; -import { currentModeAtom } from '../../../atoms/mode'; +import { currentModeAtom, currentPageIdAtom } 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 { ImagePreviewModal } from '../../../components/image-preview'; 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 { useDocumentTitle } from '../../../hooks/use-global-state'; import { useNavigateHelper } from '../../../hooks/use-navigate-helper'; import { performanceRenderLogger } from '../../../shared'; import { PageNotFound } from '../../404'; @@ -113,6 +115,7 @@ const DetailPageImpl = memo(function DetailPageImpl({ page }: { page: Page }) { const mode = useAtomValue(currentModeAtom); const setPageMode = useSetAtom(setPageModeAtom); useRegisterBlocksuiteEditorCommands(currentPageId, mode); + useDocumentTitle(pageMeta?.title ? `${pageMeta.title} · AFFiNE` : null); const onLoad = useCallback( (page: Page, editor: AffineEditorContainer) => { @@ -174,14 +177,17 @@ const DetailPageImpl = memo(function DetailPageImpl({ page }: { page: Page }) { } main={ -
- - -
+ // Add a key to force rerender when page changed, to avoid error boundary persisting. + +
+ + +
+
} footer={isInTrash ? : null} sidebar={ @@ -193,6 +199,10 @@ const DetailPageImpl = memo(function DetailPageImpl({ page }: { page: Page }) { ) : null } /> + ); @@ -261,10 +271,5 @@ export const Component = () => { const pageId = params.pageId; - // Add a key to force rerender when page changed, to avoid error boundary persisting. - return ( - - {pageId ? : null} - - ); + return pageId ? : null; }; diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/copilot.tsx b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/copilot.tsx index db04dbce69..18a3e99363 100644 --- a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/copilot.tsx +++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/copilot.tsx @@ -1,8 +1,7 @@ -import { editorContainerAtom } from '@affine/component/block-suite-editor'; +import { useActiveBlocksuiteEditor } from '@affine/core/hooks/use-block-suite-editor'; import { assertExists } from '@blocksuite/global/utils'; import { AiIcon } from '@blocksuite/icons'; import { CopilotPanel } from '@blocksuite/presets'; -import { useAtom } from 'jotai'; import { useCallback, useRef } from 'react'; import type { EditorExtension } from '../types'; @@ -11,7 +10,7 @@ import * as styles from './outline.css'; // A wrapper for CopilotPanel const EditorCopilotPanel = () => { const copilotPanelRef = useRef(null); - const [editorContainer] = useAtom(editorContainerAtom); + const [editor] = useActiveBlocksuiteEditor(); const onRefChange = useCallback((container: HTMLDivElement | null) => { if (container) { @@ -23,7 +22,7 @@ const EditorCopilotPanel = () => { } }, []); - if (!editorContainer) { + if (!editor) { return; } @@ -31,8 +30,8 @@ const EditorCopilotPanel = () => { copilotPanelRef.current = new CopilotPanel(); } - if (editorContainer !== copilotPanelRef.current?.editor) { - (copilotPanelRef.current as CopilotPanel).editor = editorContainer; + if (editor !== copilotPanelRef.current?.editor) { + (copilotPanelRef.current as CopilotPanel).editor = editor; // (copilotPanelRef.current as CopilotPanel).fitPadding = [20, 20, 20, 20]; } 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 index 1a661bd18b..c3a258c8f5 100644 --- 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 @@ -1,8 +1,7 @@ -import { editorContainerAtom } from '@affine/component/block-suite-editor'; +import { useActiveBlocksuiteEditor } from '@affine/core/hooks/use-block-suite-editor'; import { assertExists } from '@blocksuite/global/utils'; import { FrameIcon } from '@blocksuite/icons'; import { FramePanel } from '@blocksuite/presets'; -import { useAtom } from 'jotai'; import { useCallback, useRef } from 'react'; import type { EditorExtension } from '../types'; @@ -11,7 +10,8 @@ import * as styles from './frame.css'; // A wrapper for FramePanel const EditorFramePanel = () => { const framePanelRef = useRef(null); - const [editorContainer] = useAtom(editorContainerAtom); + + const [editor] = useActiveBlocksuiteEditor(); const onRefChange = useCallback((container: HTMLDivElement | null) => { if (container) { @@ -20,7 +20,7 @@ const EditorFramePanel = () => { } }, []); - if (!editorContainer) { + if (!editor) { return; } @@ -28,8 +28,8 @@ const EditorFramePanel = () => { framePanelRef.current = new FramePanel(); } - if (editorContainer !== framePanelRef.current?.editor) { - (framePanelRef.current as FramePanel).editor = editorContainer; + if (editor !== framePanelRef.current?.editor) { + (framePanelRef.current as FramePanel).editor = editor; (framePanelRef.current as FramePanel).fitPadding = [20, 20, 20, 20]; } 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 index f28c281760..6d24a27010 100644 --- 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 @@ -1,8 +1,7 @@ -import { editorContainerAtom } from '@affine/component/block-suite-editor'; +import { useActiveBlocksuiteEditor } from '@affine/core/hooks/use-block-suite-editor'; import { assertExists } from '@blocksuite/global/utils'; import { TocIcon } from '@blocksuite/icons'; import { TOCPanel } from '@blocksuite/presets'; -import { useAtom } from 'jotai'; import { useCallback, useRef } from 'react'; import type { EditorExtension } from '../types'; @@ -11,7 +10,7 @@ import * as styles from './outline.css'; // A wrapper for TOCNotesPanel const EditorOutline = () => { const tocPanelRef = useRef(null); - const [editorContainer] = useAtom(editorContainerAtom); + const [editor] = useActiveBlocksuiteEditor(); const onRefChange = useCallback((container: HTMLDivElement | null) => { if (container) { @@ -20,7 +19,7 @@ const EditorOutline = () => { } }, []); - if (!editorContainer) { + if (!editor) { return; } @@ -28,8 +27,8 @@ const EditorOutline = () => { tocPanelRef.current = new TOCPanel(); } - if (editorContainer !== tocPanelRef.current?.editor) { - (tocPanelRef.current as TOCPanel).editor = editorContainer; + if (editor !== tocPanelRef.current?.editor) { + (tocPanelRef.current as TOCPanel).editor = editor; (tocPanelRef.current as TOCPanel).fitPadding = [20, 20, 20, 20]; } diff --git a/packages/frontend/core/src/pages/workspace/index.tsx b/packages/frontend/core/src/pages/workspace/index.tsx index 249ac3f88b..a21b87daae 100644 --- a/packages/frontend/core/src/pages/workspace/index.tsx +++ b/packages/frontend/core/src/pages/workspace/index.tsx @@ -1,12 +1,12 @@ import { WorkspaceFallback } from '@affine/component/workspace'; -import { type Workspace } from '@affine/workspace'; +import { useWorkspace } from '@affine/core/hooks/use-workspace'; import { currentWorkspaceAtom, workspaceListAtom, workspaceListLoadingStatusAtom, workspaceManagerAtom, -} from '@affine/workspace/atom'; -import { useWorkspace } from '@toeverything/hooks/use-workspace'; +} from '@affine/core/modules/workspace'; +import { type Workspace } from '@affine/workspace'; import { useAtom, useAtomValue } from 'jotai'; import { type ReactElement, Suspense, useEffect, useMemo } from 'react'; import { Outlet, useParams } from 'react-router-dom'; diff --git a/packages/frontend/core/src/pages/workspace/trash-page.tsx b/packages/frontend/core/src/pages/workspace/trash-page.tsx index 637fda9215..71eaa01749 100644 --- a/packages/frontend/core/src/pages/workspace/trash-page.tsx +++ b/packages/frontend/core/src/pages/workspace/trash-page.tsx @@ -4,12 +4,12 @@ import { TrashOperationCell, VirtualizedPageList, } from '@affine/component/page-list'; +import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta'; +import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { waitForCurrentWorkspaceAtom } from '@affine/workspace/atom'; import { assertExists } from '@blocksuite/global/utils'; import { DeleteIcon } from '@blocksuite/icons'; import type { PageMeta } from '@blocksuite/store'; -import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { getCurrentStore } from '@toeverything/infra/atom'; import { useAtomValue } from 'jotai'; import { useCallback } from 'react'; @@ -58,7 +58,6 @@ export const loader: LoaderFunction = async () => { export const TrashPage = () => { const currentWorkspace = useAtomValue(waitForCurrentWorkspaceAtom); - // todo(himself65): refactor to plugin const blockSuiteWorkspace = currentWorkspace.blockSuiteWorkspace; assertExists(blockSuiteWorkspace); diff --git a/packages/frontend/core/src/providers/modal-provider.tsx b/packages/frontend/core/src/providers/modal-provider.tsx index 8d8fca03b8..c15d0bf245 100644 --- a/packages/frontend/core/src/providers/modal-provider.tsx +++ b/packages/frontend/core/src/providers/modal-provider.tsx @@ -1,11 +1,10 @@ -import { WorkspaceFlavour, WorkspaceSubPath } from '@affine/env/workspace'; import { currentWorkspaceAtom, waitForCurrentWorkspaceAtom, workspaceListAtom, -} from '@affine/workspace/atom'; +} from '@affine/core/modules/workspace'; +import { WorkspaceFlavour, WorkspaceSubPath } from '@affine/env/workspace'; import { assertExists } from '@blocksuite/global/utils'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import { useAtom, useAtomValue } from 'jotai'; import type { ReactElement } from 'react'; import { lazy, Suspense, useCallback } from 'react'; @@ -19,6 +18,7 @@ import { openSignOutModalAtom, } from '../atoms'; import { PaymentDisableModal } from '../components/affine/payment-disable'; +import { useAsyncCallback } from '../hooks/affine-async-hooks'; import { useNavigateHelper } from '../hooks/use-navigate-helper'; import { signOutCloud } from '../utils/cloud-utils'; diff --git a/packages/frontend/core/src/providers/session-provider.tsx b/packages/frontend/core/src/providers/session-provider.tsx index 3980fbe1ac..9decb80477 100644 --- a/packages/frontend/core/src/providers/session-provider.tsx +++ b/packages/frontend/core/src/providers/session-provider.tsx @@ -1,9 +1,8 @@ -import '@toeverything/hooks/use-affine-ipc-renderer'; - import { pushNotificationAtom } from '@affine/component/notification-center'; +import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; +import { affine } from '@affine/electron-api'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { CLOUD_WORKSPACE_CHANGED_BROADCAST_CHANNEL_KEY } from '@affine/workspace'; -import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; +import { CLOUD_WORKSPACE_CHANGED_BROADCAST_CHANNEL_KEY } from '@affine/workspace-impl'; import { useAtom, useSetAtom } from 'jotai'; // eslint-disable-next-line @typescript-eslint/no-restricted-imports import { SessionProvider, useSession } from 'next-auth/react'; @@ -51,7 +50,7 @@ const SessionDefence = (props: PropsWithChildren) => { }); if (environment.isDesktop) { - window.affine.ipcRenderer.send('affine:login'); + affine?.ipcRenderer.send('affine:login'); } } prevSession.current = session; diff --git a/packages/frontend/core/src/components/cloud/provider.tsx b/packages/frontend/core/src/providers/swr-config-provider.tsx similarity index 87% rename from packages/frontend/core/src/components/cloud/provider.tsx rename to packages/frontend/core/src/providers/swr-config-provider.tsx index 6b9daf4d24..fcdbc2b3bc 100644 --- a/packages/frontend/core/src/components/cloud/provider.tsx +++ b/packages/frontend/core/src/providers/swr-config-provider.tsx @@ -7,7 +7,7 @@ import { useCallback } from 'react'; import type { SWRConfiguration } from 'swr'; import { SWRConfig } from 'swr'; -const cloudConfig: SWRConfiguration = { +const swrConfig: SWRConfiguration = { suspense: true, use: [ useSWRNext => (key, fetcher, config) => { @@ -49,10 +49,6 @@ const cloudConfig: SWRConfiguration = { ], }; -export const Provider = (props: PropsWithChildren): ReactNode => { - if (!runtimeConfig.enableCloud) { - return props.children; - } - - return {props.children}; +export const SWRConfigProvider = (props: PropsWithChildren): ReactNode => { + return {props.children}; }; diff --git a/packages/frontend/core/src/utils/cloud-utils.tsx b/packages/frontend/core/src/utils/cloud-utils.tsx index 7d18b3eddc..4d27e54515 100644 --- a/packages/frontend/core/src/utils/cloud-utils.tsx +++ b/packages/frontend/core/src/utils/cloud-utils.tsx @@ -4,7 +4,7 @@ import { TRACE_ID_BYTES, traceReporter, } from '@affine/graphql'; -import { CLOUD_WORKSPACE_CHANGED_BROADCAST_CHANNEL_KEY } from '@affine/workspace'; +import { CLOUD_WORKSPACE_CHANGED_BROADCAST_CHANNEL_KEY } from '@affine/workspace-impl'; // eslint-disable-next-line @typescript-eslint/no-restricted-imports import { signIn, signOut } from 'next-auth/react'; diff --git a/packages/frontend/core/tsconfig.json b/packages/frontend/core/tsconfig.json index 36a7d8aba7..0251c35af4 100644 --- a/packages/frontend/core/tsconfig.json +++ b/packages/frontend/core/tsconfig.json @@ -14,14 +14,17 @@ { "path": "../../frontend/graphql" }, - { - "path": "../../frontend/hooks" - }, { "path": "../../frontend/i18n" }, { - "path": "../../frontend/workspace" + "path": "../../common/workspace" + }, + { + "path": "../../frontend/workspace-impl" + }, + { + "path": "../../frontend/electron-api" }, { "path": "../../common/debug" @@ -30,7 +33,7 @@ "path": "../../common/env" }, { - "path": "../../plugins/copilot" + "path": "../../common/y-indexeddb" }, { "path": "./tsconfig.node.json" diff --git a/packages/frontend/electron-api/package.json b/packages/frontend/electron-api/package.json new file mode 100644 index 0000000000..678d84f280 --- /dev/null +++ b/packages/frontend/electron-api/package.json @@ -0,0 +1,14 @@ +{ + "name": "@affine/electron-api", + "version": "0.10.3-canary.2", + "type": "module", + "private": true, + "main": "./src/index.ts", + "exports": { + ".": "./src/index.ts" + }, + "devDependencies": { + "@toeverything/infra": "workspace:*", + "electron": "^27.1.0" + } +} diff --git a/packages/frontend/electron-api/src/index.ts b/packages/frontend/electron-api/src/index.ts new file mode 100644 index 0000000000..2cccdddc6a --- /dev/null +++ b/packages/frontend/electron-api/src/index.ts @@ -0,0 +1,39 @@ +import type { + events as helperEvents, + handlers as helperHandlers, +} from '@affine/electron/helper/exposed'; +import type { + events as mainEvents, + handlers as mainHandlers, +} from '@affine/electron/main/exposed'; +import type { + affine as exposedAffineGlobal, + appInfo as exposedAppInfo, +} from '@affine/electron/preload/electron-api'; + +type MainHandlers = typeof mainHandlers; +type HelperHandlers = typeof helperHandlers; +type HelperEvents = typeof helperEvents; +type MainEvents = typeof mainEvents; +type ClientHandler = { + [namespace in keyof MainHandlers]: { + [method in keyof MainHandlers[namespace]]: MainHandlers[namespace][method] extends ( + arg0: any, + ...rest: infer A + ) => any + ? (...args: A) => Promise> + : never; + }; +} & HelperHandlers; +type ClientEvents = MainEvents & HelperEvents; + +export const appInfo = (globalThis as any).appInfo as + | typeof exposedAppInfo + | null; +export const apis = (globalThis as any).apis as ClientHandler | null; +export const events = (globalThis as any).events as ClientEvents | null; +export const affine = (globalThis as any).affine as + | typeof exposedAffineGlobal + | null; + +export type { UpdateMeta } from '@affine/electron/main/updater/event'; diff --git a/packages/common/sdk/tsconfig.json b/packages/frontend/electron-api/tsconfig.json similarity index 68% rename from packages/common/sdk/tsconfig.json rename to packages/frontend/electron-api/tsconfig.json index 33871974ad..638bfe244c 100644 --- a/packages/common/sdk/tsconfig.json +++ b/packages/frontend/electron-api/tsconfig.json @@ -4,12 +4,14 @@ "compilerOptions": { "composite": true, "noEmit": false, - "moduleResolution": "bundler", "outDir": "lib" }, "references": [ { - "path": "./tsconfig.node.json" + "path": "../../common/infra" + }, + { + "path": "../../frontend/electron" } ] } diff --git a/packages/frontend/electron/package.json b/packages/frontend/electron/package.json index 7a755f9ca1..fa70114ea2 100644 --- a/packages/frontend/electron/package.json +++ b/packages/frontend/electron/package.json @@ -23,19 +23,12 @@ "main": "./dist/main.js", "devDependencies": { "@affine-test/kit": "workspace:*", - "@affine/copilot-plugin": "workspace:*", "@affine/env": "workspace:*", - "@affine/hello-world-plugin": "workspace:*", - "@affine/image-preview-plugin": "workspace:*", "@affine/native": "workspace:*", - "@affine/outline-plugin": "workspace:*", - "@affine/sdk": "workspace:*", - "@affine/templates": "workspace:*", - "@affine/vue-hello-world-plugin": "workspace:*", - "@blocksuite/blocks": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/lit": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/presets": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/store": "0.11.0-nightly-202312220916-e3abcbb", + "@blocksuite/blocks": "0.11.0-nightly-202401020419-752a5b8", + "@blocksuite/lit": "0.11.0-nightly-202401020419-752a5b8", + "@blocksuite/presets": "0.11.0-nightly-202401020419-752a5b8", + "@blocksuite/store": "0.11.0-nightly-202401020419-752a5b8", "@electron-forge/cli": "^7.2.0", "@electron-forge/core": "^7.2.0", "@electron-forge/core-utils": "^7.2.0", @@ -45,7 +38,7 @@ "@electron-forge/maker-zip": "^7.2.0", "@electron-forge/plugin-auto-unpack-natives": "^7.2.0", "@electron-forge/shared-types": "^7.2.0", - "@electron/remote": "2.1.0", + "@electron/remote": "2.1.1", "@reforged/maker-appimage": "^3.3.1", "@toeverything/infra": "workspace:*", "@types/uuid": "^9.0.7", @@ -67,7 +60,7 @@ "ts-node": "^10.9.1", "undici": "^6.0.0", "uuid": "^9.0.1", - "vitest": "1.0.4", + "vitest": "1.1.3", "which": "^4.0.0", "zod": "^3.22.4" }, diff --git a/packages/frontend/electron/project.json b/packages/frontend/electron/project.json index d2044e8ee7..7f87ea5387 100644 --- a/packages/frontend/electron/project.json +++ b/packages/frontend/electron/project.json @@ -7,14 +7,7 @@ "targets": { "build": { "executor": "nx:run-script", - "dependsOn": [ - { - "projects": ["tag:plugin"], - "target": "build", - "params": "ignore" - }, - "^build" - ], + "dependsOn": ["^build"], "options": { "script": "build" }, diff --git a/packages/frontend/electron/scripts/common.ts b/packages/frontend/electron/scripts/common.ts index bcb6d31ec2..ab2b6f41b0 100644 --- a/packages/frontend/electron/scripts/common.ts +++ b/packages/frontend/electron/scripts/common.ts @@ -28,14 +28,7 @@ export const config = (): BuildOptions => { bundle: true, target: `node${NODE_MAJOR_VERSION}`, platform: 'node', - external: [ - 'electron', - 'electron-updater', - '@toeverything/plugin-infra', - 'yjs', - 'semver', - 'tinykeys', - ], + external: ['electron', 'electron-updater', 'yjs', 'semver', 'tinykeys'], format: 'cjs', loader: { '.node': 'copy', diff --git a/packages/frontend/electron/scripts/generate-assets.ts b/packages/frontend/electron/scripts/generate-assets.ts index 3b7de92dda..7d0fdefb7b 100755 --- a/packages/frontend/electron/scripts/generate-assets.ts +++ b/packages/frontend/electron/scripts/generate-assets.ts @@ -43,14 +43,6 @@ const cwd = repoRootDir; const { SKIP_NX_CACHE } = process.env; const nxFlag = SKIP_NX_CACHE ? '--skip-nx-cache' : ''; -if (!process.env.SKIP_PLUGIN_BUILD) { - spawnSync('yarn', ['build:plugins'], { - stdio: 'inherit', - env: process.env, - cwd, - }); -} - // step 1: build web dist if (!process.env.SKIP_WEB_BUILD) { spawnSync('yarn', ['nx', 'build', '@affine/core', nxFlag], { diff --git a/packages/frontend/electron/src/helper/dialog/dialog.ts b/packages/frontend/electron/src/helper/dialog/dialog.ts index 3c92d41183..c532556d7b 100644 --- a/packages/frontend/electron/src/helper/dialog/dialog.ts +++ b/packages/frontend/electron/src/helper/dialog/dialog.ts @@ -2,13 +2,6 @@ import path from 'node:path'; import { ValidationResult } from '@affine/native'; import { WorkspaceVersion } from '@toeverything/infra/blocksuite'; -import type { - FakeDialogResult, - LoadDBFileResult, - MoveDBFileResult, - SaveDBFileResult, - SelectDBFileLocationResult, -} from '@toeverything/infra/type'; import fs from 'fs-extra'; import { nanoid } from 'nanoid'; @@ -28,6 +21,45 @@ import { getWorkspacesBasePath, } from '../workspace/meta'; +export type ErrorMessage = + | 'DB_FILE_ALREADY_LOADED' + | 'DB_FILE_PATH_INVALID' + | 'DB_FILE_INVALID' + | 'DB_FILE_MIGRATION_FAILED' + | 'FILE_ALREADY_EXISTS' + | 'UNKNOWN_ERROR'; + +export interface LoadDBFileResult { + workspaceId?: string; + error?: ErrorMessage; + canceled?: boolean; +} + +export interface SaveDBFileResult { + filePath?: string; + canceled?: boolean; + error?: ErrorMessage; +} + +export interface SelectDBFileLocationResult { + filePath?: string; + error?: ErrorMessage; + canceled?: boolean; +} + +export interface MoveDBFileResult { + filePath?: string; + error?: ErrorMessage; + canceled?: boolean; +} + +// provide a backdoor to set dialog path for testing in playwright +export interface FakeDialogResult { + canceled?: boolean; + filePath?: string; + filePaths?: string[]; +} + // NOTE: // we are using native dialogs because HTML dialogs do not give full file paths diff --git a/packages/frontend/electron/src/helper/exposed.ts b/packages/frontend/electron/src/helper/exposed.ts index 63ac85e39f..e3adb3d465 100644 --- a/packages/frontend/electron/src/helper/exposed.ts +++ b/packages/frontend/electron/src/helper/exposed.ts @@ -1,25 +1,13 @@ -import type { - DBHandlers, - DialogHandlers, - WorkspaceHandlers, -} from '@toeverything/infra/type'; - import { dbEvents, dbHandlers } from './db'; import { dialogHandlers } from './dialog'; import { provideExposed } from './provide'; import { workspaceEvents, workspaceHandlers } from './workspace'; -type AllHandlers = { - db: DBHandlers; - workspace: WorkspaceHandlers; - dialog: DialogHandlers; -}; - export const handlers = { db: dbHandlers, workspace: workspaceHandlers, dialog: dialogHandlers, -} satisfies AllHandlers; +}; export const events = { db: dbEvents, diff --git a/packages/frontend/electron/src/helper/index.ts b/packages/frontend/electron/src/helper/index.ts index bdc862659d..7fe622259c 100644 --- a/packages/frontend/electron/src/helper/index.ts +++ b/packages/frontend/electron/src/helper/index.ts @@ -1,6 +1,6 @@ -import type { RendererToHelper } from '@toeverything/infra/preload/electron'; import { AsyncCall } from 'async-call-rpc'; +import type { RendererToHelper } from '../shared/type'; import { events, handlers } from './exposed'; import { logger } from './logger'; diff --git a/packages/frontend/electron/src/helper/main-rpc.ts b/packages/frontend/electron/src/helper/main-rpc.ts index 12e3623360..64862e44c9 100644 --- a/packages/frontend/electron/src/helper/main-rpc.ts +++ b/packages/frontend/electron/src/helper/main-rpc.ts @@ -1,10 +1,7 @@ import { assertExists } from '@blocksuite/global/utils'; -import type { - HelperToMain, - MainToHelper, -} from '@toeverything/infra/preload/electron'; import { AsyncCall } from 'async-call-rpc'; +import type { HelperToMain, MainToHelper } from '../shared/type'; import { exposed } from './provide'; const helperToMainServer: HelperToMain = { diff --git a/packages/frontend/electron/src/helper/provide.ts b/packages/frontend/electron/src/helper/provide.ts index 6c7b4e9b30..d508654cf9 100644 --- a/packages/frontend/electron/src/helper/provide.ts +++ b/packages/frontend/electron/src/helper/provide.ts @@ -1,4 +1,4 @@ -import type { ExposedMeta } from '@toeverything/infra/preload/electron'; +import type { ExposedMeta } from '../shared/type'; /** * A naive DI implementation to get rid of circular dependency. diff --git a/packages/frontend/electron/src/main/handlers.ts b/packages/frontend/electron/src/main/handlers.ts index 985ce0946f..c97bdafb94 100644 --- a/packages/frontend/electron/src/main/handlers.ts +++ b/packages/frontend/electron/src/main/handlers.ts @@ -1,12 +1,3 @@ -import type { - ClipboardHandlerManager, - ConfigStorageHandlerManager, - DebugHandlerManager, - ExportHandlerManager, - UIHandlerManager, - UnwrapManagerHandlerToServerSide, - UpdaterHandlerManager, -} from '@toeverything/infra/index'; import { ipcMain } from 'electron'; import { clipboardHandlers } from './clipboard'; @@ -25,33 +16,6 @@ export const debugHandlers = { }, }; -type AllHandlers = { - debug: UnwrapManagerHandlerToServerSide< - Electron.IpcMainInvokeEvent, - DebugHandlerManager - >; - clipboard: UnwrapManagerHandlerToServerSide< - Electron.IpcMainInvokeEvent, - ClipboardHandlerManager - >; - export: UnwrapManagerHandlerToServerSide< - Electron.IpcMainInvokeEvent, - ExportHandlerManager - >; - ui: UnwrapManagerHandlerToServerSide< - Electron.IpcMainInvokeEvent, - UIHandlerManager - >; - updater: UnwrapManagerHandlerToServerSide< - Electron.IpcMainInvokeEvent, - UpdaterHandlerManager - >; - configStorage: UnwrapManagerHandlerToServerSide< - Electron.IpcMainInvokeEvent, - ConfigStorageHandlerManager - >; -}; - // Note: all of these handlers will be the single-source-of-truth for the apis exposed to the renderer process export const allHandlers = { debug: debugHandlers, @@ -60,7 +24,7 @@ export const allHandlers = { export: exportHandlers, updater: updaterHandlers, configStorage: configStorageHandlers, -} satisfies AllHandlers; +}; export const registerHandlers = () => { // TODO: listen to namespace instead of individual event types diff --git a/packages/frontend/electron/src/main/helper-process.ts b/packages/frontend/electron/src/main/helper-process.ts index d2d3d05fce..2af2c3ef08 100644 --- a/packages/frontend/electron/src/main/helper-process.ts +++ b/packages/frontend/electron/src/main/helper-process.ts @@ -1,9 +1,5 @@ import path from 'node:path'; -import type { - HelperToMain, - MainToHelper, -} from '@toeverything/infra/preload/electron'; import { type _AsyncVersionOf, AsyncCall } from 'async-call-rpc'; import { app, @@ -15,6 +11,7 @@ import { type WebContents, } from 'electron'; +import type { HelperToMain, MainToHelper } from '../shared/type'; import { MessageEventChannel } from '../shared/utils'; import { logger } from './logger'; diff --git a/packages/frontend/electron/src/main/updater/custom-github-provider.ts b/packages/frontend/electron/src/main/updater/custom-github-provider.ts index 75a9861f6a..1690e9391d 100644 --- a/packages/frontend/electron/src/main/updater/custom-github-provider.ts +++ b/packages/frontend/electron/src/main/updater/custom-github-provider.ts @@ -24,6 +24,17 @@ interface GithubUpdateInfo extends UpdateInfo { tag: string; } +interface GithubRelease { + id: number; + tag_name: string; + target_commitish: string; + name: string; + draft: boolean; + prerelease: boolean; + created_at: string; + published_at: string; +} + const hrefRegExp = /\/tag\/([^/]+)$/; export class CustomGitHubProvider extends BaseGitHubProvider { @@ -54,7 +65,7 @@ export class CustomGitHubProvider extends BaseGitHubProvider { const feed = parseXml(feedXml); // noinspection TypeScriptValidateJSTypes - const latestRelease = feed.element( + let latestRelease = feed.element( 'entry', false, `No published versions on GitHub` @@ -74,6 +85,10 @@ export class CustomGitHubProvider extends BaseGitHubProvider { ); } + const releaseTag = await this.getLatestTagByRelease( + currentChannel, + cancellationToken + ); for (const element of feed.getElements('entry')) { // noinspection TypeScriptValidateJSTypes const hrefElement = hrefRegExp.exec( @@ -90,9 +105,16 @@ export class CustomGitHubProvider extends BaseGitHubProvider { const hrefChannel = (semver.prerelease(hrefTag)?.[0] as string) || 'stable'; - const isNextPreRelease = hrefChannel === currentChannel; + let isNextPreRelease = false; + if (releaseTag) { + isNextPreRelease = releaseTag === hrefTag; + } else { + isNextPreRelease = hrefChannel === currentChannel; + } + if (isNextPreRelease) { tag = hrefTag; + latestRelease = element; break; } } @@ -154,7 +176,7 @@ export class CustomGitHubProvider extends BaseGitHubProvider { } const result = parseUpdateInfo(rawData, channelFile, channelFileUrl); - if (result.releaseName === null) { + if (result.releaseName == null) { result.releaseName = latestRelease.elementValueOrEmpty('title'); } @@ -176,6 +198,51 @@ export class CustomGitHubProvider extends BaseGitHubProvider { return `/${this.options.owner}/${this.options.repo}/releases`; } + /** + * Use release api to get latest version to filter draft version. + * But this api have low request limit 60-times/1-hour, use this to help, not depend on it + * https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28 + * https://api.github.com/repos/toeverything/affine/releases + * https://docs.github.com/en/rest/rate-limit/rate-limit?apiVersion=2022-11-28#about-rate-limits + */ + private async getLatestTagByRelease( + currentChannel: string, + cancellationToken: CancellationToken + ) { + try { + const releasesStr = await this.httpRequest( + newUrlFromBase(`/repos${this.basePath}`, this.baseApiUrl), + { + accept: 'Accept: application/vnd.github+json', + 'X-GitHub-Api-Version': '2022-11-28', + }, + cancellationToken + ); + + if (!releasesStr) { + return null; + } + + const releases: GithubRelease[] = JSON.parse(releasesStr); + for (const release of releases) { + if (release.draft) { + continue; + } + + const releaseTag = release.tag_name; + const releaseChannel = + (semver.prerelease(releaseTag)?.[0] as string) || 'stable'; + if (releaseChannel === currentChannel) { + return release.tag_name; + } + } + } catch (e: any) { + console.info(`Cannot parse release: ${e.stack || e.message}`); + } + + return null; + } + resolveFiles(updateInfo: GithubUpdateInfo): Array { // still replace space to - due to backward compatibility return resolveFiles(updateInfo, this.baseUrl, p => diff --git a/packages/frontend/electron/src/preload/bootstrap.ts b/packages/frontend/electron/src/preload/bootstrap.ts index 444d0deb40..d1c1137517 100644 --- a/packages/frontend/electron/src/preload/bootstrap.ts +++ b/packages/frontend/electron/src/preload/bootstrap.ts @@ -1,57 +1,15 @@ -import { contextBridge, ipcRenderer } from 'electron'; +import { contextBridge } from 'electron'; -(async () => { - const { appInfo, getElectronAPIs } = await import( - '@toeverything/infra/preload/electron' - ); - const { apis, events } = getElectronAPIs(); +import { affine, appInfo, getElectronAPIs } from './electron-api'; - contextBridge.exposeInMainWorld('appInfo', appInfo); - contextBridge.exposeInMainWorld('apis', apis); - contextBridge.exposeInMainWorld('events', events); +const { apis, events } = getElectronAPIs(); - // Credit to microsoft/vscode - const globals = { - ipcRenderer: { - send(channel: string, ...args: any[]) { - ipcRenderer.send(channel, ...args); - }, +contextBridge.exposeInMainWorld('appInfo', appInfo); +contextBridge.exposeInMainWorld('apis', apis); +contextBridge.exposeInMainWorld('events', events); - invoke(channel: string, ...args: any[]) { - return ipcRenderer.invoke(channel, ...args); - }, - - on( - channel: string, - listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void - ) { - ipcRenderer.on(channel, listener); - return this; - }, - - once( - channel: string, - listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void - ) { - ipcRenderer.once(channel, listener); - return this; - }, - - removeListener( - channel: string, - listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void - ) { - ipcRenderer.removeListener(channel, listener); - return this; - }, - }, - }; - - try { - contextBridge.exposeInMainWorld('affine', globals); - } catch (error) { - console.error('Failed to expose affine APIs to window object!', error); - } -})().catch(err => { - console.error('Failed to bootstrap preload script!', err); -}); +try { + contextBridge.exposeInMainWorld('affine', affine); +} catch (error) { + console.error('Failed to expose affine APIs to window object!', error); +} diff --git a/packages/common/infra/src/preload/electron.ts b/packages/frontend/electron/src/preload/electron-api.ts similarity index 85% rename from packages/common/infra/src/preload/electron.ts rename to packages/frontend/electron/src/preload/electron-api.ts index 087141aaa5..aaec001f6d 100644 --- a/packages/common/infra/src/preload/electron.ts +++ b/packages/frontend/electron/src/preload/electron-api.ts @@ -1,37 +1,50 @@ // Please add modules to `external` in `rollupOptions` to avoid wrong bundling. import { AsyncCall, type EventBasedChannel } from 'async-call-rpc'; -import type { app, dialog, shell } from 'electron'; import { ipcRenderer } from 'electron'; import { Subject } from 'rxjs'; import { z } from 'zod'; -export interface ExposedMeta { - handlers: [string, string[]][]; - events: [string, string[]][]; -} +import type { + ExposedMeta, + HelperToRenderer, + RendererToHelper, +} from '../shared/type'; -// render <-> helper -export interface RendererToHelper { - postEvent: (channel: string, ...args: any[]) => void; -} +export const affine = { + ipcRenderer: { + send(channel: string, ...args: any[]) { + ipcRenderer.send(channel, ...args); + }, -export interface HelperToRenderer { - [key: string]: (...args: any[]) => Promise; -} + invoke(channel: string, ...args: any[]) { + return ipcRenderer.invoke(channel, ...args); + }, -// helper <-> main -export interface HelperToMain { - getMeta: () => ExposedMeta; -} + on( + channel: string, + listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void + ) { + ipcRenderer.on(channel, listener); + return this; + }, -export type MainToHelper = Pick< - typeof dialog & typeof shell & typeof app, - | 'showOpenDialog' - | 'showSaveDialog' - | 'openExternal' - | 'showItemInFolder' - | 'getPath' ->; + once( + channel: string, + listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void + ) { + ipcRenderer.once(channel, listener); + return this; + }, + + removeListener( + channel: string, + listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void + ) { + ipcRenderer.removeListener(channel, listener); + return this; + }, + }, +}; export function getElectronAPIs() { const mainAPIs = getMainAPIs(); diff --git a/packages/frontend/electron/src/shared/type.ts b/packages/frontend/electron/src/shared/type.ts new file mode 100644 index 0000000000..0cf8db77e3 --- /dev/null +++ b/packages/frontend/electron/src/shared/type.ts @@ -0,0 +1,29 @@ +import type { app, dialog, shell } from 'electron'; + +export interface ExposedMeta { + handlers: [string, string[]][]; + events: [string, string[]][]; +} + +// render <-> helper +export interface RendererToHelper { + postEvent: (channel: string, ...args: any[]) => void; +} + +export interface HelperToRenderer { + [key: string]: (...args: any[]) => Promise; +} + +// helper <-> main +export interface HelperToMain { + getMeta: () => ExposedMeta; +} + +export type MainToHelper = Pick< + typeof dialog & typeof shell & typeof app, + | 'showOpenDialog' + | 'showSaveDialog' + | 'openExternal' + | 'showItemInFolder' + | 'getPath' +>; diff --git a/packages/frontend/electron/test/db/ensure-db.spec.ts b/packages/frontend/electron/test/db/ensure-db.spec.ts index a485418cc4..b4ad5c1f60 100644 --- a/packages/frontend/electron/test/db/ensure-db.spec.ts +++ b/packages/frontend/electron/test/db/ensure-db.spec.ts @@ -3,7 +3,7 @@ import { setTimeout } from 'node:timers/promises'; import { removeWithRetry } from '@affine-test/kit/utils/utils'; import { v4 } from 'uuid'; -import { afterEach, beforeEach, expect, test, vi } from 'vitest'; +import { afterAll, afterEach, beforeEach, expect, test, vi } from 'vitest'; const tmpDir = path.join(__dirname, 'tmp'); const appDataPath = path.join(tmpDir, 'app-data'); @@ -48,6 +48,10 @@ afterEach(async () => { vi.useRealTimers(); }); +afterAll(() => { + vi.doUnmock('@affine/electron/helper/main-rpc'); +}); + test('can get a valid WorkspaceSQLiteDB', async () => { const { ensureSQLiteDB } = await import( '@affine/electron/helper/db/ensure-db' diff --git a/packages/frontend/electron/test/db/migration.spec.ts b/packages/frontend/electron/test/db/migration.spec.ts index 809e05e3b8..c0e96d2d68 100644 --- a/packages/frontend/electron/test/db/migration.spec.ts +++ b/packages/frontend/electron/test/db/migration.spec.ts @@ -1,12 +1,16 @@ import path from 'node:path'; -import { - copyToTemp, - migrateToSubdocAndReplaceDatabase, -} from '@affine/electron/helper/db/migration'; import { SqliteConnection } from '@affine/native'; import { removeWithRetry } from '@affine-test/kit/utils/utils'; -import { afterEach, describe, expect, it, vi } from 'vitest'; +import { + afterAll, + afterEach, + beforeAll, + describe, + expect, + it, + vi, +} from 'vitest'; import { applyUpdate, Doc as YDoc } from 'yjs'; const tmpDir = path.join(__dirname, 'tmp'); @@ -14,18 +18,31 @@ const testDBFilePath = path.resolve(__dirname, 'old-db.affine'); const appDataPath = path.join(tmpDir, 'app-data'); -vi.mock('@affine/electron/helper/main-rpc', () => ({ - mainRPC: { - getPath: async () => appDataPath, - }, -})); +beforeAll(() => { + vi.doMock('@affine/electron/helper/main-rpc', () => ({ + mainRPC: { + getPath: async () => appDataPath, + channel: { + on: () => {}, + send: () => {}, + }, + }, + })); +}); afterEach(async () => { await removeWithRetry(tmpDir); }); +afterAll(() => { + vi.doUnmock('@affine/electron/helper/main-rpc'); +}); + describe('migrateToSubdocAndReplaceDatabase', () => { it('should migrate and replace the database', async () => { + const { copyToTemp, migrateToSubdocAndReplaceDatabase } = await import( + '@affine/electron/helper/db/migration' + ); const copiedDbFilePath = await copyToTemp(testDBFilePath); await migrateToSubdocAndReplaceDatabase(copiedDbFilePath); diff --git a/packages/frontend/electron/test/db/workspace-db-adapter.spec.ts b/packages/frontend/electron/test/db/workspace-db-adapter.spec.ts index f910d3db7e..2394df4ad4 100644 --- a/packages/frontend/electron/test/db/workspace-db-adapter.spec.ts +++ b/packages/frontend/electron/test/db/workspace-db-adapter.spec.ts @@ -4,22 +4,28 @@ import { dbSubjects } from '@affine/electron/helper/db/subjects'; import { removeWithRetry } from '@affine-test/kit/utils/utils'; import fs from 'fs-extra'; import { v4 } from 'uuid'; -import { afterEach, expect, test, vi } from 'vitest'; +import { afterAll, afterEach, beforeAll, expect, test, vi } from 'vitest'; import { Doc as YDoc, encodeStateAsUpdate } from 'yjs'; const tmpDir = path.join(__dirname, 'tmp'); const appDataPath = path.join(tmpDir, 'app-data'); -vi.doMock('@affine/electron/helper/main-rpc', () => ({ - mainRPC: { - getPath: async () => appDataPath, - }, -})); +beforeAll(() => { + vi.doMock('@affine/electron/helper/main-rpc', () => ({ + mainRPC: { + getPath: async () => appDataPath, + }, + })); +}); afterEach(async () => { await removeWithRetry(tmpDir); }); +afterAll(() => { + vi.doUnmock('@affine/electron/helper/main-rpc'); +}); + let testYDoc: YDoc; let testYSubDoc: YDoc; diff --git a/packages/frontend/electron/test/main/fixtures/feeds.txt b/packages/frontend/electron/test/main/fixtures/feeds.txt new file mode 100644 index 0000000000..479db4e7f4 --- /dev/null +++ b/packages/frontend/electron/test/main/fixtures/feeds.txt @@ -0,0 +1,103 @@ + + + tag:github.com,2008:https://github.com/toeverything/AFFiNE/releases + + + Release notes from AFFiNE + 2023-12-28T16:36:36+08:00 + + tag:github.com,2008:Repository/519859998/0.11.0-nightly-202312280901-e11e827 + 2023-12-28T17:23:15+08:00 + + 0.11.0-nightly-202312280901-e11e827 + No content. + + github-actions[bot] + + + + + tag:github.com,2008:Repository/519859998/v0.11.1 + 2023-12-27T19:40:15+08:00 + + 0.11.1 + <p>fix(core): enable page history for beta/stable (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056965718" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5415" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5415/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5415">#5415</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/pengx17/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/pengx17">@pengx17</a><br> +fix(component): fix font display on safari (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2055383919" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5393" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5393/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5393">#5393</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/EYHN/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/EYHN">@EYHN</a><br> +fix(core): avatars are not aligned (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056136302" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5404" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5404/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5404">#5404</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/JimmFly/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/JimmFly">@JimmFly</a><br> +fix(core): trash page footer display issue (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056129348" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5402" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5402/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5402">#5402</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/pengx17/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/pengx17">@pengx17</a><br> +fix(electron): set stable base url to app.affine.pro (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056113464" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5401" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5401/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5401">#5401</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/joooye34/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/joooye34">@joooye34</a><br> +fix(core): about setting blink issue (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056090521" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5399" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5399/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5399">#5399</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/pengx17/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/pengx17">@pengx17</a><br> +fix(core): workpace list blink issue on open (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056096694" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5400" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5400/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5400">#5400</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/pengx17/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/pengx17">@pengx17</a><br> +chore(core): add background color to questionnaire (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2055986563" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5396" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5396/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5396">#5396</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/JimmFly/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/JimmFly">@JimmFly</a><br> +fix(core): correct title of onboarding article-2 (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2053650286" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5387" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5387/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5387">#5387</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/CatsJuice/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/CatsJuice">@CatsJuice</a><br> +fix: use prefix in electron to prevent formdata bug (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2055616549" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5395" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5395/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5395">#5395</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/darkskygit/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/darkskygit">@darkskygit</a><br> +fix(core): fix flickering workspace list (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2055363698" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5391" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5391/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5391">#5391</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/EYHN/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/EYHN">@EYHN</a><br> +fix(workspace): fix svg file with xml header (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2053693777" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5388" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5388/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5388">#5388</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/EYHN/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/EYHN">@EYHN</a><br> +feat: bump blocksuite (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2053645163" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5386" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5386/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5386">#5386</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/regischen/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/regischen">@regischen</a></p> + + github-actions[bot] + + + + + tag:github.com,2008:Repository/519859998/v0.11.1-beta.1 + 2023-12-27T18:30:52+08:00 + + 0.11.1-beta.1 + <p>fix(core): enable page history for beta/stable (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056965718" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5415" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5415/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5415">#5415</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/pengx17/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/pengx17">@pengx17</a><br> +fix(component): fix font display on safari (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2055383919" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5393" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5393/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5393">#5393</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/EYHN/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/EYHN">@EYHN</a><br> +fix(core): avatars are not aligned (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056136302" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5404" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5404/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5404">#5404</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/JimmFly/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/JimmFly">@JimmFly</a><br> +fix(core): trash page footer display issue (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056129348" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5402" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5402/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5402">#5402</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/pengx17/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/pengx17">@pengx17</a><br> +fix(electron): set stable base url to app.affine.pro (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056113464" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5401" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5401/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5401">#5401</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/joooye34/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/joooye34">@joooye34</a><br> +fix(core): about setting blink issue (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056090521" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5399" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5399/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5399">#5399</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/pengx17/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/pengx17">@pengx17</a><br> +fix(core): workpace list blink issue on open (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056096694" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5400" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5400/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5400">#5400</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/pengx17/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/pengx17">@pengx17</a><br> +chore(core): add background color to questionnaire (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2055986563" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5396" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5396/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5396">#5396</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/JimmFly/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/JimmFly">@JimmFly</a><br> +fix(core): correct title of onboarding article-2 (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2053650286" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5387" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5387/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5387">#5387</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/CatsJuice/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/CatsJuice">@CatsJuice</a><br> +fix: use prefix in electron to prevent formdata bug (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2055616549" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5395" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5395/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5395">#5395</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/darkskygit/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/darkskygit">@darkskygit</a><br> +fix(core): fix flickering workspace list (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2055363698" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5391" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5391/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5391">#5391</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/EYHN/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/EYHN">@EYHN</a><br> +fix(workspace): fix svg file with xml header (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2053693777" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5388" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5388/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5388">#5388</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/EYHN/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/EYHN">@EYHN</a><br> +feat: bump blocksuite (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2053645163" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5386" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5386/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5386">#5386</a>) <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/regischen/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/regischen">@regischen</a></p> + + github-actions[bot] + + + + + tag:github.com,2008:Repository/519859998/v0.11.1-canary.2 + 2023-12-28T10:47:52+08:00 + + 0.11.1-canary.2 + No content. + + github-actions[bot] + + + + + tag:github.com,2008:Repository/519859998/v0.11.1-canary.1 + 2023-12-27T10:47:52+08:00 + + 0.11.1-canary.1 + <h2>What's Changed</h2> +<ul> +<li>fix(core): remove plugins settings by <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/pengx17/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/pengx17">@pengx17</a> in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2048206391" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5337" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5337/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5337">#5337</a></li> +<li>chore: bump up @vitejs/plugin-vue version to v5 by <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/renovate/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/renovate">@renovate</a> in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2055571407" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5394" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5394/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5394">#5394</a></li> +<li>fix(core): correct title of onboarding article-2 by <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/CatsJuice/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/CatsJuice">@CatsJuice</a> in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2053650286" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5387" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5387/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5387">#5387</a></li> +<li>chore(core): add background color to questionnaire by <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/JimmFly/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/JimmFly">@JimmFly</a> in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2055986563" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5396" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5396/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5396">#5396</a></li> +<li>ci: define tag name to fix nightly release failing by <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/joooye34/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/joooye34">@joooye34</a> in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056068417" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5397" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5397/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5397">#5397</a></li> +<li>fix(core): workpace list blink issue on open by <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/pengx17/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/pengx17">@pengx17</a> in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056096694" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5400" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5400/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5400">#5400</a></li> +<li>fix(core): about setting blink issue by <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/pengx17/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/pengx17">@pengx17</a> in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056090521" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5399" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5399/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5399">#5399</a></li> +<li>fix(electron): set stable base url to app.affine.pro by <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/joooye34/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/joooye34">@joooye34</a> in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056113464" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5401" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5401/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5401">#5401</a></li> +<li>fix(core): trash page footer display issue by <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/pengx17/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/pengx17">@pengx17</a> in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056129348" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5402" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5402/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5402">#5402</a></li> +<li>chore: bump up @types/supertest version to v6 by <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/renovate/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/renovate">@renovate</a> in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2053226342" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5376" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5376/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5376">#5376</a></li> +<li>chore: bump up react-i18next version to v14 by <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/renovate/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/renovate">@renovate</a> in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2052675317" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5375" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5375/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5375">#5375</a></li> +<li>fix(core): avatars are not aligned by <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/JimmFly/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/JimmFly">@JimmFly</a> in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056136302" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5404" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5404/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5404">#5404</a></li> +<li>fix(infra): workaround for self-referencing in storybook by <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/pengx17/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/pengx17">@pengx17</a> in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="2056165203" data-permission-text="Title is private" data-url="https://github.com/toeverything/AFFiNE/issues/5406" data-hovercard-type="pull_request" data-hovercard-url="/toeverything/AFFiNE/pull/5406/hovercard" href="https://github.com/toeverything/AFFiNE/pull/5406">#5406</a></li> +</ul> +<p><strong>Full Changelog</strong>: <a class="commit-link" href="https://github.com/toeverything/AFFiNE/compare/v0.11.1-canary.0...v0.11.1-canary.1"><tt>v0.11.1-canary.0...v0.11.1-canary.1</tt></a></p> + + github-actions[bot] + + + + diff --git a/packages/frontend/electron/test/main/fixtures/release-list.txt b/packages/frontend/electron/test/main/fixtures/release-list.txt new file mode 100644 index 0000000000..172fd73260 --- /dev/null +++ b/packages/frontend/electron/test/main/fixtures/release-list.txt @@ -0,0 +1,94 @@ +[ + { + "url": "https://api.github.com/repos/toeverything/AFFiNE/releases/135252810", + "assets_url": "https://api.github.com/repos/toeverything/AFFiNE/releases/135252810/assets", + "upload_url": "https://uploads.github.com/repos/toeverything/AFFiNE/releases/135252810/assets{?name,label}", + "html_url": "https://github.com/toeverything/AFFiNE/releases/tag/0.11.0-nightly-202312280901-e11e827", + "id": 135252810, + "node_id": "RE_kwDOHvxvHs4ID8tK", + "tag_name": "0.11.0-nightly-202312280901-e11e827", + "target_commitish": "canary", + "name": "0.11.0-nightly-202312280901-e11e827", + "draft": false, + "prerelease": true, + "created_at": "2023-12-28T08:36:36Z", + "published_at": "2023-12-28T09:23:07Z", + "tarball_url": "https://api.github.com/repos/toeverything/AFFiNE/tarball/0.11.0-nightly-202312280901-e11e827", + "zipball_url": "https://api.github.com/repos/toeverything/AFFiNE/zipball/0.11.0-nightly-202312280901-e11e827", + "body": "" + }, + { + "url": "https://api.github.com/repos/toeverything/AFFiNE/releases/135173430", + "assets_url": "https://api.github.com/repos/toeverything/AFFiNE/releases/135173430/assets", + "upload_url": "https://uploads.github.com/repos/toeverything/AFFiNE/releases/135173430/assets{?name,label}", + "html_url": "https://github.com/toeverything/AFFiNE/releases/tag/v0.11.1", + "id": 135173430, + "node_id": "RE_kwDOHvxvHs4IDpU2", + "tag_name": "v0.11.1", + "target_commitish": "canary", + "name": "0.11.1", + "draft": false, + "prerelease": false, + "created_at": "2023-12-27T06:39:59Z", + "published_at": "2023-12-27T11:40:15Z", + "tarball_url": "https://api.github.com/repos/toeverything/AFFiNE/tarball/v0.11.1", + "zipball_url": "https://api.github.com/repos/toeverything/AFFiNE/zipball/v0.11.1", + "mentions_count": 7 + }, + { + "url": "https://api.github.com/repos/toeverything/AFFiNE/releases/135163918", + "assets_url": "https://api.github.com/repos/toeverything/AFFiNE/releases/135163918/assets", + "upload_url": "https://uploads.github.com/repos/toeverything/AFFiNE/releases/135163918/assets{?name,label}", + "html_url": "https://github.com/toeverything/AFFiNE/releases/tag/v0.11.1-beta.1", + "id": 135163918, + "node_id": "RE_kwDOHvxvHs4IDnAO", + "tag_name": "v0.11.1-beta.1", + "target_commitish": "canary", + "name": "0.11.1-beta.1", + "draft": false, + "prerelease": true, + "created_at": "2023-12-27T06:39:59Z", + "published_at": "2023-12-27T10:30:52Z", + "tarball_url": "https://api.github.com/repos/toeverything/AFFiNE/tarball/v0.11.1-beta.1", + "zipball_url": "https://api.github.com/repos/toeverything/AFFiNE/zipball/v0.11.1-beta.1", + "mentions_count": 7 + }, + { + "url": "https://api.github.com/repos/toeverything/AFFiNE/releases/135103520", + "assets_url": "https://api.github.com/repos/toeverything/AFFiNE/releases/135103520/assets", + "upload_url": "https://uploads.github.com/repos/toeverything/AFFiNE/releases/135103520/assets{?name,label}", + "html_url": "https://github.com/toeverything/AFFiNE/releases/tag/v0.11.1-canary.1", + "id": 135103520, + "node_id": "RE_kwDOHvxvHs4IDYQg", + "tag_name": "v0.11.1-canary.2", + "target_commitish": "canary", + "name": "0.11.1-canary.2", + "draft": true, + "prerelease": true, + "created_at": "2023-12-26T12:27:20Z", + "published_at": "2023-12-26T13:24:28Z", + "tarball_url": "https://api.github.com/repos/toeverything/AFFiNE/tarball/v0.11.1-canary.1", + "zipball_url": "https://api.github.com/repos/toeverything/AFFiNE/zipball/v0.11.1-canary.1", + "body": "## What's Changed\r\n* fix(core): remove plugins settings by @pengx17 in https://github.com/toeverything/AFFiNE/pull/5337\r\n* chore: bump up @vitejs/plugin-vue version to v5 by @renovate in https://github.com/toeverything/AFFiNE/pull/5394\r\n* fix(core): correct title of onboarding article-2 by @CatsJuice in https://github.com/toeverything/AFFiNE/pull/5387\r\n* chore(core): add background color to questionnaire by @JimmFly in https://github.com/toeverything/AFFiNE/pull/5396\r\n* ci: define tag name to fix nightly release failing by @joooye34 in https://github.com/toeverything/AFFiNE/pull/5397\r\n* fix(core): workpace list blink issue on open by @pengx17 in https://github.com/toeverything/AFFiNE/pull/5400\r\n* fix(core): about setting blink issue by @pengx17 in https://github.com/toeverything/AFFiNE/pull/5399\r\n* fix(electron): set stable base url to app.affine.pro by @joooye34 in https://github.com/toeverything/AFFiNE/pull/5401\r\n* fix(core): trash page footer display issue by @pengx17 in https://github.com/toeverything/AFFiNE/pull/5402\r\n* chore: bump up @types/supertest version to v6 by @renovate in https://github.com/toeverything/AFFiNE/pull/5376\r\n* chore: bump up react-i18next version to v14 by @renovate in https://github.com/toeverything/AFFiNE/pull/5375\r\n* fix(core): avatars are not aligned by @JimmFly in https://github.com/toeverything/AFFiNE/pull/5404\r\n* fix(infra): workaround for self-referencing in storybook by @pengx17 in https://github.com/toeverything/AFFiNE/pull/5406\r\n\r\n\r\n**Full Changelog**: https://github.com/toeverything/AFFiNE/compare/v0.11.1-canary.0...v0.11.1-canary.1", + "mentions_count": 5 + }, + { + "url": "https://api.github.com/repos/toeverything/AFFiNE/releases/135103520", + "assets_url": "https://api.github.com/repos/toeverything/AFFiNE/releases/135103520/assets", + "upload_url": "https://uploads.github.com/repos/toeverything/AFFiNE/releases/135103520/assets{?name,label}", + "html_url": "https://github.com/toeverything/AFFiNE/releases/tag/v0.11.1-canary.1", + "id": 135103520, + "node_id": "RE_kwDOHvxvHs4IDYQg", + "tag_name": "v0.11.1-canary.1", + "target_commitish": "canary", + "name": "0.11.1-canary.1", + "draft": false, + "prerelease": true, + "created_at": "2023-12-26T12:27:20Z", + "published_at": "2023-12-26T13:24:28Z", + "tarball_url": "https://api.github.com/repos/toeverything/AFFiNE/tarball/v0.11.1-canary.1", + "zipball_url": "https://api.github.com/repos/toeverything/AFFiNE/zipball/v0.11.1-canary.1", + "body": "## What's Changed\r\n* fix(core): remove plugins settings by @pengx17 in https://github.com/toeverything/AFFiNE/pull/5337\r\n* chore: bump up @vitejs/plugin-vue version to v5 by @renovate in https://github.com/toeverything/AFFiNE/pull/5394\r\n* fix(core): correct title of onboarding article-2 by @CatsJuice in https://github.com/toeverything/AFFiNE/pull/5387\r\n* chore(core): add background color to questionnaire by @JimmFly in https://github.com/toeverything/AFFiNE/pull/5396\r\n* ci: define tag name to fix nightly release failing by @joooye34 in https://github.com/toeverything/AFFiNE/pull/5397\r\n* fix(core): workpace list blink issue on open by @pengx17 in https://github.com/toeverything/AFFiNE/pull/5400\r\n* fix(core): about setting blink issue by @pengx17 in https://github.com/toeverything/AFFiNE/pull/5399\r\n* fix(electron): set stable base url to app.affine.pro by @joooye34 in https://github.com/toeverything/AFFiNE/pull/5401\r\n* fix(core): trash page footer display issue by @pengx17 in https://github.com/toeverything/AFFiNE/pull/5402\r\n* chore: bump up @types/supertest version to v6 by @renovate in https://github.com/toeverything/AFFiNE/pull/5376\r\n* chore: bump up react-i18next version to v14 by @renovate in https://github.com/toeverything/AFFiNE/pull/5375\r\n* fix(core): avatars are not aligned by @JimmFly in https://github.com/toeverything/AFFiNE/pull/5404\r\n* fix(infra): workaround for self-referencing in storybook by @pengx17 in https://github.com/toeverything/AFFiNE/pull/5406\r\n\r\n\r\n**Full Changelog**: https://github.com/toeverything/AFFiNE/compare/v0.11.1-canary.0...v0.11.1-canary.1", + "mentions_count": 5 + } +] diff --git a/packages/frontend/electron/test/main/fixtures/releases/0.11.1-beta.1.txt b/packages/frontend/electron/test/main/fixtures/releases/0.11.1-beta.1.txt new file mode 100644 index 0000000000..fd9a7434cf --- /dev/null +++ b/packages/frontend/electron/test/main/fixtures/releases/0.11.1-beta.1.txt @@ -0,0 +1,20 @@ +version: 0.11.1-beta.1 +files: + - url: affine-beta-windows-x64.exe + sha512: uQdF7bEZteCMp/bT7vwCjlEcAf6osW9zZ+Q5grEkmbHPpcqCCzLudguXqHIwohO4GGq9pS8H4kJzG0LZc+SmXg== + size: 179515752 + - url: affine-beta-macos-arm64.dmg + sha512: gRsi4XO4+kREQuLX2CnS2V9vvUmBMmoGR6MvoB6TEFm1WiC8k8v69DRKYQ0Vjlom/j9HZlBEYUTqcW7IsMgrpw== + size: 169726061 + - url: affine-beta-macos-arm64.zip + sha512: +aXkjJfQnp2dUz3Y0i5cL6V5Hm1OkYVlwM/KAcZfLlvLoU3zQ0zSZvJ6+H2IlIhOebSq56Ip76H5NP8fe7UnOw== + size: 169007175 + - url: affine-beta-macos-x64.dmg + sha512: i5V95dLx3iWFpNj89wFU40THT3Oeow8g706Z6/mG1zYOIR3kXUkIpp6+wJmlfe9g4iwNmRd0rgI4HAG5LaQagg== + size: 175712730 + - url: affine-beta-macos-x64.zip + sha512: DnRUHcj+4FluII5kTbUuEAQI2CIRufd1Z0P98pwa/uX5hk2iOj1QzMD8WM+MTbFNC6rZvMtMlos8GyVLsZmK0w== + size: 175235583 +path: affine-beta-windows-x64.exe +sha512: uQdF7bEZteCMp/bT7vwCjlEcAf6osW9zZ+Q5grEkmbHPpcqCCzLudguXqHIwohO4GGq9pS8H4kJzG0LZc+SmXg== +releaseDate: 2023-12-27T08:59:31.826Z diff --git a/packages/frontend/electron/test/main/fixtures/releases/0.11.1-canary.1.txt b/packages/frontend/electron/test/main/fixtures/releases/0.11.1-canary.1.txt new file mode 100644 index 0000000000..36dc8f50c8 --- /dev/null +++ b/packages/frontend/electron/test/main/fixtures/releases/0.11.1-canary.1.txt @@ -0,0 +1,20 @@ +version: 0.11.1-canary.1 +files: + - url: affine-canary-windows-x64.exe + sha512: qbK4N6+axVO2dA/iPzfhANWxCZXY1S3ci9qYIT1v/h0oCjc6vqpXU+2KRGL5mplL6wmVgJAOpqrfnq9gHMsfDg== + size: 179526504 + - url: affine-canary-macos-arm64.dmg + sha512: ++LAGuxTmFAVd65k8UpKKfU19iisvXHKDDfPkGlTVC000QP3foeS21BmTgYnM1ZuhEC6KGzSGrqvUDVDNYnRmA== + size: 169903530 + - url: affine-canary-macos-arm64.zip + sha512: IAWbCpVqPPVVzDowGKGnKZzHN2jPgAW40v+bUZR2tdgDrqIAVy4YdamYz8WmEwpg1TXmi0ueSsWgGFPgBIr0iA== + size: 169085665 + - url: affine-canary-macos-x64.dmg + sha512: 4y4/KkmkmFmZ94ntRAN0lSX7aZzgEd4Wg7f85Tff296P3x85sbPF4FFIp++Zx/cgBZBUQwMWe9xeGlefompQ/g== + size: 175920978 + - url: affine-canary-macos-x64.zip + sha512: S1MuMHooMOQ9eJ+coRYmyz6k5lnWIMqHotSrywxGGo7sFXBY+O5F4PeKgNREJtwXjAIxv0GxZVvbe5jc+onw9w== + size: 175315484 +path: affine-canary-windows-x64.exe +sha512: qbK4N6+axVO2dA/iPzfhANWxCZXY1S3ci9qYIT1v/h0oCjc6vqpXU+2KRGL5mplL6wmVgJAOpqrfnq9gHMsfDg== +releaseDate: 2023-12-26T13:24:28.221Z diff --git a/packages/frontend/electron/test/main/fixtures/releases/0.11.1-canary.2.txt b/packages/frontend/electron/test/main/fixtures/releases/0.11.1-canary.2.txt new file mode 100644 index 0000000000..fe8f4de82d --- /dev/null +++ b/packages/frontend/electron/test/main/fixtures/releases/0.11.1-canary.2.txt @@ -0,0 +1,20 @@ +version: 0.11.1-canary.2 +files: + - url: affine-canary-windows-x64.exe + sha512: qbK4N6+axVO2dA/iPzfhANWxCZXY1S3ci9qYIT1v/h0oCjc6vqpXU+2KRGL5mplL6wmVgJAOpqrfnq9gHMsfDg== + size: 179526504 + - url: affine-canary-macos-arm64.dmg + sha512: ++LAGuxTmFAVd65k8UpKKfU19iisvXHKDDfPkGlTVC000QP3foeS21BmTgYnM1ZuhEC6KGzSGrqvUDVDNYnRmA== + size: 169903530 + - url: affine-canary-macos-arm64.zip + sha512: IAWbCpVqPPVVzDowGKGnKZzHN2jPgAW40v+bUZR2tdgDrqIAVy4YdamYz8WmEwpg1TXmi0ueSsWgGFPgBIr0iA== + size: 169085665 + - url: affine-canary-macos-x64.dmg + sha512: 4y4/KkmkmFmZ94ntRAN0lSX7aZzgEd4Wg7f85Tff296P3x85sbPF4FFIp++Zx/cgBZBUQwMWe9xeGlefompQ/g== + size: 175920978 + - url: affine-canary-macos-x64.zip + sha512: S1MuMHooMOQ9eJ+coRYmyz6k5lnWIMqHotSrywxGGo7sFXBY+O5F4PeKgNREJtwXjAIxv0GxZVvbe5jc+onw9w== + size: 175315484 +path: affine-canary-windows-x64.exe +sha512: qbK4N6+axVO2dA/iPzfhANWxCZXY1S3ci9qYIT1v/h0oCjc6vqpXU+2KRGL5mplL6wmVgJAOpqrfnq9gHMsfDg== +releaseDate: 2023-12-26T13:24:28.221Z diff --git a/packages/frontend/electron/test/main/fixtures/releases/0.11.1.txt b/packages/frontend/electron/test/main/fixtures/releases/0.11.1.txt new file mode 100644 index 0000000000..843b632287 --- /dev/null +++ b/packages/frontend/electron/test/main/fixtures/releases/0.11.1.txt @@ -0,0 +1,20 @@ +version: 0.11.1 +files: + - url: affine-stable-windows-x64.exe + sha512: qHRO31Fb8F+Q/hiGiJJ2WH+PpSC5iUIPtWujUoI+XNMz7UfhCGxoVW9U38CTE9LecILS119SZN0rrHkmu+nQiw== + size: 179504488 + - url: affine-stable-macos-arm64.dmg + sha512: uDS7bZusoU5p2t4bi1k/IdvChj3BRIWbOLanbhAfIjwBmf9FM3553wgeUzQLRMRuD5wavsw/aA1BaqFJIbwkyQ== + size: 169793193 + - url: affine-stable-macos-arm64.zip + sha512: n4CrOgNPd70WqPfe0ZEKzmyOdqOlVnFvQqylIlt92eqKrUb8jcxVThQY+GU2Jy3jVjvKqUvubodDbIkQhXQ1xQ== + size: 169014676 + - url: affine-stable-macos-x64.dmg + sha512: xFy1kt1025h1wqBjHt+IoPweC40UqAZvZLI2XD3LIlPG60jZ03+QM75UwaXYuVYEqe3kO9a3WeFcrfGdoj4Hzw== + size: 175720872 + - url: affine-stable-macos-x64.zip + sha512: FfgX22ytleb8fv35odzhDyFsmiUVPdI4XQjTB4uDmkhQ719i+W4yctUnP5TSCpymYC0HgVRFSVg4Jkuuli+8ug== + size: 175244411 +path: affine-stable-windows-x64.exe +sha512: qHRO31Fb8F+Q/hiGiJJ2WH+PpSC5iUIPtWujUoI+XNMz7UfhCGxoVW9U38CTE9LecILS119SZN0rrHkmu+nQiw== +releaseDate: 2023-12-27T11:04:53.014Z diff --git a/packages/frontend/electron/test/main/mocks/app-adapter.ts b/packages/frontend/electron/test/main/mocks/app-adapter.ts new file mode 100644 index 0000000000..cf18d6b314 --- /dev/null +++ b/packages/frontend/electron/test/main/mocks/app-adapter.ts @@ -0,0 +1,34 @@ +import type { AppAdapter } from 'electron-updater/out/AppAdapter'; + +/** + * For testing and same as: + * https://github.com/electron-userland/electron-builder/blob/master/packages/electron-updater/src/ElectronAppAdapter.ts + */ +export class MockedAppAdapter implements AppAdapter { + version: string; + name = 'AFFiNE-testing'; + isPackaged = true; + appUpdateConfigPath = ''; + userDataPath = ''; + baseCachePath = ''; + + constructor(version: string) { + this.version = version; + } + + whenReady() { + return Promise.resolve(); + } + + relaunch() { + return; + } + + quit() { + return; + } + + onQuit(_handler: (exitCode: number) => void) { + return; + } +} diff --git a/packages/frontend/electron/test/main/mocks/http-executor.ts b/packages/frontend/electron/test/main/mocks/http-executor.ts new file mode 100644 index 0000000000..b803eb7c6d --- /dev/null +++ b/packages/frontend/electron/test/main/mocks/http-executor.ts @@ -0,0 +1,26 @@ +import http from 'node:https'; + +import { HttpExecutor } from 'builder-util-runtime'; +import type { ClientRequest } from 'electron'; + +/** + * For testing and same as: + * https://github.com/electron-userland/electron-builder/blob/master/packages/electron-updater/src/electronHttpExecutor.ts + */ +export class MockedHttpExecutor extends HttpExecutor { + createRequest( + options: any, + callback: (response: any) => void + ): ClientRequest { + if (options.headers && options.headers.Host) { + // set host value from headers.Host + options.host = options.headers.Host; + // remove header property 'Host', if not removed causes net::ERR_INVALID_ARGUMENT exception + delete options.headers.Host; + } + + const request = http.request(options); + request.on('response', callback); + return request as unknown as ClientRequest; + } +} diff --git a/packages/frontend/electron/test/main/mocks/index.ts b/packages/frontend/electron/test/main/mocks/index.ts new file mode 100644 index 0000000000..2ba1e3996c --- /dev/null +++ b/packages/frontend/electron/test/main/mocks/index.ts @@ -0,0 +1,3 @@ +export * from './app-adapter'; +export * from './http-executor'; +export * from './updater'; diff --git a/packages/frontend/electron/test/main/mocks/updater.ts b/packages/frontend/electron/test/main/mocks/updater.ts new file mode 100644 index 0000000000..f3fd31bd61 --- /dev/null +++ b/packages/frontend/electron/test/main/mocks/updater.ts @@ -0,0 +1,37 @@ +import 'electron-updater'; // Prevent BaseUpdater is undefined. + +import { type AllPublishOptions, UUID } from 'builder-util-runtime'; +import { randomBytes } from 'crypto'; +import type { AppAdapter } from 'electron-updater/out/AppAdapter'; +import type { DownloadUpdateOptions } from 'electron-updater/out/AppUpdater'; +import type { InstallOptions } from 'electron-updater/out/BaseUpdater'; +import { BaseUpdater } from 'electron-updater/out/BaseUpdater'; + +import { MockedHttpExecutor } from './http-executor'; + +/** + * For testing, like: + * https://github.com/electron-userland/electron-builder/blob/master/packages/electron-updater/src/MacUpdater.ts + */ +export class MockedUpdater extends BaseUpdater { + httpExecutor: MockedHttpExecutor; + + constructor(options?: AllPublishOptions | null, app?: AppAdapter) { + super(options, app); + + this.httpExecutor = new MockedHttpExecutor(); + Object.assign(this, { + getOrCreateStagingUserId: () => { + const id = UUID.v5(randomBytes(4096), UUID.OID); + return id; + }, + }); + } + + doInstall(_options: InstallOptions) { + return true; + } + doDownloadUpdate(_options: DownloadUpdateOptions): Promise { + return Promise.resolve([]); + } +} diff --git a/packages/frontend/electron/test/main/updater.spec.ts b/packages/frontend/electron/test/main/updater.spec.ts new file mode 100644 index 0000000000..bfcd6e4ee9 --- /dev/null +++ b/packages/frontend/electron/test/main/updater.spec.ts @@ -0,0 +1,175 @@ +import nodePath from 'node:path'; + +import type { UpdateCheckResult } from 'electron-updater'; +import fs from 'fs-extra'; +import { flatten } from 'lodash-es'; +import { http, HttpResponse } from 'msw'; +import { setupServer } from 'msw/node'; +import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest'; + +import { CustomGitHubProvider } from '../../src/main/updater/custom-github-provider'; +import { MockedAppAdapter, MockedUpdater } from './mocks'; + +const platformTail = (() => { + // https://github.com/electron-userland/electron-builder/blob/master/packages/electron-updater/src/providers/Provider.ts#L30 + const platform = process.platform; + if (platform === 'linux') { + const arch = process.env['TEST_UPDATER_ARCH'] || process.arch; + const archSuffix = arch === 'x64' ? '' : `-${arch}`; + return '-linux' + archSuffix; + } else { + return platform === 'darwin' ? '-mac' : ''; + } +})(); + +function response404() { + return HttpResponse.text('Not Found', { status: 404 }); +} +function response403() { + return HttpResponse.text('403', { status: 403 }); +} + +describe('testing for client update', () => { + const expectReleaseList = [ + { buildType: 'stable', version: '0.11.1' }, + { buildType: 'beta', version: '0.11.1-beta.1' }, + { buildType: 'canary', version: '0.11.1-canary.1' }, + ]; + + const basicRequestHandlers = [ + http.get( + 'https://github.com/toeverything/AFFiNE/releases.atom', + async () => { + const buffer = await fs.readFile( + nodePath.join(__dirname, 'fixtures', 'feeds.txt') + ); + const content = buffer.toString(); + return HttpResponse.xml(content); + } + ), + ...flatten( + expectReleaseList.map(({ version, buildType }) => { + return [ + http.get( + `https://github.com/toeverything/AFFiNE/releases/download/v${version}/latest${platformTail}.yml`, + async () => { + const buffer = await fs.readFile( + nodePath.join( + __dirname, + 'fixtures', + 'releases', + `${version}.txt` + ) + ); + const content = buffer.toString(); + return HttpResponse.text(content); + } + ), + http.get( + `https://github.com/toeverything/AFFiNE/releases/download/v${version}/${buildType}${platformTail}.yml`, + response404 + ), + ]; + }) + ), + ]; + + describe('release api request successfully', () => { + const server = setupServer( + ...basicRequestHandlers, + http.get( + 'https://api.github.com/repos/toeverything/affine/releases', + async () => { + const buffer = await fs.readFile( + nodePath.join(__dirname, 'fixtures', 'release-list.txt') + ); + const content = buffer.toString(); + return HttpResponse.xml(content); + } + ) + ); + beforeAll(() => server.listen({ onUnhandledRequest: 'error' })); + afterAll(() => server.close()); + afterEach(() => server.resetHandlers()); + + for (const { buildType, version } of expectReleaseList) { + it(`check update for ${buildType} channel successfully`, async () => { + const app = new MockedAppAdapter('0.10.0'); + const updater = new MockedUpdater(null, app); + updater.allowPrerelease = buildType !== 'stable'; + + const feedUrl: Parameters[0] = { + channel: buildType, + // hack for custom provider + provider: 'custom' as 'github', + repo: 'AFFiNE', + owner: 'toeverything', + releaseType: buildType === 'stable' ? 'release' : 'prerelease', + // @ts-expect-error hack for custom provider + updateProvider: CustomGitHubProvider, + }; + + updater.setFeedURL(feedUrl); + + const info = (await updater.checkForUpdates()) as UpdateCheckResult; + expect(info).not.toBe(null); + expect(info.updateInfo.releaseName).toBe(version); + expect(info.updateInfo.version).toBe(version); + expect(info.updateInfo.releaseNotes?.length).toBeGreaterThan(0); + }); + } + }); + + describe('release api request limited', () => { + const server = setupServer( + ...basicRequestHandlers, + http.get( + 'https://api.github.com/repos/toeverything/affine/releases', + response403 + ), + http.get( + `https://github.com/toeverything/AFFiNE/releases/download/v0.11.1-canary.2/latest${platformTail}.yml`, + async () => { + const buffer = await fs.readFile( + nodePath.join( + __dirname, + 'fixtures', + 'releases', + `0.11.1-canary.2.txt` + ) + ); + const content = buffer.toString(); + return HttpResponse.text(content); + } + ) + ); + beforeAll(() => server.listen({ onUnhandledRequest: 'error' })); + afterAll(() => server.close()); + afterEach(() => server.resetHandlers()); + + it('check update for canary channel get v0.11.1-canary.2', async () => { + const app = new MockedAppAdapter('0.10.0'); + const updater = new MockedUpdater(null, app); + updater.allowPrerelease = true; + + const feedUrl: Parameters[0] = { + channel: 'canary', + // hack for custom provider + provider: 'custom' as 'github', + repo: 'AFFiNE', + owner: 'toeverything', + releaseType: 'prerelease', + // @ts-expect-error hack for custom provider + updateProvider: CustomGitHubProvider, + }; + + updater.setFeedURL(feedUrl); + + const info = (await updater.checkForUpdates()) as UpdateCheckResult; + expect(info).not.toBe(null); + expect(info.updateInfo.releaseName).toBe('0.11.1-canary.2'); + expect(info.updateInfo.version).toBe('0.11.1-canary.2'); + expect(info.updateInfo.releaseNotes?.length).toBe(0); + }); + }); +}); diff --git a/packages/frontend/electron/test/workspace/handlers.spec.ts b/packages/frontend/electron/test/workspace/handlers.spec.ts index 68e0194253..0ef62453e6 100644 --- a/packages/frontend/electron/test/workspace/handlers.spec.ts +++ b/packages/frontend/electron/test/workspace/handlers.spec.ts @@ -3,7 +3,7 @@ import path from 'node:path'; import { removeWithRetry } from '@affine-test/kit/utils/utils'; import fs from 'fs-extra'; import { v4 } from 'uuid'; -import { afterEach, describe, expect, test, vi } from 'vitest'; +import { afterAll, afterEach, describe, expect, test, vi } from 'vitest'; const tmpDir = path.join(__dirname, 'tmp'); const appDataPath = path.join(tmpDir, 'app-data'); @@ -24,6 +24,10 @@ afterEach(async () => { await removeWithRetry(tmpDir); }); +afterAll(() => { + vi.doUnmock('@affine/electron/helper/main-rpc'); +}); + describe('list workspaces', () => { test('listWorkspaces (valid)', async () => { const { listWorkspaces } = await import( diff --git a/packages/frontend/electron/tsconfig.json b/packages/frontend/electron/tsconfig.json index 7d62f4f91d..f207aca9fd 100644 --- a/packages/frontend/electron/tsconfig.json +++ b/packages/frontend/electron/tsconfig.json @@ -22,9 +22,6 @@ { "path": "../../common/infra" }, - { - "path": "../../common/sdk" - }, { "path": "../../common/env" }, diff --git a/packages/frontend/electron/tsconfig.node.json b/packages/frontend/electron/tsconfig.node.json index a8afe56bee..34fc8e1641 100644 --- a/packages/frontend/electron/tsconfig.node.json +++ b/packages/frontend/electron/tsconfig.node.json @@ -12,11 +12,7 @@ "types": ["node"], "allowJs": true }, - "include": [ - "./scripts", - "esbuild.main.config.ts", - "esbuild.plugin.config.ts" - ], + "include": ["./scripts"], "ts-node": { "esm": true, "experimentalSpecifierResolution": "node" diff --git a/packages/frontend/graphql/package.json b/packages/frontend/graphql/package.json index e2badd9bde..d1a5651ecf 100644 --- a/packages/frontend/graphql/package.json +++ b/packages/frontend/graphql/package.json @@ -15,10 +15,8 @@ "@graphql-codegen/typescript": "^4.0.1", "@graphql-codegen/typescript-operations": "^4.0.1", "@types/lodash-es": "^4.17.9", - "lodash": "^4.17.21", - "lodash-es": "^4.17.21", "prettier": "^3.0.3", - "vitest": "1.0.4" + "vitest": "1.1.3" }, "scripts": { "postinstall": "gql-gen --errors-only" @@ -26,6 +24,8 @@ "dependencies": { "@affine/env": "workspace:*", "graphql": "^16.8.1", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", "nanoid": "^5.0.1" }, "installConfig": { diff --git a/packages/frontend/graphql/src/graphql/get-user.gql b/packages/frontend/graphql/src/graphql/get-user.gql index 8b9f82e343..82ad9e3667 100644 --- a/packages/frontend/graphql/src/graphql/get-user.gql +++ b/packages/frontend/graphql/src/graphql/get-user.gql @@ -1,9 +1,16 @@ query getUser($email: String!) { user(email: $email) { - id - name - avatarUrl - email - hasPassword + __typename + ... on UserType { + id + name + avatarUrl + email + hasPassword + } + ... on LimitedUserType { + email + hasPassword + } } } diff --git a/packages/frontend/graphql/src/graphql/get-workspace-features.gql b/packages/frontend/graphql/src/graphql/get-workspace-features.gql new file mode 100644 index 0000000000..8644712835 --- /dev/null +++ b/packages/frontend/graphql/src/graphql/get-workspace-features.gql @@ -0,0 +1,5 @@ +query getWorkspaceFeatures($workspaceId: String!) { + workspace(id: $workspaceId) { + features + } +} diff --git a/packages/frontend/graphql/src/graphql/index.ts b/packages/frontend/graphql/src/graphql/index.ts index 9178e0f355..18cc67d522 100644 --- a/packages/frontend/graphql/src/graphql/index.ts +++ b/packages/frontend/graphql/src/graphql/index.ts @@ -345,11 +345,31 @@ export const getUserQuery = { query: ` query getUser($email: String!) { user(email: $email) { - id - name - avatarUrl - email - hasPassword + __typename + ... on UserType { + id + name + avatarUrl + email + hasPassword + } + ... on LimitedUserType { + email + hasPassword + } + } +}`, +}; + +export const getWorkspaceFeaturesQuery = { + id: 'getWorkspaceFeaturesQuery' as const, + operationName: 'getWorkspaceFeatures', + definitionName: 'workspace', + containsFile: false, + query: ` +query getWorkspaceFeatures($workspaceId: String!) { + workspace(id: $workspaceId) { + features } }`, }; @@ -760,6 +780,76 @@ mutation uploadAvatar($avatar: Upload!) { }`, }; +export const availableFeaturesQuery = { + id: 'availableFeaturesQuery' as const, + operationName: 'availableFeatures', + definitionName: 'workspace', + containsFile: false, + query: ` +query availableFeatures($id: String!) { + workspace(id: $id) { + availableFeatures + } +}`, +}; + +export const setWorkspaceExperimentalFeatureMutation = { + id: 'setWorkspaceExperimentalFeatureMutation' as const, + operationName: 'setWorkspaceExperimentalFeature', + definitionName: 'setWorkspaceExperimentalFeature', + containsFile: false, + query: ` +mutation setWorkspaceExperimentalFeature($workspaceId: String!, $feature: FeatureType!, $enable: Boolean!) { + setWorkspaceExperimentalFeature( + workspaceId: $workspaceId + feature: $feature + enable: $enable + ) +}`, +}; + +export const addWorkspaceFeatureMutation = { + id: 'addWorkspaceFeatureMutation' as const, + operationName: 'addWorkspaceFeature', + definitionName: 'addWorkspaceFeature', + containsFile: false, + query: ` +mutation addWorkspaceFeature($workspaceId: String!, $feature: FeatureType!) { + addWorkspaceFeature(workspaceId: $workspaceId, feature: $feature) +}`, +}; + +export const listWorkspaceFeaturesQuery = { + id: 'listWorkspaceFeaturesQuery' as const, + operationName: 'listWorkspaceFeatures', + definitionName: 'listWorkspaceFeatures', + containsFile: false, + query: ` +query listWorkspaceFeatures($feature: FeatureType!) { + listWorkspaceFeatures(feature: $feature) { + id + public + createdAt + memberCount + owner { + id + } + features + } +}`, +}; + +export const removeWorkspaceFeatureMutation = { + id: 'removeWorkspaceFeatureMutation' as const, + operationName: 'removeWorkspaceFeature', + definitionName: 'removeWorkspaceFeature', + containsFile: false, + query: ` +mutation removeWorkspaceFeature($workspaceId: String!, $feature: FeatureType!) { + removeWorkspaceFeature(workspaceId: $workspaceId, feature: $feature) +}`, +}; + export const inviteByEmailMutation = { id: 'inviteByEmailMutation' as const, operationName: 'inviteByEmail', diff --git a/packages/frontend/graphql/src/graphql/workspace-experimental-feature-get.gql b/packages/frontend/graphql/src/graphql/workspace-experimental-feature-get.gql new file mode 100644 index 0000000000..2720fa6fe3 --- /dev/null +++ b/packages/frontend/graphql/src/graphql/workspace-experimental-feature-get.gql @@ -0,0 +1,5 @@ +query availableFeatures($id: String!) { + workspace(id: $id) { + availableFeatures + } +} diff --git a/packages/frontend/graphql/src/graphql/workspace-experimental-feature-set.gql b/packages/frontend/graphql/src/graphql/workspace-experimental-feature-set.gql new file mode 100644 index 0000000000..1b9ce1f683 --- /dev/null +++ b/packages/frontend/graphql/src/graphql/workspace-experimental-feature-set.gql @@ -0,0 +1,11 @@ +mutation setWorkspaceExperimentalFeature( + $workspaceId: String! + $feature: FeatureType! + $enable: Boolean! +) { + setWorkspaceExperimentalFeature( + workspaceId: $workspaceId + feature: $feature + enable: $enable + ) +} diff --git a/packages/frontend/graphql/src/graphql/workspace-feature-add.gql b/packages/frontend/graphql/src/graphql/workspace-feature-add.gql new file mode 100644 index 0000000000..d77eafbd16 --- /dev/null +++ b/packages/frontend/graphql/src/graphql/workspace-feature-add.gql @@ -0,0 +1,3 @@ +mutation addWorkspaceFeature($workspaceId: String!, $feature: FeatureType!) { + addWorkspaceFeature(workspaceId: $workspaceId, feature: $feature) +} diff --git a/packages/frontend/graphql/src/graphql/workspace-feature-list.gql b/packages/frontend/graphql/src/graphql/workspace-feature-list.gql new file mode 100644 index 0000000000..4bd21fbe45 --- /dev/null +++ b/packages/frontend/graphql/src/graphql/workspace-feature-list.gql @@ -0,0 +1,12 @@ +query listWorkspaceFeatures($feature: FeatureType!) { + listWorkspaceFeatures(feature: $feature) { + id + public + createdAt + memberCount + owner { + id + } + features + } +} diff --git a/packages/frontend/graphql/src/graphql/workspace-feature-remove.gql b/packages/frontend/graphql/src/graphql/workspace-feature-remove.gql new file mode 100644 index 0000000000..e856885b56 --- /dev/null +++ b/packages/frontend/graphql/src/graphql/workspace-feature-remove.gql @@ -0,0 +1,3 @@ +mutation removeWorkspaceFeature($workspaceId: String!, $feature: FeatureType!) { + removeWorkspaceFeature(workspaceId: $workspaceId, feature: $feature) +} diff --git a/packages/frontend/graphql/src/index.ts b/packages/frontend/graphql/src/index.ts index cd1cdef895..7a4a44387c 100644 --- a/packages/frontend/graphql/src/index.ts +++ b/packages/frontend/graphql/src/index.ts @@ -5,6 +5,8 @@ export * from './utils'; import { setupGlobal } from '@affine/env/global'; +import { gqlFetcherFactory } from './fetcher'; + setupGlobal(); export function getBaseUrl(): string { @@ -14,3 +16,5 @@ export function getBaseUrl(): string { const { protocol, hostname, port } = window.location; return `${protocol}//${hostname}${port ? `:${port}` : ''}`; } + +export const fetcher = gqlFetcherFactory(getBaseUrl() + '/graphql'); diff --git a/packages/frontend/graphql/src/schema.ts b/packages/frontend/graphql/src/schema.ts index 7f55ef17ff..5a279e5d43 100644 --- a/packages/frontend/graphql/src/schema.ts +++ b/packages/frontend/graphql/src/schema.ts @@ -32,6 +32,12 @@ export interface Scalars { Upload: { input: File; output: File }; } +/** The type of workspace feature */ +export enum FeatureType { + Copilot = 'Copilot', + EarlyAccess = 'EarlyAccess', +} + export enum InvoiceStatus { Draft = 'Draft', Open = 'Open', @@ -357,14 +363,30 @@ export type GetUserQueryVariables = Exact<{ export type GetUserQuery = { __typename?: 'Query'; - user: { - __typename?: 'UserType'; - id: string; - name: string; - avatarUrl: string | null; - email: string; - hasPassword: boolean | null; - } | null; + user: + | { + __typename: 'LimitedUserType'; + email: string; + hasPassword: boolean | null; + } + | { + __typename: 'UserType'; + id: string; + name: string; + avatarUrl: string | null; + email: string; + hasPassword: boolean | null; + } + | null; +}; + +export type GetWorkspaceFeaturesQueryVariables = Exact<{ + workspaceId: Scalars['String']['input']; +}>; + +export type GetWorkspaceFeaturesQuery = { + __typename?: 'Query'; + workspace: { __typename?: 'WorkspaceType'; features: Array }; }; export type GetWorkspacePublicByIdQueryVariables = Exact<{ @@ -724,6 +746,66 @@ export type UploadAvatarMutation = { }; }; +export type AvailableFeaturesQueryVariables = Exact<{ + id: Scalars['String']['input']; +}>; + +export type AvailableFeaturesQuery = { + __typename?: 'Query'; + workspace: { + __typename?: 'WorkspaceType'; + availableFeatures: Array; + }; +}; + +export type SetWorkspaceExperimentalFeatureMutationVariables = Exact<{ + workspaceId: Scalars['String']['input']; + feature: FeatureType; + enable: Scalars['Boolean']['input']; +}>; + +export type SetWorkspaceExperimentalFeatureMutation = { + __typename?: 'Mutation'; + setWorkspaceExperimentalFeature: boolean; +}; + +export type AddWorkspaceFeatureMutationVariables = Exact<{ + workspaceId: Scalars['String']['input']; + feature: FeatureType; +}>; + +export type AddWorkspaceFeatureMutation = { + __typename?: 'Mutation'; + addWorkspaceFeature: number; +}; + +export type ListWorkspaceFeaturesQueryVariables = Exact<{ + feature: FeatureType; +}>; + +export type ListWorkspaceFeaturesQuery = { + __typename?: 'Query'; + listWorkspaceFeatures: Array<{ + __typename?: 'WorkspaceType'; + id: string; + public: boolean; + createdAt: string; + memberCount: number; + features: Array; + owner: { __typename?: 'UserType'; id: string }; + }>; +}; + +export type RemoveWorkspaceFeatureMutationVariables = Exact<{ + workspaceId: Scalars['String']['input']; + feature: FeatureType; +}>; + +export type RemoveWorkspaceFeatureMutation = { + __typename?: 'Mutation'; + removeWorkspaceFeature: number; +}; + export type InviteByEmailMutationVariables = Exact<{ workspaceId: Scalars['String']['input']; email: Scalars['String']['input']; @@ -805,6 +887,11 @@ export type Queries = variables: GetUserQueryVariables; response: GetUserQuery; } + | { + name: 'getWorkspaceFeaturesQuery'; + variables: GetWorkspaceFeaturesQueryVariables; + response: GetWorkspaceFeaturesQuery; + } | { name: 'getWorkspacePublicByIdQuery'; variables: GetWorkspacePublicByIdQueryVariables; @@ -859,6 +946,16 @@ export type Queries = name: 'subscriptionQuery'; variables: SubscriptionQueryVariables; response: SubscriptionQuery; + } + | { + name: 'availableFeaturesQuery'; + variables: AvailableFeaturesQueryVariables; + response: AvailableFeaturesQuery; + } + | { + name: 'listWorkspaceFeaturesQuery'; + variables: ListWorkspaceFeaturesQueryVariables; + response: ListWorkspaceFeaturesQuery; }; export type Mutations = @@ -1002,6 +1099,21 @@ export type Mutations = variables: UploadAvatarMutationVariables; response: UploadAvatarMutation; } + | { + name: 'setWorkspaceExperimentalFeatureMutation'; + variables: SetWorkspaceExperimentalFeatureMutationVariables; + response: SetWorkspaceExperimentalFeatureMutation; + } + | { + name: 'addWorkspaceFeatureMutation'; + variables: AddWorkspaceFeatureMutationVariables; + response: AddWorkspaceFeatureMutation; + } + | { + name: 'removeWorkspaceFeatureMutation'; + variables: RemoveWorkspaceFeatureMutationVariables; + response: RemoveWorkspaceFeatureMutation; + } | { name: 'inviteByEmailMutation'; variables: InviteByEmailMutationVariables; diff --git a/packages/frontend/hooks/package.json b/packages/frontend/hooks/package.json deleted file mode 100644 index d83466ab05..0000000000 --- a/packages/frontend/hooks/package.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "name": "@toeverything/hooks", - "type": "module", - "exports": { - "./*": "./src/*" - }, - "private": true, - "dependencies": { - "foxact": "^0.2.20", - "jotai": "^2.5.1", - "jotai-effect": "^0.2.3", - "lodash.debounce": "^4.0.8", - "p-queue": "^8.0.0", - "react": "18.2.0", - "rxjs": "^7.8.1", - "swr": "2.2.4", - "uuid": "^9.0.1" - }, - "devDependencies": { - "@affine/debug": "workspace:*", - "@affine/env": "workspace:*", - "@affine/workspace": "workspace:*", - "@blocksuite/block-std": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/blocks": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/global": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/lit": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/presets": "0.11.0-nightly-202312220916-e3abcbb", - "@blocksuite/store": "0.11.0-nightly-202312220916-e3abcbb", - "@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", - "vitest": "1.0.4", - "y-provider": "workspace:*" - }, - "peerDependencies": { - "@blocksuite/block-std": "*", - "@blocksuite/blocks": "*", - "@blocksuite/global": "*", - "@blocksuite/presets": "*", - "@blocksuite/store": "*", - "y-provider": "workspace:*" - }, - "peerDependenciesMeta": { - "@affine/env": { - "optional": true - }, - "@blocksuite/block-std": { - "optional": true - }, - "@blocksuite/blocks": { - "optional": true - }, - "@blocksuite/global": { - "optional": true - }, - "@blocksuite/presets": { - "optional": true - }, - "@blocksuite/store": { - "optional": true - }, - "y-provider": { - "optional": true - } - }, - "version": "0.11.0" -} diff --git a/packages/frontend/hooks/project.json b/packages/frontend/hooks/project.json deleted file mode 100644 index a4b0460d78..0000000000 --- a/packages/frontend/hooks/project.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "hooks", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "library", - "sourceRoot": "packages/frontend/hooks/src" -} diff --git a/packages/frontend/hooks/src/use-affine-ipc-renderer.ts b/packages/frontend/hooks/src/use-affine-ipc-renderer.ts deleted file mode 100644 index 0cf2176b3e..0000000000 --- a/packages/frontend/hooks/src/use-affine-ipc-renderer.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { useCallback, useEffect, useRef } from 'react'; - -declare global { - interface IPCRenderer { - send(channel: string, ...args: any[]): void; - invoke(channel: string, ...args: any[]): Promise; - on( - channel: string, - listener: (event: unknown, ...args: any[]) => void - ): this; - once( - channel: string, - listener: (event: unknown, ...args: any[]) => void - ): this; - removeListener(channel: string, listener: (...args: any[]) => void): this; - } - - interface Window { - affine: { - ipcRenderer: IPCRenderer; - }; - } -} - -/** - * Unsafe - */ -export function useAffineAsyncCallback(channel: string) { - return useCallback( - (...args: any[]): Promise => { - return window.affine.ipcRenderer.invoke(channel, ...args); - }, - [channel] - ); -} - -/** - * Unsafe - */ -export function useAffineListener( - channel: string, - listener: (event: unknown, ...args: any[]) => void, - once?: boolean -): void { - const fnRef = useRef<((event: unknown, ...args: any[]) => void) | null>(null); - if (!fnRef.current) { - fnRef.current = listener; - } - const ipcListener = fnRef.current ?? (fnRef.current = listener); - useEffect(() => { - if (once) { - window.affine.ipcRenderer.once(channel, ipcListener); - } else { - window.affine.ipcRenderer.on(channel, ipcListener); - } - return () => { - window.affine.ipcRenderer.removeListener(channel, ipcListener); - }; - }, [channel, once, ipcListener]); -} diff --git a/packages/frontend/hooks/src/use-block-suite-workspace-page.ts b/packages/frontend/hooks/src/use-block-suite-workspace-page.ts deleted file mode 100644 index 4df7360604..0000000000 --- a/packages/frontend/hooks/src/use-block-suite-workspace-page.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { DebugLogger } from '@affine/debug'; -import { assertExists, DisposableGroup } from '@blocksuite/global/utils'; -import type { Page, Workspace } from '@blocksuite/store'; -import type { Atom } from 'jotai'; -import { atom, useAtomValue } from 'jotai'; -import PQueue from 'p-queue'; -import { useEffect } from 'react'; - -const logger = new DebugLogger('use-block-suite-workspace-page'); - -const weakMap = new WeakMap>>(); - -const emptyAtom = atom(null); - -function getAtom(w: Workspace, pageId: string | null): Atom { - if (!pageId) { - return emptyAtom; - } - if (!weakMap.has(w)) { - weakMap.set(w, new Map()); - } - const map = weakMap.get(w); - assertExists(map); - if (!map.has(pageId)) { - const baseAtom = atom(w.getPage(pageId)); - baseAtom.onMount = set => { - const group = new DisposableGroup(); - group.add( - w.slots.pageAdded.on(id => { - if (pageId === id) { - set(w.getPage(id)); - } - }) - ); - group.add( - w.slots.pageRemoved.on(id => { - if (pageId === id) { - set(null); - } - }) - ); - return () => { - group.dispose(); - }; - }; - map.set(pageId, baseAtom); - return baseAtom; - } else { - return map.get(pageId) as Atom; - } -} -// concurrently load 3 pages at most -const CONCURRENT_JOBS = 3; - -const loadPageQueue = new PQueue({ - concurrency: CONCURRENT_JOBS, -}); - -const loadedPages = new WeakSet(); - -const awaitForIdle = () => - new Promise(resolve => - requestIdleCallback(resolve, { - timeout: 1000, // do not wait for too long - }) - ); - -const awaitForTimeout = (timeout: number) => - new Promise(resolve => setTimeout(resolve, timeout)); - -/** - * Load a page and wait for it to be loaded - * This page will be loaded in a queue so that it will not jam the network and browser CPU - */ -export function loadPage(page: Page, priority = 0) { - if (loadedPages.has(page)) { - return Promise.resolve(); - } - loadedPages.add(page); - return loadPageQueue.add( - async () => { - if (!page.loaded) { - await awaitForIdle(); - await page.waitForLoaded(); - logger.debug('page loaded', page.id); - // we do not know how long it takes to load a page here - // so that we just use 300ms timeout as the default page processing time - await awaitForTimeout(300); - } else { - // do nothing if it is already loaded - } - }, - { - priority, - } - ); -} - -export function useBlockSuiteWorkspacePage( - blockSuiteWorkspace: Workspace, - pageId: string | null -): Page | null { - const pageAtom = getAtom(blockSuiteWorkspace, pageId); - assertExists(pageAtom); - const page = useAtomValue(pageAtom); - - useEffect(() => { - if (page && !page.loaded) { - loadPage(page).catch(err => { - logger.error('Failed to load page', err); - }); - } - }, [page]); - - return page; -} diff --git a/packages/frontend/hooks/src/use-workspace-info.ts b/packages/frontend/hooks/src/use-workspace-info.ts deleted file mode 100644 index 73a5eba04d..0000000000 --- a/packages/frontend/hooks/src/use-workspace-info.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { Workspace, WorkspaceMetadata } from '@affine/workspace'; -import { workspaceManagerAtom } from '@affine/workspace/atom'; -import { useAtomValue } from 'jotai'; -import { useEffect, useState } from 'react'; - -export function useWorkspaceInfo( - meta: WorkspaceMetadata, - workspace?: Workspace -) { - const workspaceManager = useAtomValue(workspaceManagerAtom); - - const [information, setInformation] = useState( - () => workspaceManager.list.getInformation(meta).info - ); - - useEffect(() => { - const information = workspaceManager.list.getInformation(meta); - - setInformation(information.info); - return information.onUpdated.on(info => { - setInformation(info); - }).dispose; - }, [meta, workspace, workspaceManager]); - - return information; -} diff --git a/packages/frontend/i18n/package.json b/packages/frontend/i18n/package.json index 4b4e70cba7..b451233687 100644 --- a/packages/frontend/i18n/package.json +++ b/packages/frontend/i18n/package.json @@ -28,7 +28,7 @@ }, "dependencies": { "i18next": "^23.5.1", - "react-i18next": "^13.3.0" + "react-i18next": "^14.0.0" }, "devDependencies": { "@types/prettier": "^3.0.0", diff --git a/packages/frontend/i18n/src/index.ts b/packages/frontend/i18n/src/index.ts index 8987941ef5..d7966aa781 100644 --- a/packages/frontend/i18n/src/index.ts +++ b/packages/frontend/i18n/src/index.ts @@ -45,10 +45,9 @@ export function useI18N() { return i18n; } -const resources = LOCALES.reduce( - (acc, { tag, res }) => ({ ...acc, [tag]: { translation: res } }), - {} -); +const resources = LOCALES.reduce((acc, { tag, res }) => { + return Object.assign(acc, { [tag]: { translation: res } }); +}, {}); const fallbackLng = 'en'; const standardizeLocale = (language: string) => { @@ -68,7 +67,6 @@ const standardizeLocale = (language: string) => { }; export const createI18n = (): I18nextProviderProps['i18n'] => { - // @ts-expect-error ts bug const i18n: I18nextProviderProps['i18n'] = i18next.createInstance(); i18n .use(initReactI18next) diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index ee2b37977c..38da25994c 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -799,6 +799,11 @@ "com.affine.payment.upgrade-success-page.support": "If you have any questions, please contact our <1> customer support.", "com.affine.payment.upgrade-success-page.text": "Congratulations! Your AFFiNE account has been successfully upgraded to a Pro account.", "com.affine.payment.upgrade-success-page.title": "Upgrade Successful!", + "com.affine.payment.member-limit.title": "You have reached the limit", + "com.affine.payment.member-limit.free.description": "Each {{planName}} user can invite up to {{quota}} members to join their workspace. You can upgrade your account to unlock more members.", + "com.affine.payment.member-limit.pro.description": "Each {{planName}} user can invite up to {{quota}} members to join their workspace. If you want to continue adding collaboration members, you can create a new workspace.", + "com.affine.payment.member-limit.free.confirm": "Upgrade", + "com.affine.payment.member-limit.pro.confirm": "Got it", "com.affine.publicLinkDisableModal.button.cancel": "Cancel", "com.affine.publicLinkDisableModal.button.disable": "Disable", "com.affine.publicLinkDisableModal.description": "Disabling this public link will prevent anyone with the link from accessing this page.", @@ -1011,6 +1016,12 @@ "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.load-more": "Load More", + "com.affine.history.confirm-restore-modal.plan-prompt.title": "HELP INFO", + "com.affine.history.confirm-restore-modal.plan-prompt.limited-title": "LIMITED PAGE HISTORY", + "com.affine.history.confirm-restore-modal.free-plan-prompt.description": "Free users can view up to the <1>last 7 days<1> of page history.", + "com.affine.history.confirm-restore-modal.pro-plan-prompt.description": "Pro users can view up to the <1>last 30 days<1> of page history.", + "com.affine.history.confirm-restore-modal.pro-plan-prompt.upgrade": "Upgrade", "com.affine.share-page.header.present": "Present", "com.affine.page-operation.add-linked-page": "Add linked page", "com.affine.onboarding.workspace-guide.title": "Start AFFiNE by creating your own Workspace here!", diff --git a/packages/frontend/i18n/src/resources/ko.json b/packages/frontend/i18n/src/resources/ko.json index 6adc19ebc9..623c61a6cb 100644 --- a/packages/frontend/i18n/src/resources/ko.json +++ b/packages/frontend/i18n/src/resources/ko.json @@ -1,255 +1,1030 @@ { - "404 - Page Not Found": "404 - 발견되지 않음", + "404 - Page Not Found": "404 - 페이지를 찾을 수 없음", + "404.back": "내 콘텐츠로 돌아가기", + "404.hint": "죄송합니다, 액세스 권한이 없거나 해당 콘텐츠가 없습니다....", + "404.signOut": "다른 계정으로 로그인", "AFFiNE Cloud": "AFFiNE 클라우드", "AFFiNE Community": "AFFiNE 커뮤니티", + "About AFFiNE": "AFFiNE 소개", "Access level": "접근 권한", + "Actions": "Actions", + "Add Filter": "필터 추가", "Add Workspace": "워크스페이스 추가", - "Add a subpage inside": "내부에서 하부 페이지 생성", - "Add to Favorites": "즐겨찾기에 추가", + "Add Workspace Hint": "기존 데이터베이스 파일 선택", + "Add a subpage inside": "내부에 하위 페이지 추가", + "Add to Favorites": "즐겨찾기 추가", "Add to favorites": "즐겨찾기에 추가", - "Added Successfully": "성공적으로 추가됨", - "Added to Favorites": "즐겨찾기에 추가됨", - "All changes are saved locally": "모든 변경사항이 로컬에 저장 완료", - "All data has been stored in the cloud": "모든 데이터가 클라우드에 저장됨", + "Added Successfully": "성공적으로 추가함", + "Added to Favorites": "즐겨찾기에 추가함", + "All changes are saved locally": "모든 변경 사항을 로컬에 저장함", + "All data has been stored in the cloud": "모든 데이터를 클라우드에 저장했습니다.", "All pages": "모든 페이지", - "Available Offline": "오프라인 작업 가능", + "App Version": "앱 버전", + "Appearance Settings": "외형 설정", + "Append to Daily Note": "데일리 노트에 연결", + "Available Offline": "오프라인 사용가능", "Back Home": "홈으로 돌아가기", "Back to Quick Search": "빠른 검색으로 돌아가기", - "Body text": "본문", - "Bold": "굵은 글꼴", + "Back to all": "모두로 돌아가기", + "Body text": "본문 내용", + "Bold": "굵게", "Cancel": "취소", - "Change avatar hint": "새로운 아바타는 모든 사람에게 공개됩니다.", - "Change workspace name hint": "새로운 이름은 모든 사람에게 공개됩니다.", - "Check Our Docs": "문서를 확인하세요", + "Change avatar hint": "모든 사람에게 새 아바타가 표시됩니다.", + "Change workspace name hint": "모든 사람에게 새 이름이 표시됩니다.", + "Changelog description": "AFFiNE 변경 로그를 확인합니다.", + "Check Keyboard Shortcuts quickly": "키보드 단축키 빠른 확인", + "Check Our Docs": "우리의 공식 문서 확인", + "Check for updates": "업데이트 확인", + "Check for updates automatically": "자동으로 업데이트 확인", + "Choose your font style": "나의 폰트 스타일 선택", + "Click to replace photo": "클릭하여 사진 바꾸기", + "Client Border Style": "클라이언트 테두리 스타일", "Cloud Workspace": "클라우드 워크스페이스", - "Cloud Workspace Description": "모든 데이터가 AFFiNE 계정 <1>{{email}}으로 동기화 및 저장됨.", - "Code block": "코드 블록", + "Cloud Workspace Description": "모든 데이터는 동기화되어 AFFiNE 계정 <1>{{email}}에 저장됩니다.", + "Code block": "코드 블럭", "Collaboration": "협업", - "Collaboration Description": "다른 사람과 협업하기 위해서 AFFiNE 클라우드 서비스가 필요합니다.", - "Collapse sidebar": "사이드바 닫기", - "Confirm": "동의", - "Contact Us": "우리에게 연락하세요", + "Collaboration Description": "다른 회원과 협업하려면 AFFiNE 클라우드 서비스가 필요합니다.", + "Collapse sidebar": "사이드바 축소", + "Collections": "컬렉션", + "Communities": "커뮤니티", + "Confirm": "확인", + "Connector": "커넥터", + "Contact Us": "문의하기", + "Contact with us": "문의하기", "Continue": "계속", - "Continue with Google": "구글로 계속하기", - "Convert to ": "전환", - "Copied link to clipboard": "링크가 클립보드로 복사됨", + "Continue with Google": "Google로 계속하기", + "Convert to ": "다음으로 변환 ", + "Copied link to clipboard": "클립보드에 링크 복사함", + "Copy": "복사", "Copy Link": "링크 복사", "Create": "생성", - "Create Or Import": "생성하거나 불러오기", - "Create Shared Link Description": "다른 사람과 공유할 수 있는 링크 생성하기", - "Create your own workspace": "새로운 워크스페이스 생성", - "Created": "생성됨", - "Created Successfully": "성공적으로 생성됨", - "Created with": "와 함께 생성됨", - "Customize": "커스터마이즈", - "DB_FILE_INVALID": "유요하지 않은 데이터베이스 파일", - "DB_FILE_PATH_INVALID": "데이터베이스 파일 경로가 유효하지 않음", + "Create Or Import": "생성 또는 가져오기", + "Create Shared Link Description": "누구와도 쉽게 공유할 수 있는 링크를 만들어 보세요.", + "Create a collection": "컬렉션 생성", + "Create your own workspace": "나만의 워크스페이스 만들기", + "Created": "생성함", + "Created Successfully": "성공적으로 생성함", + "Created with": "다음과 같이 생성함", + "Curve Connector": "곡선 커넥터", + "Customize": "사용자 정의", + "Customize your AFFiNE Appearance": "AFFiNE 외형 사용자 정의", + "DB_FILE_ALREADY_LOADED": "데이터베이스 파일을 이미 로드함", + "DB_FILE_INVALID": "유효하지 않은 데이터베이스 파일", + "DB_FILE_MIGRATION_FAILED": "데이터베이스 파일 마이그레이션 실패", + "DB_FILE_PATH_INVALID": "데이터베이스 파일 경로가 잘못됨", + "Data sync mode": "데이터 동기화 모드", + "Date": "날짜", + "Date Format": "날짜 형식", "Default Location": "기본 위치", - "Default db location hint": "기본적으로 {{location}}에 저장됨", + "Default db location hint": "기본적으로 {{location}}에 저장", "Delete": "삭제", - "Delete Member?": "멤버를 삭제할까요?", - "Delete Workspace": "워크스페이스 삭제", - "Delete Workspace Description": "<1>{{workspace}} 를 삭제하면 되돌릴 수 없으니 주의해주세요. 모든 항목을 잃게 됩니다.", - "Delete Workspace Description2": "<1>{{workspace}}를 삭제하면 로컬과 클라우드 데이터 모두 삭제되며, 되돌릴 수 없으므로, 주의해주세요 ", - "Delete Workspace Label Hint": "워크스페이스를 삭제하게 되면, 모든 사용자의 데이터가 영구적으로 삭제됩니다. 워크스페이스의 데이터는 아무도 복구할 수 없습니다.", - "Delete Workspace placeholder": "진행하기 위해서 \"Delete\"를 입력", - "Delete page?": "페이지 삭제", - "Delete permanently": "영원히 삭제", + "Delete Member?": "멤버를 삭제하시겠습니까?", + "Delete Workspace": "워크스페이스 제거", + "Delete Workspace Description": "<1>{{workspace}} 삭제는 되돌릴 수 없으니 주의해서 진행해 주세요. 모든 내용이 손실됩니다.", + "Delete Workspace Description2": "<1>{{workspace}}을 삭제하면 로컬 데이터와 클라우드 데이터가 모두 삭제되며, 이 작업은 되돌릴 수 없으므로 주의해서 진행해 주세요.", + "Delete Workspace Label Hint": "이 워크스페이스를 삭제하면 모든 사용자의 모든 콘텐츠가 영구적으로 삭제됩니다. 이 워크스페이스의 콘텐츠를 복원할 수 있는 방법은 없습니다.", + "Delete Workspace placeholder": "\"Delete\"를 입력하여 확인", + "Delete page?": "페이지를 삭제하시겠습니까?", + "Delete permanently": "영구적으로 삭제", "Disable": "비활성화", "Disable Public Link": "공개 링크 비활성화", - "Disable Public Link ?": "공개 링크를 비활성화 할까요?", - "Disable Public Sharing": "공유 비활성화", - "Discover what's new!": "새로운 기능 탐색", + "Disable Public Link ?": "공개 링크를 비활성화 하시겠습니까?", + "Disable Public Link Description": "이 공개 링크를 비활성화하면 해당하는 링크를 가지고 있더라도 이 페이지에 액세스할 수 없습니다.", + "Disable Public Sharing": "공개 공유 비활성화", + "Discover what's new": "새로운 소식 알아보기", + "Discover what's new!": "새로운 소식을 알아봅니다!", + "Display Language": "표시 언어", "Divider": "구분자", "Download all data": "모든 데이터 다운로드", - "Download core data": "코어 데이터 다운로드", + "Download core data": "주요 데이터 다운로드", "Download data": "{{CoreOrAll}} 데이터 다운로드", - "Edgeless": "엣지리스", + "Download data Description1": "나의 디바이스 공간을 더 많이 차지합니다.", + "Download data Description2": "나의 디바이스 공간을 적게 차지합니다.", + "Download updates automatically": "업데이트 자동 다운로드", + "Early Access Stage": "얼리 액세스 스테이지", + "Edgeless": "Edgeless", "Edit": "수정", + "Edit Filter": "필터 수정", + "Editor Version": "에디터 버전", + "Elbowed Connector": "직각 커넥터", "Enable": "활성화", "Enable AFFiNE Cloud": "AFFiNE 클라우드 활성화", - "Enable AFFiNE Cloud Description": "만약 활성화 한다면, 워크스페이스 데이터가 AFFiNE 클라우드에 백업 및 싱크 됩니다.", - "Enabled success": "활성화 성공", - "Expand sidebar": "사이드바 열기", + "Enable AFFiNE Cloud Description": "이 기능을 활성화하면, 이 워크스페이스의 데이터가 AFFiNE 클라우드를 통해 백업 및 동기화됩니다.", + "Enable cloud hint": "다음 기능은 AFFiNE 클라우드에 의존하며, 현재 모든 데이터는 현재 디바이스에 저장되어 있습니다. 이 워크스페이스에서 AFFiNE 클라우드를 활성화하여 데이터를 클라우드와 동기화할 수 있습니다.", + "Enabled success": "성공적으로 활성화함", + "Exclude from filter": "필터에서 제외", + "Expand sidebar": "사이드바 확장", + "Expand/Collapse Sidebar": "사이드바 확장/축소", "Export": "내보내기", "Export AFFiNE backup file": "AFFiNE 백업 파일 내보내기", - "Export Description": "워크스페이스의 모든 데이터 백업을 내보내기 할 수 있으며, 내보내기 한 데이터는 다시 불러올 수 있습니다.", - "Export Shared Pages Description": "다른 사람과 공유하기 위한 페이지의 정적 복사본 다운로드", - "Export Workspace": "워크스페이스 <1>{{workspace}} 내보내기는 추가될 예정입니다.", - "Export success": "내보내기 ", + "Export Description": "백업을 위해 전체 워크스페이스 데이터를 내보낼 수 있으며, 내보낸 데이터를 다시 가져올 수 있습니다.", + "Export Shared Pages Description": "다른 사람들과 공유할 수 있도록 페이지의 정적 사본을 다운로드하세요.", + "Export Workspace": "워크스페이스 내보내기 <1>{{workspace}}가 곧 제공됩니다", + "Export failed": "내보내기 실패", + "Export success": "내보내기 성공", "Export to HTML": "HTML로 내보내기", - "Export to Markdown": "Markdown으로 내보내기", + "Export to Markdown": "마크다운으로 내보내기", "Export to PDF": "PDF로 내보내기", "Export to PNG": "PNG로 내보내기", - "FILE_ALREADY_EXISTS": "파일이 이미 존재함", - "Failed to publish workspace": "워크스페이스 공개 실패", + "FILE_ALREADY_EXISTS": "파일이 이미 있음", + "Failed to publish workspace": "워크스페이스 발행 실패", "Favorite": "즐겨찾기", - "Favorite pages for easy access": "쉬운 접근을 위해 즐겨찾기에 추가", - "Favorited": "즐겨찾기 됨", + "Favorite pages for easy access": "쉽게 액세스할 수 있는 즐겨찾기 페이지", + "Favorited": "즐겨찾기", "Favorites": "즐겨찾기", - "Find 0 result": "0개 결과 발견", - "Find results": "{{number}}개 결과 발견", + "Filters": "필터", + "Find 0 result": "결과 0건 발견", + "Find results": "결과 {{number}}건을 발견", + "Font Style": "폰트 스타일", "Force Sign Out": "강제 로그아웃", + "Full width Layout": "전체 너비 레이아웃", "General": "일반", - "Get in touch!": "연락하세요!", - "Get in touch! Join our communities": "연락하세요! 우리의 커뮤니티에 가입하세요.", - "Get in touch! Join our communities.": "연락하세요! 우리의 커뮤니티에 가입하세요.", - "Got it": "확인", + "Get in touch!": "연락해 주세요!", + "Get in touch! Join our communities": "연락해 주세요! 우리 커뮤니티에 참여해보세요.", + "Get in touch! Join our communities.": "연락해 주세요! 우리 커뮤니티에 참여해보세요.", + "Go Back": "이전으로", + "Go Forward": "다음으로", + "Got it": "알겠습니다", + "Group": "그룹", + "Group as Database": "데이터베이스로 그룹화", + "Hand": "손", "Heading": "헤딩 {{number}}", - "Help and Feedback": "도움과 피드백", + "Help and Feedback": "도움말과 피드백", + "How is AFFiNE Alpha different?": "AFFiNE 알파는 어떤 점이 다른가요?", + "Image": "이미지", "Import": "불러오기", - "Increase indent": "들여쓰기", + "Increase indent": "들여쓰기 증가", + "Info": "정보", + "Info of legal": "법적 정보", "Inline code": "인라인 코드", + "Invitation sent": "초대 전송", + "Invitation sent hint": "초대된 구성원에게 이 워크스페이스 참여를 위한 이메일이 발송되었습니다.", "Invite": "초대", "Invite Members": "멤버 초대", + "Invite Members Message": "초대된 구성원은 현재 워크스페이스에서 사용자와 공동 작업하게 됩니다.", "Invite placeholder": "메일 검색 (Gmail만 지원)", - "It takes up little space on your device": "당신의 장치의 작은 저장공간을 사용합니다.", - "It takes up little space on your device.": "당신의 장치의 작은 저장공간을 사용합니다.", - "It takes up more space on your device": "당신의 장치의 많은 저장공간을 사용합니다.", - "It takes up more space on your device.": "당신의 장치의 많은 저장공간을 사용합니다.", - "Italic": "이탤릭체", + "It takes up little space on your device": "디바이스 공간을 거의 차지하지 않습니다.", + "It takes up little space on your device.": "디바이스 공간을 거의 차지하지 않습니다.", + "It takes up more space on your device": "디바이스 공간을 더 많이 차지합니다.", + "It takes up more space on your device.": "디바이스 공간을 더 많이 차지합니다.", + "Italic": "기울임체", "Joined Workspace": "참가한 워크스페이스", - "Jump to": "이동", + "Jump to": "다음으로 이동", "Keyboard Shortcuts": "키보드 단축키", "Leave": "떠나기", "Leave Workspace": "워크스페이스 떠나기", - "Leave Workspace Description": "워스크페이스를 떠나게 되면 데이터에 더이상 접근할 수 없습니다.", - "Link": "하이퍼링크 (선택된 텍스트)", - "Loading": "불러오는 중...", + "Leave Workspace Description": "워크스페이스를 떠나고 나면, 더 이상 이 워크스페이스의 콘텐츠에 액세스할 수 없습니다.", + "Leave Workspace hint": "워크스페이스를 떠나고 나면, 더 이상 이 워크스페이스 내의 콘텐츠에 액세스할 수 없습니다.", + "Link": "하이퍼링크 ( 선택한 텍스트 포함 )", + "Loading": "로딩...", + "Loading All Workspaces": "모든 워크스페이스 불러오기", + "Local": "로컬", "Local Workspace": "로컬 워크스페이스", + "Local Workspace Description": "모든 데이터는 현재 디바이스에 저장됩니다. 이 워크스페이스에서 AFFiNE 클라우드를 활성화하여 데이터를 클라우드와 동기화할 수 있습니다.", "Markdown Syntax": "마크다운 문법", "Member": "멤버", - "Member has been removed": "{{name}}이 삭제됨", - "Members": "멤버들", + "Member has been removed": "{{name}} 삭제함", + "Members": "멤버", + "Members hint": "여기에서 멤버를 관리하고 이메일로 새 멤버를 초대하세요.", + "Move Down": "아래로 이동", + "Move Up": "위로 이동", "Move folder": "폴더 이동", - "Move folder hint": "새로운 저장소 위치 선택", - "Move folder success": "폴더 이동 성공", - "Move page to": "페이지를 이동...", - "Move page to...": "페이지를 이동...", - "Move to": "이동", - "Move to Trash": "휴지통으로 이동", - "Moved to Trash": "휴지통으로 이동됨", + "Move folder hint": "새 저장 위치를 선택해 주세요.", + "Move folder success": "성공적으로 폴더를 옮겼습니다", + "Move page to": "다음으로 페이지를 이동...", + "Move page to...": "다음으로 페이지를 이동...", + "Move to": "다음으로 옮기기", + "Move to Trash": "휴지통으로 옮기기", + "Moved to Trash": "휴지통으로 옮김", "My Workspaces": "내 워크스페이스", - "Name Your Workspace": "워크스페이스 이름을 입력하세요", - "New Keyword Page": "새로운 '{{query}}' 페이지", - "New Page": "새로운 페이지", - "New Workspace": "새로운 워크스페이스", - "No item": "새로운 아이템", - "Non-Gmail": "Gmail 외에는 지원되지 않습니다", - "Not now": "나중에", + "Name Your Workspace": "워크스페이스 이름 지정", + "NativeTitleBar": "기본 제목 표시줄", + "Navigation Path": "탐색 경로", + "New Keyword Page": "새 '{{query}}' 페이지", + "New Page": "새 페이지", + "New Workspace": "새 워크스페이스", + "New version is ready": "새 버전 준비 완료", + "No item": "항목 없음", + "Non-Gmail": "Gmail 이외의 이메일은 지원되지 않음", + "None yet": "아직 없음", + "Not now": "Not now", + "Note": "노트", "Official Website": "공식 웹사이트", "Open Workspace Settings": "워크스페이스 설정 열기", "Open folder": "폴더 열기", - "Open folder hint": "저장소의 위치 확인", - "Open in new tab": "새로운 탭에서 열기", - "Organize pages to build knowledge": "페이지를 구성하여 지식을 쌓으세요", + "Open folder hint": "저장 폴더가 있는 위치를 확인합니다.", + "Open in new tab": "새 탭에서 열기", + "Organize pages to build knowledge": "지식 도출을 위한 페이지 구성", "Owner": "소유자", "Page": "페이지", - "Pen": "펜 (추가 예정)", - "Permanently deleted": "영원히 삭제됨", - "Pivots": "피봇", - "Placeholder of delete workspace": "워크스페이스 이름을 입력후 확인", - "Please make sure you are online": "온라인 인것을 확인하세요", - "Publish": "공개", - "Publish to web": "웹에서 공개", - "Published Description": "현재 워크스페이스가 웹을 통해 공개되었습니다, 링크를 가진 누구든지 워크스페이스의 콘텐츠를 볼 수 있습니다.", - "Published to Web": "웹에서 공개됨", - "Publishing": "웹으로 공개하기 위해서는 AFFiNE 클라우스 서비스가 필요함.", - "Publishing Description": "웹을 통해 공유하게 되면, 링크를 가진 모든 사람이 콘텐츠를 볼 수 있습니다.", + "Paper": "페이퍼", + "Pen": "펜", + "Pending": "보류", + "Permanently deleted": "영구적으로 삭제함", + "Pivots": "피벗", + "Placeholder of delete workspace": "워크스페이스 이름을 입력해서 확인", + "Please make sure you are online": "온라인 상태인지 확인", + "Privacy": "개인정보처리방침", + "Publish": "발행", + "Publish to web": "웹으로 발행", + "Published Description": " 현재 워크스페이스를 웹으로 발행했으므로, 누구나 링크를 통해 이 워크스페이스의 콘텐츠를 볼 수 있습니다.", + "Published hint": "방문자는 제공된 링크를 통해 콘텐츠를 열람할 수 있습니다.", + "Published to Web": "웹으로 발행함", + "Publishing": "웹으로 발행하려면 AFFiNE 클라우드 서비스가 필요합니다.", + "Publishing Description": "웹으로 발행한 후에는, 누구나 링크를 통해 이 워크스페이스의 콘텐츠를 볼 수 있습니다.", + "Quick Search": "빠른 검색", "Quick search": "빠른 검색", "Quick search placeholder": "빠른 검색...", "Quick search placeholder2": "{{workspace}}에서 검색", + "RFP": "피벗에서 페이지를 자유롭게 추가/제거할 수 있으며, \"모든 페이지\"에서 계속 액세스할 수 있습니다.", "Recent": "최근", - "Redo": "되돌리기", + "Redo": "재실행", "Reduce indent": "들여쓰기 감소", + "Remove from Pivots": "피벗에서 제거", "Remove from favorites": "즐겨찾기에서 제거", - "Remove from workspace": "워크스페이스에서 삭제", - "Removed from Favorites": "즐겨찾기에서 제거됨", + "Remove from workspace": "워크스페이스에서 제거", + "Remove photo": "사진 제거", + "Remove special filter": "특수 필터 제거", + "Removed from Favorites": "즐겨찾기에서 제거함", + "Removed successfully": "성공적으로 제거함", "Rename": "이름 변경", - "Restart Install Client Update": "업데이트 하기 위해 재시작", - "Restore it": "복구하기", + "Restart Install Client Update": "업데이트 설치를 위해 재시작", + "Restore it": "항목 복원", + "Retain cached cloud data": "캐시된 클라우드 데이터 보존", + "Retain local cached data": "캐시된 로컬 데이터 보존", "Save": "저장", - "Saved then enable AFFiNE Cloud": "모든 변경사항이 로컬에 저장되었습니다, 클릭하여 AFFiNE 클라우드를 활성화 하세요.", + "Save As New Collection": "새 컬렉션으로 저장", + "Save as New Collection": "새 컬렉션으로 저장", + "Saved then enable AFFiNE Cloud": "모든 변경사항은 로컬에 저장되며, AFFiNE 클라우드를 활성화하려면 클릭하세요.", "Select": "선택", - "Set a Workspace name": "워크스페이스 이름 입력", - "Set database location": "데이터베이스 위치 선택", - "Set up an AFFiNE account to sync data": "AFFiNE 계정을 지정하여 데이터 동기화", + "Select All": "모두 선택", + "Set a Workspace name": "워크스페이스 이름 지정", + "Set database location": "데이터베이스 위치 지정", + "Set up an AFFiNE account to sync data": "데이터 동기화를 위해 AFFiNE 계정 설정하기", "Settings": "설정", "Shape": "모양", - "Share Menu Public Workspace Description1": "다른사람을 워크스페이스로 초대하거나 웹을 통하여 공유.", - "Share Menu Public Workspace Description2": "현재 워크스페이스는 웹을 통해 공유된 공유 워크스페이스입니다.", + "Share Menu Public Workspace Description1": "다른 사람들을 워크스페이스에 참여하도록 초대하거나 웹으로 발행합니다.", + "Share Menu Public Workspace Description2": "공개 워크스페이스로 현재 워크스페이스를 웹에 발행했습니다.", "Share with link": "링크로 공유", - "Shared Pages": "공유된 페이지", - "Shared Pages Description": "페이지를 공유하기 위해서 AFFiNE 클라우드 서비스가 필요합니다.", + "Shared Pages": "공유한 페이지", + "Shared Pages Description": "페이지를 공개적으로 공유하려면 AFFiNE 클라우드 서비스가 필요합니다.", + "Shared Pages In Public Workspace Description": "전체 워크스페이스를 웹으로 발행했습니다. <1>워크스페이스 설정을 통해 편집할 수 있습니다.", "Shortcuts": "단축키", - "Sign in": "AFFiNE 클라우드 로그인", - "Sign in and Enable": "로그인하고 활성화", + "Sidebar": "사이드바", + "Sign in": "AFFiNE 클라우드로 로그인", + "Sign in and Enable": "로그인 및 활성화", "Sign out": "로그아웃", - "Skip": "넘기기", + "Sign out description": "로그아웃하면 동기화되지 않은 콘텐츠가 손실됩니다.", + "Skip": "생략", + "Start Week On Monday": "한 주의 시작은 월요일", "Stay logged out": "로그아웃 상태 유지", - "Sticky": "스티키 노트 (추가 예정)", - "Stop publishing": "공유 중지", + "Sticky": "스티키 노트", + "Stop publishing": "게시 중지", + "Storage": "스토리지", "Storage Folder": "저장 폴더", + "Storage and Export": "스토리지 및 내보내기", + "Straight Connector": "직선 커넥터", "Strikethrough": "취소선", - "Successfully deleted": "성공적으로 삭제됨", - "Sync": "싱크", - "Sync across devices with AFFiNE Cloud": "AFFiNE 클라우드로 장치간 동기화", - "Synced with AFFiNE Cloud": "AFFiNE 클라우드로 동기화됨", + "Successfully deleted": "성공적으로 삭제함", + "Successfully enabled AFFiNE Cloud": "AFFiNE 클라우드 활성화 성공", + "Successfully joined!": "성공적으로 가입했습니다!", + "Switch": "전환", + "Sync": "동기화", + "Sync across devices with AFFiNE Cloud": "AFFiNE 클라우드를 통한 여러 디바이스 간 데이터 동기화", + "Synced with AFFiNE Cloud": "AFFiNE 클라우드를 통해 동기화함", + "Tags": "태그", + "Terms of Use": "이용 약관", + "Text": "본문", + "Theme": "테마", "Title": "제목", "Trash": "휴지통", - "TrashButtonGroupTitle": "영원히 삭제", - "UNKNOWN_ERROR": "알수 없는 에러", + "TrashButtonGroupDescription": "삭제한 후에는, 이 작업을 실행 취소할 수 없습니다. 확인하셨습니까?", + "TrashButtonGroupTitle": "영구적으로 삭제", + "UNKNOWN_ERROR": "알 수 없는 오류", "Underline": "밑줄", - "Undo": "되돌리기", - "Untitled": "제목 없음", + "Undo": "실행 취소", + "Ungroup": "그룹 취소", + "Unpin": "고정 취소", + "Unpublished hint": "웹에 발행하면, 방문자는 제공된 링크를 통해 콘텐츠를 볼 수 있습니다.", + "Untitled": "무제", + "Untitled Collection": "무제 컬렉션", "Update Available": "업데이트 가능", + "Update Collection": "컬렉션 업데이트", + "Update workspace name success": "성공적으로 워크스페이스 이름을 변경함", "Updated": "업데이트됨", "Upload": "업로드", - "Users": "유저들", - "Wait for Sync": "싱크 대기중", + "Use on current device only": "현재 디바이스에서만 사용", + "Users": "사용자", + "Version": "버전", + "View Navigation Path": "탐색 경로 보기", + "Visit Workspace": "워크스페이스 방문", + "Wait for Sync": "동기화 대기", + "Window frame style": "창 프레임 스타일", "Workspace Avatar": "워크스페이스 아바타", "Workspace Icon": "워크스페이스 아이콘", "Workspace Name": "워크스페이스 이름", "Workspace Not Found": "워크스페이스를 찾을 수 없음", - "Workspace Owner": "워크스페이스 주인", + "Workspace Owner": "워크스페이스 소유자", + "Workspace Profile": "워크스페이스 프로필", "Workspace Settings": "워크스페이스 설정", - "Workspace Type": "워크스페이스 종류", - "You cannot delete the last workspace": "모든", - "all": "모든", - "com.affine.cloudTempDisable.title": "AFFiNE 클라우드는 현재 개선중입니다.", - "com.affine.currentYear": "올해", - "com.affine.edgelessMode": "엣지리스 모드", + "Workspace Settings with name": "{{name}}의 설정", + "Workspace Type": "워크스페이스 유형", + "Workspace database storage description": "워크스페이스를 만들 위치를 선택합니다. 워크스페이스의 데이터는 기본적으로 로컬에 저장됩니다.", + "Workspace description": "워크스페이스는 개인 또는 팀으로 협력하여 프로젝트를 포착, 생성 및 계획할 수 있는 가상 공간입니다.", + "Workspace saved locally": "{{name}} - 로컬로 저장함", + "You cannot delete the last workspace": "마지막 워크스페이스는 삭제할 수 없음", + "Zoom in": "확대", + "Zoom out": "축소", + "Zoom to 100%": "100%로 확대", + "Zoom to fit": "크기에 맞게 확대", + "all": "모두", + "com.affine.aboutAFFiNE.autoCheckUpdate.description": "정기적으로 새 업데이트를 자동으로 확인합니다.", + "com.affine.aboutAFFiNE.autoCheckUpdate.title": "자동으로 업데이트 확인", + "com.affine.aboutAFFiNE.autoDownloadUpdate.description": "(이 디바이스로) 업데이트를 자동으로 다운로드합니다.", + "com.affine.aboutAFFiNE.autoDownloadUpdate.title": "자동으로 업데이트 다운로드", + "com.affine.aboutAFFiNE.changelog.description": "AFFiNE 변경 로그를 확인합니다.", + "com.affine.aboutAFFiNE.changelog.title": "새로운 소식 알아보기", + "com.affine.aboutAFFiNE.checkUpdate.description": "새 버전 준비됨", + "com.affine.aboutAFFiNE.checkUpdate.title": "업데이트 확인", + "com.affine.aboutAFFiNE.checkUpdate.button.check": "업데이트 확인", + "com.affine.aboutAFFiNE.checkUpdate.button.download": "업데이트 다운로드", + "com.affine.aboutAFFiNE.checkUpdate.button.restart": "업데이트 설치를 위해 재시작", + "com.affine.aboutAFFiNE.checkUpdate.button.retry": "재시도", + "com.affine.aboutAFFiNE.checkUpdate.subtitle.check": "업데이트를 수동으로 확인합니다.", + "com.affine.aboutAFFiNE.checkUpdate.subtitle.checking": "업데이트를 확인하고 있습니다...", + "com.affine.aboutAFFiNE.checkUpdate.subtitle.update-available": "업데이트 할 수 있습니다. ({{version}})", + "com.affine.aboutAFFiNE.checkUpdate.subtitle.downloading": "최신 버전을 다운로드하고 있습니다...", + "com.affine.aboutAFFiNE.checkUpdate.subtitle.restart": "다시 시작하여 업데이트를 적용합니다.", + "com.affine.aboutAFFiNE.checkUpdate.subtitle.latest": "최신 버전의 AFFiNE을 사용 중입니다.", + "com.affine.aboutAFFiNE.checkUpdate.subtitle.error": "업데이트 서버에 연결할 수 없습니다.", + "com.affine.aboutAFFiNE.community.title": "커뮤니티", + "com.affine.aboutAFFiNE.contact.community": "AFFiNE 커뮤니티", + "com.affine.aboutAFFiNE.contact.title": "문의하기", + "com.affine.aboutAFFiNE.contact.website": "공식 웹사이트", + "com.affine.aboutAFFiNE.legal.privacy": "개인정보처리방침", + "com.affine.aboutAFFiNE.legal.title": "법적 정보", + "com.affine.aboutAFFiNE.legal.tos": "이용 약관", + "com.affine.aboutAFFiNE.subtitle": "AFFiNE에 대한 정보", + "com.affine.aboutAFFiNE.title": "AFFiNE 소개", + "com.affine.aboutAFFiNE.version.app": "앱 버전", + "com.affine.aboutAFFiNE.version.editor.title": "에디터 버전", + "com.affine.aboutAFFiNE.version.title": "버전", + "com.affine.all-pages.header": "모든 페이지", + "com.affine.appUpdater.downloading": "다운로드 중", + "com.affine.appUpdater.installUpdate": "업데이트 설치를 위해 재시작", + "com.affine.appUpdater.openDownloadPage": "다운로드한 페이지 열기", + "com.affine.appUpdater.downloadUpdate": "업데이트 다운로드", + "com.affine.appUpdater.updateAvailable": "업데이트할 수 있음", + "com.affine.appUpdater.whatsNew": "새로운 소식을 알아보세요!", + "com.affine.appearanceSettings.clientBorder.description": "클라이언트의 외형을 사용자 정의합니다.", + "com.affine.appearanceSettings.clientBorder.title": "클라이언트 테두리 스타일", + "com.affine.appearanceSettings.color.description": "색상 모드 선택", + "com.affine.appearanceSettings.color.title": "색상 모드", + "com.affine.appearanceSettings.date.title": "날짜", + "com.affine.appearanceSettings.dateFormat.description": "날짜 스타일을 사용자 정의합니다.", + "com.affine.appearanceSettings.dateFormat.title": "날짜 형식", + "com.affine.appearanceSettings.font.description": "폰트 스타일 선택", + "com.affine.appearanceSettings.font.title": "폰트 스타일", + "com.affine.appearanceSettings.fontStyle.mono": "Mono", + "com.affine.appearanceSettings.fontStyle.sans": "Sans", + "com.affine.appearanceSettings.fontStyle.serif": "Serif", + "com.affine.appearanceSettings.fullWidth.description": "페이지 내 콘텐츠의 최대 표시 크기입니다.", + "com.affine.appearanceSettings.fullWidth.title": "전체 너비 레이아웃", + "com.affine.appearanceSettings.language.description": "인터페이스에 사용할 언어를 선택합니다.", + "com.affine.appearanceSettings.language.title": "표시 언어", + "com.affine.appearanceSettings.noisyBackground.description": "사이드바에 배경 노이즈 효과를 사용합니다.", + "com.affine.appearanceSettings.noisyBackground.title": "사이드바의 배경 노이즈", + "com.affine.appearanceSettings.sidebar.title": "사이드바", + "com.affine.appearanceSettings.startWeek.description": "기본적으로, 한 주는 일요일에 시작합니다.", + "com.affine.appearanceSettings.startWeek.title": "한 주의 시작은 월요일", + "com.affine.appearanceSettings.subtitle": "AFFiNE 외형 사용자 정의", + "com.affine.appearanceSettings.theme.title": "테마", + "com.affine.appearanceSettings.title": "외형 설정", + "com.affine.appearanceSettings.translucentUI.description": "사이드바에 투명도 효과를 사용합니다.", + "com.affine.appearanceSettings.translucentUI.title": "사이드바의 반투명 UI", + "com.affine.appearanceSettings.windowFrame.NativeTitleBar": "기본 제목 표시줄", + "com.affine.appearanceSettings.windowFrame.description": "Windows 클라이언트의 모양을 사용자 정의합니다.", + "com.affine.appearanceSettings.windowFrame.frameless": "프레임 없이", + "com.affine.appearanceSettings.windowFrame.title": "윈도우 프레임 스타일", + "com.affine.auth.change.email.message": "현재 이메일은 {{email}}입니다. 이 이메일 주소로 임시 인증 링크를 보내드립니다.", + "com.affine.auth.change.email.page.subtitle": "아래에 새 이메일 주소를 입력해 주세요. 절차를 완료하기 위해 이 이메일 주소로 인증 링크를 보내드립니다.", + "com.affine.auth.change.email.page.success.subtitle": "축하합니다! AFFiNE 클라우드 계정에 연결된 이메일 주소를 성공적으로 업데이트했습니다.", + "com.affine.auth.change.email.page.success.title": "이메일 주소를 업데이트했습니다!", + "com.affine.auth.change.email.page.title": "이메일 주소 변경", + "com.affine.auth.create.count": "계정 생성", + "com.affine.auth.desktop.signing.in": "로그인 하고 있습니다...", + "com.affine.auth.forget": "비밀번호 찾기", + "com.affine.auth.has.signed": "로그인 완료", + "com.affine.auth.has.signed.message": "로그인이 완료되었으며, AFFiNE 클라우드를 통해 데이터 동기화를 시작하세요!", + "com.affine.auth.later": "나중에", + "com.affine.auth.open.affine": "AFFiNE 열기", + "com.affine.auth.open.affine.download-app": "앱 다운로드", + "com.affine.auth.open.affine.prompt": "<1>AFFiNE 앱을 지금 열기", + "com.affine.auth.open.affine.try-again": "다시 시도해 주세요", + "com.affine.auth.page.sent.email.subtitle": "계속 가입하려면 문자와 숫자가 모두 포함된 8~20자의 비밀번호를 설정해 주세요.", + "com.affine.auth.page.sent.email.title": "AFFiNE 클라우드에 오신 것을 환영합니다. 거의 다 오셨습니다!", + "com.affine.auth.password": "비밀번호", + "com.affine.auth.password.error": "유효하지 않은 비밀번호", + "com.affine.auth.password.set-failed": "비밀번호 설정 실패", + "com.affine.auth.reset.password": "비밀번호 재설정", + "com.affine.auth.reset.password.message": "비밀번호를 재설정할 수 있는 링크가 포함된 이메일을 받게 됩니다. 받은 편지함을 확인해 주세요.", + "com.affine.auth.reset.password.page.success": "비밀번호 재설정 성공", + "com.affine.auth.reset.password.page.title": "AFFiNE 클라우드 비밀번호 재설정", + "com.affine.auth.send.change.email.link": "인증 링크 전송", + "com.affine.auth.send.reset.password.link": "재설정 링크 전송", + "com.affine.auth.send.set.password.link": "설정 링크 전송", + "com.affine.auth.sent": "보냄", + "com.affine.auth.sent.change.email.hint": "인증 링크를 보냈습니다.", + "com.affine.auth.sent.change.password.hint": "비밀번호 재설정 링크를 보냈습니다.", + "com.affine.auth.sent.reset.password.success.message": "비밀번호를 업그레이드했습니다! 새 비밀번호로 AFFiNE 클라우드에 로그인할 수 있습니다!", + "com.affine.auth.sent.set.password.hint": "비밀번호 설정 링크를 보냈습니다.", + "com.affine.auth.sent.set.password.success.message": "비밀번호를 저장했습니다! 이메일과 비밀번호로 AFFiNE 클라우드에 로그인할 수 있습니다!", + "com.affine.auth.set.email.save": "이메일 저장", + "com.affine.auth.set.password": "비밀번호 설정", + "com.affine.auth.set.password.message": "계속 가입하려면 문자와 숫자가 모두 포함된 8~20자의 비밀번호를 설정해 주세요.", + "com.affine.auth.set.password.page.success": "비밀번호 설정 성공", + "com.affine.auth.set.password.page.title": "AFFiNE 클라우드 비밀번호 설정", + "com.affine.auth.set.password.placeholder": "비밀번호는 최소 8자 이상", + "com.affine.auth.set.password.placeholder.confirm": "비밀번호 확인", + "com.affine.auth.set.password.save": "비밀번호 저장", + "com.affine.auth.sign-out.confirm-modal.cancel": "취소", + "com.affine.auth.sign-out.confirm-modal.confirm": "로그아웃", + "com.affine.auth.sign-out.confirm-modal.description": "로그아웃하면, 이 계정과 연결된 클라우드 워크스페이스가 현재 장치에서 제거되며, 다시 로그인하면 다시 추가됩니다.", + "com.affine.auth.sign-out.confirm-modal.title": "로그아웃하시겠어요?", + "com.affine.auth.sign.auth.code.error.hint": "잘못된 코드입니다. 다시 시도해 주세요.", + "com.affine.auth.sign.auth.code.message": "이메일을 받지 못했다면, 스팸 폴더를 확인해 주세요.", + "com.affine.auth.sign.auth.code.message.password": "또는 <1>sign in with password을 대신 사용합니다.", + "com.affine.auth.sign.auth.code.on.resend.hint": "코드 다시 전송", + "com.affine.auth.sign.auth.code.resend.hint": "코드 재전송", + "com.affine.auth.sign.condition": "약관 및 조건", + "com.affine.auth.sign.email.continue": "이메일로 계속하기", + "com.affine.auth.sign.email.error": "유효하지 않은 이메일", + "com.affine.auth.sign.email.placeholder": "이메일 주소 입력", + "com.affine.auth.sign.in": "로그인", + "com.affine.auth.sign.in.sent.email.subtitle": "이메일 확인", + "com.affine.auth.sign.message": "위의 \"Google/이메일로 계속하기\"를 클릭하면, AFFiNE의 <1>이용약관 및 <3>개인정보처리방침에 동의함을 인정하는 것입니다.", + "com.affine.auth.sign.no.access.hint": "AFFiNE 클라우드는 얼리 액세스 버전입니다. 이 링크를 통해 AFFiNE 클라우드 얼리 서포터가 되면 받을 수 있는 혜택에 대해 자세히 알아보세요: ", + "com.affine.auth.sign.no.access.link": "AFFiNE 클라우드 얼리 액세스", + "com.affine.auth.sign.no.access.wait": "공개 릴리스 대기", + "com.affine.auth.sign.policy": "개인정보처리방침", + "com.affine.auth.sign.sent.email.message.end": "링크를 클릭해서 계정을 자동으로 만들 수 있습니다.", + "com.affine.auth.sign.sent.email.message.start": "매직 링크가 포함된 이메일이 다음 주소로 전송됨 ", + "com.affine.auth.sign.up": "등록하기", + "com.affine.auth.sign.up.sent.email.subtitle": "계정 만들기", + "com.affine.auth.sign.up.success.subtitle": "앱이 자동으로 열리거나 웹 버전으로 리디렉션됩니다. 문제가 발생하면 아래 버튼을 클릭해서 AFFiNE 앱을 수동으로 열 수도 있습니다.", + "com.affine.auth.sign.up.success.title": "계정이 생성했으며 로그인했습니다!", + "com.affine.auth.signed.success.subtitle": "로그인에 성공했습니다. 앱이 자동으로 열리거나 웹 버전으로 리디렉션됩니다. 문제가 발생하면 아래 버튼을 클릭하여 AFFiNE 앱을 수동으로 열 수도 있습니다.", + "com.affine.auth.signed.success.title": "거의 다 왔습니다!", + "com.affine.auth.toast.message.failed": "서버 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.", + "com.affine.auth.toast.message.signed-in": "로그인이 완료되었으며, AFFiNE 클라우드를 통해 데이터 동기화를 시작하세요!", + "com.affine.auth.toast.title.failed": "로그인할 수 없음", + "com.affine.auth.toast.title.signed-in": "로그인함", + "com.affine.backButton": "뒤로", + "com.affine.banner.content": "이 데모는 제한적으로 제공됩니다. 최신 기능 및 성능을 확인하려면 <1>AFFiNE Client를 다운로드 해 주세요.", + "com.affine.banner.local-warning": "로컬 데이터는 브라우저에 저장되며 손실될 수 있습니다. 위험을 감수할 필요는 없습니다 - 지금 클라우드를 활성화 하세요!", + "com.affine.brand.affineCloud": "AFFiNE 클라우드", + "com.affine.cloudTempDisable.description": "AFFiNE 클라우드 서비스를 업그레이드 중이며, 현재 클라이언트 측에서는 일시적으로 서비스를 이용하실 수 없습니다. 진행 상황을 계속 확인하시고 이용 가능 여부에 대한 알림을 받으려면, <1>AFFiNE Cloud Signup 양식을 작성해 주세요.", + "com.affine.cloudTempDisable.title": "AFFiNE 클라우드는 현재 업그레이드 중입니다.", + "com.affine.cmdk.affine.category.affine.collections": "컬렉션", + "com.affine.cmdk.affine.category.affine.creation": "생성", + "com.affine.cmdk.affine.category.affine.edgeless": "Edgeless", + "com.affine.cmdk.affine.category.affine.general": "일반", + "com.affine.cmdk.affine.category.affine.help": "도움말", + "com.affine.cmdk.affine.category.affine.layout": "레이아웃 조정", + "com.affine.cmdk.affine.category.affine.navigation": "탐색", + "com.affine.cmdk.affine.category.affine.pages": "페이지", + "com.affine.cmdk.affine.category.affine.recent": "최근", + "com.affine.cmdk.affine.category.affine.settings": "설정", + "com.affine.cmdk.affine.category.affine.updates": "업데이트", + "com.affine.cmdk.affine.category.editor.edgeless": "Edgeless 명령", + "com.affine.cmdk.affine.category.editor.insert-object": "개체 삽입", + "com.affine.cmdk.affine.category.editor.page": "페이지 명령", + "com.affine.cmdk.affine.category.results": "결과", + "com.affine.cmdk.affine.client-border-style.to": "클라이언트 테두리 스타일을 다음과 같이 변경", + "com.affine.cmdk.affine.color-mode.to": "색상 모드를 다음과 같이 변경", + "com.affine.cmdk.affine.color-scheme.to": "색 구성표를 다음과 같이 변경", + "com.affine.cmdk.affine.contact-us": "문의하기", + "com.affine.cmdk.affine.create-new-edgeless-as": "새 \"{{keyWord}}\" Edgeless", + "com.affine.cmdk.affine.create-new-page-as": "새 \"{{keyWord}}\" 페이지", + "com.affine.cmdk.affine.display-language.to": "표시 언어를 다음과 같이 변경", + "com.affine.cmdk.affine.editor.add-to-favourites": "즐겨찾기에 추가", + "com.affine.cmdk.affine.editor.edgeless.presentation-start": "프리젠테이션 시작", + "com.affine.cmdk.affine.editor.remove-from-favourites": "즐겨찾기에서 제거", + "com.affine.cmdk.affine.editor.restore-from-trash": "휴지통에서 복원", + "com.affine.cmdk.affine.editor.trash-footer-hint": "이 페이지는 휴지통으로 이동되었으며, 복원하거나 영구적으로 삭제할 수 있습니다.", + "com.affine.cmdk.affine.font-style.to": "글꼴 스타일을 다음과 같이 변경", + "com.affine.cmdk.affine.full-width-layout.to": "전체 너비 레이아웃을 다음과 같이 변경", + "com.affine.cmdk.affine.getting-started": "시작하기", + "com.affine.cmdk.affine.import-workspace": "워크스페이스 가져오기", + "com.affine.cmdk.affine.left-sidebar.collapse": "왼쪽 사이드바 축소", + "com.affine.cmdk.affine.left-sidebar.expand": "왼쪽 사이드바 확장", + "com.affine.cmdk.affine.navigation.goto-all-pages": "전체 페이지로 이동", + "com.affine.cmdk.affine.navigation.goto-edgeless-list": "Edgeless 목록으로 이동", + "com.affine.cmdk.affine.navigation.goto-page-list": "페이지 목록으로 이동", + "com.affine.cmdk.affine.navigation.goto-trash": "휴지통으로 이동", + "com.affine.cmdk.affine.navigation.goto-workspace": "워크스페이스로 이동", + "com.affine.cmdk.affine.navigation.open-settings": "설정으로 이동", + "com.affine.cmdk.affine.new-edgeless-page": "새 Edgeless", + "com.affine.cmdk.affine.new-page": "새 페이지", + "com.affine.cmdk.affine.new-workspace": "새 워크스페이스", + "com.affine.cmdk.affine.noise-background-on-the-sidebar.to": "사이드바의 배경 노이즈를 다음과 같이 변경", + "com.affine.cmdk.affine.restart-to-upgrade": "다시 시작하여 업그레이드", + "com.affine.cmdk.affine.switch-state.off": "끔", + "com.affine.cmdk.affine.switch-state.on": "켬", + "com.affine.cmdk.affine.translucent-ui-on-the-sidebar.to": "사이드바의 반투명 UI를 다음과 같이 변경", + "com.affine.cmdk.affine.whats-new": "새로운 소식", + "com.affine.cmdk.affine.editor.reveal-page-history-modal": "페이지 기록 모달 표시", + "com.affine.cmdk.placeholder": "명령어를 입력하거나 무엇이든 검색합니다...", + "com.affine.collection-bar.action.tooltip.delete": "삭제", + "com.affine.collection-bar.action.tooltip.edit": "수정", + "com.affine.collection-bar.action.tooltip.pin": "사이드바에 고정", + "com.affine.collection-bar.action.tooltip.unpin": "고정 해제", + "com.affine.collection.addPage.alreadyExists": "페이지가 이미 존재함", + "com.affine.collection.addPage.success": "성공적으로 추가함", + "com.affine.collection.removePage.success": "성공적으로 제거함", + "com.affine.collection.addPages": "페이지 추가", + "com.affine.collection.addPages.tips": "<0>Add pages: 페이지를 자유롭게 선택하여 컬렉션에 추가할 수 있습니다.", + "com.affine.collection.addRules": "규칙 추가", + "com.affine.collection.addRules.tips": "<0>Add rules: 규칙은 필터링을 기반으로 합니다. 규칙을 추가하면, 현재 컬렉션에 요구 사항을 충족하는 페이지가 자동으로 추가됩니다.", + "com.affine.collection.allCollections": "모든 컬렉션", + "com.affine.collection.emptyCollection": "빈 컬렉션", + "com.affine.collection.emptyCollectionDescription": "컬렉션은 페이지를 수동으로 추가하거나 규칙을 통해 자동으로 페이지를 추가할 수 있는 스마트 폴더입니다.", + "com.affine.collection.helpInfo": "HELP INFO", + "com.affine.collection.menu.edit": "컬렉션 정보", + "com.affine.collection.menu.rename": "이름 바꾸기", + "com.affine.collectionBar.backToAll": "모두로 돌아가기", + "com.affine.collections.header": "컬렉션", + "com.affine.collections.empty.message": "컬렉션 없음", + "com.affine.collections.empty.new-collection-button": "새 컬렉션", + "com.affine.confirmModal.button.cancel": "취소", + "com.affine.currentYear": "현재 연도", + "com.affine.deleteLeaveWorkspace.description": "이 장치에서 워크스페이스를 삭제하고 선택적으로 모든 데이터를 삭제합니다.", + "com.affine.deleteLeaveWorkspace.leave": "워크스페이스 떠나기", + "com.affine.deleteLeaveWorkspace.leaveDescription": "워크스페이스를 떠나고 나면, 더 이상 이 워크스페이스 내의 콘텐츠에 액세스할 수 없습니다.", + "com.affine.draw_with_a_blank_whiteboard": "빈 화이트보드에 그리기", + "com.affine.earlier": "이름", + "com.affine.edgelessMode": "Edgeless 모드", + "com.affine.editCollection.button.cancel": "취소", + "com.affine.editCollection.button.create": "생성", + "com.affine.editCollection.createCollection": "컬렉션 생성", + "com.affine.editCollection.filters": "필터", + "com.affine.editCollection.pages": "페이지", + "com.affine.editCollection.pages.clear": "선택 취소", + "com.affine.editCollection.renameCollection": "컬렉션 이름 변경", + "com.affine.editCollection.rules": "규칙", + "com.affine.editCollection.rules.countTips": "<1>{{selectedCount}}개 선택함, <3>{{filteredCount}} 개 필터링함", + "com.affine.editCollection.rules.countTips.more": "Showing <1>{{count}} pages.", + "com.affine.editCollection.rules.countTips.one": "Showing <1>{{count}} page.", + "com.affine.editCollection.rules.countTips.zero": "Showing <1>{{count}} pages.", + "com.affine.editCollection.rules.empty.noResults": "결과 없음", + "com.affine.editCollection.rules.empty.noResults.tips": "필터링 규칙을 충족하는 페이지가 없음", + "com.affine.editCollection.rules.empty.noRules": "규칙 없음", + "com.affine.editCollection.rules.empty.noRules.tips": "<1>add rules을 통해 이 컬렉션을 저장하거나 <3>Pages로 전환하려면, 수동 선택 모드를 사용해 주세요.", + "com.affine.editCollection.rules.include.add": "선택한 페이지 추가", + "com.affine.editCollection.rules.include.is": "는", + "com.affine.editCollection.rules.include.page": "페이지", + "com.affine.editCollection.rules.include.tips": "\"선택한 페이지\"는 규칙을 통해 자동으로 추가되는 것이 아니라 수동으로 추가하는 것을 의미합니다. \"선택한 페이지 추가\" 옵션을 사용하거나 끌어서 놓기를 통해 페이지를 수동으로 추가할 수 있습니다.", + "com.affine.editCollection.rules.include.tipsTitle": "\"선택한 페이지\"란 무엇인가요?", + "com.affine.editCollection.rules.include.title": "선택한 페이지", + "com.affine.editCollection.rules.preview": "미리보기", + "com.affine.editCollection.rules.reset": "재설정", + "com.affine.editCollection.rules.tips": "규칙을 충족하는 페이지가 현재 컬렉션 <2>{{highlight}}에 추가됩니다", + "com.affine.editCollection.rules.tips.highlight": "자동", + "com.affine.editCollection.save": "저장", + "com.affine.editCollection.saveCollection": "새 컬렉션으로 저장", + "com.affine.editCollection.search.placeholder": "페이지를 검색합니다...", + "com.affine.editCollection.untitledCollection": "무제 컬렉션", + "com.affine.editCollection.updateCollection": "컬렉션 업데이트", + "com.affine.editCollectionName.createTips": "컬렉션은 페이지를 수동으로 추가하거나 규칙을 통해 페이지를 자동으로 추가할 수 있는 스마트 폴더입니다.", + "com.affine.editCollectionName.name": "이름", + "com.affine.editCollectionName.name.placeholder": "컬렉션 이름", + "com.affine.editorModeSwitch.tooltip": "전환", + "com.affine.emptyDesc": "아직 페이지가 없음", + "com.affine.enableAffineCloudModal.button.cancel": "취소", + "com.affine.expired.page.subtitle": "비밀번호 재설정 링크를 요청해 주세요.", + "com.affine.expired.page.title": "이 링크는 만료되었습니다...", + "com.affine.export.error.message": "나중에 다시 시도해 주세요.", + "com.affine.export.error.title": "예기치 않은 오류로 인해 내보내기 실패", + "com.affine.export.success.message": "다운로드 폴더를 열어 확인하시기 바랍니다.", + "com.affine.export.success.title": "성공적으로 내보냄", + "com.affine.favoritePageOperation.add": "즐겨찾기에 추가", + "com.affine.favoritePageOperation.remove": "즐겨찾기에서 제거", + "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": "모두 포함하지 않음", + "com.affine.filter.does not contains one of": "하나라도 포함하지 않음", + "com.affine.filter.false": "거짓임", + "com.affine.filter.is": "는", + "com.affine.filter.is empty": "는 비어있음", + "com.affine.filter.is not empty": "는 비어있지 않음", + "com.affine.filter.is-favourited": "즐겨찾기함", + "com.affine.filter.save-view": "보기 저장", + "com.affine.filter.true": "참임", + "com.affine.filterList.button.add": "필터 추가", + "com.affine.header.option.add-tag": "태그 추가", + "com.affine.header.option.duplicate": "복제하기", + "com.affine.helpIsland.contactUs": "Contact us", "com.affine.helpIsland.gettingStarted": "시작하기", - "com.affine.import_file": "마크다운(Markdown)/노션(Notion) 지원", - "com.affine.last30Days": "최근 30일", - "com.affine.last7Days": "최근 7일", + "com.affine.helpIsland.helpAndFeedback": "도움말과 피드백", + "com.affine.import_file": "마크다운/Notion 지원", + "com.affine.inviteModal.button.cancel": "취소", + "com.affine.keyboardShortcuts.appendDailyNote": "데일리 노트에 연결", + "com.affine.keyboardShortcuts.bodyText": "본문 내용", + "com.affine.keyboardShortcuts.bold": "굵게", + "com.affine.keyboardShortcuts.cancel": "취소", + "com.affine.keyboardShortcuts.codeBlock": "코드 블럭", + "com.affine.keyboardShortcuts.curveConnector": "곡선 커넥터", + "com.affine.keyboardShortcuts.divider": "구분자", + "com.affine.keyboardShortcuts.elbowedConnector": "직각 커넥터", + "com.affine.keyboardShortcuts.expandOrCollapseSidebar": "사이드바 확장/축소", + "com.affine.keyboardShortcuts.goBack": "이전으로", + "com.affine.keyboardShortcuts.goForward": "다음으로", + "com.affine.keyboardShortcuts.group": "그룹", + "com.affine.keyboardShortcuts.groupDatabase": "데이터베이스로 그룹화", + "com.affine.keyboardShortcuts.hand": "손", + "com.affine.keyboardShortcuts.heading": "헤딩 {{number}}", + "com.affine.keyboardShortcuts.image": "이미지", + "com.affine.keyboardShortcuts.increaseIndent": "들여쓰기 증가", + "com.affine.keyboardShortcuts.inlineCode": "인라인 코드", + "com.affine.keyboardShortcuts.italic": "기울임체", + "com.affine.keyboardShortcuts.link": "하이퍼링크 ( 선택한 텍스트 포함 )", + "com.affine.keyboardShortcuts.moveDown": "아래로 이동", + "com.affine.keyboardShortcuts.moveUp": "위로 이동", + "com.affine.keyboardShortcuts.newPage": "새 페이지", + "com.affine.keyboardShortcuts.note": "노트", + "com.affine.keyboardShortcuts.pen": "펜", + "com.affine.keyboardShortcuts.quickSearch": "빠른 검색", + "com.affine.keyboardShortcuts.redo": "재실행", + "com.affine.keyboardShortcuts.reduceIndent": "들여쓰기 감소", + "com.affine.keyboardShortcuts.select": "선택", + "com.affine.keyboardShortcuts.selectAll": "모두 선택", + "com.affine.keyboardShortcuts.shape": "모양", + "com.affine.keyboardShortcuts.straightConnector": "직선 커넥터", + "com.affine.keyboardShortcuts.strikethrough": "취소선", + "com.affine.keyboardShortcuts.subtitle": "키보드 단축키 빠른 확인", + "com.affine.keyboardShortcuts.switch": "전환", + "com.affine.keyboardShortcuts.text": "본문", + "com.affine.keyboardShortcuts.title": "키보드 단축키", + "com.affine.keyboardShortcuts.unGroup": "그룹 취소", + "com.affine.keyboardShortcuts.underline": "밑줄", + "com.affine.keyboardShortcuts.undo": "실행 취소", + "com.affine.keyboardShortcuts.zoomIn": "확대", + "com.affine.keyboardShortcuts.zoomOut": "축소", + "com.affine.keyboardShortcuts.zoomTo100": "100%로 확대", + "com.affine.keyboardShortcuts.zoomToFit": "크기에 맞게 확대", + "com.affine.last30Days": "지난 30일", + "com.affine.last7Days": "지난 7일", "com.affine.lastMonth": "지난 달", "com.affine.lastWeek": "지난 주", - "com.affine.lastYear": "작년", - "com.affine.new_edgeless": "새로운 엣지리스", + "com.affine.lastYear": "지난 해", + "com.affine.loading": "로딩...", + "com.affine.moreThan30Days": "한 달 이상", + "com.affine.moveToTrash.confirmModal.description": "{{title}} - 휴지통으로 옮김", + "com.affine.moveToTrash.confirmModal.description.multiple": "{{ number }} 페이지를 휴지통으로 옮김", + "com.affine.moveToTrash.confirmModal.title": "페이지를 삭제하시겠습니까?", + "com.affine.moveToTrash.confirmModal.title.multiple": "{{ number }} 페이지를 삭제하시겠습니까?", + "com.affine.moveToTrash.title": "휴지통으로 옮기기", + "com.affine.nameWorkspace.button.cancel": "취소", + "com.affine.nameWorkspace.button.create": "생성", + "com.affine.nameWorkspace.description": "워크스페이스는 개인 또는 팀으로 협력하여 프로젝트를 포착, 생성 및 계획할 수 있는 가상 공간입니다.", + "com.affine.nameWorkspace.placeholder": "워크스페이스 이름 지정", + "com.affine.nameWorkspace.title": "워크스페이스 이름 지정", + "com.affine.new_edgeless": "새 Edgeless", "com.affine.new_import": "불러오기", + "com.affine.notFoundPage.backButton": "홈으로 돌아가기", + "com.affine.notFoundPage.title": "페이지를 찾을 수 없음", + "com.affine.onboarding.title1": "하이퍼 병합 화이트보드와 문서", + "com.affine.onboarding.title2": "직관적이고 강력한 블록-기반 편집", + "com.affine.onboarding.videoDescription1": "구조화된 문서 작성을 위한 페이지 모드와 창의적인 아이디어를 자유롭게 시각적으로 표현할 수 있는 화이트보드 모드 간에 쉽게 전환할 수 있습니다.", + "com.affine.onboarding.videoDescription2": "모듈식 인터페이스를 사용해 텍스트, 이미지, 기타 콘텐츠 블록을 끌어다 놓는 방식으로 구조화된 문서를 손쉽게 만들 수 있습니다.", + "com.affine.openPageOperation.newTab": "새 탭에서 열기", + "com.affine.other-page.nav.affine-community": "AFFiNE 커뮤니티", + "com.affine.other-page.nav.blog": "블로그", + "com.affine.other-page.nav.contact-us": "문의하기", + "com.affine.other-page.nav.download-app": "앱 다운로드", + "com.affine.other-page.nav.official-website": "공식 웹사이트", + "com.affine.other-page.nav.open-affine": "AFFiNE 열기", + "com.affine.page.group-header.clear": "선택 취소", + "com.affine.page.group-header.select-all": "모두 선택", + "com.affine.page.toolbar.selected": "<0>{{count}} 선택함", + "com.affine.page.toolbar.selected_one": "<0>{{count}} 개의 페이지 선택함", + "com.affine.page.toolbar.selected_others": "<0>{{count}} 개의 페이지 선택함", "com.affine.pageMode": "페이지 모드", + "com.affine.pageMode.all": "모두", + "com.affine.pageMode.edgeless": "Edgeless", + "com.affine.pageMode.page": "페이지", + "com.affine.payment.benefit-1": "무제한 로컬 워크스페이스", + "com.affine.payment.benefit-2": "무제한 로그인 디바이스", + "com.affine.payment.benefit-3": "무제한 블록", + "com.affine.payment.benefit-4": "{{capacity}} 의 클라우드 스토리지", + "com.affine.payment.benefit-5": "{{capacity}} 의 최대 파일 크기", + "com.affine.payment.benefit-6": "워크스페이스당 멤버 수 ≤ {{capacity}}", + "com.affine.payment.billing-setting.cancel-subscription": "구독 취소", + "com.affine.payment.billing-setting.cancel-subscription.description": "구독이 취소되면, Pro 계정은 {{cancelDate}}에 만료됨", + "com.affine.payment.billing-setting.change-plan": "플랜 변경", + "com.affine.payment.billing-setting.current-plan": "현재 플랜", + "com.affine.payment.billing-setting.current-plan.description": "현재 <1>{{planName}} plan에 가입되어 있습니다.", + "com.affine.payment.billing-setting.current-plan.description.monthly": "현재 월간 <1>{{planName}} plan에 가입되어 있습니다.", + "com.affine.payment.billing-setting.current-plan.description.yearly": "현재 연간 <1>{{planName}} plan에 가입되어 있습니다.", + "com.affine.payment.billing-setting.expiration-date": "만료일", + "com.affine.payment.billing-setting.expiration-date.description": "구독은 {{expirationDate}}까지 유효함", + "com.affine.payment.billing-setting.history": "결제 내역", + "com.affine.payment.billing-setting.information": "정보", + "com.affine.payment.billing-setting.month": "월", + "com.affine.payment.billing-setting.no-invoice": "표시할 청구서가 없습니다.", + "com.affine.payment.billing-setting.paid": "지불", + "com.affine.payment.billing-setting.payment-method": "지불 방법", + "com.affine.payment.billing-setting.payment-method.description": "Provided by Stripe.", + "com.affine.payment.billing-setting.renew-date": "갱신일", + "com.affine.payment.billing-setting.renew-date.description": "다음 청구일: {{renewDate}}", + "com.affine.payment.billing-setting.resume-subscription": "계속", + "com.affine.payment.billing-setting.subtitle": "청구 정보 및 청구서를 관리하세요.", + "com.affine.payment.billing-setting.title": "결제", + "com.affine.payment.billing-setting.update": "업데이트", + "com.affine.payment.billing-setting.upgrade": "업그레이드", + "com.affine.payment.billing-setting.view-invoice": "청구서 표시", + "com.affine.payment.billing-setting.year": "연", + "com.affine.payment.buy-pro": "Pro 구매", + "com.affine.payment.change-to": "{{to}}로 결제를 변경", + "com.affine.payment.contact-sales": "판매 부서에 문의하기", + "com.affine.payment.current-plan": "현재 플랜", + "com.affine.payment.disable-payment.description": "AFFiNE의 특별 테스트(카나리아) 버전입니다. 이 버전에서는 계정 업그레이드가 지원되지 않습니다. 정식 서비스를 경험하고 싶으시면 웹사이트에서 안정 버전을 다운로드해 주세요.", + "com.affine.payment.disable-payment.title": "계정 업그레이드 불가", + "com.affine.payment.discount-amount": "{{amount}}% 할인", + "com.affine.payment.downgrade": "다운그레이드", + "com.affine.payment.downgraded-tooltip": "성공적으로 다운그레이드했습니다. 현재 청구 기간이 종료되면 계정이 자동으로 무료 요금제로 전환됩니다.", + "com.affine.payment.dynamic-benefit-1": "협업과 지식 도출을 위한 최고의 팀 워크스페이스입니다.", + "com.affine.payment.dynamic-benefit-2": "팀 프로젝트 관리 및 자동화를 통해 정말 중요한 것에 집중하세요.", + "com.affine.payment.dynamic-benefit-3": "모든 팀 규모에 맞는 자리를 결제하세요.", + "com.affine.payment.dynamic-benefit-4": "전용 요구 사항을 위한 솔루션 및 모범 사례", + "com.affine.payment.dynamic-benefit-5": "Embedable & interrogations with IT support.", + "com.affine.payment.member.description": "여기에서 멤버를 관리합니다. {{planName}} 사용자는 최대 {{memberLimit}}명까지 초대할 수 있습니다.", + "com.affine.payment.member.description.go-upgrade": "업그레이드 하기", + "com.affine.payment.modal.change.cancel": "취소", + "com.affine.payment.modal.change.confirm": "변경", + "com.affine.payment.modal.change.title": "구독 변경", + "com.affine.payment.modal.downgrade.cancel": "구독 취소", + "com.affine.payment.modal.downgrade.caption": "이 청구 기간이 끝날 때까지 AFFiNE 클라우드 Pro를 계속 사용할 수 있습니다. :)", + "com.affine.payment.modal.downgrade.confirm": "AFFiNE 클라우드 Pro 유지", + "com.affine.payment.modal.downgrade.content": "회원님이 떠나게 되어 아쉽지만, 저희는 항상 개선을 위해 노력하고 있으며 여러분의 피드백을 환영합니다. 나중에 다시 찾아뵙기를 기대합니다.", + "com.affine.payment.modal.downgrade.title": "정말 다운그레이드 하시겠습니까?", + "com.affine.payment.modal.resume.cancel": "취소", + "com.affine.payment.modal.resume.confirm": "확인", + "com.affine.payment.modal.resume.content": "Pro 계정의 구독을 다시 시작하시겠습니까? 다음 청구 주기부터 각 청구 주기가 끝날 때마다 결제 수단에 자동으로 요금이 청구됩니다.", + "com.affine.payment.modal.resume.title": "자동 갱신을 재개하시겠습니까?", + "com.affine.payment.plans-error-retry": "새로고침", + "com.affine.payment.plans-error-tip": "요금제를 불러올 수 없습니다. 네트워크를 확인해 주세요.", + "com.affine.payment.price-description.per-month": "월간", + "com.affine.payment.recurring-monthly": "월간", + "com.affine.payment.recurring-yearly": "연간", + "com.affine.payment.resume": "재개", + "com.affine.payment.resume-renewal": "자동 갱신 재개", + "com.affine.payment.see-all-plans": "모든 플랜 보기", + "com.affine.payment.sign-up-free": "무료 가입", + "com.affine.payment.subscription.exist": "이미 구독 중입니다.", + "com.affine.payment.subscription.go-to-subscribe": "AFFiNE 구독", + "com.affine.payment.subtitle-active": "현재 {{currentPlan}} 요금제를 사용 중입니다. 궁금한 점이 있으면 <3>customer support에 문의해 주세요.", + "com.affine.payment.subtitle-canceled": "현재 {{plan}} 요금제를 사용 중입니다. 현재 청구 기간이 끝나면 계정이 자동으로 무료 요금제로 전환됩니다.", + "com.affine.payment.subtitle-not-signed-in": "AFFiNE 클라우드의 요금제입니다. 먼저 계정에 가입하거나 로그인할 수 있습니다.", + "com.affine.payment.tag-tooltips": "모든 플랜 보기", + "com.affine.payment.title": "요금제", + "com.affine.payment.updated-notify-msg": "요금제를 {{plan}} 청구로 변경했습니다.", + "com.affine.payment.updated-notify-msg.cancel-subscription": "다음 청구 주기부터 추가 요금이 부과되지 않습니다.", + "com.affine.payment.updated-notify-title": "구독 업데이트 완료", + "com.affine.payment.upgrade": "업그레이드", + "com.affine.payment.upgrade-success-page.support": "궁금한 점이 있으면 <1>customer support에 문의해 주세요.", + "com.affine.payment.upgrade-success-page.text": "축하합니다! AFFiNE 계정이 Pro 계정으로 성공적으로 업그레이드 했습니다.", + "com.affine.payment.upgrade-success-page.title": "업그레이드에 성공했습니다!", + "com.affine.payment.member-limit.title": "제한에 도달함", + "com.affine.payment.member-limit.free.description": "각 {{planName}} 사용자는 최대 {{quota}}명의 구성원을 워크스페이스에 초대할 수 있습니다. 계정을 업그레이드하여 더 많은 구성원을 잠금 해제할 수 있습니다.", + "com.affine.payment.member-limit.pro.description": "각 {{planName}} 사용자는 최대 {{quota}}명의 구성원을 자신의 워크스페이스에 초대할 수 있습니다. 공동 작업 멤버를 계속 추가하려면 새 워크스페이스를 만들면 됩니다.", + "com.affine.payment.member-limit.free.confirm": "업그레이드", + "com.affine.payment.member-limit.pro.confirm": "알겠습니다", + "com.affine.publicLinkDisableModal.button.cancel": "취소", + "com.affine.publicLinkDisableModal.button.disable": "비활성화", + "com.affine.publicLinkDisableModal.description": "이 공개 링크를 비활성화하면 해당하는 링크를 가지고 있더라도 이 페이지에 액세스할 수 없습니다.", + "com.affine.publicLinkDisableModal.title": "공개 링크 비활성화", + "com.affine.rootAppSidebar.collections": "컬렉션", + "com.affine.rootAppSidebar.favorites": "즐겨찾기", + "com.affine.rootAppSidebar.favorites.empty": "즐겨찾기에 문서 추가 가능", + "com.affine.rootAppSidebar.others": "기타", + "com.affine.selectPage.empty": "비어 있음", + "com.affine.selectPage.empty.tips": "<1>{{search}}이 포함된 제목을 가진 페이지 없음", + "com.affine.selectPage.selected": "선택함", + "com.affine.selectPage.title": "포함하는 페이지 추가", + "com.affine.setDBLocation.button.customize": "사용자 정의", + "com.affine.setDBLocation.button.defaultLocation": "기본 위치", + "com.affine.setDBLocation.description": "워크스페이스를 만들 위치를 선택합니다. 워크스페이스의 데이터는 기본적으로 로컬에 저장됩니다.", + "com.affine.setDBLocation.title": "데이터베이스 위치 지정", + "com.affine.setDBLocation.tooltip.defaultLocation": "기본적으로 {{location}}에 저장", + "com.affine.setSyncingMode.button.continue": "계속", + "com.affine.setSyncingMode.cloud": "AFFiNE 클라우드를 통해 여러 디바이스 간 동기화", + "com.affine.setSyncingMode.deviceOnly": "현재 디바이스에서만 사용", + "com.affine.setSyncingMode.title.added": "성공적으로 추가함", + "com.affine.setSyncingMode.title.created": "성공적으로 생성함", + "com.affine.setting.account": "계정 설정", + "com.affine.setting.account.delete": "계정 삭제", + "com.affine.setting.account.delete.message": "이 계정과 워크스페이스 데이터 백업을 AFFiNE 클라우드에서 영구적으로 삭제합니다. 이 작업은 실행 취소할 수 없습니다.", + "com.affine.setting.account.message": "사용자 개인 정보", + "com.affine.setting.sign.message": "AFFiNE 클라우드를 통해 동기화", + "com.affine.setting.sign.out.message": "계정에서 안전하게 로그아웃합니다.", + "com.affine.settingSidebar.settings.general": "일반", + "com.affine.settingSidebar.settings.workspace": "워크스페이스", + "com.affine.settingSidebar.title": "설정", + "com.affine.settings.about.message": "AFFiNE에 관한 정보", + "com.affine.settings.about.update.check.message": "정기적으로 새 업데이트를 자동으로 확인합니다.", + "com.affine.settings.about.update.download.message": "(이 디바이스로) 업데이트를 자동으로 다운로드합니다.", + "com.affine.settings.appearance": "외형", + "com.affine.settings.appearance.border-style-description": "클라이언트의 외형을 사용자 정의합니다.", + "com.affine.settings.appearance.date-format-description": "날짜 스타일을 사용자 정의합니다.", + "com.affine.settings.appearance.full-width-description": "페이지 내 콘텐츠의 최대 표시 크기입니다.", + "com.affine.settings.appearance.language-description": "인터페이스에 사용할 언어를 선택합니다.", + "com.affine.settings.appearance.start-week-description": "기본적으로, 한 주는 일요일에 시작합니다.", + "com.affine.settings.appearance.window-frame-description": "Windows 클라이언트의 모양을 사용자 정의합니다.", + "com.affine.settings.auto-check-description": "이 기능을 활성화하면, 정기적으로 새 버전을 자동으로 확인합니다.", + "com.affine.settings.auto-download-description": "이 기능을 활성화하면, 새 버전이 현재 디바이스에 자동으로 다운로드됩니다.", + "com.affine.settings.email": "이메일", + "com.affine.settings.email.action": "이메일 변경", + "com.affine.settings.member-tooltip": "다른 사람들과 협업할 수 있는 AFFiNE 클라우드 활성화", + "com.affine.settings.noise-style": "사이드바의 배경 노이즈", + "com.affine.settings.noise-style-description": "사이드바에 배경 노이즈 효과를 사용합니다.", + "com.affine.settings.password": "비밀번호", + "com.affine.settings.password.action.change": "비밀번호 변경", + "com.affine.settings.password.action.set": "비밀번호 설정", + "com.affine.settings.password.message": "계정에 로그인하기 위한 비밀번호 설정", + "com.affine.settings.profile": "내 프로필", + "com.affine.settings.profile.message": "내 계정 프로필은 모든 사람에게 표시됩니다.", + "com.affine.settings.profile.name": "표시 이름", + "com.affine.settings.profile.placeholder": "계정 이름 입력", + "com.affine.settings.remove-workspace": "워크스페이스 삭제", + "com.affine.settings.remove-workspace-description": "이 장치에서 워크스페이스를 삭제하고 선택적으로 모든 데이터를 삭제합니다.", + "com.affine.settings.sign": "로그인 / 가입", + "com.affine.settings.storage.db-location.change-hint": "저장소 위치를 이동하려면 클릭합니다.", + "com.affine.settings.storage.description": "저장소 위치를 확인 또는 변경", + "com.affine.settings.storage.description-alt": "저장소 위치를 확인 또는 변경합니다. 경로를 클릭하여 위치를 수정합니다.", + "com.affine.settings.suggestion": "더 많은 사용자 정의 옵션이 필요하신가요? 커뮤니티에서 알려주세요.", + "com.affine.settings.translucent-style": "사이드바의 반투명 UI", + "com.affine.settings.translucent-style-description": "사이드바에 투명도 효과를 사용합니다.", + "com.affine.settings.workspace": "워크스페이스", + "com.affine.settings.workspace.description": "현재 워크스페이스의 정보는 여기에서 확인할 수 있습니다.", + "com.affine.settings.workspace.not-owner": "소유자만 워크스페이스 아바타와 이름을 수정할 수 있으며, 변경사항은 모든 사람에게 표시됩니다.", + "com.affine.settings.workspace.publish-tooltip": "이 워크스페이스를 발행하려면 AFFiNE 클라우드를 활성화해 주세요.", + "com.affine.settings.workspace.storage.tip": "저장소 위치를 이동하려면 클릭합니다.", + "com.affine.share-menu.EnableCloudDescription": "페이지 공유 기능을 사용하려면 AFFiNE 클라우드가 필요합니다.", + "com.affine.share-menu.ShareMode": "공유 모드", + "com.affine.share-menu.SharePage": "페이지 공유", + "com.affine.share-menu.ShareViaExport": "내보내기를 통한 공유", + "com.affine.share-menu.ShareViaExportDescription": "다른 사람들과 공유할 수 있도록 페이지의 정적 사본을 다운로드하세요.", + "com.affine.share-menu.ShareWithLink": "링크로 공유", + "com.affine.share-menu.ShareWithLinkDescription": "누구와도 쉽게 공유할 수 있는 링크를 만드세요. 방문자는 문서 형식으로 페이지를 열게 됩니다.", + "com.affine.share-menu.SharedPage": "공유한 페이지", + "com.affine.share-menu.confirm-modify-mode.notification.fail.message": "나중에 다시 시도해 주세요.", + "com.affine.share-menu.confirm-modify-mode.notification.fail.title": "수정 실패", + "com.affine.share-menu.confirm-modify-mode.notification.success.message": "공개 링크를 {{preMode}} 모드에서 {{currentMode}} 모드로 변경했습니다.", + "com.affine.share-menu.confirm-modify-mode.notification.success.title": "성공적으로 수정함", + "com.affine.share-menu.copy-private-link": "비공개 링크 복사", + "com.affine.share-menu.create-public-link.notification.fail.message": "나중에 다시 시도해 주세요.", + "com.affine.share-menu.create-public-link.notification.fail.title": "공개 링크 생성 실패", + "com.affine.share-menu.create-public-link.notification.success.message": "링크를 통해 이 문서를 공유할 수 있습니다.", + "com.affine.share-menu.create-public-link.notification.success.title": "공개 링크 생성함", + "com.affine.share-menu.disable-publish-link.notification.fail.message": "나중에 다시 시도해 주세요.", + "com.affine.share-menu.disable-publish-link.notification.fail.title": "공개 링크 비활성화 실패", + "com.affine.share-menu.disable-publish-link.notification.success.message": "이 페이지는 더 이상 공개적으로 공유하지 않습니다.", + "com.affine.share-menu.disable-publish-link.notification.success.title": "공개 링크 비활성화함", + "com.affine.share-menu.publish-to-web": "웹으로 발행", + "com.affine.share-menu.publish-to-web.description": "링크가 있는 사람은 누구나 이 페이지의 읽기 전용 버전을 볼 수 있습니다.", + "com.affine.share-menu.share-privately": "비공개 공유", + "com.affine.share-menu.share-privately.description": "이 워크스페이스의 구성원만 이 링크를 열 수 있습니다.", + "com.affine.share-menu.shareButton": "공유", + "com.affine.share-menu.sharedButton": "공유함", + "com.affine.shortcutsTitle.edgeless": "Edgeless", + "com.affine.shortcutsTitle.general": "일반", + "com.affine.shortcutsTitle.markdownSyntax": "마크다운 문법", + "com.affine.shortcutsTitle.page": "페이지", + "com.affine.sidebarSwitch.collapse": "사이드바 축소", + "com.affine.sidebarSwitch.expand": "사이드바 확장", + "com.affine.storage.change-plan": "변경", + "com.affine.storage.disabled.hint": "AFFiNE 클라우드는 현재 얼리 액세스 단계이며 업그레이드가 지원되지 않으므로, 요금제가 출시될 때까지 기다려 주시기 바랍니다.", + "com.affine.storage.extend.hint": "사용량이 최대 용량에 도달했으며, AFFiNE 클라우드는 현재 얼리 액세스 단계로 업그레이드가 지원되지 않으니 조금만 기다려 주시고, 요금제를 기다려 주시기 바랍니다.", + "com.affine.storage.extend.link": "자세한 정보를 보려면 여기를 클릭해 주세요.", + "com.affine.storage.maximum-tips": "현재 계정의 최대 용량 한도에 도달함", + "com.affine.storage.plan": "플랜", + "com.affine.storage.title": "AFFiNE 클라우드 스토리지", + "com.affine.storage.upgrade": "업그레이드", + "com.affine.storage.used.hint": "공간 사용", + "com.affine.themeSettings.dark": "어두움", + "com.affine.themeSettings.light": "밝음", + "com.affine.themeSettings.system": "시스템", + "com.affine.toastMessage.addedFavorites": "즐겨찾기에 추가함", + "com.affine.toastMessage.edgelessMode": "Edgeless 모드", + "com.affine.toastMessage.movedTrash": "휴지통으로 옮김", + "com.affine.toastMessage.pageMode": "페이지 모드", + "com.affine.toastMessage.permanentlyDeleted": "영구적으로 삭제함", + "com.affine.toastMessage.removedFavorites": "즐겨찾기에서 제거함", + "com.affine.toastMessage.restored": "{{title}} 복원함", + "com.affine.toastMessage.successfullyDeleted": "성공적으로 제거함", + "com.affine.toastMessage.rename": "성공적으로 이름을 변경함", + "com.affine.toastMessage.addLinkedPage": "성공적으로 링크한 페이지에 추가함", "com.affine.today": "오늘", - "com.affine.updater.downloading": "다운로드 진행중", - "com.affine.updater.open-download-page": "다운로드 페이지 열기", - "com.affine.updater.restart-to-update": "업데이트 하기 위해 재시작", - "com.affine.updater.update-available": "업데이트 가능", - "com.affine.workspace.cannot-delete": "마지막 워크스페이스를 삭제할 수 없습니다", - "com.affine.write_with_a_blank_page": "새로운 페이지로 작성", + "com.affine.trashOperation.delete": "삭제", + "com.affine.trashOperation.delete.description": "한번 삭제하면, 이 작업을 실행 취소할 수 없습니다. 확인 하셨습니까?", + "com.affine.trashOperation.delete.title": "영구적으로 삭제", + "com.affine.trashOperation.deleteDescription": "한번 삭제하면, 이 작업을 실행 취소할 수 없습니다. 확인 하셨습니까?", + "com.affine.trashOperation.deletePermanently": "영구적으로 삭제", + "com.affine.trashOperation.restoreIt": "항목 복원", + "com.affine.updater.downloading": "다운로드 중", + "com.affine.updater.open-download-page": "다운로드한 페이지 열기", + "com.affine.updater.restart-to-update": "업데이트 설치를 위해 재시작", + "com.affine.updater.update-available": "업데이트할 수 있음", + "com.affine.upgrade.button-text.done": "현재 페이지 새로고침", + "com.affine.upgrade.button-text.error": "데이터 업그레이드 오류", + "com.affine.upgrade.button-text.pending": "워크스페이스 데이터 업그레이드", + "com.affine.upgrade.button-text.upgrading": "업그레이드 중", + "com.affine.upgrade.tips.done": "워크스페이스 데이터를 업그레이드한 후 변경 사항을 확인하려면, 페이지를 새로 고침해 주세요.", + "com.affine.upgrade.tips.error": "워크스페이스 데이터를 업그레이드하는 동안 몇 가지 오류가 발생했습니다.", + "com.affine.upgrade.tips.normal": "업데이트된 AFFiNE 클라이언트와의 호환성을 보장하려면 아래의 \"워크스페이스 데이터 업그레이드\" 버튼을 클릭하여 데이터를 업그레이드해 주세요.", + "com.affine.workspace.cannot-delete": "마지막 워크스페이스는 삭제할 수 없음", + "com.affine.workspace.cloud": "클라우드 워크스페이스", + "com.affine.workspace.cloud.account.logout": "로그아웃", + "com.affine.workspace.cloud.account.settings": "계정 설정", + "com.affine.workspace.cloud.auth": "로그인 / 가입", + "com.affine.workspace.cloud.description": "AFFiNE 클라우드를 통해 동기화", + "com.affine.workspace.cloud.join": "워크스페이스 가입", + "com.affine.workspace.cloud.sync": "클라우드 동기화", + "com.affine.workspace.local": "로컬 워크스페이스", + "com.affine.workspace.local.import": "워크스페이스 가져오기", + "com.affine.workspaceDelete.button.cancel": "취소", + "com.affine.workspaceDelete.button.delete": "삭제", + "com.affine.workspaceDelete.description": "<1>{{workspace}} 삭제는 되돌릴 수 없으니 주의해서 진행해 주세요. 모든 내용이 손실됩니다.", + "com.affine.workspaceDelete.description2": "<1>{{workspace}} 삭제는 로컬 데이터와 클라우드 데이터가 모두 삭제되며, 이 작업은 되돌릴 수 없으므로 주의해서 진행해 주세요.", + "com.affine.workspaceDelete.placeholder": "워크스페이스 이름을 입력해 확인", + "com.affine.workspaceDelete.title": "워크스페이스 제거", + "com.affine.workspaceLeave.button.cancel": "취소", + "com.affine.workspaceLeave.button.leave": "떠나기", + "com.affine.workspaceLeave.description": "워크스페이스를 떠나고 나면, 더 이상 이 워크스페이스의 콘텐츠에 액세스할 수 없습니다.", + "com.affine.workspaceList.addWorkspace.create": "워크스페이스 생성", + "com.affine.workspaceList.workspaceListType.cloud": "클라우드 동기화", + "com.affine.workspaceList.workspaceListType.local": "로컬 스토리지", + "com.affine.workspaceSubPath.all": "모든 페이지", + "com.affine.workspaceSubPath.trash": "휴지통", + "com.affine.workspaceSubPath.trash.empty-description": "삭제한 페이지가 여기에 나타납니다.", + "com.affine.workspaceType.cloud": "클라우드 워크스페이스", + "com.affine.workspaceType.joined": "가입한 워크스페이스", + "com.affine.workspaceType.local": "로컬 워크스페이스", + "com.affine.workspaceType.offline": "오프라인 사용가능", + "com.affine.write_with_a_blank_page": "빈 페이지로 작성", "com.affine.yesterday": "어제", + "com.affine.error.retry": "새로고침", + "com.affine.error.refetch": "다시 가져오기", + "com.affine.error.reload": "다시 로드", + "com.affine.error.page-not-found.title": "새로고침", + "com.affine.error.unexpected-error.title": "뭔가 잘못되었습니다...", + "com.affine.error.contact.description": "이 문제가 계속 발생하면, <1>contact us through the community를 통해 문의해 주세요.", + "com.affine.error.no-page-root.title": "페이지 콘텐츠가 누락됨", "core": "코어", - "dark": "다크", - "emptyAllPages": "이 워크스페이스는 비어있습니다. 새로운 페이지를 생성하여 이어가세요.", - "emptySharedPages": "공유된 페이지는 여기서 확인할 수 있습니다.", - "is a Cloud Workspace": "는 클라우드 워크스페이스 입니다.", - "is a Local Workspace": "는 로컬 워크스페이스 입니다.", - "light": "밝은", + "dark": "어두움", + "emptyAllPages": "<1>$t(New Page) 버튼을 클릭하여 첫 번째 페이지를 만듭니다.", + "emptyAllPagesClient": "<1>$t(New Page) 버튼을 클릭하거나 <3>{{shortcut}} 을 눌러 첫 페이지를 만듭니다.", + "emptyFavorite": "즐겨찾기에 추가를 클릭하면 해당 페이지가 여기에 표시됩니다.", + "emptySharedPages": "공유한 페이지가 여기에 표시됩니다.", + "emptyTrash": "휴지통에 추가를 클릭하면 여기에 페이지가 표시됩니다.", + "frameless": "Frameless", + "invited you to join": "가입 초대", + "is a Cloud Workspace": "는 클라우드 워크스페이스입니다.", + "is a Local Workspace": "는 로컬 워크스페이스입니다.", + "light": "밝음", "login success": "로그인 성공", - "mobile device": "모바일 기기에서 탐색 중인 것으로 보임.", - "others": "다른", - "restored": "{{title}} 복구됨", - "still designed": "(현재 페이지는 지속적으로 수정되고 있습니다.)", + "mobile device": "모바일 디바이스에서 사용 중인 것 같습니다.", + "mobile device description": "모바일 지원은 아직 진행 중이므로 데스크톱 기기를 사용하는 것이 좋습니다.", + "others": "기타", + "recommendBrowser": " 최적의 환경을 위해 <1>Chrome 브라우저를 사용하는 것이 좋습니다.", + "restored": "{{title}} 복원함", + "still designed": "(이 페이지는 아직 설계 중입니다.)", "system": "시스템", - "upgradeBrowser": "최상의 경험을 위해 크롬을 최신 버전으로 업테이트 해주세요.", - "will delete member": "멤버를 삭제합니다" + "upgradeBrowser": "최상의 사용 환경을 위해 최신 버전의 Chrome으로 업그레이드해 주세요.", + "will be moved to Trash": "{{title}} 이 휴지통으로 옮겨집니다", + "will delete member": "멤버를 삭제", + "com.affine.history.version-history": "버전 이력", + "com.affine.history.view-history-version": "버전 이력 보기", + "com.affine.history.restore-current-version": "현재 버전 복원", + "com.affine.history.back-to-page": "페이지로 가기", + "com.affine.history.empty-prompt.title": "비어 있음", + "com.affine.history.empty-prompt.description": "이 문서는 정말 풋내기예요, 아직 역사적인 가지 하나도 돋아나지 않았어요!", + "com.affine.history.confirm-restore-modal.restore": "복원", + "com.affine.history.confirm-restore-modal.hint": "페이지의 현재 버전을 사용 가능한 최신 버전으로 복원하려고 합니다. 이 작업은 최신 버전 이전에 변경한 모든 내용을 덮어씁니다.", + "com.affine.history.confirm-restore-modal.load-more": "더 보기", + "com.affine.history.confirm-restore-modal.plan-prompt.title": "도움말 정보", + "com.affine.history.confirm-restore-modal.plan-prompt.limited-title": "페이지 기록 제한", + "com.affine.history.confirm-restore-modal.free-plan-prompt.description": "무료 사용자는 최대 <1>최근 7일<1>의 페이지 기록을 볼 수 있습니다.", + "com.affine.history.confirm-restore-modal.pro-plan-prompt.description": "Pro 사용자는 최대 <1>최근 30일<1>의 페이지 기록을 볼 수 있습니다.", + "com.affine.history.confirm-restore-modal.pro-plan-prompt.upgrade": "업그레이드", + "com.affine.share-page.header.present": "현재", + "com.affine.page-operation.add-linked-page": "링크한 페이지에 추가", + "com.affine.onboarding.workspace-guide.title": "여기에서 나만의 워크스페이스를 생성하여 AFFiNE를 시작하세요!", + "com.affine.onboarding.workspace-guide.content": "워크스페이스는 개인 또는 팀으로 협력하여 프로젝트를 포착, 생성 및 계획할 수 있는 가상 공간입니다.", + "com.affine.onboarding.workspace-guide.got-it": "알겠습니다!" } diff --git a/packages/frontend/i18n/src/scripts/utils.ts b/packages/frontend/i18n/src/scripts/utils.ts index 1eb9e58a47..00689866f7 100644 --- a/packages/frontend/i18n/src/scripts/utils.ts +++ b/packages/frontend/i18n/src/scripts/utils.ts @@ -27,9 +27,9 @@ export const flattenTranslation = ( if (!(obj instanceof Object)) return { [path ?? '']: obj }; return Object.keys(obj).reduce((output, key) => { - return { - ...output, - ...flattenTranslation(obj[key], path ? path + '.' + key : key), - }; + return Object.assign( + output, + flattenTranslation(obj[key], path ? path + '.' + key : key) + ); }, {}); }; diff --git a/packages/frontend/native/index.js b/packages/frontend/native/index.js index 0f5779e9f2..33cc759db6 100644 --- a/packages/frontend/native/index.js +++ b/packages/frontend/native/index.js @@ -269,6 +269,35 @@ switch (platform) { loadError = e } break + case 'riscv64': + if (isMusl()) { + localFileExisted = existsSync( + join(__dirname, 'affine.linux-riscv64-musl.node') + ) + try { + if (localFileExisted) { + nativeBinding = require('./affine.linux-riscv64-musl.node') + } else { + nativeBinding = require('@affine/native-linux-riscv64-musl') + } + } catch (e) { + loadError = e + } + } else { + localFileExisted = existsSync( + join(__dirname, 'affine.linux-riscv64-gnu.node') + ) + try { + if (localFileExisted) { + nativeBinding = require('./affine.linux-riscv64-gnu.node') + } else { + nativeBinding = require('@affine/native-linux-riscv64-gnu') + } + } catch (e) { + loadError = e + } + } + break default: throw new Error(`Unsupported architecture on Linux: ${arch}`) } @@ -277,6 +306,21 @@ switch (platform) { throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`) } +if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) { + try { + nativeBinding = require('./affine.wasi.cjs') + } catch { + // ignore + } + if (!nativeBinding) { + try { + nativeBinding = require('@affine/native-wasm32-wasi') + } catch (err) { + console.error(err) + } + } +} + if (!nativeBinding) { if (loadError) { throw loadError diff --git a/packages/frontend/native/package.json b/packages/frontend/native/package.json index 8050f1e04f..96ddf8004c 100644 --- a/packages/frontend/native/package.json +++ b/packages/frontend/native/package.json @@ -35,7 +35,7 @@ } }, "devDependencies": { - "@napi-rs/cli": "3.0.0-alpha.15", + "@napi-rs/cli": "3.0.0-alpha.33", "@types/node": "^20.9.3", "@types/uuid": "^9.0.7", "ava": "^6.0.0", diff --git a/packages/frontend/templates/AFFiNE-Docs.md b/packages/frontend/templates/AFFiNE-Docs.md deleted file mode 100644 index afe027faa1..0000000000 --- a/packages/frontend/templates/AFFiNE-Docs.md +++ /dev/null @@ -1,25 +0,0 @@ -# AFFiNE Docs - -This repo is the source for AFFiNE documentation. - -The published documentation is at: - -- https://docs.affine.pro - -Please [file an issue](https://github.com/toeverything/AFFiNE-docs/issues) if you can't find what you are looking for. - -## Get Started - -This repo is split into folders - with each folder being a separate category of documentation. - -You can browse the folders to view the pages within each category. - -Please note: Some pages may not display correctly on GitHub, be sure to check the corresponding page on GitBook. - -### Asset Naming - -All assets should be stored in `.gitbook/assets/`. - -The asset should be named according to where it will be used. - -If an image is needed for `/developer-docs/coding/setup.md` the expected filename would be `developer-docs_coding_setup` where `_` separates each folder/file. And if multiple images are required a unique filename can be appeneded, such as `_01`. diff --git a/packages/frontend/templates/AFFiNE-beta-0.5.4.md b/packages/frontend/templates/AFFiNE-beta-0.5.4.md deleted file mode 100644 index aab304608b..0000000000 --- a/packages/frontend/templates/AFFiNE-beta-0.5.4.md +++ /dev/null @@ -1,53 +0,0 @@ -## AFFiNE - not just a note taking app - -There are lots of apps out there, so why chose AFFiNE? - -AFFiNE is **feature-rich** - offering a wide-range of _blocks_ that _empower_ your docs - -AFFiNE is **privacy focused** - with a local-first approach, keep control of your data - -AFFiNE is **open**\-**source** - you can check us out on[GitHub: toeverything/AFFiNE](https://github.com/toeverything/affine) - ---- - -## Let's **Write**, **Draw** and **Plan** with **AFFiNE** - -### Create Your Workspace - -Creating a workspace in AFFiNE is easy! With just a few clicks, you can create a new workspace and start organising your content in a clean, user-friendly interface. - -![](https://cdn.affine.pro/94d27d73c5767986d26b81a0ced25ac6ab16686971734ba0d9c0987c.gif) - -### Organise Your Pages - -Manage your favourites and add links between your pages to easily navigate between them. This makes it easy to keep track of your content, and ensures that you always know where everything is. - -![](https://cdn.affine.pro/1326bc48553a572c6756d9ee1b30a0dfdda26222fc2d2c872b14e609.gif) - -### Write Your Content - -Easily create rich, interactive pages that combine text, images, and other media to create a dynamic, engaging experience. - -![](https://cdn.affine.pro/5fa80ab814f052d259ed6863a336a719eef746ed1bdd9b973750ce0c.gif) - -### Draw and Be Creative - -Our Edgeless Mode can be utilised as a whiteboard, allowing you to create content that extends beyond the boundaries of a traditional page - limited only by your imagination. - -![](https://cdn.affine.pro/cf5d44d36ec3167519e6d6518914d28acaf426f001da717c1e98e760.gif) - -### Plan Effectively and Efficiently - -With powerful databases you can create your content inside of tables but leverage powerful features. From adding due dates, tracking progress, adding tags and status labels. - -![](https://cdn.affine.pro/eb9832e551551d480f38b70145b28b7552e2a9f899ddfa6d813049a9.gif) - ---- - -## More support? - -There are some useful resources in the Help and Feedback section. - -![](https://cdn.affine.pro/601da8e8231a9845f03215ffe63ae71f0bc58c1cdeb5af3d47b770ed.gif) - -If you are looking for more support, you can come and join the awesome [AFFiNE Community](https://community.affine.pro) . Whether you want to share new ideas or interact with other like minded individuals - we look forward to having you. diff --git a/packages/frontend/templates/README.md b/packages/frontend/templates/README.md new file mode 100644 index 0000000000..c4630213f8 --- /dev/null +++ b/packages/frontend/templates/README.md @@ -0,0 +1,30 @@ +# @affine/templates + +Manages template files for use in AFFiNE. For now we only support onboarding templates. + +## How to update + +Before we offer a better solution, to update the onboarding templates: + +1. run AFFiNE core locally (dev mode) +2. expose `ZipTransformer` to window. e.g., `import {ZipTransformer} from '@blocksuite/blocks'; window.ZipTransformer = ZipTransformer;` +3. run the following script + ```js + (async () => { + // make sure ZipTransformer is imported and attached to window + const { ZipTransformer } = window; + await Promise.all([...currentWorkspace.blockSuiteWorkspace.pages.values()].map(p => p.load())); + // wait for a few more seconds + await new Promise(resolve => setTimeout(resolve, 5000)); + const zipblob = await ZipTransformer.exportPages(currentWorkspace.blockSuiteWorkspace, [...currentWorkspace.blockSuiteWorkspace.pages.values()]); + const url = URL.createObjectURL(zipblob); + const a = document.createElement('a'); + a.setAttribute('href', url); + a.setAttribute('download', `${currentWorkspace.id}.affine.zip`); + a.click(); + a.remove(); + URL.revokeObjectURL(url); + })(); + ``` +4. unzip the file, replace the json files into onboarding folder (no need to include the `assets`) +5. run `yarn postinstall` to update the `templates.gen.ts` file diff --git a/packages/frontend/templates/Welcome-to-AFFiNE-Abbey-Alpha-Wood.md b/packages/frontend/templates/Welcome-to-AFFiNE-Abbey-Alpha-Wood.md deleted file mode 100644 index c1ab91e47f..0000000000 --- a/packages/frontend/templates/Welcome-to-AFFiNE-Abbey-Alpha-Wood.md +++ /dev/null @@ -1,66 +0,0 @@ -# Welcome to AFFiNE Abbey Alpha Wood - -We've been working hard on improving the user experience and have made several new features live for you to use. - -If you are looking for **support**, would like to **suggest** your ideas, and keep on top of all the **latest updates**. We suggest you visit the [AFFiNE Community](https://community.affine.pro/home) site. - -Let us know what you think of this latest version. - -**AFFiNE Alpha brings:** - -1. A much smoother editing experience, with much greater stability; -2. More complete Markdown support and improved keyboard shortcuts; -3. New features such as dark mode; Switch between view styles using the ☀ and 🌙. -4. Clean and modern UI/UX design. -5. You can self-host locally with Docker. - -```basic -docker run -it --name affine -d -v [YOUR_PATH]:/app/data -p 3000:3000 ghcr.io/toeverything/affine-self-hosted:alpha-abbey-wood -``` - -**Looking for Markdown syntax or keyboard shortcuts?** - -- Find the (?) in the bottom right, then the ️⌨️, to view a full list of Keyboard Shortcuts - -### In this release, you can now: - -- Manage your pages from the collapsible **sidebar**, which allows you to add **favorites** and restore deleted files from the **trash** -- Search through all your content with the quick search - activate with `Ctrl/⌘ + K` - - A friendly Reminder: - - In the case of unselected text, `Ctrl/⌘ + K` activates quick search; - - In the case of selected text, `Ctrl/⌘ + K` will firstly ask to add a hyperlink, and then using `Ctrl/⌘ + K` again activates the quick search -- Quickly format text with the **pop-up toolbar** (highlight any text to give it a try) -- Copy and paste **images** into your pages, resize them and add captions -- Add horizontal line dividers to your text with `---` and `***` -- Changes are saved **locally**, but we still recommend you export your data to avoid data loss -- Insert code blocks with syntax highlighting support using ````` - -### Playground: - -[] Try a horizontal line: `---` - -[] What about a code block? ````` - -```JavaScript -console.log('Hello world'); -``` - -[] Can you resize this image? - -![](https://cdn.affine.pro/694fdbab78e0da3ed7922eba7d506dcf12f57308e1904dd694f53eb2.jpg) - -**How about page management?** - -[] Create a new page - -[] Send a page to trash - -[] Favorite a page - -**Have an enjoyable editing experience !!!** 😃 - -Have some feedback or just want to get in touch? Use the (?), then 🎧 to get in touch and join our communities. - -**Interested in AFFiNE cloud?** - -Head over to [general](https://community.affine.pro/c/general-discussion/) in our community and post what interests you about AFFiNE! diff --git a/packages/frontend/templates/Welcome-to-AFFiNE-Alpha-Downhills.md b/packages/frontend/templates/Welcome-to-AFFiNE-Alpha-Downhills.md deleted file mode 100644 index a990c074fd..0000000000 --- a/packages/frontend/templates/Welcome-to-AFFiNE-Alpha-Downhills.md +++ /dev/null @@ -1,43 +0,0 @@ -# Welcome to AFFiNE Alpha Downhills - -Welcome, - -Thank you for checking out **AFFiNE**. We've put together a playground below for you to try out the app. If you are looking for more **support**, offer your **ideas** and **feedback**, or find the l**atest updates** don't forget to head over to our [AFFiNE Community](https://community.affine.pro/home) site. - -###### Playground - -There are common styling options from such as **bold**, _italics,_ underline and strikethrough. As well as `in-line code blocks`. These can all be applied throughout your text, just highlight some text - you can try it here. - -> Let's explore some different blocks and features of AFFiNE. Such as this quote block - -Or maybe we can list the latest features: - -- Drag Handle - - Easily select, drag and rearrange your blocks. - - Find the `⬦` to the left of your blocks and interact with it! -- Block Hub - - A convenient home for all blocks that can empower your doc. - - Find it in the lower right, look for the `□+`. -- Slash Menu - - Want a convenient way to edit and empower your doc without the mouse? - - Try activating this feature with just the `/` key. -- Workspaces - - Further improved, with cloud support, collaboration and publishing. - - Explore more features from the `Settings` in the sidebar. - -Let's explore how to collaborate with an image: - -![](https://cdn.affine.pro/81edf5adca8f4c0d6552d0b2a54248e88054b8f8831732fc13dc03da.png) - -```JavaScript -// We can also write code in a code block -let scrambled = "V2VsY29tZSB0byBBRkZpTkU=" - -// To-do - function to unscramble -``` - -And for some future features and updates, let's make a to-do list: - -- [ ] Database Block -- [ ] Desktop and mobile clients -- [ ] Edgeless update (whiteboard mode) diff --git a/packages/frontend/templates/Welcome-to-AFFiNE.md b/packages/frontend/templates/Welcome-to-AFFiNE.md deleted file mode 100644 index 278e6fa826..0000000000 --- a/packages/frontend/templates/Welcome-to-AFFiNE.md +++ /dev/null @@ -1,31 +0,0 @@ -# Welcome to AFFiNE - -# 👋 Quick Start - -> Your content should be as feature-rich as you like. We offer you an easy and simple approach to writing your content, with advanced tools that stay out of the way when you don't need them. - -## AFFiNE - not just a note taking app - -There are lots of apps out there, so why choose AFFiNE? - -AFFiNE is **easy** - _create_ a workspace, _collaborate_ with others and _share_ it online - -AFFiNE is **feature-rich** - offering a wide-range of _blocks_ that _empower_ your docs - -AFFiNE is **privacy focused** - with a local-first approach, keep control of your data - -AFFiNE is **open source** - you can check us out on [GitHub: toeverything/AFFiNE](https://github.com/toeverything/affine) - -## Let's get started! - -- Create a new page and begin editing - in `Paper` or `Edgeless` - -![](https://cdn.affine.pro/38928b822e78a8cf6cc1278c3cd7563967dfefcf744c2517a60c7bfe.gif) - -- Head over to `Workspace Settings` to find your `General` settings, enable multiplayer with `Collaboration`, share your docs with `Publish` and download your data with `Export` - -![](https://cdn.affine.pro/34043644b47f7bab79255a80e1b11d6cb173ec9d2eb0e3208c7badd2.png) - -## Looking for more support? - -Why not come and join the awesome [AFFiNE Community](https://community.affine.pro)? Whether you want to share new ideas or interact with other like minded individuals - we look forward to having you. diff --git a/packages/frontend/templates/Welcome-to-the-AFFiNE-Alpha.md b/packages/frontend/templates/Welcome-to-the-AFFiNE-Alpha.md deleted file mode 100644 index c8be843c59..0000000000 --- a/packages/frontend/templates/Welcome-to-the-AFFiNE-Alpha.md +++ /dev/null @@ -1,61 +0,0 @@ -# Welcome to the AFFiNE Alpha - -The AFFiNE Alpha is here! You can also view our [Official Website](https://affine.pro/)! - -**What's different in AFFiNE Alpha?** - -1. A much ~smoother editing~ experience, with much ~greater stability~; -2. More complete ~Markdown~ support and improved ~keyboard shortcuts~; -3. New features such as ~dark mode~; - - **Switch between view styles using the** ☀ **and** 🌙. -4. ~Clean and modern UI/UX~ design. - -**Looking for Markdown syntax or keyboard shortcuts?** - -- Find the (?) in the bottom right, then the ️⌨️, to view a full list of `Keyboard Shortcuts` - -**Have an enjoyable editing experience !!!** 😃 - -Have some feedback or just want to get in touch? Use the (?), then 🎧 to get in touch and join our communities. - -**Still in development** - -There are also some things you should consider about this AFFiNE Alpha including some ~limitations~: - -- Single page editing - currently editing multiple docs/pages is not supported; -- Changes are not automatically stored, to save changes you should export your data. Options can be found by going to the top right and finding the `⋮` icon; -- Without an import/open feature you are still able to access your data by copying it back in. - -**Keyboard Shortcuts:** - -1. Undo: `⌘+Z` or `Ctrl+Z` -2. Redo: `⌘+⇧+Z` or `Ctrl+Shift+Z` -3. Bold: `⌘+B` or `Ctrl+B` -4. Italic: `⌘+I` or `Ctrl+I` -5. Underline: `⌘+U` or `Ctrl+U` -6. Strikethrough: `⌘+⇧+S` or `Ctrl+Shift+S` -7. Inline code: `⌘+E` or `Ctrl+E` -8. Link: `⌘+K` or `Ctrl+K` -9. Body text: `⌘+⌥+0` or `Ctrl+Shift+0` -10. Heading 1: `⌘+⌥+1` or `Ctrl+Shift+1` -11. Heading 2: `⌘+⌥+2` or `Ctrl+Shift+2` -12. Heading 3: `⌘+⌥+3` or `Ctrl+Shift+3` -13. Heading 4: `⌘+⌥+4` or `Ctrl+Shift+4` -14. Heading 5: `⌘+⌥+5` or `Ctrl+Shift+5` -15. Heading 6: `⌘+⌥+6` or `Ctrl+Shift+6` -16. Increase indent: `Tab` -17. Reduce indent: `⇧+Tab` or `Shift+Tab` - -**Markdown Syntax:** - -1. Bold: `**text**` -2. Italic: `*text*` -3. Underline: `~text~` -4. Strikethrough: `~~text~~` -5. Inline code: `` `text` `` -6. Heading 1: `# text` -7. Heading 2: `## text` -8. Heading 3: `### text` -9. Heading 4: `#### text` -10. Heading 5: `##### text` -11. Heading 6: `###### text` diff --git a/packages/frontend/templates/build.mjs b/packages/frontend/templates/build.mjs new file mode 100644 index 0000000000..8b59728a11 --- /dev/null +++ b/packages/frontend/templates/build.mjs @@ -0,0 +1,30 @@ +import fs from 'fs'; +import * as glob from 'glob'; +import path from 'path'; + +// purpose: bundle all json files into one json file in onboarding folder +const __dirname = new URL('.', import.meta.url).pathname; + +const jsonFiles = glob.sync('./*.json', { + cwd: path.resolve(__dirname, 'onboarding'), +}); + +const imports = jsonFiles + .map((fileName, index) => { + return `import json_${index} from './onboarding/${fileName}';`; + }) + .join('\n'); + +const exports = `export const onboarding = { + ${jsonFiles + .map((fileName, index) => { + return `'${fileName}': json_${index}`; + }) + .join(',\n')} +}`; + +const template = `/* eslint-disable simple-import-sort/imports */ +// Auto generated, do not edit manually +${imports}\n\n${exports}`; + +fs.writeFileSync(path.resolve(__dirname, 'templates.gen.ts'), template); diff --git a/packages/frontend/templates/onboarding/-P-O4GSfVGTkI16zJRVa4.snapshot.json b/packages/frontend/templates/onboarding/-P-O4GSfVGTkI16zJRVa4.snapshot.json new file mode 100644 index 0000000000..58cf1f459a --- /dev/null +++ b/packages/frontend/templates/onboarding/-P-O4GSfVGTkI16zJRVa4.snapshot.json @@ -0,0 +1,211 @@ +{ + "type": "page", + "meta": { + "id": "-P-O4GSfVGTkI16zJRVa4", + "title": "Templates Galleries ", + "createDate": 1691551731225, + "tags": [] + }, + "blocks": { + "type": "block", + "id": "Tz41kDyemg", + "flavour": "affine:page", + "props": { + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Templates Galleries " + } + ] + } + }, + "children": [ + { + "type": "block", + "id": "PCxQvHuwt1", + "flavour": "affine:surface", + "props": { + "elements": {} + }, + "children": [] + }, + { + "type": "block", + "id": "PMY4JPuq4o", + "flavour": "affine:note", + "props": { + "xywh": "[0,0,800,529]", + "background": "--affine-background-secondary-color", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "1KJadeDAj-", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "_a8e4OM_PP", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "No matter if you're " + }, + { + "insert": "organizing your personal life", + "attributes": { + "bold": true + } + }, + { + "insert": " or " + }, + { + "insert": "getting things done at work", + "attributes": { + "bold": true + } + }, + { + "insert": ", our templates galleries have got you covered! " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "ndmUz6zEr1", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Here We offer a wide range of resources to meet your unique needs and help you achieve your goals, whether in your personal or professional life." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "yIcapYtWKm", + "flavour": "affine:divider", + "props": {}, + "children": [] + }, + { + "type": "block", + "id": "gPqHv8Whaq", + "flavour": "affine:paragraph", + "props": { + "type": "quote", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Tired of managing your notes or coming up with a effient work plan? Whether you're a student, parent, or have diverse interests, here we provide a few templates to kick off your journey and unlock your true potential with AFFiNE and reap incredible benefits." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "pp12c2slOV", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": " ", + "attributes": { + "reference": { + "type": "LinkedPage", + "pageId": "0nee_XzkrN23Xy7HMoXL-" + } + } + }, + { + "insert": " " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "VyoH5kUerc", + "flavour": "affine:paragraph", + "props": { + "type": "quote", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Staying organized and efficient in a dynamic work environment is essential for today's working people. AFFiNE elevates productivity for project managers, software engineers, and professionals alike. We're excited to provide insights into how AFFiNE transforms work life. " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "XvCeZ-f-ib", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": " ", + "attributes": { + "reference": { + "type": "LinkedPage", + "pageId": "nPPyOV0JBNKu5hvW9hE00" + } + } + } + ] + } + }, + "children": [] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/packages/frontend/templates/onboarding/0nee_XzkrN23Xy7HMoXL-.snapshot.json b/packages/frontend/templates/onboarding/0nee_XzkrN23Xy7HMoXL-.snapshot.json new file mode 100644 index 0000000000..69b15f5ddd --- /dev/null +++ b/packages/frontend/templates/onboarding/0nee_XzkrN23Xy7HMoXL-.snapshot.json @@ -0,0 +1,178 @@ +{ + "type": "page", + "meta": { + "id": "0nee_XzkrN23Xy7HMoXL-", + "title": "Personal Home", + "createDate": 1691552082822, + "tags": [] + }, + "blocks": { + "type": "block", + "id": "QmQb34xduM", + "flavour": "affine:page", + "props": { + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Personal Home" + } + ] + } + }, + "children": [ + { + "type": "block", + "id": "f4WAKZ67ki", + "flavour": "affine:surface", + "props": { + "elements": {} + }, + "children": [] + }, + { + "type": "block", + "id": "xj7QSmgQgT", + "flavour": "affine:note", + "props": { + "xywh": "[0,0,800,547]", + "background": "--affine-background-secondary-color", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "qlnEliaAwr", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "U6w_UTi2p5", + "flavour": "affine:list", + "props": { + "type": "bulleted", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": " ", + "attributes": { + "reference": { + "type": "LinkedPage", + "pageId": "e5cYLNpIRUb-nwpSZGdix" + } + } + }, + { + "insert": " " + } + ] + }, + "checked": false, + "collapsed": false + }, + "children": [] + }, + { + "type": "block", + "id": "YN7S4Pe19c", + "flavour": "affine:paragraph", + "props": { + "type": "quote", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Personal knowledge management (PKM for short) is " + }, + { + "insert": "the process of collecting, organizing, and storing information", + "attributes": { + "bold": true + } + }, + { + "insert": ", so it's easier to search for, retrieve, share, expand upon, and use later on." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "V9nK8F7B0R", + "flavour": "affine:divider", + "props": {}, + "children": [] + }, + { + "type": "block", + "id": "jj-qOseCj-", + "flavour": "affine:list", + "props": { + "type": "bulleted", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": " ", + "attributes": { + "reference": { + "type": "LinkedPage", + "pageId": "Scod6coKmJJ-waH1-jkiW" + } + } + } + ] + }, + "checked": false, + "collapsed": false + }, + "children": [] + }, + { + "type": "block", + "id": "hgSgQdYwWJ", + "flavour": "affine:paragraph", + "props": { + "type": "quote", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "An event planning template ", + "attributes": { + "bold": true + } + }, + { + "insert": "is an efficient framework that details the steps you and your team need to consider taking to plan and execute a successful event." + } + ] + } + }, + "children": [] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/packages/frontend/templates/onboarding/9iIScyvuIB_kUKbs1AYOQ.snapshot.json b/packages/frontend/templates/onboarding/9iIScyvuIB_kUKbs1AYOQ.snapshot.json new file mode 100644 index 0000000000..10e9e39f75 --- /dev/null +++ b/packages/frontend/templates/onboarding/9iIScyvuIB_kUKbs1AYOQ.snapshot.json @@ -0,0 +1,18594 @@ +{ + "type": "page", + "meta": { + "id": "9iIScyvuIB_kUKbs1AYOQ", + "title": "AFFiNE - not just a note-taking app", + "createDate": 1691548220794, + "tags": [] + }, + "blocks": { + "type": "block", + "id": "_9D75ibqvd", + "flavour": "affine:page", + "props": { + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "AFFiNE - not just a note-taking app" + } + ] + } + }, + "children": [ + { + "type": "block", + "id": "4-IQVC-U5A", + "flavour": "affine:note", + "props": { + "xywh": "[866.5055790704506,86.14140870095756,326.7016703680564,539]", + "background": "--affine-background-secondary-color", + "index": "a2", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "1906676326:0", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "There are lots of apps out there, so why chose AFFiNE?" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "QWiWzO9705", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "AFFiNE is " + }, + { + "insert": "feature-rich", + "attributes": { + "bold": true + } + }, + { + "insert": " - offering a wide-range of " + }, + { + "insert": "blocks", + "attributes": { + "italic": true + } + }, + { + "insert": " that " + }, + { + "insert": "empower", + "attributes": { + "italic": true + } + }, + { + "insert": " your docs" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "XddQMjB8JC", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "AFFiNE is " + }, + { + "insert": "privacy focused", + "attributes": { + "bold": true + } + }, + { + "insert": " - with a local-first approach, keep control of your data" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "3uLMBGieUN", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "AFFiNE is " + }, + { + "insert": "open", + "attributes": { + "bold": true + } + }, + { + "insert": "-" + }, + { + "insert": "source", + "attributes": { + "bold": true + } + }, + { + "insert": " - you can check us out on" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "dhMxx0X4Dd", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "b0CE_s6CBB", + "flavour": "affine:bookmark", + "props": { + "style": "horizontal", + "url": "https://github.com/toeverything/affine", + "caption": "", + "description": "There can be more than Notion and Miro. AFFiNE is a next-gen knowledge base that brings planning, sorting and creating all together. Privacy first, open-source, customizable and ready to use. - Gi...", + "icon": "https://github.githubassets.com/favicons/favicon.svg", + "image": "https://repository-images.githubusercontent.com/519859998/a99cfab6-8d58-4c05-9523-ea31e0cda7f8", + "title": null, + "index": "a0", + "xywh": "[0,0,0,0]", + "rotate": 0, + "bookmarkTitle": "GitHub - toeverything/AFFiNE: There can be more than Notion and Miro. AFFiNE is a next-gen knowledge base that brings planning, sorting and creating all together. Privacy first, open-source, customizable and ready to use.", + "hasCrawled": true, + "language": "en", + "type": "object", + "provider": "GitHub", + "twitter": "@github", + "crawled": true + }, + "children": [] + }, + { + "type": "block", + "id": "i1Jsmcp-sy", + "flavour": "affine:divider", + "props": {}, + "children": [] + } + ] + }, + { + "type": "block", + "id": "Y4oz3g1LB6", + "flavour": "affine:note", + "props": { + "xywh": "[855.7586305793726,-38.93174493636967,1488.043436415603,120]", + "background": "--affine-tag-yellow", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "Prrnq7ruGC", + "flavour": "affine:paragraph", + "props": { + "type": "h1", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Let's " + }, + { + "insert": "Write", + "attributes": { + "bold": true + } + }, + { + "insert": ", " + }, + { + "insert": "Draw", + "attributes": { + "bold": true + } + }, + { + "insert": " and " + }, + { + "insert": "Plan", + "attributes": { + "bold": true + } + }, + { + "insert": " with " + }, + { + "insert": "AFFiNE", + "attributes": { + "bold": true + } + } + ] + } + }, + "children": [] + } + ] + }, + { + "type": "block", + "id": "VNbg-Wz6Vs", + "flavour": "affine:note", + "props": { + "xywh": "[1211.9913594556406,86.0748937043449,552.5830233754074,567]", + "background": "--affine-tag-green", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "hiVj6pUziI", + "flavour": "affine:paragraph", + "props": { + "type": "h3", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Create Your Workspace" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "6rgGQmAmfb", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Creating a workspace in AFFiNE is easy! With just a few clicks, you can create a new workspace and start organising your content in a clean, user-friendly interface." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "KRefAQRNnh", + "flavour": "affine:image", + "props": { + "caption": "", + "sourceId": "/static/27f983d0765289c19d10ee0b51c00c3c7665236a1a82406370d46e0a.gif", + "width": 0, + "height": 0, + "index": "a0", + "xywh": "[0,0,0,0]", + "rotate": 0, + "size": -1, + "type": "image" + }, + "children": [] + } + ] + }, + { + "type": "block", + "id": "V7dUwRJxpY", + "flavour": "affine:surface", + "props": { + "elements": { + "HAcJCK_5pU": { + "type": "brush", + "xywh": "[1199.3739609288366,67.69610348511776,41.289133053352316,60.53518339203259]", + "points": [ + [ + 10.42011260861841, + 27.26039288914211 + ], + [ + 13.427283883849213, + 21.847451555754503 + ], + [ + 15.231597661645083, + 18.840280280523643 + ], + [ + 16.434510222366725, + 17.637395251445298 + ], + [ + 17.637367719801887, + 14.630196444571084 + ], + [ + 18.238824000162595, + 14.02876769585356 + ], + [ + 18.238824000162595, + 13.427338947136036 + ], + [ + 19.441681497597756, + 11.623025169340167 + ], + [ + 20.043137777958464, + 9.818711391544298 + ], + [ + 21.245995275393625, + 8.014397613748429 + ], + [ + 22.44885277282856, + 6.8115125846700835 + ], + [ + 23.651765333550202, + 5.608627555591738 + ], + [ + 24.253166550624428, + 5.007198806874214 + ], + [ + 24.854622830985363, + 4.405770058156747 + ], + [ + 26.057480328420297, + 3.202885029078402 + ], + [ + 26.658936608781232, + 2.6014562803608783 + ], + [ + 27.861794106216166, + 2 + ], + [ + 28.4632503865771, + 2 + ], + [ + 28.4632503865771, + 2.6014562803608783 + ], + [ + 29.666107884012035, + 4.405770058156747 + ], + [ + 29.666107884012035, + 8.014397613748429 + ], + [ + 28.4632503865771, + 12.825882666775215 + ], + [ + 27.861794106216166, + 18.238824000162765 + ], + [ + 26.658936608781232, + 23.651765333550372 + ], + [ + 26.057480328420297, + 29.6661629472988 + ], + [ + 24.854622830985363, + 35.680533029403875 + ], + [ + 24.854622830985363, + 38.68773183627809 + ], + [ + 23.651765333550202, + 43.499216889304876 + ], + [ + 23.050309053189494, + 47.10784444489656 + ], + [ + 21.847451555754333, + 49.513614503053304 + ], + [ + 21.847451555754333, + 51.31792828084917 + ], + [ + 21.847451555754333, + 53.12224205864504 + ], + [ + 21.847451555754333, + 54.926555836440855 + ], + [ + 21.847451555754333, + 55.52798458515838 + ], + [ + 21.847451555754333, + 56.1294133338759 + ], + [ + 21.847451555754333, + 57.33229836295425 + ], + [ + 21.847451555754333, + 57.93372711167177 + ], + [ + 21.245995275393625, + 57.93372711167177 + ], + [ + 20.64453899503269, + 57.93372711167177 + ], + [ + 20.043137777958464, + 57.93372711167177 + ], + [ + 19.441681497597756, + 57.93372711167177 + ], + [ + 18.238824000162595, + 57.93372711167177 + ], + [ + 17.637367719801887, + 57.93372711167177 + ], + [ + 15.833053942006018, + 57.93372711167177 + ], + [ + 14.028740164210149, + 57.93372711167177 + ], + [ + 12.22442638641428, + 57.93372711167177 + ], + [ + 10.42011260861841, + 57.93372711167177 + ], + [ + 9.217255111183249, + 57.93372711167177 + ], + [ + 7.41294133338738, + 57.93372711167177 + ], + [ + 6.210028772665737, + 57.93372711167177 + ], + [ + 4.405714994870095, + 57.93372711167177 + ], + [ + 3.804313777795869, + 57.93372711167177 + ], + [ + 2.6014012170742262, + 57.93372711167177 + ], + [ + 2, + 57.93372711167177 + ], + [ + 2.6014012170742262, + 57.93372711167177 + ], + [ + 3.804313777795869, + 57.93372711167177 + ], + [ + 6.811485053026672, + 58.53518339203259 + ], + [ + 10.42011260861841, + 58.53518339203259 + ], + [ + 14.630196444570856, + 58.53518339203259 + ], + [ + 18.238824000162595, + 58.53518339203259 + ], + [ + 20.64453899503269, + 58.53518339203259 + ], + [ + 24.253166550624428, + 58.53518339203259 + ], + [ + 26.658936608781232, + 58.53518339203259 + ], + [ + 28.4632503865771, + 58.53518339203259 + ], + [ + 29.06470666693781, + 58.53518339203259 + ], + [ + 30.26756416437297, + 58.53518339203259 + ], + [ + 29.666107884012035, + 58.53518339203259 + ], + [ + 28.4632503865771, + 58.53518339203259 + ], + [ + 26.658936608781232, + 57.93372711167177 + ], + [ + 23.651765333550202, + 57.93372711167177 + ], + [ + 20.043137777958464, + 57.33229836295425 + ], + [ + 17.03591143944095, + 57.33229836295425 + ], + [ + 14.630196444570856, + 57.33229836295425 + ], + [ + 13.427283883849213, + 57.33229836295425 + ], + [ + 11.622970106053344, + 57.33229836295425 + ], + [ + 10.42011260861841, + 57.33229836295425 + ], + [ + 9.217255111183249, + 57.33229836295425 + ], + [ + 9.818656328257475, + 57.33229836295425 + ], + [ + 12.22442638641428, + 57.33229836295425 + ], + [ + 14.028740164210149, + 57.33229836295425 + ], + [ + 17.637367719801887, + 57.33229836295425 + ], + [ + 21.245995275393625, + 57.93372711167177 + ], + [ + 24.854622830985363, + 57.93372711167177 + ], + [ + 28.4632503865771, + 58.53518339203259 + ], + [ + 31.470421661807904, + 58.53518339203259 + ], + [ + 33.87619171996471, + 58.53518339203259 + ], + [ + 35.68050549776058, + 58.53518339203259 + ], + [ + 36.88336299519551, + 58.53518339203259 + ], + [ + 37.48481927555645, + 58.53518339203259 + ], + [ + 38.68767677299138, + 58.53518339203259 + ], + [ + 39.289133053352316, + 58.53518339203259 + ] + ], + "color": "--affine-palette-line-orange", + "lineWidth": 4, + "id": "HAcJCK_5pU", + "index": "b52", + "seed": 884497414 + }, + "-1rQ-C-Pj4": { + "type": "brush", + "xywh": "[1174.1135680396944,56.8702208183426,103.23725777877257,85.79557628117465]", + "points": [ + [ + 38.086275555917155, + 10.420140140261822 + ], + [ + 34.477648000325416, + 11.021568888979346 + ], + [ + 28.4632503865771, + 12.825882666775158 + ], + [ + 26.057480328420525, + 14.02876769585356 + ], + [ + 23.050309053189494, + 16.434510222366896 + ], + [ + 19.441681497597756, + 18.840280280523586 + ], + [ + 16.434510222366953, + 22.448907836115325 + ], + [ + 10.42011260861841, + 30.869020444733792 + ], + [ + 5.608627555591738, + 41.69490311150901 + ], + [ + 2.6014012170742262, + 51.91935702956664 + ], + [ + 2, + 56.1294133338759 + ], + [ + 2, + 65.15098222285519 + ], + [ + 3.804313777795869, + 72.36823733403861 + ], + [ + 5.608627555591738, + 76.57832116999123 + ], + [ + 11.021568888979346, + 80.18694872558291 + ], + [ + 19.441681497597756, + 83.19412000081383 + ], + [ + 31.470421661807904, + 83.79557628117465 + ], + [ + 43.49921688930476, + 81.38980622301796 + ], + [ + 49.513559439766595, + 79.58549244522209 + ], + [ + 62.14375588433768, + 72.96969361439949 + ], + [ + 72.36823733403867, + 66.35386725193354 + ], + [ + 82.59266372045295, + 59.738040889467584 + ], + [ + 89.80991883163642, + 53.72367080736251 + ], + [ + 95.82431644538497, + 45.904986947461566 + ], + [ + 99.4329440009767, + 36.88341805848222 + ], + [ + 101.23725777877257, + 27.861849169502932 + ], + [ + 101.23725777877257, + 23.651765333550372 + ], + [ + 100.0343452180507, + 15.833081473649372 + ], + [ + 97.0271739428199, + 9.818711391544298 + ], + [ + 89.80991883163642, + 5.007198806874214 + ], + [ + 78.38257988450027, + 2 + ], + [ + 62.14375588433768, + 3.2028850290783453 + ], + [ + 44.70207438673992, + 9.818711391544298 + ], + [ + 36.281961778121286, + 15.231652724931905 + ] + ], + "color": "--affine-palette-line-orange", + "lineWidth": 4, + "id": "-1rQ-C-Pj4", + "index": "b5C", + "seed": 1115310003 + }, + "qv6cv86xys": { + "type": "brush", + "xywh": "[1193.961019595449,106.18814909919172,175.40980889060688,54.520785778284164]", + "points": [ + [ + 2, + 52.520785778284164 + ], + [ + 6.210028772665964, + 50.115015720127474 + ], + [ + 16.434510222366725, + 44.1006731696657 + ], + [ + 23.050309053189494, + 40.49201808243066 + ], + [ + 30.86902044473368, + 36.2819617781214 + ], + [ + 48.31070194233166, + 28.4632503865771 + ], + [ + 67.55669721772506, + 20.043137777958634 + ], + [ + 86.8027475564054, + 12.825882666775215 + ], + [ + 103.64297277364244, + 6.811485053026786 + ], + [ + 115.6717680011393, + 3.804313777795869 + ], + [ + 120.48325305416597, + 3.2028574974350477 + ], + [ + 127.09905188498874, + 2 + ], + [ + 129.50482194314532, + 2 + ], + [ + 130.10627822350625, + 2 + ], + [ + 129.50482194314532, + 2 + ], + [ + 124.69333689011864, + 3.2028574974350477 + ], + [ + 111.46168416518663, + 7.412941333387607 + ], + [ + 103.64297277364244, + 10.420112608618467 + ], + [ + 85.59983499568375, + 17.03593897108442 + ], + [ + 68.158153498086, + 23.651765333550372 + ], + [ + 54.9265007731542, + 30.26756416437297 + ], + [ + 45.30353066710063, + 33.87619171996471 + ], + [ + 39.289133053352316, + 36.88339052683892 + ], + [ + 36.88336299519551, + 38.68770430463479 + ], + [ + 36.281961778121286, + 38.68770430463479 + ], + [ + 45.90493188417486, + 36.2819617781214 + ], + [ + 58.535128328745714, + 32.67333422252966 + ], + [ + 90.41137511199713, + 23.651765333550372 + ], + [ + 124.69333689011864, + 14.630196444571084 + ], + [ + 158.37384238787922, + 7.412941333387607 + ], + [ + 173.40980889060688, + 5.007171275230917 + ] + ], + "color": "--affine-palette-line-orange", + "lineWidth": 4, + "id": "qv6cv86xys", + "index": "b53", + "seed": 1714424609 + }, + "ZQgZb5kQsz": { + "type": "brush", + "xywh": "[1778.968233049558,154.9461089126983,238.56079111346207,19.637367719801944]", + "points": [ + [ + 2, + 2 + ], + [ + 2.6014012170742262, + 6.811485053026729 + ], + [ + 3.804313777795869, + 9.818656328257646 + ], + [ + 5.608627555591738, + 11.622970106053458 + ], + [ + 7.412941333387607, + 13.427283883849327 + ], + [ + 8.014342550461834, + 14.028740164210205 + ], + [ + 11.021568888979346, + 15.231597661645196 + ], + [ + 13.42728388384944, + 15.833053942006075 + ], + [ + 15.833109005292727, + 15.833053942006075 + ], + [ + 20.644538995032917, + 15.833053942006075 + ], + [ + 24.253166550624655, + 14.630196444571027 + ], + [ + 27.861794106216394, + 14.028740164210205 + ], + [ + 31.470421661808132, + 13.427283883849327 + ], + [ + 35.07904921739987, + 12.825882666775158 + ], + [ + 36.88336299519551, + 12.825882666775158 + ], + [ + 40.49199055078725, + 12.825882666775158 + ], + [ + 43.49921688930476, + 12.825882666775158 + ], + [ + 46.5064432278225, + 12.825882666775158 + ], + [ + 48.31075700561837, + 14.028740164210205 + ], + [ + 50.71647200048824, + 14.630196444571027 + ], + [ + 53.122186995358334, + 15.833053942006075 + ], + [ + 55.52801211680185, + 16.434510222366896 + ], + [ + 57.332325894597716, + 17.035911439441065 + ], + [ + 60.94095345018946, + 17.637367719801944 + ], + [ + 63.94806966213355, + 17.637367719801944 + ], + [ + 66.95529600065106, + 16.434510222366896 + ], + [ + 69.36101099552093, + 15.833053942006075 + ], + [ + 72.36823733403867, + 14.630196444571027 + ], + [ + 74.77395232890854, + 13.427283883849327 + ], + [ + 78.38257988450027, + 12.224426386414336 + ], + [ + 79.58549244522214, + 11.622970106053458 + ], + [ + 82.59271878373966, + 11.021568888979289 + ], + [ + 85.59983499568375, + 11.622970106053458 + ], + [ + 88.60706133420149, + 12.825882666775158 + ], + [ + 91.01277632907136, + 13.427283883849327 + ], + [ + 93.41860145051487, + 14.028740164210205 + ], + [ + 96.42571766245896, + 15.231597661645196 + ], + [ + 98.83154278390225, + 15.833053942006075 + ], + [ + 100.63585656169812, + 16.434510222366896 + ], + [ + 104.24448411728986, + 17.035911439441065 + ], + [ + 107.8531116728816, + 17.035911439441065 + ], + [ + 110.86022788482592, + 16.434510222366896 + ], + [ + 113.86745422334343, + 15.833053942006075 + ], + [ + 116.87468056186094, + 14.630196444571027 + ], + [ + 119.88179677380526, + 14.028740164210205 + ], + [ + 123.490424329397, + 13.427283883849327 + ], + [ + 124.69333689011866, + 13.427283883849327 + ], + [ + 127.09905188498873, + 13.427283883849327 + ], + [ + 128.9033656627846, + 13.427283883849327 + ], + [ + 130.70767944058048, + 14.028740164210205 + ], + [ + 132.51199321837635, + 15.231597661645196 + ], + [ + 134.31630699617222, + 15.833053942006075 + ], + [ + 136.7221321176155, + 16.434510222366896 + ], + [ + 138.52644589541137, + 16.434510222366896 + ], + [ + 142.1350734510031, + 15.833053942006075 + ], + [ + 143.93938722879898, + 15.833053942006075 + ], + [ + 146.9465034407433, + 14.028740164210205 + ], + [ + 149.35232856218659, + 12.825882666775158 + ], + [ + 151.15664233998245, + 12.224426386414336 + ], + [ + 152.96095611777832, + 11.021568888979289 + ], + [ + 154.7652698955742, + 11.021568888979289 + ], + [ + 155.96807232972265, + 11.021568888979289 + ], + [ + 157.17098489044406, + 11.021568888979289 + ], + [ + 158.37389745116593, + 11.622970106053458 + ], + [ + 160.1782112289618, + 12.825882666775158 + ], + [ + 161.98252500675767, + 13.427283883849327 + ], + [ + 164.38824000162754, + 14.028740164210205 + ], + [ + 167.99686755721928, + 15.231597661645196 + ], + [ + 171.605495112811, + 15.833053942006075 + ], + [ + 175.21412266840275, + 15.833053942006075 + ], + [ + 177.01843644619862, + 15.231597661645196 + ], + [ + 181.82997656251223, + 14.028740164210205 + ], + [ + 186.04000533517797, + 11.021568888979289 + ], + [ + 190.2500341078437, + 8.615798830822598 + ], + [ + 193.85866166343544, + 6.210028772665908 + ], + [ + 196.86588800195318, + 5.00717127523086 + ], + [ + 199.27160299682305, + 4.4057149948700385 + ], + [ + 201.07591677461892, + 5.00717127523086 + ], + [ + 202.2788293353408, + 5.608627555591738 + ], + [ + 204.08314311313666, + 6.811485053026729 + ], + [ + 206.48885810800653, + 8.615798830822598 + ], + [ + 208.89468322945004, + 10.420112608618467 + ], + [ + 211.90179944139413, + 11.622970106053458 + ], + [ + 215.51042699698587, + 12.825882666775158 + ], + [ + 219.1190545525776, + 12.825882666775158 + ], + [ + 224.53199588596522, + 12.825882666775158 + ], + [ + 229.34353600227882, + 12.825882666775158 + ], + [ + 231.1478497800747, + 12.825882666775158 + ], + [ + 236.56079111346207, + 11.021568888979289 + ] + ], + "color": "--affine-palette-line-orange", + "lineWidth": 4, + "id": "ZQgZb5kQsz", + "index": "b54", + "seed": 818015273 + }, + "89QPeYObe_": { + "type": "brush", + "xywh": "[1768.0340525335532,73.24604840149084,42.72564510589291,59.64028110831856]", + "points": [ + [ + 4.670734145234064, + 29.597572583361625 + ], + [ + 3.3353670726169184, + 26.036607307105953 + ], + [ + 2.4451766937643242, + 22.030506089254942 + ], + [ + 2, + 18.914636002425652 + ], + [ + 2, + 17.134153364297816 + ], + [ + 2.4451766937643242, + 15.798786291680813 + ], + [ + 3.3353670726169184, + 13.128052146446805 + ], + [ + 5.1159108389983885, + 10.457318001212826 + ], + [ + 7.786644984232225, + 7.341468290467986 + ], + [ + 11.347569508319111, + 4.670734145234007 + ], + [ + 15.353670726170094, + 3.3353670726170037 + ], + [ + 17.134214492551337, + 2.4451155655108607 + ], + [ + 20.695139016637995, + 2 + ], + [ + 23.811049855636156, + 2.890231131021693 + ], + [ + 26.92687919029663, + 4.225598203638668 + ], + [ + 29.597613335530696, + 6.006101217851011 + ], + [ + 31.378075597574025, + 8.23169942148968 + ], + [ + 32.26834748076453, + 9.567066494106655 + ], + [ + 34.9390816259986, + 14.018303653552977 + ], + [ + 35.38417681542501, + 15.798786291680813 + ], + [ + 35.38417681542501, + 20.695139016637967 + ], + [ + 33.60371455338168, + 26.036607307105953 + ], + [ + 30.932980408147614, + 31.823191163084772 + ], + [ + 26.92687919029663, + 37.60977501906359 + ], + [ + 21.58541089982873, + 43.39637925112689 + ], + [ + 18.914676754594666, + 46.06711339636087 + ], + [ + 14.018303653553176, + 50.51833017972274 + ], + [ + 9.567107246275555, + 53.18906432495669 + ], + [ + 6.896373101041718, + 54.969546963084554 + ], + [ + 5.1159108389983885, + 55.8597984701907 + ], + [ + 4.225638955807881, + 56.30491403570153 + ], + [ + 3.3353670726169184, + 56.30491403570153 + ], + [ + 2.890271883190735, + 56.30491403570153 + ], + [ + 3.3353670726169184, + 56.30491403570153 + ], + [ + 6.00610121785121, + 55.8597984701907 + ], + [ + 10.457379129466517, + 55.414682904679864 + ], + [ + 15.353670726170094, + 55.414682904679864 + ], + [ + 19.8049486377854, + 55.414682904679864 + ], + [ + 24.256145045062567, + 55.8597984701907 + ], + [ + 27.817151073487366, + 56.75004997729684 + ], + [ + 29.597613335530696, + 56.75004997729684 + ], + [ + 32.26834748076453, + 57.64028110831856 + ], + [ + 34.49398643657241, + 57.64028110831856 + ], + [ + 36.27444869861574, + 57.64028110831856 + ], + [ + 37.60981577123266, + 57.64028110831856 + ], + [ + 38.94518284384958, + 57.64028110831856 + ], + [ + 39.835454727040315, + 57.64028110831856 + ], + [ + 40.2805499164665, + 57.64028110831856 + ], + [ + 40.72564510589291, + 57.64028110831856 + ] + ], + "color": "--affine-palette-line-orange", + "lineWidth": 4, + "id": "89QPeYObe_", + "index": "b55", + "seed": 532445074 + }, + "A_oVc16bDm": { + "type": "brush", + "xywh": "[1748.0035464442983,36.300885933725795,93.46959386533877,115.28058259272154]", + "points": [ + [ + 39.39027803327599, + 103.48789751889171 + ], + [ + 36.719543888041926, + 103.93301308440255 + ], + [ + 34.493986436572186, + 104.82326459150869 + ], + [ + 31.82325229133835, + 105.26838015701958 + ], + [ + 28.26224626291355, + 105.26838015701958 + ], + [ + 26.036607307106124, + 104.82326459150869 + ], + [ + 24.256145045062794, + 104.37814902599786 + ], + [ + 20.250043827211584, + 102.59764601178557 + ], + [ + 16.2439426093606, + 99.92691186655156 + ], + [ + 11.792746202083208, + 95.03057951767892 + ], + [ + 7.786644984232453, + 88.35374415459393 + ], + [ + 6.006101217850983, + 84.79275850225378 + ], + [ + 3.7805437663812427, + 76.33544050104095 + ], + [ + 2, + 68.76835363084982 + ], + [ + 2.4451766937643242, + 59.42078412253085 + ], + [ + 3.335367072617146, + 50.96346612131802 + ], + [ + 5.561006028424572, + 41.61589661299905 + ], + [ + 10.012202435702193, + 33.15855823570175 + ], + [ + 15.353670726169867, + 25.14635579999978 + ], + [ + 18.469581565168255, + 21.58539052374411 + ], + [ + 26.92687919029663, + 14.463419219063809 + ], + [ + 36.719543888041926, + 8.23171979757413 + ], + [ + 46.957385279551545, + 3.780482638127836 + ], + [ + 56.304954787870656, + 2 + ], + [ + 64.76225241299903, + 3.3353670726169753 + ], + [ + 72.774454848701, + 6.896352724957126 + ], + [ + 79.45129021178582, + 11.34756950831897 + ], + [ + 82.12202435701988, + 14.463419219063809 + ], + [ + 87.0183974580616, + 22.475621654765774 + ], + [ + 90.13422679272185, + 30.487824090467768 + ], + [ + 91.46959386533877, + 39.83539359878674 + ], + [ + 90.57940348648594, + 48.29273197608401 + ], + [ + 87.90866934125211, + 57.64030148440301 + ], + [ + 83.90256812340112, + 66.98787099272198 + ], + [ + 78.1159231391689, + 76.78055606655178 + ], + [ + 75.00009380450842, + 81.23177284991363 + ], + [ + 67.8781632519972, + 89.24397528561559 + ], + [ + 60.75615119514782, + 96.36594659029589 + ], + [ + 52.74394875944586, + 103.04278195338088 + ], + [ + 43.84155594489107, + 108.38425024384884 + ], + [ + 34.9390816259986, + 111.94521552010451 + ], + [ + 30.48788521872143, + 113.28058259272154 + ] + ], + "color": "--affine-palette-line-orange", + "lineWidth": 4, + "id": "A_oVc16bDm", + "index": "b5B", + "seed": 924502832 + }, + "ejQqAIbwyV": { + "type": "brush", + "xywh": "[1958.0171685463724,124.04480145592629,109.21424480419864,43.98768379515283]", + "points": [ + [ + 30.808662267816118, + 41.719301915775375 + ], + [ + 28.303561201049796, + 41.98768379515283 + ], + [ + 24.12834179002687, + 41.98768379515283 + ], + [ + 19.535707467366347, + 41.719301915775375 + ], + [ + 15.360488056343689, + 41.18256272736573 + ], + [ + 13.272954800449758, + 40.64581125378342 + ], + [ + 9.932794561555276, + 39.57232059179137 + ], + [ + 7.427693494788418, + 37.96207845621705 + ], + [ + 4.922592428021833, + 35.546715252855584 + ], + [ + 2.834982722510503, + 32.86297017011674 + ], + [ + 2, + 29.910867778345562 + ], + [ + 2, + 26.153632033614613 + ], + [ + 2.417491361255255, + 24.543389898040317 + ], + [ + 4.087533255893929, + 21.591275221096573 + ], + [ + 7.427693494788418, + 18.639172829325368 + ], + [ + 14.107937522960265, + 14.88193708459448 + ], + [ + 23.710850428771607, + 11.12470133986356 + ], + [ + 34.148746057093454, + 8.172592805506088 + ], + [ + 45.83926866841613, + 5.757229602144631 + ], + [ + 57.94720619137643, + 3.6102421355743055 + ], + [ + 64.209958858293, + 2.8051210677871476 + ], + [ + 75.90048146961566, + 2 + ], + [ + 85.92088573668231, + 2.5367453309960113 + ], + [ + 93.85368029823752, + 4.146987466570318 + ], + [ + 100.11643296515417, + 6.562350669931771 + ], + [ + 103.87408456530412, + 8.97771387329324 + ], + [ + 106.79667699332602, + 12.466573881233005 + ], + [ + 107.21424480419864, + 16.492179220168758 + ], + [ + 105.96169427081539, + 20.51778455910454 + ], + [ + 104.70914373743206, + 22.664778168261137 + ], + [ + 100.11643296515417, + 26.95875310140179 + ], + [ + 92.18363840359885, + 30.71598884613269 + ], + [ + 80.07562443102093, + 35.27833337347822 + ], + [ + 67.55011909718776, + 38.230448050421934 + ], + [ + 56.27716429673798, + 40.37744165957853 + ], + [ + 46.67425139092665, + 40.914180847988305 + ], + [ + 38.32396546811611, + 40.64581125378342 + ], + [ + 34.98380522922164, + 40.64581125378342 + ], + [ + 28.72105256230505, + 39.84069018599621 + ] + ], + "color": "--affine-palette-line-tangerine", + "lineWidth": 4, + "id": "ejQqAIbwyV", + "index": "b56", + "seed": 1680628608 + }, + "6Jp3AVejnM": { + "type": "brush", + "xywh": "[2360.423645473335,227.68729583129817,221.43622144545589,541.050290364407]", + "points": [ + [ + 24.668485715856374, + 5 + ], + [ + 33.68325835516946, + 5 + ], + [ + 37.78075950688154, + 5 + ], + [ + 46.795532146194624, + 5 + ], + [ + 52.53212379377371, + 5 + ], + [ + 57.4492452227379, + 5 + ], + [ + 71.3811392910152, + 7.516826020684391 + ], + [ + 86.13250357790737, + 11.711510452680159 + ], + [ + 103.34242857928186, + 16.74516249404894 + ], + [ + 118.91326308478892, + 24.295640556102114 + ], + [ + 134.48424764893366, + 33.52401543318399 + ], + [ + 141.04030951512715, + 38.55766747455277 + ], + [ + 153.33311308753744, + 47.786042351634705 + ], + [ + 164.80644644133284, + 58.69227564002796 + ], + [ + 177.09925001374313, + 72.1153349491056 + ], + [ + 188.57258336753853, + 89.73311709389634 + ], + [ + 198.40682622546697, + 108.18986684806015 + ], + [ + 206.60197858752792, + 127.48550740416226 + ], + [ + 211.5191000164917, + 147.6201155696374 + ], + [ + 213.97766073097398, + 158.52638726174797 + ], + [ + 215.61675122684102, + 179.49992463287884 + ], + [ + 216.43622144545589, + 202.15135881903836 + ], + [ + 215.61675122684102, + 226.4806898202266 + ], + [ + 213.15819051235914, + 250.80998241769748 + ], + [ + 209.0605393020098, + 275.9782426245414 + ], + [ + 204.1434178730456, + 299.46864442007404 + ], + [ + 200.86538693994882, + 311.213806914123 + ], + [ + 193.48970479650276, + 332.1873826889712 + ], + [ + 186.11402265305668, + 353.9998492657577 + ], + [ + 177.918720232358, + 372.45659901992144 + ], + [ + 168.90409765168218, + 390.9133487740852 + ], + [ + 158.25023451650165, + 410.20898933018736 + ], + [ + 146.7770512213439, + 426.9878038656051 + ], + [ + 134.48424764893366, + 442.9277275990844 + ], + [ + 127.92803572410243, + 451.3171732705107 + ], + [ + 115.63523215169214, + 464.7401941758709 + ], + [ + 102.52295836066698, + 477.32432427929297 + ], + [ + 90.23015478825668, + 487.3916283620305 + ], + [ + 78.75682143446127, + 497.45893244476804 + ], + [ + 66.46401786205098, + 505.8483781161942 + ], + [ + 55.81030478550815, + 512.5598885688744 + ], + [ + 45.97606192757975, + 518.4325082196162 + ], + [ + 36.96128928826666, + 524.3051278703581 + ], + [ + 29.58560714482013, + 528.4998123023538 + ], + [ + 25.487955934471255, + 530.1776707136652 + ], + [ + 18.931894068277288, + 532.6944967343495 + ], + [ + 14.014772639313081, + 535.211322755034 + ], + [ + 10.736591647578638, + 535.211322755034 + ], + [ + 8.27803093309676, + 536.050290364407 + ], + [ + 7.458560714481881, + 536.050290364407 + ], + [ + 5.81947021861488, + 536.050290364407 + ], + [ + 5, + 536.050290364407 + ] + ], + "color": "--affine-palette-line-orange", + "lineWidth": 10, + "id": "6Jp3AVejnM", + "index": "b57", + "seed": 209981851 + }, + "wu6_2zbm4B": { + "type": "brush", + "xywh": "[2343.2289649844256,726.054431401066,61.175411215626355,63.69231404374534]", + "points": [ + [ + 27.65143418615935, + 5 + ], + [ + 24.29556374866752, + 8.355793630057406 + ], + [ + 18.423020905360318, + 16.745239301483707 + ], + [ + 13.389368863991422, + 25.9735757748482 + ], + [ + 11.711433645245506, + 29.329369404905606 + ], + [ + 10.033652041368896, + 32.68508622752836 + ], + [ + 6.6777816038766105, + 38.55770587827021 + ], + [ + 5, + 42.75239031026592 + ], + [ + 5, + 44.43032552901195 + ], + [ + 5.838890801938305, + 45.26921633095026 + ], + [ + 7.516826020684221, + 46.9471515496964 + ], + [ + 13.389368863991422, + 47.786042351634705 + ], + [ + 21.778737727982843, + 49.463977570380735 + ], + [ + 31.84604181072064, + 51.98080359106518 + ], + [ + 42.75239031026604, + 55.33652041368782 + ], + [ + 51.980649976195764, + 58.69231404374534 + ], + [ + 56.175411215626355, + 58.69231404374534 + ] + ], + "color": "--affine-palette-line-orange", + "lineWidth": 10, + "id": "wu6_2zbm4B", + "index": "b58", + "seed": 338012081 + }, + "raHEkjUvv_": { + "type": "brush", + "xywh": "[1819.1087993210533,641.4106664311514,45.67937701858909,75.57980958071607]", + "points": [ + [ + 5.62428103042771, + 27.82310084910955 + ], + [ + 4.718221142011544, + 25.55790965130609 + ], + [ + 3.359110571005658, + 21.480577938288885 + ], + [ + 2.9060598884161664, + 17.403246225271573 + ], + [ + 2.9060598884161664, + 15.591105710057718 + ], + [ + 3.359110571005658, + 12.419833885456567 + ], + [ + 4.2651704594218245, + 9.248582799236942 + ], + [ + 6.077331713017429, + 6.530361657225399 + ], + [ + 8.795552855028745, + 4.265170459422052 + ], + [ + 13.325935250635666, + 2.453029944208083 + ], + [ + 17.856276169479543, + 2 + ], + [ + 22.839709247676183, + 2 + ], + [ + 25.104879707098007, + 2.453029944208083 + ], + [ + 29.182211420115436, + 3.3591105710058855 + ], + [ + 33.25954313313264, + 5.171251086219627 + ], + [ + 36.88382416356035, + 7.436442284023087 + ], + [ + 39.60204530557189, + 10.154663426034631 + ], + [ + 41.86725724175676, + 13.77894445646234 + ], + [ + 43.226367812762646, + 16.95021628106349 + ], + [ + 43.67937701858909, + 20.121467367283117 + ], + [ + 43.226367812762646, + 21.933607882496972 + ], + [ + 42.320266447583435, + 25.55790965130609 + ], + [ + 40.5081466707511, + 28.276130793317634 + ], + [ + 37.78992552873956, + 30.541321991121094 + ], + [ + 35.071704386728015, + 32.806492450543146 + ], + [ + 31.900432562126753, + 34.165603021548804 + ], + [ + 28.729160737525717, + 35.52471359255469 + ], + [ + 25.104879707098007, + 35.97776427514418 + ], + [ + 23.29271845350263, + 36.430794219352265 + ], + [ + 20.12148810566464, + 36.88382416356035 + ], + [ + 17.856276169479543, + 36.88382416356035 + ], + [ + 16.04415639264721, + 36.88382416356035 + ], + [ + 14.685045821641552, + 36.88382416356035 + ], + [ + 13.778944456462343, + 36.88382416356035 + ], + [ + 13.325935250635666, + 36.88382416356035 + ], + [ + 14.231995139051831, + 36.88382416356035 + ], + [ + 16.497165598473885, + 37.336874846149954 + ], + [ + 20.574497311491086, + 38.24293473456612 + ], + [ + 25.104879707098007, + 39.14901536136381 + ], + [ + 29.182211420115436, + 40.50812593236959 + ], + [ + 33.25954313313264, + 42.77331713017304 + ], + [ + 34.618653704138524, + 44.13242770117881 + ], + [ + 37.78992552873956, + 46.850648843190356 + ], + [ + 39.60204530557189, + 49.5688699852019 + ], + [ + 41.41420655916727, + 52.28709112721344 + ], + [ + 41.86725724175676, + 55.45836295181448 + ], + [ + 41.41420655916727, + 58.62959329965269 + ], + [ + 39.60204530557189, + 62.25391580684345 + ], + [ + 36.88382416356035, + 65.87819683727105 + ], + [ + 35.97776427514418, + 67.23730740827682 + ], + [ + 30.994372673710586, + 71.31463912129414 + ], + [ + 29.182211420115436, + 72.2206990097103 + ], + [ + 24.19881981868184, + 73.57980958071607 + ], + [ + 20.12148810566464, + 73.57980958071607 + ], + [ + 16.497165598473885, + 73.57980958071607 + ], + [ + 12.419833885456455, + 72.2206990097103 + ], + [ + 9.248603537618465, + 71.31463912129414 + ], + [ + 7.889492966612579, + 69.95552855028836 + ], + [ + 5.62428103042771, + 68.59641797928259 + ], + [ + 4.2651704594218245, + 66.78425672568721 + ], + [ + 2.9060598884161664, + 64.97213694885488 + ], + [ + 2, + 63.61302637784911 + ], + [ + 2, + 62.25391580684345 + ], + [ + 2, + 60.894805235837566 + ] + ], + "color": "--affine-palette-line-orange", + "lineWidth": 4, + "id": "raHEkjUvv_", + "index": "b59", + "seed": 1935886551 + }, + "seZnsPPCAq": { + "type": "brush", + "color": "--affine-palette-line-orange", + "id": "seZnsPPCAq", + "seed": 351264218, + "index": "b5A", + "lineWidth": 4, + "xywh": "[1789.8574858146808,628.3050933273901,120.97354401506209,108.80294601251569]", + "points": [ + [ + 18.90365304472016, + 18.903714947897356 + ], + [ + 22.28443317620622, + 16.199103223344082 + ], + [ + 31.074312950443755, + 10.113773270482397 + ], + [ + 38.51197971717079, + 6.7330550421738735 + ], + [ + 41.21652953854664, + 5.380780131485949 + ], + [ + 43.9211412630998, + 5.380780131485949 + ], + [ + 47.978027898341224, + 4.028443317620599 + ], + [ + 51.35874612664975, + 3.3523368138653495 + ], + [ + 53.387189444270234, + 2.6761684069326748 + ], + [ + 54.739526258135584, + 2.6761684069326748 + ], + [ + 52.7110829405151, + 2.6761684069326748 + ], + [ + 50.00647121596171, + 3.3523368138653495 + ], + [ + 47.30185949140832, + 3.3523368138653495 + ], + [ + 45.27341617378784, + 3.3523368138653495 + ], + [ + 43.9211412630998, + 3.3523368138653495 + ], + [ + 45.27341617378784, + 3.3523368138653495 + ], + [ + 52.7110829405151, + 2.6761684069326748 + ], + [ + 58.79641289337678, + 2 + ], + [ + 76.37623434502939, + 2 + ], + [ + 81.10928938720326, + 2 + ], + [ + 89.22306265768566, + 3.3523368138653495 + ], + [ + 94.6322242036149, + 5.380780131485949 + ], + [ + 98.68911083885587, + 7.409223449106548 + ], + [ + 102.06982906716439, + 8.761498359794473 + ], + [ + 102.7459974740973, + 9.437666766727148 + ], + [ + 104.0982723847851, + 10.113773270482397 + ], + [ + 104.0982723847851, + 11.466110084347633 + ], + [ + 102.7459974740973, + 12.142216588102997 + ], + [ + 100.71755415647658, + 13.494553401968233 + ], + [ + 98.01294243192342, + 14.170659905723483 + ], + [ + 95.30839261054734, + 14.170659905723483 + ], + [ + 93.956055796682, + 14.170659905723483 + ], + [ + 93.27994929292686, + 14.170659905723483 + ], + [ + 92.60378088599418, + 14.170659905723483 + ], + [ + 91.92761247906151, + 14.170659905723483 + ], + [ + 93.956055796682, + 14.846828312656157 + ], + [ + 95.98449911430271, + 15.522996719588832 + ], + [ + 102.06982906716439, + 16.875271630276757 + ], + [ + 106.80288410933827, + 19.57988335483003 + ], + [ + 111.53593915151214, + 24.31287649382648 + ], + [ + 113.56438246913285, + 27.017488218379754 + ], + [ + 116.2689322905087, + 32.42664976430888 + ], + [ + 118.29737560812919, + 37.835873213415425 + ], + [ + 118.97354401506209, + 41.892759848656624 + ], + [ + 118.97354401506209, + 47.30192139458575 + ], + [ + 118.29737560812919, + 52.03497643675962 + ], + [ + 116.94510069744138, + 56.76796957575607 + ], + [ + 114.24048897288822, + 62.85329952861787 + ], + [ + 112.88821406220018, + 66.23407966010382 + ], + [ + 108.1551590200263, + 72.3194096129655 + ], + [ + 102.7459974740973, + 77.72857115889462 + ], + [ + 98.68911083885587, + 83.13773270482375 + ], + [ + 94.6322242036149, + 87.19461934006495 + ], + [ + 89.22306265768566, + 91.25150597530615 + ], + [ + 81.78539589095863, + 94.6322861067921 + ], + [ + 73.67162262047623, + 98.68917274203329 + ], + [ + 69.61473598523503, + 100.71761605965389 + ], + [ + 61.500962714752625, + 104.09833428796242 + ], + [ + 54.739526258135584, + 106.12677760558302 + ], + [ + 47.30185949140832, + 106.80294601251569 + ], + [ + 40.540423034791274, + 106.80294601251569 + ], + [ + 33.778924674996915, + 105.45060919865034 + ], + [ + 25.665151404514745, + 103.42216588102974 + ], + [ + 18.90365304472016, + 101.39372256340914 + ], + [ + 16.19910322334431, + 100.04144765272122 + ], + [ + 10.789879774237761, + 96.6607294244127 + ], + [ + 6.732993138996562, + 93.95611769985942 + ], + [ + 4.028443317620713, + 88.5469561539303 + ], + [ + 2, + 79.75701447651522 + ], + [ + 2.6761065037553635, + 62.85329952861787 + ], + [ + 3.3522749106880383, + 57.444137982688744 + ], + [ + 5.380718228308524, + 49.33036471220635 + ], + [ + 6.732993138996562, + 45.27347807696515 + ], + [ + 10.113773270482397, + 37.835873213415425 + ], + [ + 15.522934816411635, + 31.750543260553627 + ], + [ + 23.636708086894032, + 24.31287649382648 + ], + [ + 33.778924674996915, + 17.55144003720943 + ], + [ + 43.24497285616735, + 13.494553401968233 + ], + [ + 51.35874612664975, + 10.789941677415072 + ], + [ + 58.79641289337678, + 10.113773270482397 + ], + [ + 64.20557443930602, + 10.789941677415072 + ] + ] + }, + "Up_Mw9AQ5n": { + "type": "shape", + "xywh": "[857.1915051196747,-43.39418916752216,1492.6518602855915,124.57724809946413]", + "shapeType": "rect", + "radius": 0, + "filled": false, + "fillColor": "--affine-palette-transparent", + "strokeWidth": 4, + "strokeColor": "--affine-palette-line-orange", + "strokeStyle": "solid", + "roughness": 2, + "id": "Up_Mw9AQ5n", + "index": "b50", + "seed": 401827629 + }, + "5PoPey_Lmj": { + "type": "brush", + "xywh": "[1274.3716617962953,643.2031417400783,52.58851382983676,52.58851382983664]", + "points": [ + [ + 2.418879391524115, + 2 + ], + [ + 4.094320260853465, + 4.094320260853465 + ], + [ + 5.769799478566256, + 5.769799478566256 + ], + [ + 8.701878522467723, + 10.377319391797187 + ], + [ + 9.958478348656627, + 16.660318522740795 + ], + [ + 9.120719565608397, + 25.456517306061983 + ], + [ + 7.026399304754932, + 34.25271608938317 + ], + [ + 4.932079043901467, + 41.373474003374895 + ], + [ + 3.2565998261889035, + 46.39987330812983 + ], + [ + 2.8377204346647886, + 47.65647313431862 + ], + [ + 2, + 49.75079339517208 + ], + [ + 2, + 50.58851382983664 + ], + [ + 2.8377204346647886, + 50.58851382983664 + ], + [ + 5.769799478566256, + 50.58851382983664 + ], + [ + 10.796198783321188, + 49.75079339517208 + ], + [ + 15.82259808807612, + 48.07531417745929 + ], + [ + 18.754677131977587, + 47.2375937427945 + ], + [ + 24.19991747987342, + 45.14327348194104 + ], + [ + 29.226316784628125, + 43.88667365575236 + ], + [ + 33.414995654718496, + 42.630073829563685 + ], + [ + 36.76591574176064, + 42.21119443803957 + ], + [ + 39.697994785662104, + 41.373474003374895 + ], + [ + 42.21119443803968, + 40.954594611850894 + ], + [ + 44.30551469889315, + 40.53571522032678 + ], + [ + 45.14327348194115, + 40.53571522032678 + ], + [ + 46.8187143512705, + 39.697994785662104 + ], + [ + 48.49419356898329, + 39.2791153941381 + ], + [ + 49.33191400364808, + 39.2791153941381 + ], + [ + 50.16967278669608, + 39.2791153941381 + ], + [ + 50.58851382983676, + 39.2791153941381 + ], + [ + 50.58851382983676, + 38.441394959473314 + ], + [ + 50.58851382983676, + 38.022515567949426 + ], + [ + 50.58851382983676, + 37.60367452480875 + ] + ], + "color": "--affine-palette-line-orange", + "lineWidth": 4, + "id": "5PoPey_Lmj", + "index": "b5D", + "seed": 1785430482 + }, + "HWW9D4QEIx": { + "type": "brush", + "xywh": "[1294.6728677900371,640.4943333129787,9.026399304754932,83.16578904989046]", + "points": [ + [ + 2, + 2 + ], + [ + 2.4188410431406737, + 8.282999130943722 + ], + [ + 4.094320260853465, + 21.686756175879168 + ], + [ + 5.350920087042141, + 32.158395828529706 + ], + [ + 7.026399304754932, + 49.75079339517208 + ], + [ + 7.026399304754932, + 55.196072091451015 + ], + [ + 6.607519913230817, + 69.85639061419181 + ], + [ + 6.18864052170693, + 73.20734904961739 + ], + [ + 5.769799478566256, + 77.81486896284821 + ], + [ + 4.932040695518253, + 79.90918922370167 + ], + [ + 4.932040695518253, + 80.32806861522579 + ], + [ + 4.932040695518253, + 81.16578904989046 + ], + [ + 4.932040695518253, + 80.74694800674979 + ] + ], + "color": "--affine-palette-line-orange", + "lineWidth": 4, + "id": "HWW9D4QEIx", + "index": "b5E", + "seed": 838292120 + }, + "1bux65v5hI": { + "type": "brush", + "xywh": "[1242.792223309561,623.230802236176,127.14678296649618,120.44492361822017]", + "points": [ + [ + 100.85254522576952, + 69.01865100533541 + ], + [ + 100.01478644272152, + 70.69413022304809 + ], + [ + 98.75818661653284, + 76.13940891932702 + ], + [ + 96.66386635567937, + 82.00352865874675 + ], + [ + 94.98838713796658, + 85.35444874578889 + ], + [ + 93.31294626863723, + 88.28652778969035 + ], + [ + 89.54314679007098, + 93.7318064859694 + ], + [ + 84.09786809379193, + 99.59592622538912 + ], + [ + 79.90918922370179, + 102.94684631243126 + ], + [ + 69.85639061419192, + 110.06760422642299 + ], + [ + 57.29039235230448, + 115.51284457431859 + ], + [ + 44.30555304727636, + 118.44492361822017 + ], + [ + 32.15839582852959, + 118.44492361822017 + ], + [ + 22.943356002067958, + 115.9317239658426 + ], + [ + 15.403757044935446, + 111.32420405261178 + ], + [ + 9.539598957132512, + 104.20344613861994 + ], + [ + 6.607558261614258, + 99.59592622538912 + ], + [ + 2.83775878304823, + 88.28652778969035 + ], + [ + 2, + 75.30165013627902 + ], + [ + 3.675479217712791, + 62.31681083125101 + ], + [ + 8.701878522467723, + 50.16965361250436 + ], + [ + 15.403757044935446, + 38.860255176805595 + ], + [ + 25.037676262921423, + 27.969736132631056 + ], + [ + 53.10175183059755, + 9.120738739800004 + ], + [ + 60.64135078773006, + 6.188659695898423 + ], + [ + 82.42238887607914, + 2 + ], + [ + 88.70538800702298, + 2 + ], + [ + 100.85254522576952, + 4.513199652377352 + ], + [ + 110.4864644437555, + 9.120738739800004 + ], + [ + 118.02606340088778, + 16.2414583054084 + ], + [ + 124.30906253183161, + 28.388596349963336 + ], + [ + 125.14678296649618, + 35.509335089763454 + ], + [ + 116.35058418317522, + 59.38473178734944 + ], + [ + 109.22986461756682, + 68.18093057067063 + ], + [ + 94.1506667033018, + 84.93560770264821 + ], + [ + 87.0299471376934, + 92.05632726825661 + ] + ], + "color": "--affine-palette-line-orange", + "lineWidth": 4, + "id": "1bux65v5hI", + "index": "b5F", + "seed": 752261619 + }, + "goGplwPL_A": { + "type": "brush", + "xywh": "[793.767515790554,650.1460398223759,64.73567104858341,79.81486896284821]", + "points": [ + [ + 20.011276958166377, + 2 + ], + [ + 18.754677131977587, + 6.1886596958985365 + ], + [ + 18.335797740453586, + 9.120719565608397 + ], + [ + 16.24147747960012, + 18.335797740453586 + ], + [ + 16.24147747960012, + 21.267876784355053 + ], + [ + 14.984877653411331, + 28.80747574148745 + ], + [ + 14.56599826188733, + 30.482916610816915 + ], + [ + 14.14713804455505, + 32.57727522005371 + ], + [ + 13.728277827222655, + 33.41499565471838 + ], + [ + 13.728277827222655, + 33.83387504624238 + ], + [ + 14.14713804455505, + 33.83387504624238 + ], + [ + 14.984877653411331, + 33.41499565471838 + ], + [ + 17.49807730578891, + 32.99611626319438 + ], + [ + 21.686737001687447, + 32.57727522005371 + ], + [ + 27.550875915298775, + 32.15839582852959 + ], + [ + 33.83387504624238, + 32.57727522005371 + ], + [ + 39.697994785662104, + 34.25271608938306 + ], + [ + 45.14327348194104, + 36.34707469861996 + ], + [ + 48.0753333516509, + 38.44139495947343 + ], + [ + 53.10173265640583, + 42.21119443803956 + ], + [ + 57.290392352304366, + 46.39987330812983 + ], + [ + 60.64135078772995, + 51.42627261288476 + ], + [ + 62.31679165705941, + 55.61491313459169 + ], + [ + 62.73567104858341, + 58.96587157001716 + ], + [ + 62.73567104858341, + 61.47907122239462 + ], + [ + 61.06019183087062, + 63.9922708747722 + ], + [ + 59.80359200468183, + 65.66771174410155 + ], + [ + 56.45267191763969, + 68.59979078800302 + ], + [ + 51.00739322136076, + 72.36959026656928 + ], + [ + 42.63007382956357, + 75.72051035361142 + ], + [ + 34.25273526357478, + 77.81486896284821 + ], + [ + 26.71313630644238, + 77.81486896284821 + ], + [ + 20.011276958166377, + 75.72051035361142 + ], + [ + 14.56599826188733, + 73.20731070123395 + ], + [ + 10.377338565988794, + 70.27527000571581 + ], + [ + 9.120738739800004, + 68.59979078800302 + ], + [ + 6.188678870090257, + 65.66771174410155 + ], + [ + 4.094339435045072, + 62.316791657059404 + ], + [ + 2.8377396088563955, + 59.38471261315783 + ], + [ + 2, + 57.70927174382837 + ], + [ + 2, + 56.45267191763969 + ] + ], + "color": "--affine-palette-line-orange", + "lineWidth": 4, + "id": "goGplwPL_A", + "index": "b5G", + "seed": 238056563 + }, + "SrfBqzpFMh": { + "type": "brush", + "xywh": "[820.9938517493738,649.3083002135194,45.048934046895965,6.094339435045072]", + "points": [ + [ + 2, + 4.094339435045072 + ], + [ + 4.094339435045072, + 4.094339435045072 + ], + [ + 14.56599826188733, + 3.25659982618879 + ], + [ + 18.335797740453586, + 3.25659982618879 + ], + [ + 22.105597219019728, + 2.8377396088563955 + ], + [ + 25.875396697585984, + 2.4188602173322806 + ], + [ + 32.9961354373861, + 2 + ], + [ + 38.86025517680571, + 2 + ], + [ + 41.79233422070729, + 2.4188602173322806 + ], + [ + 42.630054655371964, + 2.4188602173322806 + ], + [ + 43.048934046895965, + 2.4188602173322806 + ] + ], + "color": "--affine-palette-line-orange", + "lineWidth": 4, + "id": "SrfBqzpFMh", + "index": "b5I", + "seed": 1158828239 + }, + "I-pL2OzPot": { + "type": "brush", + "xywh": "[787.256986904014,615.7518235378745,121.24769174190533,124.88328601707224]", + "points": [ + [ + 70.62170412391436, + 117.88436729209025 + ], + [ + 67.89501361828866, + 117.88436729209025 + ], + [ + 64.71388162939127, + 117.88436729209025 + ], + [ + 56.98825159578587, + 117.88436729209025 + ], + [ + 47.899297112365275, + 117.42990500582073 + ], + [ + 39.26476330921878, + 115.61209746673853 + ], + [ + 31.084691792341573, + 112.88540696111295 + ], + [ + 23.81352404500558, + 108.34095052240014 + ], + [ + 17.451239264213086, + 103.34199019142284 + ], + [ + 11.543416769689884, + 95.16191867454586 + ], + [ + 6.544477241710297, + 85.61850190485575 + ], + [ + 5.181131988897391, + 80.61958317987376 + ], + [ + 2.908903769541098, + 69.71282115737085 + ], + [ + 2, + 58.351596848598774 + ], + [ + 2.908903769541098, + 46.53595185955237 + ], + [ + 6.090035758438489, + 36.083652123318984 + ], + [ + 11.543416769689884, + 26.085773067359696 + ], + [ + 19.72348828656709, + 17.4512392642132 + ], + [ + 32.90249933142388, + 9.271167747335994 + ], + [ + 40.62810856203157, + 6.090035758438489 + ], + [ + 56.07936862924237, + 2.908903769541098 + ], + [ + 71.07616641018376, + 2 + ], + [ + 84.25515665204284, + 3.3633452528129055 + ], + [ + 95.16191867454575, + 6.544477241710297 + ], + [ + 103.34199019142284, + 11.08897528641819 + ], + [ + 113.79431073065393, + 23.81354484800329 + ], + [ + 119.24769174190533, + 42.44591610111388 + ], + [ + 118.79325025863363, + 54.716023376429575 + ], + [ + 117.42990500582073, + 60.16940438768097 + ], + [ + 112.88540696111284, + 70.16728344364037 + ], + [ + 105.15979773050503, + 80.16512089360435 + ], + [ + 94.70747719127394, + 92.43522816892005 + ], + [ + 82.89181139923005, + 103.79645247769236 + ], + [ + 57.44271388205516, + 120.61105779771583 + ], + [ + 51.534870584534474, + 122.88328601707224 + ], + [ + 36.083631320321274, + 122.88328601707224 + ], + [ + 31.993595561882785, + 121.06547847798993 + ], + [ + 26.085773067359582, + 116.52102203927734 + ], + [ + 21.08683353937988, + 110.61317874175654 + ] + ], + "color": "--affine-palette-line-orange", + "lineWidth": 4, + "id": "I-pL2OzPot", + "index": "b5K", + "seed": 810018507 + }, + "J3cb6llzpc": { + "type": "brush", + "xywh": "[515.4988013714616,929.7910731686342,293.2151468941754,522.5049086204381]", + "points": [ + [ + 283.6957560688417, + 12.188028454264412 + ], + [ + 279.176365243508, + 9.312803910925723 + ], + [ + 272.3972790055075, + 7.15640195546292 + ], + [ + 267.1247023495722, + 5.718822587876108 + ], + [ + 265.61819276750674, + 5 + ], + [ + 264.11175214587246, + 5 + ], + [ + 259.59236132053877, + 5 + ], + [ + 253.56652987357097, + 5 + ], + [ + 246.78744363557047, + 6.437612271669515 + ], + [ + 238.50184781550428, + 7.15640195546292 + ], + [ + 234.73571178120343, + 8.594014227132206 + ], + [ + 227.20337075217012, + 10.750416182595126 + ], + [ + 218.91784389253527, + 12.906818138057933 + ], + [ + 210.63231703290054, + 16.500832365190142 + ], + [ + 202.3467212128345, + 20.81363627611575 + ], + [ + 192.55475373156554, + 25.12644018704136 + ], + [ + 182.00946249883262, + 30.876823465553787 + ], + [ + 176.7368858428976, + 33.75204800889259 + ], + [ + 163.9318991974977, + 40.94004355907441 + ], + [ + 150.37372672149652, + 48.84686169713223 + ], + [ + 136.81555424549538, + 57.47246951898346 + ], + [ + 124.76389135155966, + 65.37925475295879 + ], + [ + 108.19276867185887, + 77.59890970602471 + ], + [ + 90.86842568134091, + 91.97490080638836 + ], + [ + 84.84255975415739, + 98.44410667277688 + ], + [ + 73.54408269082306, + 109.94493903796692 + ], + [ + 62.99886041852147, + 121.44570559499175 + ], + [ + 54.71329907867098, + 133.6653605480579 + ], + [ + 45.67451742800353, + 145.8849496929586 + ], + [ + 38.14221087918591, + 159.54218401361157 + ], + [ + 30.60986985015256, + 173.9181751139752 + ], + [ + 23.077563301334905, + 189.73181139009085 + ], + [ + 20.064647577850977, + 197.6385966240662 + ], + [ + 14.038781650667453, + 214.17098967989276 + ], + [ + 10.27261113615079, + 230.7034485438843 + ], + [ + 7.259695412666865, + 247.23584159971085 + ], + [ + 5, + 264.4870572434133 + ], + [ + 5, + 281.0194502992396 + ], + [ + 5.753220310817065, + 298.98948853081816 + ], + [ + 8.01291572348393, + 316.95952676239676 + ], + [ + 11.779086238000591, + 335.64832177368606 + ], + [ + 14.038781650667453, + 344.99275218341336 + ], + [ + 17.80495216518412, + 362.2439678271158 + ], + [ + 24.5840384031847, + 380.213940250529 + ], + [ + 32.86956526281942, + 396.7463991145207 + ], + [ + 52.45360366600424, + 425.49838131524797 + ], + [ + 64.50530104015563, + 438.43679304802504 + ], + [ + 78.0634735161568, + 450.65644800109095 + ], + [ + 84.84255975415739, + 456.4068312796034 + ], + [ + 99.90724181222403, + 467.90766364479344 + ], + [ + 116.47829553149347, + 477.97085083423144 + ], + [ + 132.29616342016163, + 485.8776360682066 + ], + [ + 148.11403130882965, + 493.06566452247097 + ], + [ + 164.68515398853046, + 499.5348703888597 + ], + [ + 180.50302187719848, + 504.56649688766123 + ], + [ + 196.32088976586664, + 508.8793007985869 + ], + [ + 204.60641662550134, + 510.31688016617363 + ], + [ + 219.671098683568, + 513.1921047095125 + ], + [ + 234.73571178120343, + 514.6296840770992 + ], + [ + 247.54062946617177, + 516.786086032562 + ], + [ + 260.3456161115716, + 517.5049086204381 + ], + [ + 270.89083838387313, + 517.5049086204381 + ], + [ + 277.66992462187386, + 517.5049086204381 + ], + [ + 282.9425702382402, + 517.5049086204381 + ], + [ + 287.461961063574, + 516.0672634446862 + ], + [ + 288.2151468941754, + 514.6296840770992 + ], + [ + 288.2151468941754, + 513.9108614892232 + ], + [ + 288.2151468941754, + 513.1921047095125 + ], + [ + 287.461961063574, + 512.4732821216363 + ], + [ + 285.9554514815088, + 511.0357027540495 + ], + [ + 284.44901085987453, + 510.31688016617363 + ], + [ + 283.6957560688417, + 508.8793007985869 + ], + [ + 277.66992462187386, + 503.8476742997851 + ], + [ + 267.1247023495722, + 495.2220664779339 + ], + [ + 255.07297049520514, + 484.4400567006198 + ], + [ + 243.02123864083802, + 472.93922433542997 + ], + [ + 232.47601636853645, + 463.59485973386785 + ], + [ + 223.437234717869, + 456.4068312796034 + ], + [ + 218.91784389253527, + 451.37520478080165 + ], + [ + 217.41140327090113, + 449.93762541321485 + ], + [ + 215.1517078582343, + 448.50004604562804 + ], + [ + 215.1517078582343, + 447.7812234577522 + ] + ], + "color": "--affine-palette-line-orange", + "lineWidth": 10, + "id": "J3cb6llzpc", + "index": "b5J", + "seed": 2037177940 + }, + "MwVEs53XNx": { + "type": "brush", + "xywh": "[715.7479027121274,1449.5767181390754,92.39896116886877,56.68309298865049]", + "points": [ + [ + 87.39896116886877, + 5 + ], + [ + 81.35128654479502, + 7.872824942557235 + ], + [ + 68.50009908546559, + 12.900252153721512 + ], + [ + 57.16079567734659, + 17.927613611642915 + ], + [ + 46.57746889964088, + 23.67326349675739 + ], + [ + 35.99407291231963, + 28.700624954678798 + ], + [ + 25.41074613461388, + 34.44627483979327 + ], + [ + 7.267860681623764, + 48.810268046093036 + ], + [ + 5, + 51.68309298865049 + ] + ], + "color": "--affine-palette-line-orange", + "lineWidth": 10, + "id": "MwVEs53XNx", + "index": "b5L", + "seed": 1423229383 + }, + "KfJharb6ec": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "KfJharb6ec", + "index": "b5t", + "seed": 2021747914, + "points": [ + [ + 3.373134931502591, + 105.44284547478344 + ], + [ + 2.9154512241698285, + 102.23884999969312 + ], + [ + 2.4576837073323077, + 99.95030574877296 + ], + [ + 2, + 90.33836122825437 + ], + [ + 2.4576837073323077, + 87.59209136524908 + ], + [ + 3.373134931502591, + 82.55723534657102 + ], + [ + 3.373134931502591, + 79.81096548356572 + ], + [ + 5.661721087175465, + 70.19902096304725 + ], + [ + 6.57708850184008, + 66.99506739270942 + ], + [ + 9.323358364845717, + 60.12939273519618 + ], + [ + 12.069628227850899, + 53.26371807768294 + ], + [ + 14.81589809085608, + 46.398043420169756 + ], + [ + 17.562167953861717, + 39.990052469988996 + ], + [ + 24.427842611374672, + 27.631838086465223 + ], + [ + 28.089563698550137, + 22.597023972539716 + ], + [ + 29.462698630052728, + 20.766163428951984 + ], + [ + 32.20896849305791, + 17.104484246529125 + ], + [ + 36.786056994898445, + 11.611944520518534 + ], + [ + 38.159191926401036, + 9.781083976930802 + ], + [ + 40.90546178940622, + 7.034814113925506 + ], + [ + 42.736364237746784, + 5.6616791824228585 + ], + [ + 43.651731652411854, + 4.7462698630052955 + ], + [ + 44.56718287658168, + 3.8308605435877325 + ], + [ + 45.024866583914445, + 2.915409319417563 + ], + [ + 45.94031780808473, + 2.4577256120850848 + ], + [ + 46.85576903225456, + 2 + ], + [ + 47.31345273958732, + 2 + ], + [ + 48.68658767108991, + 2 + ], + [ + 51.890541241427854, + 3.3731349315026478 + ], + [ + 53.26367617292999, + 4.288544250920211 + ], + [ + 54.636811104433036, + 4.7462698630052955 + ], + [ + 57.38308096743822, + 6.119404794507943 + ], + [ + 60.58711834728092, + 7.950265338095676 + ], + [ + 64.70652314178915, + 10.238809589015887 + ], + [ + 70.19906286779951, + 13.900488771438745 + ], + [ + 75.69160259381033, + 18.01989356594669 + ], + [ + 81.1841423198207, + 22.13929836045463 + ], + [ + 86.67668204583151, + 26.716428767047603 + ], + [ + 90.79608684033929, + 31.29355917364063 + ], + [ + 94.91549163484706, + 34.95523835606349 + ], + [ + 98.119445205185, + 38.61691753848635 + ], + [ + 99.95026384402036, + 39.990052469988996 + ], + [ + 103.61198493119537, + 43.65177355716446 + ], + [ + 106.81593850153331, + 47.77117835167235 + ], + [ + 110.01997588137647, + 51.43285753409526 + ], + [ + 113.68161315904672, + 55.552262328603206 + ], + [ + 115.05474809054931, + 57.38312287219088 + ], + [ + 119.17415288505708, + 63.33334630553401 + ], + [ + 120.54728781656013, + 64.70648123703666 + ], + [ + 123.29355767956531, + 70.19902096304725 + ], + [ + 124.2090089037356, + 72.02988150663492 + ], + [ + 125.58214383523773, + 74.77615136964022 + ], + [ + 126.95527876674078, + 76.60701191322795 + ], + [ + 128.32841369824337, + 78.43783055206308 + ], + [ + 128.78609740557567, + 79.81096548356572 + ], + [ + 129.2438649224132, + 81.64182602715346 + ], + [ + 130.15923233707827, + 83.0149609586561 + ], + [ + 130.61699985391624, + 84.38809589015875 + ], + [ + 130.61699985391624, + 85.30350520957631 + ], + [ + 130.61699985391624, + 86.21895643374643 + ], + [ + 130.61699985391624, + 86.67664014107896 + ], + [ + 130.61699985391624, + 87.59209136524908 + ], + [ + 130.61699985391624, + 88.04977507258161 + ], + [ + 130.61699985391624, + 88.5075006846667 + ], + [ + 130.61699985391624, + 88.96522629675172 + ], + [ + 130.61699985391624, + 89.42291000408426 + ], + [ + 130.61699985391624, + 89.88063561616934 + ], + [ + 130.61699985391624, + 90.33836122825437 + ], + [ + 130.61699985391624, + 91.25377054767199 + ] + ], + "lineWidth": 4, + "xywh": "[2158.279924438907,-177.0711991923539,132.61699985391624,107.44284547478344]" + }, + "Jdt-Re4f6a": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "Jdt-Re4f6a", + "index": "b5u", + "seed": 1018565993, + "points": [ + [ + 2, + 13.417474526446057 + ], + [ + 2.475742623338192, + 11.990290210640296 + ], + [ + 3.6650774045792787, + 8.660189844242794 + ], + [ + 4.378669562482173, + 7.470876840106143 + ], + [ + 5.092261720385068, + 6.757284682203263 + ], + [ + 6.757295570755559, + 4.616508208494622 + ], + [ + 7.946630351996646, + 3.9029160505917417 + ], + [ + 9.849513736931385, + 2.7135921579028803 + ], + [ + 11.752440676075366, + 2 + ], + [ + 13.655367615219347, + 2 + ], + [ + 14.606809307686945, + 2 + ], + [ + 16.271843158057436, + 2.4757317347859953 + ], + [ + 17.699027473863225, + 3.1893238926888614 + ], + [ + 18.88836225510431, + 3.9029160505917417 + ], + [ + 19.83980394757191, + 5.092229054728406 + ], + [ + 21.029138728812995, + 6.043692524300383 + ], + [ + 21.74273088671589, + 6.281552947417268 + ] + ], + "lineWidth": 4, + "xywh": "[2185.5188636787684,-122.3248987248711,23.74273088671589,15.417474526446057]" + }, + "QjCRV81glI": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "QjCRV81glI", + "index": "b5v", + "seed": 1513930659, + "points": [ + [ + 2, + 11.038826741068306 + ], + [ + 2.951441692467597, + 7.9465976863399135 + ], + [ + 3.6650338503704916, + 5.8058212126312725 + ], + [ + 4.616519097046876, + 4.140776473708627 + ], + [ + 6.281552947417367, + 2.9514525810197654 + ], + [ + 8.422329421125596, + 2 + ], + [ + 10.56310589483428, + 2 + ], + [ + 13.65532406101056, + 2.9514525810197654 + ], + [ + 16.271843158057436, + 4.616497319942411 + ], + [ + 17.461177939298523, + 5.567960789514387 + ], + [ + 19.601954413007206, + 7.708737263223028 + ], + [ + 21.266988263377698, + 9.61164242526256 + ], + [ + 22.456323044618784, + 11.276698052737416 + ], + [ + 23.16991520252168, + 11.752418898971186 + ] + ], + "lineWidth": 4, + "xywh": "[2225.955767144668,-127.55790425330814,25.16991520252168,13.752418898971186]" + }, + "3BjSH224j3": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "3BjSH224j3", + "index": "b5w", + "seed": 1523623020, + "points": [ + [ + 2, + 16.50970358117445 + ], + [ + 3.1892912270322995, + 17.22329573907733 + ], + [ + 6.995145105320262, + 18.888351366552186 + ], + [ + 8.42232942112605, + 19.12621178966907 + ], + [ + 11.514547587302332, + 18.888351366552186 + ], + [ + 14.606765753478157, + 18.174759208649306 + ], + [ + 16.50969269262214, + 17.69902747386331 + ], + [ + 19.83980394757191, + 16.271843158057564 + ], + [ + 23.40776473708638, + 14.844658842251803 + ], + [ + 26.737832437827365, + 13.179614103329158 + ], + [ + 30.067943692777135, + 11.514558475854301 + ], + [ + 31.49512800858247, + 10.563105894834536 + ], + [ + 33.63590448229115, + 8.89806115591189 + ], + [ + 34.82523926353224, + 7.233005528437033 + ], + [ + 35.776680955999836, + 5.567960789514387 + ], + [ + 36.25242357933803, + 4.140776473708627 + ], + [ + 36.72812264846743, + 3.1893238926888614 + ], + [ + 36.96601573724092, + 2 + ] + ], + "lineWidth": 4, + "xywh": "[2202.882968557211,-100.20354182988187,38.96601573724092,21.12621178966907]" + }, + "N0R7kGAEg6": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "N0R7kGAEg6", + "index": "b5x", + "seed": 1809486954, + "points": [ + [ + 2, + 14.368927107465808 + ], + [ + 4.378626008273386, + 14.131066684348937 + ], + [ + 5.567960789514473, + 14.131066684348937 + ], + [ + 10.325256360270032, + 14.844658842251803 + ], + [ + 11.514547587302332, + 15.082519265368703 + ], + [ + 13.17962499188161, + 15.082519265368703 + ], + [ + 16.27184315805789, + 15.558251000154698 + ], + [ + 20.07765348213661, + 16.271843158057564 + ], + [ + 24.83494905289217, + 16.98543531596043 + ], + [ + 27.451468149939046, + 17.22329573907733 + ], + [ + 30.305836781550624, + 17.46115616219423 + ], + [ + 36.014574044773326, + 17.699027473863325 + ], + [ + 42.19901037712589, + 17.699027473863325 + ], + [ + 48.62133979825194, + 17.699027473863325 + ], + [ + 55.51941184271618, + 17.22329573907733 + ], + [ + 62.65533342174467, + 16.747564004291334 + ], + [ + 70.02910453533832, + 16.033971846388468 + ], + [ + 73.35921579028809, + 15.558251000154698 + ], + [ + 80.49513736931658, + 15.082519265368703 + ], + [ + 86.91746679044263, + 13.893195372679813 + ], + [ + 93.10194667700398, + 12.941742791660047 + ], + [ + 99.04853347479138, + 11.752418898971186 + ], + [ + 104.51942120344984, + 10.80096631795142 + ], + [ + 109.27667321999661, + 9.61164242526256 + ], + [ + 114.03396879075217, + 8.660189844242794 + ], + [ + 115.9368957298957, + 8.184458109456799 + ], + [ + 123.54851637805405, + 5.567960789514387 + ], + [ + 127.59221979090671, + 4.140776473708627 + ], + [ + 128.7815545721478, + 3.6650447389226457 + ], + [ + 131.3980301149859, + 2 + ] + ], + "lineWidth": 4, + "xywh": "[2158.878104301797,-82.6015983054268,133.3980301149859,19.699027473863325]" + }, + "TR9ZVf5tjb": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "TR9ZVf5tjb", + "index": "b5y", + "seed": 107669226, + "points": [ + [ + 2, + 2 + ], + [ + 2, + 4.786884310580888 + ], + [ + 2, + 8.569055807558726 + ], + [ + 2, + 10.161566334947139 + ], + [ + 2, + 11.75405863763794 + ], + [ + 2, + 12.749364048732502 + ], + [ + 2.1990501474006123, + 15.735298506713747 + ], + [ + 2.3981367441961083, + 17.924995925698397 + ], + [ + 2.3981367441961083, + 18.522182817294663 + ], + [ + 2.3981367441961083, + 19.1193697088909 + ] + ], + "lineWidth": 4, + "xywh": "[2201.2302431050553,-67.09250707330088,5,21.1193697088909]" + }, + "_u_KQilo1c": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "_u_KQilo1c", + "index": "b5z", + "seed": 652216400, + "points": [ + [ + 2.597186891596266, + 2 + ], + [ + 2.3981367441961083, + 2.9953054110945345 + ], + [ + 2.199086596795496, + 3.791560674788741 + ], + [ + 2, + 5.185002830079213 + ], + [ + 2, + 6.578426760672045 + ], + [ + 2.199086596795496, + 7.374682024366251 + ], + [ + 2.3981367441961083, + 8.170937288060458 + ], + [ + 2.597186891596266, + 10.758735001845793 + ], + [ + 2.9953236357919195, + 12.948432420830443 + ], + [ + 3.393460379988028, + 15.13811161511751 + ] + ], + "lineWidth": 4, + "xywh": "[2209.789909734803,-63.31031735162543,5.393460379988028,17.13811161511751]" + }, + "_91RffGkrY": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "_91RffGkrY", + "index": "b60", + "seed": 927614511, + "points": [ + [ + 12.15217715713652, + 4.587815938482947 + ], + [ + 9.962479738151615, + 4.985934457981244 + ], + [ + 7.971868915962659, + 5.185002830079185 + ], + [ + 6.379358388574019, + 5.583121349577482 + ], + [ + 5.782171496977753, + 5.782189721675451 + ], + [ + 4.388747566385064, + 6.180308241173748 + ], + [ + 3.5924740779933018, + 6.777495132769985 + ], + [ + 2.7962370389968783, + 7.374682024366251 + ], + [ + 2.1990501474006123, + 7.971868915962489 + ], + [ + 2, + 8.370005660158398 + ], + [ + 2, + 8.569055807558726 + ], + [ + 2, + 8.967192551754664 + ], + [ + 2.39810029480077, + 9.365311071252933 + ], + [ + 3.5924740779933018, + 9.962497962849199 + ], + [ + 5.9812216443783655, + 10.758753226543405 + ], + [ + 8.768105954959083, + 11.355940118139642 + ], + [ + 14.142787979325476, + 11.355940118139642 + ], + [ + 18.124046073098725, + 10.559684854445436 + ], + [ + 19.318419856291257, + 10.161566334947139 + ], + [ + 21.508080825880825, + 9.564379443350902 + ], + [ + 23.49872809746512, + 8.768124179656695 + ], + [ + 25.09120217545842, + 7.772818768562161 + ], + [ + 25.688389067054686, + 7.175631876965895 + ], + [ + 26.882762850246763, + 5.782189721675451 + ], + [ + 27.479949741843484, + 4.587815938482947 + ], + [ + 27.67903633863898, + 3.592510527388413 + ], + [ + 27.479949741843484, + 2.995323635792147 + ], + [ + 26.882762850246763, + 2.5971868915962375 + ], + [ + 25.688389067054686, + 2.3981367441959094 + ], + [ + 24.095914989061384, + 2 + ], + [ + 22.503404461672744, + 2 + ], + [ + 21.309030678480212, + 2 + ] + ], + "lineWidth": 4, + "xywh": "[2193.2677633669036,-50.968461000202126,29.67903633863898,13.355940118139642]" + }, + "QcrVYrs_UL": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "QcrVYrs_UL", + "index": "b61", + "seed": 95764264, + "points": [ + [ + 2.9953236357923743, + 2 + ], + [ + 2, + 4.189679194287066 + ], + [ + 2, + 7.175613652268311 + ], + [ + 2.1990501474001576, + 8.967174327057052 + ], + [ + 2.7962370389964235, + 10.559666629747852 + ], + [ + 2.9953236357923743, + 11.754040412940356 + ], + [ + 3.5925105273881854, + 12.948414196132859 + ], + [ + 3.9906108221889554, + 13.744669459827065 + ] + ], + "lineWidth": 4, + "xywh": "[2237.6586434924266,-64.30562276271999,5.990610822188955,15.744669459827065]" + }, + "gMGCAteIGn": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "gMGCAteIGn", + "index": "b62", + "seed": 2075547794, + "points": [ + [ + 2, + 2 + ], + [ + 2, + 3.393442155290444 + ], + [ + 2, + 4.786884310580916 + ], + [ + 2, + 12.550313901332174 + ], + [ + 2, + 15.934366878811716 + ], + [ + 2, + 16.531553770407953 + ], + [ + 2.597186891596266, + 19.318419856291257 + ], + [ + 2.597186891596266, + 20.114675119985463 + ] + ], + "lineWidth": 4, + "xywh": "[2246.41739671897,-70.67562842287839,5,22.114675119985463]" + }, + "fYAjJEjpXJ": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "fYAjJEjpXJ", + "index": "b63", + "seed": 710908840, + "points": [ + [ + 9.166242699155191, + 7.175613652268282 + ], + [ + 8.170919063362817, + 6.578426760672045 + ], + [ + 6.57844498536997, + 6.180308241173748 + ], + [ + 5.384071202177438, + 6.180308241173748 + ], + [ + 4.189697418984906, + 6.578426760672045 + ], + [ + 3.7915606747887978, + 6.777495132770014 + ], + [ + 3.393423930593144, + 7.175613652268282 + ], + [ + 2.9953236357923743, + 7.772800543864548 + ], + [ + 2.1990501474006123, + 8.967174327057052 + ], + [ + 2, + 9.763429590751258 + ], + [ + 2, + 10.559666629747852 + ], + [ + 2.597186891596266, + 11.355921893442058 + ], + [ + 3.59251052738864, + 12.152177157136265 + ], + [ + 5.981258093773704, + 12.749364048732502 + ], + [ + 7.374682024366393, + 13.1474825682308 + ], + [ + 10.360616482347723, + 13.34655094032874 + ], + [ + 13.346550940329053, + 13.1474825682308 + ], + [ + 16.13343525090977, + 12.749364048732502 + ], + [ + 18.522182817294834, + 11.554990265539999 + ], + [ + 20.711843786883946, + 9.763429590751258 + ], + [ + 22.702491058468695, + 7.971868915962489 + ], + [ + 24.095914989061384, + 6.578426760672045 + ], + [ + 24.69310188065765, + 5.981239869075807 + ], + [ + 25.489338919654074, + 4.985934457981244 + ], + [ + 25.887475663850182, + 3.9906108221890975 + ], + [ + 25.68842551644957, + 2.9953054110945345 + ], + [ + 24.294965136461542, + 2.199050147400328 + ], + [ + 22.10530416687243, + 2 + ], + [ + 19.51747000369187, + 2.199050147400328 + ], + [ + 16.13343525090977, + 2.796237038996594 + ] + ], + "lineWidth": 4, + "xywh": "[2232.8811483596564,-54.15344560558373,27.887475663850182,15.34655094032874]" + }, + "L6-cOeoDup": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "L6-cOeoDup", + "index": "b64", + "seed": 1932186181, + "points": [ + [ + 28.07715485813742, + 24.29497424881039 + ], + [ + 25.290288772253916, + 22.7024728337708 + ], + [ + 22.901541205868853, + 21.309039790829146 + ], + [ + 20.7118437868844, + 19.71653837578954 + ], + [ + 18.323096220499338, + 17.924977701000785 + ], + [ + 15.934348654114274, + 15.735298506713747 + ], + [ + 14.739974870921742, + 14.540924723521243 + ], + [ + 12.351227304536678, + 12.3512364168854 + ], + [ + 9.763429590751457, + 9.76342959075123 + ], + [ + 7.573732171767006, + 7.5737412841153855 + ], + [ + 5.782171496978208, + 5.782180609326645 + ], + [ + 4.388747566385064, + 4.587806826134141 + ], + [ + 3.194373783192532, + 3.3934330429416377 + ], + [ + 2.398118519498439, + 2.597186891596266 + ], + [ + 2, + 2 + ] + ], + "lineWidth": 4, + "xywh": "[2142.7059277286226,-146.91648217510044,30.07715485813742,26.29497424881039]" + }, + "OrjsNdVQtR": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "OrjsNdVQtR", + "index": "b65", + "seed": 1983895390, + "points": [ + [ + 25.09122040015609, + 31.8593445798125 + ], + [ + 23.896846616963558, + 30.864030056369145 + ], + [ + 23.10060957796668, + 30.067783905023745 + ], + [ + 20.51279363948379, + 27.479977078889604 + ], + [ + 17.9249777010009, + 23.896855729312108 + ], + [ + 16.53155377040821, + 21.906226682425398 + ], + [ + 14.939043243019569, + 20.114666007636643 + ], + [ + 13.346550940329053, + 18.323105332847888 + ], + [ + 11.95310878503824, + 16.92967228990625 + ], + [ + 10.55968485444555, + 15.536239246964612 + ], + [ + 9.763429590751457, + 14.739983983270406 + ], + [ + 8.369987435461098, + 13.346550940328768 + ], + [ + 6.976563504867954, + 11.754049525289133 + ], + [ + 5.782189721675422, + 10.360616482347496 + ], + [ + 4.786866085883503, + 9.166242699154992 + ], + [ + 3.9906290468866246, + 8.369996547809592 + ], + [ + 3.3934421552908134, + 7.772809656213354 + ], + [ + 2.995305411094705, + 7.175622764617117 + ], + [ + 2.7962552636945475, + 6.976554392519148 + ], + [ + 2.398118519498439, + 6.3793675009229105 + ], + [ + 2.1990683720982815, + 5.782180609326645 + ], + [ + 2, + 5.184993717730379 + ], + [ + 2, + 4.587806826134141 + ], + [ + 2, + 3.9906199345379036 + ], + [ + 2.1990683720982815, + 3.1943737831925034 + ], + [ + 2.398118519498439, + 2.398127631847103 + ], + [ + 2.597186891596266, + 2 + ] + ], + "lineWidth": 4, + "xywh": "[2140.715298681736,-147.31460980694754,27.09122040015609,33.8593445798125]" + }, + "7dFARP6mfP": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "7dFARP6mfP", + "index": "b66", + "seed": 1602905652, + "points": [ + [ + 9.664892588335078, + 18.6072770211411 + ], + [ + 8.068049712180255, + 16.691053874057303 + ], + [ + 3.916223147083656, + 10.942384432805937 + ], + [ + 2.958111573541828, + 9.664892588335135 + ], + [ + 2.958111573541828, + 8.70678101479325 + ], + [ + 2, + 7.10993813863837 + ], + [ + 2, + 6.1518265650964565 + ], + [ + 2, + 5.832446294167568 + ], + [ + 2, + 5.193714991554572 + ], + [ + 2, + 4.235603418012687 + ], + [ + 2, + 3.9162231470837696 + ], + [ + 2, + 3.2774918444707737 + ], + [ + 2.638731302612996, + 2.958111573541885 + ], + [ + 2.958111573541828, + 2.638745922235387 + ], + [ + 3.27749184447066, + 2.638745922235387 + ], + [ + 3.596842876154824, + 2.638745922235387 + ], + [ + 4.554954449696652, + 3.2774918444707737 + ], + [ + 5.51306602323848, + 3.5968574957772717 + ], + [ + 5.832446294167312, + 4.235603418012687 + ], + [ + 6.471177596780308, + 4.554969069319185 + ], + [ + 7.109938138638427, + 5.193714991554572 + ], + [ + 8.068049712180255, + 6.7905578677094525 + ], + [ + 9.026161285722083, + 8.70678101479325 + ], + [ + 9.345512317406246, + 9.984272859264053 + ], + [ + 9.98427285926391, + 11.90049600634785 + ], + [ + 10.303623890948074, + 13.177973231196233 + ], + [ + 10.623004161876906, + 13.81671915343162 + ], + [ + 10.623004161876906, + 14.136084804738118 + ], + [ + 10.623004161876906, + 13.497338882502731 + ], + [ + 10.623004161876906, + 13.177973231196233 + ], + [ + 10.942384432805738, + 12.539227308960847 + ], + [ + 12.85860757988985, + 9.345526937028637 + ], + [ + 13.816719153431677, + 7.429303789944868 + ], + [ + 15.413562029586501, + 4.874334720625683 + ], + [ + 17.329785176670157, + 2.958111573541885 + ], + [ + 18.287896750211985, + 2.319380270928889 + ], + [ + 18.607277021140817, + 2 + ], + [ + 18.92662805282498, + 2 + ], + [ + 19.246008323753813, + 2 + ], + [ + 19.246008323753813, + 2.319380270928889 + ], + [ + 19.246008323753813, + 3.2774918444707737 + ], + [ + 19.246008323753813, + 4.554969069319185 + ], + [ + 19.246008323753813, + 6.4711922164029545 + ], + [ + 18.92662805282498, + 8.70678101479325 + ], + [ + 18.607277021140817, + 9.984272859264053 + ], + [ + 17.968516479283153, + 12.219861657654349 + ], + [ + 17.329785176670157, + 14.136084804738118 + ], + [ + 16.69105387405716, + 15.732942300515418 + ], + [ + 16.37167360312833, + 16.371673603128414 + ], + [ + 16.052293332199497, + 16.691053874057303 + ], + [ + 16.052293332199497, + 17.3297851766703 + ], + [ + 16.052293332199497, + 17.649165447599216 + ] + ], + "lineWidth": 4, + "xywh": "[2130.6418292704334,-178.8994990993742,21.246008323753813,20.6072770211411]" + }, + "WKyXlguxe9": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "WKyXlguxe9", + "index": "b67", + "seed": 430279161, + "points": [ + [ + 2.638731302612996, + 2 + ], + [ + 2.319380270928832, + 3.2774772248483828 + ], + [ + 2, + 5.193700371932181 + ], + [ + 2.319380270928832, + 6.151811945474066 + ], + [ + 3.27749184447066, + 7.109923519015979 + ], + [ + 4.235603418012488, + 8.068035092557864 + ], + [ + 5.193714991554771, + 8.70678101479325 + ], + [ + 5.193714991554771, + 9.345526937028666 + ], + [ + 5.51306602323848, + 9.664892588335164 + ], + [ + 5.832446294167312, + 10.30363851057055 + ], + [ + 6.151826565096599, + 10.942369813183547 + ], + [ + 6.151826565096599, + 11.900481386725431 + ], + [ + 6.151826565096599, + 12.858592960267345 + ], + [ + 6.151826565096599, + 13.497338882502731 + ], + [ + 6.151826565096599, + 13.81670453380923 + ], + [ + 6.151826565096599, + 14.136084804738118 + ] + ], + "lineWidth": 4, + "xywh": "[2138.3067218587685,-155.26607541213332,8.151826565096599,16.13608480473812]" + }, + "Wgx8ea7h4Q": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "Wgx8ea7h4Q", + "index": "b68", + "seed": 130303619, + "points": [ + [ + 9.664892588335533, + 2 + ], + [ + 6.790557867709595, + 2.638731302612996 + ], + [ + 5.1936857523101025, + 2.9581115735419132 + ], + [ + 3.9162231470841107, + 3.916223147083798 + ], + [ + 2.958111573541828, + 4.874334720625683 + ], + [ + 2.319351031684164, + 5.513066023238679 + ], + [ + 2, + 7.109923519015979 + ], + [ + 2.319351031684164, + 9.026146666099777 + ], + [ + 3.277462605225992, + 10.942369813183575 + ], + [ + 3.9162231470841107, + 11.90048138672546 + ], + [ + 4.554954449697107, + 13.177958611573843 + ], + [ + 5.832446294167767, + 15.09418175865764 + ], + [ + 7.4292891703225905, + 16.052293332199525 + ], + [ + 9.345512317406246, + 17.01040490574144 + ] + ], + "lineWidth": 4, + "xywh": "[2133.8355442619877,-159.4178873576074,11.664892588335533,19.01040490574144]" + }, + "CFlRl6nmiJ": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "CFlRl6nmiJ", + "index": "b69", + "seed": 323835930, + "points": [ + [ + 4.554954449696652, + 2 + ], + [ + 5.832446294167767, + 3.5968428761548807 + ], + [ + 7.1099088993937585, + 5.513066023238679 + ], + [ + 8.068020472935586, + 7.109923519015979 + ], + [ + 8.068020472935586, + 8.70678101479325 + ], + [ + 6.151797325851476, + 15.09418175865764 + ], + [ + 4.554954449696652, + 17.649150827976825 + ], + [ + 3.596842876154824, + 18.60726240151871 + ], + [ + 2, + 20.20411989729601 + ] + ], + "lineWidth": 4, + "xywh": "[2145.3328831444906,-163.25033365177498,10.068020472935586,22.20411989729601]" + }, + "dpF_i3KqRO": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "dpF_i3KqRO", + "index": "b6A", + "seed": 1145658273, + "points": [ + [ + 2, + 10.623004161877049 + ], + [ + 4.23557417876782, + 8.70678101479325 + ], + [ + 9.026132046477414, + 4.554969069319185 + ], + [ + 12.539227308960562, + 3.2774918444707737 + ], + [ + 14.13609942436051, + 2.638745922235387 + ], + [ + 17.329785176670157, + 2.319380270928889 + ], + [ + 20.84288043915376, + 2 + ], + [ + 23.717215159779244, + 2 + ], + [ + 25.952789338547063, + 2.638745922235387 + ], + [ + 26.910900912089346, + 2.638745922235387 + ], + [ + 28.188363517315338, + 3.5968574957772717 + ], + [ + 29.146475090857166, + 4.874334720625683 + ], + [ + 30.104586664398994, + 7.10993813863837 + ], + [ + 28.827124059173002, + 16.052307951821916 + ], + [ + 24.67532673332107, + 22.12034304437978 + ], + [ + 20.84288043915376, + 25.952789338547376 + ], + [ + 18.926657292070104, + 27.549646834324676 + ], + [ + 18.287896750211985, + 28.188392756560063 + ], + [ + 17.329785176670157, + 28.82712405917306 + ], + [ + 17.010434144985993, + 28.82712405917306 + ] + ], + "lineWidth": 4, + "xywh": "[2266.0549414107695,-139.6169245841565,32.104586664398994,30.82712405917306]" + }, + "JssFckYvF2": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "JssFckYvF2", + "index": "b6B", + "seed": 1985502786, + "points": [ + [ + 2, + 7.10992351901595 + ], + [ + 4.874334720625484, + 5.51308064286107 + ], + [ + 7.429318409567259, + 4.235588798390268 + ], + [ + 9.664892588335078, + 3.2774772248483828 + ], + [ + 11.261764703735025, + 2.319365651306498 + ], + [ + 12.539227308961017, + 2 + ], + [ + 13.497338882502845, + 2 + ], + [ + 14.13609942436051, + 2 + ], + [ + 14.455450456044673, + 2 + ], + [ + 15.094210997902337, + 2 + ], + [ + 15.413562029586501, + 2.319365651306498 + ], + [ + 15.732913061270665, + 3.2774772248483828 + ], + [ + 15.732913061270665, + 4.874334720625683 + ], + [ + 15.094210997902337, + 7.10992351901595 + ], + [ + 14.13609942436051, + 10.30363851057055 + ], + [ + 13.177987850818681, + 12.858592960267345 + ], + [ + 12.539227308961017, + 13.81670453380923 + ], + [ + 11.261764703735025, + 15.41356202958653 + ], + [ + 10.623004161876906, + 16.691039254434912 + ], + [ + 9.664892588335078, + 17.329785176670327 + ], + [ + 9.345541556650915, + 17.649150827976825 + ], + [ + 8.70678101479325, + 18.287896750212212 + ], + [ + 8.70678101479325, + 18.60726240151871 + ], + [ + 8.70678101479325, + 18.9266426724476 + ] + ], + "lineWidth": 4, + "xywh": "[2273.7198339991046,-132.27139764712783,17.732913061270665,20.9266426724476]" + }, + "FFlu1Fkqcg": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "FFlu1Fkqcg", + "index": "b6C", + "seed": 1613829028, + "points": [ + [ + 2, + 289.15284038424835 + ], + [ + 5.891007273199648, + 289.15284038424835 + ], + [ + 9.392871071718446, + 287.98555245140875 + ], + [ + 12.894734870237471, + 286.429156666689 + ], + [ + 16.39659866875627, + 284.48368865288984 + ], + [ + 17.952958830675243, + 283.31640072005024 + ], + [ + 19.898462467275067, + 281.7600049353305 + ], + [ + 24.17847072395284, + 278.2581411368116 + ], + [ + 28.458550226231864, + 274.75627733829276 + ], + [ + 33.12770195759049, + 270.47623345881436 + ], + [ + 35.85135005234906, + 268.1416575931351 + ], + [ + 40.90964525838831, + 262.69432578081705 + ], + [ + 48.69151731358511, + 252.18873438526043 + ], + [ + 50.63702095018493, + 248.2977627348614 + ], + [ + 55.30617268154333, + 240.12674720498404 + ], + [ + 58.418964250982754, + 232.34483952698673 + ], + [ + 60.75354011666218, + 224.17382399710937 + ], + [ + 61.92082804950155, + 220.2828523467104 + ], + [ + 63.47718821142075, + 211.3336567358735 + ], + [ + 65.03354837333995, + 202.3844255022359 + ], + [ + 66.97905200993978, + 193.04612203951888 + ], + [ + 68.53541217185852, + 183.3187285363221 + ], + [ + 69.70270010469835, + 173.59133503312532 + ], + [ + 70.48091580845835, + 163.47483367804844 + ], + [ + 70.48091580845835, + 153.35833232297156 + ], + [ + 70.48091580845835, + 133.51443746469792 + ], + [ + 70.48091580845835, + 114.05965045830436 + ], + [ + 70.09184357937897, + 109.39049872694585 + ], + [ + 69.31362787561898, + 101.21948319706846 + ], + [ + 68.14633994277915, + 92.65935981531098 + ], + [ + 66.20083630617933, + 85.26654217779344 + ], + [ + 65.81176407709995, + 81.76467837927456 + ], + [ + 64.25540391518098, + 74.37186074175708 + ], + [ + 62.69897250766053, + 66.58993525235945 + ], + [ + 61.92082804950155, + 59.97529769580143 + ], + [ + 61.142612345741554, + 53.74976799112352 + ], + [ + 60.75354011666218, + 48.30241836740515 + ], + [ + 59.97532441290173, + 44.022374487926754 + ], + [ + 59.58625218382235, + 40.90960072988764 + ], + [ + 58.808036480062356, + 38.18593482372859 + ], + [ + 58.418964250982754, + 37.01864689088899 + ], + [ + 58.418964250982754, + 33.90587313284988 + ], + [ + 58.418964250982754, + 31.57129726717062 + ], + [ + 58.418964250982754, + 29.236721401491366 + ], + [ + 58.418964250982754, + 27.291235576291854 + ], + [ + 58.418964250982754, + 25.734857602972454 + ], + [ + 58.418964250982754, + 24.567569670132855 + ], + [ + 58.808036480062356, + 23.400281737293255 + ], + [ + 59.19710870914173, + 23.0111827911133 + ], + [ + 59.58625218382235, + 21.8438948582737 + ], + [ + 60.75354011666218, + 19.898417938774344 + ], + [ + 61.142612345741554, + 18.342031059754788 + ], + [ + 61.142612345741554, + 17.17474312691519 + ], + [ + 61.142612345741554, + 16.39655414025549 + ], + [ + 61.142612345741554, + 16.007455194075533 + ], + [ + 61.142612345741554, + 15.229266207415833 + ], + [ + 61.92082804950155, + 16.39655414025549 + ], + [ + 62.30990027858115, + 17.56384207309509 + ], + [ + 62.69897250766053, + 19.509318992594388 + ], + [ + 63.08811598234138, + 20.676606925434044 + ], + [ + 64.25540391518098, + 23.789371777773 + ], + [ + 66.20083630617933, + 27.68033452247181 + ], + [ + 68.9245556465396, + 33.12767524049002 + ], + [ + 71.64820374129818, + 38.18593482372859 + ], + [ + 73.20456390321715, + 42.46597870320704 + ], + [ + 74.37185183605675, + 45.9678425017259 + ], + [ + 75.15006753981697, + 49.08061625976501 + ], + [ + 75.53913976889658, + 50.24790419260461 + ], + [ + 75.9282832435772, + 51.80428216592401 + ], + [ + 75.9282832435772, + 52.58248005828392 + ], + [ + 75.9282832435772, + 52.971570098763664 + ], + [ + 75.9282832435772, + 52.58248005828392 + ], + [ + 76.31735547265657, + 51.415192125444264 + ], + [ + 77.87371563457577, + 47.1351304345655 + ], + [ + 80.20829150025497, + 43.63326663604664 + ], + [ + 83.71015529877377, + 39.35322275656824 + ], + [ + 89.15752273389239, + 33.51677418666992 + ], + [ + 96.16125033093044, + 27.291235576291854 + ], + [ + 103.16497792796804, + 21.8438948582737 + ], + [ + 110.55777775408524, + 16.39655414025549 + ], + [ + 117.17243312204369, + 12.116492449376722 + ], + [ + 120.67429692056248, + 10.171015529877423 + ], + [ + 126.89980881383985, + 7.447340718018211 + ], + [ + 132.3471762489587, + 5.501863798518912 + ], + [ + 135.45996781839835, + 3.945476919499356 + ], + [ + 137.40540020939693, + 2.7781889866597 + ], + [ + 138.18361591315693, + 2 + ], + [ + 138.5726881422363, + 2 + ], + [ + 138.18361591315693, + 2.7781889866597 + ], + [ + 138.18361591315693, + 3.167287932839656 + ], + [ + 137.0163279803171, + 4.723674811859212 + ], + [ + 134.6817521146379, + 7.447340718018211 + ], + [ + 128.06709674667968, + 13.283780382216321 + ], + [ + 112.50328139068506, + 28.069433468651766 + ], + [ + 102.38676222420781, + 39.35322275656824 + ], + [ + 95.77217810185084, + 48.691526219285265 + ], + [ + 93.82667446525102, + 51.415192125444264 + ], + [ + 91.49209859957182, + 56.86254174916263 + ], + [ + 89.93573843765284, + 61.92078352100094 + ], + [ + 89.15752273389239, + 67.36813314471925 + ], + [ + 87.99023480105302, + 72.8154649570373 + ], + [ + 87.21201909729257, + 78.65190462123545 + ], + [ + 86.8229468682132, + 84.09925424495381 + ], + [ + 86.43387463913382, + 87.21202800299295 + ], + [ + 86.0447311644532, + 96.93942150618972 + ], + [ + 86.0447311644532, + 100.4412853047086 + ], + [ + 86.0447311644532, + 109.00139087506574 + ], + [ + 86.0447311644532, + 116.78331636446336 + ], + [ + 86.0447311644532, + 124.56524185386101 + ], + [ + 86.8229468682132, + 131.95805949137852 + ], + [ + 87.21201909729257, + 140.12907502125591 + ], + [ + 87.60116257197365, + 144.02002886025454 + ], + [ + 88.3793070301324, + 152.58015224201205 + ], + [ + 89.546594962972, + 160.7511677718894 + ], + [ + 90.7138828958116, + 169.70036338272632 + ], + [ + 91.8811708286512, + 178.6495768049636 + ], + [ + 92.65938653241142, + 187.59879022720082 + ], + [ + 93.82667446525102, + 195.38069790519813 + ], + [ + 94.99396239809062, + 202.3844255022359 + ], + [ + 95.77217810185084, + 205.10810921979527 + ], + [ + 97.32853826376981, + 210.55544103211327 + ], + [ + 101.60861776604906, + 220.6719601985905 + ], + [ + 104.33226586080764, + 228.84297572846788 + ], + [ + 107.05591395556644, + 235.45759547362553 + ], + [ + 107.83412965932644, + 237.40306348742467 + ], + [ + 110.55777775408524, + 242.8504309225434 + ], + [ + 111.33599345784546, + 244.79589893634255 + ], + [ + 113.28149709444529, + 248.2977627348614 + ], + [ + 114.83785725636449, + 251.02144645242078 + ], + [ + 117.17243312204369, + 253.74509454717946 + ], + [ + 119.50700898772288, + 257.2469583456983 + ], + [ + 122.6198005571623, + 260.74882214421723 + ], + [ + 125.34344865192111, + 263.8616137136567 + ], + [ + 128.06709674667968, + 266.58526180841534 + ], + [ + 129.62352815420013, + 268.1416575931351 + ], + [ + 132.3471762489587, + 270.47623345881436 + ], + [ + 134.6817521146379, + 273.1999171763737 + ], + [ + 137.40540020939693, + 275.92356527113236 + ], + [ + 140.51819177883613, + 278.2581411368116 + ], + [ + 143.2418398735947, + 279.8145369215313 + ], + [ + 145.96555921395498, + 281.7600049353305 + ], + [ + 149.07827953779292, + 283.31640072005024 + ], + [ + 150.2455674706323, + 284.0945808010097 + ], + [ + 152.96928681099257, + 286.0400844376096 + ], + [ + 158.02751077143034, + 289.54194823612846 + ], + [ + 160.36208663711, + 291.4874162499276 + ], + [ + 163.08573473186857, + 293.43288426372675 + ], + [ + 163.4748782065492, + 293.43288426372675 + ], + [ + 164.64216613938902, + 294.211099967487 + ], + [ + 165.42031059754777, + 294.6001721965664 + ], + [ + 165.8094540722284, + 294.6001721965664 + ], + [ + 166.19852630130777, + 294.6001721965664 + ] + ], + "lineWidth": 4, + "xywh": "[1916.7523462949223,-332.841730886717,168.19852630130777,296.6001721965664]" + }, + "VOBcxx9aN5": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "VOBcxx9aN5", + "index": "b6D", + "seed": 536825619, + "points": [ + [ + 14.84016726123582, + 2 + ], + [ + 10.94923123363742, + 2 + ], + [ + 9.78194330079782, + 2 + ], + [ + 5.890936027598173, + 3.5563957847197116 + ], + [ + 4.334575865679199, + 5.501863798518855 + ], + [ + 2.3890722290793747, + 9.781907677997253 + ], + [ + 2, + 14.840167261235877 + ], + [ + 2.3890722290793747, + 16.396563045955588 + ], + [ + 2.7782157037599973, + 18.731138911634844 + ], + [ + 3.5563601619187466, + 21.0657147773141 + ], + [ + 4.723648094758573, + 23.0111827911133 + ], + [ + 6.280079502279023, + 25.73486650867261 + ], + [ + 8.225511893277371, + 28.458514603431297 + ], + [ + 9.78194330079782, + 30.40401824003112 + ], + [ + 11.338303462716794, + 31.571306172870777 + ], + [ + 13.672879328395993, + 32.738594105710376 + ], + [ + 14.451095032156445, + 32.738594105710376 + ], + [ + 16.396527423154794, + 33.51677418666992 + ], + [ + 18.731103288833992, + 33.90588203855003 + ], + [ + 21.06567915451342, + 33.90588203855003 + ], + [ + 23.78939849487324, + 33.90588203855003 + ], + [ + 26.123974360552438, + 33.90588203855003 + ], + [ + 28.069406751551014, + 33.90588203855003 + ], + [ + 28.84762245531124, + 33.90588203855003 + ], + [ + 29.236694684390613, + 33.12766633478981 + ], + [ + 29.625838159071463, + 32.738594105710376 + ] + ], + "lineWidth": 4, + "xywh": "[2000.408005230296,-114.55887854000662,31.625838159071463,35.90588203855003]" + }, + "XGInnztLrh": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "XGInnztLrh", + "index": "b6E", + "seed": 351197654, + "points": [ + [ + 10.560159004558045, + 2.3891078518801123 + ], + [ + 8.614655367958221, + 2 + ], + [ + 5.891007273199648, + 2 + ], + [ + 3.945503636599824, + 2.778215703760168 + ], + [ + 3.5564314075204493, + 2.778215703760168 + ], + [ + 2.7782157037599973, + 3.5563957847197116 + ], + [ + 2, + 4.3345758656792555 + ], + [ + 2, + 6.280079502279079 + ], + [ + 2.3891434746806226, + 7.447367435118679 + ], + [ + 2.7782157037599973, + 8.225547516078223 + ], + [ + 3.5564314075204493, + 10.560123381757478 + ], + [ + 4.334575865679199, + 12.11651916647719 + ], + [ + 5.501863798518798, + 13.672879328396277 + ], + [ + 7.058295206039247, + 14.840167261235877 + ], + [ + 7.8364396641979965, + 15.229275113115989 + ], + [ + 10.171015529877423, + 16.007455194075533 + ], + [ + 12.505591395556621, + 16.007455194075533 + ], + [ + 14.84016726123582, + 14.840167261235877 + ], + [ + 15.618382964996044, + 14.061987180276333 + ] + ], + "lineWidth": 4, + "xywh": "[2011.302668854932,-103.27510706349045,17.618382964996044,18.007455194075533]" + }, + "QB8EDW6m5k": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "QB8EDW6m5k", + "index": "b6F", + "seed": 228116749, + "points": [ + [ + 2, + 2 + ], + [ + 5.5018637985185705, + 2 + ], + [ + 7.4473674351183945, + 2 + ], + [ + 9.78194330079782, + 2 + ], + [ + 11.338303462716794, + 3.1672879328396277 + ], + [ + 12.116519166477019, + 4.3345758656792555 + ], + [ + 13.283807099316618, + 6.669151731358511 + ], + [ + 13.672879328395993, + 7.447331812318026 + ], + [ + 14.062022803076843, + 8.614619745157654 + ], + [ + 14.84016726123582, + 10.94919561083691 + ], + [ + 15.229310735916442, + 12.116483543676537 + ], + [ + 16.39659866875627, + 14.451059409355793 + ], + [ + 17.56388660159564, + 17.563833167394904 + ], + [ + 18.342031059754618, + 20.676606925434044 + ], + [ + 18.731174534435468, + 23.789362872072815 + ], + [ + 18.342031059754618, + 26.12393873775207 + ], + [ + 17.56388660159564, + 28.069424562951554 + ], + [ + 17.17474312691502, + 29.23671249579118 + ], + [ + 16.785670897835644, + 30.014910388151037 + ], + [ + 15.618382964995817, + 31.182198320990665 + ], + [ + 14.451095032156445, + 31.571288361470437 + ], + [ + 12.894734870237244, + 32.738576294310064 + ] + ], + "lineWidth": 4, + "xywh": "[1979.7858946682622,-231.2876718239693,20.731174534435468,34.738576294310064]" + }, + "B-E2-FhYd3": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "B-E2-FhYd3", + "index": "b6G", + "seed": 1031142728, + "points": [ + [ + 9.39279982611697, + 2 + ], + [ + 9.781872055196345, + 4.3345758656792555 + ], + [ + 9.39279982611697, + 10.171015529877394 + ], + [ + 8.225511893277371, + 12.894681436036421 + ], + [ + 5.890936027598173, + 17.17474312691516 + ], + [ + 5.112720323837948, + 18.342031059754788 + ], + [ + 3.5563601619187466, + 20.28751688495427 + ], + [ + 2.7781444581587493, + 20.676606925434044 + ], + [ + 2.3890722290793747, + 21.065696965913787 + ], + [ + 2, + 21.065696965913787 + ] + ], + "lineWidth": 4, + "xywh": "[1983.676901941462,-215.7238386565744,11.781872055196345,23.065696965913787]" + }, + "HRcBCFjK3b": { + "type": "brush", + "color": "--affine-palette-line-black", + "id": "HRcBCFjK3b", + "index": "b6H", + "seed": 1452820428, + "points": [ + [ + 2, + 2 + ], + [ + 8.614655367958221, + 9.781925489397622 + ], + [ + 10.560159004558045, + 14.06196936887605 + ], + [ + 8.614655367958221, + 20.28751688495427 + ] + ], + "lineWidth": 4, + "xywh": "[1982.1204705339414,-216.89112658941403,12.560159004558045,22.28751688495427]" + }, + "kZdlb3P-Uu": { + "type": "brush", + "color": "--affine-palette-line-green", + "id": "kZdlb3P-Uu", + "index": "b6I", + "seed": 955774118, + "points": [ + [ + 31.960342779149414, + 28.458532414831666 + ], + [ + 30.014910388151065, + 30.014910388151037 + ], + [ + 23.78932724927199, + 34.294972079029804 + ], + [ + 14.061951557475595, + 37.407745837068916 + ], + [ + 9.781872055196573, + 37.407745837068916 + ], + [ + 8.225511893277599, + 37.0186379851888 + ], + [ + 5.8909360275984, + 33.90588203855003 + ], + [ + 5.501863798518798, + 30.40401824003115 + ], + [ + 5.8909360275984, + 24.178470723952927 + ], + [ + 10.949159988036172, + 14.451077220756133 + ], + [ + 17.563815355994393, + 11.338303462717022 + ], + [ + 17.563815355994393, + 11.727411314597134 + ], + [ + 17.563815355994393, + 12.116501355076878 + ], + [ + 16.785599652234396, + 12.50559139555665 + ], + [ + 14.451023786555197, + 14.06198718027639 + ], + [ + 12.894663624635996, + 14.840167261235905 + ], + [ + 9.003727597037596, + 16.396563045955645 + ], + [ + 5.8909360275984, + 16.78565308643539 + ], + [ + 3.556360161918974, + 16.007455194075533 + ], + [ + 2, + 12.116501355076878 + ], + [ + 3.1672879328395993, + 2 + ] + ], + "lineWidth": 4, + "xywh": "[1929.981657030839,-193.15627789214173,33.960342779149414,39.407745837068916]" + }, + "-r2j5siu92": { + "type": "brush", + "color": "--affine-palette-line-green", + "id": "-r2j5siu92", + "index": "b6J", + "seed": 1902444094, + "points": [ + [ + 48.302445084505734, + 60.75350449386144 + ], + [ + 42.07686194562689, + 59.97530660150156 + ], + [ + 33.905846415749465, + 58.029820776302074 + ], + [ + 29.62583815907169, + 56.473442802982674 + ], + [ + 24.956686427713066, + 54.52795697778319 + ], + [ + 16.39652742315502, + 51.02609317926431 + ], + [ + 10.171015529877423, + 46.74604929978591 + ], + [ + 7.836439664198224, + 44.800563474586426 + ], + [ + 3.9454680137992, + 39.74232170274814 + ], + [ + 2, + 32.738594105710376 + ], + [ + 2.7781800809596007, + 24.56757857583301 + ], + [ + 6.669151731358625, + 14.840167261235877 + ], + [ + 19.120211140714446, + 2 + ] + ], + "lineWidth": 4, + "xywh": "[1884.8464998791728,-237.51321934004753,50.302445084505734,62.75350449386144]" + }, + "WmV93wXgmk": { + "type": "brush", + "color": "--affine-palette-line-green", + "id": "WmV93wXgmk", + "index": "b6K", + "seed": 1732289373, + "points": [ + [ + 49.85880524642471, + 148.68918059161302 + ], + [ + 46.74604929978568, + 148.30009055113325 + ], + [ + 36.24045790422906, + 147.13280261829362 + ], + [ + 28.458550226231864, + 146.35460472593377 + ], + [ + 20.676606925434044, + 143.241848779295 + ], + [ + 10.560123381757421, + 135.84901333037712 + ], + [ + 4.723683717559197, + 126.51070986766013 + ], + [ + 2, + 116.78331636446333 + ], + [ + 3.167287932839372, + 106.66681500938648 + ], + [ + 7.447367435118622, + 96.55033146570992 + ], + [ + 13.283807099316618, + 89.54660386867215 + ], + [ + 36.62956575610929, + 75.53914867459662 + ], + [ + 40.909609635587685, + 74.76095078223676 + ], + [ + 41.687789716547286, + 74.76095078223676 + ], + [ + 42.076897568427285, + 74.76095078223676 + ], + [ + 39.742321702748086, + 75.92823871507642 + ], + [ + 32.73859410571026, + 77.87372454027593 + ], + [ + 29.236730307191465, + 78.26281458075567 + ], + [ + 23.40029064299324, + 77.09552664791602 + ], + [ + 19.120246763514842, + 73.9827528898769 + ], + [ + 14.84016726123582, + 66.5899352523594 + ], + [ + 12.89469924743662, + 56.86253284346242 + ], + [ + 13.283807099316618, + 46.3569414479058 + ], + [ + 16.00745519407542, + 35.851350052349176 + ], + [ + 19.509318992594217, + 26.513046589632154 + ], + [ + 21.843894858273416, + 22.2329938044536 + ], + [ + 35.07316997138946, + 8.61462865085781 + ], + [ + 50.637020950184706, + 2 + ], + [ + 53.749776896823505, + 2 + ], + [ + 60.36439664198133, + 5.112764852338955 + ], + [ + 63.08808035954053, + 7.83643966419811 + ], + [ + 64.25536829238013, + 9.781916583697466 + ], + [ + 65.81176407709995, + 12.505591395556621 + ], + [ + 65.81176407709995, + 14.451068315055977 + ], + [ + 65.81176407709995, + 15.618356247895576 + ], + [ + 65.42265622521973, + 16.396554140255432 + ], + [ + 64.64447614426035, + 16.396554140255432 + ], + [ + 64.25536829238013, + 16.007455194075533 + ], + [ + 63.86626044050013, + 15.229266207415833 + ] + ], + "lineWidth": 4, + "xywh": "[1836.2094789289881,-365.52579300622654,67.81176407709995,150.68918059161302]" + }, + "lfqCdODkbN": { + "type": "brush", + "color": "--affine-palette-line-green", + "id": "lfqCdODkbN", + "index": "b6L", + "seed": 540741484, + "points": [ + [ + 2, + 46.356941447905854 + ], + [ + 3.5563957847198253, + 43.2441765955669 + ], + [ + 10.171015529877423, + 34.684062119509576 + ], + [ + 14.451095032156445, + 30.014910388151065 + ], + [ + 18.731174534435695, + 24.956668616312754 + ], + [ + 26.513046589632268, + 16.78565308643539 + ], + [ + 29.62583815907169, + 13.672879328396277 + ], + [ + 33.51677418667009, + 10.171015529877366 + ], + [ + 34.68406211950969, + 9.003727597037766 + ], + [ + 34.68406211950969, + 9.392826543217723 + ], + [ + 31.182198320990665, + 14.840167261235877 + ], + [ + 26.513046589632268, + 20.2875168849543 + ], + [ + 23.011182791113242, + 22.6220927506335 + ], + [ + 21.065750400114894, + 23.4002817372932 + ], + [ + 21.065750400114894, + 23.0111827911133 + ], + [ + 22.233038332954493, + 20.2875168849543 + ], + [ + 30.79312609191129, + 12.1164969022268 + ], + [ + 41.687789716547286, + 4.723674811859212 + ], + [ + 46.35694144790591, + 2 + ] + ], + "lineWidth": 4, + "xywh": "[1893.4065876381296,-388.0933626763594,48.35694144790591,48.356941447905854]" + }, + "R6A6jSkx2s": { + "type": "brush", + "color": "--affine-palette-line-green", + "id": "R6A6jSkx2s", + "index": "b6M", + "seed": 1939520361, + "points": [ + [ + 8.614655367958449, + 20.287516884954243 + ], + [ + 4.723719340360049, + 19.509318992594388 + ], + [ + 3.1672879328395993, + 19.120228952114587 + ], + [ + 2, + 17.952941019274988 + ], + [ + 2, + 16.78565308643539 + ], + [ + 4.334575865679426, + 12.116496902226743 + ], + [ + 7.8364396641979965, + 9.781921036547544 + ], + [ + 17.56388660159587, + 5.112769305189033 + ], + [ + 23.011182791113242, + 3.945481372349377 + ], + [ + 34.68406211950946, + 2 + ], + [ + 47.52422938074551, + 2 + ], + [ + 59.58625218382235, + 3.1672879328395993 + ], + [ + 65.42269184802058, + 3.945481372349377 + ], + [ + 77.09557117641702, + 5.890962744698811 + ], + [ + 87.60116257197365, + 8.225538610378067 + ], + [ + 96.16125033093044, + 12.505591395556621 + ], + [ + 101.60861776604929, + 17.56384207309503 + ], + [ + 105.11048156456786, + 24.1784707239529 + ], + [ + 103.94319363172849, + 30.404009334330965 + ], + [ + 100.05218635852839, + 33.90587313284982 + ], + [ + 94.99396239809062, + 36.240448998529075 + ], + [ + 93.04845876149079, + 36.62954794470903 + ], + [ + 91.88117082865142, + 36.62954794470903 + ] + ], + "lineWidth": 4, + "xywh": "[1977.4513188025828,-389.260650609199,107.11048156456786,38.62954794470903]" + }, + "UkLPIhvvCA": { + "type": "brush", + "color": "--affine-palette-line-green", + "id": "UkLPIhvvCA", + "index": "b6N", + "seed": 1337365816, + "points": [ + [ + 2, + 7.83643966419811 + ], + [ + 10.171015529877423, + 3.945481372349434 + ], + [ + 32.34948625383049, + 2 + ], + [ + 40.90964525838854, + 3.5563824261694776 + ], + [ + 44.022365582226485, + 5.501863798518912 + ], + [ + 45.578796989746934, + 7.447345170868289 + ], + [ + 46.74608492258676, + 9.781921036547544 + ], + [ + 49.85880524642471, + 17.174747579765267 + ], + [ + 52.19338111210391, + 26.902141082962032 + ], + [ + 53.749812519624356, + 36.240444545679 + ], + [ + 53.360669044943734, + 44.41146007555642 + ], + [ + 52.58252458678453, + 47.913323874075274 + ], + [ + 49.85880524642471, + 54.13886248445334 + ], + [ + 45.18965351506586, + 59.97530214865151 + ], + [ + 41.29871748746791, + 64.64445388001002 + ], + [ + 37.796853688948886, + 68.92450666518857 + ], + [ + 35.85135005234906, + 72.03728042322768 + ], + [ + 35.46227782326969, + 73.59365839654708 + ], + [ + 35.46227782326969, + 74.37185628890694 + ], + [ + 37.796853688948886, + 76.31733320840624 + ], + [ + 40.52050178370746, + 77.4846211412459 + ], + [ + 46.74608492258676, + 81.76467392642445 + ], + [ + 52.58252458678453, + 87.99022144250273 + ], + [ + 56.862532843462304, + 96.55032701285984 + ], + [ + 58.02982077630213, + 103.94314465037735 + ], + [ + 56.47346061438293, + 110.16869216645563 + ], + [ + 53.360669044943734, + 116.00513183065374 + ], + [ + 49.85880524642471, + 121.06337360249199 + ], + [ + 47.91337285542613, + 123.3979494681713 + ], + [ + 44.41150905690711, + 127.2889033071699 + ], + [ + 41.687789716547286, + 130.01258702472927 + ], + [ + 40.131429554628085, + 131.56896499804867 + ], + [ + 38.96414162178871, + 131.9580550385284 + ], + [ + 38.96414162178871, + 132.34716289040853 + ] + ], + "lineWidth": 4, + "xywh": "[2080.17265689247,-384.98059337117036,60.02982077630213,134.34716289040853]" + }, + "aOE0sH3dnH": { + "type": "brush", + "color": "--affine-palette-line-green", + "id": "aOE0sH3dnH", + "index": "b6O", + "seed": 1219029993, + "points": [ + [ + 102.77590569888866, + 2 + ], + [ + 107.44505743024706, + 7.83643966419811 + ], + [ + 111.33599345784546, + 15.229257301715649 + ], + [ + 113.28149709444529, + 19.509318992594388 + ], + [ + 115.22692948544386, + 28.458532414831637 + ], + [ + 110.94692122876609, + 45.18965351506617 + ], + [ + 103.16497792796804, + 56.862532843462446 + ], + [ + 94.21574669433062, + 66.58992634665921 + ], + [ + 83.32108306969462, + 73.98276179557709 + ], + [ + 73.20456390321738, + 77.48462559409597 + ], + [ + 65.42269184802058, + 77.09551774221586 + ], + [ + 59.58625218382235, + 75.53913976889646 + ], + [ + 56.08438838530333, + 73.2045639032172 + ], + [ + 54.52795697778333, + 72.03727597037758 + ], + [ + 53.749812519624584, + 71.25907807801772 + ], + [ + 53.360669044943506, + 70.0917901451781 + ], + [ + 53.360669044943506, + 69.70270010469832 + ], + [ + 53.360669044943506, + 68.92450221233847 + ], + [ + 54.138884748703504, + 68.92450221233847 + ], + [ + 54.52795697778333, + 69.31361006421858 + ], + [ + 55.30617268154333, + 70.86998803753795 + ], + [ + 55.30617268154333, + 74.7609418765366 + ], + [ + 54.52795697778333, + 76.70642770173609 + ], + [ + 51.02609317926431, + 82.54286736593423 + ], + [ + 49.08066078826573, + 84.09924533925363 + ], + [ + 43.24422112406796, + 87.21201909729274 + ], + [ + 36.62956575610929, + 89.93568500345177 + ], + [ + 29.62583815907169, + 90.71388289581162 + ], + [ + 22.233038332954266, + 90.32479285533185 + ], + [ + 15.618382964996044, + 88.37930703013237 + ], + [ + 7.8364396641979965, + 85.26653327209326 + ], + [ + 4.334575865679199, + 81.76466947357437 + ], + [ + 2.7782157037602246, + 79.81920145977523 + ], + [ + 2, + 77.48462559409597 + ] + ], + "lineWidth": 4, + "xywh": "[2014.804532653451,-255.4116283731217,117.22692948544386,92.71388289581162]" + }, + "BFegxcdohb": { + "type": "brush", + "xywh": "[1928.9255185125135,-404.9079588939192,85.6320490819935,42.74063399938581]", + "points": [ + [ + 2, + 40.74063399938581 + ], + [ + 4.075390541610886, + 33.82264441538513 + ], + [ + 8.91798958400068, + 24.137509666498545 + ], + [ + 17.911306373719526, + 15.144129540886922 + ], + [ + 26.904686499331092, + 8.226171624832716 + ], + [ + 35.89806662494266, + 3.3836042503894532 + ], + [ + 45.58320137382907, + 2 + ], + [ + 54.576581499440636, + 4.075390541610886 + ], + [ + 68.41249733154928, + 11.685166416833056 + ], + [ + 71.87146045560303, + 15.144129540886922 + ], + [ + 79.48126799877173, + 22.75390541610909 + ], + [ + 82.94023112282548, + 31.05546758255275 + ], + [ + 83.6320490819935, + 31.05546758255275 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 4, + "id": "BFegxcdohb", + "index": "b6P", + "seed": 1065697393 + }, + "MWy51BkPqD": { + "type": "brush", + "xywh": "[1977.8102820527863,-388.5376577281996,149.742984247099,41.13085812416364]", + "points": [ + [ + 5, + 18.144161208833452 + ], + [ + 9.842599042389793, + 15.376952708054546 + ], + [ + 22.986696915330185, + 9.842599042389793 + ], + [ + 34.74728554172066, + 6.383604250389453 + ], + [ + 44.43248362650024, + 5 + ], + [ + 54.117555039493936, + 5 + ], + [ + 64.49450774754837, + 6.383604250389453 + ], + [ + 74.17970583232795, + 9.842599042389793 + ], + [ + 77.63873229227488, + 11.226171624832773 + ], + [ + 82.4812679987715, + 14.685166416833113 + ], + [ + 85.94029445871843, + 16.760556958444 + ], + [ + 88.01568500032931, + 19.527733791276376 + ], + [ + 89.39919424687946, + 21.60312433288732 + ], + [ + 90.78283016521505, + 23.678514874498205 + ], + [ + 91.47458478849035, + 24.37033283366617 + ], + [ + 91.47458478849035, + 25.75390541610909 + ], + [ + 90.78283016521505, + 25.75390541610909 + ], + [ + 89.39919424687946, + 25.75390541610909 + ], + [ + 89.39919424687946, + 24.37033283366617 + ], + [ + 89.39919424687946, + 22.986728583276715 + ], + [ + 89.39919424687946, + 22.294942292055282 + ], + [ + 91.47458478849035, + 19.527733791276376 + ], + [ + 118.45466182943187, + 12.60977587522217 + ], + [ + 125.37271474932572, + 14.685166416833113 + ], + [ + 135.05778616231942, + 19.527733791276376 + ], + [ + 137.1331767039303, + 20.91133804166583 + ], + [ + 139.90044854060193, + 24.37033283366617 + ], + [ + 144.05122962382416, + 32.67189500010983 + ], + [ + 144.742984247099, + 36.13085812416364 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 10, + "id": "MWy51BkPqD", + "index": "b6Q", + "seed": 309108426 + }, + "2MBs127OeD": { + "type": "brush", + "xywh": "[1895.4864783475177,-346.3380289368134,53.58320137382907,30.75390541610909]", + "points": [ + [ + 48.58320137382907, + 11.91795791605415 + ], + [ + 41.66521178982839, + 8.45896312405381 + ], + [ + 31.98007704094198, + 5.691786291221433 + ], + [ + 22.294878956162393, + 5 + ], + [ + 19.527733791276432, + 5.691786291221433 + ], + [ + 13.993316789718847, + 7.075390541610886 + ], + [ + 10.534353665664867, + 7.7671768328323765 + ], + [ + 8.45896312405398, + 8.45896312405381 + ], + [ + 6.383572582443094, + 9.842567374443263 + ], + [ + 5.691754623275074, + 12.60974420727564 + ], + [ + 5, + 13.993348457665093 + ], + [ + 5, + 17.452343249665432 + ], + [ + 6.383572582443094, + 21.603124332887262 + ], + [ + 7.075390541610886, + 24.37030116571964 + ], + [ + 7.76714516488596, + 25.75390541610909 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 10, + "id": "2MBs127OeD", + "index": "b6R", + "seed": 1719671446 + }, + "wTSCR6f1jj": { + "type": "brush", + "xywh": "[2064.2848668412767,-268.8567609380418,40.43910350088845,60.5011276219368]", + "points": [ + [ + 18.83597916800136, + 6.38357258244298 + ], + [ + 23.678514874497978, + 5 + ], + [ + 33.363712959277564, + 5.69175462327496 + ], + [ + 34.747348877613604, + 7.76714516488596 + ], + [ + 35.43910350088845, + 18.835915832108412 + ], + [ + 34.05546758255241, + 25.062087456941185 + ], + [ + 29.21293187605579, + 36.13085812416364 + ], + [ + 23.678514874497978, + 44.4324202906073 + ], + [ + 18.83597916800136, + 49.96677395627205 + ], + [ + 13.301562166443546, + 53.425737080325916 + ], + [ + 7.767271836671625, + 55.5011276219368 + ], + [ + 6.383635918335585, + 55.5011276219368 + ], + [ + 5, + 54.117555039493936 + ], + [ + 5, + 53.425737080325916 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 10, + "id": "wTSCR6f1jj", + "index": "b6S", + "seed": 985927068 + }, + "OhRlZB2nTN": { + "type": "brush", + "xywh": "[1896.8700509299608,-265.39779781398795,41.822676083331544,41.82267608333166]", + "points": [ + [ + 7.075390541610886, + 5 + ], + [ + 5.691817959167793, + 9.150781083221887 + ], + [ + 5, + 18.835915832108412 + ], + [ + 5.691817959167793, + 24.370332833666225 + ], + [ + 8.45896312405398, + 29.904686499330978 + ], + [ + 11.22617162483266, + 32.67189500010977 + ], + [ + 16.068770667222452, + 35.43904016499573 + ], + [ + 25.06208745694107, + 36.82267608333166 + ], + [ + 30.596504458498885, + 36.82267608333166 + ], + [ + 36.822676083331544, + 36.13085812416364 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 10, + "id": "OhRlZB2nTN", + "index": "b6T", + "seed": 944159691 + }, + "kbhV0AgzHn": { + "type": "brush", + "xywh": "[1120.2318533086293,-168.50070046629594,148.93510301086712,95.19015427794585]", + "points": [ + [ + 2, + 93.19015427794585 + ], + [ + 2, + 86.5821720838918 + ], + [ + 2.8810911804889656, + 82.61738276745933 + ], + [ + 7.726931345487856, + 69.84196396959572 + ], + [ + 9.489073374543068, + 65.43662906291883 + ], + [ + 11.691720661920272, + 60.59078889792005 + ], + [ + 15.215964388108205, + 52.220664674810735 + ], + [ + 17.85915726572989, + 48.25587535837832 + ], + [ + 23.145543020973037, + 39.445245876947126 + ], + [ + 35.039910970270284, + 24.467139459783766 + ], + [ + 43.85058078362408, + 16.09701523667445 + ], + [ + 48.25587535837826, + 11.251175071675618 + ], + [ + 50.458562977678184, + 9.929578632864832 + ], + [ + 54.4233522941106, + 6.845840164998776 + ], + [ + 55.30440314267685, + 5.964789316432416 + ], + [ + 57.94759602029853, + 4.202647287377147 + ], + [ + 59.26919245910926, + 2.8810508485663036 + ], + [ + 60.59078889792022, + 2.440545590244426 + ], + [ + 61.47183974648647, + 2 + ], + [ + 62.35293092697543, + 2 + ], + [ + 62.79343618529742, + 2 + ], + [ + 63.67452736578616, + 2 + ], + [ + 64.55557821435264, + 2 + ], + [ + 65.87717465316337, + 2 + ], + [ + 67.1987710919741, + 2.440545590244426 + ], + [ + 68.96091312102931, + 2.8810508485663036 + ], + [ + 70.72301481816226, + 3.762142029055269 + ], + [ + 78.2120881927051, + 5.964789316432416 + ], + [ + 83.49847394794847, + 7.286385755243202 + ], + [ + 86.14166682556993, + 8.607982194054046 + ], + [ + 94.51175071675675, + 13.013317100730887 + ], + [ + 97.59548918462292, + 14.775418797863608 + ], + [ + 103.32242053011055, + 19.62129929478499 + ], + [ + 109.48985713392017, + 25.788735898594553 + ], + [ + 115.65729373772979, + 31.956172502404115 + ], + [ + 121.82477067346167, + 39.445245876947126 + ], + [ + 124.46796355108336, + 42.52894401289063 + ], + [ + 129.31380371608225, + 49.13692620694468 + ], + [ + 133.27859303251466, + 55.744908400998725 + ], + [ + 136.8028367587026, + 61.912385336730836 + ], + [ + 138.5649787877578, + 66.31767991148519 + ], + [ + 140.767626075135, + 70.2824692279176 + ], + [ + 142.08922251394574, + 74.24725854435002 + ], + [ + 143.4108189527567, + 77.33099701221613 + ], + [ + 144.29191013324566, + 81.73633191889303 + ], + [ + 144.73241539156743, + 82.61738276745933 + ], + [ + 145.6135065720564, + 83.93897920627018 + ], + [ + 146.49455742062287, + 85.70112123532545 + ], + [ + 146.93510301086712, + 86.5821720838918 + ], + [ + 146.93510301086712, + 87.02271767413623 + ], + [ + 146.93510301086712, + 87.46322293245811 + ], + [ + 146.93510301086712, + 87.90376852270259 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "kbhV0AgzHn", + "index": "b6U", + "seed": 1652155770 + }, + "ODCqCfNSpk": { + "type": "brush", + "xywh": "[1125.9587846541172,-83.91852838240413,139.24338234894685,22.50235014335135]", + "points": [ + [ + 2, + 9.048527784298471 + ], + [ + 3.7621420290552123, + 10.370124223109315 + ], + [ + 9.929578632864832, + 13.894367949297248 + ], + [ + 12.572771510486291, + 14.775418797863608 + ], + [ + 15.65650997835246, + 15.656509978352517 + ], + [ + 21.383400991917597, + 16.537560826918877 + ], + [ + 27.110332337405225, + 17.859157265729664 + ], + [ + 30.194070805271394, + 18.299702855974147 + ], + [ + 35.92096181883653, + 19.180753704540507 + ], + [ + 42.08843875456864, + 20.061804553106867 + ], + [ + 47.81532976813378, + 20.50235014335135 + ], + [ + 53.54226111362141, + 20.50235014335135 + ], + [ + 59.70969771743103, + 20.50235014335135 + ], + [ + 65.43662906291866, + 20.50235014335135 + ], + [ + 68.52036753078482, + 20.50235014335135 + ], + [ + 74.68780413459444, + 20.061804553106867 + ], + [ + 79.97418988983759, + 19.62129929478499 + ], + [ + 84.37952479651449, + 19.180753704540507 + ], + [ + 88.7848193712689, + 18.740208114296024 + ], + [ + 93.1901542779458, + 18.299702855974147 + ], + [ + 97.15494359437821, + 17.859157265729664 + ], + [ + 100.23868206224415, + 16.537560826918877 + ], + [ + 102.00078375937687, + 16.537560826918877 + ], + [ + 106.84666425629825, + 15.656509978352517 + ], + [ + 109.93036239224193, + 14.33491353954173 + ], + [ + 113.45464645035236, + 13.894367949297248 + ], + [ + 116.09783932797382, + 13.013317100730887 + ], + [ + 118.7410322055955, + 12.132225920241979 + ], + [ + 119.62208305416198, + 11.691720661920101 + ], + [ + 121.38422508321719, + 11.251175071675618 + ], + [ + 123.58687237059439, + 10.370124223109315 + ], + [ + 124.46792321916064, + 9.929578632864832 + ], + [ + 126.23006524821585, + 9.048527784298471 + ], + [ + 127.55166168702681, + 8.607982194054046 + ], + [ + 128.87325812583754, + 8.167436603809563 + ], + [ + 129.754308974404, + 7.286385755243202 + ], + [ + 131.07590541321474, + 6.845840164998776 + ], + [ + 131.9569965937037, + 5.964789316432416 + ], + [ + 133.27859303251444, + 5.524243726187933 + ], + [ + 133.71909829083643, + 5.0837384678660555 + ], + [ + 135.04069472964716, + 4.202647287377147 + ], + [ + 135.48124031989164, + 3.762142029055269 + ], + [ + 136.3622911684579, + 2.8810508485663036 + ], + [ + 136.80283675870237, + 2.440545590244426 + ], + [ + 137.24338234894685, + 2 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "ODCqCfNSpk", + "index": "b6V", + "seed": 660273861 + }, + "6rDKmEpsDm": { + "type": "brush", + "xywh": "[1164.725626969875,-121.8042795176732,26.46713945978354,26.467139459783766]", + "points": [ + [ + 24.46713945978354, + 2 + ], + [ + 21.383400991917597, + 4.64319287762163 + ], + [ + 18.74020811429591, + 6.845840164998776 + ], + [ + 12.132225920242035, + 13.894367949297305 + ], + [ + 10.370083891186596, + 16.09701523667445 + ], + [ + 8.607982194053875, + 17.418611675485295 + ], + [ + 5.964789316432416, + 20.50235014335135 + ], + [ + 3.3215964388107295, + 23.14554302097298 + ], + [ + 2, + 24.467139459783766 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "6rDKmEpsDm", + "index": "b6W", + "seed": 346419107 + }, + "eUdoNF0lYW": { + "type": "brush", + "xywh": "[1173.5362564513061,-120.04217782054047,28.229281488839206,31.75352521502697]", + "points": [ + [ + 19.621299294785103, + 2 + ], + [ + 16.09701523667445, + 5.524284058110538 + ], + [ + 12.132225920242035, + 10.370124223109315 + ], + [ + 9.048527784298585, + 13.894367949297305 + ], + [ + 7.726931345487628, + 15.215964388108091 + ], + [ + 6.405334906676899, + 16.537560826918877 + ], + [ + 5.524243726187933, + 17.418652007407843 + ], + [ + 3.7621420290552123, + 19.180753704540507 + ], + [ + 2.440545590244483, + 20.50235014335135 + ], + [ + 2, + 21.823946582162137 + ], + [ + 2, + 23.145543020972923 + ], + [ + 2, + 24.467139459783766 + ], + [ + 2, + 25.788735898594553 + ], + [ + 2.440545590244483, + 25.788735898594553 + ], + [ + 3.321596438810957, + 26.66982707908346 + ], + [ + 7.286385755243373, + 27.55087792764982 + ], + [ + 8.607982194054102, + 27.991423517894304 + ], + [ + 10.810629481431306, + 28.872474366460665 + ], + [ + 14.33491353954173, + 29.31301995670509 + ], + [ + 17.41861167548541, + 29.75352521502697 + ], + [ + 19.18075370454062, + 29.75352521502697 + ], + [ + 22.264492172406563, + 29.31301995670509 + ], + [ + 24.90768505002825, + 27.991423517894304 + ], + [ + 26.229281488839206, + 27.55087792764982 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "eUdoNF0lYW", + "index": "b6X", + "seed": 1585361883 + }, + "jFENPM1sEX": { + "type": "brush", + "xywh": "[1165.6066778184413,-100.21823123837834,31.75352521502691,17.656509978352574]", + "points": [ + [ + 2, + 2 + ], + [ + 2.440545590244483, + 4.202687619299752 + ], + [ + 3.321596438810957, + 7.726931345487685 + ], + [ + 3.7621420290552123, + 10.370124223109315 + ], + [ + 4.643192877621686, + 11.691720661920101 + ], + [ + 5.083738467866169, + 12.572771510486461 + ], + [ + 6.845840164998663, + 13.894367949297305 + ], + [ + 9.489033042620349, + 14.775459129786213 + ], + [ + 13.013317100731001, + 15.656509978352574 + ], + [ + 16.09701523667445, + 15.656509978352574 + ], + [ + 20.061804553106867, + 15.656509978352574 + ], + [ + 23.145543020973037, + 15.656509978352574 + ], + [ + 24.467139459783766, + 15.656509978352574 + ], + [ + 27.110332337405453, + 15.656509978352574 + ], + [ + 29.75352521502691, + 15.656509978352574 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "jFENPM1sEX", + "index": "b6Y", + "seed": 853013629 + }, + "u5noFLsEVU": { + "type": "brush", + "xywh": "[1193.8007486237127,-101.53982767718912,24.704997430728554,21.62129929478499]", + "points": [ + [ + 10.810629481431306, + 10.81066981335374 + ], + [ + 12.132225920242035, + 7.286385755243202 + ], + [ + 12.132225920242035, + 5.964789316432416 + ], + [ + 14.334873207619239, + 3.3215964388107864 + ], + [ + 15.215964388108205, + 2.8810911804889088 + ], + [ + 16.537560826918934, + 2.440545590244426 + ], + [ + 17.85915726572989, + 2 + ], + [ + 18.740208114296138, + 2 + ], + [ + 20.50235014335135, + 3.3215964388107864 + ], + [ + 21.823946582162307, + 5.0837384678660555 + ], + [ + 22.26445184048407, + 7.286385755243202 + ], + [ + 22.704997430728554, + 10.81066981335374 + ], + [ + 21.383400991917824, + 13.894367949297248 + ], + [ + 19.621258962862385, + 16.537560826918877 + ], + [ + 17.41861167548541, + 18.299702855974147 + ], + [ + 15.656469646429969, + 19.180753704540507 + ], + [ + 13.01327676880851, + 19.62129929478499 + ], + [ + 12.132225920242035, + 19.62129929478499 + ], + [ + 9.489033042620349, + 18.299702855974147 + ], + [ + 6.84584016499889, + 16.97810641716336 + ], + [ + 4.643192877621686, + 15.215964388108091 + ], + [ + 3.321596438810957, + 13.894367949297248 + ], + [ + 2, + 13.013317100730887 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "u5noFLsEVU", + "index": "b6Z", + "seed": 152910531 + }, + "apgo0WiVXw": { + "type": "brush", + "xywh": "[1218.4705353708737,-139.8661244027026,15.453822359052765,17.21596438810809]", + "points": [ + [ + 4.202647287377204, + 15.215964388108091 + ], + [ + 2.440545590244483, + 11.251175071675675 + ], + [ + 2, + 8.607982194054046 + ], + [ + 2, + 5.964789316432416 + ], + [ + 2.440545590244483, + 5.524284058110538 + ], + [ + 3.321596438810957, + 4.202687619299752 + ], + [ + 4.643192877621686, + 2.8810911804889088 + ], + [ + 5.964789316432416, + 2.440545590244483 + ], + [ + 7.286385755243373, + 2 + ], + [ + 8.16743660380962, + 2 + ], + [ + 8.607982194054102, + 2.8810911804889088 + ], + [ + 9.929578632864832, + 4.64319287762163 + ], + [ + 11.251175071675789, + 8.167476935732168 + ], + [ + 12.132225920242035, + 9.489073374542954 + ], + [ + 13.013317100731001, + 12.572771510486461 + ], + [ + 13.453822359052765, + 13.894367949297305 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "apgo0WiVXw", + "index": "b6a", + "seed": 1146064757 + }, + "2I4X5NQLnK": { + "type": "brush", + "xywh": "[1231.6864997589819,-115.19629732361915,19.859157265729664,9.28638575524326]", + "points": [ + [ + 2, + 7.286385755243259 + ], + [ + 5.964789316432416, + 7.286385755243259 + ], + [ + 8.607982194053875, + 6.40529457475435 + ], + [ + 12.572771510486291, + 4.64319287762163 + ], + [ + 13.894367949297248, + 3.762101697132721 + ], + [ + 15.215964388107977, + 3.321596438810843 + ], + [ + 16.537560826918707, + 2.4405052583219344 + ], + [ + 17.41861167548518, + 2.4405052583219344 + ], + [ + 17.859157265729664, + 2 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "2I4X5NQLnK", + "index": "b6b", + "seed": 918817015 + }, + "9H6vsfCcGM": { + "type": "brush", + "xywh": "[1231.2459541687374,-114.31524647505279,26.026634201461775,27.788735898594553]", + "points": [ + [ + 2.8810911804887382, + 5.083738467866112 + ], + [ + 2, + 7.726931345487685 + ], + [ + 2, + 10.810629481431192 + ], + [ + 5.524284058110425, + 21.38340099191771 + ], + [ + 5.964789316432416, + 22.704997430728497 + ], + [ + 7.2863857552431455, + 24.467139459783766 + ], + [ + 9.048527784298358, + 25.348190308350127 + ], + [ + 14.775459129786213, + 25.788735898594553 + ], + [ + 16.097055568596943, + 25.788735898594553 + ], + [ + 18.299702855974147, + 24.90768505002825 + ], + [ + 19.180753704540393, + 24.026593869539283 + ], + [ + 20.942895733595606, + 22.26449217240662 + ], + [ + 22.264492172406563, + 20.061804553106867 + ], + [ + 23.14554302097281, + 18.74020811429608 + ], + [ + 23.586088611217292, + 16.09701523667445 + ], + [ + 24.026634201461775, + 14.33491353954173 + ], + [ + 24.026634201461775, + 12.572771510486461 + ], + [ + 23.586088611217292, + 11.251175071675675 + ], + [ + 22.705037762651045, + 9.489033042620406 + ], + [ + 22.264492172406563, + 6.405334906676899 + ], + [ + 21.383441323840316, + 2 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "9H6vsfCcGM", + "index": "b6c", + "seed": 978438289 + }, + "iooTUvCJjn": { + "type": "brush", + "xywh": "[1238.2944819530358,-139.42557881245813,13.251175071675789,20.299702855974147]", + "points": [ + [ + 2.440545590244483, + 13.453822359052822 + ], + [ + 2, + 9.489033042620349 + ], + [ + 2, + 7.726931345487685 + ], + [ + 2, + 5.964789316432416 + ], + [ + 2.440545590244483, + 4.202647287377147 + ], + [ + 2.881050848566474, + 2.8810508485663036 + ], + [ + 3.321596438810957, + 2.8810508485663036 + ], + [ + 3.7621420290552123, + 2 + ], + [ + 4.202647287377204, + 2 + ], + [ + 5.083738467866169, + 2 + ], + [ + 5.964789316432416, + 2.8810508485663036 + ], + [ + 7.286385755243373, + 5.964789316432416 + ], + [ + 8.607982194054102, + 9.489033042620349 + ], + [ + 9.929578632864832, + 13.013317100730887 + ], + [ + 10.810629481431306, + 15.656509978352517 + ], + [ + 11.251175071675789, + 18.299702855974147 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "iooTUvCJjn", + "index": "b6d", + "seed": 1667061378 + }, + "1VkbZ4u499": { + "type": "brush", + "xywh": "[1166.3320692687987,-63.255304977858884,12.001201215598485,20.38340619914746]", + "points": [ + [ + 10.001201215598485, + 2 + ], + [ + 9.239176238463415, + 3.3335393496779204 + ], + [ + 6.953118748292354, + 7.715143725427538 + ], + [ + 5.619579398614405, + 10.382204983548888 + ], + [ + 5.048065026071754, + 11.715744333226837 + ], + [ + 4.286057490171061, + 13.049283682904758 + ], + [ + 3.714543117628182, + 14.382805591348188 + ], + [ + 2.5715143725426515, + 17.240377454061957 + ], + [ + 2, + 18.38340619914746 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "1VkbZ4u499", + "index": "b6e", + "seed": 321109056 + }, + "uFqjjuaOnd": { + "type": "brush", + "xywh": "[1164.6175261511705,-46.49087756952659,16.001801823397727,7.810090003206881]", + "points": [ + [ + 2, + 2 + ], + [ + 2.762007535900466, + 2.762007535900665 + ], + [ + 4.476550653528875, + 4.095546885578614 + ], + [ + 6.000600607799242, + 4.667061258121379 + ], + [ + 6.572114980341894, + 4.667061258121379 + ], + [ + 7.334122516242587, + 4.857571862713797 + ], + [ + 8.667661865920536, + 5.238575630664116 + ], + [ + 9.239176238463415, + 5.429086235256534 + ], + [ + 10.572715588141136, + 5.810090003206881 + ], + [ + 11.906237496584708, + 5.810090003206881 + ], + [ + 13.430287450855076, + 5.238575630664116 + ], + [ + 14.001801823397727, + 4.857571862713797 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "uFqjjuaOnd", + "index": "b6f", + "seed": 1308798557 + }, + "rTKTY3-fDw": { + "type": "brush", + "xywh": "[1171.0946774124986,-47.63390631461209,8.191111212391661,6.286057490171032]", + "points": [ + [ + 2, + 2 + ], + [ + 2.952535581727716, + 2.571514372542765 + ], + [ + 4.667078699355898, + 3.5240325130358485 + ], + [ + 6.191111212391661, + 4.286057490171032 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "rTKTY3-fDw", + "index": "b6g", + "seed": 717997295 + }, + "u1XotDBKlm": { + "type": "brush", + "xywh": "[1196.8128241769227,-65.73187307262234,9.52465056206961,17.33534117307579]", + "points": [ + [ + 3.714543117628182, + 2 + ], + [ + 4.095564326813019, + 3.143028745085502 + ], + [ + 5.429086235256364, + 6.191111212391718 + ], + [ + 6.3816218169840795, + 8.477168702562722 + ], + [ + 6.572114980341894, + 9.620197447648223 + ], + [ + 6.762625584934312, + 10.763226192733754 + ], + [ + 7.143629352884545, + 11.715744333226837 + ], + [ + 7.334139957477191, + 13.239794287497176 + ], + [ + 7.52465056206961, + 14.001801823397841 + ], + [ + 7.52465056206961, + 14.382823032582706 + ], + [ + 7.52465056206961, + 14.573316195940606 + ], + [ + 7.334139957477191, + 14.573316195940606 + ], + [ + 7.143629352884545, + 14.573316195940606 + ], + [ + 6.762625584934312, + 14.573316195940606 + ], + [ + 6.000600607799242, + 14.573316195940606 + ], + [ + 4.66707869935567, + 14.763826800533025 + ], + [ + 3.143028745085303, + 14.954337405125443 + ], + [ + 2, + 15.33534117307579 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "u1XotDBKlm", + "index": "b6h", + "seed": 1527581589 + }, + "J2APUGxfDa": { + "type": "brush", + "xywh": "[1194.1457629188012,-54.873099994309996,6.095546885578642,13.525251169868937]", + "points": [ + [ + 2, + 2 + ], + [ + 2.3810037679504603, + 3.9050537222206856 + ], + [ + 2.952518140493112, + 6.000600607799299 + ], + [ + 3.5240325130357633, + 8.858172470513068 + ], + [ + 3.5240325130357633, + 9.429686843055833 + ], + [ + 3.714543117628409, + 10.00120121559857 + ], + [ + 3.9050537222208277, + 10.572715588141335 + ], + [ + 4.095546885578642, + 11.144229960684072 + ], + [ + 4.095546885578642, + 11.334740565276519 + ], + [ + 4.095546885578642, + 11.525251169868937 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "J2APUGxfDa", + "index": "b6i", + "seed": 1462262301 + }, + "LXTKtXMWGT": { + "type": "brush", + "xywh": "[1194.717277291344,-53.53956064463205,5.14302874508553,11.620197447648223]", + "points": [ + [ + 2.9525181404928844, + 2 + ], + [ + 2.9525181404928844, + 3.3335393496779204 + ], + [ + 2.9525181404928844, + 5.619596839848953 + ], + [ + 2.9525181404928844, + 7.143629352884773 + ], + [ + 3.1430287450855303, + 7.715143725427538 + ], + [ + 3.1430287450855303, + 8.096147493377885 + ], + [ + 3.1430287450855303, + 7.715143725427538 + ], + [ + 2.9525181404928844, + 6.3816043757496175 + ], + [ + 2.76202497713507, + 5.238575630664087 + ], + [ + 2.381003767950233, + 4.476568094763422 + ], + [ + 2.1905106045924185, + 4.095546885578585 + ], + [ + 2, + 3.9050537222206856 + ], + [ + 2, + 3.714543117628267 + ], + [ + 2, + 4.857571862713769 + ], + [ + 2.381003767950233, + 7.524633120835119 + ], + [ + 3.1430287450855303, + 9.620197447648223 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "LXTKtXMWGT", + "index": "b6j", + "seed": 225921457 + }, + "ksKLQcJCMu": { + "type": "brush", + "xywh": "[1191.47870166068,-53.920564412582394,5,10.66766186592065]", + "points": [ + [ + 2.7620075359006933, + 2 + ], + [ + 2.381003767950233, + 2.762007535900665 + ], + [ + 2, + 4.476550653528932 + ], + [ + 2, + 5.810090003206852 + ], + [ + 2.381003767950233, + 7.334122516242701 + ], + [ + 2.952518140493112, + 8.66766186592065 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "ksKLQcJCMu", + "index": "b6k", + "seed": 372339608 + }, + "OabCj7h_5D": { + "type": "brush", + "xywh": "[1192.431219801173,-54.49207878512516,6.095546885578642,10.477151261328231]", + "points": [ + [ + 2, + 2 + ], + [ + 2.1905106045924185, + 3.33352190844343 + ], + [ + 2.5715143725426515, + 5.810090003206881 + ], + [ + 4.095546885578642, + 8.477151261328231 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "OabCj7h_5D", + "index": "b6l", + "seed": 1679229950 + }, + "QUVgZCzEsd": { + "type": "brush", + "xywh": "[1193.0027341737157,-51.062992549868625,8.953118748292354,10.477151261328231]", + "points": [ + [ + 2, + 2 + ], + [ + 2.571514372542879, + 3.33352190844343 + ], + [ + 4.286057490171061, + 6.000600607799299 + ], + [ + 5.810090003207051, + 7.334122516242701 + ], + [ + 6.953118748292354, + 8.477151261328231 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "QUVgZCzEsd", + "index": "b6m", + "seed": 1151757165 + }, + "EYvJOcSWJa": { + "type": "brush", + "xywh": "[1193.3837379416661,-49.538960036832776,7.6195968398490095,9.33413995747722]", + "points": [ + [ + 2, + 2 + ], + [ + 2.381021209184837, + 3.9050537222206856 + ], + [ + 2.5715143725426515, + 4.476568094763451 + ], + [ + 3.9050537222206003, + 6.191111212391718 + ], + [ + 4.857571862713712, + 6.7626255849344545 + ], + [ + 5.6195968398490095, + 7.33413995747722 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "EYvJOcSWJa", + "index": "b6n", + "seed": 99489098 + }, + "Anh4lrYNn9": { + "type": "brush", + "xywh": "[1192.8122235691233,-48.96744566429001,7.429086235256591,8.762625584934455]", + "points": [ + [ + 2, + 2 + ], + [ + 2.7620249771352974, + 3.714543117628267 + ], + [ + 3.333539349677949, + 4.857571862713769 + ], + [ + 4.286057490171061, + 5.619596839848953 + ], + [ + 5.429086235256591, + 6.7626255849344545 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "Anh4lrYNn9", + "index": "b6o", + "seed": 1094095616 + }, + "MvmWlRSMNX": { + "type": "brush", + "xywh": "[1193.5742485462586,-47.44341315125416,7.238575630664172,6.667078699355841]", + "points": [ + [ + 2, + 2 + ], + [ + 4.286057490171061, + 4.095564326813104 + ], + [ + 5.238575630664172, + 4.667078699355841 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "MvmWlRSMNX", + "index": "b6p", + "seed": 757701587 + }, + "-TQPvCuecZ": { + "type": "shape", + "xywh": "[861.1704388638423,-460.22601366120034,171.5436462431324,150.86648088967036]", + "shapeType": "ellipse", + "radius": 0, + "filled": true, + "fillColor": "--affine-palette-shape-yellow", + "strokeWidth": 4, + "strokeColor": "--affine-palette-line-yellow", + "strokeStyle": "solid", + "roughness": 2, + "id": "-TQPvCuecZ", + "index": "b6q", + "seed": 1735631317 + }, + "Bu61nIERZ-": { + "type": "brush", + "xywh": "[1059.0494001248194,-441.54888336417946,69.09469282454529,13.95564364271496]", + "points": [ + [ + 2, + 11.95564364271496 + ], + [ + 8.892376765673816, + 10.424027732414856 + ], + [ + 37.99349973866856, + 3.5316509667412106 + ], + [ + 52.544096281606926, + 2 + ], + [ + 56.37317111379821, + 2 + ], + [ + 67.09469282454529, + 4.297458921891234 + ] + ], + "color": "--affine-palette-line-yellow", + "lineWidth": 4, + "id": "Bu61nIERZ-", + "index": "b6r", + "seed": 1304224718 + }, + "N0rqqXSjH3": { + "type": "brush", + "xywh": "[1087.3847501991052,-374.9225746293341,57.60732810220702,10.126568810523622]", + "points": [ + [ + 2, + 2 + ], + [ + 10.42399267597375, + 3.5316509667412106 + ], + [ + 27.272048140803463, + 6.5949178437824685 + ], + [ + 45.65171951593334, + 8.126568810523622 + ], + [ + 49.48079434812439, + 8.126568810523622 + ], + [ + 55.60732810220702, + 8.126568810523622 + ] + ], + "color": "--affine-palette-line-yellow", + "lineWidth": 4, + "id": "N0rqqXSjH3", + "index": "b6s", + "seed": 489177967 + }, + "aI_S8qjShN": { + "type": "brush", + "xywh": "[1067.4733928007931,-313.6569916934213,47.65171951593311,38.461883828368286]", + "points": [ + [ + 2, + 2 + ], + [ + 6.5949178437824685, + 4.297458921891234 + ], + [ + 21.91135739831202, + 15.784753531347349 + ], + [ + 36.4618838283684, + 28.037856095953487 + ], + [ + 39.52518576185071, + 30.33531501784472 + ], + [ + 43.35426059404199, + 34.93023286162713 + ], + [ + 45.65171951593311, + 36.461883828368286 + ] + ], + "color": "--affine-palette-line-yellow", + "lineWidth": 4, + "id": "aI_S8qjShN", + "index": "b6t", + "seed": 1504988946 + }, + "2yJWZcFV9U": { + "type": "brush", + "xywh": "[1025.3533242516012,-285.32167667557655,26.20878126376215,54.544096281606755]", + "points": [ + [ + 2, + 2 + ], + [ + 4.297458921891348, + 6.594917843782412 + ], + [ + 11.955643642714904, + 24.20878126376209 + ], + [ + 18.84802040838872, + 38.759342750259464 + ], + [ + 20.379671375129874, + 41.82260962730072 + ], + [ + 24.20878126376215, + 52.544096281606755 + ] + ], + "color": "--affine-palette-line-yellow", + "lineWidth": 4, + "id": "2yJWZcFV9U", + "index": "b6u", + "seed": 1727003972 + }, + "yR8EmHbTwl": { + "type": "brush", + "xywh": "[970.214310126212,-263.87873842340554,6.297458921891234,57.60739821508912]", + "points": [ + [ + 3.5316509667411538, + 2 + ], + [ + 2, + 11.189835687564823 + ], + [ + 2.7658079551500805, + 29.569507062694584 + ], + [ + 4.297458921891234, + 44.120103605633005 + ], + [ + 4.297458921891234, + 47.18337048267426 + ], + [ + 4.297458921891234, + 54.84155520349799 + ], + [ + 4.297458921891234, + 55.60739821508912 + ] + ], + "color": "--affine-palette-line-yellow", + "lineWidth": 4, + "id": "yR8EmHbTwl", + "index": "b6v", + "seed": 217366119 + }, + "NhvwG4BQMh": { + "type": "brush", + "xywh": "[927.3283985654289,-262.3470874566644,13.189835687564937,53.77828832645673]", + "points": [ + [ + 11.189835687564937, + 2 + ], + [ + 8.892376765673703, + 12.721486654306034 + ], + [ + 5.0633019334823075, + 31.101158029435794 + ], + [ + 2.765843011591187, + 43.35426059404193 + ], + [ + 2.765843011591187, + 46.41752747108319 + ], + [ + 2.765843011591187, + 51.0124453148656 + ], + [ + 2.765843011591187, + 51.77828832645673 + ], + [ + 2, + 51.77828832645673 + ] + ], + "color": "--affine-palette-line-yellow", + "lineWidth": 4, + "id": "NhvwG4BQMh", + "index": "b6w", + "seed": 1329973834 + }, + "w1H7NkFYl8": { + "type": "brush", + "xywh": "[893.6323577486518,-265.41035433370564,8.594917843782355,61.43647304728046]", + "points": [ + [ + 6.594917843782355, + 2 + ], + [ + 5.8290748321912815, + 6.594917843782412 + ], + [ + 5.8290748321912815, + 28.803664051103453 + ], + [ + 4.297458921891234, + 41.82260962730072 + ], + [ + 3.5316159103000473, + 47.183335426233214 + ], + [ + 2, + 57.90482208053925 + ], + [ + 2, + 59.43647304728046 + ] + ], + "color": "--affine-palette-line-yellow", + "lineWidth": 4, + "id": "w1H7NkFYl8", + "index": "b6x", + "seed": 910613227 + }, + "0czhGhqZSx": { + "type": "brush", + "xywh": "[1032.245701017275,-508.1751920990248,39.22769178351814,29.272048140803463]", + "points": [ + [ + 2, + 27.272048140803463 + ], + [ + 8.892376765673589, + 22.677130297020994 + ], + [ + 24.974589218912115, + 10.42401020419436 + ], + [ + 37.22769178351814, + 2 + ] + ], + "color": "--affine-palette-line-yellow", + "lineWidth": 4, + "id": "0czhGhqZSx", + "index": "b6y", + "seed": 1591045967 + }, + "0_lEiNrE4c": { + "type": "brush", + "xywh": "[2432.799407524681,1452.7059747189228,17.38242223961879,15.152018533015507]", + "points": [ + [ + 4.97389429764371, + 12.408527941974626 + ], + [ + 10.921614826412679, + 7.947720528768514 + ], + [ + 14.638931648577909, + 4.9738942976439375 + ], + [ + 15.38242223961879, + 3.486913115562402 + ], + [ + 15.38242223961879, + 2.7434905910408816 + ], + [ + 15.38242223961879, + 2 + ], + [ + 14.638931648577909, + 2 + ], + [ + 13.152018533015507, + 2 + ], + [ + 10.921614826412679, + 2 + ], + [ + 8.691211119809395, + 2 + ], + [ + 6.460807413206112, + 2 + ], + [ + 5.71731682216523, + 2 + ], + [ + 4.230403706603283, + 2 + ], + [ + 3.486913115562402, + 2 + ], + [ + 4.230403706603283, + 2 + ], + [ + 4.97389429764371, + 2.7434905910408816 + ], + [ + 6.460807413206112, + 4.230403706603056 + ], + [ + 8.691211119809395, + 6.460807413206112 + ], + [ + 9.434701710850277, + 8.691211119809395 + ], + [ + 10.178124235371797, + 9.434701710850277 + ], + [ + 10.921614826412679, + 10.921614826412451 + ], + [ + 10.921614826412679, + 12.408527941974626 + ], + [ + 9.434701710850277, + 12.408527941974626 + ], + [ + 8.691211119809395, + 12.408527941974626 + ], + [ + 8.691211119809395, + 11.665105417453333 + ], + [ + 6.460807413206112, + 10.921614826412451 + ], + [ + 4.230403706603283, + 9.434701710850277 + ], + [ + 2.7434905910408816, + 7.947720528768514 + ], + [ + 2, + 7.947720528768514 + ], + [ + 4.230403706603283, + 9.434701710850277 + ], + [ + 7.2042980042469935, + 10.921614826412451 + ], + [ + 8.691211119809395, + 11.665105417453333 + ], + [ + 9.434701710850277, + 12.408527941974626 + ], + [ + 10.921614826412679, + 13.152018533015507 + ], + [ + 11.66510541745356, + 13.152018533015507 + ], + [ + 10.921614826412679, + 11.665105417453333 + ], + [ + 9.434701710850277, + 10.921614826412451 + ], + [ + 8.691211119809395, + 10.178124235371797 + ], + [ + 7.947720528768514, + 9.434701710850277 + ], + [ + 7.947720528768514, + 8.691211119809395 + ], + [ + 8.691211119809395, + 8.691211119809395 + ], + [ + 9.434701710850277, + 9.434701710850277 + ], + [ + 10.921614826412679, + 10.178124235371797 + ], + [ + 10.921614826412679, + 10.921614826412451 + ] + ], + "color": "--affine-palette-line-purple", + "lineWidth": 4, + "id": "0_lEiNrE4c", + "index": "b5M", + "seed": 175045379 + }, + "kb_HfQzTZx": { + "type": "brush", + "xywh": "[2522.759046379847,1421.480322826479,15.895441057537028,15.895509124056389]", + "points": [ + [ + 5.717384888684592, + 2 + ], + [ + 6.460807413206112, + 5.71731682216523 + ], + [ + 7.204229937727632, + 7.947720528768514 + ], + [ + 7.204229937727632, + 10.921614826412451 + ], + [ + 7.204229937727632, + 12.408527941974626 + ], + [ + 7.204229937727632, + 13.152018533015507 + ], + [ + 7.204229937727632, + 13.895509124056389 + ], + [ + 7.204229937727632, + 13.152018533015507 + ], + [ + 5.717384888684592, + 12.408527941974626 + ], + [ + 4.973826231124349, + 10.921614826412451 + ], + [ + 4.230403706603283, + 10.921614826412451 + ], + [ + 3.486981182081763, + 9.434701710850277 + ], + [ + 2, + 8.691211119809395 + ], + [ + 2, + 7.2042980042469935 + ], + [ + 2, + 6.460807413206112 + ], + [ + 2, + 5.71731682216523 + ], + [ + 3.486981182081763, + 6.460807413206112 + ], + [ + 6.460807413206112, + 10.921614826412451 + ], + [ + 7.204229937727632, + 11.665105417453105 + ], + [ + 7.204229937727632, + 12.408527941974626 + ], + [ + 7.204229937727632, + 11.665105417453105 + ], + [ + 7.204229937727632, + 10.921614826412451 + ], + [ + 7.204229937727632, + 9.434701710850277 + ], + [ + 7.204229937727632, + 8.691211119809395 + ], + [ + 7.204229937727632, + 7.2042980042469935 + ], + [ + 7.204229937727632, + 6.460807413206112 + ], + [ + 7.947788595287875, + 6.460807413206112 + ], + [ + 9.434633644330916, + 6.460807413206112 + ], + [ + 10.178192301891158, + 6.460807413206112 + ], + [ + 10.921614826412679, + 6.460807413206112 + ], + [ + 11.665037350934199, + 7.2042980042469935 + ], + [ + 13.152018533015507, + 8.691211119809395 + ], + [ + 13.895441057537028, + 10.17812423537157 + ], + [ + 13.895441057537028, + 10.921614826412451 + ], + [ + 13.152018533015507, + 11.665105417453105 + ], + [ + 11.665037350934199, + 11.665105417453105 + ], + [ + 10.921614826412679, + 11.665105417453105 + ], + [ + 10.178192301891158, + 10.921614826412451 + ], + [ + 9.434633644330916, + 10.17812423537157 + ], + [ + 7.947788595287875, + 9.434701710850277 + ], + [ + 7.204229937727632, + 7.947720528768514 + ], + [ + 7.204229937727632, + 7.2042980042469935 + ] + ], + "color": "--affine-palette-line-purple", + "lineWidth": 4, + "id": "kb_HfQzTZx", + "index": "b5N", + "seed": 1562620217 + }, + "ClKromwpTE": { + "type": "brush", + "xywh": "[2453.438838887268,1484.1503017020839,166.09588720217334,128.56882473140786]", + "points": [ + [ + 4.084802340883016, + 126.56882473140786 + ], + [ + 3.0424011704417353, + 118.75069665792898 + ], + [ + 2.521176726187605, + 115.1022687023501 + ], + [ + 2.521176726187605, + 107.8053173550586 + ], + [ + 2, + 104.6780661256671 + ], + [ + 2, + 97.38116249644236 + ], + [ + 2, + 92.69028565235499 + ], + [ + 3.0424011704417353, + 84.35093313462198 + ], + [ + 4.606026785137601, + 73.92677827600573 + ], + [ + 7.2120535702747475, + 64.545024587831 + ], + [ + 11.38175368817474, + 54.64204645540235 + ], + [ + 15.551406088007752, + 45.78151721148174 + ], + [ + 18.678657317399484, + 41.09064036739437 + ], + [ + 23.890758605740757, + 32.23011112347399 + ], + [ + 29.624036620269635, + 25.454384220436623 + ], + [ + 34.83613790861136, + 19.721106205907745 + ], + [ + 39.527014752698506, + 15.030229361820375 + ], + [ + 43.696667152531965, + 11.38175368817474 + ], + [ + 47.866319552364985, + 8.775726903037366 + ], + [ + 51.51479522601085, + 6.69087684408737 + ], + [ + 54.642046455402124, + 5.127251229391504 + ], + [ + 55.68444762584386, + 4.606026785137374 + ], + [ + 58.29052212904798, + 3.563625614695866 + ], + [ + 60.37532446993101, + 2.521224444254358 + ], + [ + 61.41777335843972, + 2 + ], + [ + 61.93895008462687, + 2 + ], + [ + 62.460174528881, + 2 + ], + [ + 62.981398973135576, + 2 + ], + [ + 63.50257569932273, + 2 + ], + [ + 65.0662013140186, + 2 + ], + [ + 68.19345254340988, + 2.521224444254358 + ], + [ + 72.88432938749747, + 3.563625614695866 + ], + [ + 80.18128073478874, + 7.2121012883415005 + ], + [ + 88.52058553445524, + 12.424154858616248 + ], + [ + 97.38116249644236, + 18.157480591212106 + ], + [ + 105.19929056992123, + 24.411983049995115 + ], + [ + 109.36894296975424, + 27.01800983513249 + ], + [ + 116.665846598979, + 31.70888667921986 + ], + [ + 122.39917233157485, + 36.399763523307 + ], + [ + 127.09004917566199, + 40.56941592314024 + ], + [ + 131.25974929356198, + 43.696667152531745 + ], + [ + 134.90817724914086, + 47.34514282617761 + ], + [ + 144.2899309373156, + 55.68449534391061 + ], + [ + 146.37473327819862, + 58.290522129047986 + ], + [ + 152.10805901079448, + 65.06624903208535 + ], + [ + 154.1928613516775, + 67.6722758172225 + ], + [ + 157.84138474339034, + 72.36315266130987 + ], + [ + 159.92618708427335, + 76.01162833495573 + ], + [ + 162.01098942515637, + 79.13887956434723 + ], + [ + 163.05343831366508, + 81.22368190523048 + ], + [ + 164.09588720217334, + 81.74490634948461 + ], + [ + 164.09588720217334, + 82.78730751992612 + ] + ], + "color": "--affine-palette-line-purple", + "lineWidth": 4, + "id": "ClKromwpTE", + "index": "b5O", + "seed": 43417698 + }, + "_ZBzALDynY": { + "type": "brush", + "xywh": "[2458.650892457543,1567.5436837252141,166.09588720217334,55.59964528496084]", + "points": [ + [ + 2, + 53.59964528496084 + ], + [ + 5.648475673645862, + 53.59964528496084 + ], + [ + 16.593854976516013, + 53.078420840706485 + ], + [ + 25.454384220436623, + 52.03601967026498 + ], + [ + 34.31496118242376, + 50.47239405556911 + ], + [ + 43.6967148705985, + 48.908768440873246 + ], + [ + 52.557244114519115, + 47.34514282617761 + ], + [ + 61.93899780269385, + 45.78151721148197 + ], + [ + 70.27835032042685, + 43.69666715253197 + ], + [ + 79.66010400860159, + 41.611817093581976 + ], + [ + 84.35098085268874, + 40.04819147888611 + ], + [ + 93.73273454086348, + 37.963389138003095 + ], + [ + 102.0720393405295, + 35.8785390790531 + ], + [ + 109.89016741400837, + 34.31491346435723 + ], + [ + 117.70829548748723, + 31.70888667921986 + ], + [ + 124.48407010859182, + 29.624036620269866 + ], + [ + 130.73857256737483, + 27.01800983513249 + ], + [ + 136.47180286383673, + 24.411935331928362 + ], + [ + 139.07787736704086, + 23.369534161486854 + ], + [ + 143.24757748494085, + 21.284684102536858 + ], + [ + 146.89600544051973, + 18.678657317399484 + ], + [ + 152.6293311731156, + 15.030181643753622 + ], + [ + 159.92618708427335, + 8.254502458783236 + ], + [ + 160.44745924659446, + 7.2120535702747475 + ], + [ + 161.48981269896922, + 6.69087684408737 + ], + [ + 162.53226158747748, + 5.127251229391732 + ], + [ + 163.05343831366508, + 4.084802340883243 + ], + [ + 163.5747104759862, + 3.563625614695866 + ], + [ + 164.09588720217334, + 3.0424011704417353 + ], + [ + 164.09588720217334, + 2 + ] + ], + "color": "--affine-palette-line-purple", + "lineWidth": 4, + "id": "_ZBzALDynY", + "index": "b5P", + "seed": 1806250475 + }, + "wTnu7snGTf": { + "type": "brush", + "xywh": "[2440.408609525448,1461.2171419259016,29.018009835132034,76.96917944644701]", + "points": [ + [ + 6.690876844087143, + 2 + ], + [ + 5.127251229391277, + 7.21205357027452 + ], + [ + 3.563625614695411, + 13.987780473311886 + ], + [ + 2.5212244442541305, + 19.199881761653387 + ], + [ + 2, + 21.80590854679076 + ], + [ + 2, + 24.933159776182265 + ], + [ + 2, + 30.145261064523766 + ], + [ + 3.0424011704412806, + 36.399763523307 + ], + [ + 3.563625614695411, + 42.13304153783588 + ], + [ + 4.606026785137146, + 47.34514282617738 + ], + [ + 6.169652399833012, + 51.51479522601039 + ], + [ + 6.690876844087143, + 53.59964528496038 + ], + [ + 8.775726903037139, + 58.29052212904776 + ], + [ + 10.860529243920155, + 61.93895008462664 + ], + [ + 12.945379302870151, + 65.5874257582725 + ], + [ + 15.551406088007752, + 68.19345254340988 + ], + [ + 18.15748059121188, + 70.799527046614 + ], + [ + 20.763507376349025, + 72.36315266130987 + ], + [ + 22.84835743529902, + 73.9267782760055 + ], + [ + 23.890758605740757, + 74.44795500219288 + ], + [ + 25.454384220436623, + 74.96917944644701 + ], + [ + 26.496785390877903, + 74.96917944644701 + ], + [ + 27.018009835132034, + 74.96917944644701 + ] + ], + "color": "--affine-palette-line-purple", + "lineWidth": 4, + "id": "wTnu7snGTf", + "index": "b5Q", + "seed": 353350136 + }, + "0w9mBYbIZA": { + "type": "brush", + "xywh": "[2535.7897720218903,1433.0718570023441,47.781517211481514,93.12668389669261]", + "points": [ + [ + 2, + 2 + ], + [ + 6.690876844087143, + 3.563625614695866 + ], + [ + 8.775726903037139, + 5.127251229391504 + ], + [ + 18.15748059121188, + 10.339328658699742 + ], + [ + 20.763507376349025, + 12.424178717649738 + ], + [ + 22.84835743529902, + 13.987804332345377 + ], + [ + 27.018009835132034, + 17.63625614695775 + ], + [ + 30.666485508777896, + 21.80593240582425 + ], + [ + 34.31491346435678, + 26.496809249911625 + ], + [ + 37.96338913800264, + 33.27253615294899 + ], + [ + 40.048239196952636, + 37.442188552782 + ], + [ + 41.6118648116485, + 41.61184095261524 + ], + [ + 43.17549042634437, + 46.30271779670238 + ], + [ + 43.69666715253152, + 48.908792299906736 + ], + [ + 44.73911604103978, + 53.07844469973975 + ], + [ + 45.781517211481514, + 56.20569592913125 + ], + [ + 45.781517211481514, + 59.332947158522984 + ], + [ + 45.781517211481514, + 60.89657277321863 + ], + [ + 45.781517211481514, + 64.02382400261013 + ], + [ + 45.781517211481514, + 67.15107523200186 + ], + [ + 45.781517211481514, + 70.27832646139336 + ], + [ + 45.781517211481514, + 73.40557769078487 + ], + [ + 45.260292767227384, + 77.05405336443073 + ], + [ + 43.17549042634437, + 81.22370576426374 + ], + [ + 42.13304153783565, + 83.30855582321374 + ], + [ + 41.6118648116485, + 85.39335816409675 + ], + [ + 40.569415923139786, + 87.47820822304675 + ], + [ + 40.048239196952636, + 88.52060939348848 + ], + [ + 39.527014752698506, + 90.08423500818412 + ], + [ + 38.48461358225677, + 90.60545945243848 + ], + [ + 38.48461358225677, + 91.12668389669261 + ] + ], + "color": "--affine-palette-line-purple", + "lineWidth": 4, + "id": "0w9mBYbIZA", + "index": "b5R", + "seed": 907857809 + }, + "fLWczUkw6x": { + "type": "brush", + "xywh": "[2516.6582144323397,1612.4734835163072,15.105804216339038,57.099677049169486]", + "points": [ + [ + 2, + 2 + ], + [ + 2, + 5.817613248806083 + ], + [ + 2.3470673040696965, + 10.329361105752014 + ], + [ + 2.6941028341489073, + 16.229314630996214 + ], + [ + 3.388237442288755, + 19.00575774158324 + ], + [ + 3.735272972367966, + 21.782232626161203 + ], + [ + 4.776443110587024, + 28.0292534554751 + ], + [ + 6.1646805528757795, + 33.58213967664915 + ], + [ + 6.858783387024687, + 36.01154725715742 + ], + [ + 8.594088133384048, + 41.91150078240139 + ], + [ + 10.329361105752014, + 46.770315943417245 + ], + [ + 11.370531243971072, + 50.58792919222333 + ], + [ + 13.105804216339038, + 55.099677049169486 + ] + ], + "color": "--affine-palette-line-purple", + "lineWidth": 4, + "id": "fLWczUkw6x", + "index": "b5S", + "seed": 1673498523 + }, + "Hi2UpFBeuU": { + "type": "brush", + "xywh": "[2548.5874214130586,1608.3088029634312,18.229346404987155,58.4878827174673]", + "points": [ + [ + 2, + 2 + ], + [ + 2, + 5.817613248806083 + ], + [ + 2, + 8.941123663463031 + ], + [ + 3.0411701382190586, + 12.758736912269114 + ], + [ + 4.77647488457842, + 17.617552073285196 + ], + [ + 6.164680552876234, + 22.823402764380035 + ], + [ + 7.899985299235141, + 28.376288985554083 + ], + [ + 8.9411554374542, + 31.152763870132276 + ], + [ + 10.329361105752469, + 36.35861456122734 + ], + [ + 11.370531243971527, + 40.176227810033424 + ], + [ + 12.758768686260282, + 43.99384105883928 + ], + [ + 13.79993882447934, + 48.50558891578544 + ], + [ + 14.8411089626984, + 51.629099330442386 + ], + [ + 15.535211796847307, + 54.05850691095043 + ], + [ + 15.882279100917458, + 55.099677049169486 + ], + [ + 16.229346404987155, + 56.4878827174673 + ] + ], + "color": "--affine-palette-line-purple", + "lineWidth": 4, + "id": "Hi2UpFBeuU", + "index": "b5T", + "seed": 1918482090 + }, + "nc2y7u7TRo": { + "type": "brush", + "xywh": "[2520.8228949852155,1661.7555155426796,17.882279100917458,20.658722211504028]", + "points": [ + [ + 12.06463407811998, + 2.694134608140075 + ], + [ + 8.941123663463259, + 4.776474884577965 + ], + [ + 6.858783387025142, + 6.164680552876007 + ], + [ + 5.123510414657176, + 7.899985299234913 + ], + [ + 4.082340276438117, + 8.941155437453972 + ], + [ + 2.3470673040701513, + 12.758768686260055 + ], + [ + 2, + 13.799938824479113 + ], + [ + 2, + 15.53521179684708 + ], + [ + 2.3470673040701513, + 16.923449239136062 + ], + [ + 3.0411701382190586, + 17.61755207328497 + ], + [ + 3.735272972367966, + 18.311686681425044 + ], + [ + 5.470577718727327, + 18.658722211504028 + ], + [ + 7.8999535252442, + 17.61755207328497 + ], + [ + 9.982293801682317, + 16.576381935066138 + ], + [ + 12.06463407811998, + 14.841108962697945 + ], + [ + 13.105804216339038, + 13.799938824479113 + ], + [ + 14.8411089626984, + 10.676428409821938 + ], + [ + 15.882279100917458, + 7.552917995164989 + ], + [ + 15.882279100917458, + 4.776474884577965 + ], + [ + 15.188144492777155, + 3.3882374422889825 + ], + [ + 14.146974354558097, + 2.347067304069924 + ], + [ + 12.411701382190131, + 2 + ], + [ + 11.370531243971072, + 2 + ] + ], + "color": "--affine-palette-line-purple", + "lineWidth": 4, + "id": "nc2y7u7TRo", + "index": "b5U", + "seed": 898022918 + }, + "WoPuCv0HRz": { + "type": "brush", + "xywh": "[2556.916782518811,1661.0614127085305,18.57638193506591,18.229314630996214]", + "points": [ + [ + 8.941155437453745, + 3.735272972367966 + ], + [ + 5.470577718726872, + 6.511747856946158 + ], + [ + 2.694134608139848, + 10.329361105752014 + ], + [ + 2, + 12.06463407811998 + ], + [ + 2, + 13.45287152040919 + ], + [ + 2.694134608139848, + 14.494041658628248 + ], + [ + 3.7353047463589064, + 15.188144492777155 + ], + [ + 6.1646805528757795, + 15.88227910091723 + ], + [ + 8.594088133384048, + 16.229314630996214 + ], + [ + 11.370531243971072, + 15.88227910091723 + ], + [ + 12.758768686259828, + 15.53521179684708 + ], + [ + 14.494041658627793, + 14.146974354558097 + ], + [ + 15.535211796846852, + 12.411701382190131 + ], + [ + 16.229346404987155, + 9.635258271603107 + ], + [ + 16.57638193506591, + 7.2058506910950655 + ], + [ + 16.229346404987155, + 4.776443110587024 + ], + [ + 15.188176266768096, + 3.735272972367966 + ], + [ + 14.494041658627793, + 2.6941028341491347 + ], + [ + 13.105835990329979, + 2 + ], + [ + 12.411701382189676, + 2 + ], + [ + 11.370531243971072, + 2 + ] + ], + "color": "--affine-palette-line-purple", + "lineWidth": 4, + "id": "WoPuCv0HRz", + "index": "b5V", + "seed": 1639691852 + }, + "USf-tNzasM": { + "type": "brush", + "xywh": "[2477.834721556139,1537.6671216244983,37.58766903829155,39.11437491391257]", + "points": [ + [ + 5.053429223070907, + 2 + ], + [ + 5.816790896795737, + 2.3816721009479807 + ], + [ + 9.633564321762606, + 4.0992227629574245 + ], + [ + 15.93125008246352, + 6.770979885080124 + ], + [ + 18.221317631809143, + 7.725168873364737 + ], + [ + 24.519003392510513, + 10.015236422710586 + ], + [ + 27.954104716528946, + 11.541942298331378 + ], + [ + 31.007533939599853, + 12.686976073004416 + ], + [ + 32.152567714272664, + 13.068665645781266 + ], + [ + 33.870118376282335, + 13.641182533117671 + ], + [ + 35.015152150955146, + 14.022854634065652 + ], + [ + 35.58766903829155, + 14.404526735013633 + ], + [ + 35.58766903829155, + 14.595371521402285 + ], + [ + 35.58766903829155, + 14.977043622350266 + ], + [ + 34.633462578178296, + 15.740405296075096 + ], + [ + 32.34339502883222, + 17.648800744472965 + ], + [ + 30.625844366823003, + 18.793834519145776 + ], + [ + 27.19074304280411, + 21.46557416943983 + ], + [ + 23.183124831448822, + 23.94648650517388 + ], + [ + 18.79383451914555, + 26.618226155467937 + ], + [ + 13.832027319505869, + 29.862482693098173 + ], + [ + 9.44271953537418, + 32.53423981522087 + ], + [ + 5.6259461104073125, + 35.01513467912628 + ], + [ + 3.145033774672811, + 36.541858026576165 + ], + [ + 2, + 37.11437491391257 + ] + ], + "color": "--affine-palette-line-purple", + "lineWidth": 4, + "id": "USf-tNzasM", + "index": "b5W", + "seed": 953924125 + }, + "xk7ZrYqOZ4": { + "type": "brush", + "xywh": "[2475.162981905845,1553.3159223689713,37.01513467912628,10.679357861649123]", + "points": [ + [ + 2, + 8.679357861649123 + ], + [ + 3.5267058756212464, + 8.297685760700915 + ], + [ + 8.67935786164935, + 6.961807199639679 + ], + [ + 12.496148758445088, + 6.389290312303274 + ], + [ + 14.213699420454304, + 6.198445525914849 + ], + [ + 16.3129221834115, + 5.816773424966868 + ], + [ + 21.84724627038804, + 5.053411751241811 + ], + [ + 26.99989825641614, + 4.0992227629574245 + ], + [ + 30.053327479486597, + 3.335861089232367 + ], + [ + 32.15255024244425, + 2.7633442018959613 + ], + [ + 33.106756702557504, + 2.3816721009479807 + ], + [ + 34.44261779178987, + 2 + ], + [ + 35.01513467912628, + 2 + ] + ], + "color": "--affine-palette-line-purple", + "lineWidth": 4, + "id": "xk7ZrYqOZ4", + "index": "b5X", + "seed": 941503918 + }, + "pVWqHrvzmZ": { + "type": "brush", + "xywh": "[2524.7811063177287,1529.651885201788,38.73270281296436,29.954104716529173]", + "points": [ + [ + 25.85488195357175, + 2 + ], + [ + 23.3739696178377, + 4.0992227629574245 + ], + [ + 18.221317631809598, + 9.061047434425973 + ], + [ + 14.213699420454304, + 12.686976073004189 + ], + [ + 12.496148758444633, + 14.404526735013633 + ], + [ + 9.0610474344262, + 17.457955958084312 + ], + [ + 6.198462997743718, + 19.74802350743016 + ], + [ + 3.5267233474496607, + 22.03809105677601 + ], + [ + 2.190844786388425, + 22.992280045060625 + ], + [ + 2, + 23.18312483144905 + ], + [ + 2.190844786388425, + 23.373969617837474 + ], + [ + 3.335878561061236, + 23.56479693239703 + ], + [ + 4.290067549346077, + 23.56479693239703 + ], + [ + 7.343496772416529, + 23.56479693239703 + ], + [ + 11.351114983771822, + 23.755641718785455 + ], + [ + 15.93125008246352, + 23.94648650517388 + ], + [ + 20.8930572821032, + 24.519003392510285 + ], + [ + 25.664037167183324, + 25.091520279846918 + ], + [ + 27.9541047165294, + 25.4731923807949 + ], + [ + 31.770878141495814, + 26.427381369079285 + ], + [ + 34.82430736456672, + 27.190743042804115 + ], + [ + 36.73270281296436, + 27.954104716529173 + ] + ], + "color": "--affine-palette-line-purple", + "lineWidth": 4, + "id": "pVWqHrvzmZ", + "index": "b5Y", + "seed": 1612042689 + }, + "mIALZeG_Rq": { + "type": "brush", + "xywh": "[2530.1246030901452,1543.0106183969149,42.54947623793123,9.915996187924293]", + "points": [ + [ + 2, + 7.534324086976312 + ], + [ + 2.954188988284386, + 7.725168873364737 + ], + [ + 6.389290312303274, + 7.915996187924293 + ], + [ + 13.832009847677455, + 7.343479300587887 + ], + [ + 19.557178721041964, + 6.198445525914849 + ], + [ + 24.90067549345849, + 5.053411751242038 + ], + [ + 32.15255024244425, + 3.526705875621019 + ], + [ + 35.96934113923953, + 2.7633442018961887 + ], + [ + 39.213597676869995, + 2.190827314559783 + ], + [ + 40.54947623793123, + 2 + ] + ], + "color": "--affine-palette-line-purple", + "lineWidth": 4, + "id": "mIALZeG_Rq", + "index": "b5Z", + "seed": 550631409 + }, + "JUsmRjwfVD": { + "type": "brush", + "xywh": "[2517.7200763551314,1566.102121204933,21.557178721041964,34.91591191616908]", + "points": [ + [ + 3.5267058756212464, + 21.465574169439833 + ], + [ + 2.954188988284841, + 19.557196192870833 + ], + [ + 2.3816721009484354, + 18.030472845420945 + ], + [ + 2, + 14.02285463406588 + ], + [ + 2.1908273145600106, + 10.206081209099011 + ], + [ + 2.3816721009484354, + 9.061047434425973 + ], + [ + 3.1450337746732657, + 6.961824671468776 + ], + [ + 3.908377976569227, + 5.244274009459332 + ], + [ + 5.6259286385784435, + 3.1450337746730384 + ], + [ + 11.160270197383397, + 2 + ], + [ + 14.02285463406588, + 3.335878561061463 + ], + [ + 15.740405296075096, + 5.6259461104073125 + ], + [ + 18.412144946369153, + 12.877820859392841 + ], + [ + 18.98466183370556, + 16.69461175618858 + ], + [ + 19.36633393465354, + 18.793834519146003 + ], + [ + 19.557178721041964, + 22.419780629553088 + ], + [ + 19.36633393465354, + 25.854881953571976 + ], + [ + 18.793817047317134, + 28.71746639025423 + ], + [ + 17.839628059032748, + 30.435017052263674 + ], + [ + 17.076266385307918, + 31.77087814149604 + ], + [ + 16.12207739702353, + 32.53423981522087 + ], + [ + 14.595371521402285, + 32.91591191616908 + ], + [ + 13.068648173952624, + 32.725084601609524 + ], + [ + 11.351097511943408, + 31.961722927884466 + ], + [ + 9.251874748985756, + 30.24417226587525 + ], + [ + 6.961807199640134, + 26.809070941856362 + ], + [ + 5.053411751242038, + 22.992297516889494 + ], + [ + 2.3816721009484354, + 14.40454420684273 + ] + ], + "color": "--affine-palette-line-purple", + "lineWidth": 4, + "id": "JUsmRjwfVD", + "index": "b5a", + "seed": 407818161 + }, + "OPzi2nhJWc": { + "type": "brush", + "xywh": "[2538.719303837619,1554.3168680434264,81.65754394099167,89.85259938173431]", + "points": [ + [ + 2, + 2 + ], + [ + 5.121936090030886, + 4.341434203797689 + ], + [ + 9.024302611393068, + 7.073119350711977 + ], + [ + 11.755952030856863, + 9.804786633900903 + ], + [ + 14.487672905221642, + 13.316937939597436 + ], + [ + 16.04860522278659, + 15.658372143395352 + ], + [ + 18.78025464224993, + 19.560756528483125 + ], + [ + 21.90219073228082, + 24.243624936078504 + ], + [ + 24.633840151744607, + 28.146027184891636 + ], + [ + 27.36556102610939, + 32.04841156997941 + ], + [ + 28.536278128008234, + 33.60936175126949 + ], + [ + 30.487425661238834, + 36.34104689818378 + ], + [ + 32.82885986503652, + 39.0727141813727 + ], + [ + 35.5605807394013, + 42.19463240767823 + ], + [ + 40.633664362662785, + 47.267733894664836 + ], + [ + 45.706747985924274, + 52.34083538165168 + ], + [ + 51.95062016598558, + 58.584671834262515 + ], + [ + 55.85298668734822, + 62.48707408307563 + ], + [ + 59.75535320871086, + 66.38944060443805 + ], + [ + 64.04800640064013, + 69.90159191013481 + ], + [ + 67.95037292200277, + 73.02352800016547 + ], + [ + 71.4625242276993, + 76.14542836274563 + ], + [ + 74.19424510206454, + 78.87711350965992 + ], + [ + 75.75517741962904, + 80.82829677034101 + ], + [ + 78.09661162342672, + 83.95019713292118 + ], + [ + 79.26732872532557, + 86.68188227983546 + ], + [ + 79.65754394099167, + 87.85259938173431 + ] + ], + "color": "--affine-palette-line-blue", + "lineWidth": 4, + "id": "OPzi2nhJWc", + "index": "b5b", + "seed": 1466550751 + }, + "ac_kl-cvQK": { + "type": "brush", + "xywh": "[2548.865542539043,1558.6095033716306,74.6332413295986,83.99899600851472]", + "points": [ + [ + 2, + 2 + ], + [ + 3.560932317564948, + 3.95120112440668 + ], + [ + 6.292581737028286, + 7.853585509494451 + ], + [ + 11.75595203085686, + 15.658372143395354 + ], + [ + 16.43882043845224, + 20.731473630382197 + ], + [ + 17.609537540351084, + 22.29242381167205 + ], + [ + 22.682621163612566, + 27.36554316238426 + ], + [ + 25.414342037977352, + 29.70697736618195 + ], + [ + 28.14599145744114, + 32.048411569979635 + ], + [ + 29.316708559339986, + 32.82887772876211 + ], + [ + 31.658142763137676, + 34.78007885316856 + ], + [ + 33.999576966935365, + 37.51174613635749 + ], + [ + 37.511728272632354, + 41.02389744205425 + ], + [ + 40.633664362662785, + 44.926299690867374 + ], + [ + 44.14581566835932, + 49.999401177854 + ], + [ + 46.09696320159037, + 52.34083538165169 + ], + [ + 50.77983160918575, + 58.974904913653745 + ], + [ + 52.731050597317335, + 60.926088174335064 + ], + [ + 56.24320190301387, + 65.60895658193067 + ], + [ + 59.75535320871086, + 69.90160977386017 + ], + [ + 62.487002628174196, + 73.41376107955693 + ], + [ + 65.60893871820508, + 76.53566144213687 + ], + [ + 67.95037292200277, + 78.87709564593479 + ], + [ + 69.90159191013481, + 80.82827890661588 + ], + [ + 71.46252422769976, + 81.60878079284885 + ], + [ + 72.2430261139325, + 81.99899600851472 + ], + [ + 72.6332413295986, + 81.99899600851472 + ] + ], + "color": "--affine-palette-line-blue", + "lineWidth": 4, + "id": "ac_kl-cvQK", + "index": "b5c", + "seed": 774145730 + }, + "QasEppRUkM": { + "type": "brush", + "xywh": "[2475.5005803350796,1569.9264413112282,23.511975516615166,101.94998561638909]", + "points": [ + [ + 21.511975516615166, + 2 + ], + [ + 21.511975516615166, + 3.9511832606813186 + ], + [ + 20.73147363038197, + 9.804768770175542 + ], + [ + 19.560756528483125, + 17.219322324685436 + ], + [ + 19.170541312817477, + 19.170505585366755 + ], + [ + 18.780254642250384, + 21.511939789164444 + ], + [ + 17.999824210918632, + 23.463140913570896 + ], + [ + 17.60953754035154, + 25.414342037977576 + ], + [ + 16.438820438452694, + 29.31672642306535 + ], + [ + 15.26810333655385, + 33.21911080815312 + ], + [ + 14.877888120887746, + 34.38982791005196 + ], + [ + 14.097386234655005, + 37.901979215748725 + ], + [ + 13.316955803322799, + 42.194614543952866 + ], + [ + 12.92666913275616, + 45.70678371337499 + ], + [ + 12.92666913275616, + 49.21893501907152 + ], + [ + 12.536453917090057, + 53.51155248355054 + ], + [ + 12.146238701423954, + 57.80416994802954 + ], + [ + 12.146238701423954, + 60.14560415182723 + ], + [ + 12.146238701423954, + 65.6089744456558 + ], + [ + 11.75595203085686, + 76.53564357841151 + ], + [ + 10.585234928958016, + 83.95019713292118 + ], + [ + 8.63408739572742, + 89.41353169929926 + ], + [ + 7.853585509494679, + 91.75496590309695 + ], + [ + 6.682868407595834, + 93.70618489122876 + ], + [ + 4.341434203797689, + 96.82808525380892 + ], + [ + 2.7805018862331963, + 99.16951945760661 + ], + [ + 2, + 99.94998561638909 + ] + ], + "color": "--affine-palette-line-blue", + "lineWidth": 4, + "id": "QasEppRUkM", + "index": "b5d", + "seed": 540644783 + }, + "WeQ6-uPUcU": { + "type": "brush", + "xywh": "[2480.963950628908,1568.3654732662128,24.682621163612566,113.65719236282848]", + "points": [ + [ + 22.682621163612566, + 2 + ], + [ + 22.682621163612566, + 6.682868407595606 + ], + [ + 22.292405947946918, + 15.268139064004117 + ], + [ + 21.902190732280815, + 18.39003942658428 + ], + [ + 21.121688846048073, + 25.02410895858634 + ], + [ + 20.73147363038197, + 31.267927547472024 + ], + [ + 19.560756528483125, + 35.950795955067406 + ], + [ + 18.39003942658428, + 40.243431283271775 + ], + [ + 17.609537540351084, + 42.19463240767823 + ], + [ + 16.829035654118343, + 46.09703465649159 + ], + [ + 16.43882043845224, + 49.21893501907152 + ], + [ + 15.658318552219498, + 53.121337267884655 + ], + [ + 15.268103336553395, + 57.80420567548026 + ], + [ + 14.877888120887746, + 61.70657219684268 + ], + [ + 14.877888120887746, + 64.82850828687356 + ], + [ + 14.877888120887746, + 66.38944060443828 + ], + [ + 14.877888120887746, + 68.73087480823597 + ], + [ + 14.877888120887746, + 71.07230901203366 + ], + [ + 14.877888120887746, + 74.19424510206431 + ], + [ + 14.09738623465455, + 76.92589452152811 + ], + [ + 13.316884348421809, + 80.04783061155877 + ], + [ + 11.365736815191212, + 85.90141612105322 + ], + [ + 7.073083623261482, + 95.65736815191008 + ], + [ + 6.292581737028286, + 97.21833619692552 + ], + [ + 4.341434203797689, + 100.73048750262205 + ], + [ + 3.1707171018988447, + 103.85238786520222 + ], + [ + 2, + 106.5840730121165 + ], + [ + 2, + 108.9255072159142 + ], + [ + 2, + 110.48647526092964 + ], + [ + 2, + 111.65719236282848 + ] + ], + "color": "--affine-palette-line-blue", + "lineWidth": 4, + "id": "WeQ6-uPUcU", + "index": "b5e", + "seed": 661083165 + }, + "f0rq1haE2k": { + "type": "brush", + "xywh": "[2487.988253240301,1576.1702599001137,13.75595203085686,61.75537107243622]", + "points": [ + [ + 11.75595203085686, + 2 + ], + [ + 10.195019713292368, + 7.463352430103214 + ], + [ + 7.463298838927585, + 18.39003942658428 + ], + [ + 6.2925817370287405, + 26.19482606048541 + ], + [ + 5.121864635129896, + 30.87769446808079 + ], + [ + 3.1707171018988447, + 44.14583353208491 + ], + [ + 2.390215215666103, + 53.90178556294177 + ], + [ + 2, + 59.75537107243622 + ] + ], + "color": "--affine-palette-line-blue", + "lineWidth": 4, + "id": "f0rq1haE2k", + "index": "b5f", + "seed": 1070973809 + }, + "BtsxwZmM1Q": { + "type": "brush", + "xywh": "[2556.4541226654687,-38.34953763537584,103.63189820981097,20.40352156760781]", + "points": [ + [ + 5, + 15.403521567607811 + ], + [ + 8.715528838335558, + 15.403521567607811 + ], + [ + 25.0639781878167, + 14.66042260331875 + ], + [ + 30.26577298851089, + 13.917323639029632 + ], + [ + 40.66929455611853, + 13.174190657850431 + ], + [ + 46.61415430421175, + 12.431091693561314 + ], + [ + 52.5590820860848, + 10.944859748093052 + ], + [ + 63.705736634871755, + 10.201760783803934 + ], + [ + 74.85239118365871, + 8.715528838335615 + ], + [ + 83.02658184150914, + 7.972429874046497 + ], + [ + 89.71457457078168, + 7.2293309097574365 + ], + [ + 94.1732363902961, + 5.743098964289118 + ], + [ + 95.65943431887445, + 5 + ], + [ + 97.88876522863166, + 5 + ], + [ + 98.63189820981097, + 5 + ] + ], + "color": "--affine-palette-line-white", + "lineWidth": 10, + "id": "BtsxwZmM1Q", + "index": "b6z", + "seed": 940952658 + }, + "fk5hOd3-F4": { + "type": "brush", + "xywh": "[2592.8665502027666,-13.08379866375526,21.88971949618599,133.35629900094636]", + "points": [ + [ + 16.88971949618599, + 5 + ], + [ + 16.88971949618599, + 9.458661819514816 + ], + [ + 16.146654548786955, + 13.917323639029632 + ], + [ + 13.917323639029291, + 39.183062610650154 + ], + [ + 13.174190657850431, + 45.87105533992241 + ], + [ + 12.431057676671116, + 53.302181050373804 + ], + [ + 11.687992729272082, + 68.16436443749643 + ], + [ + 10.944859748092767, + 75.59549014794788 + ], + [ + 10.201726766913907, + 81.54034989604088 + ], + [ + 8.715528838335558, + 94.91633535458533 + ], + [ + 7.972395857156243, + 106.80612288455154 + ], + [ + 7.229330909757209, + 111.26478470406636 + ], + [ + 6.486197928578349, + 119.43897536191679 + ], + [ + 5, + 125.38386912689987 + ], + [ + 5, + 127.61323405354739 + ], + [ + 5, + 128.35629900094636 + ] + ], + "color": "--affine-palette-line-white", + "lineWidth": 10, + "id": "fk5hOd3-F4", + "index": "b70", + "seed": 517781008 + }, + "fKZjO4N_6x": { + "type": "brush", + "xywh": "[2648.599822946702,11.4388413435762,99.1732363902961,86.54034989604088]", + "points": [ + [ + 8.715528838335558, + 5 + ], + [ + 6.486197928577894, + 12.431091693561314 + ], + [ + 5, + 25.063978187816645 + ], + [ + 5, + 35.46753377231454 + ], + [ + 5.7430649473985795, + 40.66929455611847 + ], + [ + 7.972395857156243, + 52.5590480691946 + ], + [ + 13.174190657850431, + 63.705702617981615 + ], + [ + 16.146654548786955, + 68.16436443749643 + ], + [ + 20.60531636830183, + 75.59549014794788 + ], + [ + 24.320845206637387, + 80.05415196746264 + ], + [ + 27.29330909757391, + 81.54034989604088 + ], + [ + 31.00883793590947, + 80.05415196746264 + ], + [ + 35.46749975542434, + 76.33858911223695 + ], + [ + 38.439963646360866, + 71.87992729272213 + ], + [ + 47.35728728539061, + 59.2470407984668 + ], + [ + 56.27461092442036, + 43.64172443016497 + ], + [ + 62.219470672513125, + 30.26573897162052 + ], + [ + 66.678132492028, + 19.86221740401271 + ], + [ + 69.65059638296452, + 11.687992729272196 + ], + [ + 71.13679431154287, + 7.972429874046497 + ], + [ + 71.13679431154287, + 6.486231945468319 + ], + [ + 71.13679431154287, + 5.743098964289118 + ], + [ + 69.65059638296452, + 7.22933090975738 + ], + [ + 65.93499951084868, + 17.63288649425533 + ], + [ + 61.47633769133381, + 28.779541043042343 + ], + [ + 57.76080885299825, + 51.815949104905485 + ], + [ + 57.76080885299825, + 59.2470407984668 + ], + [ + 59.990139762755916, + 66.67816650891825 + ], + [ + 68.90746340178566, + 69.65059638296475 + ], + [ + 71.13679431154287, + 68.90749741867563 + ], + [ + 77.82478704081495, + 67.42126547320731 + ], + [ + 81.54031587915051, + 65.19193456344993 + ], + [ + 87.48524366102401, + 61.476371708224235 + ], + [ + 90.45763951818026, + 59.990173779646 + ], + [ + 94.1732363902961, + 57.01770988870942 + ] + ], + "color": "--affine-palette-line-white", + "lineWidth": 10, + "id": "fKZjO4N_6x", + "index": "b71", + "seed": 925352227 + }, + "Lsu7vyerFn": { + "type": "brush", + "xywh": "[2737.773059336998,-15.313129573512697,69.44880158227079,89.51281378697752]", + "points": [ + [ + 13.174190657850431, + 20.605316368301885 + ], + [ + 12.431057676671116, + 39.18306261065021 + ], + [ + 12.431057676671116, + 46.61418832110161 + ], + [ + 12.431057676671116, + 54.78837897895204 + ], + [ + 11.687992729272082, + 61.476371708224235 + ], + [ + 10.944859748093222, + 73.3661592381905 + ], + [ + 9.458661819514873, + 80.0541519674627 + ], + [ + 8.715528838335558, + 83.76968080579832 + ], + [ + 7.229330909757209, + 84.51281378697752 + ], + [ + 5.743064947399034, + 84.51281378697752 + ], + [ + 5.743064947399034, + 83.76968080579832 + ], + [ + 5, + 83.0265818415092 + ], + [ + 5, + 81.54034989604088 + ], + [ + 5, + 77.82482105770532 + ], + [ + 7.229330909757209, + 71.13682832843307 + ], + [ + 11.687992729272082, + 61.476371708224235 + ], + [ + 15.40352156760764, + 54.78837897895204 + ], + [ + 24.320845206637387, + 38.43996364636109 + ], + [ + 33.98130182684599, + 25.807077152105762 + ], + [ + 42.89862546587574, + 15.403555584497951 + ], + [ + 51.815949104905485, + 7.972429874046554 + ], + [ + 57.76080885299871, + 5 + ], + [ + 61.476337691334265, + 5 + ], + [ + 62.96260365369244, + 5 + ], + [ + 64.44880158227079, + 10.944893764983135 + ] + ], + "color": "--affine-palette-line-white", + "lineWidth": 10, + "id": "Lsu7vyerFn", + "index": "b72", + "seed": 143102530 + }, + "LwpBPRaMZm": { + "type": "brush", + "xywh": "[2792.763199099754,-6.3958059344830644,74.6506644167448,99.17323639029621]", + "points": [ + [ + 21.348449349481143, + 8.715562855225699 + ], + [ + 19.862183387122514, + 13.174224674740515 + ], + [ + 17.632852477365304, + 28.779541043042343 + ], + [ + 13.917323639029746, + 45.12795637563329 + ], + [ + 10.944859748092767, + 56.27461092442036 + ], + [ + 8.715528838335558, + 62.219504689403436 + ], + [ + 7.229330909757209, + 65.19193456344993 + ], + [ + 5.743132981179315, + 65.93503352773905 + ], + [ + 5, + 65.93503352773905 + ], + [ + 5, + 64.44883559916087 + ], + [ + 7.972463890936524, + 54.78837897895204 + ], + [ + 14.660456620209061, + 41.41239352040759 + ], + [ + 22.091514296880177, + 27.29330909757408 + ], + [ + 25.0639781878167, + 22.091548313770147 + ], + [ + 31.00883793590947, + 14.66042260331875 + ], + [ + 34.72443480802531, + 10.944893764983135 + ], + [ + 41.41242753729739, + 6.486231945468319 + ], + [ + 48.84348521396896, + 5 + ], + [ + 55.53147794324104, + 5.743098964289118 + ], + [ + 60.73334077771551, + 7.972429874046497 + ], + [ + 62.96267168747272, + 9.458661819514816 + ], + [ + 66.678132492028, + 15.403555584497951 + ], + [ + 68.90746340178566, + 27.29330909757408 + ], + [ + 69.6506644167448, + 41.41239352040759 + ], + [ + 68.16439845438663, + 59.247040798466855 + ], + [ + 67.4213335069876, + 73.36615923819045 + ], + [ + 66.678132492028, + 83.76968080579832 + ], + [ + 66.678132492028, + 91.20080651624971 + ], + [ + 66.678132492028, + 93.43013742600715 + ], + [ + 66.678132492028, + 94.17323639029621 + ] + ], + "color": "--affine-palette-line-white", + "lineWidth": 10, + "id": "LwpBPRaMZm", + "index": "b73", + "seed": 1739090864 + }, + "XxoVbwP1r3": { + "type": "brush", + "xywh": "[2925.779920704019,-7.882037879951383,70.93513557840924,88.0265818415092]", + "points": [ + [ + 16.146654548786955, + 33.98130182684628 + ], + [ + 16.88985556374655, + 30.265772988510662 + ], + [ + 19.862251420902794, + 22.091548313770147 + ], + [ + 24.320913240417667, + 16.14665454878707 + ], + [ + 33.98130182684645, + 7.972463890936638 + ], + [ + 42.155560518476705, + 5 + ], + [ + 49.586618195148276, + 5.743132981179258 + ], + [ + 53.302215067264115, + 7.2293309097574365 + ], + [ + 58.50394183417757, + 10.201794800694017 + ], + [ + 59.9902077965362, + 12.431125710451454 + ], + [ + 63.705804668652036, + 19.11911843972365 + ], + [ + 65.93513557840924, + 31.751970917088897 + ], + [ + 65.1919345634501, + 45.127956375633346 + ], + [ + 61.47647375889483, + 58.50394183417774 + ], + [ + 55.531545977021324, + 68.90749741867569 + ], + [ + 48.84355324774924, + 75.59549014794788 + ], + [ + 40.66929455611853, + 80.79725093175182 + ], + [ + 36.21063273660366, + 81.54038391293102 + ], + [ + 28.036510112533506, + 83.0265818415092 + ], + [ + 20.60531636830183, + 81.54038391293102 + ], + [ + 14.660524653988887, + 78.56792002199438 + ], + [ + 9.458661819514873, + 71.13682832843307 + ], + [ + 5.743201014959595, + 59.99017377964606 + ], + [ + 5, + 54.78841299584212 + ], + [ + 5.743201014959595, + 40.66929455611853 + ], + [ + 7.9725319247168045, + 33.98130182684628 + ], + [ + 10.201862834474014, + 29.52264000733146 + ], + [ + 12.431193744231678, + 26.550210133284963 + ], + [ + 13.917323639029746, + 25.0639781878167 + ] + ], + "color": "--affine-palette-line-white", + "lineWidth": 10, + "id": "XxoVbwP1r3", + "index": "b4g", + "seed": 937814705 + }, + "W2Yv8XGQ2J": { + "type": "brush", + "xywh": "[3004.5497035604876,-13.826897628044378,71.67806445824772,95.45767353507051]", + "points": [ + [ + 17.63278444358457, + 24.320845206637443 + ], + [ + 16.88971949618599, + 29.52264000733146 + ], + [ + 14.66038858642878, + 42.89862546587591 + ], + [ + 11.687992729272082, + 57.01770988870942 + ], + [ + 8.715460804555278, + 68.90746340178555 + ], + [ + 7.972395857156243, + 72.62302625701125 + ], + [ + 6.486129894798069, + 77.82478704081518 + ], + [ + 5, + 79.3110189862835 + ], + [ + 5, + 77.82478704081518 + ], + [ + 6.486129894798069, + 68.90746340178555 + ], + [ + 25.80704313521528, + 28.036408061863142 + ], + [ + 41.41235950351711, + 11.687992729272253 + ], + [ + 45.1279563756334, + 9.458661819514816 + ], + [ + 51.815949104905485, + 5.743098964289118 + ], + [ + 57.01767587181894, + 5 + ], + [ + 59.2470067815766, + 6.486197928578235 + ], + [ + 60.73327274393523, + 7.972429874046497 + ], + [ + 64.44873354849051, + 15.403521567607811 + ], + [ + 66.67806445824772, + 31.008837935909696 + ], + [ + 66.67806445824772, + 47.357287285390726 + ], + [ + 64.44873354849051, + 62.962603653692554 + ], + [ + 63.705668601091475, + 75.5954561310578 + ], + [ + 62.96260365369244, + 83.76968080579832 + ], + [ + 62.96260365369244, + 86.74211067984481 + ], + [ + 62.96260365369244, + 88.9714415896022 + ], + [ + 62.96260365369244, + 90.45767353507051 + ] + ], + "color": "--affine-palette-line-white", + "lineWidth": 10, + "id": "W2Yv8XGQ2J", + "index": "b4h", + "seed": 1545395480 + }, + "w9_u4MCAY6": { + "type": "brush", + "xywh": "[2606.98560060871,197.95953879890914,79.85239118365871,21.14665454878707]", + "points": [ + [ + 5, + 16.14665454878707 + ], + [ + 8.715596872115839, + 16.14665454878707 + ], + [ + 22.834647278059492, + 13.917323639029632 + ], + [ + 30.26577298851089, + 12.431125710451397 + ], + [ + 33.23823687944741, + 12.431125710451397 + ], + [ + 39.926229608719495, + 10.944859748092995 + ], + [ + 47.35728728539061, + 9.45866181951476 + ], + [ + 51.072884157506905, + 7.972463890936638 + ], + [ + 54.788412995842464, + 7.2293309097574365 + ], + [ + 59.9902077965362, + 5.743132981179201 + ], + [ + 62.21953870629386, + 5.743132981179201 + ], + [ + 64.44886961605107, + 5.743132981179201 + ], + [ + 65.93506754462942, + 5.743132981179201 + ], + [ + 68.16439845438663, + 5.743132981179201 + ], + [ + 69.65059638296498, + 5.743132981179201 + ], + [ + 70.39372936414429, + 5.743132981179201 + ], + [ + 72.6230602739015, + 5.743132981179201 + ], + [ + 74.85239118365871, + 5 + ] + ], + "color": "--affine-palette-line-white", + "lineWidth": 10, + "id": "w9_u4MCAY6", + "index": "b74", + "seed": 847280562 + }, + "uVMe6UaTm_": { + "type": "brush", + "xywh": "[2625.5633808679486,144.45559696473146,58.30214703348338,143.01672160426506]", + "points": [ + [ + 43.641758447055054, + 5 + ], + [ + 41.41242753729739, + 9.45866181951476 + ], + [ + 31.00883793590947, + 36.95376571778286 + ], + [ + 10.944859748092767, + 94.91636937147541 + ], + [ + 5.74313298117886, + 120.1820743262058 + ], + [ + 5, + 128.3563330178365 + ], + [ + 5.74313298117886, + 133.5580597847503 + ], + [ + 7.229330909757209, + 135.0443257471087 + ], + [ + 10.201794800693733, + 137.27365665686602 + ], + [ + 17.632852477365304, + 138.01672160426506 + ], + [ + 27.29330909757391, + 132.81499483735126 + ], + [ + 38.439963646360866, + 123.89767119832163 + ], + [ + 48.84348521396896, + 112.75101664953456 + ], + [ + 53.30214703348338, + 107.5492218488406 + ] + ], + "color": "--affine-palette-line-white", + "lineWidth": 10, + "id": "uVMe6UaTm_", + "index": "b75", + "seed": 2012563762 + }, + "7ntelMzJfo": { + "type": "brush", + "xywh": "[2677.581124773548,130.33647852500775,74.65059638296498,149.70478236731753]", + "points": [ + [ + 52.55901405230452, + 5 + ], + [ + 44.38482339445409, + 22.09158233066023 + ], + [ + 35.46749975542434, + 45.87108935681249 + ], + [ + 28.0363740449734, + 71.87992729272219 + ], + [ + 17.632852477365304, + 106.80615690144168 + ], + [ + 11.687992729272537, + 126.87013508925827 + ], + [ + 7.972395857156243, + 139.50298756662357 + ], + [ + 5, + 144.70478236731753 + ], + [ + 6.486197928578349, + 140.24612054780278 + ], + [ + 25.0639781878167, + 103.83369301050504 + ], + [ + 29.522640007331574, + 94.91636937147541 + ], + [ + 39.183028593760355, + 82.28351689411022 + ], + [ + 46.61415430421175, + 71.87992729272219 + ], + [ + 48.84348521396896, + 68.90753143556583 + ], + [ + 54.78834496206218, + 62.962603653692554 + ], + [ + 59.247006781577056, + 60.73327274393512 + ], + [ + 62.21947067251358, + 59.9902077965362 + ], + [ + 64.44880158227079, + 62.21953870629352 + ], + [ + 67.42126547320731, + 71.87992729272219 + ], + [ + 69.65059638296498, + 86.74217871362498 + ], + [ + 68.90746340178566, + 95.65950235265461 + ], + [ + 66.678132492028, + 111.26481872095644 + ], + [ + 63.705668601091475, + 126.12700210807907 + ], + [ + 60.73327274393523, + 136.53052367568694 + ], + [ + 59.247006781577056, + 141.7323184763809 + ], + [ + 59.247006781577056, + 142.4754514575602 + ] + ], + "color": "--affine-palette-line-white", + "lineWidth": 10, + "id": "7ntelMzJfo", + "index": "b76", + "seed": 977720318 + }, + "eqPO6DfJdl": { + "type": "brush", + "xywh": "[2748.1765809046055,175.6662297013351,96.94397351431917,99.91636937147541]", + "points": [ + [ + 32.4951038982681, + 48.10042026656993 + ], + [ + 43.641758447055054, + 46.614154304211525 + ], + [ + 48.10042026656993, + 45.12795637563329 + ], + [ + 60.73327274393523, + 36.95376571778286 + ], + [ + 66.67813249202845, + 28.77950702615226 + ], + [ + 69.65059638296498, + 19.862183387122627 + ], + [ + 68.90746340178566, + 11.687992729272196 + ], + [ + 68.90746340178566, + 9.45866181951476 + ], + [ + 66.67813249202845, + 6.486197928578122 + ], + [ + 63.705736634871755, + 5 + ], + [ + 60.73327274393523, + 5 + ], + [ + 54.04528001466315, + 8.715528838335558 + ], + [ + 40.66929455611853, + 22.834647278059265 + ], + [ + 26.55017611639505, + 42.15549248469665 + ], + [ + 16.14665454878741, + 59.990139762755916 + ], + [ + 11.687992729272082, + 68.16439845438651 + ], + [ + 6.486197928578349, + 80.0541179505725 + ], + [ + 5, + 88.22837664220322 + ], + [ + 6.486197928578349, + 91.94390548053877 + ], + [ + 11.687992729272082, + 94.91636937147541 + ], + [ + 24.320845206637387, + 94.91636937147541 + ], + [ + 43.641758447055054, + 91.20077249935957 + ], + [ + 61.476405725114546, + 84.51277977008738 + ], + [ + 70.39372936414384, + 80.7972509317517 + ], + [ + 82.28344886033028, + 74.85239118365871 + ], + [ + 91.94397351431917, + 69.65059638296475 + ] + ], + "color": "--affine-palette-line-white", + "lineWidth": 10, + "id": "eqPO6DfJdl", + "index": "b77", + "seed": 24484761 + }, + "_LdRTPaDnP": { + "type": "brush", + "xywh": "[2899.7363238579596,100.61496606121042,118.09674126738173,130.48601517097805]", + "points": [ + [ + 36.90247494282494, + 125.48601517097805 + ], + [ + 36.283019045785295, + 123.0081632259461 + ], + [ + 35.973276918829015, + 120.84002505099735 + ], + [ + 34.73433676787636, + 112.16750070807566 + ], + [ + 34.114880870836714, + 109.99939089000017 + ], + [ + 33.185682846840336, + 106.28259879401563 + ], + [ + 32.56622694980069, + 103.18526259507072 + ], + [ + 31.94674269588768, + 100.39766852308233 + ], + [ + 31.63702892580477, + 98.53927247509009 + ], + [ + 31.327286798848036, + 97.61007445109394 + ], + [ + 31.327286798848036, + 96.37113430014134 + ], + [ + 31.327286798848036, + 95.75167840310164 + ], + [ + 31.327286798848036, + 95.13222250606199 + ], + [ + 31.327286798848036, + 94.82248037910549 + ], + [ + 31.327286798848036, + 94.20302448206584 + ], + [ + 31.327286798848036, + 93.89328235510939 + ], + [ + 30.707830901808393, + 93.58354022815288 + ], + [ + 30.398088774852113, + 93.58354022815288 + ], + [ + 30.088346647895833, + 93.58354022815288 + ], + [ + 28.539692726859812, + 92.96408433111324 + ], + [ + 27.61049470286389, + 92.65434220415673 + ], + [ + 25.442356527914853, + 92.03488630711709 + ], + [ + 23.274246709839645, + 91.10568828312094 + ], + [ + 21.10610853489061, + 89.86674813216834 + ], + [ + 18.937970359942028, + 88.627836338089 + ], + [ + 16.769860541866365, + 86.45969816314025 + ], + [ + 14.601722366917784, + 83.67210409115185 + ], + [ + 12.12387042188584, + 80.26505412212381 + ], + [ + 9.95573224693726, + 76.8579757962224 + ], + [ + 9.026534222940882, + 74.99957974823016 + ], + [ + 7.477880301905316, + 71.28278765224562 + ], + [ + 5.929198023995923, + 67.25628178617788 + ], + [ + 5, + 59.822697594208734 + ], + [ + 5.30974212695628, + 52.389113402239644 + ], + [ + 5.929198023995923, + 47.74312328225898 + ], + [ + 7.477880301905316, + 43.406846932361475 + ], + [ + 10.26547437389354, + 36.592746994305315 + ], + [ + 11.504386167972825, + 34.424608819356564 + ], + [ + 13.053068445881763, + 32.56621277136429 + ], + [ + 16.15037628795335, + 28.849420675379747 + ], + [ + 20.176910510894686, + 24.822886452438723 + ], + [ + 24.82290063087521, + 20.796380586370987 + ], + [ + 29.468890750855735, + 17.699044387426085 + ], + [ + 34.424622997792994, + 14.911450315437662 + ], + [ + 36.90247494282494, + 13.36278221596524 + ], + [ + 42.16792095984556, + 10.884916092496638 + ], + [ + 47.4333953337391, + 8.716792095984545 + ], + [ + 52.69884135075927, + 7.168123996512094 + ], + [ + 57.654573597696526, + 5.6194700754763005 + ], + [ + 62.30056371767705, + 5.309727948519821 + ], + [ + 66.94655383765803, + 5 + ], + [ + 71.28280183068227, + 5.309727948519821 + ], + [ + 81.19423796768342, + 6.5486680994724225 + ], + [ + 86.1499985714936, + 7.787594071988394 + ], + [ + 88.31810838956926, + 8.407064147464723 + ], + [ + 92.65438473946642, + 10.265460195456996 + ], + [ + 97.91980239961367, + 13.98225229144154 + ], + [ + 102.8755630034243, + 18.008786514382564 + ], + [ + 104.4241885675865, + 19.557440435418357 + ], + [ + 107.21178263957518, + 22.964490404446423 + ], + [ + 109.07017868756748, + 26.681282500430996 + ], + [ + 110.92857473555978, + 30.088360826332348 + ], + [ + 112.16754324338535, + 33.495410795360414 + ], + [ + 112.78697078355208, + 36.90246076438851 + ], + [ + 113.09674126738173, + 40.30953909028986 + ], + [ + 113.09674126738173, + 43.716589059317954 + ], + [ + 113.09674126738173, + 45.26524298035375 + ], + [ + 112.78697078355208, + 48.36257917929862 + ], + [ + 112.16754324338535, + 51.45991537824352 + ], + [ + 111.54805898947279, + 54.247509450231945 + ], + [ + 110.61886096547642, + 56.41561926830738 + ], + [ + 109.68966294148004, + 58.89349957021261 + ], + [ + 107.83126689348819, + 62.610291666197185 + ], + [ + 107.21178263957518, + 63.84920346027647 + ], + [ + 106.5923550994089, + 64.77840148427262 + ], + [ + 104.73395905141615, + 66.94653965922137 + ], + [ + 102.8755630034243, + 68.80493570721367 + ], + [ + 101.017166955432, + 70.66333175520592 + ], + [ + 99.1587709074397, + 72.21198567624171 + ], + [ + 97.3003748594474, + 73.45092582719437 + ], + [ + 95.4419788114551, + 74.68986597814697 + ], + [ + 93.5835827634628, + 75.92877777222631 + ], + [ + 91.7251867154705, + 76.8579757962224 + ], + [ + 90.79598869147458, + 77.47746005013536 + ], + [ + 89.24730641356518, + 78.40665807413151 + ], + [ + 87.69862413565625, + 79.33585609812766 + ], + [ + 86.1499985714936, + 80.26505412212381 + ], + [ + 84.60131629358466, + 81.19425214611991 + ], + [ + 83.362376142632, + 82.12345017011606 + ], + [ + 81.81372222159598, + 82.7429060671557 + ], + [ + 81.19423796768342, + 83.3623619641954 + ], + [ + 79.95532617360368, + 84.601302115148 + ], + [ + 78.09693012561138, + 87.07915406017995 + ], + [ + 77.16773210161546, + 88.0083520841761 + ], + [ + 76.23853407761953, + 89.55703436208515 + ], + [ + 75.61904982370652, + 91.41543041007745 + ], + [ + 74.99959392666688, + 92.96408433111324 + ], + [ + 74.68985179971014, + 95.13222250606199 + ], + [ + 74.68985179971014, + 96.06142053005814 + ], + [ + 74.68985179971014, + 98.53927247509009 + ], + [ + 74.68985179971014, + 101.01712442012197 + ], + [ + 74.68985179971014, + 103.80471849211042 + ], + [ + 74.68985179971014, + 106.59231256409882 + ], + [ + 74.68985179971014, + 109.68964876304372 + ], + [ + 74.99959392666688, + 112.16750070807566 + ], + [ + 74.99959392666688, + 114.02589675606791 + ], + [ + 74.99959392666688, + 114.95509478006406 + ], + [ + 74.99959392666688, + 115.88429280406021 + ], + [ + 74.99959392666688, + 117.43297508196932 + ], + [ + 74.99959392666688, + 119.29137112996156 + ], + [ + 74.99959392666688, + 120.5302829240409 + ], + [ + 74.99959392666688, + 121.7692230749935 + ], + [ + 74.99959392666688, + 122.38867897203315 + ], + [ + 74.99959392666688, + 122.69842109898966 + ] + ], + "color": "--affine-palette-line-yellow", + "lineWidth": 10, + "id": "_LdRTPaDnP", + "index": "b4m", + "seed": 328056657 + }, + "tgAoWM0n5I": { + "type": "brush", + "xywh": "[2917.3910863138863,119.1989407195698,80.61904982370652,107.56579251959448]", + "points": [ + [ + 26.991038805824246, + 80.26503994368716 + ], + [ + 31.327286798848036, + 79.02609979273456 + ], + [ + 35.973276918829015, + 78.40664389569486 + ], + [ + 39.07061311777397, + 78.0969017687384 + ], + [ + 40.30952491185326, + 78.0969017687384 + ], + [ + 41.23872293584918, + 78.0969017687384 + ], + [ + 39.690069014813616, + 83.36234778575874 + ], + [ + 35.04407889483264, + 90.17647608068819 + ], + [ + 32.25648482284441, + 93.27381227963309 + ], + [ + 31.63702892580477, + 93.89326817667273 + ], + [ + 30.707830901808393, + 94.20301030362924 + ], + [ + 30.398088774852113, + 94.20301030362924 + ], + [ + 31.63702892580477, + 94.51272407371243 + ], + [ + 34.114880870836714, + 94.82246620066888 + ], + [ + 36.90247494282494, + 94.82246620066888 + ], + [ + 38.141415093777596, + 94.82246620066888 + ], + [ + 39.690069014813616, + 96.37112012170468 + ], + [ + 39.38032688785688, + 96.68086224866119 + ], + [ + 37.831672966821316, + 97.91980239961379 + ], + [ + 35.973276918829015, + 99.15871419369307 + ], + [ + 33.49542497379707, + 100.39765434464573 + ], + [ + 32.875940719884056, + 100.70739647160218 + ], + [ + 32.25648482284441, + 101.32685236864182 + ], + [ + 32.56622694980069, + 101.32685236864182 + ], + [ + 34.114880870836714, + 101.32685236864182 + ], + [ + 35.66353479187228, + 101.32685236864182 + ], + [ + 38.45112886386096, + 101.32685236864182 + ], + [ + 40.30952491185326, + 101.32685236864182 + ], + [ + 41.54846506280592, + 101.32685236864182 + ], + [ + 42.16792095984556, + 101.32685236864182 + ], + [ + 41.23872293584918, + 101.32685236864182 + ], + [ + 39.690069014813616, + 101.63659449559833 + ], + [ + 38.76087099081724, + 101.94630826568152 + ], + [ + 36.90247494282494, + 102.56579251959448 + ], + [ + 35.66353479187228, + 102.56579251959448 + ], + [ + 35.04407889483264, + 102.56579251959448 + ], + [ + 34.73433676787636, + 102.56579251959448 + ], + [ + 35.35382102178937, + 100.08791221768922 + ], + [ + 37.831672966821316, + 93.27381227963309 + ], + [ + 39.9998111417699, + 86.1499418577472 + ], + [ + 41.23872293584918, + 79.6455556897742 + ], + [ + 41.23872293584918, + 76.8579616177858 + ], + [ + 41.23872293584918, + 64.15893140879632 + ], + [ + 42.16792095984556, + 52.698812993886236 + ], + [ + 42.16792095984556, + 50.22096104885429 + ], + [ + 42.47766308680184, + 45.5749709288736 + ], + [ + 42.16792095984556, + 45.26522880191709 + ], + [ + 41.23872293584918, + 44.64577290487745 + ], + [ + 39.9998111417699, + 44.955515031833954 + ], + [ + 35.66353479187228, + 47.12362484990939 + ], + [ + 30.707830901808393, + 50.53070317581074 + ], + [ + 26.37155455191123, + 53.93775314483884 + ], + [ + 23.274246709839645, + 56.10589131978759 + ], + [ + 21.725564431930252, + 56.41560508987078 + ], + [ + 21.10610853489061, + 53.93775314483884 + ], + [ + 21.725564431930252, + 49.60150515181459 + ], + [ + 25.13264275783149, + 37.831644609948 + ], + [ + 28.849434853816092, + 27.300752575907325 + ], + [ + 31.327286798848036, + 20.48662428097788 + ], + [ + 31.63702892580477, + 17.079574311949784 + ], + [ + 31.017544671891756, + 16.460090058036855 + ], + [ + 27.610494702863434, + 19.24768413002525 + ], + [ + 19.86716838393795, + 27.61046634599049 + ], + [ + 16.460118414910085, + 31.32725844197506 + ], + [ + 10.575188143976902, + 37.831644609948 + ], + [ + 7.168138174948581, + 41.23872293584935 + ], + [ + 5.619484253913015, + 42.477634729928695 + ], + [ + 5, + 42.477634729928695 + ], + [ + 6.548682277908938, + 36.592732815868686 + ], + [ + 12.12387042188584, + 25.132614400958573 + ], + [ + 19.24771248689831, + 14.601694010044582 + ], + [ + 21.725564431930252, + 10.575188143976845 + ], + [ + 25.13264275783149, + 6.238911794079343 + ], + [ + 26.06184078182787, + 5 + ], + [ + 26.06184078182787, + 5 + ], + [ + 23.274246709839645, + 9.645990119980695 + ], + [ + 17.699058565862742, + 18.318486106029127 + ], + [ + 12.743326318925483, + 25.132614400958573 + ], + [ + 9.645990119980524, + 29.77860452093924 + ], + [ + 8.716792095984601, + 31.32725844197506 + ], + [ + 7.168138174948581, + 32.25645646597118 + ], + [ + 10.884930270933182, + 26.371554551911174 + ], + [ + 20.486652637850966, + 14.911436137001033 + ], + [ + 23.274246709839645, + 11.81409993805616 + ], + [ + 27.92023682982017, + 7.1681098180754645 + ], + [ + 30.398088774852113, + 5.309713770083192 + ], + [ + 30.707830901808393, + 5.309713770083192 + ], + [ + 31.017544671891756, + 6.238911794079343 + ], + [ + 28.539692726859812, + 15.530892034040704 + ], + [ + 22.96450458288291, + 28.53966436998664 + ], + [ + 18.318514462902385, + 39.07058476090063 + ], + [ + 15.840662517870442, + 46.19442682591324 + ], + [ + 15.221178263957427, + 49.29176302485814 + ], + [ + 15.530920390913707, + 49.60150515181459 + ], + [ + 17.079574311949727, + 48.6723071278185 + ], + [ + 26.06184078182787, + 40.9289808088929 + ], + [ + 37.52193083986458, + 28.53966436998664 + ], + [ + 47.4333953337391, + 18.008772335945935 + ], + [ + 53.628039374755645, + 11.81409993805616 + ], + [ + 56.10589131978759, + 9.336247993024216 + ], + [ + 56.10589131978759, + 11.194644041016488 + ], + [ + 47.123653206782365, + 28.53966436998664 + ], + [ + 43.40686111079776, + 34.734336767876414 + ], + [ + 37.21221706978167, + 45.88471305583005 + ], + [ + 34.73433676787636, + 50.53070317581074 + ], + [ + 31.327286798848036, + 58.274001137863024 + ], + [ + 31.017544671891756, + 59.51294128881568 + ], + [ + 31.327286798848036, + 59.203199161859175 + ], + [ + 35.973276918829015, + 53.93775314483884 + ], + [ + 40.92900916576582, + 47.74310910382235 + ], + [ + 50.530703175810686, + 34.734336767876414 + ], + [ + 59.51296964568883, + 23.893674250005944 + ], + [ + 66.32706958374501, + 16.460090058036855 + ], + [ + 70.35363216355927, + 12.743297962052281 + ], + [ + 71.28283018755519, + 11.81409993805616 + ], + [ + 70.66334593364263, + 13.982238113004911 + ], + [ + 64.46867353575271, + 26.061812424954695 + ], + [ + 60.13242554272847, + 33.185654489967334 + ], + [ + 52.69884135075927, + 45.88471305583005 + ], + [ + 46.813911079826084, + 57.34480311386693 + ], + [ + 46.19445518278644, + 59.203199161859175 + ], + [ + 46.19445518278644, + 59.82268341577213 + ], + [ + 49.2917913817314, + 57.96428736777983 + ], + [ + 57.344831470740246, + 50.53070317581074 + ], + [ + 65.70761368670537, + 41.85817883288905 + ], + [ + 72.21202821155157, + 36.592732815868686 + ], + [ + 74.07042425954387, + 34.734336767876414 + ], + [ + 75.61904982370652, + 33.80513874388026 + ], + [ + 75.30933605362316, + 35.044050537959606 + ], + [ + 70.35363216355927, + 43.406832753924846 + ], + [ + 62.30056371767705, + 55.17669329579144 + ], + [ + 54.55723739875157, + 66.63678335382826 + ], + [ + 48.982049254774665, + 74.68985179971031 + ], + [ + 44.02631700783786, + 81.5039517377665 + ], + [ + 44.33605913479414, + 80.8844958407268 + ], + [ + 51.769643326763344, + 70.66331757676932 + ], + [ + 60.13242554272847, + 59.82268341577213 + ], + [ + 61.99082159072077, + 57.34480311386693 + ], + [ + 63.22976174167343, + 54.55720904187848 + ], + [ + 63.22976174167343, + 54.24749527179529 + ], + [ + 61.99082159072077, + 54.86695116883499 + ], + [ + 55.796177549704225, + 61.061595209851475 + ], + [ + 48.36259335773502, + 68.49517940182056 + ], + [ + 42.787405213758575, + 74.07036754579735 + ] + ], + "color": "--affine-palette-line-yellow", + "lineWidth": 10, + "id": "tgAoWM0n5I", + "index": "b4n", + "seed": 702774134 + }, + "Elf7auM2_Q": { + "type": "brush", + "xywh": "[2957.656343472677,126.94223868162209,38.4953966169237,52.74310910382235]", + "points": [ + [ + 21.10610853489061, + 5 + ], + [ + 22.654762455926175, + 5.309742126956479 + ], + [ + 24.82290063087521, + 5.92919802399615 + ], + [ + 25.752098654871133, + 6.238940150952629 + ], + [ + 26.68129667886751, + 6.5486822779091085 + ], + [ + 28.84940649694272, + 7.787594071988423 + ], + [ + 30.70780254493502, + 9.955732246937174 + ], + [ + 32.25648482284441, + 12.743326318925597 + ], + [ + 33.185682846840336, + 15.530920390913991 + ], + [ + 33.4953966169237, + 18.318514462902414 + ], + [ + 33.185682846840336, + 19.557454613855043 + ], + [ + 32.56619859292732, + 22.65476245592663 + ], + [ + 31.017573028764673, + 25.75209865487153 + ], + [ + 28.84940649694272, + 29.468890750856076 + ], + [ + 26.061812424954496, + 33.18568284684062 + ], + [ + 23.274218352966273, + 36.28301904578552 + ], + [ + 22.345020328969895, + 37.52193083986481 + ], + [ + 20.486624280977594, + 39.07061311777392 + ], + [ + 18.628228232985293, + 40.61926703880971 + ], + [ + 17.69903020898937, + 41.23872293584941 + ], + [ + 15.221178263957427, + 42.78740521375846 + ], + [ + 13.053040089008391, + 44.026317007837804 + ], + [ + 10.884930270933182, + 45.265257158790405 + ], + [ + 9.026534222940882, + 46.504197309743006 + ], + [ + 7.168138174948581, + 47.123653206782706 + ], + [ + 6.238940150952203, + 47.74310910382235 + ], + [ + 5.619455897039188, + 47.74310910382235 + ], + [ + 5.30974212695628, + 47.74310910382235 + ], + [ + 5, + 47.74310910382235 + ] + ], + "color": "--affine-palette-line-white", + "lineWidth": 10, + "id": "Elf7auM2_Q", + "index": "b4o", + "seed": 2137967754 + }, + "p1ffIEODtC": { + "type": "brush", + "xywh": "[2935.355590896769,227.29562527320496,48.716574880881126,37.56622694980098]", + "points": [ + [ + 5, + 7.168138174948751 + ], + [ + 10.575188143976902, + 7.168138174948751 + ], + [ + 16.460118414910085, + 6.858396047992301 + ], + [ + 21.725564431930707, + 6.5486822779091085 + ], + [ + 24.20341637696265, + 6.5486822779091085 + ], + [ + 28.539692726859812, + 5.92919802399615 + ], + [ + 30.398088774852113, + 5.619484253912958 + ], + [ + 32.875940719884056, + 5.3097421269565075 + ], + [ + 33.805138743880434, + 5 + ], + [ + 34.73433676787636, + 5 + ], + [ + 35.04407889483309, + 5 + ], + [ + 34.73433676787636, + 5 + ], + [ + 33.805138743880434, + 5.3097421269565075 + ], + [ + 32.875940719884056, + 5.619484253912958 + ], + [ + 29.46889075085619, + 6.5486822779091085 + ], + [ + 25.132614400958573, + 7.477880301905259 + ], + [ + 21.41582230497397, + 8.407078325901352 + ], + [ + 18.008772335946105, + 9.336276349897503 + ], + [ + 14.911436137001147, + 9.955732246937146 + ], + [ + 9.95573224693726, + 10.884930270933296 + ], + [ + 9.336247993024244, + 10.884930270933296 + ], + [ + 10.575188143976902, + 11.194672397889804 + ], + [ + 13.982238113005224, + 11.814128294929446 + ], + [ + 18.318514462902385, + 12.43358419196909 + ], + [ + 22.34502032897035, + 12.743326318925597 + ], + [ + 24.20341637696265, + 13.053068445882047 + ], + [ + 26.37155455191123, + 13.36278221596524 + ], + [ + 27.61049470286389, + 13.36278221596524 + ], + [ + 28.539692726859812, + 13.36278221596524 + ], + [ + 28.539692726859812, + 13.672524342921747 + ], + [ + 28.539692726859812, + 13.982266469878198 + ], + [ + 27.30075257590761, + 14.60172236691784 + ], + [ + 25.132614400958573, + 16.150376287953634 + ], + [ + 23.274218352966273, + 17.079574311949784 + ], + [ + 19.55742625698167, + 18.628256589858893 + ], + [ + 16.150376287953804, + 19.557454613855043 + ], + [ + 13.362782215965126, + 20.486652637851137 + ], + [ + 11.81412829492956, + 21.106108534890836 + ], + [ + 11.81412829492956, + 21.415850661847287 + ], + [ + 12.743326318925483, + 22.035306558886987 + ], + [ + 13.67252434292186, + 22.345048685843437 + ], + [ + 14.911436137001147, + 22.96450458288308 + ], + [ + 18.628228232985748, + 23.89370260687923 + ], + [ + 22.96450458288291, + 24.513158503918874 + ], + [ + 25.752098654871588, + 24.82290063087538 + ], + [ + 28.229950599903532, + 25.13264275783183 + ], + [ + 29.46889075085619, + 25.13264275783183 + ], + [ + 30.088346647895833, + 25.13264275783183 + ], + [ + 29.46889075085619, + 25.13264275783183 + ], + [ + 26.68129667886751, + 25.75209865487153 + ], + [ + 21.41582230497397, + 27.610494702863775 + ], + [ + 16.150376287953804, + 29.15914862389957 + ], + [ + 10.265446017020622, + 31.01754467189187 + ], + [ + 8.716792095984601, + 31.637028925804827 + ], + [ + 9.336247993024244, + 31.637028925804827 + ], + [ + 11.81412829492956, + 32.25648482284447 + ], + [ + 16.769832184993447, + 32.56622694980098 + ], + [ + 22.96450458288291, + 32.56622694980098 + ], + [ + 29.159148623899455, + 31.94674269588802 + ], + [ + 34.42459464092008, + 31.637028925804827 + ], + [ + 36.59273281586866, + 31.637028925804827 + ], + [ + 39.99978278489698, + 31.637028925804827 + ], + [ + 42.16792095984556, + 31.32728679884832 + ], + [ + 43.40686111079822, + 31.01754467189187 + ], + [ + 43.716574880881126, + 31.01754467189187 + ] + ], + "color": "--affine-palette-line-grey", + "lineWidth": 10, + "id": "p1ffIEODtC", + "index": "b4p", + "seed": 1139150634 + }, + "PdRE_KBuRQ": { + "type": "brush", + "xywh": "[2932.8777389517372,233.49029767109477,12.787594071988678,23.008772335945935]", + "points": [ + [ + 5, + 5 + ], + [ + 5.619455897039643, + 9.026505866067737 + ], + [ + 7.7875940719886785, + 18.008772335945935 + ] + ], + "color": "--affine-palette-line-grey", + "lineWidth": 10, + "id": "PdRE_KBuRQ", + "index": "b4q", + "seed": 870221598 + }, + "8aOMT-vJPn": { + "type": "brush", + "xywh": "[2968.496977633674,230.70270359910631,12.477880301905316,34.77860452093927]", + "points": [ + [ + 5.619484253913015, + 5 + ], + [ + 5.30974212695628, + 7.477851945031944 + ], + [ + 5, + 13.36278221596524 + ], + [ + 5.30974212695628, + 18.937970359942085 + ], + [ + 5.929198023995923, + 21.72556443193048 + ], + [ + 6.238940150952658, + 24.51315850391893 + ], + [ + 7.168138174948581, + 28.539664369986667 + ], + [ + 7.477880301905316, + 29.77860452093927 + ] + ], + "color": "--affine-palette-line-grey", + "lineWidth": 10, + "id": "8aOMT-vJPn", + "index": "b4r", + "seed": 1001390231 + }, + "qVRqjts8rV": { + "type": "brush", + "xywh": "[3000.9549505829555,39.86601173441619,19.510842108623365,40.19198069856566]", + "points": [ + [ + 2, + 38.19198069856566 + ], + [ + 5.1021684217244, + 33.02168421724673 + ], + [ + 13.374491318767468, + 11.306505265174053 + ], + [ + 17.510842108623365, + 2 + ] + ], + "color": "--affine-palette-line-yellow", + "lineWidth": 4, + "id": "qVRqjts8rV", + "index": "b4s", + "seed": 2005059071 + }, + "iX9WqzbzML": { + "type": "brush", + "xywh": "[3052.6576313841433,98.80721174718491,40.19202803389908,12.27246490304367]", + "points": [ + [ + 2, + 10.27246490304367 + ], + [ + 9.238519211580751, + 9.238377205579695 + ], + [ + 20.61301053034822, + 6.136208783855068 + ], + [ + 33.02168421724673, + 3.0340403621303267 + ], + [ + 38.19202803389908, + 2 + ] + ], + "color": "--affine-palette-line-yellow", + "lineWidth": 4, + "id": "iX9WqzbzML", + "index": "b4t", + "seed": 1358476857 + }, + "m9R30Xu5Ag": { + "type": "brush", + "xywh": "[3056.793982173999,185.66792755547561,36.05567724404318,10.204336843449369]", + "points": [ + [ + 2, + 2 + ], + [ + 8.20433684344971, + 4.068128059594301 + ], + [ + 28.885333427390833, + 7.170296481318928 + ], + [ + 34.05567724404318, + 8.204336843449369 + ] + ], + "color": "--affine-palette-line-yellow", + "lineWidth": 4, + "id": "m9R30Xu5Ag", + "index": "b4u", + "seed": 369614434 + }, + "BAYGNl-aWq": { + "type": "brush", + "xywh": "[2863.425357658939,231.16641351921527,21.57901750355086,7.1021684217246275]", + "points": [ + [ + 19.57901750355086, + 2 + ], + [ + 10.272512238377203, + 5.1021684217246275 + ], + [ + 4.0681753949274935, + 5.1021684217246275 + ], + [ + 2, + 5.1021684217246275 + ] + ], + "color": "--affine-palette-line-yellow", + "lineWidth": 4, + "id": "BAYGNl-aWq", + "index": "b4v", + "seed": 892965863 + }, + "RRqS8C0-tX": { + "type": "brush", + "xywh": "[2902.719554114563,53.308725783445254,16.40867368689851,38.12385263897136]", + "points": [ + [ + 14.40867368689851, + 36.12385263897136 + ], + [ + 12.340498291970562, + 28.88547543339166 + ], + [ + 6.136161448521307, + 9.238424540913286 + ], + [ + 3.033993026796452, + 3.0340876974639173 + ], + [ + 2, + 2 + ] + ], + "color": "--affine-palette-line-yellow", + "lineWidth": 4, + "id": "RRqS8C0-tX", + "index": "b4y", + "seed": 2036606473 + }, + "BMEs4nn9u8": { + "type": "brush", + "xywh": "[2822.0631751497212,139.13540122960563,55.70287014252244,6.068128059594301]", + "points": [ + [ + 53.70287014252244, + 4.068128059594301 + ], + [ + 45.43035790414524, + 2 + ], + [ + 17.510842108623365, + 2 + ], + [ + 2, + 4.068128059594301 + ] + ], + "color": "--affine-palette-line-yellow", + "lineWidth": 4, + "id": "BMEs4nn9u8", + "index": "b78", + "seed": 1660863471 + }, + "Wx6FFK2yna": { + "type": "brush", + "xywh": "[3125.4392599782495,88.52009117708948,63.628523543932715,159.79017166523548]", + "points": [ + [ + 16.09556827149845, + 152.01627959736075 + ], + [ + 11.472358390062254, + 137.22220267744015 + ], + [ + 6.849148508625603, + 111.33255748602065 + ], + [ + 5, + 81.74436131994551 + ], + [ + 8.698466322187414, + 61.40247910115855 + ], + [ + 14.246250457937094, + 41.98523462603541 + ], + [ + 25.341818729435545, + 18.86946033937366 + ], + [ + 30.889602865185225, + 10.547784135749481 + ], + [ + 37.36196125524748, + 5.924637743663908 + ], + [ + 41.0605968823711, + 5 + ], + [ + 48.45752952674684, + 6.849275487327816 + ], + [ + 54.005313662496064, + 13.321676203624207 + ], + [ + 57.70394928961923, + 29.965028610872594 + ], + [ + 58.628523543932715, + 43.834488950246225 + ], + [ + 57.70394928961923, + 67.87490098057188 + ], + [ + 54.005313662496064, + 85.4428699683672 + ], + [ + 50.306847340308195, + 99.31233030774084 + ], + [ + 45.683637458872, + 110.40789857923974 + ], + [ + 41.0605968823711, + 116.8803416217701 + ], + [ + 39.211279068809745, + 122.42812575751955 + ], + [ + 35.51281274662142, + 128.90052647381597 + ], + [ + 31.814177119498254, + 137.22220267744015 + ], + [ + 27.19113654299781, + 146.46849546161124 + ], + [ + 24.41724447512297, + 151.09166301681378 + ], + [ + 19.79403459368632, + 154.79017166523548 + ] + ], + "color": "--affine-palette-line-white", + "lineWidth": 10, + "id": "Wx6FFK2yna", + "index": "b53", + "seed": 462012319 + }, + "6ya6TvYUel": { + "type": "brush", + "xywh": "[3107.871164011752,265.1245669405253,58.08090871311879,47.909872369699315]", + "points": [ + [ + 29.04045435655962, + 17.94484375882672 + ], + [ + 24.41724447512297, + 16.095568271498905 + ], + [ + 14.246419762872847, + 15.170951690951995 + ], + [ + 9.623209881436651, + 17.020184852045873 + ], + [ + 6.849317813561811, + 18.86946033937363 + ], + [ + 5, + 26.2665199624509 + ], + [ + 5, + 29.040412030325626 + ], + [ + 7.773892067874385, + 37.36208823394986 + ], + [ + 13.321676203624065, + 42.909872369699315 + ], + [ + 26.26656228868478, + 41.98521346291841 + ], + [ + 39.2114483737455, + 35.512812746622046 + ], + [ + 47.53312457736956, + 25.34186105567005 + ], + [ + 51.23159089955743, + 17.94484375882672 + ], + [ + 53.08090871311879, + 13.321676203624179 + ], + [ + 53.08090871311879, + 8.698508648421637 + ], + [ + 51.23159089955743, + 6.849275487327816 + ], + [ + 46.60838101812078, + 5 + ], + [ + 43.834488950246396, + 5 + ], + [ + 41.060596882371556, + 5 + ] + ], + "color": "--affine-palette-line-white", + "lineWidth": 10, + "id": "6ya6TvYUel", + "index": "b54", + "seed": 1087054233 + }, + "jZJjlTjxz_": { + "type": "brush", + "xywh": "[3198.485027997305,102.38955151646314,72.87494330680602,161.63944715256326]", + "points": [ + [ + 25.341818729436, + 149.242387529486 + ], + [ + 15.170994017185876, + 138.1468192579871 + ], + [ + 5, + 90.06603752356972 + ], + [ + 9.623209881436196, + 62.3271168448224 + ], + [ + 12.397101949311036, + 55.854716128526036 + ], + [ + 17.020142525811934, + 42.909872369699315 + ], + [ + 22.567926661561614, + 32.73892067874732 + ], + [ + 28.11571079731084, + 22.567990150912294 + ], + [ + 32.73892067874749, + 15.170951690951995 + ], + [ + 40.13602262805853, + 7.773892067874726 + ], + [ + 47.53295527243381, + 5 + ], + [ + 52.15616515387046, + 7.773892067874726 + ], + [ + 54.9300572217453, + 12.397059623077268 + ], + [ + 61.402415611807555, + 22.567990150912294 + ], + [ + 64.1763076796824, + 30.889666354536473 + ], + [ + 67.87494330680602, + 45.68376443757404 + ], + [ + 67.87494330680602, + 55.854716128526036 + ], + [ + 66.02562549324375, + 68.79951756111882 + ], + [ + 65.10105123893118, + 79.89508583261772 + ], + [ + 60.477841357494526, + 94.68920507877226 + ], + [ + 58.628523543932715, + 102.08622237561559 + ], + [ + 55.854631476057875, + 108.55866541814589 + ], + [ + 53.080739408183035, + 115.95568271498922 + ], + [ + 46.60838101812078, + 132.59903512223758 + ], + [ + 43.83448895024594, + 135.37292719011236 + ], + [ + 41.985171136684585, + 138.1468192579871 + ], + [ + 41.060596882371556, + 140.92071132586182 + ], + [ + 38.286704814496716, + 144.6192623005174 + ], + [ + 37.36213056018369, + 146.46849546161127 + ], + [ + 37.36213056018369, + 147.39315436839212 + ], + [ + 36.437387000934905, + 149.242387529486 + ], + [ + 36.437387000934905, + 150.16704643626684 + ], + [ + 34.58823849230885, + 152.01627959736072 + ], + [ + 33.663494933060065, + 152.94093850414157 + ], + [ + 31.814346424434007, + 154.79017166523545 + ], + [ + 30.889602865185225, + 154.79017166523545 + ], + [ + 29.96502861087265, + 154.79017166523545 + ], + [ + 29.04045435655962, + 154.79017166523545 + ], + [ + 28.11571079731084, + 154.79017166523545 + ], + [ + 27.19113654299781, + 154.79017166523545 + ], + [ + 26.26656228868478, + 155.7148305720163 + ], + [ + 26.26656228868478, + 156.63944715256326 + ] + ], + "color": "--affine-palette-line-white", + "lineWidth": 10, + "id": "jZJjlTjxz_", + "index": "b55", + "seed": 278569111 + }, + "gVyMhVxORH": { + "type": "brush", + "xywh": "[3189.238777539368,285.46642799619536,35.889602865185225,36.81430409820035]", + "points": [ + [ + 30.889602865185225, + 11.472443042530358 + ], + [ + 22.56792666156116, + 9.623167555202542 + ], + [ + 17.94471678012451, + 8.698550974655575 + ], + [ + 16.095568271498905, + 8.698550974655575 + ], + [ + 9.623040576500898, + 10.547784135749453 + ], + [ + 7.77389206787484, + 13.321676203624179 + ], + [ + 5.924574254312574, + 16.095568271498905 + ], + [ + 5, + 20.718735826701447 + ], + [ + 5, + 22.568011314029263 + ], + [ + 5, + 23.492627894576174 + ], + [ + 5, + 25.34190338190399 + ], + [ + 6.849148508626058, + 27.191136542997867 + ], + [ + 9.623040576500898, + 29.040412030325626 + ], + [ + 13.321676203624065, + 31.814304098200353 + ], + [ + 17.94471678012451, + 31.814304098200353 + ], + [ + 21.64335240724813, + 31.814304098200353 + ], + [ + 24.41724447512297, + 30.889687517653442 + ], + [ + 26.26639298374903, + 29.040412030325626 + ], + [ + 29.04028505162387, + 26.2665199624509 + ], + [ + 30.889602865185225, + 23.492627894576174 + ], + [ + 30.889602865185225, + 20.718735826701447 + ], + [ + 30.889602865185225, + 17.94484375882672 + ], + [ + 30.889602865185225, + 16.095568271498905 + ], + [ + 30.889602865185225, + 13.321676203624179 + ], + [ + 28.115710797310385, + 9.623167555202542 + ], + [ + 26.26639298374903, + 7.773892067874726 + ], + [ + 23.49250091587419, + 5 + ], + [ + 21.64335240724813, + 5 + ], + [ + 17.020142525811934, + 5 + ], + [ + 16.095568271498905, + 5 + ] + ], + "color": "--affine-palette-line-white", + "lineWidth": 10, + "id": "gVyMhVxORH", + "index": "b56", + "seed": 919873485 + }, + "8_LSOWfOWr": { + "type": "brush", + "xywh": "[968.6889167057709,-78.93424595022731,150.70296794607657,41.54898060081024]", + "points": [ + [ + 135.60448866517686, + 8.549239640449855 + ], + [ + 138.22418452135685, + 8.985848954248809 + ], + [ + 141.717138978521, + 10.295696882338746 + ], + [ + 144.336834834701, + 12.042174110920882 + ], + [ + 146.0832720898968, + 13.788631352809773 + ], + [ + 147.8297493184789, + 16.8449365227886 + ], + [ + 148.70296794607657, + 18.591413751370737 + ], + [ + 148.70296794607657, + 21.647718921349565 + ], + [ + 147.8297493184789, + 26.013872019418386 + ], + [ + 145.646682762791, + 29.506806489889414 + ], + [ + 142.5903576061189, + 32.56311165986824 + ], + [ + 130.3650969528171, + 36.92928474463031 + ], + [ + 115.52016043002845, + 33.872959587958235 + ], + [ + 112.46385526004974, + 31.253263731778304 + ], + [ + 109.84415940386975, + 28.19695856179942 + ], + [ + 107.66107286148872, + 25.140653391820592 + ], + [ + 106.78783424719768, + 22.08432823514846 + ], + [ + 106.35122493339873, + 20.774480307058525 + ], + [ + 106.35122493339873, + 18.591413751370737 + ], + [ + 107.22446354768977, + 15.971717895190807 + ], + [ + 108.53431147577976, + 14.22524066660867 + ], + [ + 111.15400733195975, + 12.47878342471978 + ], + [ + 115.0835511162295, + 11.60554481042874 + ], + [ + 125.12570524045714, + 12.915392738518676 + ], + [ + 127.30879178283817, + 13.788631352809773 + ], + [ + 131.23833556710815, + 15.971717895190807 + ], + [ + 135.16787935137813, + 18.591413751370737 + ], + [ + 137.35096589375894, + 20.774480307058525 + ], + [ + 138.66081382184916, + 23.8308054637306 + ], + [ + 139.53403244944684, + 27.760349248000523 + ], + [ + 138.66081382184916, + 30.816674404672597 + ], + [ + 133.85803142328814, + 35.61943681654037 + ], + [ + 131.6749448809071, + 36.92928474463031 + ], + [ + 126.87218246903922, + 38.675761973212445 + ], + [ + 120.75955214238843, + 39.54898060081024 + ], + [ + 113.7737031881395, + 39.54898060081024 + ], + [ + 106.35122493339873, + 39.112351300318096 + ], + [ + 79.28102775730827, + 35.18280751604817 + ], + [ + 52.64745988170989, + 32.12652233276259 + ], + [ + 41.732067143191216, + 29.94341580368831 + ], + [ + 36.056046130339155, + 28.19695856179942 + ], + [ + 31.253283718471494, + 26.887110633709483 + ], + [ + 22.08434822184165, + 22.52095753564066 + ], + [ + 14.225260653301802, + 17.71817513707964 + ], + [ + 7.6760210128519475, + 11.60554481042874 + ], + [ + 4.619695856179874, + 7.676001026158815 + ], + [ + 2, + 2 + ] + ], + "color": "--affine-palette-line-navy", + "lineWidth": 4, + "id": "8_LSOWfOWr", + "index": "b79", + "seed": 229708119 + }, + "fLh5q9XoHb": { + "type": "brush", + "xywh": "[1623.253486487466,-59.39384740034728,5.886932838848679,20.98239554963959]", + "points": [ + [ + 2, + 18.98239554963959 + ], + [ + 2.9434664194243396, + 10.491197774819796 + ], + [ + 3.8869328388486792, + 2 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 4, + "id": "fLh5q9XoHb", + "index": "b7A", + "seed": 1044482058 + }, + "bQwvyXEBjm": { + "type": "brush", + "xywh": "[1626.0838857457393,-82.03704146653342,25.699727646761858,40.795190357552485]", + "points": [ + [ + 2, + 38.795190357552485 + ], + [ + 2, + 32.19092542158154 + ], + [ + 2, + 24.64319406618614 + ], + [ + 2.943466419424567, + 16.151996291366345 + ], + [ + 13.321597033092985, + 7.660798516546549 + ], + [ + 18.982395549639477, + 3.8869328388488498 + ], + [ + 21.812794807912724, + 2.9434664194244533 + ], + [ + 23.699727646761858, + 2 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 4, + "id": "bQwvyXEBjm", + "index": "b7B", + "seed": 701996340 + }, + "vRMucu8MbL": { + "type": "brush", + "xywh": "[1628.9142850040125,-65.05464591689383,54.94718664891889,24.75626122733729]", + "points": [ + [ + 2, + 22.75626122733729 + ], + [ + 2, + 19.925861969064044 + ], + [ + 2, + 18.982395549639648 + ], + [ + 3.8869328388489066, + 16.151996291366345 + ], + [ + 13.321597033092985, + 11.434664194244249 + ], + [ + 21.812794807912724, + 9.547731355395399 + ], + [ + 40.682123196401335, + 4.830399258273303 + ], + [ + 50.11678739064564, + 2.9434664194244533 + ], + [ + 52.94718664891889, + 2 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 4, + "id": "vRMucu8MbL", + "index": "b7C", + "seed": 191286329 + }, + "LGu9UFkJ9N": { + "type": "brush", + "xywh": "[1634.575083520559,-49.959183206103035,67.21225010143644,8.717332097122096]", + "points": [ + [ + 2, + 6.717332097122096 + ], + [ + 5.773865677697813, + 6.717332097122096 + ], + [ + 23.699727646761858, + 6.717332097122096 + ], + [ + 53.89065306834323, + 3.8869328388488498 + ], + [ + 63.32531726258753, + 2.9434664194243965 + ], + [ + 65.21225010143644, + 2 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 4, + "id": "LGu9UFkJ9N", + "index": "b7D", + "seed": 475956093 + }, + "TNAsYlfemp": { + "type": "brush", + "xywh": "[1539.2849751586923,-80.15010862768457,18.15199629136646,37.021324679854786]", + "points": [ + [ + 16.15199629136646, + 35.021324679854786 + ], + [ + 16.15199629136646, + 32.19092542158154 + ], + [ + 13.321597033093212, + 21.812794807912894 + ], + [ + 9.547731355395626, + 13.321597033093099 + ], + [ + 3.8869328388489066, + 5.7738656776976995 + ], + [ + 2, + 2 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 4, + "id": "TNAsYlfemp", + "index": "b7E", + "seed": 1482109598 + }, + "PHGOO4yK5v": { + "type": "brush", + "xywh": "[1556.267370708332,-61.28078023919613,5,14.378130613668645]", + "points": [ + [ + 2, + 12.378130613668645 + ], + [ + 2, + 6.717332097122153 + ], + [ + 2.9434664194243396, + 2 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 4, + "id": "PHGOO4yK5v", + "index": "b7F", + "seed": 458185918 + }, + "af6brHf9CH": { + "type": "brush", + "xywh": "[1560.0412363860296,-94.30210491905092,20.982395549639705,50.229854551796734]", + "points": [ + [ + 2, + 48.229854551796734 + ], + [ + 2, + 44.455988874099035 + ], + [ + 2, + 37.85172393812809 + ], + [ + 2.943466419424567, + 30.30399258273269 + ], + [ + 4.830399258273246, + 20.86932838848844 + ], + [ + 10.491197774819966, + 12.378130613668645 + ], + [ + 17.095462710790798, + 3.8869328388488498 + ], + [ + 18.982395549639705, + 2 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 4, + "id": "af6brHf9CH", + "index": "b7G", + "seed": 235919258 + }, + "2hONa_M0tZ": { + "type": "brush", + "xywh": "[1558.154303547181,-69.77197801401593,43.625589615825675,21.925861969064044]", + "points": [ + [ + 2, + 19.925861969064044 + ], + [ + 5.773865677697586, + 15.208529871941948 + ], + [ + 10.491197774819739, + 10.491197774819796 + ], + [ + 19.925861969064044, + 5.7738656776976995 + ], + [ + 31.24745900215703, + 2.9434664194244533 + ], + [ + 39.73865677697677, + 2 + ], + [ + 41.625589615825675, + 2 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 4, + "id": "2hONa_M0tZ", + "index": "b7H", + "seed": 2089676243 + }, + "runSm8fbPf": { + "type": "brush", + "xywh": "[1819.494501727746,-81.09357504710897,6.830399258273246,37.021324679854786]", + "points": [ + [ + 4.830399258273246, + 35.021324679854786 + ], + [ + 3.8869328388486792, + 25.586660485610537 + ], + [ + 3.8869328388486792, + 18.038929130215138 + ], + [ + 2.9434664194243396, + 6.717332097122096 + ], + [ + 2.9434664194243396, + 4.830399258273246 + ], + [ + 2, + 2 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 4, + "id": "runSm8fbPf", + "index": "b7I", + "seed": 1688907564 + }, + "gW__zweRir": { + "type": "brush", + "xywh": "[1825.1553002442924,-68.82851159459148,12.491197774819739,23.812794807912837]", + "points": [ + [ + 2, + 21.812794807912837 + ], + [ + 2, + 18.038929130215138 + ], + [ + 2.9434664194243396, + 13.321597033093042 + ], + [ + 4.830399258273246, + 6.717332097122096 + ], + [ + 10.491197774819739, + 2 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 4, + "id": "gW__zweRir", + "index": "b7J", + "seed": 253085230 + }, + "TRhofVkEZO": { + "type": "brush", + "xywh": "[1825.1553002442924,-75.43277653056248,33.24745900215703,30.41705974388384]", + "points": [ + [ + 2, + 28.41705974388384 + ], + [ + 2, + 25.586660485610594 + ], + [ + 4.830399258273246, + 18.038929130215195 + ], + [ + 10.491197774819739, + 11.434664194244249 + ], + [ + 18.038929130215138, + 8.604264935971003 + ], + [ + 25.586660485610537, + 4.830399258273303 + ], + [ + 29.360526163308123, + 3.8869328388488498 + ], + [ + 31.24745900215703, + 2 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 4, + "id": "TRhofVkEZO", + "index": "b7K", + "seed": 16427940 + }, + "cMjTl5Y0l0": { + "type": "brush", + "xywh": "[1827.9856995025657,-65.05464591689383,55.89065306834323,18.151996291366345]", + "points": [ + [ + 2, + 16.151996291366345 + ], + [ + 4.830399258273246, + 13.321597033093099 + ], + [ + 8.604264935970832, + 10.491197774819852 + ], + [ + 19.925861969064044, + 7.660798516546549 + ], + [ + 41.625589615825675, + 2 + ], + [ + 49.173320971221074, + 2 + ], + [ + 53.89065306834323, + 2 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 4, + "id": "cMjTl5Y0l0", + "index": "b7L", + "seed": 261599141 + }, + "Kl9UtK_qhY": { + "type": "brush", + "xywh": "[1343.987426337837,-73.54584369171363,19.095462710790798,30.41705974388384]", + "points": [ + [ + 17.095462710790798, + 28.41705974388384 + ], + [ + 16.15199629136623, + 24.64319406618614 + ], + [ + 13.321597033092985, + 21.812794807912894 + ], + [ + 10.491197774819739, + 14.265063452517495 + ], + [ + 7.6607985165464925, + 9.547731355395399 + ], + [ + 4.830399258273246, + 5.7738656776976995 + ], + [ + 2, + 2 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 4, + "id": "Kl9UtK_qhY", + "index": "b7M", + "seed": 383374643 + }, + "DCyl4GQ8Zx": { + "type": "brush", + "xywh": "[1363.80022114575,-70.71544443344033,5,32.30399258273263]", + "points": [ + [ + 2, + 30.303992582732633 + ], + [ + 2, + 26.53012690503499 + ], + [ + 2, + 25.586660485610537 + ], + [ + 2.9434664194243396, + 20.86932838848844 + ], + [ + 2.9434664194243396, + 16.151996291366345 + ], + [ + 2.9434664194243396, + 11.434664194244192 + ], + [ + 2.9434664194243396, + 7.6607985165464925 + ], + [ + 2.9434664194243396, + 4.830399258273246 + ], + [ + 2.9434664194243396, + 2 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 4, + "id": "DCyl4GQ8Zx", + "index": "b7N", + "seed": 2083703498 + }, + "-f7Hzfl28P": { + "type": "brush", + "xywh": "[1366.6306204040231,-68.82851159459148,52.11678739064541,26.64319406618614]", + "points": [ + [ + 2, + 24.64319406618614 + ], + [ + 2.9434664194243396, + 18.98239554963959 + ], + [ + 7.6607985165464925, + 15.208529871941892 + ], + [ + 14.265063452517325, + 10.491197774819796 + ], + [ + 32.19092542158137, + 4.830399258273246 + ], + [ + 46.34292171294783, + 2.9434664194243965 + ], + [ + 49.173320971221074, + 2.9434664194243965 + ], + [ + 50.11678739064541, + 2 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 4, + "id": "-f7Hzfl28P", + "index": "b7O", + "seed": 1382833521 + }, + "sa0w4YI4NF": { + "type": "text", + "xywh": "[1912.0484548292225,1401.202455179755,418.812536020748,77.9999279585041]", + "text": { + "affine:surface:text": true, + "delta": [ + { + "insert": "Take a look at how amazing and creative\nEdgeless mode can be!!!" + } + ] + }, + "color": "--affine-text-primary-color", + "fontSize": 24, + "fontFamily": "'Kalam', cursive", + "textAlign": "left", + "id": "sa0w4YI4NF", + "index": "b5g", + "seed": 332594727 + }, + "5sTGmG66WM": { + "type": "brush", + "xywh": "[1547.4173072882395,1175.8579367371312,19.503073770491937,42.80609631147536]", + "points": [ + [ + 5, + 5 + ], + [ + 6.728995901639337, + 10.878586065573472 + ], + [ + 7.727971311475358, + 14.900102459016125 + ], + [ + 10.199795081967295, + 24.61449795081944 + ], + [ + 11.736680327868726, + 29.30199795081944 + ], + [ + 12.735655737704974, + 33.323514344262094 + ], + [ + 13.670594262295253, + 36.14113729508176 + ], + [ + 14.106045081967295, + 37.409067622950715 + ], + [ + 14.503073770491937, + 37.80609631147536 + ] + ], + "color": "--affine-palette-line-tangerine", + "lineWidth": 10, + "id": "5sTGmG66WM", + "index": "b5h", + "seed": 226810180 + }, + "rCe9sSGcdk": { + "type": "brush", + "xywh": "[1584.2641310587314,1260.1945146059834,33.66162909836066,36.0950307377052]", + "points": [ + [ + 5, + 5 + ], + [ + 7.420594262295026, + 8.253073770491937 + ], + [ + 7.817622950819441, + 9.047131147541222 + ], + [ + 8.650102459016125, + 10.315061475409948 + ], + [ + 11.051485655737451, + 13.254354508196911 + ], + [ + 12.357838114754031, + 14.996157786885306 + ], + [ + 13.190317622950715, + 16.26408811475426 + ], + [ + 14.52228483606541, + 18.06992827868862 + ], + [ + 15.354764344262094, + 19.337858606557575 + ], + [ + 16.187243852458778, + 20.208760245901885 + ], + [ + 16.58427254098342, + 21.002817622950943 + ], + [ + 16.981301229508063, + 21.399846311475585 + ], + [ + 16.981301229508063, + 21.796875 + ], + [ + 18.287653688524642, + 23.10322745901658 + ], + [ + 19.555584016393368, + 24.371157786885306 + ], + [ + 20.823514344262094, + 24.806608606557575 + ], + [ + 21.61757172131138, + 25.600665983606632 + ], + [ + 22.411629098360663, + 26.394723360655917 + ], + [ + 23.20568647540972, + 27.1887807377052 + ], + [ + 25.011526639344083, + 29.03304303278719 + ], + [ + 25.882428278688394, + 29.903944672131274 + ], + [ + 27.073514344262094, + 30.300973360655917 + ], + [ + 27.073514344262094, + 30.69800204918056 + ], + [ + 27.470543032786736, + 30.69800204918056 + ], + [ + 27.86757172131138, + 31.0950307377052 + ], + [ + 28.661629098360663, + 31.0950307377052 + ], + [ + 28.25819672131138, + 31.0950307377052 + ] + ], + "color": "--affine-palette-line-tangerine", + "lineWidth": 10, + "id": "rCe9sSGcdk", + "index": "b5i", + "seed": 2062826697 + }, + "UK9oSr12JL": { + "type": "brush", + "xywh": "[1648.5955859767641,1337.4614203436886,45.514856557377016,40.96823770491801]", + "points": [ + [ + 5, + 5 + ], + [ + 8.112192622950715, + 8.586065573770384 + ], + [ + 12.633196721311378, + 12.56915983606541 + ], + [ + 19.465932377049285, + 18.198002049180104 + ], + [ + 25.65829918032796, + 23.72438524590143 + ], + [ + 31.287141393442653, + 27.745901639344083 + ], + [ + 33.63089139344265, + 30.089651639344083 + ], + [ + 37.34503073770475, + 32.79841188524597 + ], + [ + 39.72079918032796, + 35.174180327868726 + ], + [ + 40.514856557377016, + 35.96823770491801 + ] + ], + "color": "--affine-palette-line-tangerine", + "lineWidth": 10, + "id": "UK9oSr12JL", + "index": "b5j", + "seed": 485444337 + }, + "osezYwO_vz": { + "type": "brush", + "xywh": "[1742.3263749111902,1407.3896990322132,35.37781762295094,27.949538934426073]", + "points": [ + [ + 5, + 5 + ], + [ + 8.073770491803316, + 7.59989754098342 + ], + [ + 9.918032786885306, + 9.40573770491801 + ], + [ + 11.185963114754259, + 10.238217213114694 + ], + [ + 13.926741803278674, + 12.511526639344083 + ], + [ + 15.233094262295253, + 13.817879098360663 + ], + [ + 18.652663934426528, + 15.758196721311378 + ], + [ + 23.14164959016398, + 18.60143442622939 + ], + [ + 25.42136270491801, + 19.97182377049171 + ], + [ + 29.506915983606632, + 22.51408811475403 + ], + [ + 30.377817622950943, + 22.949538934426073 + ] + ], + "color": "--affine-palette-line-tangerine", + "lineWidth": 10, + "id": "osezYwO_vz", + "index": "b5k", + "seed": 2136351046 + }, + "O_sBKPEzc9": { + "type": "brush", + "xywh": "[1843.0371843374198,1469.345385507623,59.00742827868862,33.19415983606564]", + "points": [ + [ + 5, + 5 + ], + [ + 9.021516393442653, + 8.112192622950943 + ], + [ + 14.682377049180332, + 11.787909836065637 + ], + [ + 15.553278688524642, + 12.65881147540972 + ], + [ + 17.832991803278674, + 14.029200819672269 + ], + [ + 22.05942622950829, + 16.366547131147627 + ], + [ + 29.82710040983602, + 19.978227459016352 + ], + [ + 34.51460040983602, + 21.547131147540995 + ], + [ + 39.86808401639337, + 23.148053278688394 + ], + [ + 43.88960040983602, + 24.147028688524415 + ], + [ + 47.911116803278674, + 25.65189549180309 + ], + [ + 51.33068647540995, + 27.124743852459005 + ], + [ + 54.00742827868862, + 28.194159836065637 + ] + ], + "color": "--affine-palette-line-tangerine", + "lineWidth": 10, + "id": "O_sBKPEzc9", + "index": "b5l", + "seed": 1636916996 + }, + "opXiEAUB93": { + "type": "brush", + "xywh": "[1981.2415900751248,1515.970641655164,67.48591188524597,12.132428278688622]", + "points": [ + [ + 5, + 5.640368852459005 + ], + [ + 19.075307377049057, + 5.640368852459005 + ], + [ + 29.32761270491801, + 5.640368852459005 + ], + [ + 36.429303278688394, + 5 + ], + [ + 39.24692622950806, + 5 + ], + [ + 44.07530737704906, + 5.49948770491801 + ], + [ + 48.29533811475403, + 6.101434426229389 + ], + [ + 53.64882172131138, + 6.632940573770611 + ], + [ + 59.07914959016398, + 7.1324282786886215 + ], + [ + 60.347079918032705, + 7.1324282786886215 + ], + [ + 61.61501024590166, + 7.1324282786886215 + ], + [ + 62.48591188524597, + 7.1324282786886215 + ] + ], + "color": "--affine-palette-line-tangerine", + "lineWidth": 10, + "id": "opXiEAUB93", + "index": "b5m", + "seed": 1371292318 + }, + "htFsN-jXZd": { + "type": "brush", + "xywh": "[2138.330473271846,1515.9002010813933,80.31890368852464,18.07505122950829]", + "points": [ + [ + 5, + 12.607581967213264 + ], + [ + 11.147540983606632, + 13.07505122950829 + ], + [ + 14.567110655737451, + 13.07505122950829 + ], + [ + 19.99103483606541, + 13.07505122950829 + ], + [ + 24.012551229508063, + 12.063268442622984 + ], + [ + 28.700051229508063, + 11.557377049180559 + ], + [ + 35.526383196721326, + 10.417520491803316 + ], + [ + 49.60169057377061, + 9.71311475409857 + ], + [ + 61.92879098360663, + 7.62551229508199 + ], + [ + 68.7551229508199, + 6.485655737704974 + ], + [ + 73.57710040983602, + 5.441854508196684 + ], + [ + 75.31890368852464, + 5 + ] + ], + "color": "--affine-palette-line-tangerine", + "lineWidth": 10, + "id": "htFsN-jXZd", + "index": "b5n", + "seed": 924679438 + }, + "gohqVK-zdz": { + "type": "brush", + "xywh": "[2287.33149786201,1478.047998212541,82.265625,34.51331967213105]", + "points": [ + [ + 5, + 29.225153688524415 + ], + [ + 9.072745901639337, + 29.513319672131047 + ], + [ + 15.969518442622757, + 28.367059426229616 + ], + [ + 26.42033811475403, + 25.11398565573768 + ], + [ + 34.05353483606541, + 22.193903688524415 + ], + [ + 40.950307377049285, + 19.90778688524574 + ], + [ + 50.26127049180286, + 16.821209016393595 + ], + [ + 62.32581967213082, + 12.12090163934431 + ], + [ + 70.36885245901613, + 8.42597336065569 + ], + [ + 77.265625, + 5 + ] + ], + "color": "--affine-palette-line-tangerine", + "lineWidth": 10, + "id": "gohqVK-zdz", + "index": "b5o", + "seed": 1924881192 + }, + "20N_p0pMMU": { + "type": "brush", + "xywh": "[2597.321251960371,1307.767516655164,51.739241803278674,52.12346311475403]", + "points": [ + [ + 5, + 47.12346311475403 + ], + [ + 8.336321721311378, + 44.29943647540995 + ], + [ + 13.087858606557347, + 39.061219262295026 + ], + [ + 18.812756147540767, + 31.914702868852373 + ], + [ + 21.822489754098115, + 28.89216188524597 + ], + [ + 26.343493852459233, + 23.756403688524642 + ], + [ + 34.22003073770475, + 18.614241803278674 + ], + [ + 39.98335040983602, + 13.977971311475358 + ], + [ + 41.488217213114694, + 12.466700819672042 + ], + [ + 44.93340163934408, + 7.292520491803316 + ], + [ + 46.739241803278674, + 5 + ] + ], + "color": "--affine-palette-line-tangerine", + "lineWidth": 10, + "id": "20N_p0pMMU", + "index": "b5p", + "seed": 1564006340 + }, + "GTphdQxvu1": { + "type": "brush", + "xywh": "[2705.1849814685675,1246.765979769918,43.151895491803316,41.98642418032796]", + "points": [ + [ + 5, + 36.98642418032796 + ], + [ + 8.752561475409493, + 34.25204918032796 + ], + [ + 14.54789959016398, + 28.975409836065637 + ], + [ + 17.762551229508063, + 24.140625 + ], + [ + 21.51511270491801, + 19.837346311475358 + ], + [ + 25.16521516393459, + 16.174436475409948 + ], + [ + 28.174948770491937, + 13.151895491803316 + ], + [ + 29.980788934426528, + 11.294825819672042 + ], + [ + 32.894467213114694, + 8.842213114754031 + ], + [ + 35.27023565573745, + 6.889088114754031 + ], + [ + 38.151895491803316, + 5 + ] + ], + "color": "--affine-palette-line-tangerine", + "lineWidth": 10, + "id": "GTphdQxvu1", + "index": "b5q", + "seed": 1722708483 + }, + "lAgaSmb89S": { + "type": "brush", + "xywh": "[2789.6688441734855,1167.891748212541,65.02049180327867,56.61885245901635]", + "points": [ + [ + 5, + 51.61885245901635 + ], + [ + 12.293801229508517, + 46.303790983606405 + ], + [ + 22.245133196721326, + 39.054815573770384 + ], + [ + 31.49205942622939, + 31.101434426229616 + ], + [ + 35.24462090163934, + 26.798155737704974 + ], + [ + 38.15829918032796, + 24.345543032786964 + ], + [ + 44.38908811475403, + 20.176741803278674 + ], + [ + 47.40522540983602, + 17.15420081967204 + ], + [ + 50.95927254098342, + 14.061219262295026 + ], + [ + 52.32966188524597, + 11.76869877049171 + ], + [ + 53.12371926229525, + 10.961834016393368 + ], + [ + 56.33837090163934, + 9.14318647540972 + ], + [ + 58.714139344262094, + 6.754610655737679 + ], + [ + 60.020491803278674, + 5 + ] + ], + "color": "--affine-palette-line-tangerine", + "lineWidth": 10, + "id": "lAgaSmb89S", + "index": "b5r", + "seed": 1740191672 + }, + "JrNM5WOBda": { + "type": "brush", + "xywh": "[2788.89399786201,1148.968848622377,75.86834016393414,99.31864754098365]", + "points": [ + [ + 5, + 6.812243852459005 + ], + [ + 10.481557377049285, + 8.246670081967295 + ], + [ + 20.195952868852146, + 8.944672131147627 + ], + [ + 39.31096311475403, + 9.610655737704974 + ], + [ + 54.596567622950715, + 8.067366803278674 + ], + [ + 61.493340163934135, + 6.319159836065637 + ], + [ + 62.761270491803316, + 5.915727459016352 + ], + [ + 63.5553278688526, + 5.915727459016352 + ], + [ + 64.82325819672133, + 5.915727459016352 + ], + [ + 67.10297131147536, + 5.441854508196684 + ], + [ + 68.84477459016398, + 5 + ], + [ + 69.24180327868862, + 5.397028688524642 + ], + [ + 70.0742827868853, + 6.6649590163935954 + ], + [ + 70.86834016393414, + 7.459016393442653 + ], + [ + 70.86834016393414, + 7.856045081967295 + ], + [ + 70.86834016393414, + 9.12397540983602 + ], + [ + 70.86834016393414, + 10.929815573770384 + ], + [ + 70.86834016393414, + 17.82658811475426 + ], + [ + 70.1959528688526, + 25.869620901639337 + ], + [ + 69.01767418032796, + 33.91265368852464 + ], + [ + 67.90343237704928, + 40.41239754098365 + ], + [ + 67.26306352459005, + 47.51408811475426 + ], + [ + 66.59067622950806, + 55.55712090163934 + ], + [ + 64.8424692622948, + 67.28227459016398 + ], + [ + 63.798668032786736, + 71.96977459016398 + ], + [ + 63.798668032786736, + 73.3785860655737 + ], + [ + 63.39523565573745, + 74.56967213114763 + ], + [ + 63.39523565573745, + 75.83760245901635 + ], + [ + 63.39523565573745, + 78.11731557377038 + ], + [ + 63.39523565573745, + 79.92315573770497 + ], + [ + 63.39523565573745, + 81.72899590163934 + ], + [ + 62.95338114754077, + 82.99692622950829 + ], + [ + 62.51152663934408, + 84.73872950819668 + ], + [ + 62.0696721311474, + 87.01844262295094 + ], + [ + 61.627817622950715, + 90.5660860655737 + ], + [ + 61.12192622950806, + 93.44774590163934 + ], + [ + 60.68007172131138, + 94.31864754098365 + ] + ], + "color": "--affine-palette-line-tangerine", + "lineWidth": 10, + "id": "JrNM5WOBda", + "index": "b5s", + "seed": 1954753003 + }, + "NqQ1110OTY": { + "type": "brush", + "xywh": "[2399.255165894797,1432.2744326387706,95.75819672131183,53.21209016393436]", + "points": [ + [ + 5, + 48.21209016393436 + ], + [ + 9.95645491803316, + 45.88755122950806 + ], + [ + 13.47207991803316, + 43.36449795081944 + ], + [ + 17.762551229508063, + 40.13703893442607 + ], + [ + 24.159836065573927, + 35.30225409836066 + ], + [ + 27.573002049180104, + 33.5924692622948 + ], + [ + 35.07172131147581, + 30.36501024590143 + ], + [ + 40.42520491803316, + 28.213370901639337 + ], + [ + 43.97284836065592, + 26.69569672131138 + ], + [ + 45.98360655737724, + 25.683913934426073 + ], + [ + 52.207991803278674, + 23.53227459016398 + ], + [ + 62.460297131147854, + 19.03688524590143 + ], + [ + 71.77126024590143, + 14.675973360655462 + ], + [ + 83.66290983606541, + 9.104764344262094 + ], + [ + 89.01639344262276, + 5.877305327868726 + ], + [ + 90.75819672131183, + 5 + ] + ], + "color": "--affine-palette-line-tangerine", + "lineWidth": 10, + "id": "NqQ1110OTY", + "index": "b5H", + "seed": 1884794374 + }, + "d86IZAb3VQ": { + "type": "brush", + "xywh": "[2524.255165894797,1384.374842474836,47.903432377049285,51.14369877049194]", + "points": [ + [ + 5, + 46.14369877049194 + ], + [ + 11.589395491803316, + 41.129610655737906 + ], + [ + 18.825563524590507, + 35.69287909836066 + ], + [ + 22.37961065573745, + 33.137807377049285 + ], + [ + 26.234631147541222, + 30.30737704918033 + ], + [ + 29.250768442622757, + 27.816342213114694 + ], + [ + 33.502817622950715, + 25.062756147540995 + ], + [ + 36.045081967213264, + 21.49590163934431 + ], + [ + 37.31301229508199, + 20.2151639344263 + ], + [ + 38.24795081967204, + 18.396516393442653 + ], + [ + 39.08043032786918, + 17.519211065573927 + ], + [ + 39.08043032786918, + 16.712346311475358 + ], + [ + 39.91290983606541, + 15.431608606557347 + ], + [ + 40.412397540983875, + 13.011014344262321 + ], + [ + 40.84784836065592, + 11.73027663934431 + ], + [ + 40.84784836065592, + 10.923411885245969 + ], + [ + 41.244877049180104, + 10.116547131147627 + ], + [ + 41.244877049180104, + 9.309682377049285 + ], + [ + 42.109375, + 7.491034836065637 + ], + [ + 42.50640368852464, + 5.806864754098569 + ], + [ + 42.903432377049285, + 5 + ] + ], + "color": "--affine-palette-line-tangerine", + "lineWidth": 10, + "id": "d86IZAb3VQ", + "index": "b51", + "seed": 1014840334 + }, + "BOQVr4nJPn": { + "type": "brush", + "xywh": "[758.0638968666935,856.0703562336182,72.41071428571422,27.96875]", + "points": [ + [ + 67.41071428571422, + 5 + ], + [ + 60.35714285714289, + 5.11160714285711 + ], + [ + 43.73883928571422, + 10.69196428571422 + ], + [ + 27.48883928571422, + 16.04910714285711 + ], + [ + 18.136160714285552, + 19.776785714285552 + ], + [ + 11.104910714285552, + 21.51785714285711 + ], + [ + 7.109375, + 22.276785714285552 + ], + [ + 5.703125, + 22.96875 + ], + [ + 5, + 22.96875 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 10, + "id": "BOQVr4nJPn", + "index": "b7P", + "seed": 1824565825 + }, + "egSzDjP_cw": { + "type": "brush", + "xywh": "[654.6152361524078,891.1373205193324,65.40178571428578,14.69866071428578]", + "points": [ + [ + 60.40178571428578, + 5 + ], + [ + 51.484375, + 5.926339285714221 + ], + [ + 40.60267857142867, + 7.77901785714289 + ], + [ + 33.23660714285711, + 8.828125 + ], + [ + 21.52901785714289, + 9.69866071428578 + ], + [ + 16.85267857142867, + 9.69866071428578 + ], + [ + 10.87053571428578, + 9.69866071428578 + ], + [ + 7.8125, + 9.69866071428578 + ], + [ + 6.40625, + 9.69866071428578 + ], + [ + 5, + 9.69866071428578 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 10, + "id": "egSzDjP_cw", + "index": "b7Q", + "seed": 1305932090 + }, + "TDTmNJ3PZw": { + "type": "brush", + "xywh": "[491.26702186669354,885.2667848050467,101.02678571428555,28.32589285714289]", + "points": [ + [ + 96.02678571428555, + 23.32589285714289 + ], + [ + 85.59151785714289, + 21.68526785714289 + ], + [ + 57.28794642857133, + 17.00892857142867 + ], + [ + 36.47321428571422, + 13.381696428571558 + ], + [ + 6.40625, + 7.8125 + ], + [ + 5.703125, + 7.109375 + ], + [ + 5, + 6.40625 + ], + [ + 5, + 5 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 10, + "id": "TDTmNJ3PZw", + "index": "b7R", + "seed": 414039155 + }, + "DS1eX6PCJg": { + "type": "brush", + "xywh": "[467.51702186669354,818.1016062336182,20.49107142857133,41.83035714285711]", + "points": [ + [ + 15.491071428571331, + 36.83035714285711 + ], + [ + 11.90848214285711, + 29.67633928571422 + ], + [ + 11.13839285714289, + 26.74107142857133 + ], + [ + 9.609375, + 20.51339285714289 + ], + [ + 8.839285714285552, + 17.45535714285711 + ], + [ + 8.069196428571331, + 13.459821428571331 + ], + [ + 6.473214285714221, + 9.464285714285552 + ], + [ + 5.703125, + 6.40625 + ], + [ + 5, + 5 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 10, + "id": "DS1eX6PCJg", + "index": "b7S", + "seed": 2080642130 + }, + "JTUmMg4ctN": { + "type": "brush", + "xywh": "[421.28934329526487,679.943124090761,21.23883928571422,73.08035714285722]", + "points": [ + [ + 16.23883928571422, + 68.08035714285722 + ], + [ + 15.53571428571422, + 61.506696428571445 + ], + [ + 16.22767857142867, + 60.100446428571445 + ], + [ + 16.22767857142867, + 58.694196428571445 + ], + [ + 16.22767857142867, + 55.881696428571445 + ], + [ + 16.22767857142867, + 53.649553571428555 + ], + [ + 16.22767857142867, + 47.421875 + ], + [ + 13.694196428571558, + 33.01339285714289 + ], + [ + 11.70758928571422, + 24.587053571428555 + ], + [ + 10.11160714285711, + 20.59151785714289 + ], + [ + 8.34821428571422, + 13.44866071428578 + ], + [ + 5, + 5 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 10, + "id": "JTUmMg4ctN", + "index": "b7T", + "seed": 1148380737 + }, + "Pjfc99bOvq": { + "type": "brush", + "xywh": "[355.19559329526487,598.458749090761,62.14285714285711,50.04464285714289]", + "points": [ + [ + 57.14285714285711, + 45.04464285714289 + ], + [ + 54.26339285714289, + 39.296875 + ], + [ + 49.20758928571422, + 31.785714285714334 + ], + [ + 47.09821428571422, + 28.973214285714334 + ], + [ + 45.69196428571422, + 27.566964285714334 + ], + [ + 43.34821428571422, + 24.397321428571445 + ], + [ + 41.94196428571422, + 23.694196428571445 + ], + [ + 41.23883928571422, + 22.991071428571445 + ], + [ + 40.53571428571422, + 22.287946428571445 + ], + [ + 34.78794642857156, + 19.754464285714334 + ], + [ + 27.75669642857156, + 17.109375 + ], + [ + 19.56473214285711, + 13.47098214285711 + ], + [ + 14.631696428571558, + 11.004464285714334 + ], + [ + 10.16741071428578, + 8.00223214285711 + ], + [ + 7.93526785714289, + 6.473214285714334 + ], + [ + 5.703125, + 5.703125 + ], + [ + 5, + 5 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 10, + "id": "Pjfc99bOvq", + "index": "b7U", + "seed": 554791730 + }, + "DZmWbBt4Z0": { + "type": "brush", + "xywh": "[271.166575438122,582.0413383764753,50.91517857142867,11]", + "points": [ + [ + 45.91517857142867, + 5.881696428571331 + ], + [ + 38.40401785714289, + 5 + ], + [ + 25.41294642857156, + 5 + ], + [ + 22.47767857142867, + 5 + ], + [ + 20.36830357142867, + 5 + ], + [ + 19.66517857142867, + 5 + ], + [ + 17.55580357142867, + 5 + ], + [ + 16.85267857142867, + 5 + ], + [ + 15.446428571428669, + 5 + ], + [ + 14.040178571428669, + 5 + ], + [ + 13.337053571428669, + 5 + ], + [ + 12.633928571428669, + 5 + ], + [ + 10.40178571428578, + 5 + ], + [ + 7.23214285714289, + 5 + ], + [ + 5, + 5 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 10, + "id": "DZmWbBt4Z0", + "index": "b7V", + "seed": 2130229815 + }, + "m6-LmLuT6f": { + "type": "brush", + "xywh": "[252.20452186669354,546.2600883764753,75.75892857142844,90.68080357142856]", + "points": [ + [ + 70.75892857142844, + 5 + ], + [ + 60.3125, + 8.325892857142776 + ], + [ + 49.82142857142844, + 15.37946428571422 + ], + [ + 44.54241071428555, + 18.93973214285711 + ], + [ + 40.078125, + 22.667410714285666 + ], + [ + 38.671875, + 24.05133928571422 + ], + [ + 37.96875, + 24.743303571428555 + ], + [ + 35.73660714285711, + 26.19419642857133 + ], + [ + 33.50446428571422, + 27.64508928571422 + ], + [ + 25.77008928571422, + 31.78571428571422 + ], + [ + 23.53794642857133, + 33.23660714285711 + ], + [ + 20.479910714285552, + 34.75446428571422 + ], + [ + 14.274553571428442, + 39.073660714285666 + ], + [ + 8.995535714285552, + 42.633928571428555 + ], + [ + 5, + 45.78125 + ], + [ + 5, + 46.47321428571422 + ], + [ + 5, + 47.165178571428555 + ], + [ + 7.45535714285711, + 49.48660714285711 + ], + [ + 13.928571428571331, + 54.966517857142776 + ], + [ + 17.901785714285552, + 58.11383928571422 + ], + [ + 26.83035714285711, + 65.22321428571422 + ], + [ + 33.01339285714266, + 68.78348214285711 + ], + [ + 38.27008928571422, + 72.34375 + ], + [ + 40.546875, + 74.62053571428567 + ], + [ + 44.6875, + 78.00223214285711 + ], + [ + 46.89732142857133, + 79.453125 + ], + [ + 48.28125, + 80.14508928571422 + ], + [ + 49.66517857142844, + 80.83705357142856 + ], + [ + 50.35714285714289, + 81.52901785714278 + ], + [ + 51.74107142857133, + 82.22098214285711 + ], + [ + 53.125, + 82.91294642857144 + ], + [ + 56.58482142857133, + 85.68080357142856 + ], + [ + 57.27678571428555, + 85.68080357142856 + ] + ], + "color": "--affine-palette-line-green", + "lineWidth": 10, + "id": "m6-LmLuT6f", + "index": "b7W", + "seed": 642894579 + } + } + }, + "children": [] + }, + { + "type": "block", + "id": "1cFTd-rwDr", + "flavour": "affine:note", + "props": { + "xywh": "[1782.1527708473825,83.59728260421294,554.4267667459387,568]", + "background": "--affine-tag-blue", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "aPk03I3k9L", + "flavour": "affine:paragraph", + "props": { + "type": "h3", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Organise Your Pages" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "4_plt-pF5i", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Manage your favourites and add links between your pages to easily navigate between them. This makes it easy to keep track of your content, and ensures that you always know where everything is." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "5O-z_KtfdV", + "flavour": "affine:image", + "props": { + "caption": "", + "sourceId": "/static/1326bc48553a572c6756d9ee1b30a0dfdda26222fc2d2c872b14e609.gif", + "width": 0, + "height": 0, + "index": "a0", + "xywh": "[0,0,0,0]", + "rotate": 0, + "size": -1, + "type": "image" + }, + "children": [] + } + ] + }, + { + "type": "block", + "id": "A2hTNhHJSo", + "flavour": "affine:note", + "props": { + "xywh": "[1862.2162713385649,667.8687461185463,471.2475208977704,510]", + "background": "--affine-tag-red", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "1t5gAmmDk1", + "flavour": "affine:paragraph", + "props": { + "type": "h3", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Write Your Content" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "Ru5RZxl2cw", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Easily create rich, interactive pages that combine text, images, and other media to create a dynamic, engaging experience." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "m09CQTVNta", + "flavour": "affine:image", + "props": { + "caption": "", + "sourceId": "/static/28516717d63e469cd98729ff46be6595711898bab3dc43302319a987.gif", + "width": 0, + "height": 0, + "index": "a0", + "xywh": "[0,0,0,0]", + "rotate": 0, + "size": -1, + "type": "image" + }, + "children": [] + } + ] + }, + { + "type": "block", + "id": "JSoC9zIZDz", + "flavour": "affine:note", + "props": { + "xywh": "[1310.31891616522,676.1486476697154,537.1322406806248,556]", + "background": "--affine-tag-purple", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "RYwAAsT0jt", + "flavour": "affine:paragraph", + "props": { + "type": "h3", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Draw and Be Creative" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "yui0v4a-DG", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Our Edgeless Mode can be utilised as a whiteboard, allowing you to create content that extends beyond the boundaries of a traditional page - limited only by your imagination." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "CuW0As_WD5", + "flavour": "affine:image", + "props": { + "caption": "", + "sourceId": "/static/9288be57321c8772d04e05dbb69a22742372b3534442607a2d6a9998.gif", + "width": 0, + "height": 0, + "index": "a0", + "xywh": "[0,0,0,0]", + "rotate": 0, + "size": -1, + "type": "image" + }, + "children": [] + } + ] + }, + { + "type": "block", + "id": "Vq_8QO3ruz", + "flavour": "affine:note", + "props": { + "xywh": "[824.3933427871591,677.7709857969486,475.6451530544002,536]", + "background": "--affine-tag-blue", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "Z6tZpqYD1i", + "flavour": "affine:paragraph", + "props": { + "type": "h3", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Plan Effectively and Efficiently" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "cSq4fYa62E", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "With powerful databases, you can create content within tables and leverage powerful features such as adding due dates, tracking progress, and adding tags and status labels." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "e-ekSJPKh0", + "flavour": "affine:image", + "props": { + "caption": "", + "sourceId": "/static/c820edeeba50006b531883903f5bb0b96bf523c9a6b3ce5868f03db5.gif", + "width": 0, + "height": 0, + "index": "a0", + "xywh": "[0,0,0,0]", + "rotate": 0, + "size": -1, + "type": "image" + }, + "children": [] + } + ] + }, + { + "type": "block", + "id": "g5OrKM5Fb6", + "flavour": "affine:note", + "props": { + "xywh": "[817.0745095091336,1272.4084873924633,586.3018678030073,731]", + "background": "--affine-tag-green", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "YQPpZUitL9", + "flavour": "affine:paragraph", + "props": { + "type": "h2", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "More support?" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "jNb1ieggGw", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "There are some useful resources in the Help and Feedback section." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "yln9MU-iVm", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "If you are looking for more support, you can come and join the awesome " + }, + { + "insert": "AFFiNE Community", + "attributes": { + "link": "https://community.affine.pro" + } + }, + { + "insert": " . Whether you want to share new ideas or interact with other like-minded individuals - we look forward to having you." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "rY1fVETRzE", + "flavour": "affine:image", + "props": { + "caption": "", + "sourceId": "/static/e93536e1be97e3b5206d43bf0793fdef24e60044d174f0abdefebe08.gif", + "width": 0, + "height": 0, + "index": "a0", + "xywh": "[0,0,0,0]", + "rotate": 0, + "size": -1, + "type": "image" + }, + "children": [] + }, + { + "type": "block", + "id": "snOX3HBubz", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + } + ] + }, + { + "type": "block", + "id": "3An3wRFKN_", + "flavour": "affine:note", + "props": { + "xywh": "[-264.94381566608683,389.00823320424837,494.2811077047478,380]", + "background": "--affine-background-secondary-color", + "index": "a2", + "hidden": true, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "KZrhdN52ZD", + "flavour": "affine:divider", + "props": {}, + "children": [] + }, + { + "type": "block", + "id": "3MnKwqEw_Q", + "flavour": "affine:database", + "props": { + "views": [ + { + "id": "default", + "name": "Table", + "mode": "table", + "columns": [ + { + "id": "C6B5CThJgi", + "width": 158 + }, + { + "width": 200, + "id": "fafWu_k4eb" + }, + { + "width": 200, + "id": "xta1ActkB1" + } + ], + "filter": { + "type": "group", + "op": "and", + "conditions": [] + } + } + ], + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "AFFiNE learning progress" + } + ] + }, + "cells": { + "nDNHkufUpb": { + "fafWu_k4eb": { + "columnId": "fafWu_k4eb", + "value": [ + "MXPthta90B" + ] + }, + "xta1ActkB1": { + "columnId": "xta1ActkB1", + "value": 100 + } + }, + "OwfNHKkFw6": { + "xta1ActkB1": { + "columnId": "xta1ActkB1", + "value": 41 + }, + "fafWu_k4eb": { + "columnId": "fafWu_k4eb", + "value": [ + "4LhPvJWXU2" + ] + } + }, + "zCn640WobP": { + "xta1ActkB1": { + "columnId": "xta1ActkB1", + "value": 23 + }, + "fafWu_k4eb": { + "columnId": "fafWu_k4eb", + "value": [ + "w6YYhk21ZT" + ] + } + } + }, + "columns": [ + { + "id": "C6B5CThJgi", + "type": "title", + "name": "Title", + "data": {} + }, + { + "type": "title", + "id": "3MnKwqEw_Q", + "name": "Title", + "data": {} + }, + { + "name": "Tag", + "type": "multi-select", + "data": { + "options": [ + { + "id": "MXPthta90B", + "value": "Tag1", + "color": "var(--affine-tag-yellow)" + }, + { + "id": "4LhPvJWXU2", + "value": "Tag2", + "color": "var(--affine-tag-pink)" + }, + { + "id": "w6YYhk21ZT", + "value": "Tag3", + "color": "var(--affine-tag-teal)" + }, + { + "id": "tCIAywo8av", + "value": "How to write", + "color": "var(--affine-tag-gray)" + } + ] + }, + "id": "fafWu_k4eb" + }, + { + "type": "progress", + "name": "Progress", + "data": {}, + "id": "xta1ActkB1" + } + ], + "mode": "table" + }, + "children": [ + { + "type": "block", + "id": "nDNHkufUpb", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Download AFFiNE" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "OwfNHKkFw6", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Learn AFFiNE" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "zCn640WobP", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Use AFFiNE" + } + ] + } + }, + "children": [] + } + ] + } + ] + }, + { + "type": "block", + "id": "U2hR9Lu1E7", + "flavour": "affine:note", + "props": { + "xywh": "[2918.2644723261433,881.0630462339941,539.4086027654356,462]", + "background": "--affine-background-secondary-color", + "index": "a2", + "hidden": true, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "LYes52XNDN", + "flavour": "affine:image", + "props": { + "caption": "", + "sourceId": "/static/047ebf2c9a5c7c9d8521c2ea5e6140ff7732ef9e28a9f944e9bf3ca4.png", + "width": 0, + "height": 0, + "index": "a0", + "xywh": "[0,0,0,0]", + "rotate": 0, + "size": -1, + "type": "image" + }, + "children": [] + } + ] + }, + { + "type": "block", + "id": "nOERveFG0j", + "flavour": "affine:note", + "props": { + "xywh": "[2919.8341116576826,1349.0080470072992,535.7138283708327,444]", + "background": "--affine-background-secondary-color", + "index": "a2", + "hidden": true, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "SjyfxmcAjc", + "flavour": "affine:image", + "props": { + "caption": "", + "sourceId": "/static/6aa785ee927547ce9dd9d7b43e01eac948337fe57571443e87bc3a60.png", + "width": 0, + "height": 0, + "index": "a0", + "xywh": "[0,0,0,0]", + "rotate": 0, + "size": -1, + "type": "image" + }, + "children": [] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/packages/frontend/templates/onboarding/GWsRbUtMF4Ee6foVg-H9R.snapshot.json b/packages/frontend/templates/onboarding/GWsRbUtMF4Ee6foVg-H9R.snapshot.json new file mode 100644 index 0000000000..e134df4fb6 --- /dev/null +++ b/packages/frontend/templates/onboarding/GWsRbUtMF4Ee6foVg-H9R.snapshot.json @@ -0,0 +1,310 @@ +{ + "type": "page", + "meta": { + "id": "GWsRbUtMF4Ee6foVg-H9R", + "title": "Meeting Summary ", + "createDate": 1691635388447, + "tags": [ + "Oe5dSe1DDJ" + ] + }, + "blocks": { + "type": "block", + "id": "EYboj1it1i", + "flavour": "affine:page", + "props": { + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Meeting Summary " + } + ] + } + }, + "children": [ + { + "type": "block", + "id": "86K_nlhKXG", + "flavour": "affine:surface", + "props": { + "elements": {} + }, + "children": [] + }, + { + "type": "block", + "id": "q7fYMX90uJ", + "flavour": "affine:note", + "props": { + "xywh": "[0,0,800,1237]", + "background": "--affine-background-secondary-color", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "IuRC73HZLH", + "flavour": "affine:image", + "props": { + "caption": "", + "sourceId": "/static/f9yKnlNMgKhF-CxOgHBsXkxfViCCkC6KwTv6Uj2Fcjw=.png", + "width": 752, + "height": 501.6328125, + "index": "a0", + "xywh": "[0,0,0,0]", + "rotate": 0, + "size": -1 + }, + "children": [] + }, + { + "type": "block", + "id": "YuOqeDHzoe", + "flavour": "affine:paragraph", + "props": { + "type": "quote", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Project:" + }, + { + "insert": " [Link the project plan]\n", + "attributes": { + "background": "var(--affine-text-highlight-grey)" + } + }, + { + "insert": "]Attendees: " + }, + { + "insert": "[@mention teammates]", + "attributes": { + "background": "var(--affine-text-highlight-grey)" + } + }, + { + "insert": "\nDate & Time:" + }, + { + "insert": " [type /today]", + "attributes": { + "background": "var(--affine-text-highlight-grey)" + } + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "jPGxHHA_QX", + "flavour": "affine:paragraph", + "props": { + "type": "h1", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Agenda" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "sCk70nT5eX", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Write down the main topic to discuss." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "iKwcxVFpCH", + "flavour": "affine:list", + "props": { + "type": "bulleted", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "E.g. content roadmap" + } + ] + }, + "checked": false, + "collapsed": false + }, + "children": [] + }, + { + "type": "block", + "id": "RPED0t8fSj", + "flavour": "affine:list", + "props": { + "type": "bulleted", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Get aligned on the main upcoming topics" + } + ] + }, + "checked": false, + "collapsed": false + }, + "children": [] + }, + { + "type": "block", + "id": "D3Y93H8WvU", + "flavour": "affine:paragraph", + "props": { + "type": "h1", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Notes" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "BfO7S4Gkfm", + "flavour": "affine:paragraph", + "props": { + "type": "quote", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Cover any key information discussed in the meeting." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "Stcz30R0Ad", + "flavour": "affine:paragraph", + "props": { + "type": "h1", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Outcomes" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "YKIkBUmv01", + "flavour": "affine:paragraph", + "props": { + "type": "quote", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Summarize key decisions in a tidy paragraph and communicate them to the team." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "JzAgpzljrp", + "flavour": "affine:paragraph", + "props": { + "type": "h1", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Action items" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "FSkrwB-T33", + "flavour": "affine:list", + "props": { + "type": "todo", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "E.g. @jess review the content roadmap" + } + ] + }, + "checked": false, + "collapsed": false + }, + "children": [] + }, + { + "type": "block", + "id": "abGz-82vvA", + "flavour": "affine:list", + "props": { + "type": "todo", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "E.g. @mike contact copywriter freelancers" + } + ] + }, + "checked": false, + "collapsed": false + }, + "children": [] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/packages/frontend/templates/onboarding/RCpxnWMtBWmUZy5awgJBh.snapshot.json b/packages/frontend/templates/onboarding/RCpxnWMtBWmUZy5awgJBh.snapshot.json new file mode 100644 index 0000000000..49e6b5182d --- /dev/null +++ b/packages/frontend/templates/onboarding/RCpxnWMtBWmUZy5awgJBh.snapshot.json @@ -0,0 +1,457 @@ +{ + "type": "page", + "meta": { + "id": "RCpxnWMtBWmUZy5awgJBh", + "title": "OKR Template", + "createDate": 1691636192263, + "tags": [ + "q3mceOl_zi", + "g1L5dXKctL" + ] + }, + "blocks": { + "type": "block", + "id": "0tJt1nfXpr", + "flavour": "affine:page", + "props": { + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "OKR Template" + } + ] + } + }, + "children": [ + { + "type": "block", + "id": "-SeDPuI6pE", + "flavour": "affine:surface", + "props": { + "elements": {} + }, + "children": [] + }, + { + "type": "block", + "id": "gjPKqwOdlZ", + "flavour": "affine:note", + "props": { + "xywh": "[0,0,800,1418]", + "background": "--affine-background-secondary-color", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "syUYSO6Hlw", + "flavour": "affine:image", + "props": { + "caption": "", + "sourceId": "/static/VuXYyM9JUv1Fv_qjg1v5Go4Zksz0r4NXFeh3Na7JkIc=.png", + "width": 752, + "height": 501.2734375, + "index": "a0", + "xywh": "[0,0,0,0]", + "rotate": 0, + "size": -1 + }, + "children": [] + }, + { + "type": "block", + "id": "B12ua5IVdH", + "flavour": "affine:paragraph", + "props": { + "type": "quote", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Using " + }, + { + "insert": "OKR (Objectives and Key Results)", + "attributes": { + "link": "https://en.wikipedia.org/wiki/Objectives_and_key_results" + } + }, + { + "insert": " helps teams work together to create and break down their goals. They also figure out how to reach those goals and use their own ideas and creativity. Every week, the team checks in to make sure everyone understands the goals, shares their thoughts, and makes changes as needed to get the job done well and quickly." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "UWKiht4_yF", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "tsqRR7Aqqe", + "flavour": "affine:paragraph", + "props": { + "type": "h2", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "I. Synchronization of team OKR progress " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "TzALQTyjsF", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "-RNdJMcB-M", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "All team members collectively update the latest progress and the next step plan for the person responsible for the OKR. These updates are discussed during the weekly Team OKR meeting." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "_E43A1XjkR", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "lbLc-rsTQ_", + "flavour": "affine:paragraph", + "props": { + "type": "h1", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "O1: Fourth quarter revenue 1 million " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "IbLsSTqZWu", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "LfH4-lvRy1", + "flavour": "affine:database", + "props": { + "views": [ + { + "id": "BjTEwK-3iR", + "name": "table", + "mode": "table", + "columns": [ + { + "id": "f15PcPjtU8", + "width": 246 + }, + { + "id": "LfH4-lvRy1", + "hide": false, + "width": 260 + }, + { + "id": "ftL1qPRgYl", + "hide": false, + "width": 200 + }, + { + "id": "3s4GtwNZTH", + "hide": false, + "width": 108 + }, + { + "id": "IwZCoBvV9W", + "hide": false, + "width": 142 + } + ], + "filter": { + "type": "group", + "op": "and", + "conditions": [] + } + } + ], + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "KR setting & reviewing " + } + ] + }, + "cells": { + "C75yOBGfeP": { + "ftL1qPRgYl": { + "columnId": "ftL1qPRgYl", + "value": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "the planning case has been reviewed, see doc for details " + } + ] + } + }, + "IwZCoBvV9W": { + "columnId": "IwZCoBvV9W", + "value": 85 + } + }, + "iouBY8Zk33": { + "ftL1qPRgYl": { + "columnId": "ftL1qPRgYl", + "value": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "worning on it " + } + ] + } + }, + "IwZCoBvV9W": { + "columnId": "IwZCoBvV9W", + "value": 46 + }, + "3s4GtwNZTH": { + "columnId": "3s4GtwNZTH", + "value": "Qp1kGLAbfE" + } + }, + "jRFdpU21Jf": { + "ftL1qPRgYl": { + "columnId": "ftL1qPRgYl", + "value": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "waiting " + } + ] + } + }, + "IwZCoBvV9W": { + "columnId": "IwZCoBvV9W", + "value": 3 + }, + "3s4GtwNZTH": { + "columnId": "3s4GtwNZTH", + "value": "yCF1NgLWhh" + } + }, + "CLLfj990JA": { + "ftL1qPRgYl": { + "columnId": "ftL1qPRgYl", + "value": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "The planning case has been reviewed, see doc for details " + } + ] + } + }, + "IwZCoBvV9W": { + "columnId": "IwZCoBvV9W", + "value": 70 + }, + "3s4GtwNZTH": { + "columnId": "3s4GtwNZTH", + "value": "KHKc4e79W8" + } + } + }, + "columns": [ + { + "id": "f15PcPjtU8", + "type": "title", + "name": "Key Resluts ", + "data": {} + }, + { + "type": "title", + "id": "LfH4-lvRy1", + "name": "Title", + "data": {} + }, + { + "type": "progress", + "name": "progress bar ", + "data": {}, + "id": "IwZCoBvV9W" + }, + { + "type": "rich-text", + "name": "Notes ", + "data": { + "options": [] + }, + "id": "ftL1qPRgYl" + }, + { + "type": "select", + "name": "priority ", + "data": { + "options": [ + { + "id": "yCF1NgLWhh", + "value": "not urgent", + "color": "var(--affine-tag-blue)" + }, + { + "id": "Qp1kGLAbfE", + "value": "urgent", + "color": "var(--affine-tag-red)" + }, + { + "id": "KHKc4e79W8", + "value": "important", + "color": "var(--affine-tag-yellow)" + } + ] + }, + "id": "3s4GtwNZTH" + } + ] + }, + "children": [ + { + "type": "block", + "id": "CLLfj990JA", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "The campus activity was successfully released, and the exposure was not less than 1 million people" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "iouBY8Zk33", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "describe Key results " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "jRFdpU21Jf", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "KR1: Describe Key results " + } + ] + } + }, + "children": [] + } + ] + }, + { + "type": "block", + "id": "PmstSRjisx", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/packages/frontend/templates/onboarding/Scod6coKmJJ-waH1-jkiW.snapshot.json b/packages/frontend/templates/onboarding/Scod6coKmJJ-waH1-jkiW.snapshot.json new file mode 100644 index 0000000000..01584cef2a --- /dev/null +++ b/packages/frontend/templates/onboarding/Scod6coKmJJ-waH1-jkiW.snapshot.json @@ -0,0 +1,330 @@ +{ + "type": "page", + "meta": { + "id": "Scod6coKmJJ-waH1-jkiW", + "title": "Brief Event planning ", + "createDate": 1691634722239, + "tags": [ + "ze07JVwBu4" + ] + }, + "blocks": { + "type": "block", + "id": "SMCf2aOH8T", + "flavour": "affine:page", + "props": { + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Brief Event planning " + } + ] + } + }, + "children": [ + { + "type": "block", + "id": "_07_dOaECY", + "flavour": "affine:surface", + "props": { + "elements": {} + }, + "children": [] + }, + { + "type": "block", + "id": "0n7YghSHPc", + "flavour": "affine:note", + "props": { + "xywh": "[0,0,800,527]", + "background": "--affine-background-secondary-color", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "d6Alacwr32", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "9slwdYgqgq", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "An event brief outline the goals and key elements needed to plan a successful event. This AFFiNE Event Planning template will help you gain alignment and kick off your event planning. " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "6dH3Amz0rn", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "ObsV2gBxUe", + "flavour": "affine:database", + "props": { + "views": [ + { + "id": "sfIqqrnI2q", + "name": "table", + "mode": "table", + "columns": [ + { + "id": "WtNvmw4ls-", + "width": 186 + }, + { + "id": "ObsV2gBxUe", + "hide": false, + "width": 246 + }, + { + "id": "FCYCytB4wx", + "hide": false, + "width": 162 + }, + { + "id": "mGltZNTJmw", + "hide": false, + "width": 176 + }, + { + "id": "ozFBusFd0s", + "hide": false, + "width": 200 + } + ], + "filter": { + "type": "group", + "op": "and", + "conditions": [] + } + } + ], + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Event Planning" + } + ] + }, + "cells": { + "pFlIzSXLOj": { + "mGltZNTJmw": { + "columnId": "mGltZNTJmw", + "value": [ + "bsTnd_L1aE", + "oU49ElCHCG" + ] + }, + "FCYCytB4wx": { + "columnId": "FCYCytB4wx", + "value": 100 + }, + "ozFBusFd0s": { + "columnId": "ozFBusFd0s", + "value": "https://affine.pro/blog/free-event-planning-templates-for-professsional-2023" + } + }, + "4XPamQdWpq": { + "mGltZNTJmw": { + "columnId": "mGltZNTJmw", + "value": [ + "oU49ElCHCG", + "d6riek6K4d" + ] + }, + "FCYCytB4wx": { + "columnId": "FCYCytB4wx", + "value": 50 + } + }, + "3uJL-VNu7K": { + "mGltZNTJmw": { + "columnId": "mGltZNTJmw", + "value": [ + "bsTnd_L1aE", + "z3vOXFIT-T" + ] + }, + "FCYCytB4wx": { + "columnId": "FCYCytB4wx", + "value": 8 + } + } + }, + "columns": [ + { + "id": "WtNvmw4ls-", + "type": "title", + "name": "Title", + "data": {} + }, + { + "type": "title", + "id": "ObsV2gBxUe", + "name": "Title", + "data": {} + }, + { + "type": "multi-select", + "name": "Tag", + "data": { + "options": [ + { + "id": "z3vOXFIT-T", + "value": "not important", + "color": "var(--affine-tag-teal)" + }, + { + "id": "ZbJHxXJK3L", + "value": "nit important", + "color": "var(--affine-tag-pink)" + }, + { + "id": "d6riek6K4d", + "value": "important", + "color": "var(--affine-tag-yellow)" + }, + { + "id": "oU49ElCHCG", + "value": "urgent", + "color": "var(--affine-tag-purple)" + }, + { + "id": "bsTnd_L1aE", + "value": "must", + "color": "var(--affine-tag-green)" + }, + { + "id": "hHjqAauZu6", + "value": "urgent", + "color": "var(--affine-tag-gray)" + } + ] + }, + "id": "mGltZNTJmw" + }, + { + "type": "progress", + "name": "progress", + "data": {}, + "id": "FCYCytB4wx" + }, + { + "type": "link", + "name": "additional link ", + "data": {}, + "id": "ozFBusFd0s" + } + ] + }, + "children": [ + { + "type": "block", + "id": "pFlIzSXLOj", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "research" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "4XPamQdWpq", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "book accommondation" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "3uJL-VNu7K", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "arrange transportation " + } + ] + } + }, + "children": [] + } + ] + }, + { + "type": "block", + "id": "xf1m62_pbc", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/packages/frontend/templates/onboarding/b1F2xKjgZ4ISHhXNe1kck.snapshot.json b/packages/frontend/templates/onboarding/b1F2xKjgZ4ISHhXNe1kck.snapshot.json new file mode 100644 index 0000000000..0406d5cf43 --- /dev/null +++ b/packages/frontend/templates/onboarding/b1F2xKjgZ4ISHhXNe1kck.snapshot.json @@ -0,0 +1,750 @@ +{ + "type": "page", + "meta": { + "id": "b1F2xKjgZ4ISHhXNe1kck", + "title": "Annual Performance Review", + "createDate": 1691575011078, + "tags": [ + "8qcYPCTK0h" + ] + }, + "blocks": { + "type": "block", + "id": "mMBWzZtpvM", + "flavour": "affine:page", + "props": { + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Annual Performance Review" + } + ] + } + }, + "children": [ + { + "type": "block", + "id": "XSMCxudQf4", + "flavour": "affine:surface", + "props": { + "elements": {} + }, + "children": [] + }, + { + "type": "block", + "id": "2xxenQgE1-", + "flavour": "affine:note", + "props": { + "xywh": "[0,0,800,91]", + "background": "--affine-background-secondary-color", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "G7wJk4W9lz", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + } + ] + }, + { + "type": "block", + "id": "ca2-VFQV45", + "flavour": "affine:note", + "props": { + "xywh": "[0,0,800,1719]", + "background": "--affine-background-secondary-color", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "viFZDjrBqJ", + "flavour": "affine:image", + "props": { + "caption": "", + "sourceId": "/static/gZLmSgmwumNdgf0eIfOSW44emctrLyFUaZapbk8eZ6s=.png", + "width": 752, + "height": 422.9140625, + "index": "a0", + "xywh": "[0,0,0,0]", + "rotate": 0, + "size": -1 + }, + "children": [] + }, + { + "type": "block", + "id": "XPiPjHYnvE", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Employee Information:", + "attributes": { + "underline": true, + "bold": true + } + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "qpyYi7g_c6", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Name:", + "attributes": { + "bold": true + } + }, + { + "insert": " " + }, + { + "insert": "[Employee Name]", + "attributes": { + "background": "var(--affine-text-highlight-grey)" + } + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "NpbigpLTNh", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Department:", + "attributes": { + "bold": true + } + }, + { + "insert": " " + }, + { + "insert": "[Department Name]", + "attributes": { + "background": "var(--affine-text-highlight-grey)" + } + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "e1ax4lVFjW", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Job Title:", + "attributes": { + "bold": true + } + }, + { + "insert": " " + }, + { + "insert": "[Job Title]", + "attributes": { + "background": "var(--affine-text-highlight-grey)" + } + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "rEKXjYtGDR", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Date of Joining:", + "attributes": { + "bold": true + } + }, + { + "insert": " " + }, + { + "insert": "[Date of Joining]", + "attributes": { + "background": "var(--affine-text-highlight-grey)" + } + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "gYcGYxQ88e", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Review Date: ", + "attributes": { + "bold": true + } + }, + { + "insert": " " + }, + { + "insert": "[Review Date]", + "attributes": { + "background": "var(--affine-text-highlight-grey)" + } + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "OyFSyuWbzw", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "VcUt-bpjIK", + "flavour": "affine:database", + "props": { + "views": [ + { + "id": "bIFdicaEON", + "name": "table", + "mode": "table", + "columns": [ + { + "id": "jPry_IOhQt", + "width": 343 + }, + { + "id": "VcUt-bpjIK", + "hide": false, + "width": 260 + }, + { + "id": "teyC7JgUni", + "hide": false, + "width": 200 + } + ], + "filter": { + "type": "group", + "op": "and", + "conditions": [] + } + } + ], + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Employee's performance " + } + ] + }, + "cells": { + "VwZ3681tdd": { + "teyC7JgUni": { + "columnId": "teyC7JgUni", + "value": [ + "rwjdYVF3jd" + ] + } + }, + "jT3Oo2XSeJ": { + "teyC7JgUni": { + "columnId": "teyC7JgUni", + "value": [ + "4dHxaZ8cKk" + ] + } + }, + "RviIfLJUPX": { + "teyC7JgUni": { + "columnId": "teyC7JgUni", + "value": [ + "wgvB2fzMu6" + ] + } + } + }, + "columns": [ + { + "id": "jPry_IOhQt", + "type": "title", + "name": "Title", + "data": {} + }, + { + "type": "title", + "id": "VcUt-bpjIK", + "name": "Title", + "data": {} + }, + { + "type": "multi-select", + "name": "score ", + "data": { + "options": [ + { + "id": "rwjdYVF3jd", + "value": "awsome", + "color": "var(--affine-tag-yellow)" + }, + { + "id": "4dHxaZ8cKk", + "value": "well", + "color": "var(--affine-tag-purple)" + }, + { + "id": "wgvB2fzMu6", + "value": "good", + "color": "var(--affine-tag-green)" + }, + { + "id": "YGPLhMJkni", + "value": "done", + "color": "var(--affine-tag-pink)" + }, + { + "id": "Q1-TuffR-9", + "value": "in progress", + "color": "var(--affine-tag-white)" + } + ] + }, + "id": "teyC7JgUni" + } + ] + }, + "children": [ + { + "type": "block", + "id": "VwZ3681tdd", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "[List Employee's Performance Goal 1]" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "jT3Oo2XSeJ", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "[List Employee's Performance Goal 2]" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "RviIfLJUPX", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "[List Employee's Performance Goal 3]" + } + ] + } + }, + "children": [] + } + ] + }, + { + "type": "block", + "id": "a0xWifxvFi", + "flavour": "affine:database", + "props": { + "views": [ + { + "id": "MvodpC_iAj", + "name": "table", + "mode": "table", + "columns": [ + { + "id": "lvyHgZEelb", + "width": 354 + }, + { + "id": "a0xWifxvFi", + "hide": false, + "width": 260 + }, + { + "id": "ZdTyvkjQwz", + "hide": false, + "width": 200 + } + ], + "filter": { + "type": "group", + "op": "and", + "conditions": [] + } + } + ], + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Job responsibility " + } + ] + }, + "cells": {}, + "columns": [ + { + "id": "lvyHgZEelb", + "type": "title", + "name": "Title", + "data": {} + }, + { + "type": "title", + "id": "a0xWifxvFi", + "name": "Title", + "data": {} + }, + { + "type": "multi-select", + "name": "Tag", + "data": { + "options": [] + }, + "id": "ZdTyvkjQwz" + } + ] + }, + "children": [ + { + "type": "block", + "id": "ZEi_-9SS-Y", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "[List Job Responsibility 1]" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "8LPVaagwtP", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "[List Job Responsibility 2]" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "lYnANRxW0A", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "[List Job Responsibility 3]" + } + ] + } + }, + "children": [] + } + ] + }, + { + "type": "block", + "id": "YiQINiDM6W", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Employee's Signature: " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "Zp-YtzbUUK", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Date: " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "ZMfsmg_5oZ", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "XH7xQMlX_0", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Manager's Signature: " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "mBO-_pWGoi", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Date: " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "X_SNf6rQf4", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "0LF9mjFcX9", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "[Company Name]", + "attributes": { + "background": "var(--affine-text-highlight-grey)" + } + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "d2IrxHbD5D", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "[Address]", + "attributes": { + "background": "var(--affine-text-highlight-grey)" + } + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "IdLESwrZ-U", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "[Phone Number]", + "attributes": { + "background": "var(--affine-text-highlight-grey)" + } + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "6YwnA2HHQM", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "[Email]", + "attributes": { + "background": "var(--affine-text-highlight-grey)" + } + } + ] + } + }, + "children": [] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/packages/frontend/templates/onboarding/e5cYLNpIRUb-nwpSZGdix.snapshot.json b/packages/frontend/templates/onboarding/e5cYLNpIRUb-nwpSZGdix.snapshot.json new file mode 100644 index 0000000000..115d3d7e8b --- /dev/null +++ b/packages/frontend/templates/onboarding/e5cYLNpIRUb-nwpSZGdix.snapshot.json @@ -0,0 +1,1108 @@ +{ + "type": "page", + "meta": { + "id": "e5cYLNpIRUb-nwpSZGdix", + "title": "Personal-Knowledge Management ", + "createDate": 1691574859042, + "tags": [] + }, + "blocks": { + "type": "block", + "id": "F19ZttSiUA", + "flavour": "affine:page", + "props": { + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Personal-Knowledge Management " + } + ] + } + }, + "children": [ + { + "type": "block", + "id": "OzSHckdEQ6", + "flavour": "affine:surface", + "props": { + "elements": { + "pMyfZPwXXU": { + "type": "connector", + "mode": 1, + "strokeWidth": 2, + "stroke": "--affine-palette-line-black", + "strokeStyle": "solid", + "roughness": 1.4, + "source": { + "id": "f6Owaa00aK", + "position": [ + 0.5010332476508476, + 1 + ] + }, + "target": { + "id": "-tH5D5Hf4J" + }, + "controllers": [], + "id": "pMyfZPwXXU", + "index": "a2", + "seed": 1151132732, + "xywh": "[581.9546735491072,-359.54656036633406,650.69,379.64]" + } + } + }, + "children": [] + }, + { + "type": "block", + "id": "f6Owaa00aK", + "flavour": "affine:note", + "props": { + "xywh": "[420.1386841846113,-539.5481469931723,322.9614914325105,102]", + "background": "--affine-background-secondary-color", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "qyCHSngWfV", + "flavour": "affine:paragraph", + "props": { + "type": "quote", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Write your motto here, for example:" + }, + { + "insert": " It always seems impossible until it’s done. You can do it.", + "attributes": { + "italic": true + } + } + ] + } + }, + "children": [] + } + ] + }, + { + "type": "block", + "id": "-tH5D5Hf4J", + "flavour": "affine:note", + "props": { + "xywh": "[832.6496028098738,20.093439633665938,800,1296]", + "background": "--affine-background-secondary-color", + "index": "a01", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "ir2DPxrZN9", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "Bm3PQLWdKK", + "flavour": "affine:paragraph", + "props": { + "type": "h3", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "📖 Reading list" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "rLuDwMETvq", + "flavour": "affine:image", + "props": { + "caption": "Books are a uniquely portable magic", + "sourceId": "/static/pIqdA3pM1la1gKzxOmAcpLmTh3yXBrL9mGTz_hGj5xE=.png", + "width": 752, + "height": 501.328125, + "index": "a0", + "xywh": "[0,0,0,0]", + "rotate": 0, + "size": -1 + }, + "children": [] + }, + { + "type": "block", + "id": "s7EitB9S6F", + "flavour": "affine:paragraph", + "props": { + "type": "quote", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Using a form view, you can clearly list the reading status of all books. This can give you a direct understanding of the overall progress. Make good use of your time and read more books!" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "L0wkiKeVji", + "flavour": "affine:database", + "props": { + "views": [ + { + "id": "yYzkaFTs41", + "name": "table", + "mode": "table", + "columns": [ + { + "id": "Hzbcv9VHMw", + "width": 361 + }, + { + "id": "L0wkiKeVji", + "hide": false, + "width": 302 + }, + { + "id": "scBkEETna6", + "hide": false, + "width": 180 + }, + { + "id": "QmfyAdToer", + "hide": false, + "width": 169 + }, + { + "id": "ppQS-BaKNB", + "hide": false, + "width": 200 + } + ], + "filter": { + "type": "group", + "op": "and", + "conditions": [] + } + } + ], + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": " 8 books I should finished " + } + ] + }, + "cells": { + "lMu_xf4xVF": { + "QmfyAdToer": { + "columnId": "QmfyAdToer", + "value": 87 + }, + "ppQS-BaKNB": { + "columnId": "ppQS-BaKNB", + "value": "" + }, + "scBkEETna6": { + "columnId": "scBkEETna6", + "value": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Jennifer Egan" + } + ] + } + } + }, + "vFgr3oyqOJ": { + "QmfyAdToer": { + "columnId": "QmfyAdToer", + "value": 100 + }, + "ppQS-BaKNB": { + "columnId": "ppQS-BaKNB", + "value": null + }, + "scBkEETna6": { + "columnId": "scBkEETna6", + "value": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Claire-Louise Bennett" + } + ] + } + } + }, + "1TYjc-QdZj": { + "QmfyAdToer": { + "columnId": "QmfyAdToer", + "value": 83 + }, + "ppQS-BaKNB": { + "columnId": "ppQS-BaKNB", + "value": null + }, + "scBkEETna6": { + "columnId": "scBkEETna6", + "value": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Barbara Kingsolver" + } + ] + } + } + }, + "ROpYva-V1J": { + "QmfyAdToer": { + "columnId": "QmfyAdToer", + "value": 60 + }, + "ppQS-BaKNB": { + "columnId": "ppQS-BaKNB", + "value": null + }, + "scBkEETna6": { + "columnId": "scBkEETna6", + "value": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Namwali Serpell" + } + ] + } + } + }, + "CXTh8uALzr": { + "QmfyAdToer": { + "columnId": "QmfyAdToer", + "value": 21 + }, + "ppQS-BaKNB": { + "columnId": "ppQS-BaKNB", + "value": null + }, + "scBkEETna6": { + "columnId": "scBkEETna6", + "value": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Hernan Diaz" + } + ] + } + } + }, + "WrMjzOnJH3": { + "QmfyAdToer": { + "columnId": "QmfyAdToer", + "value": 100 + }, + "ppQS-BaKNB": { + "columnId": "ppQS-BaKNB", + "value": null + }, + "scBkEETna6": { + "columnId": "scBkEETna6", + "value": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Ed Yong" + } + ] + } + } + }, + "R__nq8e75I": { + "ppQS-BaKNB": { + "columnId": "ppQS-BaKNB", + "value": null + }, + "scBkEETna6": { + "columnId": "scBkEETna6", + "value": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Hua Hsu" + } + ] + } + } + }, + "XeJ1gW0QDw": { + "ppQS-BaKNB": { + "columnId": "ppQS-BaKNB", + "value": null + }, + "scBkEETna6": { + "columnId": "scBkEETna6", + "value": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Rachel Aviv" + } + ] + } + } + } + }, + "columns": [ + { + "id": "Hzbcv9VHMw", + "type": "title", + "name": "Readlist ", + "data": {} + }, + { + "type": "title", + "id": "L0wkiKeVji", + "name": "Title", + "data": {} + }, + { + "type": "progress", + "name": "progress ", + "data": {}, + "id": "QmfyAdToer" + }, + { + "type": "link", + "name": "Additional information ", + "data": {}, + "id": "ppQS-BaKNB" + }, + { + "type": "rich-text", + "name": "Author", + "data": {}, + "id": "scBkEETna6" + } + ] + }, + "children": [ + { + "type": "block", + "id": "lMu_xf4xVF", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "THE CANDY HOUSE", + "attributes": { + "bold": true + } + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "vFgr3oyqOJ", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "CHECKOUT 19", + "attributes": { + "bold": true + } + }, + { + "insert": " " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "1TYjc-QdZj", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": " " + }, + { + "insert": "DEMON COPPERHEAD", + "attributes": { + "bold": true + } + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "ROpYva-V1J", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "THE FURROWS", + "attributes": { + "bold": true + } + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "CXTh8uALzr", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "TRUST", + "attributes": { + "bold": true + } + }, + { + "insert": " " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "WrMjzOnJH3", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "AN IMMENSE WORLD", + "attributes": { + "bold": true + } + }, + { + "insert": " " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "R__nq8e75I", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "STAY TRUE", + "attributes": { + "bold": true + } + }, + { + "insert": " " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "XeJ1gW0QDw", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "STRANGERS TO OURSELVES", + "attributes": { + "bold": true + } + }, + { + "insert": " " + } + ] + } + }, + "children": [] + } + ] + }, + { + "type": "block", + "id": "lfXQ_rY3fA", + "flavour": "affine:divider", + "props": {}, + "children": [] + } + ] + }, + { + "type": "block", + "id": "W6M-MPQ5-F", + "flavour": "affine:note", + "props": { + "xywh": "[-218.38057009913342,20.089515493697448,800,1444]", + "background": "--affine-background-secondary-color", + "index": "a01", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "A-XAaszL_S", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "n5t18a4pCF", + "flavour": "affine:paragraph", + "props": { + "type": "h3", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "📑 Study Plan" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "NzeIqA5Q6j", + "flavour": "affine:image", + "props": { + "caption": "A goal without a plan is just a wish", + "sourceId": "/static/9vXwWGEX5W9v5pzwpu0eK4pf22DZ_sCloO0zCH1aVQ4=.png", + "width": 752, + "height": 501.328125, + "index": "a0", + "xywh": "[0,0,0,0]", + "rotate": 0, + "size": -1 + }, + "children": [] + }, + { + "type": "block", + "id": "uZTvHIo1eH", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Using a " + }, + { + "insert": "kanban", + "attributes": { + "link": "https://en.wikipedia.org/wiki/Kanban_(development)" + } + }, + { + "insert": " system to manage your tasks is a very effective method." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "YsMF9PplY6", + "flavour": "affine:list", + "props": { + "type": "bulleted", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Ungroups", + "attributes": { + "background": "var(--affine-text-highlight-grey)" + } + }, + { + "insert": ": Your study task inbox." + } + ] + }, + "checked": false, + "collapsed": false + }, + "children": [] + }, + { + "type": "block", + "id": "aMVed60Zxh", + "flavour": "affine:list", + "props": { + "type": "bulleted", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "not started:", + "attributes": { + "background": "var(--affine-text-highlight-orange)" + } + }, + { + "insert": " Tasks in your plan that haven't been started yet." + } + ] + }, + "checked": false, + "collapsed": false + }, + "children": [] + }, + { + "type": "block", + "id": "G_hTd1c1Xr", + "flavour": "affine:list", + "props": { + "type": "bulleted", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "in-progress:", + "attributes": { + "background": "var(--affine-text-highlight-green)" + } + }, + { + "insert": " The tasks you are currently working on can have their progress indicated by updating a progress bar to show the approximate completion." + } + ] + }, + "checked": false, + "collapsed": false + }, + "children": [] + }, + { + "type": "block", + "id": "mcg7ZXEIUL", + "flavour": "affine:list", + "props": { + "type": "bulleted", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "finished:", + "attributes": { + "background": "var(--affine-text-highlight-blue)" + } + }, + { + "insert": " List the tasks that have been completed. Here, you can easily review what you've learned." + } + ] + }, + "checked": false, + "collapsed": false + }, + "children": [] + }, + { + "type": "block", + "id": "0H9dtAEA7G", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "z3JCtDKEnH", + "flavour": "affine:database", + "props": { + "views": [ + { + "id": "_KB-Dww-OJ", + "name": "Kanban View", + "mode": "kanban", + "columns": [ + { + "id": "z3JCtDKEnH", + "hide": false + }, + { + "id": "XdrUZWMCAy", + "hide": false + }, + { + "id": "MZ4j6XeWN2", + "hide": false + } + ], + "filter": { + "type": "group", + "op": "and", + "conditions": [] + }, + "groupBy": { + "type": "groupBy", + "columnId": "XdrUZWMCAy", + "name": "multi-select" + }, + "header": { + "titleColumn": "z3JCtDKEnH", + "iconColumn": "type" + }, + "groupProperties": [ + { + "key": "Ungroups", + "hide": false, + "manuallyCardSort": [ + "m7Wa1L5dzo" + ] + }, + { + "key": "7MeJP6tYbY", + "hide": false, + "manuallyCardSort": [ + "z38CbkWI85" + ] + }, + { + "key": "UzOiUYq0f5", + "hide": false, + "manuallyCardSort": [] + }, + { + "key": "s28YFoSwsn", + "hide": false, + "manuallyCardSort": [] + } + ] + }, + { + "id": "vyeVaTiV6v", + "name": "Table View", + "mode": "table", + "columns": [ + { + "id": "DrdPpsoRQl", + "width": 200 + } + ], + "filter": { + "type": "group", + "op": "and", + "conditions": [] + }, + "header": { + "titleColumn": "z3JCtDKEnH", + "iconColumn": "type" + } + } + ], + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Study Plan" + } + ] + }, + "cells": { + "VCBzCpQ4Pj": { + "XdrUZWMCAy": { + "columnId": "XdrUZWMCAy", + "value": [ + "UzOiUYq0f5" + ] + }, + "MZ4j6XeWN2": { + "columnId": "MZ4j6XeWN2", + "value": 76 + } + }, + "8SmLofa6Sa": { + "XdrUZWMCAy": { + "columnId": "XdrUZWMCAy", + "value": [ + "s28YFoSwsn" + ] + }, + "MZ4j6XeWN2": { + "columnId": "MZ4j6XeWN2", + "value": 100 + } + }, + "z38CbkWI85": { + "XdrUZWMCAy": { + "columnId": "XdrUZWMCAy", + "value": [ + "7MeJP6tYbY" + ] + } + }, + "m7Wa1L5dzo": { + "XdrUZWMCAy": { + "columnId": "XdrUZWMCAy", + "value": [ + null + ] + } + }, + "gSQczpW-cC": { + "XdrUZWMCAy": { + "columnId": "XdrUZWMCAy", + "value": [] + } + }, + "1GiXKFEQTr": { + "XdrUZWMCAy": { + "columnId": "XdrUZWMCAy", + "value": [] + } + } + }, + "columns": [ + { + "id": "DrdPpsoRQl", + "type": "title", + "name": "Task", + "data": {} + }, + { + "type": "title", + "id": "z3JCtDKEnH", + "name": "Title", + "data": {} + }, + { + "type": "multi-select", + "name": "Tag", + "data": { + "options": [ + { + "id": "7MeJP6tYbY", + "value": "not started", + "color": "var(--affine-tag-orange)" + }, + { + "id": "s28YFoSwsn", + "value": "finished", + "color": "var(--affine-tag-blue)" + }, + { + "id": "UzOiUYq0f5", + "value": "in-progress", + "color": "var(--affine-tag-green)" + } + ] + }, + "id": "XdrUZWMCAy" + }, + { + "type": "progress", + "name": "Progress", + "data": {}, + "id": "MZ4j6XeWN2" + } + ] + }, + "children": [ + { + "type": "block", + "id": "VCBzCpQ4Pj", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Task 1" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "8SmLofa6Sa", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Task 2" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "z38CbkWI85", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Task 3" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "gSQczpW-cC", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "New Task" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "1GiXKFEQTr", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "New Task 2" + } + ] + } + }, + "children": [] + } + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/packages/frontend/templates/onboarding/info.json b/packages/frontend/templates/onboarding/info.json new file mode 100644 index 0000000000..1565a44f30 --- /dev/null +++ b/packages/frontend/templates/onboarding/info.json @@ -0,0 +1,175 @@ +{ + "type": "info", + "id": "XdwdkEYm8fFZcYAdDa0qT", + "blockVersions": { + "affine:code": 1, + "affine:paragraph": 1, + "affine:page": 2, + "affine:list": 1, + "affine:note": 1, + "affine:divider": 1, + "affine:image": 1, + "affine:surface": 5, + "affine:bookmark": 1, + "affine:frame": 1, + "affine:database": 3, + "affine:surface-ref": 1, + "affine:data-view": 1, + "affine:attachment": 1, + "affine:embed-github": 1, + "affine:embed-html": 1 + }, + "pageVersion": 2, + "workspaceVersion": 2, + "properties": { + "tags": { + "options": [ + { + "id": "icg1n5UdkP", + "value": "Travel", + "color": "var(--affine-tag-gray)" + }, + { + "id": "Oe5dSe1DDJ", + "value": "Quick summary", + "color": "var(--affine-tag-green)" + }, + { + "id": "g1L5dXKctL", + "value": "OKR", + "color": "var(--affine-tag-purple)" + }, + { + "id": "q3mceOl_zi", + "value": "Streamline your workflow", + "color": "var(--affine-tag-teal)" + }, + { + "id": "ze07JVwBu4", + "value": "Plan", + "color": "var(--affine-tag-teal)" + }, + { + "id": "8qcYPCTK0h", + "value": "Review", + "color": "var(--affine-tag-orange)" + }, + { + "id": "wg-fBtd2eI", + "value": "Engage", + "color": "var(--affine-tag-pink)" + }, + { + "id": "QYFD_HeQc-", + "value": "Create", + "color": "var(--affine-tag-blue)" + }, + { + "id": "ZHBa2NtdSo", + "value": "Learn", + "color": "var(--affine-tag-yellow)" + } + ] + } + }, + "pages": [ + { + "id": "rzyBHDgN5KIlEYzB9oBaD", + "title": "Getting Started👇", + "createDate": 1691548231530, + "tags": [ + "ZHBa2NtdSo", + "QYFD_HeQc-", + "wg-fBtd2eI" + ], + "updatedDate": 1691676331623, + "favorite": true, + "jumpOnce": false + }, + { + "id": "9iIScyvuIB_kUKbs1AYOQ", + "title": "AFFiNE - not just a note-taking app", + "createDate": 1691548220794, + "tags": [], + "updatedDate": 1691676775642, + "favorite": false + }, + { + "id": "-P-O4GSfVGTkI16zJRVa4", + "title": "Templates Galleries ", + "createDate": 1691551731225, + "tags": [], + "updatedDate": 1691654611175, + "favorite": false + }, + { + "id": "0nee_XzkrN23Xy7HMoXL-", + "title": "Personal Home", + "createDate": 1691552082822, + "tags": [], + "updatedDate": 1691654606912, + "favorite": false + }, + { + "id": "nPPyOV0JBNKu5hvW9hE00", + "title": "Working Home", + "createDate": 1691552090989, + "tags": [], + "updatedDate": 1691646748171, + "favorite": false + }, + { + "id": "wbXL4bZcblxLKC6ETqcQ1", + "title": "AFFiNE's Personal project management", + "createDate": 1691564303138, + "tags": [], + "updatedDate": 1691646845195 + }, + { + "id": "e5cYLNpIRUb-nwpSZGdix", + "title": "Personal-Knowledge Management ", + "createDate": 1691574859042, + "tags": [], + "updatedDate": 1691648159371 + }, + { + "id": "b1F2xKjgZ4ISHhXNe1kck", + "title": "Annual Performance Review", + "createDate": 1691575011078, + "tags": [ + "8qcYPCTK0h" + ], + "updatedDate": 1691645074511, + "favorite": false + }, + { + "id": "Scod6coKmJJ-waH1-jkiW", + "title": "Brief Event planning ", + "createDate": 1691634722239, + "tags": [ + "ze07JVwBu4" + ], + "updatedDate": 1691647069662, + "favorite": false + }, + { + "id": "GWsRbUtMF4Ee6foVg-H9R", + "title": "Meeting Summary ", + "createDate": 1691635388447, + "tags": [ + "Oe5dSe1DDJ" + ], + "updatedDate": 1691645873930 + }, + { + "id": "RCpxnWMtBWmUZy5awgJBh", + "title": "OKR Template", + "createDate": 1691636192263, + "tags": [ + "q3mceOl_zi", + "g1L5dXKctL" + ], + "updatedDate": 1691645102104 + } + ] +} \ No newline at end of file diff --git a/packages/frontend/templates/onboarding/nPPyOV0JBNKu5hvW9hE00.snapshot.json b/packages/frontend/templates/onboarding/nPPyOV0JBNKu5hvW9hE00.snapshot.json new file mode 100644 index 0000000000..dc295906ea --- /dev/null +++ b/packages/frontend/templates/onboarding/nPPyOV0JBNKu5hvW9hE00.snapshot.json @@ -0,0 +1,355 @@ +{ + "type": "page", + "meta": { + "id": "nPPyOV0JBNKu5hvW9hE00", + "title": "Working Home", + "createDate": 1691552090989, + "tags": [] + }, + "blocks": { + "type": "block", + "id": "qsMt8nCetT", + "flavour": "affine:page", + "props": { + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Working Home" + } + ] + } + }, + "children": [ + { + "type": "block", + "id": "yC-F6Rj9bA", + "flavour": "affine:surface", + "props": { + "elements": {} + }, + "children": [] + }, + { + "type": "block", + "id": "pk_Cjkpyd4", + "flavour": "affine:note", + "props": { + "xywh": "[0,0,800,939]", + "background": "--affine-background-secondary-color", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "IAOXx-DBBz", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "5hR-BR03ms", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": " ", + "attributes": { + "reference": { + "type": "LinkedPage", + "pageId": "b1F2xKjgZ4ISHhXNe1kck" + } + } + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "zVgCQKWH-c", + "flavour": "affine:paragraph", + "props": { + "type": "quote", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "This " + }, + { + "insert": "Annual Performance Review template", + "attributes": { + "bold": true + } + }, + { + "insert": " is ideal for businesses seeking a structured and comprehensive approach to evaluating employee performance. By utilizing this template, you will gain clarity on individual strengths and areas for improvement, fostering effective communication and driving professional growth within your organization." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "ym2g48zSU_", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "o1UyrWV30t", + "flavour": "affine:divider", + "props": {}, + "children": [] + }, + { + "type": "block", + "id": "1EUiH1t60b", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": " ", + "attributes": { + "reference": { + "type": "LinkedPage", + "pageId": "GWsRbUtMF4Ee6foVg-H9R" + } + } + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "HmzMXKFaci", + "flavour": "affine:paragraph", + "props": { + "type": "quote", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Meeting minutes are an official record of a meeting", + "attributes": { + "bold": true + } + }, + { + "insert": " for its participants. They're also sources of information for teammates who were unable to attend." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "v5CGQQPDrt", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "lcf4te4rxB", + "flavour": "affine:divider", + "props": {}, + "children": [] + }, + { + "type": "block", + "id": "AbN8iFn0RW", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "uXdWxSSWgl", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": " ", + "attributes": { + "reference": { + "type": "LinkedPage", + "pageId": "RCpxnWMtBWmUZy5awgJBh" + } + } + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "TBjwPij8cJ", + "flavour": "affine:paragraph", + "props": { + "type": "quote", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Maximize your team's performance and drive goal-oriented success with our extensive collection of " + }, + { + "insert": "OKR templates", + "attributes": { + "bold": true + } + }, + { + "insert": ", specifically crafted to facilitate effective objective setting, key result tracking, and overall performance improvement." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "mjgDUguqmV", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "c_gX1ik0kK", + "flavour": "affine:divider", + "props": {}, + "children": [] + }, + { + "type": "block", + "id": "IU3gGmIffA", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "LnERZXy0xj", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "🔔Reminder: Click the " + }, + { + "insert": "+New page ", + "attributes": { + "bold": true + } + }, + { + "insert": "to start your journey in AFFiNE " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "oi0BxPtiit", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "✏Unleash your creativity and elevate your work efficiency to new heights by customizing templates that perfectly align with your unique needs and objectives." + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "DuRxwNJe2w", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/packages/frontend/templates/onboarding/rzyBHDgN5KIlEYzB9oBaD.snapshot.json b/packages/frontend/templates/onboarding/rzyBHDgN5KIlEYzB9oBaD.snapshot.json new file mode 100644 index 0000000000..fff2c70731 --- /dev/null +++ b/packages/frontend/templates/onboarding/rzyBHDgN5KIlEYzB9oBaD.snapshot.json @@ -0,0 +1,419 @@ +{ + "type": "page", + "meta": { + "id": "rzyBHDgN5KIlEYzB9oBaD", + "title": "Getting Started👇", + "createDate": 1691548231530, + "tags": [ + "ZHBa2NtdSo", + "QYFD_HeQc-", + "wg-fBtd2eI" + ] + }, + "blocks": { + "type": "block", + "id": "1FO1aYcosq", + "flavour": "affine:page", + "props": { + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Getting Started👇" + } + ] + } + }, + "children": [ + { + "type": "block", + "id": "f7o2Osxfa_", + "flavour": "affine:surface", + "props": { + "elements": {} + }, + "children": [] + }, + { + "type": "block", + "id": "NDAzEfCZnv", + "flavour": "affine:note", + "props": { + "xywh": "[-12.5684605441112,-52.45959387999099,800,582]", + "background": "--affine-background-secondary-color", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "jArWNRgiLB", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Basic things you should know: " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "TUx2T-R2zT", + "flavour": "affine:list", + "props": { + "type": "todo", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Drag " + }, + { + "insert": "blocks ", + "attributes": { + "bold": true + } + }, + { + "insert": "(put a symbol) at the bottom right of the page to insert a new block, list, database, etc." + } + ] + }, + "checked": false, + "collapsed": false + }, + "children": [ + { + "type": "block", + "id": "kyqLH3oNHK", + "flavour": "affine:list", + "props": { + "type": "todo", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Hit \"" + }, + { + "insert": "/", + "attributes": { + "bold": true + } + }, + { + "insert": " \"to see all the types of content you can add - headings, videos, link pages, etc." + } + ] + }, + "checked": false, + "collapsed": false + }, + "children": [] + }, + { + "type": "block", + "id": "kyQSXZ39ce", + "flavour": "affine:list", + "props": { + "type": "todo", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Edit any text, and " + }, + { + "insert": "stylish", + "attributes": { + "bold": true + } + }, + { + "insert": " " + }, + { + "insert": "your", + "attributes": { + "italic": true + } + }, + { + "insert": " " + }, + { + "insert": "writing", + "attributes": { + "underline": true + } + }, + { + "insert": " freely." + } + ] + }, + "checked": false, + "collapsed": false + }, + "children": [] + } + ] + }, + { + "type": "block", + "id": "tKK8W8BwHQ", + "flavour": "affine:list", + "props": { + "type": "todo", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Follow " + }, + { + "insert": " ", + "attributes": { + "reference": { + "type": "LinkedPage", + "pageId": "9iIScyvuIB_kUKbs1AYOQ" + } + } + }, + { + "insert": "knowing how to " + }, + { + "insert": "write, draw, and plan all at once", + "attributes": { + "bold": true + } + }, + { + "insert": "." + } + ] + }, + "checked": false, + "collapsed": false + }, + "children": [ + { + "type": "block", + "id": "APwWQJEqlF", + "flavour": "affine:list", + "props": { + "type": "todo", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Not sure where to start with? Refer to" + }, + { + "insert": " ", + "attributes": { + "reference": { + "type": "LinkedPage", + "pageId": "-P-O4GSfVGTkI16zJRVa4" + } + } + }, + { + "insert": "in your sidebar to get started with pre-built pages." + } + ] + }, + "checked": false, + "collapsed": false + }, + "children": [] + } + ] + }, + { + "type": "block", + "id": "q4UGzfn08o", + "flavour": "affine:list", + "props": { + "type": "todo", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Click the drop-down menu " + }, + { + "insert": "\"v\" next to the title for more advanced options", + "attributes": { + "bold": true + } + }, + { + "insert": ", such as the ability to one-click export your work into PDF/HTML/Markdown formats." + } + ] + }, + "checked": false, + "collapsed": false + }, + "children": [] + }, + { + "type": "block", + "id": "yDnnnpnCFh", + "flavour": "affine:list", + "props": { + "type": "todo", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "👉 Encounter" + }, + { + "insert": " ", + "attributes": { + "bold": true + } + }, + { + "insert": "a question? Click the \"" + }, + { + "insert": "?\" Button ", + "attributes": { + "bold": true + } + }, + { + "insert": "at the bottom right for more guides, or directly send us a message." + } + ] + }, + "checked": false, + "collapsed": false + }, + "children": [] + }, + { + "type": "block", + "id": "Uq0yIH932N", + "flavour": "affine:paragraph", + "props": { + "type": "h6", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "🎉 Congratulations! You already go through all of this list!" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "Ag86EJnVv0", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Click the" + }, + { + "insert": " ", + "attributes": { + "background": "var(--affine-text-highlight-grey)" + } + }, + { + "insert": "+ New Page", + "attributes": { + "background": "var(--affine-text-highlight-grey)", + "bold": true + } + }, + { + "insert": " ", + "attributes": { + "background": "var(--affine-text-highlight-grey)" + } + }, + { + "insert": "button at the bottom of your sidebar to start your journey in AFFiNE" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "iQUJlC0oQT", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "wtxc_a6qxN", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "⏰ Kindly Reminder: This page is automatically set up in the" + }, + { + "insert": " ", + "attributes": { + "background": "var(--affine-text-highlight-grey)" + } + }, + { + "insert": "+New Workspace", + "attributes": { + "background": "var(--affine-text-highlight-grey)", + "bold": true + } + }, + { + "insert": ". If you ever find yourself unsure about how to use AFFiNE, simply click on the workspace avatar and select \"Add a New Workspace\" to revisit this page and get the guidance you need." + } + ] + } + }, + "children": [] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/packages/frontend/templates/onboarding/wbXL4bZcblxLKC6ETqcQ1.snapshot.json b/packages/frontend/templates/onboarding/wbXL4bZcblxLKC6ETqcQ1.snapshot.json new file mode 100644 index 0000000000..1fde02cdc3 --- /dev/null +++ b/packages/frontend/templates/onboarding/wbXL4bZcblxLKC6ETqcQ1.snapshot.json @@ -0,0 +1,191 @@ +{ + "type": "page", + "meta": { + "id": "wbXL4bZcblxLKC6ETqcQ1", + "title": "AFFiNE's Personal project management", + "createDate": 1691564303138, + "tags": [] + }, + "blocks": { + "type": "block", + "id": "LvRIWQ1EAT", + "flavour": "affine:page", + "props": { + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "AFFiNE's Personal project management" + } + ] + } + }, + "children": [ + { + "type": "block", + "id": "9eBKQxt8ax", + "flavour": "affine:surface", + "props": { + "elements": {} + }, + "children": [] + }, + { + "type": "block", + "id": "2nHeyqzDMf", + "flavour": "affine:note", + "props": { + "xywh": "[0,0,800,361]", + "background": "--affine-background-secondary-color", + "index": "a0", + "hidden": false, + "edgeless": { + "style": { + "borderRadius": 8, + "borderSize": 4, + "borderStyle": "solid", + "shadowType": "--affine-note-shadow-box" + } + } + }, + "children": [ + { + "type": "block", + "id": "g0M-Toj8iK", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "To-do-list " + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "JpcfJa6HqU", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "/" + } + ] + } + }, + "children": [] + }, + { + "type": "block", + "id": "w9Oyl2J6bd", + "flavour": "affine:database", + "props": { + "views": [ + { + "id": "zp1yuVbPHH", + "name": "table", + "mode": "table", + "columns": [ + { + "id": "46SguLuWz3", + "width": 185 + }, + { + "id": "uSHvJhR2FN", + "width": 200 + } + ], + "filter": { + "type": "group", + "op": "and", + "conditions": [] + } + } + ], + "title": { + "$blocksuite:internal:text$": true, + "delta": [ + { + "insert": "Database" + } + ] + }, + "cells": {}, + "columns": [ + { + "id": "46SguLuWz3", + "type": "title", + "name": "Title", + "data": {} + }, + { + "type": "title", + "id": "w9Oyl2J6bd", + "name": "Title", + "data": {} + }, + { + "type": "multi-select", + "name": "Tag", + "data": { + "options": [] + }, + "id": "uSHvJhR2FN" + } + ] + }, + "children": [ + { + "type": "block", + "id": "yP88q8kMa_", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "xo7ok9TmO7", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + }, + { + "type": "block", + "id": "dOQULUJfK6", + "flavour": "affine:paragraph", + "props": { + "type": "text", + "text": { + "$blocksuite:internal:text$": true, + "delta": [] + } + }, + "children": [] + } + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/packages/frontend/templates/package.json b/packages/frontend/templates/package.json index df8e02c4d2..a9ad636084 100644 --- a/packages/frontend/templates/package.json +++ b/packages/frontend/templates/package.json @@ -2,10 +2,12 @@ "name": "@affine/templates", "private": true, "sideEffect": false, - "exports": { - "./*.md": "./*.md", - "./v1/*.json": "./v1/*.json", - "./preloading.json": "./preloading.json" + "version": "0.11.0", + "scripts": { + "postinstall": "node ./build.mjs" }, - "version": "0.11.0" + "type": "module", + "exports": { + ".": "./templates.gen.ts" + } } diff --git a/packages/frontend/templates/templates.gen.ts b/packages/frontend/templates/templates.gen.ts new file mode 100644 index 0000000000..7188531ffb --- /dev/null +++ b/packages/frontend/templates/templates.gen.ts @@ -0,0 +1,29 @@ +/* eslint-disable simple-import-sort/imports */ +// Auto generated, do not edit manually +import json_0 from './onboarding/wbXL4bZcblxLKC6ETqcQ1.snapshot.json'; +import json_1 from './onboarding/rzyBHDgN5KIlEYzB9oBaD.snapshot.json'; +import json_2 from './onboarding/nPPyOV0JBNKu5hvW9hE00.snapshot.json'; +import json_3 from './onboarding/info.json'; +import json_4 from './onboarding/e5cYLNpIRUb-nwpSZGdix.snapshot.json'; +import json_5 from './onboarding/b1F2xKjgZ4ISHhXNe1kck.snapshot.json'; +import json_6 from './onboarding/Scod6coKmJJ-waH1-jkiW.snapshot.json'; +import json_7 from './onboarding/RCpxnWMtBWmUZy5awgJBh.snapshot.json'; +import json_8 from './onboarding/GWsRbUtMF4Ee6foVg-H9R.snapshot.json'; +import json_9 from './onboarding/9iIScyvuIB_kUKbs1AYOQ.snapshot.json'; +import json_10 from './onboarding/0nee_XzkrN23Xy7HMoXL-.snapshot.json'; +import json_11 from './onboarding/-P-O4GSfVGTkI16zJRVa4.snapshot.json'; + +export const onboarding = { + 'wbXL4bZcblxLKC6ETqcQ1.snapshot.json': json_0, +'rzyBHDgN5KIlEYzB9oBaD.snapshot.json': json_1, +'nPPyOV0JBNKu5hvW9hE00.snapshot.json': json_2, +'info.json': json_3, +'e5cYLNpIRUb-nwpSZGdix.snapshot.json': json_4, +'b1F2xKjgZ4ISHhXNe1kck.snapshot.json': json_5, +'Scod6coKmJJ-waH1-jkiW.snapshot.json': json_6, +'RCpxnWMtBWmUZy5awgJBh.snapshot.json': json_7, +'GWsRbUtMF4Ee6foVg-H9R.snapshot.json': json_8, +'9iIScyvuIB_kUKbs1AYOQ.snapshot.json': json_9, +'0nee_XzkrN23Xy7HMoXL-.snapshot.json': json_10, +'-P-O4GSfVGTkI16zJRVa4.snapshot.json': json_11 +} \ No newline at end of file diff --git a/packages/frontend/templates/v1/annual-performance-review.json b/packages/frontend/templates/v1/annual-performance-review.json deleted file mode 100644 index b8ad4880d9..0000000000 --- a/packages/frontend/templates/v1/annual-performance-review.json +++ /dev/null @@ -1,546 +0,0 @@ -{ - "blocks": { - "mMBWzZtpvM": { - "sys:id": "mMBWzZtpvM", - "sys:flavour": "affine:page", - "sys:children": ["XSMCxudQf4", "2xxenQgE1-", "ca2-VFQV45"], - "prop:title": [ - { - "insert": "Annual Performance Review" - } - ] - }, - "XSMCxudQf4": { - "sys:id": "XSMCxudQf4", - "sys:flavour": "affine:surface", - "sys:children": [], - "prop:elements": { - "type": "$blocksuite:internal:native$", - "value": {} - } - }, - "2xxenQgE1-": { - "sys:id": "2xxenQgE1-", - "sys:flavour": "affine:note", - "sys:children": ["G7wJk4W9lz"], - "prop:xywh": "[0,0,800,91]", - "prop:background": "--affine-background-secondary-color", - "prop:index": "a0", - "prop:hidden": false - }, - "XPiPjHYnvE": { - "sys:id": "XPiPjHYnvE", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "Employee Information:", - "attributes": { - "underline": true, - "bold": true - } - } - ], - "prop:type": "text" - }, - "ca2-VFQV45": { - "sys:id": "ca2-VFQV45", - "sys:flavour": "affine:note", - "sys:children": [ - "viFZDjrBqJ", - "XPiPjHYnvE", - "qpyYi7g_c6", - "NpbigpLTNh", - "e1ax4lVFjW", - "rEKXjYtGDR", - "gYcGYxQ88e", - "OyFSyuWbzw", - "VcUt-bpjIK", - "a0xWifxvFi", - "YiQINiDM6W", - "Zp-YtzbUUK", - "ZMfsmg_5oZ", - "XH7xQMlX_0", - "mBO-_pWGoi", - "X_SNf6rQf4", - "0LF9mjFcX9", - "d2IrxHbD5D", - "IdLESwrZ-U", - "6YwnA2HHQM" - ], - "prop:xywh": "[0,0,800,1719]", - "prop:background": "--affine-background-secondary-color", - "prop:index": "a0", - "prop:hidden": false - }, - "G7wJk4W9lz": { - "sys:id": "G7wJk4W9lz", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "NpbigpLTNh": { - "sys:id": "NpbigpLTNh", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "Department:", - "attributes": { - "bold": true - } - }, - { - "insert": " " - }, - { - "insert": "[Department Name]", - "attributes": { - "background": "var(--affine-text-highlight-grey)" - } - } - ] - }, - "e1ax4lVFjW": { - "sys:id": "e1ax4lVFjW", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "Job Title:", - "attributes": { - "bold": true - } - }, - { - "insert": " " - }, - { - "insert": "[Job Title]", - "attributes": { - "background": "var(--affine-text-highlight-grey)" - } - } - ] - }, - "rEKXjYtGDR": { - "sys:id": "rEKXjYtGDR", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "Date of Joining:", - "attributes": { - "bold": true - } - }, - { - "insert": " " - }, - { - "insert": "[Date of Joining]", - "attributes": { - "background": "var(--affine-text-highlight-grey)" - } - } - ] - }, - "gYcGYxQ88e": { - "sys:id": "gYcGYxQ88e", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "Review Date: ", - "attributes": { - "bold": true - } - }, - { - "insert": " " - }, - { - "insert": "[Review Date]", - "attributes": { - "background": "var(--affine-text-highlight-grey)" - } - } - ] - }, - "OyFSyuWbzw": { - "sys:id": "OyFSyuWbzw", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "VcUt-bpjIK": { - "sys:id": "VcUt-bpjIK", - "sys:flavour": "affine:database", - "sys:children": ["VwZ3681tdd", "jT3Oo2XSeJ", "RviIfLJUPX"], - "prop:columns": [ - { - "type": "title", - "id": "VcUt-bpjIK", - "name": "Title", - "data": {} - }, - { - "type": "multi-select", - "name": "score ", - "data": { - "options": [ - { - "id": "rwjdYVF3jd", - "value": "awsome", - "color": "var(--affine-tag-yellow)" - }, - { - "id": "4dHxaZ8cKk", - "value": "well", - "color": "var(--affine-tag-purple)" - }, - { - "id": "wgvB2fzMu6", - "value": "good", - "color": "var(--affine-tag-green)" - }, - { - "id": "YGPLhMJkni", - "value": "done", - "color": "var(--affine-tag-pink)" - }, - { - "id": "Q1-TuffR-9", - "value": "in progress", - "color": "var(--affine-tag-white)" - } - ] - }, - "id": "teyC7JgUni" - } - ], - "prop:titleColumnName": "Title", - "prop:views": [ - { - "id": "bIFdicaEON", - "name": "table", - "mode": "table", - "columns": [ - { - "id": "VcUt-bpjIK", - "hide": false, - "width": 260 - }, - { - "id": "teyC7JgUni", - "hide": false, - "width": 200 - } - ], - "filter": { - "type": "group", - "op": "and", - "conditions": [] - } - } - ], - "prop:title": [ - { - "insert": "Employee's performance " - } - ], - "prop:cells": { - "VwZ3681tdd": { - "teyC7JgUni": { - "columnId": "teyC7JgUni", - "value": ["rwjdYVF3jd"] - } - }, - "jT3Oo2XSeJ": { - "teyC7JgUni": { - "columnId": "teyC7JgUni", - "value": ["4dHxaZ8cKk"] - } - }, - "RviIfLJUPX": { - "teyC7JgUni": { - "columnId": "teyC7JgUni", - "value": ["wgvB2fzMu6"] - } - } - }, - "prop:titleColumnWidth": 343 - }, - "VwZ3681tdd": { - "sys:id": "VwZ3681tdd", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "[List Employee's Performance Goal 1]" - } - ] - }, - "jT3Oo2XSeJ": { - "sys:id": "jT3Oo2XSeJ", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "[List Employee's Performance Goal 2]" - } - ] - }, - "RviIfLJUPX": { - "sys:id": "RviIfLJUPX", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "[List Employee's Performance Goal 3]" - } - ] - }, - "a0xWifxvFi": { - "sys:id": "a0xWifxvFi", - "sys:flavour": "affine:database", - "sys:children": ["ZEi_-9SS-Y", "8LPVaagwtP", "lYnANRxW0A"], - "prop:columns": [ - { - "type": "title", - "id": "a0xWifxvFi", - "name": "Title", - "data": {} - }, - { - "type": "multi-select", - "name": "Tag", - "data": { - "options": [] - }, - "id": "ZdTyvkjQwz" - } - ], - "prop:titleColumnName": "Title", - "prop:views": [ - { - "id": "MvodpC_iAj", - "name": "table", - "mode": "table", - "columns": [ - { - "id": "a0xWifxvFi", - "hide": false, - "width": 260 - }, - { - "id": "ZdTyvkjQwz", - "hide": false, - "width": 200 - } - ], - "filter": { - "type": "group", - "op": "and", - "conditions": [] - } - } - ], - "prop:title": [ - { - "insert": "Job responsibility " - } - ], - "prop:cells": {}, - "prop:titleColumnWidth": 354 - }, - "ZEi_-9SS-Y": { - "sys:id": "ZEi_-9SS-Y", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "[List Job Responsibility 1]" - } - ] - }, - "8LPVaagwtP": { - "sys:id": "8LPVaagwtP", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "[List Job Responsibility 2]" - } - ] - }, - "lYnANRxW0A": { - "sys:id": "lYnANRxW0A", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "[List Job Responsibility 3]" - } - ] - }, - "YiQINiDM6W": { - "sys:id": "YiQINiDM6W", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "Employee's Signature: " - } - ] - }, - "Zp-YtzbUUK": { - "sys:id": "Zp-YtzbUUK", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "Date: " - } - ], - "prop:type": "text" - }, - "ZMfsmg_5oZ": { - "sys:id": "ZMfsmg_5oZ", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "mBO-_pWGoi": { - "sys:id": "mBO-_pWGoi", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "Date: " - } - ], - "prop:type": "text" - }, - "X_SNf6rQf4": { - "sys:id": "X_SNf6rQf4", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "0LF9mjFcX9": { - "sys:id": "0LF9mjFcX9", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "[Company Name]", - "attributes": { - "background": "var(--affine-text-highlight-grey)" - } - } - ] - }, - "d2IrxHbD5D": { - "sys:id": "d2IrxHbD5D", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "[Address]", - "attributes": { - "background": "var(--affine-text-highlight-grey)" - } - } - ] - }, - "IdLESwrZ-U": { - "sys:id": "IdLESwrZ-U", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "[Phone Number]", - "attributes": { - "background": "var(--affine-text-highlight-grey)" - } - } - ] - }, - "6YwnA2HHQM": { - "sys:id": "6YwnA2HHQM", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "[Email]", - "attributes": { - "background": "var(--affine-text-highlight-grey)" - } - } - ] - }, - "qpyYi7g_c6": { - "sys:id": "qpyYi7g_c6", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "Name:", - "attributes": { - "bold": true - } - }, - { - "insert": " " - }, - { - "insert": "[Employee Name]", - "attributes": { - "background": "var(--affine-text-highlight-grey)" - } - } - ], - "prop:type": "text" - }, - "viFZDjrBqJ": { - "sys:id": "viFZDjrBqJ", - "sys:flavour": "affine:image", - "sys:children": [], - "prop:sourceId": "/static/gZLmSgmwumNdgf0eIfOSW44emctrLyFUaZapbk8eZ6s=.png", - "prop:caption": "", - "prop:width": 752, - "prop:height": 422.9140625 - }, - "XH7xQMlX_0": { - "sys:id": "XH7xQMlX_0", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "Manager's Signature: " - } - ], - "prop:type": "text" - } - } -} diff --git a/packages/frontend/templates/v1/brief-event-planning.json b/packages/frontend/templates/v1/brief-event-planning.json deleted file mode 100644 index 842d3fa471..0000000000 --- a/packages/frontend/templates/v1/brief-event-planning.json +++ /dev/null @@ -1,244 +0,0 @@ -{ - "blocks": { - "SMCf2aOH8T": { - "sys:id": "SMCf2aOH8T", - "sys:flavour": "affine:page", - "sys:children": ["_07_dOaECY", "0n7YghSHPc"], - "prop:title": [ - { - "insert": "Brief Event planning " - } - ] - }, - "_07_dOaECY": { - "sys:id": "_07_dOaECY", - "sys:flavour": "affine:surface", - "sys:children": [], - "prop:elements": { - "type": "$blocksuite:internal:native$", - "value": {} - } - }, - "0n7YghSHPc": { - "sys:id": "0n7YghSHPc", - "sys:flavour": "affine:note", - "sys:children": [ - "d6Alacwr32", - "9slwdYgqgq", - "6dH3Amz0rn", - "ObsV2gBxUe", - "xf1m62_pbc" - ], - "prop:xywh": "[0,0,800,527]", - "prop:background": "--affine-background-secondary-color", - "prop:index": "a0", - "prop:hidden": false - }, - "9slwdYgqgq": { - "sys:id": "9slwdYgqgq", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "An event brief outline the goals and key elements needed to plan a successful event. This AFFiNE Event Planning template will help you gain alignment and kick off your event planning. " - } - ] - }, - "6dH3Amz0rn": { - "sys:id": "6dH3Amz0rn", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "ObsV2gBxUe": { - "sys:id": "ObsV2gBxUe", - "sys:flavour": "affine:database", - "sys:children": ["pFlIzSXLOj", "4XPamQdWpq", "3uJL-VNu7K"], - "prop:views": [ - { - "id": "sfIqqrnI2q", - "name": "table", - "mode": "table", - "columns": [ - { - "id": "ObsV2gBxUe", - "hide": false, - "width": 246 - }, - { - "id": "FCYCytB4wx", - "hide": false, - "width": 162 - }, - { - "id": "mGltZNTJmw", - "hide": false, - "width": 176 - }, - { - "id": "ozFBusFd0s", - "hide": false, - "width": 200 - } - ], - "filter": { - "type": "group", - "op": "and", - "conditions": [] - } - } - ], - "prop:title": [ - { - "insert": "Event Planning" - } - ], - "prop:cells": { - "pFlIzSXLOj": { - "mGltZNTJmw": { - "columnId": "mGltZNTJmw", - "value": ["bsTnd_L1aE", "oU49ElCHCG"] - }, - "FCYCytB4wx": { - "columnId": "FCYCytB4wx", - "value": 100 - }, - "ozFBusFd0s": { - "columnId": "ozFBusFd0s", - "value": "https://affine.pro/blog/free-event-planning-templates-for-professsional-2023" - } - }, - "4XPamQdWpq": { - "mGltZNTJmw": { - "columnId": "mGltZNTJmw", - "value": ["oU49ElCHCG", "d6riek6K4d"] - }, - "FCYCytB4wx": { - "columnId": "FCYCytB4wx", - "value": 50 - } - }, - "3uJL-VNu7K": { - "mGltZNTJmw": { - "columnId": "mGltZNTJmw", - "value": ["bsTnd_L1aE", "z3vOXFIT-T"] - }, - "FCYCytB4wx": { - "columnId": "FCYCytB4wx", - "value": 8 - } - } - }, - "prop:columns": [ - { - "type": "title", - "id": "ObsV2gBxUe", - "name": "Title", - "data": {} - }, - { - "type": "multi-select", - "name": "Tag", - "data": { - "options": [ - { - "id": "z3vOXFIT-T", - "value": "not important", - "color": "var(--affine-tag-teal)" - }, - { - "id": "ZbJHxXJK3L", - "value": "nit important", - "color": "var(--affine-tag-pink)" - }, - { - "id": "d6riek6K4d", - "value": "important", - "color": "var(--affine-tag-yellow)" - }, - { - "id": "oU49ElCHCG", - "value": "urgent", - "color": "var(--affine-tag-purple)" - }, - { - "id": "bsTnd_L1aE", - "value": "must", - "color": "var(--affine-tag-green)" - }, - { - "id": "hHjqAauZu6", - "value": "urgent", - "color": "var(--affine-tag-gray)" - } - ] - }, - "id": "mGltZNTJmw" - }, - { - "type": "progress", - "name": "progress", - "data": {}, - "id": "FCYCytB4wx" - }, - { - "type": "link", - "name": "additional link ", - "data": {}, - "id": "ozFBusFd0s" - } - ], - "prop:titleColumnName": "Title", - "prop:titleColumnWidth": 186 - }, - "xf1m62_pbc": { - "sys:id": "xf1m62_pbc", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "d6Alacwr32": { - "sys:id": "d6Alacwr32", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "pFlIzSXLOj": { - "sys:id": "pFlIzSXLOj", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "research" - } - ], - "prop:type": "text" - }, - "4XPamQdWpq": { - "sys:id": "4XPamQdWpq", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "book accommondation" - } - ], - "prop:type": "text" - }, - "3uJL-VNu7K": { - "sys:id": "3uJL-VNu7K", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "arrange transportation " - } - ], - "prop:type": "text" - } - } -} diff --git a/packages/frontend/templates/v1/getting-started.json b/packages/frontend/templates/v1/getting-started.json deleted file mode 100644 index 1836bc24fe..0000000000 --- a/packages/frontend/templates/v1/getting-started.json +++ /dev/null @@ -1,320 +0,0 @@ -{ - "blocks": { - "1FO1aYcosq": { - "sys:id": "1FO1aYcosq", - "sys:flavour": "affine:page", - "sys:children": ["f7o2Osxfa_", "NDAzEfCZnv"], - "prop:title": [ - { - "insert": "Getting Started👇" - } - ] - }, - "f7o2Osxfa_": { - "sys:id": "f7o2Osxfa_", - "sys:flavour": "affine:surface", - "sys:children": [], - "prop:elements": { - "type": "$blocksuite:internal:native$", - "value": {} - } - }, - "NDAzEfCZnv": { - "sys:id": "NDAzEfCZnv", - "sys:flavour": "affine:note", - "sys:children": [ - "jArWNRgiLB", - "TUx2T-R2zT", - "tKK8W8BwHQ", - "q4UGzfn08o", - "yDnnnpnCFh", - "Uq0yIH932N", - "Ag86EJnVv0", - "iQUJlC0oQT", - "wtxc_a6qxN" - ], - "prop:xywh": "[-12.5684605441112,-52.45959387999099,800,582]", - "prop:background": "--affine-background-secondary-color", - "prop:index": "a0", - "prop:hidden": false - }, - "jArWNRgiLB": { - "sys:id": "jArWNRgiLB", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "Basic things you should know: " - } - ] - }, - "TUx2T-R2zT": { - "sys:id": "TUx2T-R2zT", - "sys:flavour": "affine:list", - "sys:children": ["kyqLH3oNHK", "kyQSXZ39ce"], - "prop:type": "todo", - "prop:text": [ - { - "insert": "Drag " - }, - { - "insert": "blocks ", - "attributes": { - "bold": true - } - }, - { - "insert": "(put a symbol) at the bottom right of the page to insert a new block, list, database, etc." - } - ], - "prop:checked": false - }, - "kyqLH3oNHK": { - "sys:id": "kyqLH3oNHK", - "sys:flavour": "affine:list", - "sys:children": [], - "prop:type": "todo", - "prop:text": [ - { - "insert": "Hit \"" - }, - { - "insert": "/", - "attributes": { - "bold": true - } - }, - { - "insert": " \"to see all the types of content you can add - headings, videos, link pages, etc." - } - ], - "prop:checked": false - }, - "kyQSXZ39ce": { - "sys:id": "kyQSXZ39ce", - "sys:flavour": "affine:list", - "sys:children": [], - "prop:type": "todo", - "prop:text": [ - { - "insert": "Edit any text, and " - }, - { - "insert": "stylish", - "attributes": { - "bold": true - } - }, - { - "insert": " " - }, - { - "insert": "your", - "attributes": { - "italic": true - } - }, - { - "insert": " " - }, - { - "insert": "writing", - "attributes": { - "underline": true - } - }, - { - "insert": " freely." - } - ], - "prop:checked": false - }, - "tKK8W8BwHQ": { - "sys:id": "tKK8W8BwHQ", - "sys:flavour": "affine:list", - "sys:children": ["APwWQJEqlF"], - "prop:type": "todo", - "prop:text": [ - { - "insert": "Follow " - }, - { - "insert": " ", - "attributes": { - "reference": { - "type": "LinkedPage", - "pageId": "0773e198-5de0-45d4-a35e-de22ea72b96b" - } - } - }, - { - "insert": "knowing how to " - }, - { - "insert": "write, draw, and plan all at once", - "attributes": { - "bold": true - } - }, - { - "insert": "." - } - ], - "prop:checked": false - }, - "APwWQJEqlF": { - "sys:id": "APwWQJEqlF", - "sys:flavour": "affine:list", - "sys:children": [], - "prop:type": "todo", - "prop:text": [ - { - "insert": "Not sure where to start with? Refer to" - }, - { - "insert": " ", - "attributes": { - "reference": { - "type": "LinkedPage", - "pageId": "59b140eb-4449-488f-9eeb-42412dcc044e" - } - } - }, - { - "insert": "in your sidebar to get started with pre-built pages." - } - ], - "prop:checked": false - }, - "q4UGzfn08o": { - "sys:id": "q4UGzfn08o", - "sys:flavour": "affine:list", - "sys:children": [], - "prop:type": "todo", - "prop:text": [ - { - "insert": "Click the drop-down menu " - }, - { - "insert": "\"v\" next to the title for more advanced options", - "attributes": { - "bold": true - } - }, - { - "insert": ", such as the ability to one-click export your work into PDF/HTML/Markdown formats." - } - ], - "prop:checked": false - }, - "yDnnnpnCFh": { - "sys:id": "yDnnnpnCFh", - "sys:flavour": "affine:list", - "sys:children": [], - "prop:type": "todo", - "prop:text": [ - { - "insert": "👉 Encounter" - }, - { - "insert": " ", - "attributes": { - "bold": true - } - }, - { - "insert": "a question? Click the \"" - }, - { - "insert": "?\" Button ", - "attributes": { - "bold": true - } - }, - { - "insert": "at the bottom right for more guides, or directly send us a message." - } - ], - "prop:checked": false - }, - "Uq0yIH932N": { - "sys:id": "Uq0yIH932N", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "h6", - "prop:text": [ - { - "insert": "🎉 Congratulations! You already go through all of this list!" - } - ] - }, - "Ag86EJnVv0": { - "sys:id": "Ag86EJnVv0", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "Click the" - }, - { - "insert": " ", - "attributes": { - "background": "var(--affine-text-highlight-grey)" - } - }, - { - "insert": "+ New Page", - "attributes": { - "background": "var(--affine-text-highlight-grey)", - "bold": true - } - }, - { - "insert": " ", - "attributes": { - "background": "var(--affine-text-highlight-grey)" - } - }, - { - "insert": "button at the bottom of your sidebar to start your journey in AFFiNE" - } - ] - }, - "iQUJlC0oQT": { - "sys:id": "iQUJlC0oQT", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "wtxc_a6qxN": { - "sys:id": "wtxc_a6qxN", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "⏰ Kindly Reminder: This page is automatically set up in the" - }, - { - "insert": " ", - "attributes": { - "background": "var(--affine-text-highlight-grey)" - } - }, - { - "insert": "+New Workspace", - "attributes": { - "background": "var(--affine-text-highlight-grey)", - "bold": true - } - }, - { - "insert": ". If you ever find yourself unsure about how to use AFFiNE, simply click on the workspace avatar and select \"Add a New Workspace\" to revisit this page and get the guidance you need." - } - ] - } - } -} diff --git a/packages/frontend/templates/v1/meeting-summary.json b/packages/frontend/templates/v1/meeting-summary.json deleted file mode 100644 index 69ac905c3d..0000000000 --- a/packages/frontend/templates/v1/meeting-summary.json +++ /dev/null @@ -1,215 +0,0 @@ -{ - "blocks": { - "EYboj1it1i": { - "sys:id": "EYboj1it1i", - "sys:flavour": "affine:page", - "sys:children": ["86K_nlhKXG", "q7fYMX90uJ"], - "prop:title": [ - { - "insert": "Meeting Summary " - } - ] - }, - "86K_nlhKXG": { - "sys:id": "86K_nlhKXG", - "sys:flavour": "affine:surface", - "sys:children": [], - "prop:elements": { - "type": "$blocksuite:internal:native$", - "value": {} - } - }, - "q7fYMX90uJ": { - "sys:id": "q7fYMX90uJ", - "sys:flavour": "affine:note", - "sys:children": [ - "IuRC73HZLH", - "YuOqeDHzoe", - "jPGxHHA_QX", - "sCk70nT5eX", - "iKwcxVFpCH", - "RPED0t8fSj", - "D3Y93H8WvU", - "BfO7S4Gkfm", - "Stcz30R0Ad", - "YKIkBUmv01", - "JzAgpzljrp", - "FSkrwB-T33", - "abGz-82vvA" - ], - "prop:xywh": "[0,0,800,1237]", - "prop:background": "--affine-background-secondary-color", - "prop:index": "a0", - "prop:hidden": false - }, - "YuOqeDHzoe": { - "sys:id": "YuOqeDHzoe", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "quote", - "prop:text": [ - { - "insert": "Project:" - }, - { - "insert": " [Link the project plan]\n", - "attributes": { - "background": "var(--affine-text-highlight-grey)" - } - }, - { - "insert": "]Attendees: " - }, - { - "insert": "[@mention teammates]", - "attributes": { - "background": "var(--affine-text-highlight-grey)" - } - }, - { - "insert": "\nDate & Time:" - }, - { - "insert": " [type /today]", - "attributes": { - "background": "var(--affine-text-highlight-grey)" - } - } - ] - }, - "IuRC73HZLH": { - "sys:id": "IuRC73HZLH", - "sys:flavour": "affine:image", - "sys:children": [], - "prop:sourceId": "/static/f9yKnlNMgKhF-CxOgHBsXkxfViCCkC6KwTv6Uj2Fcjw=.png", - "prop:caption": "", - "prop:width": 752, - "prop:height": 501.6328125 - }, - "jPGxHHA_QX": { - "sys:id": "jPGxHHA_QX", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "h1", - "prop:text": [ - { - "insert": "Agenda" - } - ] - }, - "sCk70nT5eX": { - "sys:id": "sCk70nT5eX", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "Write down the main topic to discuss." - } - ], - "prop:type": "text" - }, - "iKwcxVFpCH": { - "sys:id": "iKwcxVFpCH", - "sys:flavour": "affine:list", - "sys:children": [], - "prop:type": "bulleted", - "prop:text": [ - { - "insert": "E.g. content roadmap" - } - ], - "prop:checked": false - }, - "RPED0t8fSj": { - "sys:id": "RPED0t8fSj", - "sys:flavour": "affine:list", - "sys:children": [], - "prop:type": "bulleted", - "prop:text": [ - { - "insert": "Get aligned on the main upcoming topics" - } - ], - "prop:checked": false - }, - "D3Y93H8WvU": { - "sys:id": "D3Y93H8WvU", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "h1", - "prop:text": [ - { - "insert": "Notes" - } - ] - }, - "BfO7S4Gkfm": { - "sys:id": "BfO7S4Gkfm", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "quote", - "prop:text": [ - { - "insert": "Cover any key information discussed in the meeting." - } - ] - }, - "Stcz30R0Ad": { - "sys:id": "Stcz30R0Ad", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "h1", - "prop:text": [ - { - "insert": "Outcomes" - } - ] - }, - "YKIkBUmv01": { - "sys:id": "YKIkBUmv01", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "quote", - "prop:text": [ - { - "insert": "Summarize key decisions in a tidy paragraph and communicate them to the team." - } - ] - }, - "JzAgpzljrp": { - "sys:id": "JzAgpzljrp", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "h1", - "prop:text": [ - { - "insert": "Action items" - } - ] - }, - "FSkrwB-T33": { - "sys:id": "FSkrwB-T33", - "sys:flavour": "affine:list", - "sys:children": [], - "prop:type": "todo", - "prop:text": [ - { - "insert": "E.g. @jess review the content roadmap" - } - ], - "prop:checked": false - }, - "abGz-82vvA": { - "sys:id": "abGz-82vvA", - "sys:flavour": "affine:list", - "sys:children": [], - "prop:type": "todo", - "prop:text": [ - { - "insert": "E.g. @mike contact copywriter freelancers" - } - ], - "prop:checked": false - } - } -} diff --git a/packages/frontend/templates/v1/okr-template.json b/packages/frontend/templates/v1/okr-template.json deleted file mode 100644 index e86d94665c..0000000000 --- a/packages/frontend/templates/v1/okr-template.json +++ /dev/null @@ -1,336 +0,0 @@ -{ - "blocks": { - "0tJt1nfXpr": { - "sys:id": "0tJt1nfXpr", - "sys:flavour": "affine:page", - "sys:children": ["-SeDPuI6pE", "gjPKqwOdlZ"], - "prop:title": [ - { - "insert": "OKR Template" - } - ] - }, - "-SeDPuI6pE": { - "sys:id": "-SeDPuI6pE", - "sys:flavour": "affine:surface", - "sys:children": [], - "prop:elements": { - "type": "$blocksuite:internal:native$", - "value": {} - } - }, - "gjPKqwOdlZ": { - "sys:id": "gjPKqwOdlZ", - "sys:flavour": "affine:note", - "sys:children": [ - "syUYSO6Hlw", - "B12ua5IVdH", - "UWKiht4_yF", - "tsqRR7Aqqe", - "TzALQTyjsF", - "-RNdJMcB-M", - "_E43A1XjkR", - "lbLc-rsTQ_", - "IbLsSTqZWu", - "LfH4-lvRy1", - "PmstSRjisx" - ], - "prop:xywh": "[0,0,800,1418]", - "prop:background": "--affine-background-secondary-color", - "prop:index": "a0", - "prop:hidden": false - }, - "UWKiht4_yF": { - "sys:id": "UWKiht4_yF", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "B12ua5IVdH": { - "sys:id": "B12ua5IVdH", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "quote", - "prop:text": [ - { - "insert": "Using " - }, - { - "insert": "OKR (Objectives and Key Results)", - "attributes": { - "link": "https://en.wikipedia.org/wiki/Objectives_and_key_results" - } - }, - { - "insert": " helps teams work together to create and break down their goals. They also figure out how to reach those goals and use their own ideas and creativity. Every week, the team checks in to make sure everyone understands the goals, shares their thoughts, and makes changes as needed to get the job done well and quickly." - } - ] - }, - "tsqRR7Aqqe": { - "sys:id": "tsqRR7Aqqe", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "h2", - "prop:text": [ - { - "insert": "I. Synchronization of team OKR progress " - } - ] - }, - "TzALQTyjsF": { - "sys:id": "TzALQTyjsF", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "-RNdJMcB-M": { - "sys:id": "-RNdJMcB-M", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "All team members collectively update the latest progress and the next step plan for the person responsible for the OKR. These updates are discussed during the weekly Team OKR meeting." - } - ], - "prop:type": "text" - }, - "_E43A1XjkR": { - "sys:id": "_E43A1XjkR", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "PmstSRjisx": { - "sys:id": "PmstSRjisx", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "lbLc-rsTQ_": { - "sys:id": "lbLc-rsTQ_", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "h1", - "prop:text": [ - { - "insert": "O1: Fourth quarter revenue 1 million " - } - ] - }, - "IbLsSTqZWu": { - "sys:id": "IbLsSTqZWu", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "LfH4-lvRy1": { - "sys:id": "LfH4-lvRy1", - "sys:flavour": "affine:database", - "sys:children": ["CLLfj990JA", "iouBY8Zk33", "jRFdpU21Jf"], - "prop:views": [ - { - "id": "BjTEwK-3iR", - "name": "table", - "mode": "table", - "columns": [ - { - "id": "LfH4-lvRy1", - "hide": false, - "width": 260 - }, - { - "id": "ftL1qPRgYl", - "hide": false, - "width": 200 - }, - { - "id": "3s4GtwNZTH", - "hide": false, - "width": 108 - }, - { - "id": "IwZCoBvV9W", - "hide": false, - "width": 142 - } - ], - "filter": { - "type": "group", - "op": "and", - "conditions": [] - } - } - ], - "prop:title": [ - { - "insert": "KR setting & reviewing " - } - ], - "prop:cells": { - "C75yOBGfeP": { - "ftL1qPRgYl": { - "columnId": "ftL1qPRgYl", - "value": [ - { - "insert": "the planning case has been reviewed, see doc for details " - } - ] - }, - "IwZCoBvV9W": { - "columnId": "IwZCoBvV9W", - "value": 85 - } - }, - "iouBY8Zk33": { - "ftL1qPRgYl": { - "columnId": "ftL1qPRgYl", - "value": [ - { - "insert": "worning on it " - } - ] - }, - "IwZCoBvV9W": { - "columnId": "IwZCoBvV9W", - "value": 46 - }, - "3s4GtwNZTH": { - "columnId": "3s4GtwNZTH", - "value": "Qp1kGLAbfE" - } - }, - "jRFdpU21Jf": { - "ftL1qPRgYl": { - "columnId": "ftL1qPRgYl", - "value": [ - { - "insert": "waiting " - } - ] - }, - "IwZCoBvV9W": { - "columnId": "IwZCoBvV9W", - "value": 3 - }, - "3s4GtwNZTH": { - "columnId": "3s4GtwNZTH", - "value": "yCF1NgLWhh" - } - }, - "CLLfj990JA": { - "ftL1qPRgYl": { - "columnId": "ftL1qPRgYl", - "value": [ - { - "insert": "The planning case has been reviewed, see doc for details " - } - ] - }, - "IwZCoBvV9W": { - "columnId": "IwZCoBvV9W", - "value": 70 - }, - "3s4GtwNZTH": { - "columnId": "3s4GtwNZTH", - "value": "KHKc4e79W8" - } - } - }, - "prop:columns": [ - { - "type": "title", - "id": "LfH4-lvRy1", - "name": "Title", - "data": {} - }, - { - "type": "progress", - "name": "progress bar ", - "data": {}, - "id": "IwZCoBvV9W" - }, - { - "type": "rich-text", - "name": "Notes ", - "data": { - "options": [] - }, - "id": "ftL1qPRgYl" - }, - { - "type": "select", - "name": "priority ", - "data": { - "options": [ - { - "id": "yCF1NgLWhh", - "value": "not urgent", - "color": "var(--affine-tag-blue)" - }, - { - "id": "Qp1kGLAbfE", - "value": "urgent", - "color": "var(--affine-tag-red)" - }, - { - "id": "KHKc4e79W8", - "value": "important", - "color": "var(--affine-tag-yellow)" - } - ] - }, - "id": "3s4GtwNZTH" - } - ], - "prop:titleColumnName": "Key Resluts ", - "prop:titleColumnWidth": 246 - }, - "syUYSO6Hlw": { - "sys:id": "syUYSO6Hlw", - "sys:flavour": "affine:image", - "sys:children": [], - "prop:sourceId": "/static/VuXYyM9JUv1Fv_qjg1v5Go4Zksz0r4NXFeh3Na7JkIc=.png", - "prop:caption": "", - "prop:width": 752, - "prop:height": 501.2734375 - }, - "CLLfj990JA": { - "sys:id": "CLLfj990JA", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "The campus activity was successfully released, and the exposure was not less than 1 million people" - } - ] - }, - "iouBY8Zk33": { - "sys:id": "iouBY8Zk33", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "describe Key results " - } - ], - "prop:type": "text" - }, - "jRFdpU21Jf": { - "sys:id": "jRFdpU21Jf", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "KR1: Describe Key results " - } - ], - "prop:type": "text" - } - } -} diff --git a/packages/frontend/templates/v1/personal-home.json b/packages/frontend/templates/v1/personal-home.json deleted file mode 100644 index c82dac749e..0000000000 --- a/packages/frontend/templates/v1/personal-home.json +++ /dev/null @@ -1,127 +0,0 @@ -{ - "blocks": { - "QmQb34xduM": { - "sys:id": "QmQb34xduM", - "sys:flavour": "affine:page", - "sys:children": ["f4WAKZ67ki", "xj7QSmgQgT"], - "prop:title": [ - { - "insert": "Personal Home" - } - ] - }, - "f4WAKZ67ki": { - "sys:id": "f4WAKZ67ki", - "sys:flavour": "affine:surface", - "sys:children": [], - "prop:elements": { - "type": "$blocksuite:internal:native$", - "value": {} - } - }, - "xj7QSmgQgT": { - "sys:id": "xj7QSmgQgT", - "sys:flavour": "affine:note", - "sys:children": [ - "qlnEliaAwr", - "U6w_UTi2p5", - "YN7S4Pe19c", - "V9nK8F7B0R", - "jj-qOseCj-", - "hgSgQdYwWJ" - ], - "prop:xywh": "[0,0,800,547]", - "prop:background": "--affine-background-secondary-color", - "prop:index": "a0", - "prop:hidden": false - }, - "jj-qOseCj-": { - "sys:id": "jj-qOseCj-", - "sys:flavour": "affine:list", - "sys:children": [], - "prop:type": "bulleted", - "prop:text": [ - { - "insert": " ", - "attributes": { - "reference": { - "type": "LinkedPage", - "pageId": "646305d9-93e0-48df-bb92-d82944ceb5a3" - } - } - } - ], - "prop:checked": false - }, - "U6w_UTi2p5": { - "sys:id": "U6w_UTi2p5", - "sys:flavour": "affine:list", - "sys:children": [], - "prop:text": [ - { - "insert": " ", - "attributes": { - "reference": { - "type": "LinkedPage", - "pageId": "22163830-8252-43fe-b62d-fd9bbeaa4caa" - } - } - }, - { - "insert": " " - } - ], - "prop:type": "bulleted", - "prop:checked": false - }, - "qlnEliaAwr": { - "sys:id": "qlnEliaAwr", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "V9nK8F7B0R": { - "sys:id": "V9nK8F7B0R", - "sys:flavour": "affine:divider", - "sys:children": [] - }, - "hgSgQdYwWJ": { - "sys:id": "hgSgQdYwWJ", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "quote", - "prop:text": [ - { - "insert": "An event planning template ", - "attributes": { - "bold": true - } - }, - { - "insert": "is an efficient framework that details the steps you and your team need to consider taking to plan and execute a successful event." - } - ] - }, - "YN7S4Pe19c": { - "sys:id": "YN7S4Pe19c", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "quote", - "prop:text": [ - { - "insert": "Personal knowledge management (PKM for short) is " - }, - { - "insert": "the process of collecting, organizing, and storing information", - "attributes": { - "bold": true - } - }, - { - "insert": ", so it's easier to search for, retrieve, share, expand upon, and use later on." - } - ] - } - } -} diff --git a/packages/frontend/templates/v1/personal-knowledge-management.json b/packages/frontend/templates/v1/personal-knowledge-management.json deleted file mode 100644 index c6e1047f3f..0000000000 --- a/packages/frontend/templates/v1/personal-knowledge-management.json +++ /dev/null @@ -1,880 +0,0 @@ -{ - "blocks": { - "F19ZttSiUA": { - "sys:id": "F19ZttSiUA", - "sys:flavour": "affine:page", - "sys:children": ["OzSHckdEQ6", "f6Owaa00aK", "-tH5D5Hf4J", "W6M-MPQ5-F"], - "prop:title": [ - { - "insert": "Personal-Knowledge Management " - } - ] - }, - "OzSHckdEQ6": { - "sys:id": "OzSHckdEQ6", - "sys:flavour": "affine:surface", - "sys:children": [], - "prop:elements": { - "type": "$blocksuite:internal:native$", - "value": { - "aAOeeA4jhz": { - "type": "connector", - "mode": 1, - "strokeWidth": 2, - "stroke": "--affine-palette-line-blue", - "strokeStyle": "solid", - "roughness": 1.4, - "source": { - "id": "f6Owaa00aK", - "position": [0.5, 1] - }, - "target": { - "id": "W6M-MPQ5-F" - }, - "controllers": [], - "id": "aAOeeA4jhz", - "index": "a1", - "seed": 1493853178, - "xywh": "[181.61467354910715,-359.54656036633406,400,379.64]" - }, - "pMyfZPwXXU": { - "type": "connector", - "mode": 1, - "strokeWidth": 2, - "stroke": "--affine-palette-line-black", - "strokeStyle": "solid", - "roughness": 1.4, - "source": { - "id": "f6Owaa00aK", - "position": [0.5010332476508476, 1] - }, - "target": { - "id": "-tH5D5Hf4J" - }, - "controllers": [], - "id": "pMyfZPwXXU", - "index": "a2", - "seed": 1151132732, - "xywh": "[581.9546735491072,-359.54656036633406,650.69,379.64]" - } - } - } - }, - "f6Owaa00aK": { - "sys:id": "f6Owaa00aK", - "sys:flavour": "affine:note", - "sys:children": ["qyCHSngWfV"], - "prop:xywh": "[420.1386841846113,-539.5481469931723,322.9614914325105,102]", - "prop:background": "--affine-background-secondary-color", - "prop:index": "a0", - "prop:hidden": false - }, - "qyCHSngWfV": { - "sys:id": "qyCHSngWfV", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "quote", - "prop:text": [ - { - "insert": "Write your motto here, for example:" - }, - { - "insert": " It always seems impossible until it’s done. You can do it.", - "attributes": { - "italic": true - } - } - ] - }, - "z3JCtDKEnH": { - "sys:id": "z3JCtDKEnH", - "sys:flavour": "affine:database", - "sys:children": [ - "VCBzCpQ4Pj", - "8SmLofa6Sa", - "z38CbkWI85", - "gSQczpW-cC", - "1GiXKFEQTr" - ], - "prop:title": [ - { - "insert": "Study Plan" - } - ], - "prop:titleColumnName": "Task", - "prop:titleColumnWidth": 200, - "prop:views": [ - { - "id": "_KB-Dww-OJ", - "name": "Kanban View", - "mode": "kanban", - "columns": [ - { - "id": "z3JCtDKEnH", - "hide": false - }, - { - "id": "XdrUZWMCAy", - "hide": false - }, - { - "id": "MZ4j6XeWN2", - "hide": false - } - ], - "filter": { - "type": "group", - "op": "and", - "conditions": [] - }, - "groupBy": { - "type": "groupBy", - "columnId": "XdrUZWMCAy", - "name": "multi-select" - }, - "header": { - "titleColumn": "z3JCtDKEnH", - "iconColumn": "type" - }, - "groupProperties": [ - { - "key": "Ungroups", - "hide": false, - "manuallyCardSort": ["m7Wa1L5dzo"] - }, - { - "key": "7MeJP6tYbY", - "hide": false, - "manuallyCardSort": ["z38CbkWI85"] - }, - { - "key": "UzOiUYq0f5", - "hide": false, - "manuallyCardSort": [] - }, - { - "key": "s28YFoSwsn", - "hide": false, - "manuallyCardSort": [] - } - ] - }, - { - "id": "vyeVaTiV6v", - "name": "Table View", - "mode": "table", - "columns": [], - "filter": { - "type": "group", - "op": "and", - "conditions": [] - }, - "header": { - "titleColumn": "z3JCtDKEnH", - "iconColumn": "type" - } - } - ], - "prop:cells": { - "VCBzCpQ4Pj": { - "XdrUZWMCAy": { - "columnId": "XdrUZWMCAy", - "value": ["UzOiUYq0f5"] - }, - "MZ4j6XeWN2": { - "columnId": "MZ4j6XeWN2", - "value": 76 - } - }, - "8SmLofa6Sa": { - "XdrUZWMCAy": { - "columnId": "XdrUZWMCAy", - "value": ["s28YFoSwsn"] - }, - "MZ4j6XeWN2": { - "columnId": "MZ4j6XeWN2", - "value": 100 - } - }, - "z38CbkWI85": { - "XdrUZWMCAy": { - "columnId": "XdrUZWMCAy", - "value": ["7MeJP6tYbY"] - } - }, - "m7Wa1L5dzo": { - "XdrUZWMCAy": { - "columnId": "XdrUZWMCAy", - "value": [null] - } - }, - "gSQczpW-cC": { - "XdrUZWMCAy": { - "columnId": "XdrUZWMCAy", - "value": [] - } - }, - "1GiXKFEQTr": { - "XdrUZWMCAy": { - "columnId": "XdrUZWMCAy", - "value": [] - } - } - }, - "prop:columns": [ - { - "type": "title", - "id": "z3JCtDKEnH", - "name": "Title", - "data": {} - }, - { - "type": "multi-select", - "name": "Tag", - "data": { - "options": [ - { - "id": "7MeJP6tYbY", - "value": "not started", - "color": "var(--affine-tag-orange)" - }, - { - "id": "s28YFoSwsn", - "value": "finished", - "color": "var(--affine-tag-blue)" - }, - { - "id": "UzOiUYq0f5", - "value": "in-progress", - "color": "var(--affine-tag-green)" - } - ] - }, - "id": "XdrUZWMCAy" - }, - { - "type": "progress", - "name": "Progress", - "data": {}, - "id": "MZ4j6XeWN2" - } - ] - }, - "VCBzCpQ4Pj": { - "sys:id": "VCBzCpQ4Pj", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "Task 1" - } - ] - }, - "8SmLofa6Sa": { - "sys:id": "8SmLofa6Sa", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "Task 2" - } - ] - }, - "z38CbkWI85": { - "sys:id": "z38CbkWI85", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "Task 3" - } - ] - }, - "L0wkiKeVji": { - "sys:id": "L0wkiKeVji", - "sys:flavour": "affine:database", - "sys:children": [ - "lMu_xf4xVF", - "vFgr3oyqOJ", - "1TYjc-QdZj", - "ROpYva-V1J", - "CXTh8uALzr", - "WrMjzOnJH3", - "R__nq8e75I", - "XeJ1gW0QDw" - ], - "prop:columns": [ - { - "type": "title", - "id": "L0wkiKeVji", - "name": "Title", - "data": {} - }, - { - "type": "progress", - "name": "progress ", - "data": {}, - "id": "QmfyAdToer" - }, - { - "type": "link", - "name": "Additional information ", - "data": {}, - "id": "ppQS-BaKNB" - }, - { - "type": "rich-text", - "name": "Author", - "data": {}, - "id": "scBkEETna6" - } - ], - "prop:titleColumnName": "Readlist ", - "prop:views": [ - { - "id": "yYzkaFTs41", - "name": "table", - "mode": "table", - "columns": [ - { - "id": "L0wkiKeVji", - "hide": false, - "width": 302 - }, - { - "id": "scBkEETna6", - "hide": false, - "width": 180 - }, - { - "id": "QmfyAdToer", - "hide": false, - "width": 169 - }, - { - "id": "ppQS-BaKNB", - "hide": false, - "width": 200 - } - ], - "filter": { - "type": "group", - "op": "and", - "conditions": [] - } - } - ], - "prop:title": [ - { - "insert": " 8 books I should finished " - } - ], - "prop:cells": { - "lMu_xf4xVF": { - "QmfyAdToer": { - "columnId": "QmfyAdToer", - "value": 87 - }, - "ppQS-BaKNB": { - "columnId": "ppQS-BaKNB", - "value": "" - }, - "scBkEETna6": { - "columnId": "scBkEETna6", - "value": [ - { - "insert": "Jennifer Egan" - } - ] - } - }, - "vFgr3oyqOJ": { - "QmfyAdToer": { - "columnId": "QmfyAdToer", - "value": 100 - }, - "ppQS-BaKNB": { - "columnId": "ppQS-BaKNB", - "value": null - }, - "scBkEETna6": { - "columnId": "scBkEETna6", - "value": [ - { - "insert": "Claire-Louise Bennett" - } - ] - } - }, - "1TYjc-QdZj": { - "QmfyAdToer": { - "columnId": "QmfyAdToer", - "value": 83 - }, - "ppQS-BaKNB": { - "columnId": "ppQS-BaKNB", - "value": null - }, - "scBkEETna6": { - "columnId": "scBkEETna6", - "value": [ - { - "insert": "Barbara Kingsolver" - } - ] - } - }, - "ROpYva-V1J": { - "QmfyAdToer": { - "columnId": "QmfyAdToer", - "value": 60 - }, - "ppQS-BaKNB": { - "columnId": "ppQS-BaKNB", - "value": null - }, - "scBkEETna6": { - "columnId": "scBkEETna6", - "value": [ - { - "insert": "Namwali Serpell" - } - ] - } - }, - "CXTh8uALzr": { - "QmfyAdToer": { - "columnId": "QmfyAdToer", - "value": 21 - }, - "ppQS-BaKNB": { - "columnId": "ppQS-BaKNB", - "value": null - }, - "scBkEETna6": { - "columnId": "scBkEETna6", - "value": [ - { - "insert": "Hernan Diaz" - } - ] - } - }, - "WrMjzOnJH3": { - "QmfyAdToer": { - "columnId": "QmfyAdToer", - "value": 100 - }, - "ppQS-BaKNB": { - "columnId": "ppQS-BaKNB", - "value": null - }, - "scBkEETna6": { - "columnId": "scBkEETna6", - "value": [ - { - "insert": "Ed Yong" - } - ] - } - }, - "R__nq8e75I": { - "ppQS-BaKNB": { - "columnId": "ppQS-BaKNB", - "value": null - }, - "scBkEETna6": { - "columnId": "scBkEETna6", - "value": [ - { - "insert": "Hua Hsu" - } - ] - } - }, - "XeJ1gW0QDw": { - "ppQS-BaKNB": { - "columnId": "ppQS-BaKNB", - "value": null - }, - "scBkEETna6": { - "columnId": "scBkEETna6", - "value": [ - { - "insert": "Rachel Aviv" - } - ] - } - } - }, - "prop:titleColumnWidth": 361 - }, - "lMu_xf4xVF": { - "sys:id": "lMu_xf4xVF", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "THE CANDY HOUSE", - "attributes": { - "bold": true - } - } - ] - }, - "vFgr3oyqOJ": { - "sys:id": "vFgr3oyqOJ", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "CHECKOUT 19", - "attributes": { - "bold": true - } - }, - { - "insert": " " - } - ], - "prop:type": "text" - }, - "1TYjc-QdZj": { - "sys:id": "1TYjc-QdZj", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": " " - }, - { - "insert": "DEMON COPPERHEAD", - "attributes": { - "bold": true - } - } - ], - "prop:type": "text" - }, - "ROpYva-V1J": { - "sys:id": "ROpYva-V1J", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "THE FURROWS", - "attributes": { - "bold": true - } - } - ], - "prop:type": "text" - }, - "CXTh8uALzr": { - "sys:id": "CXTh8uALzr", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "TRUST", - "attributes": { - "bold": true - } - }, - { - "insert": " " - } - ], - "prop:type": "text" - }, - "WrMjzOnJH3": { - "sys:id": "WrMjzOnJH3", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "AN IMMENSE WORLD", - "attributes": { - "bold": true - } - }, - { - "insert": " " - } - ] - }, - "R__nq8e75I": { - "sys:id": "R__nq8e75I", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "STAY TRUE", - "attributes": { - "bold": true - } - }, - { - "insert": " " - } - ], - "prop:type": "text" - }, - "XeJ1gW0QDw": { - "sys:id": "XeJ1gW0QDw", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "STRANGERS TO OURSELVES", - "attributes": { - "bold": true - } - }, - { - "insert": " " - } - ], - "prop:type": "text" - }, - "s7EitB9S6F": { - "sys:id": "s7EitB9S6F", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "quote", - "prop:text": [ - { - "insert": "Using a form view, you can clearly list the reading status of all books. This can give you a direct understanding of the overall progress. Make good use of your time and read more books!" - } - ] - }, - "uZTvHIo1eH": { - "sys:id": "uZTvHIo1eH", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "Using a " - }, - { - "insert": "kanban", - "attributes": { - "link": "https://en.wikipedia.org/wiki/Kanban_(development)" - } - }, - { - "insert": " system to manage your tasks is a very effective method." - } - ] - }, - "m7Wa1L5dzo": { - "sys:id": "m7Wa1L5dzo", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "gSQczpW-cC": { - "sys:id": "gSQczpW-cC", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "New Task" - } - ] - }, - "1GiXKFEQTr": { - "sys:id": "1GiXKFEQTr", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "New Task 2" - } - ] - }, - "YsMF9PplY6": { - "sys:id": "YsMF9PplY6", - "sys:flavour": "affine:list", - "sys:children": [], - "prop:type": "bulleted", - "prop:text": [ - { - "insert": "Ungroups", - "attributes": { - "background": "var(--affine-text-highlight-grey)" - } - }, - { - "insert": ": Your study task inbox." - } - ], - "prop:checked": false - }, - "aMVed60Zxh": { - "sys:id": "aMVed60Zxh", - "sys:flavour": "affine:list", - "sys:children": [], - "prop:type": "bulleted", - "prop:text": [ - { - "insert": "not started:", - "attributes": { - "background": "var(--affine-text-highlight-orange)" - } - }, - { - "insert": " Tasks in your plan that haven't been started yet." - } - ], - "prop:checked": false - }, - "G_hTd1c1Xr": { - "sys:id": "G_hTd1c1Xr", - "sys:flavour": "affine:list", - "sys:children": [], - "prop:type": "bulleted", - "prop:text": [ - { - "insert": "in-progress:", - "attributes": { - "background": "var(--affine-text-highlight-green)" - } - }, - { - "insert": " The tasks you are currently working on can have their progress indicated by updating a progress bar to show the approximate completion." - } - ], - "prop:checked": false - }, - "mcg7ZXEIUL": { - "sys:id": "mcg7ZXEIUL", - "sys:flavour": "affine:list", - "sys:children": [], - "prop:text": [ - { - "insert": "finished:", - "attributes": { - "background": "var(--affine-text-highlight-blue)" - } - }, - { - "insert": " List the tasks that have been completed. Here, you can easily review what you've learned." - } - ], - "prop:type": "bulleted", - "prop:checked": false - }, - "0H9dtAEA7G": { - "sys:id": "0H9dtAEA7G", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "Bm3PQLWdKK": { - "sys:id": "Bm3PQLWdKK", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "📖 Reading list" - } - ], - "prop:type": "h3" - }, - "ir2DPxrZN9": { - "sys:id": "ir2DPxrZN9", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "lfXQ_rY3fA": { - "sys:id": "lfXQ_rY3fA", - "sys:flavour": "affine:divider", - "sys:children": [] - }, - "NzeIqA5Q6j": { - "sys:id": "NzeIqA5Q6j", - "sys:flavour": "affine:image", - "sys:children": [], - "prop:sourceId": "/static/9vXwWGEX5W9v5pzwpu0eK4pf22DZ_sCloO0zCH1aVQ4=.png", - "prop:caption": "A goal without a plan is just a wish", - "prop:width": 752, - "prop:height": 501.328125 - }, - "rLuDwMETvq": { - "sys:id": "rLuDwMETvq", - "sys:flavour": "affine:image", - "sys:children": [], - "prop:sourceId": "/static/pIqdA3pM1la1gKzxOmAcpLmTh3yXBrL9mGTz_hGj5xE=.png", - "prop:caption": "Books are a uniquely portable magic", - "prop:width": 752, - "prop:height": 501.328125 - }, - "n5t18a4pCF": { - "sys:id": "n5t18a4pCF", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "📑 Study Plan" - } - ], - "prop:type": "h3" - }, - "A-XAaszL_S": { - "sys:id": "A-XAaszL_S", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "-tH5D5Hf4J": { - "sys:id": "-tH5D5Hf4J", - "sys:flavour": "affine:note", - "sys:children": [ - "ir2DPxrZN9", - "Bm3PQLWdKK", - "rLuDwMETvq", - "s7EitB9S6F", - "L0wkiKeVji", - "lfXQ_rY3fA" - ], - "prop:background": "--affine-background-secondary-color", - "prop:xywh": "[832.6496028098738,20.093439633665938,800,1296]", - "prop:index": "a01", - "prop:hidden": false - }, - "W6M-MPQ5-F": { - "sys:id": "W6M-MPQ5-F", - "sys:flavour": "affine:note", - "sys:children": [ - "A-XAaszL_S", - "n5t18a4pCF", - "NzeIqA5Q6j", - "uZTvHIo1eH", - "YsMF9PplY6", - "aMVed60Zxh", - "G_hTd1c1Xr", - "mcg7ZXEIUL", - "0H9dtAEA7G", - "z3JCtDKEnH" - ], - "prop:background": "--affine-background-secondary-color", - "prop:xywh": "[-218.38057009913342,20.089515493697448,800,1444]", - "prop:index": "a01", - "prop:hidden": false - } - } -} diff --git a/packages/frontend/templates/v1/personal-project-management.json b/packages/frontend/templates/v1/personal-project-management.json deleted file mode 100644 index ca96780829..0000000000 --- a/packages/frontend/templates/v1/personal-project-management.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "blocks": { - "LvRIWQ1EAT": { - "sys:id": "LvRIWQ1EAT", - "sys:flavour": "affine:page", - "sys:children": ["9eBKQxt8ax", "2nHeyqzDMf"], - "prop:title": [ - { - "insert": "AFFiNE's Personal project management" - } - ] - }, - "9eBKQxt8ax": { - "sys:id": "9eBKQxt8ax", - "sys:flavour": "affine:surface", - "sys:children": [], - "prop:elements": { - "type": "$blocksuite:internal:native$", - "value": {} - } - }, - "2nHeyqzDMf": { - "sys:id": "2nHeyqzDMf", - "sys:flavour": "affine:note", - "sys:children": ["g0M-Toj8iK", "JpcfJa6HqU", "w9Oyl2J6bd"], - "prop:xywh": "[0,0,800,361]", - "prop:background": "--affine-background-secondary-color", - "prop:index": "a0", - "prop:hidden": false - }, - "g0M-Toj8iK": { - "sys:id": "g0M-Toj8iK", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "To-do-list " - } - ] - }, - "JpcfJa6HqU": { - "sys:id": "JpcfJa6HqU", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "/" - } - ] - }, - "w9Oyl2J6bd": { - "sys:id": "w9Oyl2J6bd", - "sys:flavour": "affine:database", - "sys:children": ["yP88q8kMa_", "xo7ok9TmO7", "dOQULUJfK6"], - "prop:views": [ - { - "id": "zp1yuVbPHH", - "name": "table", - "mode": "table", - "columns": [ - { - "id": "uSHvJhR2FN", - "width": 200 - } - ], - "filter": { - "type": "group", - "op": "and", - "conditions": [] - } - } - ], - "prop:title": [ - { - "insert": "Database" - } - ], - "prop:cells": {}, - "prop:columns": [ - { - "type": "title", - "id": "w9Oyl2J6bd", - "name": "Title", - "data": {} - }, - { - "type": "multi-select", - "name": "Tag", - "data": { - "options": [] - }, - "id": "uSHvJhR2FN" - } - ], - "prop:titleColumnName": "Title", - "prop:titleColumnWidth": 185 - }, - "yP88q8kMa_": { - "sys:id": "yP88q8kMa_", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [], - "prop:type": "text" - }, - "xo7ok9TmO7": { - "sys:id": "xo7ok9TmO7", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [], - "prop:type": "text" - }, - "dOQULUJfK6": { - "sys:id": "dOQULUJfK6", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [], - "prop:type": "text" - } - } -} diff --git a/packages/frontend/templates/v1/preloading.json b/packages/frontend/templates/v1/preloading.json deleted file mode 100644 index 23d66bfff5..0000000000 --- a/packages/frontend/templates/v1/preloading.json +++ /dev/null @@ -1,6538 +0,0 @@ -{ - "blocks": { - "_9D75ibqvd": { - "sys:id": "_9D75ibqvd", - "sys:flavour": "affine:page", - "sys:children": [ - "4-IQVC-U5A", - "Y4oz3g1LB6", - "VNbg-Wz6Vs", - "V7dUwRJxpY", - "1cFTd-rwDr", - "A2hTNhHJSo", - "JSoC9zIZDz", - "Vq_8QO3ruz", - "g5OrKM5Fb6", - "3An3wRFKN_", - "U2hR9Lu1E7", - "nOERveFG0j" - ], - "prop:title": [ - { - "insert": "AFFiNE - not just a note-taking app" - } - ] - }, - "4-IQVC-U5A": { - "sys:id": "4-IQVC-U5A", - "sys:flavour": "affine:note", - "sys:children": [ - "1906676326:0", - "QWiWzO9705", - "XddQMjB8JC", - "3uLMBGieUN", - "dhMxx0X4Dd", - "b0CE_s6CBB", - "i1Jsmcp-sy" - ], - "prop:background": "--affine-background-secondary-color", - "prop:index": "a2", - "prop:xywh": "[866.5055790704506,86.14140870095756,326.7016703680564,539]", - "prop:hidden": false - }, - "1906676326:0": { - "sys:id": "1906676326:0", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "There are lots of apps out there, so why chose AFFiNE?" - } - ] - }, - "QWiWzO9705": { - "sys:id": "QWiWzO9705", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "AFFiNE is " - }, - { - "insert": "feature-rich", - "attributes": { - "bold": true - } - }, - { - "insert": " - offering a wide-range of " - }, - { - "insert": "blocks", - "attributes": { - "italic": true - } - }, - { - "insert": " that " - }, - { - "insert": "empower", - "attributes": { - "italic": true - } - }, - { - "insert": " your docs" - } - ] - }, - "XddQMjB8JC": { - "sys:id": "XddQMjB8JC", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "AFFiNE is " - }, - { - "insert": "privacy focused", - "attributes": { - "bold": true - } - }, - { - "insert": " - with a local-first approach, keep control of your data" - } - ] - }, - "3uLMBGieUN": { - "sys:id": "3uLMBGieUN", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "AFFiNE is " - }, - { - "insert": "open", - "attributes": { - "bold": true - } - }, - { - "insert": "-" - }, - { - "insert": "source", - "attributes": { - "bold": true - } - }, - { - "insert": " - you can check us out on" - } - ] - }, - "b0CE_s6CBB": { - "sys:id": "b0CE_s6CBB", - "sys:flavour": "affine:bookmark", - "sys:children": [], - "prop:url": "https://github.com/toeverything/affine", - "prop:bookmarkTitle": "GitHub - toeverything/AFFiNE: There can be more than Notion and Miro. AFFiNE is a next-gen knowledge base that brings planning, sorting and creating all together. Privacy first, open-source, customizable and ready to use.", - "prop:description": "There can be more than Notion and Miro. AFFiNE is a next-gen knowledge base that brings planning, sorting and creating all together. Privacy first, open-source, customizable and ready to use. - Gi...", - "prop:icon": "https://github.githubassets.com/favicons/favicon.svg", - "prop:image": "https://repository-images.githubusercontent.com/519859998/a99cfab6-8d58-4c05-9523-ea31e0cda7f8", - "prop:caption": "", - "prop:hasCrawled": true, - "prop:language": "en", - "prop:type": "object", - "prop:provider": "GitHub", - "prop:twitter": "@github", - "prop:crawled": true - }, - "i1Jsmcp-sy": { - "sys:id": "i1Jsmcp-sy", - "sys:flavour": "affine:divider", - "sys:children": [] - }, - "Y4oz3g1LB6": { - "sys:id": "Y4oz3g1LB6", - "sys:flavour": "affine:note", - "sys:children": ["Prrnq7ruGC"], - "prop:xywh": "[855.7586305793726,-38.93174493636967,1488.043436415603,120]", - "prop:index": "a0", - "prop:background": "--affine-tag-yellow", - "prop:hidden": false - }, - "Prrnq7ruGC": { - "sys:id": "Prrnq7ruGC", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "h1", - "prop:text": [ - { - "insert": "Let's " - }, - { - "insert": "Write", - "attributes": { - "bold": true - } - }, - { - "insert": ", " - }, - { - "insert": "Draw", - "attributes": { - "bold": true - } - }, - { - "insert": " and " - }, - { - "insert": "Plan", - "attributes": { - "bold": true - } - }, - { - "insert": " with " - }, - { - "insert": "AFFiNE", - "attributes": { - "bold": true - } - } - ] - }, - "VNbg-Wz6Vs": { - "sys:id": "VNbg-Wz6Vs", - "sys:flavour": "affine:note", - "sys:children": ["hiVj6pUziI", "6rgGQmAmfb", "KRefAQRNnh"], - "prop:index": "a0", - "prop:background": "--affine-tag-green", - "prop:xywh": "[1211.9913594556406,86.0748937043449,552.5830233754074,567]", - "prop:hidden": false - }, - "hiVj6pUziI": { - "sys:id": "hiVj6pUziI", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "Create Your Workspace" - } - ], - "prop:type": "h3" - }, - "6rgGQmAmfb": { - "sys:id": "6rgGQmAmfb", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "Creating a workspace in AFFiNE is easy! With just a few clicks, you can create a new workspace and start organising your content in a clean, user-friendly interface." - } - ] - }, - "KRefAQRNnh": { - "sys:id": "KRefAQRNnh", - "sys:flavour": "affine:image", - "sys:children": [], - "prop:type": "image", - "prop:sourceId": "/static/27f983d0765289c19d10ee0b51c00c3c7665236a1a82406370d46e0a.gif", - "prop:caption": "", - "prop:width": 0, - "prop:height": 0 - }, - "V7dUwRJxpY": { - "sys:id": "V7dUwRJxpY", - "sys:flavour": "affine:surface", - "sys:children": [], - "prop:elements": { - "type": "$blocksuite:internal:native$", - "value": { - "HAcJCK_5pU": { - "type": "brush", - "xywh": "[1199.3739609288366,67.69610348511776,41.289133053352316,60.53518339203259]", - "points": [ - [10.42011260861841, 27.26039288914211], - [13.427283883849213, 21.847451555754503], - [15.231597661645083, 18.840280280523643], - [16.434510222366725, 17.637395251445298], - [17.637367719801887, 14.630196444571084], - [18.238824000162595, 14.02876769585356], - [18.238824000162595, 13.427338947136036], - [19.441681497597756, 11.623025169340167], - [20.043137777958464, 9.818711391544298], - [21.245995275393625, 8.014397613748429], - [22.44885277282856, 6.8115125846700835], - [23.651765333550202, 5.608627555591738], - [24.253166550624428, 5.007198806874214], - [24.854622830985363, 4.405770058156747], - [26.057480328420297, 3.202885029078402], - [26.658936608781232, 2.6014562803608783], - [27.861794106216166, 2], - [28.4632503865771, 2], - [28.4632503865771, 2.6014562803608783], - [29.666107884012035, 4.405770058156747], - [29.666107884012035, 8.014397613748429], - [28.4632503865771, 12.825882666775215], - [27.861794106216166, 18.238824000162765], - [26.658936608781232, 23.651765333550372], - [26.057480328420297, 29.6661629472988], - [24.854622830985363, 35.680533029403875], - [24.854622830985363, 38.68773183627809], - [23.651765333550202, 43.499216889304876], - [23.050309053189494, 47.10784444489656], - [21.847451555754333, 49.513614503053304], - [21.847451555754333, 51.31792828084917], - [21.847451555754333, 53.12224205864504], - [21.847451555754333, 54.926555836440855], - [21.847451555754333, 55.52798458515838], - [21.847451555754333, 56.1294133338759], - [21.847451555754333, 57.33229836295425], - [21.847451555754333, 57.93372711167177], - [21.245995275393625, 57.93372711167177], - [20.64453899503269, 57.93372711167177], - [20.043137777958464, 57.93372711167177], - [19.441681497597756, 57.93372711167177], - [18.238824000162595, 57.93372711167177], - [17.637367719801887, 57.93372711167177], - [15.833053942006018, 57.93372711167177], - [14.028740164210149, 57.93372711167177], - [12.22442638641428, 57.93372711167177], - [10.42011260861841, 57.93372711167177], - [9.217255111183249, 57.93372711167177], - [7.41294133338738, 57.93372711167177], - [6.210028772665737, 57.93372711167177], - [4.405714994870095, 57.93372711167177], - [3.804313777795869, 57.93372711167177], - [2.6014012170742262, 57.93372711167177], - [2, 57.93372711167177], - [2.6014012170742262, 57.93372711167177], - [3.804313777795869, 57.93372711167177], - [6.811485053026672, 58.53518339203259], - [10.42011260861841, 58.53518339203259], - [14.630196444570856, 58.53518339203259], - [18.238824000162595, 58.53518339203259], - [20.64453899503269, 58.53518339203259], - [24.253166550624428, 58.53518339203259], - [26.658936608781232, 58.53518339203259], - [28.4632503865771, 58.53518339203259], - [29.06470666693781, 58.53518339203259], - [30.26756416437297, 58.53518339203259], - [29.666107884012035, 58.53518339203259], - [28.4632503865771, 58.53518339203259], - [26.658936608781232, 57.93372711167177], - [23.651765333550202, 57.93372711167177], - [20.043137777958464, 57.33229836295425], - [17.03591143944095, 57.33229836295425], - [14.630196444570856, 57.33229836295425], - [13.427283883849213, 57.33229836295425], - [11.622970106053344, 57.33229836295425], - [10.42011260861841, 57.33229836295425], - [9.217255111183249, 57.33229836295425], - [9.818656328257475, 57.33229836295425], - [12.22442638641428, 57.33229836295425], - [14.028740164210149, 57.33229836295425], - [17.637367719801887, 57.33229836295425], - [21.245995275393625, 57.93372711167177], - [24.854622830985363, 57.93372711167177], - [28.4632503865771, 58.53518339203259], - [31.470421661807904, 58.53518339203259], - [33.87619171996471, 58.53518339203259], - [35.68050549776058, 58.53518339203259], - [36.88336299519551, 58.53518339203259], - [37.48481927555645, 58.53518339203259], - [38.68767677299138, 58.53518339203259], - [39.289133053352316, 58.53518339203259] - ], - "color": "--affine-palette-line-orange", - "lineWidth": 4, - "id": "HAcJCK_5pU", - "index": "b52", - "seed": 884497414 - }, - "-1rQ-C-Pj4": { - "type": "brush", - "xywh": "[1174.1135680396944,56.8702208183426,103.23725777877257,85.79557628117465]", - "points": [ - [38.086275555917155, 10.420140140261822], - [34.477648000325416, 11.021568888979346], - [28.4632503865771, 12.825882666775158], - [26.057480328420525, 14.02876769585356], - [23.050309053189494, 16.434510222366896], - [19.441681497597756, 18.840280280523586], - [16.434510222366953, 22.448907836115325], - [10.42011260861841, 30.869020444733792], - [5.608627555591738, 41.69490311150901], - [2.6014012170742262, 51.91935702956664], - [2, 56.1294133338759], - [2, 65.15098222285519], - [3.804313777795869, 72.36823733403861], - [5.608627555591738, 76.57832116999123], - [11.021568888979346, 80.18694872558291], - [19.441681497597756, 83.19412000081383], - [31.470421661807904, 83.79557628117465], - [43.49921688930476, 81.38980622301796], - [49.513559439766595, 79.58549244522209], - [62.14375588433768, 72.96969361439949], - [72.36823733403867, 66.35386725193354], - [82.59266372045295, 59.738040889467584], - [89.80991883163642, 53.72367080736251], - [95.82431644538497, 45.904986947461566], - [99.4329440009767, 36.88341805848222], - [101.23725777877257, 27.861849169502932], - [101.23725777877257, 23.651765333550372], - [100.0343452180507, 15.833081473649372], - [97.0271739428199, 9.818711391544298], - [89.80991883163642, 5.007198806874214], - [78.38257988450027, 2], - [62.14375588433768, 3.2028850290783453], - [44.70207438673992, 9.818711391544298], - [36.281961778121286, 15.231652724931905] - ], - "color": "--affine-palette-line-orange", - "lineWidth": 4, - "id": "-1rQ-C-Pj4", - "index": "b5C", - "seed": 1115310003 - }, - "qv6cv86xys": { - "type": "brush", - "xywh": "[1193.961019595449,106.18814909919172,175.40980889060688,54.520785778284164]", - "points": [ - [2, 52.520785778284164], - [6.210028772665964, 50.115015720127474], - [16.434510222366725, 44.1006731696657], - [23.050309053189494, 40.49201808243066], - [30.86902044473368, 36.2819617781214], - [48.31070194233166, 28.4632503865771], - [67.55669721772506, 20.043137777958634], - [86.8027475564054, 12.825882666775215], - [103.64297277364244, 6.811485053026786], - [115.6717680011393, 3.804313777795869], - [120.48325305416597, 3.2028574974350477], - [127.09905188498874, 2], - [129.50482194314532, 2], - [130.10627822350625, 2], - [129.50482194314532, 2], - [124.69333689011864, 3.2028574974350477], - [111.46168416518663, 7.412941333387607], - [103.64297277364244, 10.420112608618467], - [85.59983499568375, 17.03593897108442], - [68.158153498086, 23.651765333550372], - [54.9265007731542, 30.26756416437297], - [45.30353066710063, 33.87619171996471], - [39.289133053352316, 36.88339052683892], - [36.88336299519551, 38.68770430463479], - [36.281961778121286, 38.68770430463479], - [45.90493188417486, 36.2819617781214], - [58.535128328745714, 32.67333422252966], - [90.41137511199713, 23.651765333550372], - [124.69333689011864, 14.630196444571084], - [158.37384238787922, 7.412941333387607], - [173.40980889060688, 5.007171275230917] - ], - "color": "--affine-palette-line-orange", - "lineWidth": 4, - "id": "qv6cv86xys", - "index": "b53", - "seed": 1714424609 - }, - "ZQgZb5kQsz": { - "type": "brush", - "xywh": "[1778.968233049558,154.9461089126983,238.56079111346207,19.637367719801944]", - "points": [ - [2, 2], - [2.6014012170742262, 6.811485053026729], - [3.804313777795869, 9.818656328257646], - [5.608627555591738, 11.622970106053458], - [7.412941333387607, 13.427283883849327], - [8.014342550461834, 14.028740164210205], - [11.021568888979346, 15.231597661645196], - [13.42728388384944, 15.833053942006075], - [15.833109005292727, 15.833053942006075], - [20.644538995032917, 15.833053942006075], - [24.253166550624655, 14.630196444571027], - [27.861794106216394, 14.028740164210205], - [31.470421661808132, 13.427283883849327], - [35.07904921739987, 12.825882666775158], - [36.88336299519551, 12.825882666775158], - [40.49199055078725, 12.825882666775158], - [43.49921688930476, 12.825882666775158], - [46.5064432278225, 12.825882666775158], - [48.31075700561837, 14.028740164210205], - [50.71647200048824, 14.630196444571027], - [53.122186995358334, 15.833053942006075], - [55.52801211680185, 16.434510222366896], - [57.332325894597716, 17.035911439441065], - [60.94095345018946, 17.637367719801944], - [63.94806966213355, 17.637367719801944], - [66.95529600065106, 16.434510222366896], - [69.36101099552093, 15.833053942006075], - [72.36823733403867, 14.630196444571027], - [74.77395232890854, 13.427283883849327], - [78.38257988450027, 12.224426386414336], - [79.58549244522214, 11.622970106053458], - [82.59271878373966, 11.021568888979289], - [85.59983499568375, 11.622970106053458], - [88.60706133420149, 12.825882666775158], - [91.01277632907136, 13.427283883849327], - [93.41860145051487, 14.028740164210205], - [96.42571766245896, 15.231597661645196], - [98.83154278390225, 15.833053942006075], - [100.63585656169812, 16.434510222366896], - [104.24448411728986, 17.035911439441065], - [107.8531116728816, 17.035911439441065], - [110.86022788482592, 16.434510222366896], - [113.86745422334343, 15.833053942006075], - [116.87468056186094, 14.630196444571027], - [119.88179677380526, 14.028740164210205], - [123.490424329397, 13.427283883849327], - [124.69333689011866, 13.427283883849327], - [127.09905188498873, 13.427283883849327], - [128.9033656627846, 13.427283883849327], - [130.70767944058048, 14.028740164210205], - [132.51199321837635, 15.231597661645196], - [134.31630699617222, 15.833053942006075], - [136.7221321176155, 16.434510222366896], - [138.52644589541137, 16.434510222366896], - [142.1350734510031, 15.833053942006075], - [143.93938722879898, 15.833053942006075], - [146.9465034407433, 14.028740164210205], - [149.35232856218659, 12.825882666775158], - [151.15664233998245, 12.224426386414336], - [152.96095611777832, 11.021568888979289], - [154.7652698955742, 11.021568888979289], - [155.96807232972265, 11.021568888979289], - [157.17098489044406, 11.021568888979289], - [158.37389745116593, 11.622970106053458], - [160.1782112289618, 12.825882666775158], - [161.98252500675767, 13.427283883849327], - [164.38824000162754, 14.028740164210205], - [167.99686755721928, 15.231597661645196], - [171.605495112811, 15.833053942006075], - [175.21412266840275, 15.833053942006075], - [177.01843644619862, 15.231597661645196], - [181.82997656251223, 14.028740164210205], - [186.04000533517797, 11.021568888979289], - [190.2500341078437, 8.615798830822598], - [193.85866166343544, 6.210028772665908], - [196.86588800195318, 5.00717127523086], - [199.27160299682305, 4.4057149948700385], - [201.07591677461892, 5.00717127523086], - [202.2788293353408, 5.608627555591738], - [204.08314311313666, 6.811485053026729], - [206.48885810800653, 8.615798830822598], - [208.89468322945004, 10.420112608618467], - [211.90179944139413, 11.622970106053458], - [215.51042699698587, 12.825882666775158], - [219.1190545525776, 12.825882666775158], - [224.53199588596522, 12.825882666775158], - [229.34353600227882, 12.825882666775158], - [231.1478497800747, 12.825882666775158], - [236.56079111346207, 11.021568888979289] - ], - "color": "--affine-palette-line-orange", - "lineWidth": 4, - "id": "ZQgZb5kQsz", - "index": "b54", - "seed": 818015273 - }, - "89QPeYObe_": { - "type": "brush", - "xywh": "[1768.0340525335532,73.24604840149084,42.72564510589291,59.64028110831856]", - "points": [ - [4.670734145234064, 29.597572583361625], - [3.3353670726169184, 26.036607307105953], - [2.4451766937643242, 22.030506089254942], - [2, 18.914636002425652], - [2, 17.134153364297816], - [2.4451766937643242, 15.798786291680813], - [3.3353670726169184, 13.128052146446805], - [5.1159108389983885, 10.457318001212826], - [7.786644984232225, 7.341468290467986], - [11.347569508319111, 4.670734145234007], - [15.353670726170094, 3.3353670726170037], - [17.134214492551337, 2.4451155655108607], - [20.695139016637995, 2], - [23.811049855636156, 2.890231131021693], - [26.92687919029663, 4.225598203638668], - [29.597613335530696, 6.006101217851011], - [31.378075597574025, 8.23169942148968], - [32.26834748076453, 9.567066494106655], - [34.9390816259986, 14.018303653552977], - [35.38417681542501, 15.798786291680813], - [35.38417681542501, 20.695139016637967], - [33.60371455338168, 26.036607307105953], - [30.932980408147614, 31.823191163084772], - [26.92687919029663, 37.60977501906359], - [21.58541089982873, 43.39637925112689], - [18.914676754594666, 46.06711339636087], - [14.018303653553176, 50.51833017972274], - [9.567107246275555, 53.18906432495669], - [6.896373101041718, 54.969546963084554], - [5.1159108389983885, 55.8597984701907], - [4.225638955807881, 56.30491403570153], - [3.3353670726169184, 56.30491403570153], - [2.890271883190735, 56.30491403570153], - [3.3353670726169184, 56.30491403570153], - [6.00610121785121, 55.8597984701907], - [10.457379129466517, 55.414682904679864], - [15.353670726170094, 55.414682904679864], - [19.8049486377854, 55.414682904679864], - [24.256145045062567, 55.8597984701907], - [27.817151073487366, 56.75004997729684], - [29.597613335530696, 56.75004997729684], - [32.26834748076453, 57.64028110831856], - [34.49398643657241, 57.64028110831856], - [36.27444869861574, 57.64028110831856], - [37.60981577123266, 57.64028110831856], - [38.94518284384958, 57.64028110831856], - [39.835454727040315, 57.64028110831856], - [40.2805499164665, 57.64028110831856], - [40.72564510589291, 57.64028110831856] - ], - "color": "--affine-palette-line-orange", - "lineWidth": 4, - "id": "89QPeYObe_", - "index": "b55", - "seed": 532445074 - }, - "A_oVc16bDm": { - "type": "brush", - "xywh": "[1748.0035464442983,36.300885933725795,93.46959386533877,115.28058259272154]", - "points": [ - [39.39027803327599, 103.48789751889171], - [36.719543888041926, 103.93301308440255], - [34.493986436572186, 104.82326459150869], - [31.82325229133835, 105.26838015701958], - [28.26224626291355, 105.26838015701958], - [26.036607307106124, 104.82326459150869], - [24.256145045062794, 104.37814902599786], - [20.250043827211584, 102.59764601178557], - [16.2439426093606, 99.92691186655156], - [11.792746202083208, 95.03057951767892], - [7.786644984232453, 88.35374415459393], - [6.006101217850983, 84.79275850225378], - [3.7805437663812427, 76.33544050104095], - [2, 68.76835363084982], - [2.4451766937643242, 59.42078412253085], - [3.335367072617146, 50.96346612131802], - [5.561006028424572, 41.61589661299905], - [10.012202435702193, 33.15855823570175], - [15.353670726169867, 25.14635579999978], - [18.469581565168255, 21.58539052374411], - [26.92687919029663, 14.463419219063809], - [36.719543888041926, 8.23171979757413], - [46.957385279551545, 3.780482638127836], - [56.304954787870656, 2], - [64.76225241299903, 3.3353670726169753], - [72.774454848701, 6.896352724957126], - [79.45129021178582, 11.34756950831897], - [82.12202435701988, 14.463419219063809], - [87.0183974580616, 22.475621654765774], - [90.13422679272185, 30.487824090467768], - [91.46959386533877, 39.83539359878674], - [90.57940348648594, 48.29273197608401], - [87.90866934125211, 57.64030148440301], - [83.90256812340112, 66.98787099272198], - [78.1159231391689, 76.78055606655178], - [75.00009380450842, 81.23177284991363], - [67.8781632519972, 89.24397528561559], - [60.75615119514782, 96.36594659029589], - [52.74394875944586, 103.04278195338088], - [43.84155594489107, 108.38425024384884], - [34.9390816259986, 111.94521552010451], - [30.48788521872143, 113.28058259272154] - ], - "color": "--affine-palette-line-orange", - "lineWidth": 4, - "id": "A_oVc16bDm", - "index": "b5B", - "seed": 924502832 - }, - "ejQqAIbwyV": { - "type": "brush", - "xywh": "[1958.0171685463724,124.04480145592629,109.21424480419864,43.98768379515283]", - "points": [ - [30.808662267816118, 41.719301915775375], - [28.303561201049796, 41.98768379515283], - [24.12834179002687, 41.98768379515283], - [19.535707467366347, 41.719301915775375], - [15.360488056343689, 41.18256272736573], - [13.272954800449758, 40.64581125378342], - [9.932794561555276, 39.57232059179137], - [7.427693494788418, 37.96207845621705], - [4.922592428021833, 35.546715252855584], - [2.834982722510503, 32.86297017011674], - [2, 29.910867778345562], - [2, 26.153632033614613], - [2.417491361255255, 24.543389898040317], - [4.087533255893929, 21.591275221096573], - [7.427693494788418, 18.639172829325368], - [14.107937522960265, 14.88193708459448], - [23.710850428771607, 11.12470133986356], - [34.148746057093454, 8.172592805506088], - [45.83926866841613, 5.757229602144631], - [57.94720619137643, 3.6102421355743055], - [64.209958858293, 2.8051210677871476], - [75.90048146961566, 2], - [85.92088573668231, 2.5367453309960113], - [93.85368029823752, 4.146987466570318], - [100.11643296515417, 6.562350669931771], - [103.87408456530412, 8.97771387329324], - [106.79667699332602, 12.466573881233005], - [107.21424480419864, 16.492179220168758], - [105.96169427081539, 20.51778455910454], - [104.70914373743206, 22.664778168261137], - [100.11643296515417, 26.95875310140179], - [92.18363840359885, 30.71598884613269], - [80.07562443102093, 35.27833337347822], - [67.55011909718776, 38.230448050421934], - [56.27716429673798, 40.37744165957853], - [46.67425139092665, 40.914180847988305], - [38.32396546811611, 40.64581125378342], - [34.98380522922164, 40.64581125378342], - [28.72105256230505, 39.84069018599621] - ], - "color": "--affine-palette-line-tangerine", - "lineWidth": 4, - "id": "ejQqAIbwyV", - "index": "b56", - "seed": 1680628608 - }, - "6Jp3AVejnM": { - "type": "brush", - "xywh": "[2360.423645473335,227.68729583129817,221.43622144545589,541.050290364407]", - "points": [ - [24.668485715856374, 5], - [33.68325835516946, 5], - [37.78075950688154, 5], - [46.795532146194624, 5], - [52.53212379377371, 5], - [57.4492452227379, 5], - [71.3811392910152, 7.516826020684391], - [86.13250357790737, 11.711510452680159], - [103.34242857928186, 16.74516249404894], - [118.91326308478892, 24.295640556102114], - [134.48424764893366, 33.52401543318399], - [141.04030951512715, 38.55766747455277], - [153.33311308753744, 47.786042351634705], - [164.80644644133284, 58.69227564002796], - [177.09925001374313, 72.1153349491056], - [188.57258336753853, 89.73311709389634], - [198.40682622546697, 108.18986684806015], - [206.60197858752792, 127.48550740416226], - [211.5191000164917, 147.6201155696374], - [213.97766073097398, 158.52638726174797], - [215.61675122684102, 179.49992463287884], - [216.43622144545589, 202.15135881903836], - [215.61675122684102, 226.4806898202266], - [213.15819051235914, 250.80998241769748], - [209.0605393020098, 275.9782426245414], - [204.1434178730456, 299.46864442007404], - [200.86538693994882, 311.213806914123], - [193.48970479650276, 332.1873826889712], - [186.11402265305668, 353.9998492657577], - [177.918720232358, 372.45659901992144], - [168.90409765168218, 390.9133487740852], - [158.25023451650165, 410.20898933018736], - [146.7770512213439, 426.9878038656051], - [134.48424764893366, 442.9277275990844], - [127.92803572410243, 451.3171732705107], - [115.63523215169214, 464.7401941758709], - [102.52295836066698, 477.32432427929297], - [90.23015478825668, 487.3916283620305], - [78.75682143446127, 497.45893244476804], - [66.46401786205098, 505.8483781161942], - [55.81030478550815, 512.5598885688744], - [45.97606192757975, 518.4325082196162], - [36.96128928826666, 524.3051278703581], - [29.58560714482013, 528.4998123023538], - [25.487955934471255, 530.1776707136652], - [18.931894068277288, 532.6944967343495], - [14.014772639313081, 535.211322755034], - [10.736591647578638, 535.211322755034], - [8.27803093309676, 536.050290364407], - [7.458560714481881, 536.050290364407], - [5.81947021861488, 536.050290364407], - [5, 536.050290364407] - ], - "color": "--affine-palette-line-orange", - "lineWidth": 10, - "id": "6Jp3AVejnM", - "index": "b57", - "seed": 209981851 - }, - "wu6_2zbm4B": { - "type": "brush", - "xywh": "[2343.2289649844256,726.054431401066,61.175411215626355,63.69231404374534]", - "points": [ - [27.65143418615935, 5], - [24.29556374866752, 8.355793630057406], - [18.423020905360318, 16.745239301483707], - [13.389368863991422, 25.9735757748482], - [11.711433645245506, 29.329369404905606], - [10.033652041368896, 32.68508622752836], - [6.6777816038766105, 38.55770587827021], - [5, 42.75239031026592], - [5, 44.43032552901195], - [5.838890801938305, 45.26921633095026], - [7.516826020684221, 46.9471515496964], - [13.389368863991422, 47.786042351634705], - [21.778737727982843, 49.463977570380735], - [31.84604181072064, 51.98080359106518], - [42.75239031026604, 55.33652041368782], - [51.980649976195764, 58.69231404374534], - [56.175411215626355, 58.69231404374534] - ], - "color": "--affine-palette-line-orange", - "lineWidth": 10, - "id": "wu6_2zbm4B", - "index": "b58", - "seed": 338012081 - }, - "raHEkjUvv_": { - "type": "brush", - "xywh": "[1819.1087993210533,641.4106664311514,45.67937701858909,75.57980958071607]", - "points": [ - [5.62428103042771, 27.82310084910955], - [4.718221142011544, 25.55790965130609], - [3.359110571005658, 21.480577938288885], - [2.9060598884161664, 17.403246225271573], - [2.9060598884161664, 15.591105710057718], - [3.359110571005658, 12.419833885456567], - [4.2651704594218245, 9.248582799236942], - [6.077331713017429, 6.530361657225399], - [8.795552855028745, 4.265170459422052], - [13.325935250635666, 2.453029944208083], - [17.856276169479543, 2], - [22.839709247676183, 2], - [25.104879707098007, 2.453029944208083], - [29.182211420115436, 3.3591105710058855], - [33.25954313313264, 5.171251086219627], - [36.88382416356035, 7.436442284023087], - [39.60204530557189, 10.154663426034631], - [41.86725724175676, 13.77894445646234], - [43.226367812762646, 16.95021628106349], - [43.67937701858909, 20.121467367283117], - [43.226367812762646, 21.933607882496972], - [42.320266447583435, 25.55790965130609], - [40.5081466707511, 28.276130793317634], - [37.78992552873956, 30.541321991121094], - [35.071704386728015, 32.806492450543146], - [31.900432562126753, 34.165603021548804], - [28.729160737525717, 35.52471359255469], - [25.104879707098007, 35.97776427514418], - [23.29271845350263, 36.430794219352265], - [20.12148810566464, 36.88382416356035], - [17.856276169479543, 36.88382416356035], - [16.04415639264721, 36.88382416356035], - [14.685045821641552, 36.88382416356035], - [13.778944456462343, 36.88382416356035], - [13.325935250635666, 36.88382416356035], - [14.231995139051831, 36.88382416356035], - [16.497165598473885, 37.336874846149954], - [20.574497311491086, 38.24293473456612], - [25.104879707098007, 39.14901536136381], - [29.182211420115436, 40.50812593236959], - [33.25954313313264, 42.77331713017304], - [34.618653704138524, 44.13242770117881], - [37.78992552873956, 46.850648843190356], - [39.60204530557189, 49.5688699852019], - [41.41420655916727, 52.28709112721344], - [41.86725724175676, 55.45836295181448], - [41.41420655916727, 58.62959329965269], - [39.60204530557189, 62.25391580684345], - [36.88382416356035, 65.87819683727105], - [35.97776427514418, 67.23730740827682], - [30.994372673710586, 71.31463912129414], - [29.182211420115436, 72.2206990097103], - [24.19881981868184, 73.57980958071607], - [20.12148810566464, 73.57980958071607], - [16.497165598473885, 73.57980958071607], - [12.419833885456455, 72.2206990097103], - [9.248603537618465, 71.31463912129414], - [7.889492966612579, 69.95552855028836], - [5.62428103042771, 68.59641797928259], - [4.2651704594218245, 66.78425672568721], - [2.9060598884161664, 64.97213694885488], - [2, 63.61302637784911], - [2, 62.25391580684345], - [2, 60.894805235837566] - ], - "color": "--affine-palette-line-orange", - "lineWidth": 4, - "id": "raHEkjUvv_", - "index": "b59", - "seed": 1935886551 - }, - "seZnsPPCAq": { - "type": "brush", - "color": "--affine-palette-line-orange", - "id": "seZnsPPCAq", - "seed": 351264218, - "index": "b5A", - "lineWidth": 4, - "xywh": "[1789.8574858146808,628.3050933273901,120.97354401506209,108.80294601251569]", - "points": [ - [18.90365304472016, 18.903714947897356], - [22.28443317620622, 16.199103223344082], - [31.074312950443755, 10.113773270482397], - [38.51197971717079, 6.7330550421738735], - [41.21652953854664, 5.380780131485949], - [43.9211412630998, 5.380780131485949], - [47.978027898341224, 4.028443317620599], - [51.35874612664975, 3.3523368138653495], - [53.387189444270234, 2.6761684069326748], - [54.739526258135584, 2.6761684069326748], - [52.7110829405151, 2.6761684069326748], - [50.00647121596171, 3.3523368138653495], - [47.30185949140832, 3.3523368138653495], - [45.27341617378784, 3.3523368138653495], - [43.9211412630998, 3.3523368138653495], - [45.27341617378784, 3.3523368138653495], - [52.7110829405151, 2.6761684069326748], - [58.79641289337678, 2], - [76.37623434502939, 2], - [81.10928938720326, 2], - [89.22306265768566, 3.3523368138653495], - [94.6322242036149, 5.380780131485949], - [98.68911083885587, 7.409223449106548], - [102.06982906716439, 8.761498359794473], - [102.7459974740973, 9.437666766727148], - [104.0982723847851, 10.113773270482397], - [104.0982723847851, 11.466110084347633], - [102.7459974740973, 12.142216588102997], - [100.71755415647658, 13.494553401968233], - [98.01294243192342, 14.170659905723483], - [95.30839261054734, 14.170659905723483], - [93.956055796682, 14.170659905723483], - [93.27994929292686, 14.170659905723483], - [92.60378088599418, 14.170659905723483], - [91.92761247906151, 14.170659905723483], - [93.956055796682, 14.846828312656157], - [95.98449911430271, 15.522996719588832], - [102.06982906716439, 16.875271630276757], - [106.80288410933827, 19.57988335483003], - [111.53593915151214, 24.31287649382648], - [113.56438246913285, 27.017488218379754], - [116.2689322905087, 32.42664976430888], - [118.29737560812919, 37.835873213415425], - [118.97354401506209, 41.892759848656624], - [118.97354401506209, 47.30192139458575], - [118.29737560812919, 52.03497643675962], - [116.94510069744138, 56.76796957575607], - [114.24048897288822, 62.85329952861787], - [112.88821406220018, 66.23407966010382], - [108.1551590200263, 72.3194096129655], - [102.7459974740973, 77.72857115889462], - [98.68911083885587, 83.13773270482375], - [94.6322242036149, 87.19461934006495], - [89.22306265768566, 91.25150597530615], - [81.78539589095863, 94.6322861067921], - [73.67162262047623, 98.68917274203329], - [69.61473598523503, 100.71761605965389], - [61.500962714752625, 104.09833428796242], - [54.739526258135584, 106.12677760558302], - [47.30185949140832, 106.80294601251569], - [40.540423034791274, 106.80294601251569], - [33.778924674996915, 105.45060919865034], - [25.665151404514745, 103.42216588102974], - [18.90365304472016, 101.39372256340914], - [16.19910322334431, 100.04144765272122], - [10.789879774237761, 96.6607294244127], - [6.732993138996562, 93.95611769985942], - [4.028443317620713, 88.5469561539303], - [2, 79.75701447651522], - [2.6761065037553635, 62.85329952861787], - [3.3522749106880383, 57.444137982688744], - [5.380718228308524, 49.33036471220635], - [6.732993138996562, 45.27347807696515], - [10.113773270482397, 37.835873213415425], - [15.522934816411635, 31.750543260553627], - [23.636708086894032, 24.31287649382648], - [33.778924674996915, 17.55144003720943], - [43.24497285616735, 13.494553401968233], - [51.35874612664975, 10.789941677415072], - [58.79641289337678, 10.113773270482397], - [64.20557443930602, 10.789941677415072] - ] - }, - "Up_Mw9AQ5n": { - "type": "shape", - "xywh": "[857.1915051196747,-43.39418916752216,1492.6518602855915,124.57724809946413]", - "shapeType": "rect", - "radius": 0, - "filled": false, - "fillColor": "--affine-palette-transparent", - "strokeWidth": 4, - "strokeColor": "--affine-palette-line-orange", - "strokeStyle": "solid", - "roughness": 2, - "id": "Up_Mw9AQ5n", - "index": "b50", - "seed": 401827629 - }, - "5PoPey_Lmj": { - "type": "brush", - "xywh": "[1274.3716617962953,643.2031417400783,52.58851382983676,52.58851382983664]", - "points": [ - [2.418879391524115, 2], - [4.094320260853465, 4.094320260853465], - [5.769799478566256, 5.769799478566256], - [8.701878522467723, 10.377319391797187], - [9.958478348656627, 16.660318522740795], - [9.120719565608397, 25.456517306061983], - [7.026399304754932, 34.25271608938317], - [4.932079043901467, 41.373474003374895], - [3.2565998261889035, 46.39987330812983], - [2.8377204346647886, 47.65647313431862], - [2, 49.75079339517208], - [2, 50.58851382983664], - [2.8377204346647886, 50.58851382983664], - [5.769799478566256, 50.58851382983664], - [10.796198783321188, 49.75079339517208], - [15.82259808807612, 48.07531417745929], - [18.754677131977587, 47.2375937427945], - [24.19991747987342, 45.14327348194104], - [29.226316784628125, 43.88667365575236], - [33.414995654718496, 42.630073829563685], - [36.76591574176064, 42.21119443803957], - [39.697994785662104, 41.373474003374895], - [42.21119443803968, 40.954594611850894], - [44.30551469889315, 40.53571522032678], - [45.14327348194115, 40.53571522032678], - [46.8187143512705, 39.697994785662104], - [48.49419356898329, 39.2791153941381], - [49.33191400364808, 39.2791153941381], - [50.16967278669608, 39.2791153941381], - [50.58851382983676, 39.2791153941381], - [50.58851382983676, 38.441394959473314], - [50.58851382983676, 38.022515567949426], - [50.58851382983676, 37.60367452480875] - ], - "color": "--affine-palette-line-orange", - "lineWidth": 4, - "id": "5PoPey_Lmj", - "index": "b5D", - "seed": 1785430482 - }, - "HWW9D4QEIx": { - "type": "brush", - "xywh": "[1294.6728677900371,640.4943333129787,9.026399304754932,83.16578904989046]", - "points": [ - [2, 2], - [2.4188410431406737, 8.282999130943722], - [4.094320260853465, 21.686756175879168], - [5.350920087042141, 32.158395828529706], - [7.026399304754932, 49.75079339517208], - [7.026399304754932, 55.196072091451015], - [6.607519913230817, 69.85639061419181], - [6.18864052170693, 73.20734904961739], - [5.769799478566256, 77.81486896284821], - [4.932040695518253, 79.90918922370167], - [4.932040695518253, 80.32806861522579], - [4.932040695518253, 81.16578904989046], - [4.932040695518253, 80.74694800674979] - ], - "color": "--affine-palette-line-orange", - "lineWidth": 4, - "id": "HWW9D4QEIx", - "index": "b5E", - "seed": 838292120 - }, - "1bux65v5hI": { - "type": "brush", - "xywh": "[1242.792223309561,623.230802236176,127.14678296649618,120.44492361822017]", - "points": [ - [100.85254522576952, 69.01865100533541], - [100.01478644272152, 70.69413022304809], - [98.75818661653284, 76.13940891932702], - [96.66386635567937, 82.00352865874675], - [94.98838713796658, 85.35444874578889], - [93.31294626863723, 88.28652778969035], - [89.54314679007098, 93.7318064859694], - [84.09786809379193, 99.59592622538912], - [79.90918922370179, 102.94684631243126], - [69.85639061419192, 110.06760422642299], - [57.29039235230448, 115.51284457431859], - [44.30555304727636, 118.44492361822017], - [32.15839582852959, 118.44492361822017], - [22.943356002067958, 115.9317239658426], - [15.403757044935446, 111.32420405261178], - [9.539598957132512, 104.20344613861994], - [6.607558261614258, 99.59592622538912], - [2.83775878304823, 88.28652778969035], - [2, 75.30165013627902], - [3.675479217712791, 62.31681083125101], - [8.701878522467723, 50.16965361250436], - [15.403757044935446, 38.860255176805595], - [25.037676262921423, 27.969736132631056], - [53.10175183059755, 9.120738739800004], - [60.64135078773006, 6.188659695898423], - [82.42238887607914, 2], - [88.70538800702298, 2], - [100.85254522576952, 4.513199652377352], - [110.4864644437555, 9.120738739800004], - [118.02606340088778, 16.2414583054084], - [124.30906253183161, 28.388596349963336], - [125.14678296649618, 35.509335089763454], - [116.35058418317522, 59.38473178734944], - [109.22986461756682, 68.18093057067063], - [94.1506667033018, 84.93560770264821], - [87.0299471376934, 92.05632726825661] - ], - "color": "--affine-palette-line-orange", - "lineWidth": 4, - "id": "1bux65v5hI", - "index": "b5F", - "seed": 752261619 - }, - "goGplwPL_A": { - "type": "brush", - "xywh": "[793.767515790554,650.1460398223759,64.73567104858341,79.81486896284821]", - "points": [ - [20.011276958166377, 2], - [18.754677131977587, 6.1886596958985365], - [18.335797740453586, 9.120719565608397], - [16.24147747960012, 18.335797740453586], - [16.24147747960012, 21.267876784355053], - [14.984877653411331, 28.80747574148745], - [14.56599826188733, 30.482916610816915], - [14.14713804455505, 32.57727522005371], - [13.728277827222655, 33.41499565471838], - [13.728277827222655, 33.83387504624238], - [14.14713804455505, 33.83387504624238], - [14.984877653411331, 33.41499565471838], - [17.49807730578891, 32.99611626319438], - [21.686737001687447, 32.57727522005371], - [27.550875915298775, 32.15839582852959], - [33.83387504624238, 32.57727522005371], - [39.697994785662104, 34.25271608938306], - [45.14327348194104, 36.34707469861996], - [48.0753333516509, 38.44139495947343], - [53.10173265640583, 42.21119443803956], - [57.290392352304366, 46.39987330812983], - [60.64135078772995, 51.42627261288476], - [62.31679165705941, 55.61491313459169], - [62.73567104858341, 58.96587157001716], - [62.73567104858341, 61.47907122239462], - [61.06019183087062, 63.9922708747722], - [59.80359200468183, 65.66771174410155], - [56.45267191763969, 68.59979078800302], - [51.00739322136076, 72.36959026656928], - [42.63007382956357, 75.72051035361142], - [34.25273526357478, 77.81486896284821], - [26.71313630644238, 77.81486896284821], - [20.011276958166377, 75.72051035361142], - [14.56599826188733, 73.20731070123395], - [10.377338565988794, 70.27527000571581], - [9.120738739800004, 68.59979078800302], - [6.188678870090257, 65.66771174410155], - [4.094339435045072, 62.316791657059404], - [2.8377396088563955, 59.38471261315783], - [2, 57.70927174382837], - [2, 56.45267191763969] - ], - "color": "--affine-palette-line-orange", - "lineWidth": 4, - "id": "goGplwPL_A", - "index": "b5G", - "seed": 238056563 - }, - "SrfBqzpFMh": { - "type": "brush", - "xywh": "[820.9938517493738,649.3083002135194,45.048934046895965,6.094339435045072]", - "points": [ - [2, 4.094339435045072], - [4.094339435045072, 4.094339435045072], - [14.56599826188733, 3.25659982618879], - [18.335797740453586, 3.25659982618879], - [22.105597219019728, 2.8377396088563955], - [25.875396697585984, 2.4188602173322806], - [32.9961354373861, 2], - [38.86025517680571, 2], - [41.79233422070729, 2.4188602173322806], - [42.630054655371964, 2.4188602173322806], - [43.048934046895965, 2.4188602173322806] - ], - "color": "--affine-palette-line-orange", - "lineWidth": 4, - "id": "SrfBqzpFMh", - "index": "b5I", - "seed": 1158828239 - }, - "I-pL2OzPot": { - "type": "brush", - "xywh": "[787.256986904014,615.7518235378745,121.24769174190533,124.88328601707224]", - "points": [ - [70.62170412391436, 117.88436729209025], - [67.89501361828866, 117.88436729209025], - [64.71388162939127, 117.88436729209025], - [56.98825159578587, 117.88436729209025], - [47.899297112365275, 117.42990500582073], - [39.26476330921878, 115.61209746673853], - [31.084691792341573, 112.88540696111295], - [23.81352404500558, 108.34095052240014], - [17.451239264213086, 103.34199019142284], - [11.543416769689884, 95.16191867454586], - [6.544477241710297, 85.61850190485575], - [5.181131988897391, 80.61958317987376], - [2.908903769541098, 69.71282115737085], - [2, 58.351596848598774], - [2.908903769541098, 46.53595185955237], - [6.090035758438489, 36.083652123318984], - [11.543416769689884, 26.085773067359696], - [19.72348828656709, 17.4512392642132], - [32.90249933142388, 9.271167747335994], - [40.62810856203157, 6.090035758438489], - [56.07936862924237, 2.908903769541098], - [71.07616641018376, 2], - [84.25515665204284, 3.3633452528129055], - [95.16191867454575, 6.544477241710297], - [103.34199019142284, 11.08897528641819], - [113.79431073065393, 23.81354484800329], - [119.24769174190533, 42.44591610111388], - [118.79325025863363, 54.716023376429575], - [117.42990500582073, 60.16940438768097], - [112.88540696111284, 70.16728344364037], - [105.15979773050503, 80.16512089360435], - [94.70747719127394, 92.43522816892005], - [82.89181139923005, 103.79645247769236], - [57.44271388205516, 120.61105779771583], - [51.534870584534474, 122.88328601707224], - [36.083631320321274, 122.88328601707224], - [31.993595561882785, 121.06547847798993], - [26.085773067359582, 116.52102203927734], - [21.08683353937988, 110.61317874175654] - ], - "color": "--affine-palette-line-orange", - "lineWidth": 4, - "id": "I-pL2OzPot", - "index": "b5K", - "seed": 810018507 - }, - "J3cb6llzpc": { - "type": "brush", - "xywh": "[515.4988013714616,929.7910731686342,293.2151468941754,522.5049086204381]", - "points": [ - [283.6957560688417, 12.188028454264412], - [279.176365243508, 9.312803910925723], - [272.3972790055075, 7.15640195546292], - [267.1247023495722, 5.718822587876108], - [265.61819276750674, 5], - [264.11175214587246, 5], - [259.59236132053877, 5], - [253.56652987357097, 5], - [246.78744363557047, 6.437612271669515], - [238.50184781550428, 7.15640195546292], - [234.73571178120343, 8.594014227132206], - [227.20337075217012, 10.750416182595126], - [218.91784389253527, 12.906818138057933], - [210.63231703290054, 16.500832365190142], - [202.3467212128345, 20.81363627611575], - [192.55475373156554, 25.12644018704136], - [182.00946249883262, 30.876823465553787], - [176.7368858428976, 33.75204800889259], - [163.9318991974977, 40.94004355907441], - [150.37372672149652, 48.84686169713223], - [136.81555424549538, 57.47246951898346], - [124.76389135155966, 65.37925475295879], - [108.19276867185887, 77.59890970602471], - [90.86842568134091, 91.97490080638836], - [84.84255975415739, 98.44410667277688], - [73.54408269082306, 109.94493903796692], - [62.99886041852147, 121.44570559499175], - [54.71329907867098, 133.6653605480579], - [45.67451742800353, 145.8849496929586], - [38.14221087918591, 159.54218401361157], - [30.60986985015256, 173.9181751139752], - [23.077563301334905, 189.73181139009085], - [20.064647577850977, 197.6385966240662], - [14.038781650667453, 214.17098967989276], - [10.27261113615079, 230.7034485438843], - [7.259695412666865, 247.23584159971085], - [5, 264.4870572434133], - [5, 281.0194502992396], - [5.753220310817065, 298.98948853081816], - [8.01291572348393, 316.95952676239676], - [11.779086238000591, 335.64832177368606], - [14.038781650667453, 344.99275218341336], - [17.80495216518412, 362.2439678271158], - [24.5840384031847, 380.213940250529], - [32.86956526281942, 396.7463991145207], - [52.45360366600424, 425.49838131524797], - [64.50530104015563, 438.43679304802504], - [78.0634735161568, 450.65644800109095], - [84.84255975415739, 456.4068312796034], - [99.90724181222403, 467.90766364479344], - [116.47829553149347, 477.97085083423144], - [132.29616342016163, 485.8776360682066], - [148.11403130882965, 493.06566452247097], - [164.68515398853046, 499.5348703888597], - [180.50302187719848, 504.56649688766123], - [196.32088976586664, 508.8793007985869], - [204.60641662550134, 510.31688016617363], - [219.671098683568, 513.1921047095125], - [234.73571178120343, 514.6296840770992], - [247.54062946617177, 516.786086032562], - [260.3456161115716, 517.5049086204381], - [270.89083838387313, 517.5049086204381], - [277.66992462187386, 517.5049086204381], - [282.9425702382402, 517.5049086204381], - [287.461961063574, 516.0672634446862], - [288.2151468941754, 514.6296840770992], - [288.2151468941754, 513.9108614892232], - [288.2151468941754, 513.1921047095125], - [287.461961063574, 512.4732821216363], - [285.9554514815088, 511.0357027540495], - [284.44901085987453, 510.31688016617363], - [283.6957560688417, 508.8793007985869], - [277.66992462187386, 503.8476742997851], - [267.1247023495722, 495.2220664779339], - [255.07297049520514, 484.4400567006198], - [243.02123864083802, 472.93922433542997], - [232.47601636853645, 463.59485973386785], - [223.437234717869, 456.4068312796034], - [218.91784389253527, 451.37520478080165], - [217.41140327090113, 449.93762541321485], - [215.1517078582343, 448.50004604562804], - [215.1517078582343, 447.7812234577522] - ], - "color": "--affine-palette-line-orange", - "lineWidth": 10, - "id": "J3cb6llzpc", - "index": "b5J", - "seed": 2037177940 - }, - "MwVEs53XNx": { - "type": "brush", - "xywh": "[715.7479027121274,1449.5767181390754,92.39896116886877,56.68309298865049]", - "points": [ - [87.39896116886877, 5], - [81.35128654479502, 7.872824942557235], - [68.50009908546559, 12.900252153721512], - [57.16079567734659, 17.927613611642915], - [46.57746889964088, 23.67326349675739], - [35.99407291231963, 28.700624954678798], - [25.41074613461388, 34.44627483979327], - [7.267860681623764, 48.810268046093036], - [5, 51.68309298865049] - ], - "color": "--affine-palette-line-orange", - "lineWidth": 10, - "id": "MwVEs53XNx", - "index": "b5L", - "seed": 1423229383 - }, - "KfJharb6ec": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "KfJharb6ec", - "index": "b5t", - "seed": 2021747914, - "points": [ - [3.373134931502591, 105.44284547478344], - [2.9154512241698285, 102.23884999969312], - [2.4576837073323077, 99.95030574877296], - [2, 90.33836122825437], - [2.4576837073323077, 87.59209136524908], - [3.373134931502591, 82.55723534657102], - [3.373134931502591, 79.81096548356572], - [5.661721087175465, 70.19902096304725], - [6.57708850184008, 66.99506739270942], - [9.323358364845717, 60.12939273519618], - [12.069628227850899, 53.26371807768294], - [14.81589809085608, 46.398043420169756], - [17.562167953861717, 39.990052469988996], - [24.427842611374672, 27.631838086465223], - [28.089563698550137, 22.597023972539716], - [29.462698630052728, 20.766163428951984], - [32.20896849305791, 17.104484246529125], - [36.786056994898445, 11.611944520518534], - [38.159191926401036, 9.781083976930802], - [40.90546178940622, 7.034814113925506], - [42.736364237746784, 5.6616791824228585], - [43.651731652411854, 4.7462698630052955], - [44.56718287658168, 3.8308605435877325], - [45.024866583914445, 2.915409319417563], - [45.94031780808473, 2.4577256120850848], - [46.85576903225456, 2], - [47.31345273958732, 2], - [48.68658767108991, 2], - [51.890541241427854, 3.3731349315026478], - [53.26367617292999, 4.288544250920211], - [54.636811104433036, 4.7462698630052955], - [57.38308096743822, 6.119404794507943], - [60.58711834728092, 7.950265338095676], - [64.70652314178915, 10.238809589015887], - [70.19906286779951, 13.900488771438745], - [75.69160259381033, 18.01989356594669], - [81.1841423198207, 22.13929836045463], - [86.67668204583151, 26.716428767047603], - [90.79608684033929, 31.29355917364063], - [94.91549163484706, 34.95523835606349], - [98.119445205185, 38.61691753848635], - [99.95026384402036, 39.990052469988996], - [103.61198493119537, 43.65177355716446], - [106.81593850153331, 47.77117835167235], - [110.01997588137647, 51.43285753409526], - [113.68161315904672, 55.552262328603206], - [115.05474809054931, 57.38312287219088], - [119.17415288505708, 63.33334630553401], - [120.54728781656013, 64.70648123703666], - [123.29355767956531, 70.19902096304725], - [124.2090089037356, 72.02988150663492], - [125.58214383523773, 74.77615136964022], - [126.95527876674078, 76.60701191322795], - [128.32841369824337, 78.43783055206308], - [128.78609740557567, 79.81096548356572], - [129.2438649224132, 81.64182602715346], - [130.15923233707827, 83.0149609586561], - [130.61699985391624, 84.38809589015875], - [130.61699985391624, 85.30350520957631], - [130.61699985391624, 86.21895643374643], - [130.61699985391624, 86.67664014107896], - [130.61699985391624, 87.59209136524908], - [130.61699985391624, 88.04977507258161], - [130.61699985391624, 88.5075006846667], - [130.61699985391624, 88.96522629675172], - [130.61699985391624, 89.42291000408426], - [130.61699985391624, 89.88063561616934], - [130.61699985391624, 90.33836122825437], - [130.61699985391624, 91.25377054767199] - ], - "lineWidth": 4, - "xywh": "[2158.279924438907,-177.0711991923539,132.61699985391624,107.44284547478344]" - }, - "Jdt-Re4f6a": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "Jdt-Re4f6a", - "index": "b5u", - "seed": 1018565993, - "points": [ - [2, 13.417474526446057], - [2.475742623338192, 11.990290210640296], - [3.6650774045792787, 8.660189844242794], - [4.378669562482173, 7.470876840106143], - [5.092261720385068, 6.757284682203263], - [6.757295570755559, 4.616508208494622], - [7.946630351996646, 3.9029160505917417], - [9.849513736931385, 2.7135921579028803], - [11.752440676075366, 2], - [13.655367615219347, 2], - [14.606809307686945, 2], - [16.271843158057436, 2.4757317347859953], - [17.699027473863225, 3.1893238926888614], - [18.88836225510431, 3.9029160505917417], - [19.83980394757191, 5.092229054728406], - [21.029138728812995, 6.043692524300383], - [21.74273088671589, 6.281552947417268] - ], - "lineWidth": 4, - "xywh": "[2185.5188636787684,-122.3248987248711,23.74273088671589,15.417474526446057]" - }, - "QjCRV81glI": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "QjCRV81glI", - "index": "b5v", - "seed": 1513930659, - "points": [ - [2, 11.038826741068306], - [2.951441692467597, 7.9465976863399135], - [3.6650338503704916, 5.8058212126312725], - [4.616519097046876, 4.140776473708627], - [6.281552947417367, 2.9514525810197654], - [8.422329421125596, 2], - [10.56310589483428, 2], - [13.65532406101056, 2.9514525810197654], - [16.271843158057436, 4.616497319942411], - [17.461177939298523, 5.567960789514387], - [19.601954413007206, 7.708737263223028], - [21.266988263377698, 9.61164242526256], - [22.456323044618784, 11.276698052737416], - [23.16991520252168, 11.752418898971186] - ], - "lineWidth": 4, - "xywh": "[2225.955767144668,-127.55790425330814,25.16991520252168,13.752418898971186]" - }, - "3BjSH224j3": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "3BjSH224j3", - "index": "b5w", - "seed": 1523623020, - "points": [ - [2, 16.50970358117445], - [3.1892912270322995, 17.22329573907733], - [6.995145105320262, 18.888351366552186], - [8.42232942112605, 19.12621178966907], - [11.514547587302332, 18.888351366552186], - [14.606765753478157, 18.174759208649306], - [16.50969269262214, 17.69902747386331], - [19.83980394757191, 16.271843158057564], - [23.40776473708638, 14.844658842251803], - [26.737832437827365, 13.179614103329158], - [30.067943692777135, 11.514558475854301], - [31.49512800858247, 10.563105894834536], - [33.63590448229115, 8.89806115591189], - [34.82523926353224, 7.233005528437033], - [35.776680955999836, 5.567960789514387], - [36.25242357933803, 4.140776473708627], - [36.72812264846743, 3.1893238926888614], - [36.96601573724092, 2] - ], - "lineWidth": 4, - "xywh": "[2202.882968557211,-100.20354182988187,38.96601573724092,21.12621178966907]" - }, - "N0R7kGAEg6": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "N0R7kGAEg6", - "index": "b5x", - "seed": 1809486954, - "points": [ - [2, 14.368927107465808], - [4.378626008273386, 14.131066684348937], - [5.567960789514473, 14.131066684348937], - [10.325256360270032, 14.844658842251803], - [11.514547587302332, 15.082519265368703], - [13.17962499188161, 15.082519265368703], - [16.27184315805789, 15.558251000154698], - [20.07765348213661, 16.271843158057564], - [24.83494905289217, 16.98543531596043], - [27.451468149939046, 17.22329573907733], - [30.305836781550624, 17.46115616219423], - [36.014574044773326, 17.699027473863325], - [42.19901037712589, 17.699027473863325], - [48.62133979825194, 17.699027473863325], - [55.51941184271618, 17.22329573907733], - [62.65533342174467, 16.747564004291334], - [70.02910453533832, 16.033971846388468], - [73.35921579028809, 15.558251000154698], - [80.49513736931658, 15.082519265368703], - [86.91746679044263, 13.893195372679813], - [93.10194667700398, 12.941742791660047], - [99.04853347479138, 11.752418898971186], - [104.51942120344984, 10.80096631795142], - [109.27667321999661, 9.61164242526256], - [114.03396879075217, 8.660189844242794], - [115.9368957298957, 8.184458109456799], - [123.54851637805405, 5.567960789514387], - [127.59221979090671, 4.140776473708627], - [128.7815545721478, 3.6650447389226457], - [131.3980301149859, 2] - ], - "lineWidth": 4, - "xywh": "[2158.878104301797,-82.6015983054268,133.3980301149859,19.699027473863325]" - }, - "TR9ZVf5tjb": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "TR9ZVf5tjb", - "index": "b5y", - "seed": 107669226, - "points": [ - [2, 2], - [2, 4.786884310580888], - [2, 8.569055807558726], - [2, 10.161566334947139], - [2, 11.75405863763794], - [2, 12.749364048732502], - [2.1990501474006123, 15.735298506713747], - [2.3981367441961083, 17.924995925698397], - [2.3981367441961083, 18.522182817294663], - [2.3981367441961083, 19.1193697088909] - ], - "lineWidth": 4, - "xywh": "[2201.2302431050553,-67.09250707330088,5,21.1193697088909]" - }, - "_u_KQilo1c": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "_u_KQilo1c", - "index": "b5z", - "seed": 652216400, - "points": [ - [2.597186891596266, 2], - [2.3981367441961083, 2.9953054110945345], - [2.199086596795496, 3.791560674788741], - [2, 5.185002830079213], - [2, 6.578426760672045], - [2.199086596795496, 7.374682024366251], - [2.3981367441961083, 8.170937288060458], - [2.597186891596266, 10.758735001845793], - [2.9953236357919195, 12.948432420830443], - [3.393460379988028, 15.13811161511751] - ], - "lineWidth": 4, - "xywh": "[2209.789909734803,-63.31031735162543,5.393460379988028,17.13811161511751]" - }, - "_91RffGkrY": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "_91RffGkrY", - "index": "b60", - "seed": 927614511, - "points": [ - [12.15217715713652, 4.587815938482947], - [9.962479738151615, 4.985934457981244], - [7.971868915962659, 5.185002830079185], - [6.379358388574019, 5.583121349577482], - [5.782171496977753, 5.782189721675451], - [4.388747566385064, 6.180308241173748], - [3.5924740779933018, 6.777495132769985], - [2.7962370389968783, 7.374682024366251], - [2.1990501474006123, 7.971868915962489], - [2, 8.370005660158398], - [2, 8.569055807558726], - [2, 8.967192551754664], - [2.39810029480077, 9.365311071252933], - [3.5924740779933018, 9.962497962849199], - [5.9812216443783655, 10.758753226543405], - [8.768105954959083, 11.355940118139642], - [14.142787979325476, 11.355940118139642], - [18.124046073098725, 10.559684854445436], - [19.318419856291257, 10.161566334947139], - [21.508080825880825, 9.564379443350902], - [23.49872809746512, 8.768124179656695], - [25.09120217545842, 7.772818768562161], - [25.688389067054686, 7.175631876965895], - [26.882762850246763, 5.782189721675451], - [27.479949741843484, 4.587815938482947], - [27.67903633863898, 3.592510527388413], - [27.479949741843484, 2.995323635792147], - [26.882762850246763, 2.5971868915962375], - [25.688389067054686, 2.3981367441959094], - [24.095914989061384, 2], - [22.503404461672744, 2], - [21.309030678480212, 2] - ], - "lineWidth": 4, - "xywh": "[2193.2677633669036,-50.968461000202126,29.67903633863898,13.355940118139642]" - }, - "QcrVYrs_UL": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "QcrVYrs_UL", - "index": "b61", - "seed": 95764264, - "points": [ - [2.9953236357923743, 2], - [2, 4.189679194287066], - [2, 7.175613652268311], - [2.1990501474001576, 8.967174327057052], - [2.7962370389964235, 10.559666629747852], - [2.9953236357923743, 11.754040412940356], - [3.5925105273881854, 12.948414196132859], - [3.9906108221889554, 13.744669459827065] - ], - "lineWidth": 4, - "xywh": "[2237.6586434924266,-64.30562276271999,5.990610822188955,15.744669459827065]" - }, - "gMGCAteIGn": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "gMGCAteIGn", - "index": "b62", - "seed": 2075547794, - "points": [ - [2, 2], - [2, 3.393442155290444], - [2, 4.786884310580916], - [2, 12.550313901332174], - [2, 15.934366878811716], - [2, 16.531553770407953], - [2.597186891596266, 19.318419856291257], - [2.597186891596266, 20.114675119985463] - ], - "lineWidth": 4, - "xywh": "[2246.41739671897,-70.67562842287839,5,22.114675119985463]" - }, - "fYAjJEjpXJ": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "fYAjJEjpXJ", - "index": "b63", - "seed": 710908840, - "points": [ - [9.166242699155191, 7.175613652268282], - [8.170919063362817, 6.578426760672045], - [6.57844498536997, 6.180308241173748], - [5.384071202177438, 6.180308241173748], - [4.189697418984906, 6.578426760672045], - [3.7915606747887978, 6.777495132770014], - [3.393423930593144, 7.175613652268282], - [2.9953236357923743, 7.772800543864548], - [2.1990501474006123, 8.967174327057052], - [2, 9.763429590751258], - [2, 10.559666629747852], - [2.597186891596266, 11.355921893442058], - [3.59251052738864, 12.152177157136265], - [5.981258093773704, 12.749364048732502], - [7.374682024366393, 13.1474825682308], - [10.360616482347723, 13.34655094032874], - [13.346550940329053, 13.1474825682308], - [16.13343525090977, 12.749364048732502], - [18.522182817294834, 11.554990265539999], - [20.711843786883946, 9.763429590751258], - [22.702491058468695, 7.971868915962489], - [24.095914989061384, 6.578426760672045], - [24.69310188065765, 5.981239869075807], - [25.489338919654074, 4.985934457981244], - [25.887475663850182, 3.9906108221890975], - [25.68842551644957, 2.9953054110945345], - [24.294965136461542, 2.199050147400328], - [22.10530416687243, 2], - [19.51747000369187, 2.199050147400328], - [16.13343525090977, 2.796237038996594] - ], - "lineWidth": 4, - "xywh": "[2232.8811483596564,-54.15344560558373,27.887475663850182,15.34655094032874]" - }, - "L6-cOeoDup": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "L6-cOeoDup", - "index": "b64", - "seed": 1932186181, - "points": [ - [28.07715485813742, 24.29497424881039], - [25.290288772253916, 22.7024728337708], - [22.901541205868853, 21.309039790829146], - [20.7118437868844, 19.71653837578954], - [18.323096220499338, 17.924977701000785], - [15.934348654114274, 15.735298506713747], - [14.739974870921742, 14.540924723521243], - [12.351227304536678, 12.3512364168854], - [9.763429590751457, 9.76342959075123], - [7.573732171767006, 7.5737412841153855], - [5.782171496978208, 5.782180609326645], - [4.388747566385064, 4.587806826134141], - [3.194373783192532, 3.3934330429416377], - [2.398118519498439, 2.597186891596266], - [2, 2] - ], - "lineWidth": 4, - "xywh": "[2142.7059277286226,-146.91648217510044,30.07715485813742,26.29497424881039]" - }, - "OrjsNdVQtR": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "OrjsNdVQtR", - "index": "b65", - "seed": 1983895390, - "points": [ - [25.09122040015609, 31.8593445798125], - [23.896846616963558, 30.864030056369145], - [23.10060957796668, 30.067783905023745], - [20.51279363948379, 27.479977078889604], - [17.9249777010009, 23.896855729312108], - [16.53155377040821, 21.906226682425398], - [14.939043243019569, 20.114666007636643], - [13.346550940329053, 18.323105332847888], - [11.95310878503824, 16.92967228990625], - [10.55968485444555, 15.536239246964612], - [9.763429590751457, 14.739983983270406], - [8.369987435461098, 13.346550940328768], - [6.976563504867954, 11.754049525289133], - [5.782189721675422, 10.360616482347496], - [4.786866085883503, 9.166242699154992], - [3.9906290468866246, 8.369996547809592], - [3.3934421552908134, 7.772809656213354], - [2.995305411094705, 7.175622764617117], - [2.7962552636945475, 6.976554392519148], - [2.398118519498439, 6.3793675009229105], - [2.1990683720982815, 5.782180609326645], - [2, 5.184993717730379], - [2, 4.587806826134141], - [2, 3.9906199345379036], - [2.1990683720982815, 3.1943737831925034], - [2.398118519498439, 2.398127631847103], - [2.597186891596266, 2] - ], - "lineWidth": 4, - "xywh": "[2140.715298681736,-147.31460980694754,27.09122040015609,33.8593445798125]" - }, - "7dFARP6mfP": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "7dFARP6mfP", - "index": "b66", - "seed": 1602905652, - "points": [ - [9.664892588335078, 18.6072770211411], - [8.068049712180255, 16.691053874057303], - [3.916223147083656, 10.942384432805937], - [2.958111573541828, 9.664892588335135], - [2.958111573541828, 8.70678101479325], - [2, 7.10993813863837], - [2, 6.1518265650964565], - [2, 5.832446294167568], - [2, 5.193714991554572], - [2, 4.235603418012687], - [2, 3.9162231470837696], - [2, 3.2774918444707737], - [2.638731302612996, 2.958111573541885], - [2.958111573541828, 2.638745922235387], - [3.27749184447066, 2.638745922235387], - [3.596842876154824, 2.638745922235387], - [4.554954449696652, 3.2774918444707737], - [5.51306602323848, 3.5968574957772717], - [5.832446294167312, 4.235603418012687], - [6.471177596780308, 4.554969069319185], - [7.109938138638427, 5.193714991554572], - [8.068049712180255, 6.7905578677094525], - [9.026161285722083, 8.70678101479325], - [9.345512317406246, 9.984272859264053], - [9.98427285926391, 11.90049600634785], - [10.303623890948074, 13.177973231196233], - [10.623004161876906, 13.81671915343162], - [10.623004161876906, 14.136084804738118], - [10.623004161876906, 13.497338882502731], - [10.623004161876906, 13.177973231196233], - [10.942384432805738, 12.539227308960847], - [12.85860757988985, 9.345526937028637], - [13.816719153431677, 7.429303789944868], - [15.413562029586501, 4.874334720625683], - [17.329785176670157, 2.958111573541885], - [18.287896750211985, 2.319380270928889], - [18.607277021140817, 2], - [18.92662805282498, 2], - [19.246008323753813, 2], - [19.246008323753813, 2.319380270928889], - [19.246008323753813, 3.2774918444707737], - [19.246008323753813, 4.554969069319185], - [19.246008323753813, 6.4711922164029545], - [18.92662805282498, 8.70678101479325], - [18.607277021140817, 9.984272859264053], - [17.968516479283153, 12.219861657654349], - [17.329785176670157, 14.136084804738118], - [16.69105387405716, 15.732942300515418], - [16.37167360312833, 16.371673603128414], - [16.052293332199497, 16.691053874057303], - [16.052293332199497, 17.3297851766703], - [16.052293332199497, 17.649165447599216] - ], - "lineWidth": 4, - "xywh": "[2130.6418292704334,-178.8994990993742,21.246008323753813,20.6072770211411]" - }, - "WKyXlguxe9": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "WKyXlguxe9", - "index": "b67", - "seed": 430279161, - "points": [ - [2.638731302612996, 2], - [2.319380270928832, 3.2774772248483828], - [2, 5.193700371932181], - [2.319380270928832, 6.151811945474066], - [3.27749184447066, 7.109923519015979], - [4.235603418012488, 8.068035092557864], - [5.193714991554771, 8.70678101479325], - [5.193714991554771, 9.345526937028666], - [5.51306602323848, 9.664892588335164], - [5.832446294167312, 10.30363851057055], - [6.151826565096599, 10.942369813183547], - [6.151826565096599, 11.900481386725431], - [6.151826565096599, 12.858592960267345], - [6.151826565096599, 13.497338882502731], - [6.151826565096599, 13.81670453380923], - [6.151826565096599, 14.136084804738118] - ], - "lineWidth": 4, - "xywh": "[2138.3067218587685,-155.26607541213332,8.151826565096599,16.13608480473812]" - }, - "Wgx8ea7h4Q": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "Wgx8ea7h4Q", - "index": "b68", - "seed": 130303619, - "points": [ - [9.664892588335533, 2], - [6.790557867709595, 2.638731302612996], - [5.1936857523101025, 2.9581115735419132], - [3.9162231470841107, 3.916223147083798], - [2.958111573541828, 4.874334720625683], - [2.319351031684164, 5.513066023238679], - [2, 7.109923519015979], - [2.319351031684164, 9.026146666099777], - [3.277462605225992, 10.942369813183575], - [3.9162231470841107, 11.90048138672546], - [4.554954449697107, 13.177958611573843], - [5.832446294167767, 15.09418175865764], - [7.4292891703225905, 16.052293332199525], - [9.345512317406246, 17.01040490574144] - ], - "lineWidth": 4, - "xywh": "[2133.8355442619877,-159.4178873576074,11.664892588335533,19.01040490574144]" - }, - "CFlRl6nmiJ": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "CFlRl6nmiJ", - "index": "b69", - "seed": 323835930, - "points": [ - [4.554954449696652, 2], - [5.832446294167767, 3.5968428761548807], - [7.1099088993937585, 5.513066023238679], - [8.068020472935586, 7.109923519015979], - [8.068020472935586, 8.70678101479325], - [6.151797325851476, 15.09418175865764], - [4.554954449696652, 17.649150827976825], - [3.596842876154824, 18.60726240151871], - [2, 20.20411989729601] - ], - "lineWidth": 4, - "xywh": "[2145.3328831444906,-163.25033365177498,10.068020472935586,22.20411989729601]" - }, - "dpF_i3KqRO": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "dpF_i3KqRO", - "index": "b6A", - "seed": 1145658273, - "points": [ - [2, 10.623004161877049], - [4.23557417876782, 8.70678101479325], - [9.026132046477414, 4.554969069319185], - [12.539227308960562, 3.2774918444707737], - [14.13609942436051, 2.638745922235387], - [17.329785176670157, 2.319380270928889], - [20.84288043915376, 2], - [23.717215159779244, 2], - [25.952789338547063, 2.638745922235387], - [26.910900912089346, 2.638745922235387], - [28.188363517315338, 3.5968574957772717], - [29.146475090857166, 4.874334720625683], - [30.104586664398994, 7.10993813863837], - [28.827124059173002, 16.052307951821916], - [24.67532673332107, 22.12034304437978], - [20.84288043915376, 25.952789338547376], - [18.926657292070104, 27.549646834324676], - [18.287896750211985, 28.188392756560063], - [17.329785176670157, 28.82712405917306], - [17.010434144985993, 28.82712405917306] - ], - "lineWidth": 4, - "xywh": "[2266.0549414107695,-139.6169245841565,32.104586664398994,30.82712405917306]" - }, - "JssFckYvF2": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "JssFckYvF2", - "index": "b6B", - "seed": 1985502786, - "points": [ - [2, 7.10992351901595], - [4.874334720625484, 5.51308064286107], - [7.429318409567259, 4.235588798390268], - [9.664892588335078, 3.2774772248483828], - [11.261764703735025, 2.319365651306498], - [12.539227308961017, 2], - [13.497338882502845, 2], - [14.13609942436051, 2], - [14.455450456044673, 2], - [15.094210997902337, 2], - [15.413562029586501, 2.319365651306498], - [15.732913061270665, 3.2774772248483828], - [15.732913061270665, 4.874334720625683], - [15.094210997902337, 7.10992351901595], - [14.13609942436051, 10.30363851057055], - [13.177987850818681, 12.858592960267345], - [12.539227308961017, 13.81670453380923], - [11.261764703735025, 15.41356202958653], - [10.623004161876906, 16.691039254434912], - [9.664892588335078, 17.329785176670327], - [9.345541556650915, 17.649150827976825], - [8.70678101479325, 18.287896750212212], - [8.70678101479325, 18.60726240151871], - [8.70678101479325, 18.9266426724476] - ], - "lineWidth": 4, - "xywh": "[2273.7198339991046,-132.27139764712783,17.732913061270665,20.9266426724476]" - }, - "FFlu1Fkqcg": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "FFlu1Fkqcg", - "index": "b6C", - "seed": 1613829028, - "points": [ - [2, 289.15284038424835], - [5.891007273199648, 289.15284038424835], - [9.392871071718446, 287.98555245140875], - [12.894734870237471, 286.429156666689], - [16.39659866875627, 284.48368865288984], - [17.952958830675243, 283.31640072005024], - [19.898462467275067, 281.7600049353305], - [24.17847072395284, 278.2581411368116], - [28.458550226231864, 274.75627733829276], - [33.12770195759049, 270.47623345881436], - [35.85135005234906, 268.1416575931351], - [40.90964525838831, 262.69432578081705], - [48.69151731358511, 252.18873438526043], - [50.63702095018493, 248.2977627348614], - [55.30617268154333, 240.12674720498404], - [58.418964250982754, 232.34483952698673], - [60.75354011666218, 224.17382399710937], - [61.92082804950155, 220.2828523467104], - [63.47718821142075, 211.3336567358735], - [65.03354837333995, 202.3844255022359], - [66.97905200993978, 193.04612203951888], - [68.53541217185852, 183.3187285363221], - [69.70270010469835, 173.59133503312532], - [70.48091580845835, 163.47483367804844], - [70.48091580845835, 153.35833232297156], - [70.48091580845835, 133.51443746469792], - [70.48091580845835, 114.05965045830436], - [70.09184357937897, 109.39049872694585], - [69.31362787561898, 101.21948319706846], - [68.14633994277915, 92.65935981531098], - [66.20083630617933, 85.26654217779344], - [65.81176407709995, 81.76467837927456], - [64.25540391518098, 74.37186074175708], - [62.69897250766053, 66.58993525235945], - [61.92082804950155, 59.97529769580143], - [61.142612345741554, 53.74976799112352], - [60.75354011666218, 48.30241836740515], - [59.97532441290173, 44.022374487926754], - [59.58625218382235, 40.90960072988764], - [58.808036480062356, 38.18593482372859], - [58.418964250982754, 37.01864689088899], - [58.418964250982754, 33.90587313284988], - [58.418964250982754, 31.57129726717062], - [58.418964250982754, 29.236721401491366], - [58.418964250982754, 27.291235576291854], - [58.418964250982754, 25.734857602972454], - [58.418964250982754, 24.567569670132855], - [58.808036480062356, 23.400281737293255], - [59.19710870914173, 23.0111827911133], - [59.58625218382235, 21.8438948582737], - [60.75354011666218, 19.898417938774344], - [61.142612345741554, 18.342031059754788], - [61.142612345741554, 17.17474312691519], - [61.142612345741554, 16.39655414025549], - [61.142612345741554, 16.007455194075533], - [61.142612345741554, 15.229266207415833], - [61.92082804950155, 16.39655414025549], - [62.30990027858115, 17.56384207309509], - [62.69897250766053, 19.509318992594388], - [63.08811598234138, 20.676606925434044], - [64.25540391518098, 23.789371777773], - [66.20083630617933, 27.68033452247181], - [68.9245556465396, 33.12767524049002], - [71.64820374129818, 38.18593482372859], - [73.20456390321715, 42.46597870320704], - [74.37185183605675, 45.9678425017259], - [75.15006753981697, 49.08061625976501], - [75.53913976889658, 50.24790419260461], - [75.9282832435772, 51.80428216592401], - [75.9282832435772, 52.58248005828392], - [75.9282832435772, 52.971570098763664], - [75.9282832435772, 52.58248005828392], - [76.31735547265657, 51.415192125444264], - [77.87371563457577, 47.1351304345655], - [80.20829150025497, 43.63326663604664], - [83.71015529877377, 39.35322275656824], - [89.15752273389239, 33.51677418666992], - [96.16125033093044, 27.291235576291854], - [103.16497792796804, 21.8438948582737], - [110.55777775408524, 16.39655414025549], - [117.17243312204369, 12.116492449376722], - [120.67429692056248, 10.171015529877423], - [126.89980881383985, 7.447340718018211], - [132.3471762489587, 5.501863798518912], - [135.45996781839835, 3.945476919499356], - [137.40540020939693, 2.7781889866597], - [138.18361591315693, 2], - [138.5726881422363, 2], - [138.18361591315693, 2.7781889866597], - [138.18361591315693, 3.167287932839656], - [137.0163279803171, 4.723674811859212], - [134.6817521146379, 7.447340718018211], - [128.06709674667968, 13.283780382216321], - [112.50328139068506, 28.069433468651766], - [102.38676222420781, 39.35322275656824], - [95.77217810185084, 48.691526219285265], - [93.82667446525102, 51.415192125444264], - [91.49209859957182, 56.86254174916263], - [89.93573843765284, 61.92078352100094], - [89.15752273389239, 67.36813314471925], - [87.99023480105302, 72.8154649570373], - [87.21201909729257, 78.65190462123545], - [86.8229468682132, 84.09925424495381], - [86.43387463913382, 87.21202800299295], - [86.0447311644532, 96.93942150618972], - [86.0447311644532, 100.4412853047086], - [86.0447311644532, 109.00139087506574], - [86.0447311644532, 116.78331636446336], - [86.0447311644532, 124.56524185386101], - [86.8229468682132, 131.95805949137852], - [87.21201909729257, 140.12907502125591], - [87.60116257197365, 144.02002886025454], - [88.3793070301324, 152.58015224201205], - [89.546594962972, 160.7511677718894], - [90.7138828958116, 169.70036338272632], - [91.8811708286512, 178.6495768049636], - [92.65938653241142, 187.59879022720082], - [93.82667446525102, 195.38069790519813], - [94.99396239809062, 202.3844255022359], - [95.77217810185084, 205.10810921979527], - [97.32853826376981, 210.55544103211327], - [101.60861776604906, 220.6719601985905], - [104.33226586080764, 228.84297572846788], - [107.05591395556644, 235.45759547362553], - [107.83412965932644, 237.40306348742467], - [110.55777775408524, 242.8504309225434], - [111.33599345784546, 244.79589893634255], - [113.28149709444529, 248.2977627348614], - [114.83785725636449, 251.02144645242078], - [117.17243312204369, 253.74509454717946], - [119.50700898772288, 257.2469583456983], - [122.6198005571623, 260.74882214421723], - [125.34344865192111, 263.8616137136567], - [128.06709674667968, 266.58526180841534], - [129.62352815420013, 268.1416575931351], - [132.3471762489587, 270.47623345881436], - [134.6817521146379, 273.1999171763737], - [137.40540020939693, 275.92356527113236], - [140.51819177883613, 278.2581411368116], - [143.2418398735947, 279.8145369215313], - [145.96555921395498, 281.7600049353305], - [149.07827953779292, 283.31640072005024], - [150.2455674706323, 284.0945808010097], - [152.96928681099257, 286.0400844376096], - [158.02751077143034, 289.54194823612846], - [160.36208663711, 291.4874162499276], - [163.08573473186857, 293.43288426372675], - [163.4748782065492, 293.43288426372675], - [164.64216613938902, 294.211099967487], - [165.42031059754777, 294.6001721965664], - [165.8094540722284, 294.6001721965664], - [166.19852630130777, 294.6001721965664] - ], - "lineWidth": 4, - "xywh": "[1916.7523462949223,-332.841730886717,168.19852630130777,296.6001721965664]" - }, - "VOBcxx9aN5": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "VOBcxx9aN5", - "index": "b6D", - "seed": 536825619, - "points": [ - [14.84016726123582, 2], - [10.94923123363742, 2], - [9.78194330079782, 2], - [5.890936027598173, 3.5563957847197116], - [4.334575865679199, 5.501863798518855], - [2.3890722290793747, 9.781907677997253], - [2, 14.840167261235877], - [2.3890722290793747, 16.396563045955588], - [2.7782157037599973, 18.731138911634844], - [3.5563601619187466, 21.0657147773141], - [4.723648094758573, 23.0111827911133], - [6.280079502279023, 25.73486650867261], - [8.225511893277371, 28.458514603431297], - [9.78194330079782, 30.40401824003112], - [11.338303462716794, 31.571306172870777], - [13.672879328395993, 32.738594105710376], - [14.451095032156445, 32.738594105710376], - [16.396527423154794, 33.51677418666992], - [18.731103288833992, 33.90588203855003], - [21.06567915451342, 33.90588203855003], - [23.78939849487324, 33.90588203855003], - [26.123974360552438, 33.90588203855003], - [28.069406751551014, 33.90588203855003], - [28.84762245531124, 33.90588203855003], - [29.236694684390613, 33.12766633478981], - [29.625838159071463, 32.738594105710376] - ], - "lineWidth": 4, - "xywh": "[2000.408005230296,-114.55887854000662,31.625838159071463,35.90588203855003]" - }, - "XGInnztLrh": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "XGInnztLrh", - "index": "b6E", - "seed": 351197654, - "points": [ - [10.560159004558045, 2.3891078518801123], - [8.614655367958221, 2], - [5.891007273199648, 2], - [3.945503636599824, 2.778215703760168], - [3.5564314075204493, 2.778215703760168], - [2.7782157037599973, 3.5563957847197116], - [2, 4.3345758656792555], - [2, 6.280079502279079], - [2.3891434746806226, 7.447367435118679], - [2.7782157037599973, 8.225547516078223], - [3.5564314075204493, 10.560123381757478], - [4.334575865679199, 12.11651916647719], - [5.501863798518798, 13.672879328396277], - [7.058295206039247, 14.840167261235877], - [7.8364396641979965, 15.229275113115989], - [10.171015529877423, 16.007455194075533], - [12.505591395556621, 16.007455194075533], - [14.84016726123582, 14.840167261235877], - [15.618382964996044, 14.061987180276333] - ], - "lineWidth": 4, - "xywh": "[2011.302668854932,-103.27510706349045,17.618382964996044,18.007455194075533]" - }, - "QB8EDW6m5k": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "QB8EDW6m5k", - "index": "b6F", - "seed": 228116749, - "points": [ - [2, 2], - [5.5018637985185705, 2], - [7.4473674351183945, 2], - [9.78194330079782, 2], - [11.338303462716794, 3.1672879328396277], - [12.116519166477019, 4.3345758656792555], - [13.283807099316618, 6.669151731358511], - [13.672879328395993, 7.447331812318026], - [14.062022803076843, 8.614619745157654], - [14.84016726123582, 10.94919561083691], - [15.229310735916442, 12.116483543676537], - [16.39659866875627, 14.451059409355793], - [17.56388660159564, 17.563833167394904], - [18.342031059754618, 20.676606925434044], - [18.731174534435468, 23.789362872072815], - [18.342031059754618, 26.12393873775207], - [17.56388660159564, 28.069424562951554], - [17.17474312691502, 29.23671249579118], - [16.785670897835644, 30.014910388151037], - [15.618382964995817, 31.182198320990665], - [14.451095032156445, 31.571288361470437], - [12.894734870237244, 32.738576294310064] - ], - "lineWidth": 4, - "xywh": "[1979.7858946682622,-231.2876718239693,20.731174534435468,34.738576294310064]" - }, - "B-E2-FhYd3": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "B-E2-FhYd3", - "index": "b6G", - "seed": 1031142728, - "points": [ - [9.39279982611697, 2], - [9.781872055196345, 4.3345758656792555], - [9.39279982611697, 10.171015529877394], - [8.225511893277371, 12.894681436036421], - [5.890936027598173, 17.17474312691516], - [5.112720323837948, 18.342031059754788], - [3.5563601619187466, 20.28751688495427], - [2.7781444581587493, 20.676606925434044], - [2.3890722290793747, 21.065696965913787], - [2, 21.065696965913787] - ], - "lineWidth": 4, - "xywh": "[1983.676901941462,-215.7238386565744,11.781872055196345,23.065696965913787]" - }, - "HRcBCFjK3b": { - "type": "brush", - "color": "--affine-palette-line-black", - "id": "HRcBCFjK3b", - "index": "b6H", - "seed": 1452820428, - "points": [ - [2, 2], - [8.614655367958221, 9.781925489397622], - [10.560159004558045, 14.06196936887605], - [8.614655367958221, 20.28751688495427] - ], - "lineWidth": 4, - "xywh": "[1982.1204705339414,-216.89112658941403,12.560159004558045,22.28751688495427]" - }, - "kZdlb3P-Uu": { - "type": "brush", - "color": "--affine-palette-line-green", - "id": "kZdlb3P-Uu", - "index": "b6I", - "seed": 955774118, - "points": [ - [31.960342779149414, 28.458532414831666], - [30.014910388151065, 30.014910388151037], - [23.78932724927199, 34.294972079029804], - [14.061951557475595, 37.407745837068916], - [9.781872055196573, 37.407745837068916], - [8.225511893277599, 37.0186379851888], - [5.8909360275984, 33.90588203855003], - [5.501863798518798, 30.40401824003115], - [5.8909360275984, 24.178470723952927], - [10.949159988036172, 14.451077220756133], - [17.563815355994393, 11.338303462717022], - [17.563815355994393, 11.727411314597134], - [17.563815355994393, 12.116501355076878], - [16.785599652234396, 12.50559139555665], - [14.451023786555197, 14.06198718027639], - [12.894663624635996, 14.840167261235905], - [9.003727597037596, 16.396563045955645], - [5.8909360275984, 16.78565308643539], - [3.556360161918974, 16.007455194075533], - [2, 12.116501355076878], - [3.1672879328395993, 2] - ], - "lineWidth": 4, - "xywh": "[1929.981657030839,-193.15627789214173,33.960342779149414,39.407745837068916]" - }, - "-r2j5siu92": { - "type": "brush", - "color": "--affine-palette-line-green", - "id": "-r2j5siu92", - "index": "b6J", - "seed": 1902444094, - "points": [ - [48.302445084505734, 60.75350449386144], - [42.07686194562689, 59.97530660150156], - [33.905846415749465, 58.029820776302074], - [29.62583815907169, 56.473442802982674], - [24.956686427713066, 54.52795697778319], - [16.39652742315502, 51.02609317926431], - [10.171015529877423, 46.74604929978591], - [7.836439664198224, 44.800563474586426], - [3.9454680137992, 39.74232170274814], - [2, 32.738594105710376], - [2.7781800809596007, 24.56757857583301], - [6.669151731358625, 14.840167261235877], - [19.120211140714446, 2] - ], - "lineWidth": 4, - "xywh": "[1884.8464998791728,-237.51321934004753,50.302445084505734,62.75350449386144]" - }, - "WmV93wXgmk": { - "type": "brush", - "color": "--affine-palette-line-green", - "id": "WmV93wXgmk", - "index": "b6K", - "seed": 1732289373, - "points": [ - [49.85880524642471, 148.68918059161302], - [46.74604929978568, 148.30009055113325], - [36.24045790422906, 147.13280261829362], - [28.458550226231864, 146.35460472593377], - [20.676606925434044, 143.241848779295], - [10.560123381757421, 135.84901333037712], - [4.723683717559197, 126.51070986766013], - [2, 116.78331636446333], - [3.167287932839372, 106.66681500938648], - [7.447367435118622, 96.55033146570992], - [13.283807099316618, 89.54660386867215], - [36.62956575610929, 75.53914867459662], - [40.909609635587685, 74.76095078223676], - [41.687789716547286, 74.76095078223676], - [42.076897568427285, 74.76095078223676], - [39.742321702748086, 75.92823871507642], - [32.73859410571026, 77.87372454027593], - [29.236730307191465, 78.26281458075567], - [23.40029064299324, 77.09552664791602], - [19.120246763514842, 73.9827528898769], - [14.84016726123582, 66.5899352523594], - [12.89469924743662, 56.86253284346242], - [13.283807099316618, 46.3569414479058], - [16.00745519407542, 35.851350052349176], - [19.509318992594217, 26.513046589632154], - [21.843894858273416, 22.2329938044536], - [35.07316997138946, 8.61462865085781], - [50.637020950184706, 2], - [53.749776896823505, 2], - [60.36439664198133, 5.112764852338955], - [63.08808035954053, 7.83643966419811], - [64.25536829238013, 9.781916583697466], - [65.81176407709995, 12.505591395556621], - [65.81176407709995, 14.451068315055977], - [65.81176407709995, 15.618356247895576], - [65.42265622521973, 16.396554140255432], - [64.64447614426035, 16.396554140255432], - [64.25536829238013, 16.007455194075533], - [63.86626044050013, 15.229266207415833] - ], - "lineWidth": 4, - "xywh": "[1836.2094789289881,-365.52579300622654,67.81176407709995,150.68918059161302]" - }, - "lfqCdODkbN": { - "type": "brush", - "color": "--affine-palette-line-green", - "id": "lfqCdODkbN", - "index": "b6L", - "seed": 540741484, - "points": [ - [2, 46.356941447905854], - [3.5563957847198253, 43.2441765955669], - [10.171015529877423, 34.684062119509576], - [14.451095032156445, 30.014910388151065], - [18.731174534435695, 24.956668616312754], - [26.513046589632268, 16.78565308643539], - [29.62583815907169, 13.672879328396277], - [33.51677418667009, 10.171015529877366], - [34.68406211950969, 9.003727597037766], - [34.68406211950969, 9.392826543217723], - [31.182198320990665, 14.840167261235877], - [26.513046589632268, 20.2875168849543], - [23.011182791113242, 22.6220927506335], - [21.065750400114894, 23.4002817372932], - [21.065750400114894, 23.0111827911133], - [22.233038332954493, 20.2875168849543], - [30.79312609191129, 12.1164969022268], - [41.687789716547286, 4.723674811859212], - [46.35694144790591, 2] - ], - "lineWidth": 4, - "xywh": "[1893.4065876381296,-388.0933626763594,48.35694144790591,48.356941447905854]" - }, - "R6A6jSkx2s": { - "type": "brush", - "color": "--affine-palette-line-green", - "id": "R6A6jSkx2s", - "index": "b6M", - "seed": 1939520361, - "points": [ - [8.614655367958449, 20.287516884954243], - [4.723719340360049, 19.509318992594388], - [3.1672879328395993, 19.120228952114587], - [2, 17.952941019274988], - [2, 16.78565308643539], - [4.334575865679426, 12.116496902226743], - [7.8364396641979965, 9.781921036547544], - [17.56388660159587, 5.112769305189033], - [23.011182791113242, 3.945481372349377], - [34.68406211950946, 2], - [47.52422938074551, 2], - [59.58625218382235, 3.1672879328395993], - [65.42269184802058, 3.945481372349377], - [77.09557117641702, 5.890962744698811], - [87.60116257197365, 8.225538610378067], - [96.16125033093044, 12.505591395556621], - [101.60861776604929, 17.56384207309503], - [105.11048156456786, 24.1784707239529], - [103.94319363172849, 30.404009334330965], - [100.05218635852839, 33.90587313284982], - [94.99396239809062, 36.240448998529075], - [93.04845876149079, 36.62954794470903], - [91.88117082865142, 36.62954794470903] - ], - "lineWidth": 4, - "xywh": "[1977.4513188025828,-389.260650609199,107.11048156456786,38.62954794470903]" - }, - "UkLPIhvvCA": { - "type": "brush", - "color": "--affine-palette-line-green", - "id": "UkLPIhvvCA", - "index": "b6N", - "seed": 1337365816, - "points": [ - [2, 7.83643966419811], - [10.171015529877423, 3.945481372349434], - [32.34948625383049, 2], - [40.90964525838854, 3.5563824261694776], - [44.022365582226485, 5.501863798518912], - [45.578796989746934, 7.447345170868289], - [46.74608492258676, 9.781921036547544], - [49.85880524642471, 17.174747579765267], - [52.19338111210391, 26.902141082962032], - [53.749812519624356, 36.240444545679], - [53.360669044943734, 44.41146007555642], - [52.58252458678453, 47.913323874075274], - [49.85880524642471, 54.13886248445334], - [45.18965351506586, 59.97530214865151], - [41.29871748746791, 64.64445388001002], - [37.796853688948886, 68.92450666518857], - [35.85135005234906, 72.03728042322768], - [35.46227782326969, 73.59365839654708], - [35.46227782326969, 74.37185628890694], - [37.796853688948886, 76.31733320840624], - [40.52050178370746, 77.4846211412459], - [46.74608492258676, 81.76467392642445], - [52.58252458678453, 87.99022144250273], - [56.862532843462304, 96.55032701285984], - [58.02982077630213, 103.94314465037735], - [56.47346061438293, 110.16869216645563], - [53.360669044943734, 116.00513183065374], - [49.85880524642471, 121.06337360249199], - [47.91337285542613, 123.3979494681713], - [44.41150905690711, 127.2889033071699], - [41.687789716547286, 130.01258702472927], - [40.131429554628085, 131.56896499804867], - [38.96414162178871, 131.9580550385284], - [38.96414162178871, 132.34716289040853] - ], - "lineWidth": 4, - "xywh": "[2080.17265689247,-384.98059337117036,60.02982077630213,134.34716289040853]" - }, - "aOE0sH3dnH": { - "type": "brush", - "color": "--affine-palette-line-green", - "id": "aOE0sH3dnH", - "index": "b6O", - "seed": 1219029993, - "points": [ - [102.77590569888866, 2], - [107.44505743024706, 7.83643966419811], - [111.33599345784546, 15.229257301715649], - [113.28149709444529, 19.509318992594388], - [115.22692948544386, 28.458532414831637], - [110.94692122876609, 45.18965351506617], - [103.16497792796804, 56.862532843462446], - [94.21574669433062, 66.58992634665921], - [83.32108306969462, 73.98276179557709], - [73.20456390321738, 77.48462559409597], - [65.42269184802058, 77.09551774221586], - [59.58625218382235, 75.53913976889646], - [56.08438838530333, 73.2045639032172], - [54.52795697778333, 72.03727597037758], - [53.749812519624584, 71.25907807801772], - [53.360669044943506, 70.0917901451781], - [53.360669044943506, 69.70270010469832], - [53.360669044943506, 68.92450221233847], - [54.138884748703504, 68.92450221233847], - [54.52795697778333, 69.31361006421858], - [55.30617268154333, 70.86998803753795], - [55.30617268154333, 74.7609418765366], - [54.52795697778333, 76.70642770173609], - [51.02609317926431, 82.54286736593423], - [49.08066078826573, 84.09924533925363], - [43.24422112406796, 87.21201909729274], - [36.62956575610929, 89.93568500345177], - [29.62583815907169, 90.71388289581162], - [22.233038332954266, 90.32479285533185], - [15.618382964996044, 88.37930703013237], - [7.8364396641979965, 85.26653327209326], - [4.334575865679199, 81.76466947357437], - [2.7782157037602246, 79.81920145977523], - [2, 77.48462559409597] - ], - "lineWidth": 4, - "xywh": "[2014.804532653451,-255.4116283731217,117.22692948544386,92.71388289581162]" - }, - "BFegxcdohb": { - "type": "brush", - "xywh": "[1928.9255185125135,-404.9079588939192,85.6320490819935,42.74063399938581]", - "points": [ - [2, 40.74063399938581], - [4.075390541610886, 33.82264441538513], - [8.91798958400068, 24.137509666498545], - [17.911306373719526, 15.144129540886922], - [26.904686499331092, 8.226171624832716], - [35.89806662494266, 3.3836042503894532], - [45.58320137382907, 2], - [54.576581499440636, 4.075390541610886], - [68.41249733154928, 11.685166416833056], - [71.87146045560303, 15.144129540886922], - [79.48126799877173, 22.75390541610909], - [82.94023112282548, 31.05546758255275], - [83.6320490819935, 31.05546758255275] - ], - "color": "--affine-palette-line-green", - "lineWidth": 4, - "id": "BFegxcdohb", - "index": "b6P", - "seed": 1065697393 - }, - "MWy51BkPqD": { - "type": "brush", - "xywh": "[1977.8102820527863,-388.5376577281996,149.742984247099,41.13085812416364]", - "points": [ - [5, 18.144161208833452], - [9.842599042389793, 15.376952708054546], - [22.986696915330185, 9.842599042389793], - [34.74728554172066, 6.383604250389453], - [44.43248362650024, 5], - [54.117555039493936, 5], - [64.49450774754837, 6.383604250389453], - [74.17970583232795, 9.842599042389793], - [77.63873229227488, 11.226171624832773], - [82.4812679987715, 14.685166416833113], - [85.94029445871843, 16.760556958444], - [88.01568500032931, 19.527733791276376], - [89.39919424687946, 21.60312433288732], - [90.78283016521505, 23.678514874498205], - [91.47458478849035, 24.37033283366617], - [91.47458478849035, 25.75390541610909], - [90.78283016521505, 25.75390541610909], - [89.39919424687946, 25.75390541610909], - [89.39919424687946, 24.37033283366617], - [89.39919424687946, 22.986728583276715], - [89.39919424687946, 22.294942292055282], - [91.47458478849035, 19.527733791276376], - [118.45466182943187, 12.60977587522217], - [125.37271474932572, 14.685166416833113], - [135.05778616231942, 19.527733791276376], - [137.1331767039303, 20.91133804166583], - [139.90044854060193, 24.37033283366617], - [144.05122962382416, 32.67189500010983], - [144.742984247099, 36.13085812416364] - ], - "color": "--affine-palette-line-green", - "lineWidth": 10, - "id": "MWy51BkPqD", - "index": "b6Q", - "seed": 309108426 - }, - "2MBs127OeD": { - "type": "brush", - "xywh": "[1895.4864783475177,-346.3380289368134,53.58320137382907,30.75390541610909]", - "points": [ - [48.58320137382907, 11.91795791605415], - [41.66521178982839, 8.45896312405381], - [31.98007704094198, 5.691786291221433], - [22.294878956162393, 5], - [19.527733791276432, 5.691786291221433], - [13.993316789718847, 7.075390541610886], - [10.534353665664867, 7.7671768328323765], - [8.45896312405398, 8.45896312405381], - [6.383572582443094, 9.842567374443263], - [5.691754623275074, 12.60974420727564], - [5, 13.993348457665093], - [5, 17.452343249665432], - [6.383572582443094, 21.603124332887262], - [7.075390541610886, 24.37030116571964], - [7.76714516488596, 25.75390541610909] - ], - "color": "--affine-palette-line-green", - "lineWidth": 10, - "id": "2MBs127OeD", - "index": "b6R", - "seed": 1719671446 - }, - "wTSCR6f1jj": { - "type": "brush", - "xywh": "[2064.2848668412767,-268.8567609380418,40.43910350088845,60.5011276219368]", - "points": [ - [18.83597916800136, 6.38357258244298], - [23.678514874497978, 5], - [33.363712959277564, 5.69175462327496], - [34.747348877613604, 7.76714516488596], - [35.43910350088845, 18.835915832108412], - [34.05546758255241, 25.062087456941185], - [29.21293187605579, 36.13085812416364], - [23.678514874497978, 44.4324202906073], - [18.83597916800136, 49.96677395627205], - [13.301562166443546, 53.425737080325916], - [7.767271836671625, 55.5011276219368], - [6.383635918335585, 55.5011276219368], - [5, 54.117555039493936], - [5, 53.425737080325916] - ], - "color": "--affine-palette-line-green", - "lineWidth": 10, - "id": "wTSCR6f1jj", - "index": "b6S", - "seed": 985927068 - }, - "OhRlZB2nTN": { - "type": "brush", - "xywh": "[1896.8700509299608,-265.39779781398795,41.822676083331544,41.82267608333166]", - "points": [ - [7.075390541610886, 5], - [5.691817959167793, 9.150781083221887], - [5, 18.835915832108412], - [5.691817959167793, 24.370332833666225], - [8.45896312405398, 29.904686499330978], - [11.22617162483266, 32.67189500010977], - [16.068770667222452, 35.43904016499573], - [25.06208745694107, 36.82267608333166], - [30.596504458498885, 36.82267608333166], - [36.822676083331544, 36.13085812416364] - ], - "color": "--affine-palette-line-green", - "lineWidth": 10, - "id": "OhRlZB2nTN", - "index": "b6T", - "seed": 944159691 - }, - "kbhV0AgzHn": { - "type": "brush", - "xywh": "[1120.2318533086293,-168.50070046629594,148.93510301086712,95.19015427794585]", - "points": [ - [2, 93.19015427794585], - [2, 86.5821720838918], - [2.8810911804889656, 82.61738276745933], - [7.726931345487856, 69.84196396959572], - [9.489073374543068, 65.43662906291883], - [11.691720661920272, 60.59078889792005], - [15.215964388108205, 52.220664674810735], - [17.85915726572989, 48.25587535837832], - [23.145543020973037, 39.445245876947126], - [35.039910970270284, 24.467139459783766], - [43.85058078362408, 16.09701523667445], - [48.25587535837826, 11.251175071675618], - [50.458562977678184, 9.929578632864832], - [54.4233522941106, 6.845840164998776], - [55.30440314267685, 5.964789316432416], - [57.94759602029853, 4.202647287377147], - [59.26919245910926, 2.8810508485663036], - [60.59078889792022, 2.440545590244426], - [61.47183974648647, 2], - [62.35293092697543, 2], - [62.79343618529742, 2], - [63.67452736578616, 2], - [64.55557821435264, 2], - [65.87717465316337, 2], - [67.1987710919741, 2.440545590244426], - [68.96091312102931, 2.8810508485663036], - [70.72301481816226, 3.762142029055269], - [78.2120881927051, 5.964789316432416], - [83.49847394794847, 7.286385755243202], - [86.14166682556993, 8.607982194054046], - [94.51175071675675, 13.013317100730887], - [97.59548918462292, 14.775418797863608], - [103.32242053011055, 19.62129929478499], - [109.48985713392017, 25.788735898594553], - [115.65729373772979, 31.956172502404115], - [121.82477067346167, 39.445245876947126], - [124.46796355108336, 42.52894401289063], - [129.31380371608225, 49.13692620694468], - [133.27859303251466, 55.744908400998725], - [136.8028367587026, 61.912385336730836], - [138.5649787877578, 66.31767991148519], - [140.767626075135, 70.2824692279176], - [142.08922251394574, 74.24725854435002], - [143.4108189527567, 77.33099701221613], - [144.29191013324566, 81.73633191889303], - [144.73241539156743, 82.61738276745933], - [145.6135065720564, 83.93897920627018], - [146.49455742062287, 85.70112123532545], - [146.93510301086712, 86.5821720838918], - [146.93510301086712, 87.02271767413623], - [146.93510301086712, 87.46322293245811], - [146.93510301086712, 87.90376852270259] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "kbhV0AgzHn", - "index": "b6U", - "seed": 1652155770 - }, - "ODCqCfNSpk": { - "type": "brush", - "xywh": "[1125.9587846541172,-83.91852838240413,139.24338234894685,22.50235014335135]", - "points": [ - [2, 9.048527784298471], - [3.7621420290552123, 10.370124223109315], - [9.929578632864832, 13.894367949297248], - [12.572771510486291, 14.775418797863608], - [15.65650997835246, 15.656509978352517], - [21.383400991917597, 16.537560826918877], - [27.110332337405225, 17.859157265729664], - [30.194070805271394, 18.299702855974147], - [35.92096181883653, 19.180753704540507], - [42.08843875456864, 20.061804553106867], - [47.81532976813378, 20.50235014335135], - [53.54226111362141, 20.50235014335135], - [59.70969771743103, 20.50235014335135], - [65.43662906291866, 20.50235014335135], - [68.52036753078482, 20.50235014335135], - [74.68780413459444, 20.061804553106867], - [79.97418988983759, 19.62129929478499], - [84.37952479651449, 19.180753704540507], - [88.7848193712689, 18.740208114296024], - [93.1901542779458, 18.299702855974147], - [97.15494359437821, 17.859157265729664], - [100.23868206224415, 16.537560826918877], - [102.00078375937687, 16.537560826918877], - [106.84666425629825, 15.656509978352517], - [109.93036239224193, 14.33491353954173], - [113.45464645035236, 13.894367949297248], - [116.09783932797382, 13.013317100730887], - [118.7410322055955, 12.132225920241979], - [119.62208305416198, 11.691720661920101], - [121.38422508321719, 11.251175071675618], - [123.58687237059439, 10.370124223109315], - [124.46792321916064, 9.929578632864832], - [126.23006524821585, 9.048527784298471], - [127.55166168702681, 8.607982194054046], - [128.87325812583754, 8.167436603809563], - [129.754308974404, 7.286385755243202], - [131.07590541321474, 6.845840164998776], - [131.9569965937037, 5.964789316432416], - [133.27859303251444, 5.524243726187933], - [133.71909829083643, 5.0837384678660555], - [135.04069472964716, 4.202647287377147], - [135.48124031989164, 3.762142029055269], - [136.3622911684579, 2.8810508485663036], - [136.80283675870237, 2.440545590244426], - [137.24338234894685, 2] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "ODCqCfNSpk", - "index": "b6V", - "seed": 660273861 - }, - "6rDKmEpsDm": { - "type": "brush", - "xywh": "[1164.725626969875,-121.8042795176732,26.46713945978354,26.467139459783766]", - "points": [ - [24.46713945978354, 2], - [21.383400991917597, 4.64319287762163], - [18.74020811429591, 6.845840164998776], - [12.132225920242035, 13.894367949297305], - [10.370083891186596, 16.09701523667445], - [8.607982194053875, 17.418611675485295], - [5.964789316432416, 20.50235014335135], - [3.3215964388107295, 23.14554302097298], - [2, 24.467139459783766] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "6rDKmEpsDm", - "index": "b6W", - "seed": 346419107 - }, - "eUdoNF0lYW": { - "type": "brush", - "xywh": "[1173.5362564513061,-120.04217782054047,28.229281488839206,31.75352521502697]", - "points": [ - [19.621299294785103, 2], - [16.09701523667445, 5.524284058110538], - [12.132225920242035, 10.370124223109315], - [9.048527784298585, 13.894367949297305], - [7.726931345487628, 15.215964388108091], - [6.405334906676899, 16.537560826918877], - [5.524243726187933, 17.418652007407843], - [3.7621420290552123, 19.180753704540507], - [2.440545590244483, 20.50235014335135], - [2, 21.823946582162137], - [2, 23.145543020972923], - [2, 24.467139459783766], - [2, 25.788735898594553], - [2.440545590244483, 25.788735898594553], - [3.321596438810957, 26.66982707908346], - [7.286385755243373, 27.55087792764982], - [8.607982194054102, 27.991423517894304], - [10.810629481431306, 28.872474366460665], - [14.33491353954173, 29.31301995670509], - [17.41861167548541, 29.75352521502697], - [19.18075370454062, 29.75352521502697], - [22.264492172406563, 29.31301995670509], - [24.90768505002825, 27.991423517894304], - [26.229281488839206, 27.55087792764982] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "eUdoNF0lYW", - "index": "b6X", - "seed": 1585361883 - }, - "jFENPM1sEX": { - "type": "brush", - "xywh": "[1165.6066778184413,-100.21823123837834,31.75352521502691,17.656509978352574]", - "points": [ - [2, 2], - [2.440545590244483, 4.202687619299752], - [3.321596438810957, 7.726931345487685], - [3.7621420290552123, 10.370124223109315], - [4.643192877621686, 11.691720661920101], - [5.083738467866169, 12.572771510486461], - [6.845840164998663, 13.894367949297305], - [9.489033042620349, 14.775459129786213], - [13.013317100731001, 15.656509978352574], - [16.09701523667445, 15.656509978352574], - [20.061804553106867, 15.656509978352574], - [23.145543020973037, 15.656509978352574], - [24.467139459783766, 15.656509978352574], - [27.110332337405453, 15.656509978352574], - [29.75352521502691, 15.656509978352574] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "jFENPM1sEX", - "index": "b6Y", - "seed": 853013629 - }, - "u5noFLsEVU": { - "type": "brush", - "xywh": "[1193.8007486237127,-101.53982767718912,24.704997430728554,21.62129929478499]", - "points": [ - [10.810629481431306, 10.81066981335374], - [12.132225920242035, 7.286385755243202], - [12.132225920242035, 5.964789316432416], - [14.334873207619239, 3.3215964388107864], - [15.215964388108205, 2.8810911804889088], - [16.537560826918934, 2.440545590244426], - [17.85915726572989, 2], - [18.740208114296138, 2], - [20.50235014335135, 3.3215964388107864], - [21.823946582162307, 5.0837384678660555], - [22.26445184048407, 7.286385755243202], - [22.704997430728554, 10.81066981335374], - [21.383400991917824, 13.894367949297248], - [19.621258962862385, 16.537560826918877], - [17.41861167548541, 18.299702855974147], - [15.656469646429969, 19.180753704540507], - [13.01327676880851, 19.62129929478499], - [12.132225920242035, 19.62129929478499], - [9.489033042620349, 18.299702855974147], - [6.84584016499889, 16.97810641716336], - [4.643192877621686, 15.215964388108091], - [3.321596438810957, 13.894367949297248], - [2, 13.013317100730887] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "u5noFLsEVU", - "index": "b6Z", - "seed": 152910531 - }, - "apgo0WiVXw": { - "type": "brush", - "xywh": "[1218.4705353708737,-139.8661244027026,15.453822359052765,17.21596438810809]", - "points": [ - [4.202647287377204, 15.215964388108091], - [2.440545590244483, 11.251175071675675], - [2, 8.607982194054046], - [2, 5.964789316432416], - [2.440545590244483, 5.524284058110538], - [3.321596438810957, 4.202687619299752], - [4.643192877621686, 2.8810911804889088], - [5.964789316432416, 2.440545590244483], - [7.286385755243373, 2], - [8.16743660380962, 2], - [8.607982194054102, 2.8810911804889088], - [9.929578632864832, 4.64319287762163], - [11.251175071675789, 8.167476935732168], - [12.132225920242035, 9.489073374542954], - [13.013317100731001, 12.572771510486461], - [13.453822359052765, 13.894367949297305] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "apgo0WiVXw", - "index": "b6a", - "seed": 1146064757 - }, - "2I4X5NQLnK": { - "type": "brush", - "xywh": "[1231.6864997589819,-115.19629732361915,19.859157265729664,9.28638575524326]", - "points": [ - [2, 7.286385755243259], - [5.964789316432416, 7.286385755243259], - [8.607982194053875, 6.40529457475435], - [12.572771510486291, 4.64319287762163], - [13.894367949297248, 3.762101697132721], - [15.215964388107977, 3.321596438810843], - [16.537560826918707, 2.4405052583219344], - [17.41861167548518, 2.4405052583219344], - [17.859157265729664, 2] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "2I4X5NQLnK", - "index": "b6b", - "seed": 918817015 - }, - "9H6vsfCcGM": { - "type": "brush", - "xywh": "[1231.2459541687374,-114.31524647505279,26.026634201461775,27.788735898594553]", - "points": [ - [2.8810911804887382, 5.083738467866112], - [2, 7.726931345487685], - [2, 10.810629481431192], - [5.524284058110425, 21.38340099191771], - [5.964789316432416, 22.704997430728497], - [7.2863857552431455, 24.467139459783766], - [9.048527784298358, 25.348190308350127], - [14.775459129786213, 25.788735898594553], - [16.097055568596943, 25.788735898594553], - [18.299702855974147, 24.90768505002825], - [19.180753704540393, 24.026593869539283], - [20.942895733595606, 22.26449217240662], - [22.264492172406563, 20.061804553106867], - [23.14554302097281, 18.74020811429608], - [23.586088611217292, 16.09701523667445], - [24.026634201461775, 14.33491353954173], - [24.026634201461775, 12.572771510486461], - [23.586088611217292, 11.251175071675675], - [22.705037762651045, 9.489033042620406], - [22.264492172406563, 6.405334906676899], - [21.383441323840316, 2] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "9H6vsfCcGM", - "index": "b6c", - "seed": 978438289 - }, - "iooTUvCJjn": { - "type": "brush", - "xywh": "[1238.2944819530358,-139.42557881245813,13.251175071675789,20.299702855974147]", - "points": [ - [2.440545590244483, 13.453822359052822], - [2, 9.489033042620349], - [2, 7.726931345487685], - [2, 5.964789316432416], - [2.440545590244483, 4.202647287377147], - [2.881050848566474, 2.8810508485663036], - [3.321596438810957, 2.8810508485663036], - [3.7621420290552123, 2], - [4.202647287377204, 2], - [5.083738467866169, 2], - [5.964789316432416, 2.8810508485663036], - [7.286385755243373, 5.964789316432416], - [8.607982194054102, 9.489033042620349], - [9.929578632864832, 13.013317100730887], - [10.810629481431306, 15.656509978352517], - [11.251175071675789, 18.299702855974147] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "iooTUvCJjn", - "index": "b6d", - "seed": 1667061378 - }, - "1VkbZ4u499": { - "type": "brush", - "xywh": "[1166.3320692687987,-63.255304977858884,12.001201215598485,20.38340619914746]", - "points": [ - [10.001201215598485, 2], - [9.239176238463415, 3.3335393496779204], - [6.953118748292354, 7.715143725427538], - [5.619579398614405, 10.382204983548888], - [5.048065026071754, 11.715744333226837], - [4.286057490171061, 13.049283682904758], - [3.714543117628182, 14.382805591348188], - [2.5715143725426515, 17.240377454061957], - [2, 18.38340619914746] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "1VkbZ4u499", - "index": "b6e", - "seed": 321109056 - }, - "uFqjjuaOnd": { - "type": "brush", - "xywh": "[1164.6175261511705,-46.49087756952659,16.001801823397727,7.810090003206881]", - "points": [ - [2, 2], - [2.762007535900466, 2.762007535900665], - [4.476550653528875, 4.095546885578614], - [6.000600607799242, 4.667061258121379], - [6.572114980341894, 4.667061258121379], - [7.334122516242587, 4.857571862713797], - [8.667661865920536, 5.238575630664116], - [9.239176238463415, 5.429086235256534], - [10.572715588141136, 5.810090003206881], - [11.906237496584708, 5.810090003206881], - [13.430287450855076, 5.238575630664116], - [14.001801823397727, 4.857571862713797] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "uFqjjuaOnd", - "index": "b6f", - "seed": 1308798557 - }, - "rTKTY3-fDw": { - "type": "brush", - "xywh": "[1171.0946774124986,-47.63390631461209,8.191111212391661,6.286057490171032]", - "points": [ - [2, 2], - [2.952535581727716, 2.571514372542765], - [4.667078699355898, 3.5240325130358485], - [6.191111212391661, 4.286057490171032] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "rTKTY3-fDw", - "index": "b6g", - "seed": 717997295 - }, - "u1XotDBKlm": { - "type": "brush", - "xywh": "[1196.8128241769227,-65.73187307262234,9.52465056206961,17.33534117307579]", - "points": [ - [3.714543117628182, 2], - [4.095564326813019, 3.143028745085502], - [5.429086235256364, 6.191111212391718], - [6.3816218169840795, 8.477168702562722], - [6.572114980341894, 9.620197447648223], - [6.762625584934312, 10.763226192733754], - [7.143629352884545, 11.715744333226837], - [7.334139957477191, 13.239794287497176], - [7.52465056206961, 14.001801823397841], - [7.52465056206961, 14.382823032582706], - [7.52465056206961, 14.573316195940606], - [7.334139957477191, 14.573316195940606], - [7.143629352884545, 14.573316195940606], - [6.762625584934312, 14.573316195940606], - [6.000600607799242, 14.573316195940606], - [4.66707869935567, 14.763826800533025], - [3.143028745085303, 14.954337405125443], - [2, 15.33534117307579] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "u1XotDBKlm", - "index": "b6h", - "seed": 1527581589 - }, - "J2APUGxfDa": { - "type": "brush", - "xywh": "[1194.1457629188012,-54.873099994309996,6.095546885578642,13.525251169868937]", - "points": [ - [2, 2], - [2.3810037679504603, 3.9050537222206856], - [2.952518140493112, 6.000600607799299], - [3.5240325130357633, 8.858172470513068], - [3.5240325130357633, 9.429686843055833], - [3.714543117628409, 10.00120121559857], - [3.9050537222208277, 10.572715588141335], - [4.095546885578642, 11.144229960684072], - [4.095546885578642, 11.334740565276519], - [4.095546885578642, 11.525251169868937] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "J2APUGxfDa", - "index": "b6i", - "seed": 1462262301 - }, - "LXTKtXMWGT": { - "type": "brush", - "xywh": "[1194.717277291344,-53.53956064463205,5.14302874508553,11.620197447648223]", - "points": [ - [2.9525181404928844, 2], - [2.9525181404928844, 3.3335393496779204], - [2.9525181404928844, 5.619596839848953], - [2.9525181404928844, 7.143629352884773], - [3.1430287450855303, 7.715143725427538], - [3.1430287450855303, 8.096147493377885], - [3.1430287450855303, 7.715143725427538], - [2.9525181404928844, 6.3816043757496175], - [2.76202497713507, 5.238575630664087], - [2.381003767950233, 4.476568094763422], - [2.1905106045924185, 4.095546885578585], - [2, 3.9050537222206856], - [2, 3.714543117628267], - [2, 4.857571862713769], - [2.381003767950233, 7.524633120835119], - [3.1430287450855303, 9.620197447648223] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "LXTKtXMWGT", - "index": "b6j", - "seed": 225921457 - }, - "ksKLQcJCMu": { - "type": "brush", - "xywh": "[1191.47870166068,-53.920564412582394,5,10.66766186592065]", - "points": [ - [2.7620075359006933, 2], - [2.381003767950233, 2.762007535900665], - [2, 4.476550653528932], - [2, 5.810090003206852], - [2.381003767950233, 7.334122516242701], - [2.952518140493112, 8.66766186592065] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "ksKLQcJCMu", - "index": "b6k", - "seed": 372339608 - }, - "OabCj7h_5D": { - "type": "brush", - "xywh": "[1192.431219801173,-54.49207878512516,6.095546885578642,10.477151261328231]", - "points": [ - [2, 2], - [2.1905106045924185, 3.33352190844343], - [2.5715143725426515, 5.810090003206881], - [4.095546885578642, 8.477151261328231] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "OabCj7h_5D", - "index": "b6l", - "seed": 1679229950 - }, - "QUVgZCzEsd": { - "type": "brush", - "xywh": "[1193.0027341737157,-51.062992549868625,8.953118748292354,10.477151261328231]", - "points": [ - [2, 2], - [2.571514372542879, 3.33352190844343], - [4.286057490171061, 6.000600607799299], - [5.810090003207051, 7.334122516242701], - [6.953118748292354, 8.477151261328231] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "QUVgZCzEsd", - "index": "b6m", - "seed": 1151757165 - }, - "EYvJOcSWJa": { - "type": "brush", - "xywh": "[1193.3837379416661,-49.538960036832776,7.6195968398490095,9.33413995747722]", - "points": [ - [2, 2], - [2.381021209184837, 3.9050537222206856], - [2.5715143725426515, 4.476568094763451], - [3.9050537222206003, 6.191111212391718], - [4.857571862713712, 6.7626255849344545], - [5.6195968398490095, 7.33413995747722] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "EYvJOcSWJa", - "index": "b6n", - "seed": 99489098 - }, - "Anh4lrYNn9": { - "type": "brush", - "xywh": "[1192.8122235691233,-48.96744566429001,7.429086235256591,8.762625584934455]", - "points": [ - [2, 2], - [2.7620249771352974, 3.714543117628267], - [3.333539349677949, 4.857571862713769], - [4.286057490171061, 5.619596839848953], - [5.429086235256591, 6.7626255849344545] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "Anh4lrYNn9", - "index": "b6o", - "seed": 1094095616 - }, - "MvmWlRSMNX": { - "type": "brush", - "xywh": "[1193.5742485462586,-47.44341315125416,7.238575630664172,6.667078699355841]", - "points": [ - [2, 2], - [4.286057490171061, 4.095564326813104], - [5.238575630664172, 4.667078699355841] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "MvmWlRSMNX", - "index": "b6p", - "seed": 757701587 - }, - "-TQPvCuecZ": { - "type": "shape", - "xywh": "[861.1704388638423,-460.22601366120034,171.5436462431324,150.86648088967036]", - "shapeType": "ellipse", - "radius": 0, - "filled": true, - "fillColor": "--affine-palette-shape-yellow", - "strokeWidth": 4, - "strokeColor": "--affine-palette-line-yellow", - "strokeStyle": "solid", - "roughness": 2, - "id": "-TQPvCuecZ", - "index": "b6q", - "seed": 1735631317 - }, - "Bu61nIERZ-": { - "type": "brush", - "xywh": "[1059.0494001248194,-441.54888336417946,69.09469282454529,13.95564364271496]", - "points": [ - [2, 11.95564364271496], - [8.892376765673816, 10.424027732414856], - [37.99349973866856, 3.5316509667412106], - [52.544096281606926, 2], - [56.37317111379821, 2], - [67.09469282454529, 4.297458921891234] - ], - "color": "--affine-palette-line-yellow", - "lineWidth": 4, - "id": "Bu61nIERZ-", - "index": "b6r", - "seed": 1304224718 - }, - "N0rqqXSjH3": { - "type": "brush", - "xywh": "[1087.3847501991052,-374.9225746293341,57.60732810220702,10.126568810523622]", - "points": [ - [2, 2], - [10.42399267597375, 3.5316509667412106], - [27.272048140803463, 6.5949178437824685], - [45.65171951593334, 8.126568810523622], - [49.48079434812439, 8.126568810523622], - [55.60732810220702, 8.126568810523622] - ], - "color": "--affine-palette-line-yellow", - "lineWidth": 4, - "id": "N0rqqXSjH3", - "index": "b6s", - "seed": 489177967 - }, - "aI_S8qjShN": { - "type": "brush", - "xywh": "[1067.4733928007931,-313.6569916934213,47.65171951593311,38.461883828368286]", - "points": [ - [2, 2], - [6.5949178437824685, 4.297458921891234], - [21.91135739831202, 15.784753531347349], - [36.4618838283684, 28.037856095953487], - [39.52518576185071, 30.33531501784472], - [43.35426059404199, 34.93023286162713], - [45.65171951593311, 36.461883828368286] - ], - "color": "--affine-palette-line-yellow", - "lineWidth": 4, - "id": "aI_S8qjShN", - "index": "b6t", - "seed": 1504988946 - }, - "2yJWZcFV9U": { - "type": "brush", - "xywh": "[1025.3533242516012,-285.32167667557655,26.20878126376215,54.544096281606755]", - "points": [ - [2, 2], - [4.297458921891348, 6.594917843782412], - [11.955643642714904, 24.20878126376209], - [18.84802040838872, 38.759342750259464], - [20.379671375129874, 41.82260962730072], - [24.20878126376215, 52.544096281606755] - ], - "color": "--affine-palette-line-yellow", - "lineWidth": 4, - "id": "2yJWZcFV9U", - "index": "b6u", - "seed": 1727003972 - }, - "yR8EmHbTwl": { - "type": "brush", - "xywh": "[970.214310126212,-263.87873842340554,6.297458921891234,57.60739821508912]", - "points": [ - [3.5316509667411538, 2], - [2, 11.189835687564823], - [2.7658079551500805, 29.569507062694584], - [4.297458921891234, 44.120103605633005], - [4.297458921891234, 47.18337048267426], - [4.297458921891234, 54.84155520349799], - [4.297458921891234, 55.60739821508912] - ], - "color": "--affine-palette-line-yellow", - "lineWidth": 4, - "id": "yR8EmHbTwl", - "index": "b6v", - "seed": 217366119 - }, - "NhvwG4BQMh": { - "type": "brush", - "xywh": "[927.3283985654289,-262.3470874566644,13.189835687564937,53.77828832645673]", - "points": [ - [11.189835687564937, 2], - [8.892376765673703, 12.721486654306034], - [5.0633019334823075, 31.101158029435794], - [2.765843011591187, 43.35426059404193], - [2.765843011591187, 46.41752747108319], - [2.765843011591187, 51.0124453148656], - [2.765843011591187, 51.77828832645673], - [2, 51.77828832645673] - ], - "color": "--affine-palette-line-yellow", - "lineWidth": 4, - "id": "NhvwG4BQMh", - "index": "b6w", - "seed": 1329973834 - }, - "w1H7NkFYl8": { - "type": "brush", - "xywh": "[893.6323577486518,-265.41035433370564,8.594917843782355,61.43647304728046]", - "points": [ - [6.594917843782355, 2], - [5.8290748321912815, 6.594917843782412], - [5.8290748321912815, 28.803664051103453], - [4.297458921891234, 41.82260962730072], - [3.5316159103000473, 47.183335426233214], - [2, 57.90482208053925], - [2, 59.43647304728046] - ], - "color": "--affine-palette-line-yellow", - "lineWidth": 4, - "id": "w1H7NkFYl8", - "index": "b6x", - "seed": 910613227 - }, - "0czhGhqZSx": { - "type": "brush", - "xywh": "[1032.245701017275,-508.1751920990248,39.22769178351814,29.272048140803463]", - "points": [ - [2, 27.272048140803463], - [8.892376765673589, 22.677130297020994], - [24.974589218912115, 10.42401020419436], - [37.22769178351814, 2] - ], - "color": "--affine-palette-line-yellow", - "lineWidth": 4, - "id": "0czhGhqZSx", - "index": "b6y", - "seed": 1591045967 - }, - "0_lEiNrE4c": { - "type": "brush", - "xywh": "[2432.799407524681,1452.7059747189228,17.38242223961879,15.152018533015507]", - "points": [ - [4.97389429764371, 12.408527941974626], - [10.921614826412679, 7.947720528768514], - [14.638931648577909, 4.9738942976439375], - [15.38242223961879, 3.486913115562402], - [15.38242223961879, 2.7434905910408816], - [15.38242223961879, 2], - [14.638931648577909, 2], - [13.152018533015507, 2], - [10.921614826412679, 2], - [8.691211119809395, 2], - [6.460807413206112, 2], - [5.71731682216523, 2], - [4.230403706603283, 2], - [3.486913115562402, 2], - [4.230403706603283, 2], - [4.97389429764371, 2.7434905910408816], - [6.460807413206112, 4.230403706603056], - [8.691211119809395, 6.460807413206112], - [9.434701710850277, 8.691211119809395], - [10.178124235371797, 9.434701710850277], - [10.921614826412679, 10.921614826412451], - [10.921614826412679, 12.408527941974626], - [9.434701710850277, 12.408527941974626], - [8.691211119809395, 12.408527941974626], - [8.691211119809395, 11.665105417453333], - [6.460807413206112, 10.921614826412451], - [4.230403706603283, 9.434701710850277], - [2.7434905910408816, 7.947720528768514], - [2, 7.947720528768514], - [4.230403706603283, 9.434701710850277], - [7.2042980042469935, 10.921614826412451], - [8.691211119809395, 11.665105417453333], - [9.434701710850277, 12.408527941974626], - [10.921614826412679, 13.152018533015507], - [11.66510541745356, 13.152018533015507], - [10.921614826412679, 11.665105417453333], - [9.434701710850277, 10.921614826412451], - [8.691211119809395, 10.178124235371797], - [7.947720528768514, 9.434701710850277], - [7.947720528768514, 8.691211119809395], - [8.691211119809395, 8.691211119809395], - [9.434701710850277, 9.434701710850277], - [10.921614826412679, 10.178124235371797], - [10.921614826412679, 10.921614826412451] - ], - "color": "--affine-palette-line-purple", - "lineWidth": 4, - "id": "0_lEiNrE4c", - "index": "b5M", - "seed": 175045379 - }, - "kb_HfQzTZx": { - "type": "brush", - "xywh": "[2522.759046379847,1421.480322826479,15.895441057537028,15.895509124056389]", - "points": [ - [5.717384888684592, 2], - [6.460807413206112, 5.71731682216523], - [7.204229937727632, 7.947720528768514], - [7.204229937727632, 10.921614826412451], - [7.204229937727632, 12.408527941974626], - [7.204229937727632, 13.152018533015507], - [7.204229937727632, 13.895509124056389], - [7.204229937727632, 13.152018533015507], - [5.717384888684592, 12.408527941974626], - [4.973826231124349, 10.921614826412451], - [4.230403706603283, 10.921614826412451], - [3.486981182081763, 9.434701710850277], - [2, 8.691211119809395], - [2, 7.2042980042469935], - [2, 6.460807413206112], - [2, 5.71731682216523], - [3.486981182081763, 6.460807413206112], - [6.460807413206112, 10.921614826412451], - [7.204229937727632, 11.665105417453105], - [7.204229937727632, 12.408527941974626], - [7.204229937727632, 11.665105417453105], - [7.204229937727632, 10.921614826412451], - [7.204229937727632, 9.434701710850277], - [7.204229937727632, 8.691211119809395], - [7.204229937727632, 7.2042980042469935], - [7.204229937727632, 6.460807413206112], - [7.947788595287875, 6.460807413206112], - [9.434633644330916, 6.460807413206112], - [10.178192301891158, 6.460807413206112], - [10.921614826412679, 6.460807413206112], - [11.665037350934199, 7.2042980042469935], - [13.152018533015507, 8.691211119809395], - [13.895441057537028, 10.17812423537157], - [13.895441057537028, 10.921614826412451], - [13.152018533015507, 11.665105417453105], - [11.665037350934199, 11.665105417453105], - [10.921614826412679, 11.665105417453105], - [10.178192301891158, 10.921614826412451], - [9.434633644330916, 10.17812423537157], - [7.947788595287875, 9.434701710850277], - [7.204229937727632, 7.947720528768514], - [7.204229937727632, 7.2042980042469935] - ], - "color": "--affine-palette-line-purple", - "lineWidth": 4, - "id": "kb_HfQzTZx", - "index": "b5N", - "seed": 1562620217 - }, - "ClKromwpTE": { - "type": "brush", - "xywh": "[2453.438838887268,1484.1503017020839,166.09588720217334,128.56882473140786]", - "points": [ - [4.084802340883016, 126.56882473140786], - [3.0424011704417353, 118.75069665792898], - [2.521176726187605, 115.1022687023501], - [2.521176726187605, 107.8053173550586], - [2, 104.6780661256671], - [2, 97.38116249644236], - [2, 92.69028565235499], - [3.0424011704417353, 84.35093313462198], - [4.606026785137601, 73.92677827600573], - [7.2120535702747475, 64.545024587831], - [11.38175368817474, 54.64204645540235], - [15.551406088007752, 45.78151721148174], - [18.678657317399484, 41.09064036739437], - [23.890758605740757, 32.23011112347399], - [29.624036620269635, 25.454384220436623], - [34.83613790861136, 19.721106205907745], - [39.527014752698506, 15.030229361820375], - [43.696667152531965, 11.38175368817474], - [47.866319552364985, 8.775726903037366], - [51.51479522601085, 6.69087684408737], - [54.642046455402124, 5.127251229391504], - [55.68444762584386, 4.606026785137374], - [58.29052212904798, 3.563625614695866], - [60.37532446993101, 2.521224444254358], - [61.41777335843972, 2], - [61.93895008462687, 2], - [62.460174528881, 2], - [62.981398973135576, 2], - [63.50257569932273, 2], - [65.0662013140186, 2], - [68.19345254340988, 2.521224444254358], - [72.88432938749747, 3.563625614695866], - [80.18128073478874, 7.2121012883415005], - [88.52058553445524, 12.424154858616248], - [97.38116249644236, 18.157480591212106], - [105.19929056992123, 24.411983049995115], - [109.36894296975424, 27.01800983513249], - [116.665846598979, 31.70888667921986], - [122.39917233157485, 36.399763523307], - [127.09004917566199, 40.56941592314024], - [131.25974929356198, 43.696667152531745], - [134.90817724914086, 47.34514282617761], - [144.2899309373156, 55.68449534391061], - [146.37473327819862, 58.290522129047986], - [152.10805901079448, 65.06624903208535], - [154.1928613516775, 67.6722758172225], - [157.84138474339034, 72.36315266130987], - [159.92618708427335, 76.01162833495573], - [162.01098942515637, 79.13887956434723], - [163.05343831366508, 81.22368190523048], - [164.09588720217334, 81.74490634948461], - [164.09588720217334, 82.78730751992612] - ], - "color": "--affine-palette-line-purple", - "lineWidth": 4, - "id": "ClKromwpTE", - "index": "b5O", - "seed": 43417698 - }, - "_ZBzALDynY": { - "type": "brush", - "xywh": "[2458.650892457543,1567.5436837252141,166.09588720217334,55.59964528496084]", - "points": [ - [2, 53.59964528496084], - [5.648475673645862, 53.59964528496084], - [16.593854976516013, 53.078420840706485], - [25.454384220436623, 52.03601967026498], - [34.31496118242376, 50.47239405556911], - [43.6967148705985, 48.908768440873246], - [52.557244114519115, 47.34514282617761], - [61.93899780269385, 45.78151721148197], - [70.27835032042685, 43.69666715253197], - [79.66010400860159, 41.611817093581976], - [84.35098085268874, 40.04819147888611], - [93.73273454086348, 37.963389138003095], - [102.0720393405295, 35.8785390790531], - [109.89016741400837, 34.31491346435723], - [117.70829548748723, 31.70888667921986], - [124.48407010859182, 29.624036620269866], - [130.73857256737483, 27.01800983513249], - [136.47180286383673, 24.411935331928362], - [139.07787736704086, 23.369534161486854], - [143.24757748494085, 21.284684102536858], - [146.89600544051973, 18.678657317399484], - [152.6293311731156, 15.030181643753622], - [159.92618708427335, 8.254502458783236], - [160.44745924659446, 7.2120535702747475], - [161.48981269896922, 6.69087684408737], - [162.53226158747748, 5.127251229391732], - [163.05343831366508, 4.084802340883243], - [163.5747104759862, 3.563625614695866], - [164.09588720217334, 3.0424011704417353], - [164.09588720217334, 2] - ], - "color": "--affine-palette-line-purple", - "lineWidth": 4, - "id": "_ZBzALDynY", - "index": "b5P", - "seed": 1806250475 - }, - "wTnu7snGTf": { - "type": "brush", - "xywh": "[2440.408609525448,1461.2171419259016,29.018009835132034,76.96917944644701]", - "points": [ - [6.690876844087143, 2], - [5.127251229391277, 7.21205357027452], - [3.563625614695411, 13.987780473311886], - [2.5212244442541305, 19.199881761653387], - [2, 21.80590854679076], - [2, 24.933159776182265], - [2, 30.145261064523766], - [3.0424011704412806, 36.399763523307], - [3.563625614695411, 42.13304153783588], - [4.606026785137146, 47.34514282617738], - [6.169652399833012, 51.51479522601039], - [6.690876844087143, 53.59964528496038], - [8.775726903037139, 58.29052212904776], - [10.860529243920155, 61.93895008462664], - [12.945379302870151, 65.5874257582725], - [15.551406088007752, 68.19345254340988], - [18.15748059121188, 70.799527046614], - [20.763507376349025, 72.36315266130987], - [22.84835743529902, 73.9267782760055], - [23.890758605740757, 74.44795500219288], - [25.454384220436623, 74.96917944644701], - [26.496785390877903, 74.96917944644701], - [27.018009835132034, 74.96917944644701] - ], - "color": "--affine-palette-line-purple", - "lineWidth": 4, - "id": "wTnu7snGTf", - "index": "b5Q", - "seed": 353350136 - }, - "0w9mBYbIZA": { - "type": "brush", - "xywh": "[2535.7897720218903,1433.0718570023441,47.781517211481514,93.12668389669261]", - "points": [ - [2, 2], - [6.690876844087143, 3.563625614695866], - [8.775726903037139, 5.127251229391504], - [18.15748059121188, 10.339328658699742], - [20.763507376349025, 12.424178717649738], - [22.84835743529902, 13.987804332345377], - [27.018009835132034, 17.63625614695775], - [30.666485508777896, 21.80593240582425], - [34.31491346435678, 26.496809249911625], - [37.96338913800264, 33.27253615294899], - [40.048239196952636, 37.442188552782], - [41.6118648116485, 41.61184095261524], - [43.17549042634437, 46.30271779670238], - [43.69666715253152, 48.908792299906736], - [44.73911604103978, 53.07844469973975], - [45.781517211481514, 56.20569592913125], - [45.781517211481514, 59.332947158522984], - [45.781517211481514, 60.89657277321863], - [45.781517211481514, 64.02382400261013], - [45.781517211481514, 67.15107523200186], - [45.781517211481514, 70.27832646139336], - [45.781517211481514, 73.40557769078487], - [45.260292767227384, 77.05405336443073], - [43.17549042634437, 81.22370576426374], - [42.13304153783565, 83.30855582321374], - [41.6118648116485, 85.39335816409675], - [40.569415923139786, 87.47820822304675], - [40.048239196952636, 88.52060939348848], - [39.527014752698506, 90.08423500818412], - [38.48461358225677, 90.60545945243848], - [38.48461358225677, 91.12668389669261] - ], - "color": "--affine-palette-line-purple", - "lineWidth": 4, - "id": "0w9mBYbIZA", - "index": "b5R", - "seed": 907857809 - }, - "fLWczUkw6x": { - "type": "brush", - "xywh": "[2516.6582144323397,1612.4734835163072,15.105804216339038,57.099677049169486]", - "points": [ - [2, 2], - [2, 5.817613248806083], - [2.3470673040696965, 10.329361105752014], - [2.6941028341489073, 16.229314630996214], - [3.388237442288755, 19.00575774158324], - [3.735272972367966, 21.782232626161203], - [4.776443110587024, 28.0292534554751], - [6.1646805528757795, 33.58213967664915], - [6.858783387024687, 36.01154725715742], - [8.594088133384048, 41.91150078240139], - [10.329361105752014, 46.770315943417245], - [11.370531243971072, 50.58792919222333], - [13.105804216339038, 55.099677049169486] - ], - "color": "--affine-palette-line-purple", - "lineWidth": 4, - "id": "fLWczUkw6x", - "index": "b5S", - "seed": 1673498523 - }, - "Hi2UpFBeuU": { - "type": "brush", - "xywh": "[2548.5874214130586,1608.3088029634312,18.229346404987155,58.4878827174673]", - "points": [ - [2, 2], - [2, 5.817613248806083], - [2, 8.941123663463031], - [3.0411701382190586, 12.758736912269114], - [4.77647488457842, 17.617552073285196], - [6.164680552876234, 22.823402764380035], - [7.899985299235141, 28.376288985554083], - [8.9411554374542, 31.152763870132276], - [10.329361105752469, 36.35861456122734], - [11.370531243971527, 40.176227810033424], - [12.758768686260282, 43.99384105883928], - [13.79993882447934, 48.50558891578544], - [14.8411089626984, 51.629099330442386], - [15.535211796847307, 54.05850691095043], - [15.882279100917458, 55.099677049169486], - [16.229346404987155, 56.4878827174673] - ], - "color": "--affine-palette-line-purple", - "lineWidth": 4, - "id": "Hi2UpFBeuU", - "index": "b5T", - "seed": 1918482090 - }, - "nc2y7u7TRo": { - "type": "brush", - "xywh": "[2520.8228949852155,1661.7555155426796,17.882279100917458,20.658722211504028]", - "points": [ - [12.06463407811998, 2.694134608140075], - [8.941123663463259, 4.776474884577965], - [6.858783387025142, 6.164680552876007], - [5.123510414657176, 7.899985299234913], - [4.082340276438117, 8.941155437453972], - [2.3470673040701513, 12.758768686260055], - [2, 13.799938824479113], - [2, 15.53521179684708], - [2.3470673040701513, 16.923449239136062], - [3.0411701382190586, 17.61755207328497], - [3.735272972367966, 18.311686681425044], - [5.470577718727327, 18.658722211504028], - [7.8999535252442, 17.61755207328497], - [9.982293801682317, 16.576381935066138], - [12.06463407811998, 14.841108962697945], - [13.105804216339038, 13.799938824479113], - [14.8411089626984, 10.676428409821938], - [15.882279100917458, 7.552917995164989], - [15.882279100917458, 4.776474884577965], - [15.188144492777155, 3.3882374422889825], - [14.146974354558097, 2.347067304069924], - [12.411701382190131, 2], - [11.370531243971072, 2] - ], - "color": "--affine-palette-line-purple", - "lineWidth": 4, - "id": "nc2y7u7TRo", - "index": "b5U", - "seed": 898022918 - }, - "WoPuCv0HRz": { - "type": "brush", - "xywh": "[2556.916782518811,1661.0614127085305,18.57638193506591,18.229314630996214]", - "points": [ - [8.941155437453745, 3.735272972367966], - [5.470577718726872, 6.511747856946158], - [2.694134608139848, 10.329361105752014], - [2, 12.06463407811998], - [2, 13.45287152040919], - [2.694134608139848, 14.494041658628248], - [3.7353047463589064, 15.188144492777155], - [6.1646805528757795, 15.88227910091723], - [8.594088133384048, 16.229314630996214], - [11.370531243971072, 15.88227910091723], - [12.758768686259828, 15.53521179684708], - [14.494041658627793, 14.146974354558097], - [15.535211796846852, 12.411701382190131], - [16.229346404987155, 9.635258271603107], - [16.57638193506591, 7.2058506910950655], - [16.229346404987155, 4.776443110587024], - [15.188176266768096, 3.735272972367966], - [14.494041658627793, 2.6941028341491347], - [13.105835990329979, 2], - [12.411701382189676, 2], - [11.370531243971072, 2] - ], - "color": "--affine-palette-line-purple", - "lineWidth": 4, - "id": "WoPuCv0HRz", - "index": "b5V", - "seed": 1639691852 - }, - "USf-tNzasM": { - "type": "brush", - "xywh": "[2477.834721556139,1537.6671216244983,37.58766903829155,39.11437491391257]", - "points": [ - [5.053429223070907, 2], - [5.816790896795737, 2.3816721009479807], - [9.633564321762606, 4.0992227629574245], - [15.93125008246352, 6.770979885080124], - [18.221317631809143, 7.725168873364737], - [24.519003392510513, 10.015236422710586], - [27.954104716528946, 11.541942298331378], - [31.007533939599853, 12.686976073004416], - [32.152567714272664, 13.068665645781266], - [33.870118376282335, 13.641182533117671], - [35.015152150955146, 14.022854634065652], - [35.58766903829155, 14.404526735013633], - [35.58766903829155, 14.595371521402285], - [35.58766903829155, 14.977043622350266], - [34.633462578178296, 15.740405296075096], - [32.34339502883222, 17.648800744472965], - [30.625844366823003, 18.793834519145776], - [27.19074304280411, 21.46557416943983], - [23.183124831448822, 23.94648650517388], - [18.79383451914555, 26.618226155467937], - [13.832027319505869, 29.862482693098173], - [9.44271953537418, 32.53423981522087], - [5.6259461104073125, 35.01513467912628], - [3.145033774672811, 36.541858026576165], - [2, 37.11437491391257] - ], - "color": "--affine-palette-line-purple", - "lineWidth": 4, - "id": "USf-tNzasM", - "index": "b5W", - "seed": 953924125 - }, - "xk7ZrYqOZ4": { - "type": "brush", - "xywh": "[2475.162981905845,1553.3159223689713,37.01513467912628,10.679357861649123]", - "points": [ - [2, 8.679357861649123], - [3.5267058756212464, 8.297685760700915], - [8.67935786164935, 6.961807199639679], - [12.496148758445088, 6.389290312303274], - [14.213699420454304, 6.198445525914849], - [16.3129221834115, 5.816773424966868], - [21.84724627038804, 5.053411751241811], - [26.99989825641614, 4.0992227629574245], - [30.053327479486597, 3.335861089232367], - [32.15255024244425, 2.7633442018959613], - [33.106756702557504, 2.3816721009479807], - [34.44261779178987, 2], - [35.01513467912628, 2] - ], - "color": "--affine-palette-line-purple", - "lineWidth": 4, - "id": "xk7ZrYqOZ4", - "index": "b5X", - "seed": 941503918 - }, - "pVWqHrvzmZ": { - "type": "brush", - "xywh": "[2524.7811063177287,1529.651885201788,38.73270281296436,29.954104716529173]", - "points": [ - [25.85488195357175, 2], - [23.3739696178377, 4.0992227629574245], - [18.221317631809598, 9.061047434425973], - [14.213699420454304, 12.686976073004189], - [12.496148758444633, 14.404526735013633], - [9.0610474344262, 17.457955958084312], - [6.198462997743718, 19.74802350743016], - [3.5267233474496607, 22.03809105677601], - [2.190844786388425, 22.992280045060625], - [2, 23.18312483144905], - [2.190844786388425, 23.373969617837474], - [3.335878561061236, 23.56479693239703], - [4.290067549346077, 23.56479693239703], - [7.343496772416529, 23.56479693239703], - [11.351114983771822, 23.755641718785455], - [15.93125008246352, 23.94648650517388], - [20.8930572821032, 24.519003392510285], - [25.664037167183324, 25.091520279846918], - [27.9541047165294, 25.4731923807949], - [31.770878141495814, 26.427381369079285], - [34.82430736456672, 27.190743042804115], - [36.73270281296436, 27.954104716529173] - ], - "color": "--affine-palette-line-purple", - "lineWidth": 4, - "id": "pVWqHrvzmZ", - "index": "b5Y", - "seed": 1612042689 - }, - "mIALZeG_Rq": { - "type": "brush", - "xywh": "[2530.1246030901452,1543.0106183969149,42.54947623793123,9.915996187924293]", - "points": [ - [2, 7.534324086976312], - [2.954188988284386, 7.725168873364737], - [6.389290312303274, 7.915996187924293], - [13.832009847677455, 7.343479300587887], - [19.557178721041964, 6.198445525914849], - [24.90067549345849, 5.053411751242038], - [32.15255024244425, 3.526705875621019], - [35.96934113923953, 2.7633442018961887], - [39.213597676869995, 2.190827314559783], - [40.54947623793123, 2] - ], - "color": "--affine-palette-line-purple", - "lineWidth": 4, - "id": "mIALZeG_Rq", - "index": "b5Z", - "seed": 550631409 - }, - "JUsmRjwfVD": { - "type": "brush", - "xywh": "[2517.7200763551314,1566.102121204933,21.557178721041964,34.91591191616908]", - "points": [ - [3.5267058756212464, 21.465574169439833], - [2.954188988284841, 19.557196192870833], - [2.3816721009484354, 18.030472845420945], - [2, 14.02285463406588], - [2.1908273145600106, 10.206081209099011], - [2.3816721009484354, 9.061047434425973], - [3.1450337746732657, 6.961824671468776], - [3.908377976569227, 5.244274009459332], - [5.6259286385784435, 3.1450337746730384], - [11.160270197383397, 2], - [14.02285463406588, 3.335878561061463], - [15.740405296075096, 5.6259461104073125], - [18.412144946369153, 12.877820859392841], - [18.98466183370556, 16.69461175618858], - [19.36633393465354, 18.793834519146003], - [19.557178721041964, 22.419780629553088], - [19.36633393465354, 25.854881953571976], - [18.793817047317134, 28.71746639025423], - [17.839628059032748, 30.435017052263674], - [17.076266385307918, 31.77087814149604], - [16.12207739702353, 32.53423981522087], - [14.595371521402285, 32.91591191616908], - [13.068648173952624, 32.725084601609524], - [11.351097511943408, 31.961722927884466], - [9.251874748985756, 30.24417226587525], - [6.961807199640134, 26.809070941856362], - [5.053411751242038, 22.992297516889494], - [2.3816721009484354, 14.40454420684273] - ], - "color": "--affine-palette-line-purple", - "lineWidth": 4, - "id": "JUsmRjwfVD", - "index": "b5a", - "seed": 407818161 - }, - "OPzi2nhJWc": { - "type": "brush", - "xywh": "[2538.719303837619,1554.3168680434264,81.65754394099167,89.85259938173431]", - "points": [ - [2, 2], - [5.121936090030886, 4.341434203797689], - [9.024302611393068, 7.073119350711977], - [11.755952030856863, 9.804786633900903], - [14.487672905221642, 13.316937939597436], - [16.04860522278659, 15.658372143395352], - [18.78025464224993, 19.560756528483125], - [21.90219073228082, 24.243624936078504], - [24.633840151744607, 28.146027184891636], - [27.36556102610939, 32.04841156997941], - [28.536278128008234, 33.60936175126949], - [30.487425661238834, 36.34104689818378], - [32.82885986503652, 39.0727141813727], - [35.5605807394013, 42.19463240767823], - [40.633664362662785, 47.267733894664836], - [45.706747985924274, 52.34083538165168], - [51.95062016598558, 58.584671834262515], - [55.85298668734822, 62.48707408307563], - [59.75535320871086, 66.38944060443805], - [64.04800640064013, 69.90159191013481], - [67.95037292200277, 73.02352800016547], - [71.4625242276993, 76.14542836274563], - [74.19424510206454, 78.87711350965992], - [75.75517741962904, 80.82829677034101], - [78.09661162342672, 83.95019713292118], - [79.26732872532557, 86.68188227983546], - [79.65754394099167, 87.85259938173431] - ], - "color": "--affine-palette-line-blue", - "lineWidth": 4, - "id": "OPzi2nhJWc", - "index": "b5b", - "seed": 1466550751 - }, - "ac_kl-cvQK": { - "type": "brush", - "xywh": "[2548.865542539043,1558.6095033716306,74.6332413295986,83.99899600851472]", - "points": [ - [2, 2], - [3.560932317564948, 3.95120112440668], - [6.292581737028286, 7.853585509494451], - [11.75595203085686, 15.658372143395354], - [16.43882043845224, 20.731473630382197], - [17.609537540351084, 22.29242381167205], - [22.682621163612566, 27.36554316238426], - [25.414342037977352, 29.70697736618195], - [28.14599145744114, 32.048411569979635], - [29.316708559339986, 32.82887772876211], - [31.658142763137676, 34.78007885316856], - [33.999576966935365, 37.51174613635749], - [37.511728272632354, 41.02389744205425], - [40.633664362662785, 44.926299690867374], - [44.14581566835932, 49.999401177854], - [46.09696320159037, 52.34083538165169], - [50.77983160918575, 58.974904913653745], - [52.731050597317335, 60.926088174335064], - [56.24320190301387, 65.60895658193067], - [59.75535320871086, 69.90160977386017], - [62.487002628174196, 73.41376107955693], - [65.60893871820508, 76.53566144213687], - [67.95037292200277, 78.87709564593479], - [69.90159191013481, 80.82827890661588], - [71.46252422769976, 81.60878079284885], - [72.2430261139325, 81.99899600851472], - [72.6332413295986, 81.99899600851472] - ], - "color": "--affine-palette-line-blue", - "lineWidth": 4, - "id": "ac_kl-cvQK", - "index": "b5c", - "seed": 774145730 - }, - "QasEppRUkM": { - "type": "brush", - "xywh": "[2475.5005803350796,1569.9264413112282,23.511975516615166,101.94998561638909]", - "points": [ - [21.511975516615166, 2], - [21.511975516615166, 3.9511832606813186], - [20.73147363038197, 9.804768770175542], - [19.560756528483125, 17.219322324685436], - [19.170541312817477, 19.170505585366755], - [18.780254642250384, 21.511939789164444], - [17.999824210918632, 23.463140913570896], - [17.60953754035154, 25.414342037977576], - [16.438820438452694, 29.31672642306535], - [15.26810333655385, 33.21911080815312], - [14.877888120887746, 34.38982791005196], - [14.097386234655005, 37.901979215748725], - [13.316955803322799, 42.194614543952866], - [12.92666913275616, 45.70678371337499], - [12.92666913275616, 49.21893501907152], - [12.536453917090057, 53.51155248355054], - [12.146238701423954, 57.80416994802954], - [12.146238701423954, 60.14560415182723], - [12.146238701423954, 65.6089744456558], - [11.75595203085686, 76.53564357841151], - [10.585234928958016, 83.95019713292118], - [8.63408739572742, 89.41353169929926], - [7.853585509494679, 91.75496590309695], - [6.682868407595834, 93.70618489122876], - [4.341434203797689, 96.82808525380892], - [2.7805018862331963, 99.16951945760661], - [2, 99.94998561638909] - ], - "color": "--affine-palette-line-blue", - "lineWidth": 4, - "id": "QasEppRUkM", - "index": "b5d", - "seed": 540644783 - }, - "WeQ6-uPUcU": { - "type": "brush", - "xywh": "[2480.963950628908,1568.3654732662128,24.682621163612566,113.65719236282848]", - "points": [ - [22.682621163612566, 2], - [22.682621163612566, 6.682868407595606], - [22.292405947946918, 15.268139064004117], - [21.902190732280815, 18.39003942658428], - [21.121688846048073, 25.02410895858634], - [20.73147363038197, 31.267927547472024], - [19.560756528483125, 35.950795955067406], - [18.39003942658428, 40.243431283271775], - [17.609537540351084, 42.19463240767823], - [16.829035654118343, 46.09703465649159], - [16.43882043845224, 49.21893501907152], - [15.658318552219498, 53.121337267884655], - [15.268103336553395, 57.80420567548026], - [14.877888120887746, 61.70657219684268], - [14.877888120887746, 64.82850828687356], - [14.877888120887746, 66.38944060443828], - [14.877888120887746, 68.73087480823597], - [14.877888120887746, 71.07230901203366], - [14.877888120887746, 74.19424510206431], - [14.09738623465455, 76.92589452152811], - [13.316884348421809, 80.04783061155877], - [11.365736815191212, 85.90141612105322], - [7.073083623261482, 95.65736815191008], - [6.292581737028286, 97.21833619692552], - [4.341434203797689, 100.73048750262205], - [3.1707171018988447, 103.85238786520222], - [2, 106.5840730121165], - [2, 108.9255072159142], - [2, 110.48647526092964], - [2, 111.65719236282848] - ], - "color": "--affine-palette-line-blue", - "lineWidth": 4, - "id": "WeQ6-uPUcU", - "index": "b5e", - "seed": 661083165 - }, - "f0rq1haE2k": { - "type": "brush", - "xywh": "[2487.988253240301,1576.1702599001137,13.75595203085686,61.75537107243622]", - "points": [ - [11.75595203085686, 2], - [10.195019713292368, 7.463352430103214], - [7.463298838927585, 18.39003942658428], - [6.2925817370287405, 26.19482606048541], - [5.121864635129896, 30.87769446808079], - [3.1707171018988447, 44.14583353208491], - [2.390215215666103, 53.90178556294177], - [2, 59.75537107243622] - ], - "color": "--affine-palette-line-blue", - "lineWidth": 4, - "id": "f0rq1haE2k", - "index": "b5f", - "seed": 1070973809 - }, - "BtsxwZmM1Q": { - "type": "brush", - "xywh": "[2556.4541226654687,-38.34953763537584,103.63189820981097,20.40352156760781]", - "points": [ - [5, 15.403521567607811], - [8.715528838335558, 15.403521567607811], - [25.0639781878167, 14.66042260331875], - [30.26577298851089, 13.917323639029632], - [40.66929455611853, 13.174190657850431], - [46.61415430421175, 12.431091693561314], - [52.5590820860848, 10.944859748093052], - [63.705736634871755, 10.201760783803934], - [74.85239118365871, 8.715528838335615], - [83.02658184150914, 7.972429874046497], - [89.71457457078168, 7.2293309097574365], - [94.1732363902961, 5.743098964289118], - [95.65943431887445, 5], - [97.88876522863166, 5], - [98.63189820981097, 5] - ], - "color": "--affine-palette-line-white", - "lineWidth": 10, - "id": "BtsxwZmM1Q", - "index": "b6z", - "seed": 940952658 - }, - "fk5hOd3-F4": { - "type": "brush", - "xywh": "[2592.8665502027666,-13.08379866375526,21.88971949618599,133.35629900094636]", - "points": [ - [16.88971949618599, 5], - [16.88971949618599, 9.458661819514816], - [16.146654548786955, 13.917323639029632], - [13.917323639029291, 39.183062610650154], - [13.174190657850431, 45.87105533992241], - [12.431057676671116, 53.302181050373804], - [11.687992729272082, 68.16436443749643], - [10.944859748092767, 75.59549014794788], - [10.201726766913907, 81.54034989604088], - [8.715528838335558, 94.91633535458533], - [7.972395857156243, 106.80612288455154], - [7.229330909757209, 111.26478470406636], - [6.486197928578349, 119.43897536191679], - [5, 125.38386912689987], - [5, 127.61323405354739], - [5, 128.35629900094636] - ], - "color": "--affine-palette-line-white", - "lineWidth": 10, - "id": "fk5hOd3-F4", - "index": "b70", - "seed": 517781008 - }, - "fKZjO4N_6x": { - "type": "brush", - "xywh": "[2648.599822946702,11.4388413435762,99.1732363902961,86.54034989604088]", - "points": [ - [8.715528838335558, 5], - [6.486197928577894, 12.431091693561314], - [5, 25.063978187816645], - [5, 35.46753377231454], - [5.7430649473985795, 40.66929455611847], - [7.972395857156243, 52.5590480691946], - [13.174190657850431, 63.705702617981615], - [16.146654548786955, 68.16436443749643], - [20.60531636830183, 75.59549014794788], - [24.320845206637387, 80.05415196746264], - [27.29330909757391, 81.54034989604088], - [31.00883793590947, 80.05415196746264], - [35.46749975542434, 76.33858911223695], - [38.439963646360866, 71.87992729272213], - [47.35728728539061, 59.2470407984668], - [56.27461092442036, 43.64172443016497], - [62.219470672513125, 30.26573897162052], - [66.678132492028, 19.86221740401271], - [69.65059638296452, 11.687992729272196], - [71.13679431154287, 7.972429874046497], - [71.13679431154287, 6.486231945468319], - [71.13679431154287, 5.743098964289118], - [69.65059638296452, 7.22933090975738], - [65.93499951084868, 17.63288649425533], - [61.47633769133381, 28.779541043042343], - [57.76080885299825, 51.815949104905485], - [57.76080885299825, 59.2470407984668], - [59.990139762755916, 66.67816650891825], - [68.90746340178566, 69.65059638296475], - [71.13679431154287, 68.90749741867563], - [77.82478704081495, 67.42126547320731], - [81.54031587915051, 65.19193456344993], - [87.48524366102401, 61.476371708224235], - [90.45763951818026, 59.990173779646], - [94.1732363902961, 57.01770988870942] - ], - "color": "--affine-palette-line-white", - "lineWidth": 10, - "id": "fKZjO4N_6x", - "index": "b71", - "seed": 925352227 - }, - "Lsu7vyerFn": { - "type": "brush", - "xywh": "[2737.773059336998,-15.313129573512697,69.44880158227079,89.51281378697752]", - "points": [ - [13.174190657850431, 20.605316368301885], - [12.431057676671116, 39.18306261065021], - [12.431057676671116, 46.61418832110161], - [12.431057676671116, 54.78837897895204], - [11.687992729272082, 61.476371708224235], - [10.944859748093222, 73.3661592381905], - [9.458661819514873, 80.0541519674627], - [8.715528838335558, 83.76968080579832], - [7.229330909757209, 84.51281378697752], - [5.743064947399034, 84.51281378697752], - [5.743064947399034, 83.76968080579832], - [5, 83.0265818415092], - [5, 81.54034989604088], - [5, 77.82482105770532], - [7.229330909757209, 71.13682832843307], - [11.687992729272082, 61.476371708224235], - [15.40352156760764, 54.78837897895204], - [24.320845206637387, 38.43996364636109], - [33.98130182684599, 25.807077152105762], - [42.89862546587574, 15.403555584497951], - [51.815949104905485, 7.972429874046554], - [57.76080885299871, 5], - [61.476337691334265, 5], - [62.96260365369244, 5], - [64.44880158227079, 10.944893764983135] - ], - "color": "--affine-palette-line-white", - "lineWidth": 10, - "id": "Lsu7vyerFn", - "index": "b72", - "seed": 143102530 - }, - "LwpBPRaMZm": { - "type": "brush", - "xywh": "[2792.763199099754,-6.3958059344830644,74.6506644167448,99.17323639029621]", - "points": [ - [21.348449349481143, 8.715562855225699], - [19.862183387122514, 13.174224674740515], - [17.632852477365304, 28.779541043042343], - [13.917323639029746, 45.12795637563329], - [10.944859748092767, 56.27461092442036], - [8.715528838335558, 62.219504689403436], - [7.229330909757209, 65.19193456344993], - [5.743132981179315, 65.93503352773905], - [5, 65.93503352773905], - [5, 64.44883559916087], - [7.972463890936524, 54.78837897895204], - [14.660456620209061, 41.41239352040759], - [22.091514296880177, 27.29330909757408], - [25.0639781878167, 22.091548313770147], - [31.00883793590947, 14.66042260331875], - [34.72443480802531, 10.944893764983135], - [41.41242753729739, 6.486231945468319], - [48.84348521396896, 5], - [55.53147794324104, 5.743098964289118], - [60.73334077771551, 7.972429874046497], - [62.96267168747272, 9.458661819514816], - [66.678132492028, 15.403555584497951], - [68.90746340178566, 27.29330909757408], - [69.6506644167448, 41.41239352040759], - [68.16439845438663, 59.247040798466855], - [67.4213335069876, 73.36615923819045], - [66.678132492028, 83.76968080579832], - [66.678132492028, 91.20080651624971], - [66.678132492028, 93.43013742600715], - [66.678132492028, 94.17323639029621] - ], - "color": "--affine-palette-line-white", - "lineWidth": 10, - "id": "LwpBPRaMZm", - "index": "b73", - "seed": 1739090864 - }, - "XxoVbwP1r3": { - "type": "brush", - "xywh": "[2925.779920704019,-7.882037879951383,70.93513557840924,88.0265818415092]", - "points": [ - [16.146654548786955, 33.98130182684628], - [16.88985556374655, 30.265772988510662], - [19.862251420902794, 22.091548313770147], - [24.320913240417667, 16.14665454878707], - [33.98130182684645, 7.972463890936638], - [42.155560518476705, 5], - [49.586618195148276, 5.743132981179258], - [53.302215067264115, 7.2293309097574365], - [58.50394183417757, 10.201794800694017], - [59.9902077965362, 12.431125710451454], - [63.705804668652036, 19.11911843972365], - [65.93513557840924, 31.751970917088897], - [65.1919345634501, 45.127956375633346], - [61.47647375889483, 58.50394183417774], - [55.531545977021324, 68.90749741867569], - [48.84355324774924, 75.59549014794788], - [40.66929455611853, 80.79725093175182], - [36.21063273660366, 81.54038391293102], - [28.036510112533506, 83.0265818415092], - [20.60531636830183, 81.54038391293102], - [14.660524653988887, 78.56792002199438], - [9.458661819514873, 71.13682832843307], - [5.743201014959595, 59.99017377964606], - [5, 54.78841299584212], - [5.743201014959595, 40.66929455611853], - [7.9725319247168045, 33.98130182684628], - [10.201862834474014, 29.52264000733146], - [12.431193744231678, 26.550210133284963], - [13.917323639029746, 25.0639781878167] - ], - "color": "--affine-palette-line-white", - "lineWidth": 10, - "id": "XxoVbwP1r3", - "index": "b4g", - "seed": 937814705 - }, - "W2Yv8XGQ2J": { - "type": "brush", - "xywh": "[3004.5497035604876,-13.826897628044378,71.67806445824772,95.45767353507051]", - "points": [ - [17.63278444358457, 24.320845206637443], - [16.88971949618599, 29.52264000733146], - [14.66038858642878, 42.89862546587591], - [11.687992729272082, 57.01770988870942], - [8.715460804555278, 68.90746340178555], - [7.972395857156243, 72.62302625701125], - [6.486129894798069, 77.82478704081518], - [5, 79.3110189862835], - [5, 77.82478704081518], - [6.486129894798069, 68.90746340178555], - [25.80704313521528, 28.036408061863142], - [41.41235950351711, 11.687992729272253], - [45.1279563756334, 9.458661819514816], - [51.815949104905485, 5.743098964289118], - [57.01767587181894, 5], - [59.2470067815766, 6.486197928578235], - [60.73327274393523, 7.972429874046497], - [64.44873354849051, 15.403521567607811], - [66.67806445824772, 31.008837935909696], - [66.67806445824772, 47.357287285390726], - [64.44873354849051, 62.962603653692554], - [63.705668601091475, 75.5954561310578], - [62.96260365369244, 83.76968080579832], - [62.96260365369244, 86.74211067984481], - [62.96260365369244, 88.9714415896022], - [62.96260365369244, 90.45767353507051] - ], - "color": "--affine-palette-line-white", - "lineWidth": 10, - "id": "W2Yv8XGQ2J", - "index": "b4h", - "seed": 1545395480 - }, - "w9_u4MCAY6": { - "type": "brush", - "xywh": "[2606.98560060871,197.95953879890914,79.85239118365871,21.14665454878707]", - "points": [ - [5, 16.14665454878707], - [8.715596872115839, 16.14665454878707], - [22.834647278059492, 13.917323639029632], - [30.26577298851089, 12.431125710451397], - [33.23823687944741, 12.431125710451397], - [39.926229608719495, 10.944859748092995], - [47.35728728539061, 9.45866181951476], - [51.072884157506905, 7.972463890936638], - [54.788412995842464, 7.2293309097574365], - [59.9902077965362, 5.743132981179201], - [62.21953870629386, 5.743132981179201], - [64.44886961605107, 5.743132981179201], - [65.93506754462942, 5.743132981179201], - [68.16439845438663, 5.743132981179201], - [69.65059638296498, 5.743132981179201], - [70.39372936414429, 5.743132981179201], - [72.6230602739015, 5.743132981179201], - [74.85239118365871, 5] - ], - "color": "--affine-palette-line-white", - "lineWidth": 10, - "id": "w9_u4MCAY6", - "index": "b74", - "seed": 847280562 - }, - "uVMe6UaTm_": { - "type": "brush", - "xywh": "[2625.5633808679486,144.45559696473146,58.30214703348338,143.01672160426506]", - "points": [ - [43.641758447055054, 5], - [41.41242753729739, 9.45866181951476], - [31.00883793590947, 36.95376571778286], - [10.944859748092767, 94.91636937147541], - [5.74313298117886, 120.1820743262058], - [5, 128.3563330178365], - [5.74313298117886, 133.5580597847503], - [7.229330909757209, 135.0443257471087], - [10.201794800693733, 137.27365665686602], - [17.632852477365304, 138.01672160426506], - [27.29330909757391, 132.81499483735126], - [38.439963646360866, 123.89767119832163], - [48.84348521396896, 112.75101664953456], - [53.30214703348338, 107.5492218488406] - ], - "color": "--affine-palette-line-white", - "lineWidth": 10, - "id": "uVMe6UaTm_", - "index": "b75", - "seed": 2012563762 - }, - "7ntelMzJfo": { - "type": "brush", - "xywh": "[2677.581124773548,130.33647852500775,74.65059638296498,149.70478236731753]", - "points": [ - [52.55901405230452, 5], - [44.38482339445409, 22.09158233066023], - [35.46749975542434, 45.87108935681249], - [28.0363740449734, 71.87992729272219], - [17.632852477365304, 106.80615690144168], - [11.687992729272537, 126.87013508925827], - [7.972395857156243, 139.50298756662357], - [5, 144.70478236731753], - [6.486197928578349, 140.24612054780278], - [25.0639781878167, 103.83369301050504], - [29.522640007331574, 94.91636937147541], - [39.183028593760355, 82.28351689411022], - [46.61415430421175, 71.87992729272219], - [48.84348521396896, 68.90753143556583], - [54.78834496206218, 62.962603653692554], - [59.247006781577056, 60.73327274393512], - [62.21947067251358, 59.9902077965362], - [64.44880158227079, 62.21953870629352], - [67.42126547320731, 71.87992729272219], - [69.65059638296498, 86.74217871362498], - [68.90746340178566, 95.65950235265461], - [66.678132492028, 111.26481872095644], - [63.705668601091475, 126.12700210807907], - [60.73327274393523, 136.53052367568694], - [59.247006781577056, 141.7323184763809], - [59.247006781577056, 142.4754514575602] - ], - "color": "--affine-palette-line-white", - "lineWidth": 10, - "id": "7ntelMzJfo", - "index": "b76", - "seed": 977720318 - }, - "eqPO6DfJdl": { - "type": "brush", - "xywh": "[2748.1765809046055,175.6662297013351,96.94397351431917,99.91636937147541]", - "points": [ - [32.4951038982681, 48.10042026656993], - [43.641758447055054, 46.614154304211525], - [48.10042026656993, 45.12795637563329], - [60.73327274393523, 36.95376571778286], - [66.67813249202845, 28.77950702615226], - [69.65059638296498, 19.862183387122627], - [68.90746340178566, 11.687992729272196], - [68.90746340178566, 9.45866181951476], - [66.67813249202845, 6.486197928578122], - [63.705736634871755, 5], - [60.73327274393523, 5], - [54.04528001466315, 8.715528838335558], - [40.66929455611853, 22.834647278059265], - [26.55017611639505, 42.15549248469665], - [16.14665454878741, 59.990139762755916], - [11.687992729272082, 68.16439845438651], - [6.486197928578349, 80.0541179505725], - [5, 88.22837664220322], - [6.486197928578349, 91.94390548053877], - [11.687992729272082, 94.91636937147541], - [24.320845206637387, 94.91636937147541], - [43.641758447055054, 91.20077249935957], - [61.476405725114546, 84.51277977008738], - [70.39372936414384, 80.7972509317517], - [82.28344886033028, 74.85239118365871], - [91.94397351431917, 69.65059638296475] - ], - "color": "--affine-palette-line-white", - "lineWidth": 10, - "id": "eqPO6DfJdl", - "index": "b77", - "seed": 24484761 - }, - "_LdRTPaDnP": { - "type": "brush", - "xywh": "[2899.7363238579596,100.61496606121042,118.09674126738173,130.48601517097805]", - "points": [ - [36.90247494282494, 125.48601517097805], - [36.283019045785295, 123.0081632259461], - [35.973276918829015, 120.84002505099735], - [34.73433676787636, 112.16750070807566], - [34.114880870836714, 109.99939089000017], - [33.185682846840336, 106.28259879401563], - [32.56622694980069, 103.18526259507072], - [31.94674269588768, 100.39766852308233], - [31.63702892580477, 98.53927247509009], - [31.327286798848036, 97.61007445109394], - [31.327286798848036, 96.37113430014134], - [31.327286798848036, 95.75167840310164], - [31.327286798848036, 95.13222250606199], - [31.327286798848036, 94.82248037910549], - [31.327286798848036, 94.20302448206584], - [31.327286798848036, 93.89328235510939], - [30.707830901808393, 93.58354022815288], - [30.398088774852113, 93.58354022815288], - [30.088346647895833, 93.58354022815288], - [28.539692726859812, 92.96408433111324], - [27.61049470286389, 92.65434220415673], - [25.442356527914853, 92.03488630711709], - [23.274246709839645, 91.10568828312094], - [21.10610853489061, 89.86674813216834], - [18.937970359942028, 88.627836338089], - [16.769860541866365, 86.45969816314025], - [14.601722366917784, 83.67210409115185], - [12.12387042188584, 80.26505412212381], - [9.95573224693726, 76.8579757962224], - [9.026534222940882, 74.99957974823016], - [7.477880301905316, 71.28278765224562], - [5.929198023995923, 67.25628178617788], - [5, 59.822697594208734], - [5.30974212695628, 52.389113402239644], - [5.929198023995923, 47.74312328225898], - [7.477880301905316, 43.406846932361475], - [10.26547437389354, 36.592746994305315], - [11.504386167972825, 34.424608819356564], - [13.053068445881763, 32.56621277136429], - [16.15037628795335, 28.849420675379747], - [20.176910510894686, 24.822886452438723], - [24.82290063087521, 20.796380586370987], - [29.468890750855735, 17.699044387426085], - [34.424622997792994, 14.911450315437662], - [36.90247494282494, 13.36278221596524], - [42.16792095984556, 10.884916092496638], - [47.4333953337391, 8.716792095984545], - [52.69884135075927, 7.168123996512094], - [57.654573597696526, 5.6194700754763005], - [62.30056371767705, 5.309727948519821], - [66.94655383765803, 5], - [71.28280183068227, 5.309727948519821], - [81.19423796768342, 6.5486680994724225], - [86.1499985714936, 7.787594071988394], - [88.31810838956926, 8.407064147464723], - [92.65438473946642, 10.265460195456996], - [97.91980239961367, 13.98225229144154], - [102.8755630034243, 18.008786514382564], - [104.4241885675865, 19.557440435418357], - [107.21178263957518, 22.964490404446423], - [109.07017868756748, 26.681282500430996], - [110.92857473555978, 30.088360826332348], - [112.16754324338535, 33.495410795360414], - [112.78697078355208, 36.90246076438851], - [113.09674126738173, 40.30953909028986], - [113.09674126738173, 43.716589059317954], - [113.09674126738173, 45.26524298035375], - [112.78697078355208, 48.36257917929862], - [112.16754324338535, 51.45991537824352], - [111.54805898947279, 54.247509450231945], - [110.61886096547642, 56.41561926830738], - [109.68966294148004, 58.89349957021261], - [107.83126689348819, 62.610291666197185], - [107.21178263957518, 63.84920346027647], - [106.5923550994089, 64.77840148427262], - [104.73395905141615, 66.94653965922137], - [102.8755630034243, 68.80493570721367], - [101.017166955432, 70.66333175520592], - [99.1587709074397, 72.21198567624171], - [97.3003748594474, 73.45092582719437], - [95.4419788114551, 74.68986597814697], - [93.5835827634628, 75.92877777222631], - [91.7251867154705, 76.8579757962224], - [90.79598869147458, 77.47746005013536], - [89.24730641356518, 78.40665807413151], - [87.69862413565625, 79.33585609812766], - [86.1499985714936, 80.26505412212381], - [84.60131629358466, 81.19425214611991], - [83.362376142632, 82.12345017011606], - [81.81372222159598, 82.7429060671557], - [81.19423796768342, 83.3623619641954], - [79.95532617360368, 84.601302115148], - [78.09693012561138, 87.07915406017995], - [77.16773210161546, 88.0083520841761], - [76.23853407761953, 89.55703436208515], - [75.61904982370652, 91.41543041007745], - [74.99959392666688, 92.96408433111324], - [74.68985179971014, 95.13222250606199], - [74.68985179971014, 96.06142053005814], - [74.68985179971014, 98.53927247509009], - [74.68985179971014, 101.01712442012197], - [74.68985179971014, 103.80471849211042], - [74.68985179971014, 106.59231256409882], - [74.68985179971014, 109.68964876304372], - [74.99959392666688, 112.16750070807566], - [74.99959392666688, 114.02589675606791], - [74.99959392666688, 114.95509478006406], - [74.99959392666688, 115.88429280406021], - [74.99959392666688, 117.43297508196932], - [74.99959392666688, 119.29137112996156], - [74.99959392666688, 120.5302829240409], - [74.99959392666688, 121.7692230749935], - [74.99959392666688, 122.38867897203315], - [74.99959392666688, 122.69842109898966] - ], - "color": "--affine-palette-line-yellow", - "lineWidth": 10, - "id": "_LdRTPaDnP", - "index": "b4m", - "seed": 328056657 - }, - "tgAoWM0n5I": { - "type": "brush", - "xywh": "[2917.3910863138863,119.1989407195698,80.61904982370652,107.56579251959448]", - "points": [ - [26.991038805824246, 80.26503994368716], - [31.327286798848036, 79.02609979273456], - [35.973276918829015, 78.40664389569486], - [39.07061311777397, 78.0969017687384], - [40.30952491185326, 78.0969017687384], - [41.23872293584918, 78.0969017687384], - [39.690069014813616, 83.36234778575874], - [35.04407889483264, 90.17647608068819], - [32.25648482284441, 93.27381227963309], - [31.63702892580477, 93.89326817667273], - [30.707830901808393, 94.20301030362924], - [30.398088774852113, 94.20301030362924], - [31.63702892580477, 94.51272407371243], - [34.114880870836714, 94.82246620066888], - [36.90247494282494, 94.82246620066888], - [38.141415093777596, 94.82246620066888], - [39.690069014813616, 96.37112012170468], - [39.38032688785688, 96.68086224866119], - [37.831672966821316, 97.91980239961379], - [35.973276918829015, 99.15871419369307], - [33.49542497379707, 100.39765434464573], - [32.875940719884056, 100.70739647160218], - [32.25648482284441, 101.32685236864182], - [32.56622694980069, 101.32685236864182], - [34.114880870836714, 101.32685236864182], - [35.66353479187228, 101.32685236864182], - [38.45112886386096, 101.32685236864182], - [40.30952491185326, 101.32685236864182], - [41.54846506280592, 101.32685236864182], - [42.16792095984556, 101.32685236864182], - [41.23872293584918, 101.32685236864182], - [39.690069014813616, 101.63659449559833], - [38.76087099081724, 101.94630826568152], - [36.90247494282494, 102.56579251959448], - [35.66353479187228, 102.56579251959448], - [35.04407889483264, 102.56579251959448], - [34.73433676787636, 102.56579251959448], - [35.35382102178937, 100.08791221768922], - [37.831672966821316, 93.27381227963309], - [39.9998111417699, 86.1499418577472], - [41.23872293584918, 79.6455556897742], - [41.23872293584918, 76.8579616177858], - [41.23872293584918, 64.15893140879632], - [42.16792095984556, 52.698812993886236], - [42.16792095984556, 50.22096104885429], - [42.47766308680184, 45.5749709288736], - [42.16792095984556, 45.26522880191709], - [41.23872293584918, 44.64577290487745], - [39.9998111417699, 44.955515031833954], - [35.66353479187228, 47.12362484990939], - [30.707830901808393, 50.53070317581074], - [26.37155455191123, 53.93775314483884], - [23.274246709839645, 56.10589131978759], - [21.725564431930252, 56.41560508987078], - [21.10610853489061, 53.93775314483884], - [21.725564431930252, 49.60150515181459], - [25.13264275783149, 37.831644609948], - [28.849434853816092, 27.300752575907325], - [31.327286798848036, 20.48662428097788], - [31.63702892580477, 17.079574311949784], - [31.017544671891756, 16.460090058036855], - [27.610494702863434, 19.24768413002525], - [19.86716838393795, 27.61046634599049], - [16.460118414910085, 31.32725844197506], - [10.575188143976902, 37.831644609948], - [7.168138174948581, 41.23872293584935], - [5.619484253913015, 42.477634729928695], - [5, 42.477634729928695], - [6.548682277908938, 36.592732815868686], - [12.12387042188584, 25.132614400958573], - [19.24771248689831, 14.601694010044582], - [21.725564431930252, 10.575188143976845], - [25.13264275783149, 6.238911794079343], - [26.06184078182787, 5], - [26.06184078182787, 5], - [23.274246709839645, 9.645990119980695], - [17.699058565862742, 18.318486106029127], - [12.743326318925483, 25.132614400958573], - [9.645990119980524, 29.77860452093924], - [8.716792095984601, 31.32725844197506], - [7.168138174948581, 32.25645646597118], - [10.884930270933182, 26.371554551911174], - [20.486652637850966, 14.911436137001033], - [23.274246709839645, 11.81409993805616], - [27.92023682982017, 7.1681098180754645], - [30.398088774852113, 5.309713770083192], - [30.707830901808393, 5.309713770083192], - [31.017544671891756, 6.238911794079343], - [28.539692726859812, 15.530892034040704], - [22.96450458288291, 28.53966436998664], - [18.318514462902385, 39.07058476090063], - [15.840662517870442, 46.19442682591324], - [15.221178263957427, 49.29176302485814], - [15.530920390913707, 49.60150515181459], - [17.079574311949727, 48.6723071278185], - [26.06184078182787, 40.9289808088929], - [37.52193083986458, 28.53966436998664], - [47.4333953337391, 18.008772335945935], - [53.628039374755645, 11.81409993805616], - [56.10589131978759, 9.336247993024216], - [56.10589131978759, 11.194644041016488], - [47.123653206782365, 28.53966436998664], - [43.40686111079776, 34.734336767876414], - [37.21221706978167, 45.88471305583005], - [34.73433676787636, 50.53070317581074], - [31.327286798848036, 58.274001137863024], - [31.017544671891756, 59.51294128881568], - [31.327286798848036, 59.203199161859175], - [35.973276918829015, 53.93775314483884], - [40.92900916576582, 47.74310910382235], - [50.530703175810686, 34.734336767876414], - [59.51296964568883, 23.893674250005944], - [66.32706958374501, 16.460090058036855], - [70.35363216355927, 12.743297962052281], - [71.28283018755519, 11.81409993805616], - [70.66334593364263, 13.982238113004911], - [64.46867353575271, 26.061812424954695], - [60.13242554272847, 33.185654489967334], - [52.69884135075927, 45.88471305583005], - [46.813911079826084, 57.34480311386693], - [46.19445518278644, 59.203199161859175], - [46.19445518278644, 59.82268341577213], - [49.2917913817314, 57.96428736777983], - [57.344831470740246, 50.53070317581074], - [65.70761368670537, 41.85817883288905], - [72.21202821155157, 36.592732815868686], - [74.07042425954387, 34.734336767876414], - [75.61904982370652, 33.80513874388026], - [75.30933605362316, 35.044050537959606], - [70.35363216355927, 43.406832753924846], - [62.30056371767705, 55.17669329579144], - [54.55723739875157, 66.63678335382826], - [48.982049254774665, 74.68985179971031], - [44.02631700783786, 81.5039517377665], - [44.33605913479414, 80.8844958407268], - [51.769643326763344, 70.66331757676932], - [60.13242554272847, 59.82268341577213], - [61.99082159072077, 57.34480311386693], - [63.22976174167343, 54.55720904187848], - [63.22976174167343, 54.24749527179529], - [61.99082159072077, 54.86695116883499], - [55.796177549704225, 61.061595209851475], - [48.36259335773502, 68.49517940182056], - [42.787405213758575, 74.07036754579735] - ], - "color": "--affine-palette-line-yellow", - "lineWidth": 10, - "id": "tgAoWM0n5I", - "index": "b4n", - "seed": 702774134 - }, - "Elf7auM2_Q": { - "type": "brush", - "xywh": "[2957.656343472677,126.94223868162209,38.4953966169237,52.74310910382235]", - "points": [ - [21.10610853489061, 5], - [22.654762455926175, 5.309742126956479], - [24.82290063087521, 5.92919802399615], - [25.752098654871133, 6.238940150952629], - [26.68129667886751, 6.5486822779091085], - [28.84940649694272, 7.787594071988423], - [30.70780254493502, 9.955732246937174], - [32.25648482284441, 12.743326318925597], - [33.185682846840336, 15.530920390913991], - [33.4953966169237, 18.318514462902414], - [33.185682846840336, 19.557454613855043], - [32.56619859292732, 22.65476245592663], - [31.017573028764673, 25.75209865487153], - [28.84940649694272, 29.468890750856076], - [26.061812424954496, 33.18568284684062], - [23.274218352966273, 36.28301904578552], - [22.345020328969895, 37.52193083986481], - [20.486624280977594, 39.07061311777392], - [18.628228232985293, 40.61926703880971], - [17.69903020898937, 41.23872293584941], - [15.221178263957427, 42.78740521375846], - [13.053040089008391, 44.026317007837804], - [10.884930270933182, 45.265257158790405], - [9.026534222940882, 46.504197309743006], - [7.168138174948581, 47.123653206782706], - [6.238940150952203, 47.74310910382235], - [5.619455897039188, 47.74310910382235], - [5.30974212695628, 47.74310910382235], - [5, 47.74310910382235] - ], - "color": "--affine-palette-line-white", - "lineWidth": 10, - "id": "Elf7auM2_Q", - "index": "b4o", - "seed": 2137967754 - }, - "p1ffIEODtC": { - "type": "brush", - "xywh": "[2935.355590896769,227.29562527320496,48.716574880881126,37.56622694980098]", - "points": [ - [5, 7.168138174948751], - [10.575188143976902, 7.168138174948751], - [16.460118414910085, 6.858396047992301], - [21.725564431930707, 6.5486822779091085], - [24.20341637696265, 6.5486822779091085], - [28.539692726859812, 5.92919802399615], - [30.398088774852113, 5.619484253912958], - [32.875940719884056, 5.3097421269565075], - [33.805138743880434, 5], - [34.73433676787636, 5], - [35.04407889483309, 5], - [34.73433676787636, 5], - [33.805138743880434, 5.3097421269565075], - [32.875940719884056, 5.619484253912958], - [29.46889075085619, 6.5486822779091085], - [25.132614400958573, 7.477880301905259], - [21.41582230497397, 8.407078325901352], - [18.008772335946105, 9.336276349897503], - [14.911436137001147, 9.955732246937146], - [9.95573224693726, 10.884930270933296], - [9.336247993024244, 10.884930270933296], - [10.575188143976902, 11.194672397889804], - [13.982238113005224, 11.814128294929446], - [18.318514462902385, 12.43358419196909], - [22.34502032897035, 12.743326318925597], - [24.20341637696265, 13.053068445882047], - [26.37155455191123, 13.36278221596524], - [27.61049470286389, 13.36278221596524], - [28.539692726859812, 13.36278221596524], - [28.539692726859812, 13.672524342921747], - [28.539692726859812, 13.982266469878198], - [27.30075257590761, 14.60172236691784], - [25.132614400958573, 16.150376287953634], - [23.274218352966273, 17.079574311949784], - [19.55742625698167, 18.628256589858893], - [16.150376287953804, 19.557454613855043], - [13.362782215965126, 20.486652637851137], - [11.81412829492956, 21.106108534890836], - [11.81412829492956, 21.415850661847287], - [12.743326318925483, 22.035306558886987], - [13.67252434292186, 22.345048685843437], - [14.911436137001147, 22.96450458288308], - [18.628228232985748, 23.89370260687923], - [22.96450458288291, 24.513158503918874], - [25.752098654871588, 24.82290063087538], - [28.229950599903532, 25.13264275783183], - [29.46889075085619, 25.13264275783183], - [30.088346647895833, 25.13264275783183], - [29.46889075085619, 25.13264275783183], - [26.68129667886751, 25.75209865487153], - [21.41582230497397, 27.610494702863775], - [16.150376287953804, 29.15914862389957], - [10.265446017020622, 31.01754467189187], - [8.716792095984601, 31.637028925804827], - [9.336247993024244, 31.637028925804827], - [11.81412829492956, 32.25648482284447], - [16.769832184993447, 32.56622694980098], - [22.96450458288291, 32.56622694980098], - [29.159148623899455, 31.94674269588802], - [34.42459464092008, 31.637028925804827], - [36.59273281586866, 31.637028925804827], - [39.99978278489698, 31.637028925804827], - [42.16792095984556, 31.32728679884832], - [43.40686111079822, 31.01754467189187], - [43.716574880881126, 31.01754467189187] - ], - "color": "--affine-palette-line-grey", - "lineWidth": 10, - "id": "p1ffIEODtC", - "index": "b4p", - "seed": 1139150634 - }, - "PdRE_KBuRQ": { - "type": "brush", - "xywh": "[2932.8777389517372,233.49029767109477,12.787594071988678,23.008772335945935]", - "points": [ - [5, 5], - [5.619455897039643, 9.026505866067737], - [7.7875940719886785, 18.008772335945935] - ], - "color": "--affine-palette-line-grey", - "lineWidth": 10, - "id": "PdRE_KBuRQ", - "index": "b4q", - "seed": 870221598 - }, - "8aOMT-vJPn": { - "type": "brush", - "xywh": "[2968.496977633674,230.70270359910631,12.477880301905316,34.77860452093927]", - "points": [ - [5.619484253913015, 5], - [5.30974212695628, 7.477851945031944], - [5, 13.36278221596524], - [5.30974212695628, 18.937970359942085], - [5.929198023995923, 21.72556443193048], - [6.238940150952658, 24.51315850391893], - [7.168138174948581, 28.539664369986667], - [7.477880301905316, 29.77860452093927] - ], - "color": "--affine-palette-line-grey", - "lineWidth": 10, - "id": "8aOMT-vJPn", - "index": "b4r", - "seed": 1001390231 - }, - "qVRqjts8rV": { - "type": "brush", - "xywh": "[3000.9549505829555,39.86601173441619,19.510842108623365,40.19198069856566]", - "points": [ - [2, 38.19198069856566], - [5.1021684217244, 33.02168421724673], - [13.374491318767468, 11.306505265174053], - [17.510842108623365, 2] - ], - "color": "--affine-palette-line-yellow", - "lineWidth": 4, - "id": "qVRqjts8rV", - "index": "b4s", - "seed": 2005059071 - }, - "iX9WqzbzML": { - "type": "brush", - "xywh": "[3052.6576313841433,98.80721174718491,40.19202803389908,12.27246490304367]", - "points": [ - [2, 10.27246490304367], - [9.238519211580751, 9.238377205579695], - [20.61301053034822, 6.136208783855068], - [33.02168421724673, 3.0340403621303267], - [38.19202803389908, 2] - ], - "color": "--affine-palette-line-yellow", - "lineWidth": 4, - "id": "iX9WqzbzML", - "index": "b4t", - "seed": 1358476857 - }, - "m9R30Xu5Ag": { - "type": "brush", - "xywh": "[3056.793982173999,185.66792755547561,36.05567724404318,10.204336843449369]", - "points": [ - [2, 2], - [8.20433684344971, 4.068128059594301], - [28.885333427390833, 7.170296481318928], - [34.05567724404318, 8.204336843449369] - ], - "color": "--affine-palette-line-yellow", - "lineWidth": 4, - "id": "m9R30Xu5Ag", - "index": "b4u", - "seed": 369614434 - }, - "BAYGNl-aWq": { - "type": "brush", - "xywh": "[2863.425357658939,231.16641351921527,21.57901750355086,7.1021684217246275]", - "points": [ - [19.57901750355086, 2], - [10.272512238377203, 5.1021684217246275], - [4.0681753949274935, 5.1021684217246275], - [2, 5.1021684217246275] - ], - "color": "--affine-palette-line-yellow", - "lineWidth": 4, - "id": "BAYGNl-aWq", - "index": "b4v", - "seed": 892965863 - }, - "RRqS8C0-tX": { - "type": "brush", - "xywh": "[2902.719554114563,53.308725783445254,16.40867368689851,38.12385263897136]", - "points": [ - [14.40867368689851, 36.12385263897136], - [12.340498291970562, 28.88547543339166], - [6.136161448521307, 9.238424540913286], - [3.033993026796452, 3.0340876974639173], - [2, 2] - ], - "color": "--affine-palette-line-yellow", - "lineWidth": 4, - "id": "RRqS8C0-tX", - "index": "b4y", - "seed": 2036606473 - }, - "BMEs4nn9u8": { - "type": "brush", - "xywh": "[2822.0631751497212,139.13540122960563,55.70287014252244,6.068128059594301]", - "points": [ - [53.70287014252244, 4.068128059594301], - [45.43035790414524, 2], - [17.510842108623365, 2], - [2, 4.068128059594301] - ], - "color": "--affine-palette-line-yellow", - "lineWidth": 4, - "id": "BMEs4nn9u8", - "index": "b78", - "seed": 1660863471 - }, - "Wx6FFK2yna": { - "type": "brush", - "xywh": "[3125.4392599782495,88.52009117708948,63.628523543932715,159.79017166523548]", - "points": [ - [16.09556827149845, 152.01627959736075], - [11.472358390062254, 137.22220267744015], - [6.849148508625603, 111.33255748602065], - [5, 81.74436131994551], - [8.698466322187414, 61.40247910115855], - [14.246250457937094, 41.98523462603541], - [25.341818729435545, 18.86946033937366], - [30.889602865185225, 10.547784135749481], - [37.36196125524748, 5.924637743663908], - [41.0605968823711, 5], - [48.45752952674684, 6.849275487327816], - [54.005313662496064, 13.321676203624207], - [57.70394928961923, 29.965028610872594], - [58.628523543932715, 43.834488950246225], - [57.70394928961923, 67.87490098057188], - [54.005313662496064, 85.4428699683672], - [50.306847340308195, 99.31233030774084], - [45.683637458872, 110.40789857923974], - [41.0605968823711, 116.8803416217701], - [39.211279068809745, 122.42812575751955], - [35.51281274662142, 128.90052647381597], - [31.814177119498254, 137.22220267744015], - [27.19113654299781, 146.46849546161124], - [24.41724447512297, 151.09166301681378], - [19.79403459368632, 154.79017166523548] - ], - "color": "--affine-palette-line-white", - "lineWidth": 10, - "id": "Wx6FFK2yna", - "index": "b53", - "seed": 462012319 - }, - "6ya6TvYUel": { - "type": "brush", - "xywh": "[3107.871164011752,265.1245669405253,58.08090871311879,47.909872369699315]", - "points": [ - [29.04045435655962, 17.94484375882672], - [24.41724447512297, 16.095568271498905], - [14.246419762872847, 15.170951690951995], - [9.623209881436651, 17.020184852045873], - [6.849317813561811, 18.86946033937363], - [5, 26.2665199624509], - [5, 29.040412030325626], - [7.773892067874385, 37.36208823394986], - [13.321676203624065, 42.909872369699315], - [26.26656228868478, 41.98521346291841], - [39.2114483737455, 35.512812746622046], - [47.53312457736956, 25.34186105567005], - [51.23159089955743, 17.94484375882672], - [53.08090871311879, 13.321676203624179], - [53.08090871311879, 8.698508648421637], - [51.23159089955743, 6.849275487327816], - [46.60838101812078, 5], - [43.834488950246396, 5], - [41.060596882371556, 5] - ], - "color": "--affine-palette-line-white", - "lineWidth": 10, - "id": "6ya6TvYUel", - "index": "b54", - "seed": 1087054233 - }, - "jZJjlTjxz_": { - "type": "brush", - "xywh": "[3198.485027997305,102.38955151646314,72.87494330680602,161.63944715256326]", - "points": [ - [25.341818729436, 149.242387529486], - [15.170994017185876, 138.1468192579871], - [5, 90.06603752356972], - [9.623209881436196, 62.3271168448224], - [12.397101949311036, 55.854716128526036], - [17.020142525811934, 42.909872369699315], - [22.567926661561614, 32.73892067874732], - [28.11571079731084, 22.567990150912294], - [32.73892067874749, 15.170951690951995], - [40.13602262805853, 7.773892067874726], - [47.53295527243381, 5], - [52.15616515387046, 7.773892067874726], - [54.9300572217453, 12.397059623077268], - [61.402415611807555, 22.567990150912294], - [64.1763076796824, 30.889666354536473], - [67.87494330680602, 45.68376443757404], - [67.87494330680602, 55.854716128526036], - [66.02562549324375, 68.79951756111882], - [65.10105123893118, 79.89508583261772], - [60.477841357494526, 94.68920507877226], - [58.628523543932715, 102.08622237561559], - [55.854631476057875, 108.55866541814589], - [53.080739408183035, 115.95568271498922], - [46.60838101812078, 132.59903512223758], - [43.83448895024594, 135.37292719011236], - [41.985171136684585, 138.1468192579871], - [41.060596882371556, 140.92071132586182], - [38.286704814496716, 144.6192623005174], - [37.36213056018369, 146.46849546161127], - [37.36213056018369, 147.39315436839212], - [36.437387000934905, 149.242387529486], - [36.437387000934905, 150.16704643626684], - [34.58823849230885, 152.01627959736072], - [33.663494933060065, 152.94093850414157], - [31.814346424434007, 154.79017166523545], - [30.889602865185225, 154.79017166523545], - [29.96502861087265, 154.79017166523545], - [29.04045435655962, 154.79017166523545], - [28.11571079731084, 154.79017166523545], - [27.19113654299781, 154.79017166523545], - [26.26656228868478, 155.7148305720163], - [26.26656228868478, 156.63944715256326] - ], - "color": "--affine-palette-line-white", - "lineWidth": 10, - "id": "jZJjlTjxz_", - "index": "b55", - "seed": 278569111 - }, - "gVyMhVxORH": { - "type": "brush", - "xywh": "[3189.238777539368,285.46642799619536,35.889602865185225,36.81430409820035]", - "points": [ - [30.889602865185225, 11.472443042530358], - [22.56792666156116, 9.623167555202542], - [17.94471678012451, 8.698550974655575], - [16.095568271498905, 8.698550974655575], - [9.623040576500898, 10.547784135749453], - [7.77389206787484, 13.321676203624179], - [5.924574254312574, 16.095568271498905], - [5, 20.718735826701447], - [5, 22.568011314029263], - [5, 23.492627894576174], - [5, 25.34190338190399], - [6.849148508626058, 27.191136542997867], - [9.623040576500898, 29.040412030325626], - [13.321676203624065, 31.814304098200353], - [17.94471678012451, 31.814304098200353], - [21.64335240724813, 31.814304098200353], - [24.41724447512297, 30.889687517653442], - [26.26639298374903, 29.040412030325626], - [29.04028505162387, 26.2665199624509], - [30.889602865185225, 23.492627894576174], - [30.889602865185225, 20.718735826701447], - [30.889602865185225, 17.94484375882672], - [30.889602865185225, 16.095568271498905], - [30.889602865185225, 13.321676203624179], - [28.115710797310385, 9.623167555202542], - [26.26639298374903, 7.773892067874726], - [23.49250091587419, 5], - [21.64335240724813, 5], - [17.020142525811934, 5], - [16.095568271498905, 5] - ], - "color": "--affine-palette-line-white", - "lineWidth": 10, - "id": "gVyMhVxORH", - "index": "b56", - "seed": 919873485 - }, - "8_LSOWfOWr": { - "type": "brush", - "xywh": "[968.6889167057709,-78.93424595022731,150.70296794607657,41.54898060081024]", - "points": [ - [135.60448866517686, 8.549239640449855], - [138.22418452135685, 8.985848954248809], - [141.717138978521, 10.295696882338746], - [144.336834834701, 12.042174110920882], - [146.0832720898968, 13.788631352809773], - [147.8297493184789, 16.8449365227886], - [148.70296794607657, 18.591413751370737], - [148.70296794607657, 21.647718921349565], - [147.8297493184789, 26.013872019418386], - [145.646682762791, 29.506806489889414], - [142.5903576061189, 32.56311165986824], - [130.3650969528171, 36.92928474463031], - [115.52016043002845, 33.872959587958235], - [112.46385526004974, 31.253263731778304], - [109.84415940386975, 28.19695856179942], - [107.66107286148872, 25.140653391820592], - [106.78783424719768, 22.08432823514846], - [106.35122493339873, 20.774480307058525], - [106.35122493339873, 18.591413751370737], - [107.22446354768977, 15.971717895190807], - [108.53431147577976, 14.22524066660867], - [111.15400733195975, 12.47878342471978], - [115.0835511162295, 11.60554481042874], - [125.12570524045714, 12.915392738518676], - [127.30879178283817, 13.788631352809773], - [131.23833556710815, 15.971717895190807], - [135.16787935137813, 18.591413751370737], - [137.35096589375894, 20.774480307058525], - [138.66081382184916, 23.8308054637306], - [139.53403244944684, 27.760349248000523], - [138.66081382184916, 30.816674404672597], - [133.85803142328814, 35.61943681654037], - [131.6749448809071, 36.92928474463031], - [126.87218246903922, 38.675761973212445], - [120.75955214238843, 39.54898060081024], - [113.7737031881395, 39.54898060081024], - [106.35122493339873, 39.112351300318096], - [79.28102775730827, 35.18280751604817], - [52.64745988170989, 32.12652233276259], - [41.732067143191216, 29.94341580368831], - [36.056046130339155, 28.19695856179942], - [31.253283718471494, 26.887110633709483], - [22.08434822184165, 22.52095753564066], - [14.225260653301802, 17.71817513707964], - [7.6760210128519475, 11.60554481042874], - [4.619695856179874, 7.676001026158815], - [2, 2] - ], - "color": "--affine-palette-line-navy", - "lineWidth": 4, - "id": "8_LSOWfOWr", - "index": "b79", - "seed": 229708119 - }, - "fLh5q9XoHb": { - "type": "brush", - "xywh": "[1623.253486487466,-59.39384740034728,5.886932838848679,20.98239554963959]", - "points": [ - [2, 18.98239554963959], - [2.9434664194243396, 10.491197774819796], - [3.8869328388486792, 2] - ], - "color": "--affine-palette-line-green", - "lineWidth": 4, - "id": "fLh5q9XoHb", - "index": "b7A", - "seed": 1044482058 - }, - "bQwvyXEBjm": { - "type": "brush", - "xywh": "[1626.0838857457393,-82.03704146653342,25.699727646761858,40.795190357552485]", - "points": [ - [2, 38.795190357552485], - [2, 32.19092542158154], - [2, 24.64319406618614], - [2.943466419424567, 16.151996291366345], - [13.321597033092985, 7.660798516546549], - [18.982395549639477, 3.8869328388488498], - [21.812794807912724, 2.9434664194244533], - [23.699727646761858, 2] - ], - "color": "--affine-palette-line-green", - "lineWidth": 4, - "id": "bQwvyXEBjm", - "index": "b7B", - "seed": 701996340 - }, - "vRMucu8MbL": { - "type": "brush", - "xywh": "[1628.9142850040125,-65.05464591689383,54.94718664891889,24.75626122733729]", - "points": [ - [2, 22.75626122733729], - [2, 19.925861969064044], - [2, 18.982395549639648], - [3.8869328388489066, 16.151996291366345], - [13.321597033092985, 11.434664194244249], - [21.812794807912724, 9.547731355395399], - [40.682123196401335, 4.830399258273303], - [50.11678739064564, 2.9434664194244533], - [52.94718664891889, 2] - ], - "color": "--affine-palette-line-green", - "lineWidth": 4, - "id": "vRMucu8MbL", - "index": "b7C", - "seed": 191286329 - }, - "LGu9UFkJ9N": { - "type": "brush", - "xywh": "[1634.575083520559,-49.959183206103035,67.21225010143644,8.717332097122096]", - "points": [ - [2, 6.717332097122096], - [5.773865677697813, 6.717332097122096], - [23.699727646761858, 6.717332097122096], - [53.89065306834323, 3.8869328388488498], - [63.32531726258753, 2.9434664194243965], - [65.21225010143644, 2] - ], - "color": "--affine-palette-line-green", - "lineWidth": 4, - "id": "LGu9UFkJ9N", - "index": "b7D", - "seed": 475956093 - }, - "TNAsYlfemp": { - "type": "brush", - "xywh": "[1539.2849751586923,-80.15010862768457,18.15199629136646,37.021324679854786]", - "points": [ - [16.15199629136646, 35.021324679854786], - [16.15199629136646, 32.19092542158154], - [13.321597033093212, 21.812794807912894], - [9.547731355395626, 13.321597033093099], - [3.8869328388489066, 5.7738656776976995], - [2, 2] - ], - "color": "--affine-palette-line-green", - "lineWidth": 4, - "id": "TNAsYlfemp", - "index": "b7E", - "seed": 1482109598 - }, - "PHGOO4yK5v": { - "type": "brush", - "xywh": "[1556.267370708332,-61.28078023919613,5,14.378130613668645]", - "points": [ - [2, 12.378130613668645], - [2, 6.717332097122153], - [2.9434664194243396, 2] - ], - "color": "--affine-palette-line-green", - "lineWidth": 4, - "id": "PHGOO4yK5v", - "index": "b7F", - "seed": 458185918 - }, - "af6brHf9CH": { - "type": "brush", - "xywh": "[1560.0412363860296,-94.30210491905092,20.982395549639705,50.229854551796734]", - "points": [ - [2, 48.229854551796734], - [2, 44.455988874099035], - [2, 37.85172393812809], - [2.943466419424567, 30.30399258273269], - [4.830399258273246, 20.86932838848844], - [10.491197774819966, 12.378130613668645], - [17.095462710790798, 3.8869328388488498], - [18.982395549639705, 2] - ], - "color": "--affine-palette-line-green", - "lineWidth": 4, - "id": "af6brHf9CH", - "index": "b7G", - "seed": 235919258 - }, - "2hONa_M0tZ": { - "type": "brush", - "xywh": "[1558.154303547181,-69.77197801401593,43.625589615825675,21.925861969064044]", - "points": [ - [2, 19.925861969064044], - [5.773865677697586, 15.208529871941948], - [10.491197774819739, 10.491197774819796], - [19.925861969064044, 5.7738656776976995], - [31.24745900215703, 2.9434664194244533], - [39.73865677697677, 2], - [41.625589615825675, 2] - ], - "color": "--affine-palette-line-green", - "lineWidth": 4, - "id": "2hONa_M0tZ", - "index": "b7H", - "seed": 2089676243 - }, - "runSm8fbPf": { - "type": "brush", - "xywh": "[1819.494501727746,-81.09357504710897,6.830399258273246,37.021324679854786]", - "points": [ - [4.830399258273246, 35.021324679854786], - [3.8869328388486792, 25.586660485610537], - [3.8869328388486792, 18.038929130215138], - [2.9434664194243396, 6.717332097122096], - [2.9434664194243396, 4.830399258273246], - [2, 2] - ], - "color": "--affine-palette-line-green", - "lineWidth": 4, - "id": "runSm8fbPf", - "index": "b7I", - "seed": 1688907564 - }, - "gW__zweRir": { - "type": "brush", - "xywh": "[1825.1553002442924,-68.82851159459148,12.491197774819739,23.812794807912837]", - "points": [ - [2, 21.812794807912837], - [2, 18.038929130215138], - [2.9434664194243396, 13.321597033093042], - [4.830399258273246, 6.717332097122096], - [10.491197774819739, 2] - ], - "color": "--affine-palette-line-green", - "lineWidth": 4, - "id": "gW__zweRir", - "index": "b7J", - "seed": 253085230 - }, - "TRhofVkEZO": { - "type": "brush", - "xywh": "[1825.1553002442924,-75.43277653056248,33.24745900215703,30.41705974388384]", - "points": [ - [2, 28.41705974388384], - [2, 25.586660485610594], - [4.830399258273246, 18.038929130215195], - [10.491197774819739, 11.434664194244249], - [18.038929130215138, 8.604264935971003], - [25.586660485610537, 4.830399258273303], - [29.360526163308123, 3.8869328388488498], - [31.24745900215703, 2] - ], - "color": "--affine-palette-line-green", - "lineWidth": 4, - "id": "TRhofVkEZO", - "index": "b7K", - "seed": 16427940 - }, - "cMjTl5Y0l0": { - "type": "brush", - "xywh": "[1827.9856995025657,-65.05464591689383,55.89065306834323,18.151996291366345]", - "points": [ - [2, 16.151996291366345], - [4.830399258273246, 13.321597033093099], - [8.604264935970832, 10.491197774819852], - [19.925861969064044, 7.660798516546549], - [41.625589615825675, 2], - [49.173320971221074, 2], - [53.89065306834323, 2] - ], - "color": "--affine-palette-line-green", - "lineWidth": 4, - "id": "cMjTl5Y0l0", - "index": "b7L", - "seed": 261599141 - }, - "Kl9UtK_qhY": { - "type": "brush", - "xywh": "[1343.987426337837,-73.54584369171363,19.095462710790798,30.41705974388384]", - "points": [ - [17.095462710790798, 28.41705974388384], - [16.15199629136623, 24.64319406618614], - [13.321597033092985, 21.812794807912894], - [10.491197774819739, 14.265063452517495], - [7.6607985165464925, 9.547731355395399], - [4.830399258273246, 5.7738656776976995], - [2, 2] - ], - "color": "--affine-palette-line-green", - "lineWidth": 4, - "id": "Kl9UtK_qhY", - "index": "b7M", - "seed": 383374643 - }, - "DCyl4GQ8Zx": { - "type": "brush", - "xywh": "[1363.80022114575,-70.71544443344033,5,32.30399258273263]", - "points": [ - [2, 30.303992582732633], - [2, 26.53012690503499], - [2, 25.586660485610537], - [2.9434664194243396, 20.86932838848844], - [2.9434664194243396, 16.151996291366345], - [2.9434664194243396, 11.434664194244192], - [2.9434664194243396, 7.6607985165464925], - [2.9434664194243396, 4.830399258273246], - [2.9434664194243396, 2] - ], - "color": "--affine-palette-line-green", - "lineWidth": 4, - "id": "DCyl4GQ8Zx", - "index": "b7N", - "seed": 2083703498 - }, - "-f7Hzfl28P": { - "type": "brush", - "xywh": "[1366.6306204040231,-68.82851159459148,52.11678739064541,26.64319406618614]", - "points": [ - [2, 24.64319406618614], - [2.9434664194243396, 18.98239554963959], - [7.6607985165464925, 15.208529871941892], - [14.265063452517325, 10.491197774819796], - [32.19092542158137, 4.830399258273246], - [46.34292171294783, 2.9434664194243965], - [49.173320971221074, 2.9434664194243965], - [50.11678739064541, 2] - ], - "color": "--affine-palette-line-green", - "lineWidth": 4, - "id": "-f7Hzfl28P", - "index": "b7O", - "seed": 1382833521 - }, - "sa0w4YI4NF": { - "type": "text", - "xywh": "[1912.0484548292225,1401.202455179755,418.812536020748,77.9999279585041]", - "text": [ - { - "insert": "Take a look at how amazing and creative\nEdgeless mode can be!!!" - } - ], - "color": "--affine-text-primary-color", - "fontSize": 24, - "fontFamily": "'Kalam', cursive", - "textAlign": "left", - "id": "sa0w4YI4NF", - "index": "b5g", - "seed": 332594727 - }, - "5sTGmG66WM": { - "type": "brush", - "xywh": "[1547.4173072882395,1175.8579367371312,19.503073770491937,42.80609631147536]", - "points": [ - [5, 5], - [6.728995901639337, 10.878586065573472], - [7.727971311475358, 14.900102459016125], - [10.199795081967295, 24.61449795081944], - [11.736680327868726, 29.30199795081944], - [12.735655737704974, 33.323514344262094], - [13.670594262295253, 36.14113729508176], - [14.106045081967295, 37.409067622950715], - [14.503073770491937, 37.80609631147536] - ], - "color": "--affine-palette-line-tangerine", - "lineWidth": 10, - "id": "5sTGmG66WM", - "index": "b5h", - "seed": 226810180 - }, - "rCe9sSGcdk": { - "type": "brush", - "xywh": "[1584.2641310587314,1260.1945146059834,33.66162909836066,36.0950307377052]", - "points": [ - [5, 5], - [7.420594262295026, 8.253073770491937], - [7.817622950819441, 9.047131147541222], - [8.650102459016125, 10.315061475409948], - [11.051485655737451, 13.254354508196911], - [12.357838114754031, 14.996157786885306], - [13.190317622950715, 16.26408811475426], - [14.52228483606541, 18.06992827868862], - [15.354764344262094, 19.337858606557575], - [16.187243852458778, 20.208760245901885], - [16.58427254098342, 21.002817622950943], - [16.981301229508063, 21.399846311475585], - [16.981301229508063, 21.796875], - [18.287653688524642, 23.10322745901658], - [19.555584016393368, 24.371157786885306], - [20.823514344262094, 24.806608606557575], - [21.61757172131138, 25.600665983606632], - [22.411629098360663, 26.394723360655917], - [23.20568647540972, 27.1887807377052], - [25.011526639344083, 29.03304303278719], - [25.882428278688394, 29.903944672131274], - [27.073514344262094, 30.300973360655917], - [27.073514344262094, 30.69800204918056], - [27.470543032786736, 30.69800204918056], - [27.86757172131138, 31.0950307377052], - [28.661629098360663, 31.0950307377052], - [28.25819672131138, 31.0950307377052] - ], - "color": "--affine-palette-line-tangerine", - "lineWidth": 10, - "id": "rCe9sSGcdk", - "index": "b5i", - "seed": 2062826697 - }, - "UK9oSr12JL": { - "type": "brush", - "xywh": "[1648.5955859767641,1337.4614203436886,45.514856557377016,40.96823770491801]", - "points": [ - [5, 5], - [8.112192622950715, 8.586065573770384], - [12.633196721311378, 12.56915983606541], - [19.465932377049285, 18.198002049180104], - [25.65829918032796, 23.72438524590143], - [31.287141393442653, 27.745901639344083], - [33.63089139344265, 30.089651639344083], - [37.34503073770475, 32.79841188524597], - [39.72079918032796, 35.174180327868726], - [40.514856557377016, 35.96823770491801] - ], - "color": "--affine-palette-line-tangerine", - "lineWidth": 10, - "id": "UK9oSr12JL", - "index": "b5j", - "seed": 485444337 - }, - "osezYwO_vz": { - "type": "brush", - "xywh": "[1742.3263749111902,1407.3896990322132,35.37781762295094,27.949538934426073]", - "points": [ - [5, 5], - [8.073770491803316, 7.59989754098342], - [9.918032786885306, 9.40573770491801], - [11.185963114754259, 10.238217213114694], - [13.926741803278674, 12.511526639344083], - [15.233094262295253, 13.817879098360663], - [18.652663934426528, 15.758196721311378], - [23.14164959016398, 18.60143442622939], - [25.42136270491801, 19.97182377049171], - [29.506915983606632, 22.51408811475403], - [30.377817622950943, 22.949538934426073] - ], - "color": "--affine-palette-line-tangerine", - "lineWidth": 10, - "id": "osezYwO_vz", - "index": "b5k", - "seed": 2136351046 - }, - "O_sBKPEzc9": { - "type": "brush", - "xywh": "[1843.0371843374198,1469.345385507623,59.00742827868862,33.19415983606564]", - "points": [ - [5, 5], - [9.021516393442653, 8.112192622950943], - [14.682377049180332, 11.787909836065637], - [15.553278688524642, 12.65881147540972], - [17.832991803278674, 14.029200819672269], - [22.05942622950829, 16.366547131147627], - [29.82710040983602, 19.978227459016352], - [34.51460040983602, 21.547131147540995], - [39.86808401639337, 23.148053278688394], - [43.88960040983602, 24.147028688524415], - [47.911116803278674, 25.65189549180309], - [51.33068647540995, 27.124743852459005], - [54.00742827868862, 28.194159836065637] - ], - "color": "--affine-palette-line-tangerine", - "lineWidth": 10, - "id": "O_sBKPEzc9", - "index": "b5l", - "seed": 1636916996 - }, - "opXiEAUB93": { - "type": "brush", - "xywh": "[1981.2415900751248,1515.970641655164,67.48591188524597,12.132428278688622]", - "points": [ - [5, 5.640368852459005], - [19.075307377049057, 5.640368852459005], - [29.32761270491801, 5.640368852459005], - [36.429303278688394, 5], - [39.24692622950806, 5], - [44.07530737704906, 5.49948770491801], - [48.29533811475403, 6.101434426229389], - [53.64882172131138, 6.632940573770611], - [59.07914959016398, 7.1324282786886215], - [60.347079918032705, 7.1324282786886215], - [61.61501024590166, 7.1324282786886215], - [62.48591188524597, 7.1324282786886215] - ], - "color": "--affine-palette-line-tangerine", - "lineWidth": 10, - "id": "opXiEAUB93", - "index": "b5m", - "seed": 1371292318 - }, - "htFsN-jXZd": { - "type": "brush", - "xywh": "[2138.330473271846,1515.9002010813933,80.31890368852464,18.07505122950829]", - "points": [ - [5, 12.607581967213264], - [11.147540983606632, 13.07505122950829], - [14.567110655737451, 13.07505122950829], - [19.99103483606541, 13.07505122950829], - [24.012551229508063, 12.063268442622984], - [28.700051229508063, 11.557377049180559], - [35.526383196721326, 10.417520491803316], - [49.60169057377061, 9.71311475409857], - [61.92879098360663, 7.62551229508199], - [68.7551229508199, 6.485655737704974], - [73.57710040983602, 5.441854508196684], - [75.31890368852464, 5] - ], - "color": "--affine-palette-line-tangerine", - "lineWidth": 10, - "id": "htFsN-jXZd", - "index": "b5n", - "seed": 924679438 - }, - "gohqVK-zdz": { - "type": "brush", - "xywh": "[2287.33149786201,1478.047998212541,82.265625,34.51331967213105]", - "points": [ - [5, 29.225153688524415], - [9.072745901639337, 29.513319672131047], - [15.969518442622757, 28.367059426229616], - [26.42033811475403, 25.11398565573768], - [34.05353483606541, 22.193903688524415], - [40.950307377049285, 19.90778688524574], - [50.26127049180286, 16.821209016393595], - [62.32581967213082, 12.12090163934431], - [70.36885245901613, 8.42597336065569], - [77.265625, 5] - ], - "color": "--affine-palette-line-tangerine", - "lineWidth": 10, - "id": "gohqVK-zdz", - "index": "b5o", - "seed": 1924881192 - }, - "20N_p0pMMU": { - "type": "brush", - "xywh": "[2597.321251960371,1307.767516655164,51.739241803278674,52.12346311475403]", - "points": [ - [5, 47.12346311475403], - [8.336321721311378, 44.29943647540995], - [13.087858606557347, 39.061219262295026], - [18.812756147540767, 31.914702868852373], - [21.822489754098115, 28.89216188524597], - [26.343493852459233, 23.756403688524642], - [34.22003073770475, 18.614241803278674], - [39.98335040983602, 13.977971311475358], - [41.488217213114694, 12.466700819672042], - [44.93340163934408, 7.292520491803316], - [46.739241803278674, 5] - ], - "color": "--affine-palette-line-tangerine", - "lineWidth": 10, - "id": "20N_p0pMMU", - "index": "b5p", - "seed": 1564006340 - }, - "GTphdQxvu1": { - "type": "brush", - "xywh": "[2705.1849814685675,1246.765979769918,43.151895491803316,41.98642418032796]", - "points": [ - [5, 36.98642418032796], - [8.752561475409493, 34.25204918032796], - [14.54789959016398, 28.975409836065637], - [17.762551229508063, 24.140625], - [21.51511270491801, 19.837346311475358], - [25.16521516393459, 16.174436475409948], - [28.174948770491937, 13.151895491803316], - [29.980788934426528, 11.294825819672042], - [32.894467213114694, 8.842213114754031], - [35.27023565573745, 6.889088114754031], - [38.151895491803316, 5] - ], - "color": "--affine-palette-line-tangerine", - "lineWidth": 10, - "id": "GTphdQxvu1", - "index": "b5q", - "seed": 1722708483 - }, - "lAgaSmb89S": { - "type": "brush", - "xywh": "[2789.6688441734855,1167.891748212541,65.02049180327867,56.61885245901635]", - "points": [ - [5, 51.61885245901635], - [12.293801229508517, 46.303790983606405], - [22.245133196721326, 39.054815573770384], - [31.49205942622939, 31.101434426229616], - [35.24462090163934, 26.798155737704974], - [38.15829918032796, 24.345543032786964], - [44.38908811475403, 20.176741803278674], - [47.40522540983602, 17.15420081967204], - [50.95927254098342, 14.061219262295026], - [52.32966188524597, 11.76869877049171], - [53.12371926229525, 10.961834016393368], - [56.33837090163934, 9.14318647540972], - [58.714139344262094, 6.754610655737679], - [60.020491803278674, 5] - ], - "color": "--affine-palette-line-tangerine", - "lineWidth": 10, - "id": "lAgaSmb89S", - "index": "b5r", - "seed": 1740191672 - }, - "JrNM5WOBda": { - "type": "brush", - "xywh": "[2788.89399786201,1148.968848622377,75.86834016393414,99.31864754098365]", - "points": [ - [5, 6.812243852459005], - [10.481557377049285, 8.246670081967295], - [20.195952868852146, 8.944672131147627], - [39.31096311475403, 9.610655737704974], - [54.596567622950715, 8.067366803278674], - [61.493340163934135, 6.319159836065637], - [62.761270491803316, 5.915727459016352], - [63.5553278688526, 5.915727459016352], - [64.82325819672133, 5.915727459016352], - [67.10297131147536, 5.441854508196684], - [68.84477459016398, 5], - [69.24180327868862, 5.397028688524642], - [70.0742827868853, 6.6649590163935954], - [70.86834016393414, 7.459016393442653], - [70.86834016393414, 7.856045081967295], - [70.86834016393414, 9.12397540983602], - [70.86834016393414, 10.929815573770384], - [70.86834016393414, 17.82658811475426], - [70.1959528688526, 25.869620901639337], - [69.01767418032796, 33.91265368852464], - [67.90343237704928, 40.41239754098365], - [67.26306352459005, 47.51408811475426], - [66.59067622950806, 55.55712090163934], - [64.8424692622948, 67.28227459016398], - [63.798668032786736, 71.96977459016398], - [63.798668032786736, 73.3785860655737], - [63.39523565573745, 74.56967213114763], - [63.39523565573745, 75.83760245901635], - [63.39523565573745, 78.11731557377038], - [63.39523565573745, 79.92315573770497], - [63.39523565573745, 81.72899590163934], - [62.95338114754077, 82.99692622950829], - [62.51152663934408, 84.73872950819668], - [62.0696721311474, 87.01844262295094], - [61.627817622950715, 90.5660860655737], - [61.12192622950806, 93.44774590163934], - [60.68007172131138, 94.31864754098365] - ], - "color": "--affine-palette-line-tangerine", - "lineWidth": 10, - "id": "JrNM5WOBda", - "index": "b5s", - "seed": 1954753003 - }, - "NqQ1110OTY": { - "type": "brush", - "xywh": "[2399.255165894797,1432.2744326387706,95.75819672131183,53.21209016393436]", - "points": [ - [5, 48.21209016393436], - [9.95645491803316, 45.88755122950806], - [13.47207991803316, 43.36449795081944], - [17.762551229508063, 40.13703893442607], - [24.159836065573927, 35.30225409836066], - [27.573002049180104, 33.5924692622948], - [35.07172131147581, 30.36501024590143], - [40.42520491803316, 28.213370901639337], - [43.97284836065592, 26.69569672131138], - [45.98360655737724, 25.683913934426073], - [52.207991803278674, 23.53227459016398], - [62.460297131147854, 19.03688524590143], - [71.77126024590143, 14.675973360655462], - [83.66290983606541, 9.104764344262094], - [89.01639344262276, 5.877305327868726], - [90.75819672131183, 5] - ], - "color": "--affine-palette-line-tangerine", - "lineWidth": 10, - "id": "NqQ1110OTY", - "index": "b5H", - "seed": 1884794374 - }, - "d86IZAb3VQ": { - "type": "brush", - "xywh": "[2524.255165894797,1384.374842474836,47.903432377049285,51.14369877049194]", - "points": [ - [5, 46.14369877049194], - [11.589395491803316, 41.129610655737906], - [18.825563524590507, 35.69287909836066], - [22.37961065573745, 33.137807377049285], - [26.234631147541222, 30.30737704918033], - [29.250768442622757, 27.816342213114694], - [33.502817622950715, 25.062756147540995], - [36.045081967213264, 21.49590163934431], - [37.31301229508199, 20.2151639344263], - [38.24795081967204, 18.396516393442653], - [39.08043032786918, 17.519211065573927], - [39.08043032786918, 16.712346311475358], - [39.91290983606541, 15.431608606557347], - [40.412397540983875, 13.011014344262321], - [40.84784836065592, 11.73027663934431], - [40.84784836065592, 10.923411885245969], - [41.244877049180104, 10.116547131147627], - [41.244877049180104, 9.309682377049285], - [42.109375, 7.491034836065637], - [42.50640368852464, 5.806864754098569], - [42.903432377049285, 5] - ], - "color": "--affine-palette-line-tangerine", - "lineWidth": 10, - "id": "d86IZAb3VQ", - "index": "b51", - "seed": 1014840334 - }, - "BOQVr4nJPn": { - "type": "brush", - "xywh": "[758.0638968666935,856.0703562336182,72.41071428571422,27.96875]", - "points": [ - [67.41071428571422, 5], - [60.35714285714289, 5.11160714285711], - [43.73883928571422, 10.69196428571422], - [27.48883928571422, 16.04910714285711], - [18.136160714285552, 19.776785714285552], - [11.104910714285552, 21.51785714285711], - [7.109375, 22.276785714285552], - [5.703125, 22.96875], - [5, 22.96875] - ], - "color": "--affine-palette-line-green", - "lineWidth": 10, - "id": "BOQVr4nJPn", - "index": "b7P", - "seed": 1824565825 - }, - "egSzDjP_cw": { - "type": "brush", - "xywh": "[654.6152361524078,891.1373205193324,65.40178571428578,14.69866071428578]", - "points": [ - [60.40178571428578, 5], - [51.484375, 5.926339285714221], - [40.60267857142867, 7.77901785714289], - [33.23660714285711, 8.828125], - [21.52901785714289, 9.69866071428578], - [16.85267857142867, 9.69866071428578], - [10.87053571428578, 9.69866071428578], - [7.8125, 9.69866071428578], - [6.40625, 9.69866071428578], - [5, 9.69866071428578] - ], - "color": "--affine-palette-line-green", - "lineWidth": 10, - "id": "egSzDjP_cw", - "index": "b7Q", - "seed": 1305932090 - }, - "TDTmNJ3PZw": { - "type": "brush", - "xywh": "[491.26702186669354,885.2667848050467,101.02678571428555,28.32589285714289]", - "points": [ - [96.02678571428555, 23.32589285714289], - [85.59151785714289, 21.68526785714289], - [57.28794642857133, 17.00892857142867], - [36.47321428571422, 13.381696428571558], - [6.40625, 7.8125], - [5.703125, 7.109375], - [5, 6.40625], - [5, 5] - ], - "color": "--affine-palette-line-green", - "lineWidth": 10, - "id": "TDTmNJ3PZw", - "index": "b7R", - "seed": 414039155 - }, - "DS1eX6PCJg": { - "type": "brush", - "xywh": "[467.51702186669354,818.1016062336182,20.49107142857133,41.83035714285711]", - "points": [ - [15.491071428571331, 36.83035714285711], - [11.90848214285711, 29.67633928571422], - [11.13839285714289, 26.74107142857133], - [9.609375, 20.51339285714289], - [8.839285714285552, 17.45535714285711], - [8.069196428571331, 13.459821428571331], - [6.473214285714221, 9.464285714285552], - [5.703125, 6.40625], - [5, 5] - ], - "color": "--affine-palette-line-green", - "lineWidth": 10, - "id": "DS1eX6PCJg", - "index": "b7S", - "seed": 2080642130 - }, - "JTUmMg4ctN": { - "type": "brush", - "xywh": "[421.28934329526487,679.943124090761,21.23883928571422,73.08035714285722]", - "points": [ - [16.23883928571422, 68.08035714285722], - [15.53571428571422, 61.506696428571445], - [16.22767857142867, 60.100446428571445], - [16.22767857142867, 58.694196428571445], - [16.22767857142867, 55.881696428571445], - [16.22767857142867, 53.649553571428555], - [16.22767857142867, 47.421875], - [13.694196428571558, 33.01339285714289], - [11.70758928571422, 24.587053571428555], - [10.11160714285711, 20.59151785714289], - [8.34821428571422, 13.44866071428578], - [5, 5] - ], - "color": "--affine-palette-line-green", - "lineWidth": 10, - "id": "JTUmMg4ctN", - "index": "b7T", - "seed": 1148380737 - }, - "Pjfc99bOvq": { - "type": "brush", - "xywh": "[355.19559329526487,598.458749090761,62.14285714285711,50.04464285714289]", - "points": [ - [57.14285714285711, 45.04464285714289], - [54.26339285714289, 39.296875], - [49.20758928571422, 31.785714285714334], - [47.09821428571422, 28.973214285714334], - [45.69196428571422, 27.566964285714334], - [43.34821428571422, 24.397321428571445], - [41.94196428571422, 23.694196428571445], - [41.23883928571422, 22.991071428571445], - [40.53571428571422, 22.287946428571445], - [34.78794642857156, 19.754464285714334], - [27.75669642857156, 17.109375], - [19.56473214285711, 13.47098214285711], - [14.631696428571558, 11.004464285714334], - [10.16741071428578, 8.00223214285711], - [7.93526785714289, 6.473214285714334], - [5.703125, 5.703125], - [5, 5] - ], - "color": "--affine-palette-line-green", - "lineWidth": 10, - "id": "Pjfc99bOvq", - "index": "b7U", - "seed": 554791730 - }, - "DZmWbBt4Z0": { - "type": "brush", - "xywh": "[271.166575438122,582.0413383764753,50.91517857142867,11]", - "points": [ - [45.91517857142867, 5.881696428571331], - [38.40401785714289, 5], - [25.41294642857156, 5], - [22.47767857142867, 5], - [20.36830357142867, 5], - [19.66517857142867, 5], - [17.55580357142867, 5], - [16.85267857142867, 5], - [15.446428571428669, 5], - [14.040178571428669, 5], - [13.337053571428669, 5], - [12.633928571428669, 5], - [10.40178571428578, 5], - [7.23214285714289, 5], - [5, 5] - ], - "color": "--affine-palette-line-green", - "lineWidth": 10, - "id": "DZmWbBt4Z0", - "index": "b7V", - "seed": 2130229815 - }, - "m6-LmLuT6f": { - "type": "brush", - "xywh": "[252.20452186669354,546.2600883764753,75.75892857142844,90.68080357142856]", - "points": [ - [70.75892857142844, 5], - [60.3125, 8.325892857142776], - [49.82142857142844, 15.37946428571422], - [44.54241071428555, 18.93973214285711], - [40.078125, 22.667410714285666], - [38.671875, 24.05133928571422], - [37.96875, 24.743303571428555], - [35.73660714285711, 26.19419642857133], - [33.50446428571422, 27.64508928571422], - [25.77008928571422, 31.78571428571422], - [23.53794642857133, 33.23660714285711], - [20.479910714285552, 34.75446428571422], - [14.274553571428442, 39.073660714285666], - [8.995535714285552, 42.633928571428555], - [5, 45.78125], - [5, 46.47321428571422], - [5, 47.165178571428555], - [7.45535714285711, 49.48660714285711], - [13.928571428571331, 54.966517857142776], - [17.901785714285552, 58.11383928571422], - [26.83035714285711, 65.22321428571422], - [33.01339285714266, 68.78348214285711], - [38.27008928571422, 72.34375], - [40.546875, 74.62053571428567], - [44.6875, 78.00223214285711], - [46.89732142857133, 79.453125], - [48.28125, 80.14508928571422], - [49.66517857142844, 80.83705357142856], - [50.35714285714289, 81.52901785714278], - [51.74107142857133, 82.22098214285711], - [53.125, 82.91294642857144], - [56.58482142857133, 85.68080357142856], - [57.27678571428555, 85.68080357142856] - ], - "color": "--affine-palette-line-green", - "lineWidth": 10, - "id": "m6-LmLuT6f", - "index": "b7W", - "seed": 642894579 - } - } - } - }, - "1cFTd-rwDr": { - "sys:id": "1cFTd-rwDr", - "sys:flavour": "affine:note", - "sys:children": ["aPk03I3k9L", "4_plt-pF5i", "5O-z_KtfdV"], - "prop:xywh": "[1782.1527708473825,83.59728260421294,554.4267667459387,568]", - "prop:index": "a0", - "prop:background": "--affine-tag-blue", - "prop:hidden": false - }, - "aPk03I3k9L": { - "sys:id": "aPk03I3k9L", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "h3", - "prop:text": [ - { - "insert": "Organise Your Pages" - } - ] - }, - "4_plt-pF5i": { - "sys:id": "4_plt-pF5i", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "Manage your favourites and add links between your pages to easily navigate between them. This makes it easy to keep track of your content, and ensures that you always know where everything is." - } - ] - }, - "5O-z_KtfdV": { - "sys:id": "5O-z_KtfdV", - "sys:flavour": "affine:image", - "sys:children": [], - "prop:type": "image", - "prop:sourceId": "/static/1326bc48553a572c6756d9ee1b30a0dfdda26222fc2d2c872b14e609.gif", - "prop:caption": "", - "prop:width": 0, - "prop:height": 0 - }, - "A2hTNhHJSo": { - "sys:id": "A2hTNhHJSo", - "sys:flavour": "affine:note", - "sys:children": ["1t5gAmmDk1", "Ru5RZxl2cw", "m09CQTVNta"], - "prop:xywh": "[1862.2162713385649,667.8687461185463,471.2475208977704,510]", - "prop:index": "a0", - "prop:background": "--affine-tag-red", - "prop:hidden": false - }, - "1t5gAmmDk1": { - "sys:id": "1t5gAmmDk1", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "h3", - "prop:text": [ - { - "insert": "Write Your Content" - } - ] - }, - "Ru5RZxl2cw": { - "sys:id": "Ru5RZxl2cw", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "Easily create rich, interactive pages that combine text, images, and other media to create a dynamic, engaging experience." - } - ] - }, - "m09CQTVNta": { - "sys:id": "m09CQTVNta", - "sys:flavour": "affine:image", - "sys:children": [], - "prop:type": "image", - "prop:sourceId": "/static/28516717d63e469cd98729ff46be6595711898bab3dc43302319a987.gif", - "prop:caption": "", - "prop:width": 0, - "prop:height": 0 - }, - "JSoC9zIZDz": { - "sys:id": "JSoC9zIZDz", - "sys:flavour": "affine:note", - "sys:children": ["RYwAAsT0jt", "yui0v4a-DG", "CuW0As_WD5"], - "prop:index": "a0", - "prop:background": "--affine-tag-purple", - "prop:xywh": "[1310.31891616522,676.1486476697154,537.1322406806248,556]", - "prop:hidden": false - }, - "RYwAAsT0jt": { - "sys:id": "RYwAAsT0jt", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "h3", - "prop:text": [ - { - "insert": "Draw and Be Creative" - } - ] - }, - "yui0v4a-DG": { - "sys:id": "yui0v4a-DG", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "Our Edgeless Mode can be utilised as a whiteboard, allowing you to create content that extends beyond the boundaries of a traditional page - limited only by your imagination." - } - ] - }, - "CuW0As_WD5": { - "sys:id": "CuW0As_WD5", - "sys:flavour": "affine:image", - "sys:children": [], - "prop:type": "image", - "prop:sourceId": "/static/9288be57321c8772d04e05dbb69a22742372b3534442607a2d6a9998.gif", - "prop:caption": "", - "prop:width": 0, - "prop:height": 0 - }, - "Vq_8QO3ruz": { - "sys:id": "Vq_8QO3ruz", - "sys:flavour": "affine:note", - "sys:children": ["Z6tZpqYD1i", "cSq4fYa62E", "e-ekSJPKh0"], - "prop:xywh": "[824.3933427871591,677.7709857969486,475.6451530544002,536]", - "prop:index": "a0", - "prop:background": "--affine-tag-blue", - "prop:hidden": false - }, - "Z6tZpqYD1i": { - "sys:id": "Z6tZpqYD1i", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "h3", - "prop:text": [ - { - "insert": "Plan Effectively and Efficiently" - } - ] - }, - "cSq4fYa62E": { - "sys:id": "cSq4fYa62E", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "With powerful databases, you can create content within tables and leverage powerful features such as adding due dates, tracking progress, and adding tags and status labels." - } - ] - }, - "e-ekSJPKh0": { - "sys:id": "e-ekSJPKh0", - "sys:flavour": "affine:image", - "sys:children": [], - "prop:type": "image", - "prop:sourceId": "/static/c820edeeba50006b531883903f5bb0b96bf523c9a6b3ce5868f03db5.gif", - "prop:caption": "", - "prop:width": 0, - "prop:height": 0 - }, - "g5OrKM5Fb6": { - "sys:id": "g5OrKM5Fb6", - "sys:flavour": "affine:note", - "sys:children": [ - "YQPpZUitL9", - "jNb1ieggGw", - "yln9MU-iVm", - "rY1fVETRzE", - "snOX3HBubz" - ], - "prop:index": "a0", - "prop:background": "--affine-tag-green", - "prop:xywh": "[817.0745095091336,1272.4084873924633,586.3018678030073,731]", - "prop:hidden": false - }, - "dhMxx0X4Dd": { - "sys:id": "dhMxx0X4Dd", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "snOX3HBubz": { - "sys:id": "snOX3HBubz", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "YQPpZUitL9": { - "sys:id": "YQPpZUitL9", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "h2", - "prop:text": [ - { - "insert": "More support?" - } - ] - }, - "jNb1ieggGw": { - "sys:id": "jNb1ieggGw", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "There are some useful resources in the Help and Feedback section." - } - ] - }, - "yln9MU-iVm": { - "sys:id": "yln9MU-iVm", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "If you are looking for more support, you can come and join the awesome " - }, - { - "insert": "AFFiNE Community", - "attributes": { - "link": "https://community.affine.pro" - } - }, - { - "insert": " . Whether you want to share new ideas or interact with other like-minded individuals - we look forward to having you." - } - ] - }, - "rY1fVETRzE": { - "sys:id": "rY1fVETRzE", - "sys:flavour": "affine:image", - "sys:children": [], - "prop:type": "image", - "prop:sourceId": "/static/e93536e1be97e3b5206d43bf0793fdef24e60044d174f0abdefebe08.gif", - "prop:caption": "", - "prop:width": 0, - "prop:height": 0 - }, - "3An3wRFKN_": { - "sys:id": "3An3wRFKN_", - "sys:flavour": "affine:note", - "sys:children": ["KZrhdN52ZD", "3MnKwqEw_Q"], - "prop:xywh": "[-264.94381566608683,389.00823320424837,494.2811077047478,380]", - "prop:index": "a2", - "prop:background": "--affine-background-secondary-color", - "prop:hidden": true - }, - "KZrhdN52ZD": { - "sys:id": "KZrhdN52ZD", - "sys:flavour": "affine:divider", - "sys:children": [] - }, - "3MnKwqEw_Q": { - "sys:id": "3MnKwqEw_Q", - "sys:flavour": "affine:database", - "sys:children": ["nDNHkufUpb", "OwfNHKkFw6", "zCn640WobP"], - "prop:mode": "table", - "prop:title": [ - { - "insert": "AFFiNE learning progress" - } - ], - "prop:cells": { - "nDNHkufUpb": { - "fafWu_k4eb": { - "columnId": "fafWu_k4eb", - "value": ["MXPthta90B"] - }, - "xta1ActkB1": { - "columnId": "xta1ActkB1", - "value": 100 - } - }, - "OwfNHKkFw6": { - "xta1ActkB1": { - "columnId": "xta1ActkB1", - "value": 41 - }, - "fafWu_k4eb": { - "columnId": "fafWu_k4eb", - "value": ["4LhPvJWXU2"] - } - }, - "zCn640WobP": { - "xta1ActkB1": { - "columnId": "xta1ActkB1", - "value": 23 - }, - "fafWu_k4eb": { - "columnId": "fafWu_k4eb", - "value": ["w6YYhk21ZT"] - } - } - }, - "prop:views": [ - { - "id": "default", - "name": "Table", - "mode": "table", - "columns": [ - { - "width": 200, - "id": "fafWu_k4eb" - }, - { - "width": 200, - "id": "xta1ActkB1" - } - ], - "filter": { - "type": "group", - "op": "and", - "conditions": [] - } - } - ], - "prop:columns": [ - { - "type": "title", - "id": "3MnKwqEw_Q", - "name": "Title", - "data": {} - }, - { - "name": "Tag", - "type": "multi-select", - "data": { - "options": [ - { - "id": "MXPthta90B", - "value": "Tag1", - "color": "var(--affine-tag-yellow)" - }, - { - "id": "4LhPvJWXU2", - "value": "Tag2", - "color": "var(--affine-tag-pink)" - }, - { - "id": "w6YYhk21ZT", - "value": "Tag3", - "color": "var(--affine-tag-teal)" - }, - { - "id": "tCIAywo8av", - "value": "How to write", - "color": "var(--affine-tag-gray)" - } - ] - }, - "id": "fafWu_k4eb" - }, - { - "type": "progress", - "name": "Progress", - "data": {}, - "id": "xta1ActkB1" - } - ], - "prop:titleColumnName": "Title", - "prop:titleColumnWidth": 158 - }, - "nDNHkufUpb": { - "sys:id": "nDNHkufUpb", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "Download AFFiNE" - } - ], - "prop:type": "text" - }, - "OwfNHKkFw6": { - "sys:id": "OwfNHKkFw6", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "Learn AFFiNE" - } - ], - "prop:type": "text" - }, - "zCn640WobP": { - "sys:id": "zCn640WobP", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "Use AFFiNE" - } - ], - "prop:type": "text" - }, - "U2hR9Lu1E7": { - "sys:id": "U2hR9Lu1E7", - "sys:flavour": "affine:note", - "sys:children": ["LYes52XNDN"], - "prop:xywh": "[2918.2644723261433,881.0630462339941,539.4086027654356,462]", - "prop:index": "a2", - "prop:background": "--affine-background-secondary-color", - "prop:hidden": true - }, - "LYes52XNDN": { - "sys:id": "LYes52XNDN", - "sys:flavour": "affine:image", - "sys:children": [], - "prop:type": "image", - "prop:sourceId": "/static/047ebf2c9a5c7c9d8521c2ea5e6140ff7732ef9e28a9f944e9bf3ca4.png", - "prop:caption": "", - "prop:width": 0, - "prop:height": 0 - }, - "nOERveFG0j": { - "sys:id": "nOERveFG0j", - "sys:flavour": "affine:note", - "sys:children": ["SjyfxmcAjc"], - "prop:xywh": "[2919.8341116576826,1349.0080470072992,535.7138283708327,444]", - "prop:index": "a2", - "prop:background": "--affine-background-secondary-color", - "prop:hidden": true - }, - "SjyfxmcAjc": { - "sys:id": "SjyfxmcAjc", - "sys:flavour": "affine:image", - "sys:children": [], - "prop:type": "image", - "prop:sourceId": "/static/6aa785ee927547ce9dd9d7b43e01eac948337fe57571443e87bc3a60.png", - "prop:caption": "", - "prop:width": 0, - "prop:height": 0 - } - } -} diff --git a/packages/frontend/templates/v1/template-galleries.json b/packages/frontend/templates/v1/template-galleries.json deleted file mode 100644 index 60f60c68cf..0000000000 --- a/packages/frontend/templates/v1/template-galleries.json +++ /dev/null @@ -1,152 +0,0 @@ -{ - "blocks": { - "Tz41kDyemg": { - "sys:id": "Tz41kDyemg", - "sys:flavour": "affine:page", - "sys:children": ["PCxQvHuwt1", "PMY4JPuq4o"], - "prop:title": [ - { - "insert": "Templates Galleries " - } - ] - }, - "PCxQvHuwt1": { - "sys:id": "PCxQvHuwt1", - "sys:flavour": "affine:surface", - "sys:children": [], - "prop:elements": { - "type": "$blocksuite:internal:native$", - "value": {} - } - }, - "PMY4JPuq4o": { - "sys:id": "PMY4JPuq4o", - "sys:flavour": "affine:note", - "sys:children": [ - "1KJadeDAj-", - "_a8e4OM_PP", - "ndmUz6zEr1", - "yIcapYtWKm", - "gPqHv8Whaq", - "pp12c2slOV", - "VyoH5kUerc", - "XvCeZ-f-ib" - ], - "prop:xywh": "[0,0,800,529]", - "prop:background": "--affine-background-secondary-color", - "prop:index": "a0", - "prop:hidden": false - }, - "1KJadeDAj-": { - "sys:id": "1KJadeDAj-", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "_a8e4OM_PP": { - "sys:id": "_a8e4OM_PP", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "No matter if you're " - }, - { - "insert": "organizing your personal life", - "attributes": { - "bold": true - } - }, - { - "insert": " or " - }, - { - "insert": "getting things done at work", - "attributes": { - "bold": true - } - }, - { - "insert": ", our templates galleries have got you covered! " - } - ] - }, - "ndmUz6zEr1": { - "sys:id": "ndmUz6zEr1", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:text": [ - { - "insert": "Here We offer a wide range of resources to meet your unique needs and help you achieve your goals, whether in your personal or professional life." - } - ], - "prop:type": "text" - }, - "yIcapYtWKm": { - "sys:id": "yIcapYtWKm", - "sys:flavour": "affine:divider", - "sys:children": [] - }, - "gPqHv8Whaq": { - "sys:id": "gPqHv8Whaq", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "quote", - "prop:text": [ - { - "insert": "Tired of managing your notes or coming up with a effient work plan? Whether you're a student, parent, or have diverse interests, here we provide a few templates to kick off your journey and unlock your true potential with AFFiNE and reap incredible benefits." - } - ] - }, - "pp12c2slOV": { - "sys:id": "pp12c2slOV", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": " ", - "attributes": { - "reference": { - "type": "LinkedPage", - "pageId": "7217fbe2-61db-4a91-93c6-ad5c800e5a43" - } - } - }, - { - "insert": " " - } - ] - }, - "VyoH5kUerc": { - "sys:id": "VyoH5kUerc", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "quote", - "prop:text": [ - { - "insert": "Staying organized and efficient in a dynamic work environment is essential for today's working people. AFFiNE elevates productivity for project managers, software engineers, and professionals alike. We're excited to provide insights into how AFFiNE transforms work life. " - } - ] - }, - "XvCeZ-f-ib": { - "sys:id": "XvCeZ-f-ib", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": " ", - "attributes": { - "reference": { - "type": "LinkedPage", - "pageId": "6eb43ea8-8c11-456d-bb1d-5193937961ab" - } - } - } - ] - } - } -} diff --git a/packages/frontend/templates/v1/working-home.json b/packages/frontend/templates/v1/working-home.json deleted file mode 100644 index 3adeeb20d9..0000000000 --- a/packages/frontend/templates/v1/working-home.json +++ /dev/null @@ -1,254 +0,0 @@ -{ - "blocks": { - "qsMt8nCetT": { - "sys:id": "qsMt8nCetT", - "sys:flavour": "affine:page", - "sys:children": ["yC-F6Rj9bA", "pk_Cjkpyd4"], - "prop:title": [ - { - "insert": "Working Home" - } - ] - }, - "yC-F6Rj9bA": { - "sys:id": "yC-F6Rj9bA", - "sys:flavour": "affine:surface", - "sys:children": [], - "prop:elements": { - "type": "$blocksuite:internal:native$", - "value": {} - } - }, - "pk_Cjkpyd4": { - "sys:id": "pk_Cjkpyd4", - "sys:flavour": "affine:note", - "sys:children": [ - "IAOXx-DBBz", - "5hR-BR03ms", - "zVgCQKWH-c", - "ym2g48zSU_", - "o1UyrWV30t", - "1EUiH1t60b", - "HmzMXKFaci", - "v5CGQQPDrt", - "lcf4te4rxB", - "AbN8iFn0RW", - "uXdWxSSWgl", - "TBjwPij8cJ", - "mjgDUguqmV", - "c_gX1ik0kK", - "IU3gGmIffA", - "LnERZXy0xj", - "oi0BxPtiit", - "DuRxwNJe2w" - ], - "prop:xywh": "[0,0,800,939]", - "prop:background": "--affine-background-secondary-color", - "prop:index": "a0", - "prop:hidden": false - }, - "IAOXx-DBBz": { - "sys:id": "IAOXx-DBBz", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "5hR-BR03ms": { - "sys:id": "5hR-BR03ms", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": " ", - "attributes": { - "reference": { - "type": "LinkedPage", - "pageId": "b7a9e1bc-e205-44aa-8dad-7e328269d00b" - } - } - } - ] - }, - "zVgCQKWH-c": { - "sys:id": "zVgCQKWH-c", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "quote", - "prop:text": [ - { - "insert": "This " - }, - { - "insert": "Annual Performance Review template", - "attributes": { - "bold": true - } - }, - { - "insert": " is ideal for businesses seeking a structured and comprehensive approach to evaluating employee performance. By utilizing this template, you will gain clarity on individual strengths and areas for improvement, fostering effective communication and driving professional growth within your organization." - } - ] - }, - "ym2g48zSU_": { - "sys:id": "ym2g48zSU_", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "o1UyrWV30t": { - "sys:id": "o1UyrWV30t", - "sys:flavour": "affine:divider", - "sys:children": [] - }, - "1EUiH1t60b": { - "sys:id": "1EUiH1t60b", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": " ", - "attributes": { - "reference": { - "type": "LinkedPage", - "pageId": "0350509d-8702-4797-b4d7-168f5e9359c7" - } - } - } - ] - }, - "HmzMXKFaci": { - "sys:id": "HmzMXKFaci", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "quote", - "prop:text": [ - { - "insert": "Meeting minutes are an official record of a meeting", - "attributes": { - "bold": true - } - }, - { - "insert": " for its participants. They're also sources of information for teammates who were unable to attend." - } - ] - }, - "v5CGQQPDrt": { - "sys:id": "v5CGQQPDrt", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "AbN8iFn0RW": { - "sys:id": "AbN8iFn0RW", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "lcf4te4rxB": { - "sys:id": "lcf4te4rxB", - "sys:flavour": "affine:divider", - "sys:children": [] - }, - "IU3gGmIffA": { - "sys:id": "IU3gGmIffA", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "c_gX1ik0kK": { - "sys:id": "c_gX1ik0kK", - "sys:flavour": "affine:divider", - "sys:children": [] - }, - "uXdWxSSWgl": { - "sys:id": "uXdWxSSWgl", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": " ", - "attributes": { - "reference": { - "type": "LinkedPage", - "pageId": "aa02af3c-5c5c-4856-b7ce-947ad17331f3" - } - } - } - ] - }, - "TBjwPij8cJ": { - "sys:id": "TBjwPij8cJ", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "quote", - "prop:text": [ - { - "insert": "Maximize your team's performance and drive goal-oriented success with our extensive collection of " - }, - { - "insert": "OKR templates", - "attributes": { - "bold": true - } - }, - { - "insert": ", specifically crafted to facilitate effective objective setting, key result tracking, and overall performance improvement." - } - ] - }, - "mjgDUguqmV": { - "sys:id": "mjgDUguqmV", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - }, - "LnERZXy0xj": { - "sys:id": "LnERZXy0xj", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "🔔Reminder: Click the " - }, - { - "insert": "+New page ", - "attributes": { - "bold": true - } - }, - { - "insert": "to start your journey in AFFiNE " - } - ] - }, - "oi0BxPtiit": { - "sys:id": "oi0BxPtiit", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [ - { - "insert": "✏Unleash your creativity and elevate your work efficiency to new heights by customizing templates that perfectly align with your unique needs and objectives." - } - ] - }, - "DuRxwNJe2w": { - "sys:id": "DuRxwNJe2w", - "sys:flavour": "affine:paragraph", - "sys:children": [], - "prop:type": "text", - "prop:text": [] - } - } -} diff --git a/packages/frontend/workspace-impl/.gitignore b/packages/frontend/workspace-impl/.gitignore new file mode 100644 index 0000000000..a65b41774a --- /dev/null +++ b/packages/frontend/workspace-impl/.gitignore @@ -0,0 +1 @@ +lib diff --git a/packages/frontend/workspace/package.json b/packages/frontend/workspace-impl/package.json similarity index 53% rename from packages/frontend/workspace/package.json rename to packages/frontend/workspace-impl/package.json index 51266ac300..a494f6df0d 100644 --- a/packages/frontend/workspace/package.json +++ b/packages/frontend/workspace-impl/package.json @@ -1,10 +1,9 @@ { - "name": "@affine/workspace", + "name": "@affine/workspace-impl", "private": true, + "main": "./src/index.ts", "exports": { - ".": "./src/index.ts", - "./atom": "./src/atom.ts", - "./affine/*": "./src/affine/*.ts" + ".": "./src/index.ts" }, "peerDependencies": { "@blocksuite/blocks": "*", @@ -12,38 +11,25 @@ "@blocksuite/store": "*" }, "dependencies": { - "@affine-test/fixtures": "workspace:*", "@affine/debug": "workspace:*", + "@affine/electron-api": "workspace:*", "@affine/env": "workspace:*", "@affine/graphql": "workspace:*", + "@affine/workspace": "workspace:*", "@toeverything/infra": "workspace:*", - "async-call-rpc": "^6.3.1", "idb": "^8.0.0", "idb-keyval": "^6.2.1", "is-svg": "^5.0.0", - "jotai": "^2.5.1", - "js-base64": "^3.7.5", - "ky": "^1.0.1", - "lib0": "^0.2.87", "lodash-es": "^4.17.21", "nanoid": "^5.0.3", "next-auth": "^4.24.5", - "react": "18.2.0", - "react-dom": "18.2.0", - "rxjs": "^7.8.1", "socket.io-client": "^4.7.2", - "swr": "2.2.4", - "valtio": "^1.11.2", "y-protocols": "^1.0.6", - "y-provider": "workspace:*", - "yjs": "^13.6.10", - "zod": "^3.22.4" + "yjs": "^13.6.10" }, "devDependencies": { - "@testing-library/react": "^14.0.0", - "@types/ws": "^8.5.7", "fake-indexeddb": "^5.0.0", - "vitest": "1.0.4", + "vitest": "1.1.3", "ws": "^8.14.2" }, "version": "0.11.0" diff --git a/packages/frontend/workspace/src/impl/cloud/awareness.ts b/packages/frontend/workspace-impl/src/cloud/awareness.ts similarity index 92% rename from packages/frontend/workspace/src/impl/cloud/awareness.ts rename to packages/frontend/workspace-impl/src/cloud/awareness.ts index 7c2be0dc63..0add462ef6 100644 --- a/packages/frontend/workspace/src/impl/cloud/awareness.ts +++ b/packages/frontend/workspace-impl/src/cloud/awareness.ts @@ -1,4 +1,5 @@ import { DebugLogger } from '@affine/debug'; +import type { AwarenessProvider } from '@affine/workspace'; import { applyAwarenessUpdate, type Awareness, @@ -6,9 +7,8 @@ import { removeAwarenessStates, } from 'y-protocols/awareness'; -import type { AwarenessProvider } from '../../engine/awareness'; -import { getIoManager } from '../../utils/affine-io'; -import { base64ToUint8Array, uint8ArrayToBase64 } from '../../utils/base64'; +import { getIoManager } from '../utils/affine-io'; +import { base64ToUint8Array, uint8ArrayToBase64 } from '../utils/base64'; const logger = new DebugLogger('affine:awareness:socketio'); @@ -42,10 +42,9 @@ export function createCloudAwarenessProvider( return; } - const changedClients = Object.values(changes).reduce((res, cur) => [ - ...res, - ...cur, - ]); + const changedClients = Object.values(changes).reduce((res, cur) => + res.concat(cur) + ); const update = encodeAwarenessUpdate(awareness, changedClients); uint8ArrayToBase64(update) diff --git a/packages/frontend/workspace/src/impl/cloud/blob.ts b/packages/frontend/workspace-impl/src/cloud/blob.ts similarity index 70% rename from packages/frontend/workspace/src/impl/cloud/blob.ts rename to packages/frontend/workspace-impl/src/cloud/blob.ts index 601991a26c..5d6d54ec9a 100644 --- a/packages/frontend/workspace/src/impl/cloud/blob.ts +++ b/packages/frontend/workspace-impl/src/cloud/blob.ts @@ -1,15 +1,14 @@ import { - checkBlobSizesQuery, deleteBlobMutation, fetchWithTraceReport, getBaseUrl, listBlobsQuery, setBlobMutation, } from '@affine/graphql'; +import { fetcher } from '@affine/graphql'; +import type { BlobStorage } from '@affine/workspace'; -import { fetcher } from '../../affine/gql'; -import type { BlobStorage } from '../../engine/blob'; -import { bufferToBlob } from '../../utils/buffer-to-blob'; +import { bufferToBlob } from '../utils/buffer-to-blob'; export const createAffineCloudBlobStorage = ( workspaceId: string @@ -31,20 +30,7 @@ export const createAffineCloudBlobStorage = ( }); }, set: async (key, value) => { - const { - checkBlobSize: { size }, - } = await fetcher({ - query: checkBlobSizesQuery, - variables: { - workspaceId, - size: value.size, - }, - }); - - if (size <= 0) { - throw new Error('Blob size limit exceeded'); - } - + // set blob will check blob size & quota const result = await fetcher({ query: setBlobMutation, variables: { @@ -52,7 +38,6 @@ export const createAffineCloudBlobStorage = ( blob: new File([value], key), }, }); - console.assert(result.setBlob === key, 'Blob hash mismatch'); return result.setBlob; }, list: async () => { diff --git a/packages/frontend/workspace/src/impl/cloud/consts.ts b/packages/frontend/workspace-impl/src/cloud/consts.ts similarity index 100% rename from packages/frontend/workspace/src/impl/cloud/consts.ts rename to packages/frontend/workspace-impl/src/cloud/consts.ts diff --git a/packages/frontend/workspace/src/impl/cloud/index.ts b/packages/frontend/workspace-impl/src/cloud/index.ts similarity index 100% rename from packages/frontend/workspace/src/impl/cloud/index.ts rename to packages/frontend/workspace-impl/src/cloud/index.ts diff --git a/packages/frontend/workspace/src/impl/cloud/list.ts b/packages/frontend/workspace-impl/src/cloud/list.ts similarity index 96% rename from packages/frontend/workspace/src/impl/cloud/list.ts rename to packages/frontend/workspace-impl/src/cloud/list.ts index a4fd93f54f..26ffbed04c 100644 --- a/packages/frontend/workspace/src/impl/cloud/list.ts +++ b/packages/frontend/workspace-impl/src/cloud/list.ts @@ -4,14 +4,14 @@ import { deleteWorkspaceMutation, getWorkspacesQuery, } from '@affine/graphql'; +import { fetcher } from '@affine/graphql'; +import type { WorkspaceListProvider } from '@affine/workspace'; +import { globalBlockSuiteSchema } from '@affine/workspace'; import { Workspace as BlockSuiteWorkspace } from '@blocksuite/store'; import { difference } from 'lodash-es'; import { nanoid } from 'nanoid'; import { applyUpdate, encodeStateAsUpdate } from 'yjs'; -import { fetcher } from '../../affine/gql'; -import { globalBlockSuiteSchema } from '../../global-schema'; -import type { WorkspaceListProvider } from '../../list'; import { createLocalBlobStorage } from '../local/blob'; import { createLocalStorage } from '../local/sync'; import { CLOUD_WORKSPACE_CHANGED_BROADCAST_CHANNEL_KEY } from './consts'; diff --git a/packages/frontend/workspace/src/impl/cloud/sync/batch-sync-sender.ts b/packages/frontend/workspace-impl/src/cloud/sync/batch-sync-sender.ts similarity index 100% rename from packages/frontend/workspace/src/impl/cloud/sync/batch-sync-sender.ts rename to packages/frontend/workspace-impl/src/cloud/sync/batch-sync-sender.ts diff --git a/packages/frontend/workspace/src/impl/cloud/sync/index.ts b/packages/frontend/workspace-impl/src/cloud/sync/index.ts similarity index 96% rename from packages/frontend/workspace/src/impl/cloud/sync/index.ts rename to packages/frontend/workspace-impl/src/cloud/sync/index.ts index c5a092cce1..14f4588690 100644 --- a/packages/frontend/workspace/src/impl/cloud/sync/index.ts +++ b/packages/frontend/workspace-impl/src/cloud/sync/index.ts @@ -1,9 +1,9 @@ import { DebugLogger } from '@affine/debug'; import { fetchWithTraceReport } from '@affine/graphql'; +import type { SyncStorage } from '@affine/workspace'; -import type { SyncStorage } from '../../../engine/sync'; -import { getIoManager } from '../../../utils/affine-io'; -import { base64ToUint8Array, uint8ArrayToBase64 } from '../../../utils/base64'; +import { getIoManager } from '../../utils/affine-io'; +import { base64ToUint8Array, uint8ArrayToBase64 } from '../../utils/base64'; import { MultipleBatchSyncSender } from './batch-sync-sender'; const logger = new DebugLogger('affine:storage:socketio'); diff --git a/packages/frontend/workspace/src/impl/cloud/workspace-factory.ts b/packages/frontend/workspace-impl/src/cloud/workspace-factory.ts similarity index 90% rename from packages/frontend/workspace/src/impl/cloud/workspace-factory.ts rename to packages/frontend/workspace-impl/src/cloud/workspace-factory.ts index 1877817f61..1b747e2c1e 100644 --- a/packages/frontend/workspace/src/impl/cloud/workspace-factory.ts +++ b/packages/frontend/workspace-impl/src/cloud/workspace-factory.ts @@ -1,11 +1,11 @@ import { setupEditorFlags } from '@affine/env/global'; +import type { WorkspaceFactory } from '@affine/workspace'; +import { BlobEngine, SyncEngine, WorkspaceEngine } from '@affine/workspace'; +import { globalBlockSuiteSchema } from '@affine/workspace'; +import { Workspace } from '@affine/workspace'; import { Workspace as BlockSuiteWorkspace } from '@blocksuite/store'; import { nanoid } from 'nanoid'; -import { BlobEngine, SyncEngine, WorkspaceEngine } from '../../engine'; -import type { WorkspaceFactory } from '../../factory'; -import { globalBlockSuiteSchema } from '../../global-schema'; -import { Workspace } from '../../workspace'; import { createBroadcastChannelAwarenessProvider } from '../local/awareness'; import { createLocalBlobStorage } from '../local/blob'; import { createStaticBlobStorage } from '../local/blob-static'; diff --git a/packages/frontend/workspace-impl/src/index.ts b/packages/frontend/workspace-impl/src/index.ts new file mode 100644 index 0000000000..27a2e38a91 --- /dev/null +++ b/packages/frontend/workspace-impl/src/index.ts @@ -0,0 +1,41 @@ +import { WorkspaceList, WorkspaceManager } from '@affine/workspace'; + +import { + cloudWorkspaceFactory, + createCloudWorkspaceListProvider, +} from './cloud'; +import { + createLocalWorkspaceListProvider, + LOCAL_WORKSPACE_LOCAL_STORAGE_KEY, + localWorkspaceFactory, +} from './local'; + +const list = new WorkspaceList([ + createLocalWorkspaceListProvider(), + createCloudWorkspaceListProvider(), +]); + +export const workspaceManager = new WorkspaceManager(list, [ + localWorkspaceFactory, + cloudWorkspaceFactory, +]); + +(window as any).workspaceManager = workspaceManager; + +export * from './cloud'; +export * from './local'; + +/** + * a hack for directly add local workspace to workspace list + * Used after copying sqlite database file to appdata folder + */ +export function _addLocalWorkspace(id: string) { + const allWorkspaceIDs: string[] = JSON.parse( + localStorage.getItem(LOCAL_WORKSPACE_LOCAL_STORAGE_KEY) ?? '[]' + ); + allWorkspaceIDs.push(id); + localStorage.setItem( + LOCAL_WORKSPACE_LOCAL_STORAGE_KEY, + JSON.stringify(allWorkspaceIDs) + ); +} diff --git a/packages/frontend/workspace/src/engine/sync/__tests__/engine.spec.ts b/packages/frontend/workspace-impl/src/local/__tests__/engine.spec.ts similarity index 91% rename from packages/frontend/workspace/src/engine/sync/__tests__/engine.spec.ts rename to packages/frontend/workspace-impl/src/local/__tests__/engine.spec.ts index 963af3d3a4..30faa2f0ed 100644 --- a/packages/frontend/workspace/src/engine/sync/__tests__/engine.spec.ts +++ b/packages/frontend/workspace-impl/src/local/__tests__/engine.spec.ts @@ -1,12 +1,12 @@ import 'fake-indexeddb/auto'; +import { SyncEngine, SyncEngineStep, SyncPeerStep } from '@affine/workspace'; import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models'; import { Schema, Workspace } from '@blocksuite/store'; import { beforeEach, describe, expect, test, vi } from 'vitest'; import { Doc } from 'yjs'; -import { createIndexedDBStorage } from '../../../impl/local/sync-indexeddb'; -import { SyncEngine, SyncEngineStep, SyncPeerStep } from '../'; +import { createIndexedDBStorage } from '..'; import { createTestStorage } from './test-storage'; const schema = new Schema(); @@ -22,7 +22,7 @@ describe('SyncEngine', () => { let prev: any; { const workspace = new Workspace({ - id: 'test', + id: 'test - syncengine - indexeddb', schema, }); @@ -54,7 +54,7 @@ describe('SyncEngine', () => { { const workspace = new Workspace({ - id: 'test', + id: 'test - syncengine - indexeddb', schema, }); @@ -73,7 +73,7 @@ describe('SyncEngine', () => { { const workspace = new Workspace({ - id: 'test', + id: 'test - syncengine - indexeddb', schema, }); @@ -92,7 +92,7 @@ describe('SyncEngine', () => { { const workspace = new Workspace({ - id: 'test', + id: 'test - syncengine - indexeddb', schema, }); @@ -111,10 +111,12 @@ describe('SyncEngine', () => { }); test('status', async () => { - const ydoc = new Doc({ guid: 'test - status' }); + const ydoc = new Doc({ guid: 'test - syncengine - status' }); const localStorage = createTestStorage(createIndexedDBStorage(ydoc.guid)); - const remoteStorage = createTestStorage(createIndexedDBStorage(ydoc.guid)); + const remoteStorage = createTestStorage( + createIndexedDBStorage(ydoc.guid + '1') + ); localStorage.pausePull(); localStorage.pausePush(); diff --git a/packages/frontend/workspace/src/engine/sync/__tests__/peer.spec.ts b/packages/frontend/workspace-impl/src/local/__tests__/peer.spec.ts similarity index 90% rename from packages/frontend/workspace/src/engine/sync/__tests__/peer.spec.ts rename to packages/frontend/workspace-impl/src/local/__tests__/peer.spec.ts index d1fce352c0..11e28dc232 100644 --- a/packages/frontend/workspace/src/engine/sync/__tests__/peer.spec.ts +++ b/packages/frontend/workspace-impl/src/local/__tests__/peer.spec.ts @@ -1,11 +1,11 @@ import 'fake-indexeddb/auto'; +import { SyncPeer, SyncPeerStep } from '@affine/workspace'; import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models'; import { Schema, Workspace } from '@blocksuite/store'; import { beforeEach, describe, expect, test, vi } from 'vitest'; -import { createIndexedDBStorage } from '../../../impl/local/sync-indexeddb'; -import { SyncPeer, SyncPeerStep } from '../'; +import { createIndexedDBStorage } from '..'; const schema = new Schema(); @@ -20,7 +20,7 @@ describe('SyncPeer', () => { let prev: any; { const workspace = new Workspace({ - id: 'test', + id: 'test - syncpeer - indexeddb', schema, }); @@ -48,7 +48,7 @@ describe('SyncPeer', () => { { const workspace = new Workspace({ - id: 'test', + id: 'test - syncpeer - indexeddb', schema, }); @@ -66,7 +66,7 @@ describe('SyncPeer', () => { test('status', async () => { const workspace = new Workspace({ - id: 'test - status', + id: 'test - syncpeer - status', schema, }); diff --git a/packages/frontend/workspace/src/engine/sync/__tests__/test-storage.ts b/packages/frontend/workspace-impl/src/local/__tests__/test-storage.ts similarity index 95% rename from packages/frontend/workspace/src/engine/sync/__tests__/test-storage.ts rename to packages/frontend/workspace-impl/src/local/__tests__/test-storage.ts index 07cd557166..a0653d4d30 100644 --- a/packages/frontend/workspace/src/engine/sync/__tests__/test-storage.ts +++ b/packages/frontend/workspace-impl/src/local/__tests__/test-storage.ts @@ -1,4 +1,4 @@ -import type { SyncStorage } from '..'; +import type { SyncStorage } from '@affine/workspace'; export function createTestStorage(origin: SyncStorage) { const controler = { diff --git a/packages/frontend/workspace/src/impl/local/awareness.ts b/packages/frontend/workspace-impl/src/local/awareness.ts similarity index 93% rename from packages/frontend/workspace/src/impl/local/awareness.ts rename to packages/frontend/workspace-impl/src/local/awareness.ts index b012333bd4..8674046c2d 100644 --- a/packages/frontend/workspace/src/impl/local/awareness.ts +++ b/packages/frontend/workspace-impl/src/local/awareness.ts @@ -1,11 +1,10 @@ +import type { AwarenessProvider } from '@affine/workspace'; import type { Awareness } from 'y-protocols/awareness.js'; import { applyAwarenessUpdate, encodeAwarenessUpdate, } from 'y-protocols/awareness.js'; -import type { AwarenessProvider } from '../../engine/awareness'; - type AwarenessChanges = Record<'added' | 'updated' | 'removed', number[]>; type ChannelMessage = @@ -23,10 +22,9 @@ export function createBroadcastChannelAwarenessProvider( return; } - const changedClients = Object.values(changes).reduce((res, cur) => [ - ...res, - ...cur, - ]); + const changedClients = Object.values(changes).reduce((res, cur) => + res.concat(cur) + ); const update = encodeAwarenessUpdate(awareness, changedClients); channel.postMessage({ diff --git a/packages/frontend/workspace/src/impl/local/blob-indexeddb.ts b/packages/frontend/workspace-impl/src/local/blob-indexeddb.ts similarity index 88% rename from packages/frontend/workspace/src/impl/local/blob-indexeddb.ts rename to packages/frontend/workspace-impl/src/local/blob-indexeddb.ts index 8117151306..fe9ec6314a 100644 --- a/packages/frontend/workspace/src/impl/local/blob-indexeddb.ts +++ b/packages/frontend/workspace-impl/src/local/blob-indexeddb.ts @@ -1,7 +1,7 @@ +import type { BlobStorage } from '@affine/workspace'; import { createStore, del, get, keys, set } from 'idb-keyval'; -import type { BlobStorage } from '../../engine/blob'; -import { bufferToBlob } from '../../utils/buffer-to-blob'; +import { bufferToBlob } from '../utils/buffer-to-blob'; export const createIndexeddbBlobStorage = ( workspaceId: string diff --git a/packages/frontend/workspace/src/impl/local/blob-sqlite.ts b/packages/frontend/workspace-impl/src/local/blob-sqlite.ts similarity index 74% rename from packages/frontend/workspace/src/impl/local/blob-sqlite.ts rename to packages/frontend/workspace-impl/src/local/blob-sqlite.ts index 1188936133..f92ba06e51 100644 --- a/packages/frontend/workspace/src/impl/local/blob-sqlite.ts +++ b/packages/frontend/workspace-impl/src/local/blob-sqlite.ts @@ -1,15 +1,16 @@ +import { apis } from '@affine/electron-api'; +import type { BlobStorage } from '@affine/workspace'; import { assertExists } from '@blocksuite/global/utils'; -import type { BlobStorage } from '../../engine/blob'; -import { bufferToBlob } from '../../utils/buffer-to-blob'; +import { bufferToBlob } from '../utils/buffer-to-blob'; export const createSQLiteBlobStorage = (workspaceId: string): BlobStorage => { - const apis = window.apis; assertExists(apis); return { name: 'sqlite', readonly: false, get: async (key: string) => { + assertExists(apis); const buffer = await apis.db.getBlob(workspaceId, key); if (buffer) { return bufferToBlob(buffer); @@ -17,6 +18,7 @@ export const createSQLiteBlobStorage = (workspaceId: string): BlobStorage => { return null; }, set: async (key: string, value: Blob) => { + assertExists(apis); await apis.db.addBlob( workspaceId, key, @@ -25,9 +27,11 @@ export const createSQLiteBlobStorage = (workspaceId: string): BlobStorage => { return key; }, delete: async (key: string) => { + assertExists(apis); return apis.db.deleteBlob(workspaceId, key); }, list: async () => { + assertExists(apis); return apis.db.getBlobKeys(workspaceId); }, }; diff --git a/packages/frontend/workspace/src/impl/local/blob-static.ts b/packages/frontend/workspace-impl/src/local/blob-static.ts similarity index 97% rename from packages/frontend/workspace/src/impl/local/blob-static.ts rename to packages/frontend/workspace-impl/src/local/blob-static.ts index 0ac9fcaeff..35ff5d1299 100644 --- a/packages/frontend/workspace/src/impl/local/blob-static.ts +++ b/packages/frontend/workspace-impl/src/local/blob-static.ts @@ -1,4 +1,4 @@ -import type { BlobStorage } from '../../engine/blob'; +import type { BlobStorage } from '@affine/workspace'; export const predefinedStaticFiles = [ '029uztLz2CzJezK7UUhrbGiWUdZ0J7NVs_qR6RDsvb8=', diff --git a/packages/frontend/workspace/src/impl/local/blob.ts b/packages/frontend/workspace-impl/src/local/blob.ts similarity index 100% rename from packages/frontend/workspace/src/impl/local/blob.ts rename to packages/frontend/workspace-impl/src/local/blob.ts diff --git a/packages/frontend/workspace/src/impl/local/consts.ts b/packages/frontend/workspace-impl/src/local/consts.ts similarity index 100% rename from packages/frontend/workspace/src/impl/local/consts.ts rename to packages/frontend/workspace-impl/src/local/consts.ts diff --git a/packages/frontend/workspace/src/impl/local/index.ts b/packages/frontend/workspace-impl/src/local/index.ts similarity index 100% rename from packages/frontend/workspace/src/impl/local/index.ts rename to packages/frontend/workspace-impl/src/local/index.ts diff --git a/packages/frontend/workspace/src/impl/local/list.ts b/packages/frontend/workspace-impl/src/local/list.ts similarity index 93% rename from packages/frontend/workspace/src/impl/local/list.ts rename to packages/frontend/workspace-impl/src/local/list.ts index afe3088bc2..d147da76d2 100644 --- a/packages/frontend/workspace/src/impl/local/list.ts +++ b/packages/frontend/workspace-impl/src/local/list.ts @@ -1,11 +1,12 @@ +import { apis } from '@affine/electron-api'; import { WorkspaceFlavour } from '@affine/env/workspace'; +import type { WorkspaceListProvider } from '@affine/workspace'; +import { globalBlockSuiteSchema } from '@affine/workspace'; import { Workspace as BlockSuiteWorkspace } from '@blocksuite/store'; import { difference } from 'lodash-es'; import { nanoid } from 'nanoid'; import { applyUpdate, encodeStateAsUpdate } from 'yjs'; -import { globalBlockSuiteSchema } from '../../global-schema'; -import type { WorkspaceListProvider } from '../../list'; import { createLocalBlobStorage } from './blob'; import { LOCAL_WORKSPACE_CREATED_BROADCAST_CHANNEL_KEY, @@ -96,8 +97,8 @@ export function createLocalWorkspaceListProvider(): WorkspaceListProvider { JSON.stringify(allWorkspaceIDs.filter(x => x !== workspaceId)) ); - if (window.apis && environment.isDesktop) { - await window.apis.workspace.delete(workspaceId); + if (apis && environment.isDesktop) { + await apis.workspace.delete(workspaceId); } // notify all browser tabs, so they can update their workspace list diff --git a/packages/frontend/workspace/src/impl/local/sync-indexeddb.ts b/packages/frontend/workspace-impl/src/local/sync-indexeddb.ts similarity index 96% rename from packages/frontend/workspace/src/impl/local/sync-indexeddb.ts rename to packages/frontend/workspace-impl/src/local/sync-indexeddb.ts index c6cedffa5c..fe418239e9 100644 --- a/packages/frontend/workspace/src/impl/local/sync-indexeddb.ts +++ b/packages/frontend/workspace-impl/src/local/sync-indexeddb.ts @@ -1,8 +1,8 @@ +import type { SyncStorage } from '@affine/workspace'; import { type DBSchema, type IDBPDatabase, openDB } from 'idb'; import { diffUpdate, encodeStateVectorFromUpdate } from 'yjs'; -import type { SyncStorage } from '../../engine/sync'; -import { mergeUpdates } from '../../utils/merge-updates'; +import { mergeUpdates } from '../utils/merge-updates'; export const dbVersion = 1; export const DEFAULT_DB_NAME = 'affine-local'; diff --git a/packages/frontend/workspace/src/impl/local/sync-sqlite.ts b/packages/frontend/workspace-impl/src/local/sync-sqlite.ts similarity index 63% rename from packages/frontend/workspace/src/impl/local/sync-sqlite.ts rename to packages/frontend/workspace-impl/src/local/sync-sqlite.ts index 1f103e5778..a0f3b99c93 100644 --- a/packages/frontend/workspace/src/impl/local/sync-sqlite.ts +++ b/packages/frontend/workspace-impl/src/local/sync-sqlite.ts @@ -1,16 +1,19 @@ +import { apis } from '@affine/electron-api'; +import type { SyncStorage } from '@affine/workspace'; import { encodeStateVectorFromUpdate } from 'yjs'; -import type { SyncStorage } from '../../engine/sync'; - export function createSQLiteStorage(workspaceId: string): SyncStorage { - if (!window.apis?.db) { + if (!apis?.db) { throw new Error('sqlite datasource is not available'); } return { name: 'sqlite', async pull(docId, _state) { - const update = await window.apis.db.getDocAsUpdates( + if (!apis?.db) { + throw new Error('sqlite datasource is not available'); + } + const update = await apis.db.getDocAsUpdates( workspaceId, workspaceId === docId ? undefined : docId ); @@ -25,7 +28,10 @@ export function createSQLiteStorage(workspaceId: string): SyncStorage { return null; }, async push(docId, data) { - return window.apis.db.applyDocUpdate( + if (!apis?.db) { + throw new Error('sqlite datasource is not available'); + } + return apis.db.applyDocUpdate( workspaceId, data, workspaceId === docId ? undefined : docId diff --git a/packages/frontend/workspace/src/impl/local/sync.ts b/packages/frontend/workspace-impl/src/local/sync.ts similarity index 100% rename from packages/frontend/workspace/src/impl/local/sync.ts rename to packages/frontend/workspace-impl/src/local/sync.ts diff --git a/packages/frontend/workspace/src/impl/local/workspace-factory.ts b/packages/frontend/workspace-impl/src/local/workspace-factory.ts similarity index 81% rename from packages/frontend/workspace/src/impl/local/workspace-factory.ts rename to packages/frontend/workspace-impl/src/local/workspace-factory.ts index 08ed43a187..d143924142 100644 --- a/packages/frontend/workspace/src/impl/local/workspace-factory.ts +++ b/packages/frontend/workspace-impl/src/local/workspace-factory.ts @@ -1,13 +1,13 @@ import { setupEditorFlags } from '@affine/env/global'; +import type { WorkspaceFactory } from '@affine/workspace'; +import { WorkspaceEngine } from '@affine/workspace'; +import { BlobEngine } from '@affine/workspace'; +import { SyncEngine } from '@affine/workspace'; +import { globalBlockSuiteSchema } from '@affine/workspace'; +import { Workspace } from '@affine/workspace'; import { Workspace as BlockSuiteWorkspace } from '@blocksuite/store'; import { nanoid } from 'nanoid'; -import { WorkspaceEngine } from '../../engine'; -import { BlobEngine } from '../../engine/blob'; -import { SyncEngine } from '../../engine/sync'; -import type { WorkspaceFactory } from '../../factory'; -import { globalBlockSuiteSchema } from '../../global-schema'; -import { Workspace } from '../../workspace'; import { createBroadcastChannelAwarenessProvider } from './awareness'; import { createLocalBlobStorage } from './blob'; import { createStaticBlobStorage } from './blob-static'; diff --git a/packages/frontend/workspace/src/utils/__tests__/buffer-to-blob.spec.ts b/packages/frontend/workspace-impl/src/utils/__tests__/buffer-to-blob.spec.ts similarity index 100% rename from packages/frontend/workspace/src/utils/__tests__/buffer-to-blob.spec.ts rename to packages/frontend/workspace-impl/src/utils/__tests__/buffer-to-blob.spec.ts diff --git a/packages/frontend/workspace/src/utils/affine-io.ts b/packages/frontend/workspace-impl/src/utils/affine-io.ts similarity index 100% rename from packages/frontend/workspace/src/utils/affine-io.ts rename to packages/frontend/workspace-impl/src/utils/affine-io.ts diff --git a/packages/frontend/workspace/src/utils/base64.ts b/packages/frontend/workspace-impl/src/utils/base64.ts similarity index 100% rename from packages/frontend/workspace/src/utils/base64.ts rename to packages/frontend/workspace-impl/src/utils/base64.ts diff --git a/packages/frontend/workspace/src/utils/buffer-to-blob.ts b/packages/frontend/workspace-impl/src/utils/buffer-to-blob.ts similarity index 100% rename from packages/frontend/workspace/src/utils/buffer-to-blob.ts rename to packages/frontend/workspace-impl/src/utils/buffer-to-blob.ts diff --git a/packages/frontend/workspace-impl/src/utils/merge-updates.ts b/packages/frontend/workspace-impl/src/utils/merge-updates.ts new file mode 100644 index 0000000000..e3c8a4a06a --- /dev/null +++ b/packages/frontend/workspace-impl/src/utils/merge-updates.ts @@ -0,0 +1,17 @@ +import { applyUpdate, Doc, encodeStateAsUpdate } from 'yjs'; + +export function mergeUpdates(updates: Uint8Array[]) { + if (updates.length === 0) { + return new Uint8Array(); + } + if (updates.length === 1) { + return updates[0]; + } + const doc = new Doc(); + doc.transact(() => { + updates.forEach(update => { + applyUpdate(doc, update); + }); + }); + return encodeStateAsUpdate(doc); +} diff --git a/packages/frontend/hooks/tsconfig.json b/packages/frontend/workspace-impl/tsconfig.json similarity index 67% rename from packages/frontend/hooks/tsconfig.json rename to packages/frontend/workspace-impl/tsconfig.json index 6351cba59f..15af0a0980 100644 --- a/packages/frontend/hooks/tsconfig.json +++ b/packages/frontend/workspace-impl/tsconfig.json @@ -2,15 +2,15 @@ "extends": "../../../tsconfig.json", "include": ["./src"], "compilerOptions": { - "composite": true, "noEmit": false, "outDir": "lib" }, "references": [ + { "path": "../../../tests/fixtures" }, { "path": "../../common/env" }, - { "path": "../../common/y-indexeddb" }, { "path": "../../common/debug" }, { "path": "../../common/infra" }, - { "path": "../workspace" } + { "path": "../../frontend/graphql" }, + { "path": "../../frontend/electron-api" } ] } diff --git a/packages/frontend/workspace/src/impl/index.ts b/packages/frontend/workspace/src/impl/index.ts deleted file mode 100644 index a2d3a7baa9..0000000000 --- a/packages/frontend/workspace/src/impl/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './cloud'; -export * from './local'; diff --git a/packages/frontend/workspace/src/index.ts b/packages/frontend/workspace/src/index.ts deleted file mode 100644 index 84c8aa193b..0000000000 --- a/packages/frontend/workspace/src/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { - cloudWorkspaceFactory, - createCloudWorkspaceListProvider, - createLocalWorkspaceListProvider, - localWorkspaceFactory, -} from './impl'; -import { WorkspaceList } from './list'; -import { WorkspaceManager } from './manager'; - -const list = new WorkspaceList([ - createLocalWorkspaceListProvider(), - createCloudWorkspaceListProvider(), -]); - -export const workspaceManager = new WorkspaceManager(list, [ - localWorkspaceFactory, - cloudWorkspaceFactory, -]); - -(window as any).workspaceManager = workspaceManager; - -export * from './engine'; -export * from './factory'; -export * from './global-schema'; -export * from './impl'; -export * from './list'; -export * from './manager'; -export * from './metadata'; -export * from './workspace'; diff --git a/packages/plugins/copilot/README.md b/packages/plugins/copilot/README.md deleted file mode 100644 index 578c09e87d..0000000000 --- a/packages/plugins/copilot/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# AFFiNE Copilot - -> AI Copilot Plugin for your writing diff --git a/packages/plugins/copilot/package.json b/packages/plugins/copilot/package.json deleted file mode 100644 index 9a17c79763..0000000000 --- a/packages/plugins/copilot/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "@affine/copilot-plugin", - "type": "module", - "private": true, - "description": "Copilot plugin", - "affinePlugin": { - "release": false, - "entry": { - "core": "./src/index.ts" - } - }, - "scripts": { - "dev": "af dev", - "build": "af build" - }, - "dependencies": { - "@affine/component": "workspace:*", - "@affine/sdk": "workspace:*", - "@blocksuite/icons": "2.1.36", - "@vanilla-extract/css": "^1.13.0", - "clsx": "^2.0.0", - "idb": "^8.0.0", - "langchain": "^0.0.166", - "marked": "^11.0.0", - "marked-gfm-heading-id": "^3.1.0", - "marked-mangle": "^1.1.4", - "zod": "^3.22.4" - }, - "devDependencies": { - "@affine/plugin-cli": "workspace:*", - "@types/marked": "^6.0.0", - "jotai": "^2.5.1", - "react": "18.2.0", - "react-dom": "18.2.0" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - }, - "version": "0.11.0" -} diff --git a/packages/plugins/copilot/project.json b/packages/plugins/copilot/project.json deleted file mode 100644 index f683b4b6a9..0000000000 --- a/packages/plugins/copilot/project.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "@affine/copilot-plugin", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "namedInputs": { - "default": [ - "{projectRoot}/**/*", - "{workspaceRoot}/tools/plugin-cli/src/**/*", - "sharedGlobals" - ] - }, - "targets": { - "build": { - "executor": "nx:run-script", - "options": { - "script": "build" - }, - "dependsOn": ["^build"], - "inputs": ["default"], - "outputs": [ - "{workspaceRoot}/packages/frontend/core/public/plugins/copilot", - "{workspaceRoot}/packages/frontend/electron/dist/plugins/copilot" - ] - } - }, - "tags": ["plugin"] -} diff --git a/packages/plugins/copilot/src/UI/debug-content.tsx b/packages/plugins/copilot/src/UI/debug-content.tsx deleted file mode 100644 index 5ccea36fb8..0000000000 --- a/packages/plugins/copilot/src/UI/debug-content.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { FlexWrapper, Input } from '@affine/component'; -import { Button } from '@affine/component/ui/button'; -import { useAtom } from 'jotai'; -import { type ReactElement, useCallback } from 'react'; - -import { openAIApiKeyAtom } from '../core/hooks'; -import { conversationHistoryDBName } from '../core/langchain/message-history'; - -export const DebugContent = (): ReactElement => { - const [key, setKey] = useAtom(openAIApiKeyAtom); - return ( -
- - { - setKey(newValue); - }, - [setKey] - )} - placeholder="Enter your API_KEY here" - /> - - -
- ); -}; diff --git a/packages/plugins/copilot/src/UI/detail-content.tsx b/packages/plugins/copilot/src/UI/detail-content.tsx deleted file mode 100644 index 78855ff672..0000000000 --- a/packages/plugins/copilot/src/UI/detail-content.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { IconButton } from '@affine/component/ui/button'; -import { SendIcon } from '@blocksuite/icons'; -import { useAtomValue, useSetAtom } from 'jotai'; -import type { ReactElement } from 'react'; -import { Suspense, useCallback, useState } from 'react'; - -import { ConversationList } from '../core/components/conversation-list'; -import { FollowingUp } from '../core/components/following-up'; -import { openAIApiKeyAtom, useChatAtoms } from '../core/hooks'; -import { - detailContentActionsStyle, - detailContentStyle, - sendButtonStyle, - textareaStyle, -} from './index.css'; - -const Actions = () => { - const { conversationAtom, followingUpAtoms } = useChatAtoms(); - const call = useSetAtom(conversationAtom); - const questions = useAtomValue(followingUpAtoms.questionsAtom); - const generateFollowingUp = useSetAtom(followingUpAtoms.generateChatAtom); - const [input, setInput] = useState(''); - return ( - <> - -
-