Merge remote-tracking branch 'origin/canary' into stable

This commit is contained in:
LongYinan
2023-11-28 15:12:52 +08:00
63 changed files with 2109 additions and 410 deletions
Generated
+101 -43
View File
@@ -83,14 +83,15 @@ dependencies = [
[[package]]
name = "ahash"
version = "0.8.3"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
dependencies = [
"cfg-if",
"getrandom",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
@@ -240,6 +241,16 @@ dependencies = [
"num-traits",
]
[[package]]
name = "atomic-write-file"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c232177ba50b16fe7a4588495bd474a62a9e45a8e4ca6fd7d0b7ac29d164631e"
dependencies = [
"nix",
"rand",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@@ -941,7 +952,7 @@ version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
dependencies = [
"ahash 0.8.3",
"ahash 0.8.6",
]
[[package]]
@@ -950,7 +961,7 @@ version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
dependencies = [
"ahash 0.8.3",
"ahash 0.8.6",
"allocator-api2",
]
@@ -1112,7 +1123,7 @@ dependencies = [
[[package]]
name = "jwst-codec"
version = "0.1.0"
source = "git+https://github.com/toeverything/OctoBase.git?rev=aad9e5b#aad9e5b7e9d6f479e6cf7555f5845bbbaaadbc66"
source = "git+https://github.com/toeverything/OctoBase.git?rev=49a6b7a#49a6b7af25ce1fe54e8383e10980e9536821d286"
dependencies = [
"arbitrary",
"bitvec",
@@ -1133,7 +1144,7 @@ dependencies = [
[[package]]
name = "jwst-core"
version = "0.1.0"
source = "git+https://github.com/toeverything/OctoBase.git?rev=aad9e5b#aad9e5b7e9d6f479e6cf7555f5845bbbaaadbc66"
source = "git+https://github.com/toeverything/OctoBase.git?rev=49a6b7a#49a6b7af25ce1fe54e8383e10980e9536821d286"
dependencies = [
"async-trait",
"base64",
@@ -1151,7 +1162,7 @@ dependencies = [
[[package]]
name = "jwst-logger"
version = "0.1.0"
source = "git+https://github.com/toeverything/OctoBase.git?rev=aad9e5b#aad9e5b7e9d6f479e6cf7555f5845bbbaaadbc66"
source = "git+https://github.com/toeverything/OctoBase.git?rev=49a6b7a#49a6b7af25ce1fe54e8383e10980e9536821d286"
dependencies = [
"chrono",
"nu-ansi-term 0.49.0",
@@ -1164,7 +1175,7 @@ dependencies = [
[[package]]
name = "jwst-storage"
version = "0.1.0"
source = "git+https://github.com/toeverything/OctoBase.git?rev=aad9e5b#aad9e5b7e9d6f479e6cf7555f5845bbbaaadbc66"
source = "git+https://github.com/toeverything/OctoBase.git?rev=49a6b7a#49a6b7af25ce1fe54e8383e10980e9536821d286"
dependencies = [
"anyhow",
"async-trait",
@@ -1189,7 +1200,7 @@ dependencies = [
[[package]]
name = "jwst-storage-migration"
version = "0.1.0"
source = "git+https://github.com/toeverything/OctoBase.git?rev=aad9e5b#aad9e5b7e9d6f479e6cf7555f5845bbbaaadbc66"
source = "git+https://github.com/toeverything/OctoBase.git?rev=49a6b7a#49a6b7af25ce1fe54e8383e10980e9536821d286"
dependencies = [
"sea-orm-migration",
"tokio",
@@ -1257,9 +1268,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]]
name = "libsqlite3-sys"
version = "0.26.0"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326"
checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716"
dependencies = [
"cc",
"pkg-config",
@@ -1337,6 +1348,15 @@ version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "memoffset"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"autocfg",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@@ -1375,9 +1395,9 @@ dependencies = [
[[package]]
name = "napi"
version = "2.14.0"
version = "2.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d90182620f32fe34b6ac9b52cba898af26e94c7f5abc01eb4094c417ae2e6c"
checksum = "1133249c46e92da921bafc8aba4912bf84d6c475f7625183772ed2d0844dc3a7"
dependencies = [
"anyhow",
"bitflags 2.4.1",
@@ -1399,9 +1419,9 @@ checksum = "d4b4532cf86bfef556348ac65e561e3123879f0e7566cca6d43a6ff5326f13df"
[[package]]
name = "napi-derive"
version = "2.14.1"
version = "2.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3619fa472d23cd5af94d63a2bae454a77a8863251f40230fbf59ce20eafa8a86"
checksum = "a0cca5738c6e81eb5ffd2c8ff2b4f05ece9c4c60c7e2b36cec6524492cf7f330"
dependencies = [
"cfg-if",
"convert_case",
@@ -1413,9 +1433,9 @@ dependencies = [
[[package]]
name = "napi-derive-backend"
version = "1.0.54"
version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecd3ea4b54020c73d591a49cd192f6334c5f37f71a63ead54dbc851fa991ef00"
checksum = "35960e5f33228192a9b661447d0dfe8f5a3790ff5b4058c4d67680ded4f65b91"
dependencies = [
"convert_case",
"once_cell",
@@ -1435,6 +1455,19 @@ dependencies = [
"libloading",
]
[[package]]
name = "nix"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
dependencies = [
"bitflags 1.3.2",
"cfg-if",
"libc",
"memoffset",
"pin-utils",
]
[[package]]
name = "no-std-compat"
version = "0.4.1"
@@ -2292,18 +2325,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
version = "1.0.192"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.192"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
@@ -2450,9 +2483,9 @@ dependencies = [
[[package]]
name = "sqlx"
version = "0.7.2"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e50c216e3624ec8e7ecd14c6a6a6370aad6ee5d8cfc3ab30b5162eeeef2ed33"
checksum = "dba03c279da73694ef99763320dea58b51095dfe87d001b1d4b5fe78ba8763cf"
dependencies = [
"sqlx-core",
"sqlx-macros",
@@ -2463,11 +2496,11 @@ dependencies = [
[[package]]
name = "sqlx-core"
version = "0.7.2"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d6753e460c998bbd4cd8c6f0ed9a64346fcca0723d6e75e52fdc351c5d2169d"
checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd"
dependencies = [
"ahash 0.8.3",
"ahash 0.8.6",
"atoi",
"bigdecimal",
"byteorder",
@@ -2511,9 +2544,9 @@ dependencies = [
[[package]]
name = "sqlx-macros"
version = "0.7.2"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a793bb3ba331ec8359c1853bd39eed32cdd7baaf22c35ccf5c92a7e8d1189ec"
checksum = "89961c00dc4d7dffb7aee214964b065072bff69e36ddb9e2c107541f75e4f2a5"
dependencies = [
"proc-macro2",
"quote",
@@ -2524,10 +2557,11 @@ dependencies = [
[[package]]
name = "sqlx-macros-core"
version = "0.7.2"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a4ee1e104e00dedb6aa5ffdd1343107b0a4702e862a84320ee7cc74782d96fc"
checksum = "d0bd4519486723648186a08785143599760f7cc81c52334a55d6a83ea1e20841"
dependencies = [
"atomic-write-file",
"dotenvy",
"either",
"heck",
@@ -2550,9 +2584,9 @@ dependencies = [
[[package]]
name = "sqlx-mysql"
version = "0.7.2"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db"
checksum = "e37195395df71fd068f6e2082247891bc11e3289624bbc776a0cdfa1ca7f1ea4"
dependencies = [
"atoi",
"base64",
@@ -2597,9 +2631,9 @@ dependencies = [
[[package]]
name = "sqlx-postgres"
version = "0.7.2"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624"
checksum = "d6ac0ac3b7ccd10cc96c7ab29791a7dd236bd94021f31eec7ba3d46a74aa1c24"
dependencies = [
"atoi",
"base64",
@@ -2642,9 +2676,9 @@ dependencies = [
[[package]]
name = "sqlx-sqlite"
version = "0.7.2"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59dc83cf45d89c555a577694534fcd1b55c545a816c816ce51f20bbe56a4f3f"
checksum = "210976b7d948c7ba9fced8ca835b11cbb2d677c59c79de41ac0d397e14547490"
dependencies = [
"atoi",
"chrono",
@@ -2662,6 +2696,7 @@ dependencies = [
"time",
"tracing",
"url",
"urlencoding",
"uuid",
]
@@ -3024,6 +3059,12 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "urlencoding"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "utf8parse"
version = "0.2.1"
@@ -3032,9 +3073,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "uuid"
version = "1.6.0"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c58fe91d841bc04822c9801002db4ea904b9e4b8e6bbad25127b46eff8dc516b"
checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
dependencies = [
"getrandom",
"rand",
@@ -3141,12 +3182,9 @@ dependencies = [
[[package]]
name = "webpki-roots"
version = "0.24.0"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888"
dependencies = [
"rustls-webpki",
]
checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10"
[[package]]
name = "whoami"
@@ -3278,6 +3316,26 @@ dependencies = [
"tap",
]
[[package]]
name = "zerocopy"
version = "0.7.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
]
[[package]]
name = "zeroize"
version = "1.6.0"
+28
View File
@@ -0,0 +1,28 @@
# Issues Triaging
When we receive your issue, we will first triaging it. Triaging an issue usually takes around one business day but may take longer. Goal of triaging is to provide you with a clear understanding of what will happen to your issue. For example, after your feature request was triaged you know whether we plan to tackle the issue or whether we'll wait to hear what the broader community thinks about this request.
Here are issue states and their descriptions:
| State | Description |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Untriaged | The team has not yet reviewed the issue. We usually do it within one business day. |
| As designed | The behavior described in the issue is intentional. If you find it seriously disruptive or if weve misunderstood you, please let us know in the issues comments section. |
| Blocked | We cant work on this issue until another one (linked) is resolved. |
| Cant Reproduce | We have been unable to reproduce the issue on our side. It could be flaky or fixed already, or we may not have had all the details we needed. If youre still experiencing the issue and have any further details, please share them. |
| Duplicate | The issue is the same (or has the same cause) as another one (linked). |
| Fixed | If the issue was a bug, its been fixed; if it was a missing feature, its been implemented. |
| Fixed In Branch | If the issue was a bug, its been fixed; if it was a missing feature, its been implemented; the changes are now in a separate branch and havent been merged into the default branch yet. |
| In Progress | Were currently working on the issue. |
| Incomplete | Unfortunately we dont have enough information to proceed. If youre willing to share any further details about the issue, please do so in the comments. |
| Obsolete | The part of the product that was causing this issue has been removed or significantly reworked since it was created. |
| Upvoting | We are currently evaluating demand for the issue and checking whether it requires complicated or risky changes. Please leave a vote or comment if you think it should be prioritized. |
| Open | We want to implement the fix or feature in the near future. We cant promise it will appear in the next public release, but its on our short list. |
| Shelved | We have reviewed the issue and decided that, even though it has merit, we cannot currently include it in our near-term plan. |
| Third Party Problem | The issue is caused by a third party. We've done our best to inform them about it. |
| To be Discussed | We need some time to discuss the issue. |
| To Reproduce | We will try to find the steps needed to reproduce the issue on our side. |
| Under Investigation | Weve triaged the issue, but now we need to investigate it more thoroughly. This may require processing additional information like logs or dumps. |
| Waiting for Info | Weve requested additional information from the person who created the issue and are waiting for them to get back to us. |
| Declined | Weve reviewed the suggestion and, while we appreciate its value, we unfortunately do not have the resources to implement it. |
| Answered | The issue actually turned out to be a question or a misunderstanding, and it has been answered or resolved. |
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "optimized_blobs" ADD COLUMN "deleted_at" TIMESTAMPTZ(6);
+8 -6
View File
@@ -178,13 +178,15 @@ model Blob {
}
model OptimizedBlob {
id Int @id @default(autoincrement()) @db.Integer
hash String @db.VarChar
workspaceId String @map("workspace_id") @db.VarChar
params String @db.VarChar
blob Bytes @db.ByteA
id Int @id @default(autoincrement()) @db.Integer
hash String @db.VarChar
workspaceId String @map("workspace_id") @db.VarChar
params String @db.VarChar
blob Bytes @db.ByteA
length BigInt
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
// not for keeping, but for snapshot history
deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6)
@@unique([workspaceId, hash, params])
@@map("optimized_blobs")
@@ -0,0 +1,30 @@
import { Module } from '@nestjs/common';
import { Field, ObjectType, Query } from '@nestjs/graphql';
import { SERVER_FLAVOR } from '../modules';
@ObjectType()
export class ServerConfigType {
@Field({ description: 'server version' })
version!: string;
@Field({ description: 'server flavor' })
flavor!: string;
}
export class ServerConfigResolver {
@Query(() => ServerConfigType, {
description: 'server config',
})
serverConfig(): ServerConfigType {
return {
version: AFFiNE.version,
flavor: SERVER_FLAVOR || 'allinone',
};
}
}
@Module({
providers: [ServerConfigResolver],
})
export class ServerConfigModule {}
@@ -90,7 +90,7 @@ export class DocHistoryManager {
workspaceId,
id,
timestamp: {
lte: before,
lt: before,
},
// only include the ones has not expired
expiredAt: {
+18 -5
View File
@@ -3,9 +3,10 @@ import { EventEmitterModule } from '@nestjs/event-emitter';
import { ScheduleModule } from '@nestjs/schedule';
import { GqlModule } from '../graphql.module';
import { AuthModule } from './auth';
import { ServerConfigModule } from './config';
import { DocModule } from './doc';
import { PaymentModule } from './payment';
import { SelfHostedModule } from './self-hosted';
import { SyncModule } from './sync';
import { UsersModule } from './users';
import { WorkspaceModule } from './workspaces';
@@ -22,13 +23,25 @@ switch (SERVER_FLAVOR) {
case 'sync':
BusinessModules.push(SyncModule, DocModule.forSync());
break;
case 'graphql':
case 'selfhosted':
BusinessModules.push(
ServerConfigModule,
SelfHostedModule,
ScheduleModule.forRoot(),
GqlModule,
WorkspaceModule,
UsersModule,
SyncModule,
DocModule.forRoot()
);
break;
case 'graphql':
BusinessModules.push(
ServerConfigModule,
ScheduleModule.forRoot(),
GqlModule,
WorkspaceModule,
UsersModule,
AuthModule,
DocModule.forRoot(),
PaymentModule
);
@@ -36,11 +49,11 @@ switch (SERVER_FLAVOR) {
case 'allinone':
default:
BusinessModules.push(
ServerConfigModule,
ScheduleModule.forRoot(),
GqlModule,
WorkspaceModule,
UsersModule,
AuthModule,
SyncModule,
DocModule.forRoot(),
PaymentModule
@@ -48,4 +61,4 @@ switch (SERVER_FLAVOR) {
break;
}
export { BusinessModules };
export { BusinessModules, SERVER_FLAVOR };
@@ -52,7 +52,7 @@ class SubscriptionPrice {
}
@ObjectType('UserSubscription')
class UserSubscriptionType implements Partial<UserSubscription> {
export class UserSubscriptionType implements Partial<UserSubscription> {
@Field({ name: 'id' })
stripeSubscriptionId!: string;
@@ -30,6 +30,7 @@ export enum SubscriptionPlan {
Pro = 'pro',
Team = 'team',
Enterprise = 'enterprise',
SelfHosted = 'selfhosted',
}
export function encodeLookupKey(
@@ -0,0 +1,38 @@
import { Module } from '@nestjs/common';
import { ResolveField, Resolver } from '@nestjs/graphql';
import { UserSubscriptionType } from './payment/resolver';
import {
SubscriptionPlan,
SubscriptionRecurring,
SubscriptionStatus,
} from './payment/service';
import { UserType } from './users';
const YEAR = 1000 * 60 * 60 * 24 * 30 * 12;
@Resolver(() => UserType)
export class SelfHostedDummyResolver {
private readonly start = new Date();
private readonly end = new Date(Number(this.start) + YEAR);
constructor() {}
@ResolveField(() => UserSubscriptionType)
async subscription() {
return {
stripeSubscriptionId: 'dummy',
plan: SubscriptionPlan.SelfHosted,
recurring: SubscriptionRecurring.Yearly,
status: SubscriptionStatus.Active,
start: this.start,
end: this.end,
createdAt: this.start,
updatedAt: this.start,
};
}
}
@Module({
providers: [SelfHostedDummyResolver],
})
export class SelfHostedModule {}
+12
View File
@@ -2,6 +2,14 @@
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
# ------------------------------------------------------
type ServerConfigType {
"""server version"""
version: String!
"""server flavor"""
flavor: String!
}
type UserType {
id: ID!
@@ -73,6 +81,7 @@ enum SubscriptionPlan {
Pro
Team
Enterprise
SelfHosted
}
type UserSubscription {
@@ -240,6 +249,9 @@ type DocHistoryType {
}
type Query {
"""server config"""
serverConfig: ServerConfigType!
"""Get is owner of workspace"""
isOwner(workspaceId: String!): Boolean!
+3 -3
View File
@@ -8,9 +8,9 @@ crate-type = ["cdylib"]
[dependencies]
chrono = "0.4"
jwst-codec = { git = "https://github.com/toeverything/OctoBase.git", rev = "aad9e5b" }
jwst-core = { git = "https://github.com/toeverything/OctoBase.git", rev = "aad9e5b" }
jwst-storage = { git = "https://github.com/toeverything/OctoBase.git", rev = "aad9e5b" }
jwst-codec = { git = "https://github.com/toeverything/OctoBase.git", rev = "49a6b7a" }
jwst-core = { git = "https://github.com/toeverything/OctoBase.git", rev = "49a6b7a" }
jwst-storage = { git = "https://github.com/toeverything/OctoBase.git", rev = "49a6b7a" }
napi = { version = "2", default-features = false, features = [
"napi5",
"async",
+2 -2
View File
@@ -3,8 +3,8 @@
"private": true,
"type": "module",
"devDependencies": {
"@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly",
"react": "18.2.0",
"react-dom": "18.2.0",
"vitest": "0.34.6",
+1
View File
@@ -32,6 +32,7 @@ export const runtimeFlagsSchema = z.object({
enableCaptcha: z.boolean(),
enableEnhanceShareMode: z.boolean(),
enablePayment: z.boolean(),
enablePageHistory: z.boolean(),
// this is for the electron app
serverUrlPrefix: z.string(),
enableMoveDatabase: z.boolean(),
+5 -5
View File
@@ -55,9 +55,9 @@
},
"dependencies": {
"@affine/sdk": "workspace:*",
"@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly",
"jotai": "^2.5.1",
"jotai-effect": "^0.2.3",
"tinykeys": "^2.1.0",
@@ -66,8 +66,8 @@
"devDependencies": {
"@affine-test/fixtures": "workspace:*",
"@affine/templates": "workspace:*",
"@blocksuite/editor": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/lit": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/lit": "0.0.0-20231124123613-7c06e95d-nightly",
"@testing-library/react": "^14.0.0",
"async-call-rpc": "^6.3.1",
"electron": "link:../../frontend/electron/node_modules/electron",
+5 -5
View File
@@ -22,11 +22,11 @@
"dist"
],
"dependencies": {
"@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/editor": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly",
"jotai": "^2.5.1",
"zod": "^3.22.4"
},
+2 -2
View File
@@ -37,8 +37,8 @@
"y-provider": "workspace:*"
},
"devDependencies": {
"@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly",
"fake-indexeddb": "^5.0.0",
"vite": "^4.4.11",
"vite-plugin-dts": "3.6.0",
+1 -1
View File
@@ -24,7 +24,7 @@
"build": "vite build"
},
"devDependencies": {
"@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly",
"vite": "^4.4.11",
"vite-plugin-dts": "3.6.0",
"vitest": "0.34.6",
+5 -5
View File
@@ -64,12 +64,12 @@
"uuid": "^9.0.1"
},
"devDependencies": {
"@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/editor": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/icons": "2.1.36",
"@blocksuite/lit": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/lit": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly",
"@storybook/jest": "^0.2.3",
"@storybook/testing-library": "^0.2.2",
"@testing-library/react": "^14.0.0",
@@ -1,4 +1,6 @@
import { style } from '@vanilla-extract/css';
import { createVar, style } from '@vanilla-extract/css';
export const hoverMaxWidth = createVar();
export const root = style({
position: 'relative',
@@ -15,7 +17,8 @@ export const tagsContainer = style({
export const tagsScrollContainer = style([
tagsContainer,
{
overflow: 'auto',
overflowX: 'hidden',
position: 'relative',
height: '100%',
gap: '8px',
},
@@ -41,7 +44,7 @@ export const innerContainer = style({
transition: 'all 0.2s 0.3s ease-in-out',
selectors: {
[`${root}:hover &`]: {
maxWidth: 'var(--hover-max-width)',
maxWidth: hoverMaxWidth,
},
},
});
@@ -66,6 +69,16 @@ export const innerBackdrop = style({
export const tag = style({
height: '20px',
display: 'flex',
minWidth: 0,
alignItems: 'center',
justifyContent: 'space-between',
':last-child': {
minWidth: 'max-content',
},
});
export const tagInnerWrapper = style({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
@@ -74,7 +87,7 @@ export const tag = style({
});
export const tagSticky = style([
tag,
tagInnerWrapper,
{
fontSize: 'var(--affine-font-xs)',
borderRadius: '10px',
@@ -82,10 +95,8 @@ export const tagSticky = style([
border: '1px solid var(--affine-border-color)',
background: 'var(--affine-background-primary-color)',
maxWidth: '128px',
position: 'sticky',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
left: 0,
},
]);
@@ -1,8 +1,9 @@
import type { Tag } from '@affine/env/filter';
import { MoreHorizontalIcon } from '@blocksuite/icons';
import { Menu } from '@toeverything/components/menu';
import { assignInlineVars } from '@vanilla-extract/dynamic';
import clsx from 'clsx';
import { useEffect, useMemo, useRef } from 'react';
import { useMemo } from 'react';
import * as styles from './page-tags.css';
import { stopPropagation } from './utils';
@@ -42,18 +43,22 @@ const TagItem = ({ tag, idx, mode, style }: TagItemProps) => {
return (
<div
data-testid="page-tag"
className={mode === 'sticky' ? styles.tagSticky : styles.tagListItem}
className={styles.tag}
data-idx={idx}
title={tag.value}
style={style}
>
<div
className={styles.tagIndicator}
style={{
backgroundColor: tagColorMap(tag.color),
}}
/>
<div className={styles.tagLabel}>{tag.value}</div>
className={mode === 'sticky' ? styles.tagSticky : styles.tagListItem}
>
<div
className={styles.tagIndicator}
style={{
backgroundColor: tagColorMap(tag.color),
}}
/>
<div className={styles.tagLabel}>{tag.value}</div>
</div>
</div>
);
};
@@ -69,26 +74,6 @@ export const PageTags = ({
? widthOnHover
: `${widthOnHover}px`
: 'auto';
const tagsContainerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (tagsContainerRef.current) {
const tagsContainer = tagsContainerRef.current;
const listener = () => {
// on mouseleave, reset scroll position to the hoverExpandDirection
tagsContainer.scrollTo({
left: hoverExpandDirection === 'left' ? Number.MAX_SAFE_INTEGER : 0,
behavior: 'smooth',
});
};
listener();
tagsContainerRef.current.addEventListener('mouseleave', listener);
return () => {
tagsContainer.removeEventListener('mouseleave', listener);
};
}
return;
}, [hoverExpandDirection]);
const tagsInPopover = useMemo(() => {
const lastTags = tags.slice(maxItems);
@@ -107,36 +92,17 @@ export const PageTags = ({
// sort tags by length
nTags.sort((a, b) => a.value.length - b.value.length);
const tagRightCharLength = nTags.reduceRight<number[]>(
(acc, tag) => {
const curr = acc[0] + Math.min(tag.value.length, 10);
return [curr, ...acc];
},
[0]
);
tagRightCharLength.shift();
return nTags.map((tag, idx) => (
<TagItem
key={tag.id}
tag={tag}
idx={idx}
mode="sticky"
style={{
right: `calc(${tagRightCharLength[idx]}em)`,
}}
/>
<TagItem key={tag.id} tag={tag} idx={idx} mode="sticky" />
));
}, [maxItems, tags]);
return (
<div
data-testid="page-tags"
className={styles.root}
style={{
// @ts-expect-error it's fine
'--hover-max-width': sanitizedWidthOnHover,
}}
style={assignInlineVars({
[styles.hoverMaxWidth]: sanitizedWidthOnHover,
})}
>
<div
style={{
@@ -146,9 +112,7 @@ export const PageTags = ({
className={clsx(styles.innerContainer)}
>
<div className={styles.innerBackdrop} />
<div className={styles.tagsScrollContainer} ref={tagsContainerRef}>
{tagsNormal}
</div>
<div className={styles.tagsScrollContainer}>{tagsNormal}</div>
{maxItems && tags.length > maxItems ? (
<Menu
items={tagsInPopover}
@@ -11,6 +11,7 @@ import * as styles from './share.css';
export interface StorageProgressProgress {
max: number;
value: number;
upgradable?: boolean;
onUpgrade: () => void;
plan: SubscriptionPlan;
}
@@ -23,6 +24,7 @@ enum ButtonType {
export const StorageProgress = ({
max: upperLimit,
value,
upgradable = true,
onUpgrade,
plan,
}: StorageProgressProgress) => {
@@ -63,22 +65,24 @@ export const StorageProgress = ({
</div>
</div>
<Tooltip
options={{ hidden: percent < 100 }}
content={t['com.affine.storage.maximum-tips']()}
>
<span tabIndex={0}>
<Button
type={buttonType}
onClick={onUpgrade}
className={styles.storageButton}
>
{plan === 'Free'
? t['com.affine.storage.upgrade']()
: t['com.affine.storage.change-plan']()}
</Button>
</span>
</Tooltip>
{upgradable ? (
<Tooltip
options={{ hidden: percent < 100 }}
content={t['com.affine.storage.maximum-tips']()}
>
<span tabIndex={0}>
<Button
type={buttonType}
onClick={onUpgrade}
className={styles.storageButton}
>
{plan === 'Free'
? t['com.affine.storage.upgrade']()
: t['com.affine.storage.change-plan']()}
</Button>
</span>
</Tooltip>
) : null}
</div>
);
};
@@ -33,6 +33,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
enableCaptcha: true,
enableEnhanceShareMode: false,
enablePayment: true,
enablePageHistory: false,
serverUrlPrefix: 'https://insider.affine.pro', // Let insider be stable environment temporarily.
editorFlags,
appVersion: packageJson.version,
@@ -41,6 +42,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
get beta() {
return {
...this.stable,
enablePageHistory: false,
serverUrlPrefix: 'https://insider.affine.pro',
};
},
@@ -75,6 +77,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
enableCaptcha: true,
enableEnhanceShareMode: false,
enablePayment: true,
enablePageHistory: true,
serverUrlPrefix: 'https://affine.fail',
editorFlags,
appVersion: packageJson.version,
@@ -142,6 +145,11 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
: buildFlags.mode === 'development'
? true
: currentBuildPreset.enablePayment,
enablePageHistory: process.env.ENABLE_PAGE_HISTORY
? process.env.ENABLE_PAGE_HISTORY === 'true'
: buildFlags.mode === 'development'
? true
: currentBuildPreset.enablePageHistory,
};
if (buildFlags.mode === 'development') {
+7 -7
View File
@@ -25,14 +25,14 @@
"@affine/i18n": "workspace:*",
"@affine/templates": "workspace:*",
"@affine/workspace": "workspace:*",
"@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/editor": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/icons": "2.1.36",
"@blocksuite/lit": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/virgo": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/lit": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/virgo": "0.0.0-20231124123613-7c06e95d-nightly",
"@dnd-kit/core": "^6.0.8",
"@dnd-kit/sortable": "^8.0.0",
"@emotion/cache": "^11.11.0",
@@ -0,0 +1,7 @@
import { atom } from 'jotai';
// make page history controllable by atom to make it easier to use in CMDK
export const pageHistoryModalAtom = atom({
open: false,
pageId: '',
});
@@ -28,6 +28,11 @@ const UserPlanButtonWithData = () => {
const t = useAFFiNEI18N();
if (plan === SubscriptionPlan.SelfHosted) {
// Self hosted version doesn't have a payment apis.
return <div className={styles.userPlanButton}>{plan}</div>;
}
return (
<Tooltip content={t['com.affine.payment.tag-tooltips']()} side="top">
<div className={styles.userPlanButton} onClick={handleClick}>
@@ -7,6 +7,7 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
import { useMemo } from 'react';
import { useSelfHosted } from '../../../hooks/affine/use-server-flavor';
import { useWorkspace } from '../../../hooks/use-workspace';
import { DeleteLeaveWorkspace } from './delete-leave-workspace';
import { ExportPanel } from './export';
@@ -20,6 +21,7 @@ import type { WorkspaceSettingDetailProps } from './types';
export const WorkspaceSettingDetail = (props: WorkspaceSettingDetailProps) => {
const { workspaceId } = props;
const t = useAFFiNEI18N();
const isSelfHosted = useSelfHosted();
const workspace = useWorkspace(workspaceId);
const [name] = useBlockSuiteWorkspaceName(workspace.blockSuiteWorkspace);
@@ -56,7 +58,11 @@ export const WorkspaceSettingDetail = (props: WorkspaceSettingDetailProps) => {
</SettingWrapper>
<SettingWrapper title={t['com.affine.brand.affineCloud']()}>
<PublishPanel workspace={workspace} {...props} />
<MembersPanel workspace={workspace} {...props} />
<MembersPanel
workspace={workspace}
upgradable={!isSelfHosted}
{...props}
/>
</SettingWrapper>
{storageAndExportSetting}
<SettingWrapper>
@@ -51,6 +51,7 @@ enum MemberLimitCount {
const COUNT_PER_PAGE = 8;
export interface MembersPanelProps extends WorkspaceSettingDetailProps {
upgradable: boolean;
workspace: AffineOfficialWorkspace;
}
type OnRevoke = (memberId: string) => void;
@@ -70,6 +71,7 @@ const MembersPanelLocal = () => {
export const CloudWorkspaceMembersPanel = ({
workspace,
isOwner,
upgradable,
}: MembersPanelProps) => {
const workspaceId = workspace.id;
const memberCount = useMemberCount(workspaceId);
@@ -165,16 +167,20 @@ export const CloudWorkspaceMembersPanel = ({
planName: plan,
memberLimit,
})}
,
<div className={style.goUpgradeWrapper} onClick={handleUpgrade}>
<span className={style.goUpgrade}>
{t['com.affine.payment.member.description.go-upgrade']()}
</span>
<ArrowRightBigIcon className={style.arrowRight} />
</div>
{upgradable ? (
<>
,
<div className={style.goUpgradeWrapper} onClick={handleUpgrade}>
<span className={style.goUpgrade}>
{t['com.affine.payment.member.description.go-upgrade']()}
</span>
<ArrowRightBigIcon className={style.arrowRight} />
</div>
</>
) : null}
</span>
);
}, [handleUpgrade, memberLimit, plan, t]);
}, [handleUpgrade, memberLimit, plan, t, upgradable]);
return (
<>
@@ -0,0 +1,251 @@
import { DebugLogger } from '@affine/debug';
import {
fetchWithTraceReport,
type ListHistoryQuery,
listHistoryQuery,
recoverDocMutation,
} from '@affine/graphql';
import {
useMutateQueryResource,
useMutation,
useQueryInfinite,
} from '@affine/workspace/affine/gql';
import { createAffineCloudBlobEngine } from '@affine/workspace/blob';
import { globalBlockSuiteSchema } from '@affine/workspace/manager';
import { assertEquals } from '@blocksuite/global/utils';
import { Workspace } from '@blocksuite/store';
import { usePageMetaHelper } from '@toeverything/hooks/use-block-suite-page-meta';
import { useBlockSuiteWorkspacePage } from '@toeverything/hooks/use-block-suite-workspace-page';
import { revertUpdate } from '@toeverything/y-indexeddb';
import { useMemo } from 'react';
import useSWRImmutable from 'swr/immutable';
import { applyUpdate } from 'yjs';
const logger = new DebugLogger('page-history');
type DocHistory = ListHistoryQuery['workspace']['histories'][number];
export const usePageSnapshotList = (workspaceId: string, pageDocId: string) => {
const pageSize = 10;
const { data, loadingMore, loadMore } = useQueryInfinite({
query: listHistoryQuery,
getVariables: (_, previousPageData) => {
// use the timestamp of the last history as the cursor
const before = previousPageData?.workspace.histories.at(-1)?.timestamp;
const vars = {
pageDocId: pageDocId,
workspaceId: workspaceId,
before: before,
take: pageSize,
};
return vars;
},
});
const shouldLoadMore = useMemo(() => {
if (!data) {
return false;
}
const lastPage = data.at(-1);
if (!lastPage) {
return false;
}
return lastPage.workspace.histories.length === pageSize;
}, [data]);
const histories = useMemo(() => {
if (!data) {
return [];
}
return data.flatMap(page => page.workspace.histories);
}, [data]);
return [
histories,
shouldLoadMore ? loadMore : undefined,
loadingMore,
] as const;
};
const snapshotFetcher = async (
[workspaceId, pageDocId, ts]: [
workspaceId: string,
pageDocId: string,
ts: string,
] // timestamp is the key to the history/snapshot
) => {
if (!ts) {
return null;
}
const res = await fetchWithTraceReport(
runtimeConfig.serverUrlPrefix +
`/api/workspaces/${workspaceId}/docs/${pageDocId}/histories/${ts}`,
{
priority: 'high',
}
);
if (!res.ok) {
throw new Error('Failed to fetch snapshot');
}
const snapshot = await res.arrayBuffer();
if (!snapshot) {
throw new Error('Invalid snapshot');
}
return snapshot;
};
// attach the Page shown in the modal to a temporary workspace
// so that we do not need to worry about providers etc
// todo: fix references to the page (the referenced page will shown as deleted)
// if we simply clone the current workspace, it maybe time consuming right?
const workspaceMap = new Map<string, Workspace>();
// assume the workspace is a cloud workspace since the history feature is only enabled for cloud workspace
const getOrCreateWorkspace = (workspaceId: string) => {
let workspace = workspaceMap.get(workspaceId);
if (!workspace) {
const blobEngine = createAffineCloudBlobEngine(workspaceId);
workspace = new Workspace({
id: workspaceId,
providerCreators: [],
blobStorages: [
() => ({
crud: {
async get(key) {
return (await blobEngine.get(key)) ?? null;
},
async set(key, value) {
await blobEngine.set(key, value);
return key;
},
async delete(key) {
return blobEngine.delete(key);
},
async list() {
return blobEngine.list();
},
},
}),
],
schema: globalBlockSuiteSchema,
});
workspaceMap.set(workspaceId, workspace);
}
return workspace;
};
// workspace id + page id + timestamp -> snapshot (update binary)
export const usePageHistory = (
workspaceId: string,
pageDocId: string,
ts?: string
) => {
// snapshot should be immutable. so we use swr immutable to disable revalidation
const { data } = useSWRImmutable<ArrayBuffer | null>(
[workspaceId, pageDocId, ts],
{
fetcher: snapshotFetcher,
suspense: false,
}
);
return data ?? undefined;
};
// workspace id + page id + timestamp + snapshot -> Page (to be used for rendering in blocksuite editor)
export const useSnapshotPage = (
workspaceId: string,
pageDocId: string,
ts?: string,
snapshot?: ArrayBuffer
) => {
const page = useMemo(() => {
if (!ts) {
return null;
}
const pageId = pageDocId + '-' + ts;
const historyShellWorkspace = getOrCreateWorkspace(workspaceId);
let page = historyShellWorkspace.getPage(pageId);
if (!page && snapshot) {
page = historyShellWorkspace.createPage({
id: pageId,
});
page.awarenessStore.setReadonly(page, true);
const spaceDoc = page.spaceDoc;
page
.load(() => applyUpdate(spaceDoc, new Uint8Array(snapshot)))
.catch(console.error); // must load before applyUpdate
}
return page;
}, [pageDocId, snapshot, ts, workspaceId]);
return page;
};
export const historyListGroupByDay = (histories: DocHistory[]) => {
const map = new Map<string, DocHistory[]>();
for (const history of histories) {
const day = new Date(history.timestamp).toLocaleDateString();
const list = map.get(day) ?? [];
list.push(history);
map.set(day, list);
}
return [...map.entries()];
};
export const useRestorePage = (workspace: Workspace, pageId: string) => {
const page = useBlockSuiteWorkspacePage(workspace, pageId);
const mutateQueryResource = useMutateQueryResource();
const { trigger: recover, isMutating } = useMutation({
mutation: recoverDocMutation,
});
const { getPageMeta, setPageTitle } = usePageMetaHelper(workspace);
const onRestore = useMemo(() => {
return async (version: string, update: Uint8Array) => {
if (!page) {
return;
}
const pageDocId = page.spaceDoc.guid;
revertUpdate(page.spaceDoc, update, key => {
assertEquals(key, 'blocks'); // only expect this value is 'blocks'
return 'Map';
});
// should also update the page title, since it may be changed in the history
const title = page.meta.title;
if (getPageMeta(pageDocId)?.title !== title) {
setPageTitle(pageDocId, title);
}
await recover({
docId: pageDocId,
timestamp: version,
workspaceId: workspace.id,
});
await mutateQueryResource(listHistoryQuery, vars => {
return (
vars.pageDocId === pageDocId && vars.workspaceId === workspace.id
);
});
logger.info('Page restored', pageDocId, version);
};
}, [
getPageMeta,
mutateQueryResource,
page,
recover,
setPageTitle,
workspace.id,
]);
return {
onRestore,
isMutating,
};
};
@@ -0,0 +1,119 @@
export const EmptyHistoryShape = () => (
<svg
width="200"
height="174"
viewBox="0 0 200 174"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="51.724"
y="38.4615"
width="96.5517"
height="96.5517"
stroke="var(--affine-text-disable-color)"
/>
<path
d="M51.8339 86.7374L99.9999 38.5714L148.166 86.7374L99.9999 134.903L51.8339 86.7374Z"
stroke="var(--affine-text-disable-color)"
/>
<path
d="M99.6052 38.1966C107.662 33.4757 117.043 30.7695 127.055 30.7695C157.087 30.7695 181.432 55.1148 181.432 85.1462C181.432 107.547 167.887 126.783 148.541 135.113"
stroke="var(--affine-text-disable-color)"
/>
<path
d="M148.375 86.4722C153.096 94.5291 155.802 103.91 155.802 113.922C155.802 143.954 131.457 168.299 101.426 168.299C79.0254 168.299 59.7886 154.754 51.4587 135.408"
stroke="var(--affine-text-disable-color)"
/>
<path
d="M100.394 135.113C92.3373 139.834 82.9568 142.54 72.9441 142.54C42.9127 142.54 18.5675 118.195 18.5675 88.1634C18.5675 65.763 32.1124 46.5261 51.4587 38.1963"
stroke="var(--affine-text-disable-color)"
/>
<path
d="M51.4585 87.1318C46.7377 79.0749 44.0315 69.6943 44.0315 59.6817C44.0315 29.6503 68.3767 5.30503 98.4081 5.30503C120.809 5.30503 140.045 18.8499 148.375 38.1963"
stroke="var(--affine-text-disable-color)"
/>
<path
d="M51.4587 38.1963L148.541 135.279"
stroke="var(--affine-text-disable-color)"
/>
<path
d="M148.541 38.1963L51.4587 135.279"
stroke="var(--affine-text-disable-color)"
/>
<path
d="M99.9998 38.1963V135.279"
stroke="var(--affine-text-disable-color)"
/>
<path
d="M148.541 86.7378L51.4588 86.7378"
stroke="var(--affine-text-disable-color)"
/>
<ellipse
cx="148.275"
cy="38.4617"
rx="3.97878"
ry="3.97878"
fill="var(--affine-text-primary-color)"
/>
<ellipse
cx="148.275"
cy="135.013"
rx="3.97878"
ry="3.97878"
fill="var(--affine-text-primary-color)"
/>
<ellipse
cx="148.275"
cy="86.7376"
rx="3.97878"
ry="3.97878"
fill="var(--affine-text-primary-color)"
/>
<ellipse
cx="51.7241"
cy="38.4617"
rx="3.97878"
ry="3.97878"
fill="var(--affine-text-primary-color)"
/>
<ellipse
cx="51.7241"
cy="135.013"
rx="3.97878"
ry="3.97878"
fill="var(--affine-text-primary-color)"
/>
<ellipse
cx="51.7241"
cy="86.7376"
rx="3.97878"
ry="3.97878"
fill="var(--affine-text-primary-color)"
/>
<ellipse
cx="100"
cy="38.4617"
rx="3.97878"
ry="3.97878"
transform="rotate(-90 100 38.4617)"
fill="var(--affine-text-primary-color)"
/>
<ellipse
cx="100"
cy="86.2073"
rx="3.97878"
ry="3.97878"
transform="rotate(-90 100 86.2073)"
fill="var(--affine-text-primary-color)"
/>
<ellipse
cx="100"
cy="135.013"
rx="3.97878"
ry="3.97878"
transform="rotate(-90 100 135.013)"
fill="var(--affine-text-primary-color)"
/>
</svg>
);
@@ -0,0 +1,446 @@
import { Scrollable } from '@affine/component';
import {
BlockSuiteEditor,
BlockSuiteFallback,
} from '@affine/component/block-suite-editor';
import type { PageMode } from '@affine/core/atoms';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { Workspace } from '@blocksuite/store';
import type { DialogContentProps } from '@radix-ui/react-dialog';
import { Button } from '@toeverything/components/button';
import { ConfirmModal, Modal } from '@toeverything/components/modal';
import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks';
import { useAtom, useAtomValue } from 'jotai';
import {
type PropsWithChildren,
Suspense,
useCallback,
useLayoutEffect,
useMemo,
useState,
} from 'react';
import { currentModeAtom } from '../../../atoms/mode';
import { pageHistoryModalAtom } from '../../../atoms/page-history';
import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace';
import { StyledEditorModeSwitch } from '../../blocksuite/block-suite-mode-switch/style';
import {
EdgelessSwitchItem,
PageSwitchItem,
} from '../../blocksuite/block-suite-mode-switch/switch-items';
import { AffineErrorBoundary } from '../affine-error-boundary';
import {
historyListGroupByDay,
usePageHistory,
usePageSnapshotList,
useRestorePage,
useSnapshotPage,
} from './data';
import { EmptyHistoryShape } from './empty-history-shape';
import * as styles from './styles.css';
export interface PageHistoryModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
workspace: Workspace;
pageId: string;
}
const contentOptions: DialogContentProps = {
['data-testid' as string]: 'page-history-modal',
onPointerDownOutside: e => {
e.preventDefault();
},
style: {
padding: 0,
maxWidth: 944,
backgroundColor: 'var(--affine-background-primary-color)',
overflow: 'hidden',
},
};
const ModalContainer = ({
onOpenChange,
open,
children,
}: PropsWithChildren<{
open: boolean;
onOpenChange: (open: boolean) => void;
}>) => {
return (
<Modal
open={open}
onOpenChange={onOpenChange}
width="calc(100% - 64px)"
height="80%"
withoutCloseButton
contentOptions={contentOptions}
>
<AffineErrorBoundary>{children}</AffineErrorBoundary>
</Modal>
);
};
const localTimeFormatter = new Intl.DateTimeFormat('en', {
timeStyle: 'short',
});
const timestampToLocalTime = (ts: string) => {
return localTimeFormatter.format(new Date(ts));
};
interface HistoryEditorPreviewProps {
workspaceId: string;
pageDocId: string;
ts?: string;
snapshot?: ArrayBuffer;
mode: PageMode;
onModeChange: (mode: PageMode) => void;
title: string;
}
const HistoryEditorPreview = ({
ts,
snapshot,
onModeChange,
mode,
workspaceId,
pageDocId,
title,
}: HistoryEditorPreviewProps) => {
const onSwitchToPageMode = useCallback(() => {
onModeChange('page');
}, [onModeChange]);
const onSwitchToEdgelessMode = useCallback(() => {
onModeChange('edgeless');
}, [onModeChange]);
const page = useSnapshotPage(workspaceId, pageDocId, ts, snapshot);
return (
<div className={styles.previewWrapper}>
<div className={styles.previewHeader}>
<StyledEditorModeSwitch switchLeft={mode === 'page'}>
<PageSwitchItem
data-testid="switch-page-mode-button"
active={mode === 'page'}
onClick={onSwitchToPageMode}
/>
<EdgelessSwitchItem
data-testid="switch-edgeless-mode-button"
active={mode === 'edgeless'}
onClick={onSwitchToEdgelessMode}
/>
</StyledEditorModeSwitch>
<div className={styles.previewHeaderTitle}>{title}</div>
<div className={styles.previewHeaderTimestamp}>
{ts ? timestampToLocalTime(ts) : null}
</div>
</div>
{page ? (
<BlockSuiteEditor
className={styles.editor}
mode={mode}
page={page}
onModeChange={onModeChange}
/>
) : (
<BlockSuiteFallback />
)}
</div>
);
};
const PageHistoryList = ({
pageDocId,
workspaceId,
activeVersion,
onVersionChange,
}: {
workspaceId: string;
pageDocId: string;
activeVersion?: string;
onVersionChange: (version: string) => void;
}) => {
const [historyList, loadMore, loadingMore] = usePageSnapshotList(
workspaceId,
pageDocId
);
const historyListByDay = useMemo(() => {
return historyListGroupByDay(historyList);
}, [historyList]);
const t = useAFFiNEI18N();
useLayoutEffect(() => {
if (historyList.length > 0 && !activeVersion) {
onVersionChange(historyList[0].timestamp);
}
}, [activeVersion, historyList, onVersionChange]);
return (
<div className={styles.historyList}>
<div className={styles.historyListHeader}>
{t['com.affine.history.version-history']()}
</div>
<Scrollable.Root className={styles.historyListScrollable}>
<Scrollable.Viewport className={styles.historyListScrollableInner}>
{historyListByDay.map(([day, list]) => {
return (
<div key={day} className={styles.historyItemGroup}>
<div className={styles.historyItemGroupTitle}>{day}</div>
{list.map(history => (
<div
className={styles.historyItem}
key={history.timestamp}
data-testid="version-history-item"
onClick={e => {
e.stopPropagation();
onVersionChange(history.timestamp);
}}
data-active={activeVersion === history.timestamp}
>
<button>{timestampToLocalTime(history.timestamp)}</button>
</div>
))}
</div>
);
})}
{loadMore ? (
<Button
type="plain"
loading={loadingMore}
disabled={loadingMore}
className={styles.historyItemLoadMore}
onClick={loadMore}
>
Load More
</Button>
) : null}
</Scrollable.Viewport>
<Scrollable.Scrollbar />
</Scrollable.Root>
</div>
);
};
interface ConfirmRestoreModalProps {
open: boolean;
onConfirm: (res: boolean) => void;
isMutating: boolean;
}
const ConfirmRestoreModal = ({
isMutating,
open,
onConfirm,
}: ConfirmRestoreModalProps) => {
const t = useAFFiNEI18N();
const handleConfirm = useCallback(() => {
onConfirm(true);
}, [onConfirm]);
const handleCancel = useCallback(() => {
onConfirm(false);
}, [onConfirm]);
return (
<ConfirmModal
open={open}
onOpenChange={handleCancel}
title={t['com.affine.history.restore-current-version']()}
description={t['com.affine.history.confirm-restore-modal.hint']()}
cancelText={t['Cancel']()}
contentOptions={{
['data-testid' as string]: 'confirm-restore-history-modal',
style: {
padding: '20px 26px',
},
}}
confirmButtonOptions={{
loading: isMutating,
type: 'primary',
['data-testid' as string]: 'confirm-restore-history-button',
children: t['com.affine.history.confirm-restore-modal.restore'](),
}}
onConfirm={handleConfirm}
></ConfirmModal>
);
};
const EmptyHistoryPrompt = () => {
const t = useAFFiNEI18N();
return (
<div
className={styles.emptyHistoryPrompt}
data-testid="empty-history-prompt"
>
<EmptyHistoryShape />
<div className={styles.emptyHistoryPromptTitle}>
{t['com.affine.history.empty-prompt.title']()}
</div>
<div className={styles.emptyHistoryPromptDescription}>
{t['com.affine.history.empty-prompt.description']()}
</div>
</div>
);
};
const PageHistoryManager = ({
workspace,
pageId,
onClose,
}: {
workspace: Workspace;
pageId: string;
onClose: () => void;
}) => {
const workspaceId = workspace.id;
const [activeVersion, setActiveVersion] = useState<string>();
const pageDocId = useMemo(() => {
return workspace.getPage(pageId)?.spaceDoc.guid ?? pageId;
}, [pageId, workspace]);
const snapshot = usePageHistory(workspaceId, pageDocId, activeVersion);
const t = useAFFiNEI18N();
const { onRestore, isMutating } = useRestorePage(workspace, pageId);
const handleRestore = useMemo(
() => async () => {
if (!activeVersion || !snapshot) {
return;
}
await onRestore(activeVersion, new Uint8Array(snapshot));
// close the modal after restore
onClose();
},
[activeVersion, onClose, onRestore, snapshot]
);
const defaultPreviewPageMode = useAtomValue(currentModeAtom);
const [mode, setMode] = useState<PageMode>(defaultPreviewPageMode);
const title = useMemo(
() => workspace.getPage(pageId)?.meta.title || t['Untitled'](),
[pageId, t, workspace]
);
const [showRestoreConfirmModal, setShowRestoreConfirmModal] = useState(false);
const showRestoreConfirm = useCallback(() => {
setShowRestoreConfirmModal(true);
}, []);
const onConfirmRestore = useAsyncCallback(
async res => {
if (res) {
await handleRestore();
}
setShowRestoreConfirmModal(false);
},
[handleRestore]
);
return (
<div className={styles.root}>
<div className={styles.modalContent} data-empty={!activeVersion}>
<HistoryEditorPreview
workspaceId={workspaceId}
pageDocId={pageDocId}
ts={activeVersion}
snapshot={snapshot}
mode={mode}
onModeChange={setMode}
title={title}
/>
<PageHistoryList
workspaceId={workspaceId}
pageDocId={pageDocId}
activeVersion={activeVersion}
onVersionChange={setActiveVersion}
/>
</div>
{!activeVersion ? (
<div className={styles.modalContent}>
<EmptyHistoryPrompt />
</div>
) : null}
<div className={styles.historyFooter}>
<Button type="plain" onClick={onClose}>
{t['com.affine.history.back-to-page']()}
</Button>
<div className={styles.spacer} />
<Button
type="primary"
onClick={showRestoreConfirm}
disabled={isMutating || !activeVersion}
>
{t['com.affine.history.restore-current-version']()}
</Button>
</div>
<ConfirmRestoreModal
open={showRestoreConfirmModal}
isMutating={isMutating}
onConfirm={onConfirmRestore}
/>
</div>
);
};
export const PageHistoryModal = ({
onOpenChange,
open,
pageId,
workspace,
}: PageHistoryModalProps) => {
const onClose = useCallback(() => {
onOpenChange(false);
}, [onOpenChange]);
return (
<ModalContainer onOpenChange={onOpenChange} open={open}>
<Suspense fallback={<BlockSuiteFallback />}>
<PageHistoryManager
onClose={onClose}
pageId={pageId}
workspace={workspace}
/>
</Suspense>
</ModalContainer>
);
};
export const GlobalPageHistoryModal = () => {
const [{ open, pageId }, setState] = useAtom(pageHistoryModalAtom);
const [workspace] = useCurrentWorkspace();
const handleOpenChange = useCallback(
(open: boolean) => {
setState(prev => ({
...prev,
open,
}));
},
[setState]
);
return (
<PageHistoryModal
open={open}
onOpenChange={handleOpenChange}
pageId={pageId}
workspace={workspace.blockSuiteWorkspace}
/>
);
};
@@ -0,0 +1 @@
export * from './history-modal';
@@ -0,0 +1,185 @@
import { createVar, globalStyle, style } from '@vanilla-extract/css';
const headerHeight = createVar('header-height');
const footerHeight = createVar('footer-height');
const historyListWidth = createVar('history-list-width');
export const root = style({
height: '100%',
width: '100%',
vars: {
[headerHeight]: '52px',
[footerHeight]: '68px',
[historyListWidth]: '160px',
},
});
export const modalContent = style({
display: 'flex',
height: `calc(100% - ${footerHeight})`,
width: '100%',
position: 'absolute',
selectors: {
'&[data-empty="true"]': {
opacity: 0,
pointerEvents: 'none',
},
},
});
export const previewWrapper = style({
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
height: '100%',
width: `calc(100% - ${historyListWidth})`,
backgroundColor: 'var(--affine-background-secondary-color)',
});
export const previewHeader = style({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: headerHeight,
borderBottom: '1px solid var(--affine-border-color)',
backgroundColor: 'var(--affine-background-primary-color)',
padding: '0 12px',
flexShrink: 0,
gap: 12,
});
export const previewHeaderTitle = style({
fontSize: 'var(--affine-font-xs)',
fontWeight: 600,
maxWidth: 400, // better responsiveness
});
export const previewHeaderTimestamp = style({
color: 'var(--affine-text-secondary-color)',
backgroundColor: 'var(--affine-background-secondary-color)',
padding: '0 10px',
borderRadius: 4,
fontSize: 'var(--affine-font-xs)',
});
export const editor = style({
height: '100%',
flexGrow: 1,
overflow: 'hidden',
});
export const historyList = style({
overflow: 'hidden',
height: '100%',
width: historyListWidth,
flexShrink: 0,
borderLeft: '1px solid var(--affine-border-color)',
});
export const historyListScrollable = style({
height: `calc(100% - ${headerHeight})`,
});
export const historyListScrollableInner = style({
display: 'flex',
gap: 16,
flexDirection: 'column',
});
export const historyListHeader = style({
display: 'flex',
alignItems: 'center',
height: 52,
borderBottom: '1px solid var(--affine-border-color)',
fontWeight: 'bold',
flexShrink: 0,
padding: '0 12px',
});
export const historyItemGroup = style({
display: 'flex',
flexDirection: 'column',
rowGap: 6,
});
export const historyItemGroupTitle = style({
display: 'flex',
alignItems: 'center',
padding: '12px',
fontWeight: 'bold',
backgroundColor: 'var(--affine-background-primary-color)',
position: 'sticky',
top: 0,
});
export const historyItem = style({
display: 'flex',
alignItems: 'center',
padding: '0 12px',
height: 32,
cursor: 'pointer',
selectors: {
'&:hover, &[data-active=true]': {
backgroundColor: 'var(--affine-hover-color)',
},
},
});
export const historyItemLoadMore = style([
historyItem,
{
cursor: 'pointer',
color: 'var(--affine-text-secondary-color)',
flexShrink: 0,
borderRadius: 0,
selectors: {
'&:hover': {
backgroundColor: 'var(--affine-hover-color)',
},
},
},
]);
globalStyle(`${historyItem} button`, {
color: 'inherit',
});
export const historyFooter = style({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: 68,
borderTop: '1px solid var(--affine-border-color)',
padding: '0 24px',
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
});
export const spacer = style({
flexGrow: 1,
});
export const emptyHistoryPrompt = style({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
height: '100%',
width: '100%',
zIndex: 1,
gap: 20,
});
export const emptyHistoryPromptTitle = style({
fontWeight: 600,
fontSize: 'var(--affine-font-h-5)',
});
export const emptyHistoryPromptDescription = style({
width: 320,
textAlign: 'center',
fontSize: 'var(--affine-font-xs)',
color: 'var(--affine-text-secondary-color)',
});
@@ -35,6 +35,7 @@ import {
openSignOutModalAtom,
} from '../../../../atoms';
import { useCurrentUser } from '../../../../hooks/affine/use-current-user';
import { useSelfHosted } from '../../../../hooks/affine/use-server-flavor';
import { useUserSubscription } from '../../../../hooks/use-subscription';
import { Upload } from '../../../pure/file-upload';
import * as style from './style.css';
@@ -167,6 +168,7 @@ export const AvatarAndName = () => {
const StoragePanel = () => {
const t = useAFFiNEI18N();
const isSelfHosted = useSelfHosted();
const { data } = useQuery({
query: allBlobSizesQuery,
@@ -175,6 +177,7 @@ const StoragePanel = () => {
const [subscription] = useUserSubscription();
const plan = subscription?.plan ?? SubscriptionPlan.Free;
// TODO(@JimmFly): get limit from user usage query directly after #4720 is merged
const maxLimit = useMemo(() => {
return bytes.parse(plan === SubscriptionPlan.Free ? '10GB' : '100GB');
}, [plan]);
@@ -199,6 +202,7 @@ const StoragePanel = () => {
plan={plan}
value={data.collectAllBlobSizes.size}
onUpgrade={onUpgrade}
upgradable={!isSelfHosted}
/>
</SettingRow>
);
@@ -8,6 +8,7 @@ import {
import type { ReactElement, SVGProps } from 'react';
import { useCurrentLoginStatus } from '../../../../hooks/affine/use-current-login-status';
import { useSelfHosted } from '../../../../hooks/affine/use-server-flavor';
import { AboutAffine } from './about';
import { AppearanceSettings } from './appearance';
import { BillingSettings } from './billing';
@@ -36,6 +37,7 @@ export type GeneralSettingList = GeneralSettingListItem[];
export const useGeneralSettingList = (): GeneralSettingList => {
const t = useAFFiNEI18N();
const status = useCurrentLoginStatus();
const isSelfHosted = useSelfHosted();
const settings: GeneralSettingListItem[] = [
{
@@ -50,13 +52,6 @@ export const useGeneralSettingList = (): GeneralSettingList => {
icon: KeyboardIcon,
testId: 'shortcuts-panel-trigger',
},
{
key: 'plans',
title: t['com.affine.payment.title'](),
icon: UpgradeIcon,
testId: 'plans-panel-trigger',
},
{
key: 'plugins',
title: 'Plugins',
@@ -71,13 +66,21 @@ export const useGeneralSettingList = (): GeneralSettingList => {
},
];
if (status === 'authenticated') {
if (!isSelfHosted) {
settings.splice(3, 0, {
key: 'billing',
title: t['com.affine.payment.billing-setting.title'](),
icon: PaymentIcon,
testId: 'billing-panel-trigger',
key: 'plans',
title: t['com.affine.payment.title'](),
icon: UpgradeIcon,
testId: 'plans-panel-trigger',
});
if (status === 'authenticated') {
settings.splice(3, 0, {
key: 'billing',
title: t['com.affine.payment.billing-setting.title'](),
icon: PaymentIcon,
testId: 'billing-panel-trigger',
});
}
}
return settings;
@@ -1,5 +1,6 @@
import { FlexWrapper } from '@affine/component';
import { Export, MoveToTrash } from '@affine/component/page-list';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { assertExists } from '@blocksuite/global/utils';
import {
@@ -8,6 +9,7 @@ import {
EditIcon,
FavoritedIcon,
FavoriteIcon,
HistoryIcon,
ImportIcon,
PageIcon,
} from '@blocksuite/icons';
@@ -25,7 +27,7 @@ import {
} from '@toeverything/hooks/use-block-suite-page-meta';
import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper';
import { useAtomValue, useSetAtom } from 'jotai';
import { useCallback, useRef } from 'react';
import { useCallback, useRef, useState } from 'react';
import { applyUpdate, encodeStateAsUpdate } from 'yjs';
import { setPageModeAtom } from '../../../atoms';
@@ -36,6 +38,7 @@ import { useTrashModalHelper } from '../../../hooks/affine/use-trash-modal-helpe
import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace';
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
import { toast } from '../../../utils';
import { PageHistoryModal } from '../../affine/page-history-modal/history-modal';
import { HeaderDropDownButton } from '../../pure/header-drop-down-button';
import { usePageHelper } from '../block-suite-page-list/utils';
@@ -68,6 +71,12 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => {
const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
const { setTrashModal } = useTrashModalHelper(blockSuiteWorkspace);
const [historyModalOpen, setHistoryModalOpen] = useState(false);
const openHistoryModal = useCallback(() => {
setHistoryModalOpen(true);
}, []);
const handleOpenTrashModal = useCallback(() => {
setTrashModal({
open: true,
@@ -207,6 +216,23 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => {
>
{t['Import']()}
</MenuItem>
{workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD &&
runtimeConfig.enablePageHistory ? (
<MenuItem
preFix={
<MenuIcon>
<HistoryIcon />
</MenuIcon>
}
data-testid="editor-option-menu-history"
onSelect={openHistoryModal}
style={menuItemStyle}
>
{t['com.affine.history.view-history-version']()}
</MenuItem>
) : null}
<Export exportHandler={exportHandler} />
<MenuSeparator />
<MoveToTrash
@@ -228,6 +254,14 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => {
>
<HeaderDropDownButton />
</Menu>
{workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD ? (
<PageHistoryModal
workspace={workspace.blockSuiteWorkspace}
open={historyModalOpen}
pageId={pageId}
onOpenChange={setHistoryModalOpen}
/>
) : null}
</FlexWrapper>
);
};
@@ -30,8 +30,8 @@ import { useAppSettingHelper } from '../../hooks/affine/use-app-setting-helper';
import { useDeleteCollectionInfo } from '../../hooks/affine/use-delete-collection-info';
import { useGeneralShortcuts } from '../../hooks/affine/use-shortcuts';
import { useTrashModalHelper } from '../../hooks/affine/use-trash-modal-helper';
import { useRegisterBrowserHistoryCommands } from '../../hooks/use-browser-history-commands';
import { useNavigateHelper } from '../../hooks/use-navigate-helper';
import { useRegisterBlocksuiteEditorCommands } from '../../hooks/use-shortcut-commands';
import type { AllWorkspace } from '../../shared';
import { CollectionsList } from '../pure/workspace-slider-bar/collections';
import { AddCollectionButton } from '../pure/workspace-slider-bar/collections/add-collection-button';
@@ -169,7 +169,7 @@ export const RootAppSidebar = ({
const closeUserWorkspaceList = useCallback(() => {
setOpenUserWorkspaceList(false);
}, [setOpenUserWorkspaceList]);
useRegisterBlocksuiteEditorCommands(router.back, router.forward);
useRegisterBrowserHistoryCommands(router.back, router.forward);
const userInfo = useDeleteCollectionInfo();
return (
<AppSidebar
@@ -4,6 +4,7 @@ import { useSWRConfig } from 'swr';
export function useMutateCloud() {
const { mutate } = useSWRConfig();
return useCallback(async () => {
// todo: should not mutate all graphql cache
return mutate(key => {
if (Array.isArray(key)) {
return key[0] === 'cloud';
@@ -1,25 +1,29 @@
import { toast } from '@affine/component';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { assertExists } from '@blocksuite/global/utils';
import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
import type { Workspace } from '@blocksuite/store';
import { EdgelessIcon, HistoryIcon, PageIcon } from '@blocksuite/icons';
import { usePageMetaHelper } from '@toeverything/hooks/use-block-suite-page-meta';
import {
PreconditionStrategy,
registerAffineCommand,
} from '@toeverything/infra/command';
import { useSetAtom } from 'jotai';
import { useCallback, useEffect } from 'react';
import { pageHistoryModalAtom } from '../../atoms/page-history';
import { useCurrentWorkspace } from '../current/use-current-workspace';
import { useBlockSuiteMetaHelper } from './use-block-suite-meta-helper';
import { useExportPage } from './use-export-page';
import { useTrashModalHelper } from './use-trash-modal-helper';
export function useRegisterBlocksuiteEditorCommands(
blockSuiteWorkspace: Workspace,
pageId: string,
mode: 'page' | 'edgeless'
) {
const t = useAFFiNEI18N();
const [workspace] = useCurrentWorkspace();
const blockSuiteWorkspace = workspace.blockSuiteWorkspace;
const { getPageMeta } = usePageMetaHelper(blockSuiteWorkspace);
const currentPage = blockSuiteWorkspace.getPage(pageId);
assertExists(currentPage);
@@ -28,6 +32,15 @@ export function useRegisterBlocksuiteEditorCommands(
const favorite = pageMeta.favorite ?? false;
const trash = pageMeta.trash ?? false;
const setPageHistoryModalState = useSetAtom(pageHistoryModalAtom);
const openHistoryModal = useCallback(() => {
setPageHistoryModalState(() => ({
pageId,
open: true,
}));
}, [pageId, setPageHistoryModalState]);
const { togglePageMode, toggleFavorite, restoreFromTrash } =
useBlockSuiteMetaHelper(blockSuiteWorkspace);
const exportHandler = useExportPage(currentPage);
@@ -40,12 +53,14 @@ export function useRegisterBlocksuiteEditorCommands(
});
}, [pageId, pageMeta.title, setTrashModal]);
const isCloudWorkspace = workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD;
useEffect(() => {
const unsubs: Array<() => void> = [];
const preconditionStrategy = () =>
PreconditionStrategy.InPaperOrEdgeless && !trash;
//TODO: add back when edgeless presentation is ready
// TODO: add back when edgeless presentation is ready
// this is pretty hack and easy to break. need a better way to communicate with blocksuite editor
// unsubs.push(
@@ -189,6 +204,20 @@ export function useRegisterBlocksuiteEditorCommands(
})
);
if (runtimeConfig.enablePageHistory && isCloudWorkspace) {
unsubs.push(
registerAffineCommand({
id: `editor:${mode}-page-history`,
category: `editor:${mode}`,
icon: <HistoryIcon />,
label: t['com.affine.cmdk.affine.editor.reveal-page-history-modal'](),
run() {
openHistoryModal();
},
})
);
}
return () => {
unsubs.forEach(unsub => unsub());
};
@@ -198,11 +227,12 @@ export function useRegisterBlocksuiteEditorCommands(
onClickDelete,
exportHandler,
pageId,
pageMeta.title,
restoreFromTrash,
t,
toggleFavorite,
togglePageMode,
trash,
isCloudWorkspace,
openHistoryModal,
]);
}
@@ -0,0 +1,34 @@
import { serverConfigQuery } from '@affine/graphql';
import { useQuery } from '@affine/workspace/affine/gql';
import type { BareFetcher, Middleware } from 'swr';
const wrappedFetcher = (fetcher: BareFetcher<any> | null, ...args: any[]) =>
fetcher?.(...args).catch(() => null);
const errorHandler: Middleware = useSWRNext => (key, fetcher, config) => {
return useSWRNext(key, wrappedFetcher.bind(null, fetcher), config);
};
export const useServerFlavor = () => {
const { data: config, error } = useQuery(
{ query: serverConfigQuery },
{
use: [errorHandler],
revalidateOnFocus: false,
revalidateOnMount: false,
revalidateIfStale: false,
}
);
if (error || !config) {
return 'local';
}
return config.serverConfig.flavor;
};
export const useSelfHosted = () => {
const serverFlavor = useServerFlavor();
return ['local', 'selfhosted'].includes(serverFlavor);
};
@@ -4,7 +4,7 @@ import {
} from '@toeverything/infra/command';
import { useEffect } from 'react';
export function useRegisterBlocksuiteEditorCommands(
export function useRegisterBrowserHistoryCommands(
back: () => unknown,
forward: () => unknown
) {
@@ -2,6 +2,8 @@ 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-flavor';
export type Subscription = NonNullable<
NonNullable<SubscriptionQuery['currentUser']>['subscription']
>;
@@ -12,6 +14,7 @@ const selector = (data: SubscriptionQuery) =>
data.currentUser?.subscription ?? null;
export const useUserSubscription = () => {
const isSelfHosted = useSelfHosted();
const { data, mutate } = useQuery({
query: subscriptionQuery,
});
@@ -36,5 +39,9 @@ export const useUserSubscription = () => {
[mutate]
);
if (isSelfHosted) {
return [selector(data), () => {}] as const;
}
return [selector(data), set] as const;
};
@@ -24,6 +24,7 @@ import { setPageModeAtom } from '../../atoms';
import { collectionsCRUDAtom } from '../../atoms/collections';
import { currentModeAtom } from '../../atoms/mode';
import { AffineErrorBoundary } from '../../components/affine/affine-error-boundary';
import { GlobalPageHistoryModal } from '../../components/affine/page-history-modal';
import { WorkspaceHeader } from '../../components/workspace-header';
import { useRegisterBlocksuiteEditorCommands } from '../../hooks/affine/use-register-blocksuite-editor-commands';
import { useCurrentSyncEngineStatus } from '../../hooks/current/use-current-sync-engine';
@@ -41,7 +42,7 @@ const DetailPageImpl = (): ReactElement => {
const { setTemporaryFilter } = useCollectionManager(collectionsCRUDAtom);
const mode = useAtomValue(currentModeAtom);
const setPageMode = useSetAtom(setPageModeAtom);
useRegisterBlocksuiteEditorCommands(blockSuiteWorkspace, currentPageId, mode);
useRegisterBlocksuiteEditorCommands(currentPageId, mode);
const onLoad = useCallback(
(page: Page, editor: EditorContainer) => {
try {
@@ -101,6 +102,8 @@ const DetailPageImpl = (): ReactElement => {
currentPageId={currentPageId}
onLoadEditor={onLoad}
/>
<GlobalPageHistoryModal />
</>
);
};
+4 -4
View File
@@ -32,10 +32,10 @@
"@affine/sdk": "workspace:*",
"@affine/templates": "workspace:*",
"@affine/vue-hello-world-plugin": "workspace:*",
"@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/editor": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/lit": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/lit": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly",
"@electron-forge/cli": "^7.1.0",
"@electron-forge/core": "^7.1.0",
"@electron-forge/core-utils": "^7.1.0",
+6 -2
View File
@@ -1,10 +1,11 @@
import { spawn } from 'node:child_process';
import { resolve } from 'node:path';
import type { ChildProcessWithoutNullStreams } from 'child_process';
import type { BuildContext } from 'esbuild';
import * as esbuild from 'esbuild';
import { config, electronDir } from './common';
import { config, electronDir, rootDir } from './common';
// this means we don't spawn electron windows, mainly for testing
const watchMode = process.argv.includes('--watch');
@@ -29,7 +30,10 @@ function spawnOrReloadElectron() {
spawnProcess = null;
}
spawnProcess = spawn('electron', ['.'], {
const ext = process.platform === 'win32' ? '.cmd' : '';
const exe = resolve(rootDir, 'node_modules', '.bin', `electron${ext}`);
spawnProcess = spawn(exe, ['.'], {
cwd: electronDir,
env: process.env,
});
@@ -0,0 +1,13 @@
query listHistory(
$workspaceId: String!
$pageDocId: String!
$take: Int
$before: DateTime
) {
workspace(id: $workspaceId) {
histories(guid: $pageDocId, take: $take, before: $before) {
id
timestamp
}
}
}
@@ -362,6 +362,22 @@ query getWorkspaces {
}`,
};
export const listHistoryQuery = {
id: 'listHistoryQuery' as const,
operationName: 'listHistory',
definitionName: 'workspace',
containsFile: false,
query: `
query listHistory($workspaceId: String!, $pageDocId: String!, $take: Int, $before: DateTime) {
workspace(id: $workspaceId) {
histories(guid: $pageDocId, take: $take, before: $before) {
id
timestamp
}
}
}`,
};
export const getInvoicesCountQuery = {
id: 'getInvoicesCountQuery' as const,
operationName: 'getInvoicesCount',
@@ -445,6 +461,17 @@ mutation publishPage($workspaceId: String!, $pageId: String!, $mode: PublicPageM
}`,
};
export const recoverDocMutation = {
id: 'recoverDocMutation' as const,
operationName: 'recoverDoc',
definitionName: 'recoverDoc',
containsFile: false,
query: `
mutation recoverDoc($workspaceId: String!, $docId: String!, $timestamp: DateTime!) {
recoverDoc(workspaceId: $workspaceId, guid: $docId, timestamp: $timestamp)
}`,
};
export const removeAvatarMutation = {
id: 'removeAvatarMutation' as const,
operationName: 'removeAvatar',
@@ -545,6 +572,20 @@ mutation sendVerifyChangeEmail($token: String!, $email: String!, $callbackUrl: S
}`,
};
export const serverConfigQuery = {
id: 'serverConfigQuery' as const,
operationName: 'serverConfig',
definitionName: 'serverConfig',
containsFile: false,
query: `
query serverConfig {
serverConfig {
version
flavor
}
}`,
};
export const setWorkspacePublicByIdMutation = {
id: 'setWorkspacePublicByIdMutation' as const,
operationName: 'setWorkspacePublicById',
@@ -0,0 +1,7 @@
mutation recoverDoc(
$workspaceId: String!
$docId: String!
$timestamp: DateTime!
) {
recoverDoc(workspaceId: $workspaceId, guid: $docId, timestamp: $timestamp)
}
@@ -0,0 +1,6 @@
query serverConfig {
serverConfig {
version
flavor
}
}
+57
View File
@@ -62,6 +62,7 @@ export enum SubscriptionPlan {
Enterprise = 'Enterprise',
Free = 'Free',
Pro = 'Pro',
SelfHosted = 'SelfHosted',
Team = 'Team',
}
@@ -372,6 +373,25 @@ export type GetWorkspacesQuery = {
workspaces: Array<{ __typename?: 'WorkspaceType'; id: string }>;
};
export type ListHistoryQueryVariables = Exact<{
workspaceId: Scalars['String']['input'];
pageDocId: Scalars['String']['input'];
take: InputMaybe<Scalars['Int']['input']>;
before: InputMaybe<Scalars['DateTime']['input']>;
}>;
export type ListHistoryQuery = {
__typename?: 'Query';
workspace: {
__typename?: 'WorkspaceType';
histories: Array<{
__typename?: 'DocHistoryType';
id: string;
timestamp: string;
}>;
};
};
export type GetInvoicesCountQueryVariables = Exact<{ [key: string]: never }>;
export type GetInvoicesCountQuery = {
@@ -444,6 +464,17 @@ export type PublishPageMutation = {
};
};
export type RecoverDocMutationVariables = Exact<{
workspaceId: Scalars['String']['input'];
docId: Scalars['String']['input'];
timestamp: Scalars['DateTime']['input'];
}>;
export type RecoverDocMutation = {
__typename?: 'Mutation';
recoverDoc: string;
};
export type RemoveAvatarMutationVariables = Exact<{ [key: string]: never }>;
export type RemoveAvatarMutation = {
@@ -533,6 +564,17 @@ export type SendVerifyChangeEmailMutation = {
sendVerifyChangeEmail: boolean;
};
export type ServerConfigQueryVariables = Exact<{ [key: string]: never }>;
export type ServerConfigQuery = {
__typename?: 'Query';
serverConfig: {
__typename?: 'ServerConfigType';
version: string;
flavor: string;
};
};
export type SetWorkspacePublicByIdMutationVariables = Exact<{
id: Scalars['ID']['input'];
public: Scalars['Boolean']['input'];
@@ -717,6 +759,11 @@ export type Queries =
variables: GetWorkspacesQueryVariables;
response: GetWorkspacesQuery;
}
| {
name: 'listHistoryQuery';
variables: ListHistoryQueryVariables;
response: ListHistoryQuery;
}
| {
name: 'getInvoicesCountQuery';
variables: GetInvoicesCountQueryVariables;
@@ -732,6 +779,11 @@ export type Queries =
variables: PricesQueryVariables;
response: PricesQuery;
}
| {
name: 'serverConfigQuery';
variables: ServerConfigQueryVariables;
response: ServerConfigQuery;
}
| {
name: 'subscriptionQuery';
variables: SubscriptionQueryVariables;
@@ -799,6 +851,11 @@ export type Mutations =
variables: PublishPageMutationVariables;
response: PublishPageMutation;
}
| {
name: 'recoverDocMutation';
variables: RecoverDocMutationVariables;
response: RecoverDocMutation;
}
| {
name: 'removeAvatarMutation';
variables: RemoveAvatarMutationVariables;
+6 -6
View File
@@ -18,12 +18,12 @@
"devDependencies": {
"@affine/debug": "workspace:*",
"@affine/env": "workspace:*",
"@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/editor": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/lit": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/lit": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly",
"@testing-library/react": "^14.0.0",
"@types/image-blob-reduce": "^4.1.3",
"@types/lodash.debounce": "^4.0.7",
+10 -1
View File
@@ -518,6 +518,7 @@
"com.affine.cmdk.affine.switch-state.on": "ON",
"com.affine.cmdk.affine.translucent-ui-on-the-sidebar.to": "Change Translucent UI On The Sidebar to",
"com.affine.cmdk.affine.whats-new": "What's New",
"com.affine.cmdk.affine.editor.reveal-page-history-modal": "Reveal Page History Modal",
"com.affine.cmdk.placeholder": "Type a command or search anything...",
"com.affine.collection-bar.action.tooltip.delete": "Delete",
"com.affine.collection-bar.action.tooltip.edit": "Edit",
@@ -972,5 +973,13 @@
"system": "System",
"upgradeBrowser": "Please upgrade to the latest version of Chrome for the best experience.",
"will be moved to Trash": "{{title}} will be moved to Trash",
"will delete member": "will delete member"
"will delete member": "will delete member",
"com.affine.history.version-history": "Version History",
"com.affine.history.view-history-version": "View History Version",
"com.affine.history.restore-current-version": "Restore current version",
"com.affine.history.back-to-page": "Back to page",
"com.affine.history.empty-prompt.title": "Empty",
"com.affine.history.empty-prompt.description": "This document is such a spring chicken, it hasn't sprouted a single historical sprig yet!",
"com.affine.history.confirm-restore-modal.restore": "Restore",
"com.affine.history.confirm-restore-modal.hint": "You are about to restore the current version of the page to the latest version available. This action will overwrite any changes made prior to the latest version."
}
+2 -2
View File
@@ -25,7 +25,7 @@ rand = "0.8"
serde = "1"
serde_json = "1"
sha3 = "0.10"
sqlx = { version = "0.7.2", default-features = false, features = [
sqlx = { version = "0.7.3", default-features = false, features = [
"sqlite",
"migrate",
"runtime-tokio",
@@ -44,7 +44,7 @@ uuid = { version = "1", default-features = false, features = [
affine_schema = { path = "./schema" }
dotenv = "0.15"
napi-build = "2"
sqlx = { version = "0.7.2", default-features = false, features = [
sqlx = { version = "0.7.3", default-features = false, features = [
"sqlite",
"runtime-tokio",
"tls-rustls",
+1
View File
@@ -4,6 +4,7 @@
"exports": {
"./atom": "./src/atom.ts",
"./manager": "./src/manager/index.ts",
"./blob": "./src/blob/index.ts",
"./local/crud": "./src/local/crud.ts",
"./affine/*": "./src/affine/*.ts",
"./providers": "./src/providers/index.ts"
+90 -1
View File
@@ -5,12 +5,15 @@ import type {
QueryOptions,
QueryResponse,
QueryVariables,
RecursiveMaybeFields,
} from '@affine/graphql';
import { gqlFetcherFactory } from '@affine/graphql';
import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks';
import type { GraphQLError } from 'graphql';
import { useMemo } from 'react';
import type { Key, SWRConfiguration, SWRResponse } from 'swr';
import useSWR from 'swr';
import useSWR, { useSWRConfig } from 'swr';
import useSWRInfinite from 'swr/infinite';
import type {
SWRMutationConfiguration,
SWRMutationResponse,
@@ -86,6 +89,63 @@ export function useQuery<Query extends GraphQLQuery>(
);
}
export function useQueryInfinite<Query extends GraphQLQuery>(
options: Omit<QueryOptions<Query>, 'variables'> & {
getVariables: (
pageIndex: number,
previousPageData: QueryResponse<Query>
) => QueryOptions<Query>['variables'];
},
config?: Omit<
SWRConfiguration<
QueryResponse<Query>,
GraphQLError | GraphQLError[],
typeof fetcher<Query>
>,
'fetcher'
>
) {
const configWithSuspense: SWRConfiguration = useMemo(
() => ({
suspense: true,
...config,
}),
[config]
);
const { data, setSize, size, error } = useSWRInfinite<
QueryResponse<Query>,
GraphQLError | GraphQLError[]
>(
(pageIndex: number, previousPageData: QueryResponse<Query>) => [
'cloud',
options.query.id,
options.getVariables(pageIndex, previousPageData),
],
async ([_, __, variables]) => {
const params = { ...options, variables } as QueryOptions<Query>;
return fetcher(params);
},
configWithSuspense
);
const loadingMore = size > 0 && data && !data[size - 1];
// todo: find a generic way to know whether or not there are more items to load
const loadMore = useAsyncCallback(async () => {
if (loadingMore) {
return;
}
await setSize(size => size + 1);
}, [loadingMore, setSize]);
return {
data,
error,
loadingMore,
loadMore,
};
}
/**
* A useSWRMutation wrapper for sending graphql mutations
*
@@ -138,3 +198,32 @@ export function useMutation(
}
export const gql = fetcher;
// use this to revalidate all queries that match the filter
export const useMutateQueryResource = () => {
const { mutate } = useSWRConfig();
const revalidateResource = useMemo(
() =>
<Q extends GraphQLQuery>(
query: Q,
varsFilter: (
vars: RecursiveMaybeFields<QueryVariables<Q>>
) => 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;
};
+146
View File
@@ -0,0 +1,146 @@
import { test } from '@affine-test/kit/playwright';
import {
createRandomUser,
deleteUser,
enableCloudWorkspace,
loginUser,
runPrisma,
} from '@affine-test/kit/utils/cloud';
import {
getBlockSuiteEditorTitle,
waitForEditorLoad,
} from '@affine-test/kit/utils/page-logic';
import { createLocalWorkspace } from '@affine-test/kit/utils/workspace';
import { expect, type Page } from '@playwright/test';
let user: {
id: string;
name: string;
email: string;
password: string;
};
test.beforeEach(async ({ page }) => {
user = await createRandomUser();
await loginUser(page, user.email);
});
test.afterEach(async () => {
// if you want to keep the user in the database for debugging,
// comment this line
await deleteUser(user.email);
});
test('newly created page shows empty history', async ({ page }) => {
await page.reload();
await waitForEditorLoad(page);
await createLocalWorkspace(
{
name: 'test',
},
page
);
await enableCloudWorkspace(page);
// click the history button
await page.getByTestId('header-dropDownButton').click();
await page.getByTestId('editor-option-menu-history').click();
const modal = page.getByTestId('page-history-modal');
// expect history modal shown up
await expect(modal).toBeVisible();
await expect(page.getByTestId('empty-history-prompt')).toBeVisible();
});
const pushCurrentPageUpdates = async (page: Page) => {
const [workspaceId, guid, updates, state] = await page.evaluate(() => {
// @ts-expect-error
const Y = window.currentWorkspace.blockSuiteWorkspace.constructor.Y;
// @ts-expect-error
const spaceDoc = window.currentEditor.page.spaceDoc;
// @ts-expect-error
const workspaceId: string = window.currentWorkspace.id;
const updates: Uint8Array = Y.encodeStateAsUpdate(spaceDoc);
const state: Uint8Array = Y.encodeStateVector(spaceDoc);
return [workspaceId, spaceDoc.guid, [...updates], [...state]] as const;
});
const toBuffer = (arr: readonly number[]) => Buffer.from(arr);
await runPrisma(async client => {
await client.snapshotHistory.create({
data: {
workspaceId: workspaceId,
id: guid,
blob: toBuffer(updates),
state: toBuffer(state),
timestamp: new Date(Date.now() - 10000),
expiredAt: new Date(Date.now() + 100000),
},
});
});
};
test('can restore page to a history version', async ({ page }) => {
await page.reload();
await waitForEditorLoad(page);
await createLocalWorkspace(
{
name: 'test',
},
page
);
await enableCloudWorkspace(page);
await pushCurrentPageUpdates(page);
const title = getBlockSuiteEditorTitle(page);
await title.pressSequentially('TEST TITLE', {
delay: 50,
});
// write something and push to history
await pushCurrentPageUpdates(page);
await title.fill('New Title');
// click the history button
await page.getByTestId('header-dropDownButton').click();
await page.getByTestId('editor-option-menu-history').click();
const modal = page.getByTestId('page-history-modal');
// expect history modal shown up
await expect(modal).toBeVisible();
// expect history list to have 2 items
await expect(modal.getByTestId('version-history-item')).toHaveCount(2);
// check the first item in the preview should have title 'TEST TITLE'
await expect(modal.locator('[data-block-is-title]')).toHaveText('TEST TITLE');
// click restore
await modal
.getByRole('button', {
name: 'Restore current version',
})
.click();
const confirm = page.getByTestId('confirm-restore-history-modal');
// expect confirm dialog to show up
await expect(confirm).toBeVisible();
// click restore
await confirm
.getByRole('button', {
name: 'Restore',
})
.click();
// title should be restored to 'TEST TITLE'
await expect(title).toContainText('TEST TITLE');
});
@@ -9,10 +9,10 @@
"devDependencies": {
"@affine-test/fixtures": "workspace:*",
"@affine-test/kit": "workspace:*",
"@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly",
"@playwright/test": "^1.39.0",
"express": "^4.18.2",
"http-proxy-middleware": "^3.0.0-beta.1",
@@ -9,10 +9,10 @@
"devDependencies": {
"@affine-test/fixtures": "workspace:*",
"@affine-test/kit": "workspace:*",
"@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly",
"@playwright/test": "^1.39.0",
"express": "^4.18.2",
"http-proxy-middleware": "^3.0.0-beta.1",
@@ -9,10 +9,10 @@
"devDependencies": {
"@affine-test/fixtures": "workspace:*",
"@affine-test/kit": "workspace:*",
"@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly",
"@playwright/test": "^1.39.0",
"express": "^4.18.2",
"http-proxy-middleware": "^3.0.0-beta.1",
+4 -4
View File
@@ -9,10 +9,10 @@
"devDependencies": {
"@affine-test/fixtures": "workspace:*",
"@affine-test/kit": "workspace:*",
"@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly",
"@playwright/test": "^1.39.0",
"express": "^4.18.2",
"http-proxy-middleware": "^3.0.0-beta.1",
+4 -4
View File
@@ -7,10 +7,10 @@
"devDependencies": {
"@affine-test/fixtures": "workspace:*",
"@affine-test/kit": "workspace:*",
"@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly",
"@playwright/test": "^1.39.0"
},
"version": "0.10.3-beta.2"
+7 -7
View File
@@ -31,14 +31,14 @@
"wait-on": "^7.2.0"
},
"devDependencies": {
"@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/editor": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/icons": "2.1.36",
"@blocksuite/lit": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/virgo": "0.0.0-20231122113751-6bf81eb3-nightly",
"@blocksuite/lit": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly",
"@blocksuite/virgo": "0.0.0-20231124123613-7c06e95d-nightly",
"@dnd-kit/sortable": "^8.0.0",
"@tomfreudenberg/next-auth-mock": "^0.5.6",
"chromatic": "^9.1.0",
+186 -174
View File
@@ -25,10 +25,10 @@ __metadata:
dependencies:
"@affine-test/fixtures": "workspace:*"
"@affine-test/kit": "workspace:*"
"@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@playwright/test": "npm:^1.39.0"
express: "npm:^4.18.2"
http-proxy-middleware: "npm:^3.0.0-beta.1"
@@ -42,10 +42,10 @@ __metadata:
dependencies:
"@affine-test/fixtures": "workspace:*"
"@affine-test/kit": "workspace:*"
"@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@playwright/test": "npm:^1.39.0"
express: "npm:^4.18.2"
http-proxy-middleware: "npm:^3.0.0-beta.1"
@@ -59,10 +59,10 @@ __metadata:
dependencies:
"@affine-test/fixtures": "workspace:*"
"@affine-test/kit": "workspace:*"
"@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@playwright/test": "npm:^1.39.0"
express: "npm:^4.18.2"
http-proxy-middleware: "npm:^3.0.0-beta.1"
@@ -76,10 +76,10 @@ __metadata:
dependencies:
"@affine-test/fixtures": "workspace:*"
"@affine-test/kit": "workspace:*"
"@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@playwright/test": "npm:^1.39.0"
express: "npm:^4.18.2"
http-proxy-middleware: "npm:^3.0.0-beta.1"
@@ -138,10 +138,10 @@ __metadata:
dependencies:
"@affine-test/fixtures": "workspace:*"
"@affine-test/kit": "workspace:*"
"@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@playwright/test": "npm:^1.39.0"
languageName: unknown
linkType: soft
@@ -212,12 +212,12 @@ __metadata:
"@affine/graphql": "workspace:*"
"@affine/i18n": "workspace:*"
"@affine/workspace": "workspace:*"
"@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/editor": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/icons": "npm:2.1.36"
"@blocksuite/lit": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@dnd-kit/core": "npm:^6.0.8"
"@dnd-kit/modifiers": "npm:^6.0.1"
"@dnd-kit/sortable": "npm:^8.0.0"
@@ -325,14 +325,14 @@ __metadata:
"@affine/templates": "workspace:*"
"@affine/workspace": "workspace:*"
"@aws-sdk/client-s3": "npm:3.433.0"
"@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/editor": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/icons": "npm:2.1.36"
"@blocksuite/lit": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/virgo": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/virgo": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@dnd-kit/core": "npm:^6.0.8"
"@dnd-kit/sortable": "npm:^8.0.0"
"@emotion/cache": "npm:^11.11.0"
@@ -437,10 +437,10 @@ __metadata:
"@affine/sdk": "workspace:*"
"@affine/templates": "workspace:*"
"@affine/vue-hello-world-plugin": "workspace:*"
"@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/editor": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/lit": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@electron-forge/cli": "npm:^7.1.0"
"@electron-forge/core": "npm:^7.1.0"
"@electron-forge/core-utils": "npm:^7.1.0"
@@ -488,8 +488,8 @@ __metadata:
version: 0.0.0-use.local
resolution: "@affine/env@workspace:packages/common/env"
dependencies:
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly"
lit: "npm:^3.0.2"
react: "npm:18.2.0"
react-dom: "npm:18.2.0"
@@ -685,11 +685,11 @@ __metadata:
version: 0.0.0-use.local
resolution: "@affine/sdk@workspace:packages/common/sdk"
dependencies:
"@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/editor": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly"
jotai: "npm:^2.5.1"
vite: "npm:^4.4.11"
vite-plugin-dts: "npm:3.6.0"
@@ -809,14 +809,14 @@ __metadata:
dependencies:
"@affine/component": "workspace:*"
"@affine/i18n": "workspace:*"
"@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/editor": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/icons": "npm:2.1.36"
"@blocksuite/lit": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/virgo": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/virgo": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@dnd-kit/sortable": "npm:^8.0.0"
"@storybook/addon-actions": "npm:^7.5.3"
"@storybook/addon-essentials": "npm:^7.5.3"
@@ -4057,31 +4057,31 @@ __metadata:
languageName: node
linkType: hard
"@blocksuite/block-std@npm:0.0.0-20231122113751-6bf81eb3-nightly":
version: 0.0.0-20231122113751-6bf81eb3-nightly
resolution: "@blocksuite/block-std@npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/block-std@npm:0.0.0-20231124123613-7c06e95d-nightly":
version: 0.0.0-20231124123613-7c06e95d-nightly
resolution: "@blocksuite/block-std@npm:0.0.0-20231124123613-7c06e95d-nightly"
dependencies:
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
lz-string: "npm:^1.5.0"
w3c-keyname: "npm:^2.2.8"
zod: "npm:^3.22.4"
peerDependencies:
"@blocksuite/store": 0.0.0-20231122113751-6bf81eb3-nightly
checksum: 0b75b090cd676cc8ca5972fad73d1afa75f2624e9bf1c21f2b770bf9272b592f3b90e6d6e26695d4512c0c0c38f139199420525b68258d954281c89e7785fb45
"@blocksuite/store": 0.0.0-20231124123613-7c06e95d-nightly
checksum: c2fbfdc60c8a6e2c38b8b572522d70543ae89b66283d8e246b8058eb6c8de83ede739dcfd5df204434147eefadbf0aeabc0bd26eb064f2fb771b8bfc32fc5443
languageName: node
linkType: hard
"@blocksuite/blocks@npm:0.0.0-20231122113751-6bf81eb3-nightly":
version: 0.0.0-20231122113751-6bf81eb3-nightly
resolution: "@blocksuite/blocks@npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/blocks@npm:0.0.0-20231124123613-7c06e95d-nightly":
version: 0.0.0-20231124123613-7c06e95d-nightly
resolution: "@blocksuite/blocks@npm:0.0.0-20231124123613-7c06e95d-nightly"
dependencies:
"@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/lit": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/virgo": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/virgo": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@floating-ui/dom": "npm:^1.5.3"
"@toeverything/theme": "npm:^0.7.24"
"@toeverything/theme": "npm:^0.7.26"
"@types/webfontloader": "npm:^1.6.36"
buffer: "npm:^6.0.3"
date-fns: "npm:^2.30.0"
@@ -4097,30 +4097,30 @@ __metadata:
sortablejs: "npm:^1.15.0"
webfontloader: "npm:^1.6.28"
zod: "npm:^3.22.4"
checksum: eab05e86fbe9a0e5b10d82ff6867af3a2987f56e5e5656874149d656ec3cecda0b5870ab59340e0f50c22843bc5f3fb7af3cb0399b8c65f58d76e02a1a08d2af
checksum: d08b8fae9fd7d5b64d1c0a7f2278ae71608848c9df16aa52be69818868e71ff0d1abf9bd082395e6ec3ccb97ea32e8e3f9483bfec46941105366125a4c2751f3
languageName: node
linkType: hard
"@blocksuite/editor@npm:0.0.0-20231122113751-6bf81eb3-nightly":
version: 0.0.0-20231122113751-6bf81eb3-nightly
resolution: "@blocksuite/editor@npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/editor@npm:0.0.0-20231124123613-7c06e95d-nightly":
version: 0.0.0-20231124123613-7c06e95d-nightly
resolution: "@blocksuite/editor@npm:0.0.0-20231124123613-7c06e95d-nightly"
dependencies:
"@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/lit": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@toeverything/theme": "npm:^0.7.24"
"@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@toeverything/theme": "npm:^0.7.26"
lit: "npm:^3.0.2"
checksum: 2792e80cc253926f5399d8e737ec6553a69ce74b1a881f4f7f5a8aeeb9ef54fe676a8bb522171f064b2f0638a551e1bbfd49668c63f810b23144313171809e47
checksum: 9da41777250311c926c65a4d8812dc9b43709b3822859617542c6ea6afc5092df1a28d37c228e16c8d0089ff2565aadc3ff234f51429f9366e93b8c21bb4aaf3
languageName: node
linkType: hard
"@blocksuite/global@npm:0.0.0-20231122113751-6bf81eb3-nightly":
version: 0.0.0-20231122113751-6bf81eb3-nightly
resolution: "@blocksuite/global@npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global@npm:0.0.0-20231124123613-7c06e95d-nightly":
version: 0.0.0-20231124123613-7c06e95d-nightly
resolution: "@blocksuite/global@npm:0.0.0-20231124123613-7c06e95d-nightly"
dependencies:
zod: "npm:^3.22.4"
checksum: ef0e6649c29290e4eb510cf019e83f0dcc0febc3b1cae24c982f20889534333ef44886f53a97540eb1c364c4eb4591ddf45cca4bcf6fbbe4fe15175bcc19eba2
checksum: 739e00a63011212a9b74c6af913b6d6d73b3cf2b75fd8abd2680635bb295515f1634ae535334f10d10a8c670e641e5098040c8b5f9d77abdc900bcc058232d03
languageName: node
linkType: hard
@@ -4134,26 +4134,26 @@ __metadata:
languageName: node
linkType: hard
"@blocksuite/lit@npm:0.0.0-20231122113751-6bf81eb3-nightly":
version: 0.0.0-20231122113751-6bf81eb3-nightly
resolution: "@blocksuite/lit@npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/lit@npm:0.0.0-20231124123613-7c06e95d-nightly":
version: 0.0.0-20231124123613-7c06e95d-nightly
resolution: "@blocksuite/lit@npm:0.0.0-20231124123613-7c06e95d-nightly"
dependencies:
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/virgo": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/virgo": "npm:0.0.0-20231124123613-7c06e95d-nightly"
lit: "npm:^3.0.2"
peerDependencies:
"@blocksuite/block-std": 0.0.0-20231122113751-6bf81eb3-nightly
"@blocksuite/store": 0.0.0-20231122113751-6bf81eb3-nightly
checksum: 1a3d2d27823ecde0ca7453fe0d4216fa39b8623f0306486972e618b2ed9dd22f6b53a58e699f82ccc63cc001af113969eb3fa23ce7016a79d77c55d54fa8705c
"@blocksuite/block-std": 0.0.0-20231124123613-7c06e95d-nightly
"@blocksuite/store": 0.0.0-20231124123613-7c06e95d-nightly
checksum: 8ce5fa1918cf1a39cb8922ba39a4c4248690350a258bf6fdae9f098de7ca9fd45762ee994781626bdea76a02697c2bb972044a02b33ab45f1aecec6ce94260e0
languageName: node
linkType: hard
"@blocksuite/store@npm:0.0.0-20231122113751-6bf81eb3-nightly":
version: 0.0.0-20231122113751-6bf81eb3-nightly
resolution: "@blocksuite/store@npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store@npm:0.0.0-20231124123613-7c06e95d-nightly":
version: 0.0.0-20231124123613-7c06e95d-nightly
resolution: "@blocksuite/store@npm:0.0.0-20231124123613-7c06e95d-nightly"
dependencies:
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/virgo": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/virgo": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@types/flexsearch": "npm:^0.7.3"
"@types/mdast": "npm:^4.0.2"
flexsearch: "npm:0.7.21"
@@ -4171,20 +4171,20 @@ __metadata:
peerDependencies:
async-call-rpc: ^6
yjs: ^13
checksum: 97132a465bb37383b851a278826e7ffd6e6b9dfdc64d3d34887c6837b432f0ed77816701d6f97f719896b48c6f259882cda429f1c6dff9de5ba0065d4a3b468e
checksum: fb993b2db0f3720c972a341923a6215fb86d2cb48812cc8fc7aecdb04ba2b904236271d18d796e9a7a5eb1a303dc516f91bc78e76d8fa94af1cee3016fb68f73
languageName: node
linkType: hard
"@blocksuite/virgo@npm:0.0.0-20231122113751-6bf81eb3-nightly":
version: 0.0.0-20231122113751-6bf81eb3-nightly
resolution: "@blocksuite/virgo@npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/virgo@npm:0.0.0-20231124123613-7c06e95d-nightly":
version: 0.0.0-20231124123613-7c06e95d-nightly
resolution: "@blocksuite/virgo@npm:0.0.0-20231124123613-7c06e95d-nightly"
dependencies:
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
zod: "npm:^3.22.4"
peerDependencies:
lit: ^3.0.2
yjs: ^13
checksum: a18e632590306cf14c02e6d308b8604532dc7cb1085d53a99857076768eabc6999b677d0e1ba225051d75f7915cc5a4954d4a303fd459436a7b5a902dbd7b7f1
checksum: 02732e5ff5c4206e23f473c467c5b786f646af6b67c44bb38f2faf70525513e9571c9b640ff8a20fba221968c0dfd5d05391761d57b297970b9d77eb3aa21b14
languageName: node
linkType: hard
@@ -6417,20 +6417,20 @@ __metadata:
linkType: hard
"@graphql-tools/load@npm:^8.0.0":
version: 8.0.0
resolution: "@graphql-tools/load@npm:8.0.0"
version: 8.0.1
resolution: "@graphql-tools/load@npm:8.0.1"
dependencies:
"@graphql-tools/schema": "npm:^10.0.0"
"@graphql-tools/utils": "npm:^10.0.0"
"@graphql-tools/utils": "npm:^10.0.11"
p-limit: "npm:3.1.0"
tslib: "npm:^2.4.0"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 64bbcaae28bf895f0d1f0636325a5b567cca1524ffd02bcad58a063087e74c65b9c1a5743adc2cc18a4f3c0379f7426090f8784abcddfd60997f187e6f100eb4
checksum: 008acefaaa067b4de26321803a9e95749ac39a009fe2614a2b39ce331efc858e0c3fe2de098b0b486fff857d061a164cbf0da479e03671fc9aaab1b882cf003e
languageName: node
linkType: hard
"@graphql-tools/merge@npm:9.0.0, @graphql-tools/merge@npm:^9.0.0":
"@graphql-tools/merge@npm:9.0.0":
version: 9.0.0
resolution: "@graphql-tools/merge@npm:9.0.0"
dependencies:
@@ -6454,6 +6454,18 @@ __metadata:
languageName: node
linkType: hard
"@graphql-tools/merge@npm:^9.0.0, @graphql-tools/merge@npm:^9.0.1":
version: 9.0.1
resolution: "@graphql-tools/merge@npm:9.0.1"
dependencies:
"@graphql-tools/utils": "npm:^10.0.10"
tslib: "npm:^2.4.0"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 9c2e57a0eee07b568c4c781303947e4c633f1dff54ba23a449965ecdf3301d97d14ba071479e0d46e3c142ee552daf834a798a1cf57a6b846f9d055c703a4e16
languageName: node
linkType: hard
"@graphql-tools/optimize@npm:^2.0.0":
version: 2.0.0
resolution: "@graphql-tools/optimize@npm:2.0.0"
@@ -6521,16 +6533,16 @@ __metadata:
linkType: hard
"@graphql-tools/schema@npm:^10.0.0":
version: 10.0.1
resolution: "@graphql-tools/schema@npm:10.0.1"
version: 10.0.2
resolution: "@graphql-tools/schema@npm:10.0.2"
dependencies:
"@graphql-tools/merge": "npm:^9.0.0"
"@graphql-tools/utils": "npm:^10.0.9"
"@graphql-tools/merge": "npm:^9.0.1"
"@graphql-tools/utils": "npm:^10.0.10"
tslib: "npm:^2.4.0"
value-or-promise: "npm:^1.0.12"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 314283995b9e83a398dbd8b46be00056d1737d60868b88732fa651902d914f8458901efda64a976d53ff6498e4f023dfa47ed80eb451d03e5d2dda8aea42ed8b
checksum: ce9ddf9ed446c9d421d0601b226315a10339b629f2a47ae39690ee3684ec759c4784e4d45a02c84de18a39478c217399d7852b086e5c18046b45b40ea18c4ccd
languageName: node
linkType: hard
@@ -6585,9 +6597,9 @@ __metadata:
languageName: node
linkType: hard
"@graphql-tools/utils@npm:^10.0.0, @graphql-tools/utils@npm:^10.0.2, @graphql-tools/utils@npm:^10.0.5, @graphql-tools/utils@npm:^10.0.8, @graphql-tools/utils@npm:^10.0.9":
version: 10.0.9
resolution: "@graphql-tools/utils@npm:10.0.9"
"@graphql-tools/utils@npm:^10.0.0, @graphql-tools/utils@npm:^10.0.10, @graphql-tools/utils@npm:^10.0.11, @graphql-tools/utils@npm:^10.0.2, @graphql-tools/utils@npm:^10.0.5, @graphql-tools/utils@npm:^10.0.8":
version: 10.0.11
resolution: "@graphql-tools/utils@npm:10.0.11"
dependencies:
"@graphql-typed-document-node/core": "npm:^3.1.1"
cross-inspect: "npm:1.0.0"
@@ -6595,7 +6607,7 @@ __metadata:
tslib: "npm:^2.4.0"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: f11e4b3289bf4c48c25e2dfbb53bb59395a4599c75234e4bb9a9bbe6d28469a3ae2248bf3c9705ed354e47817cdff7abdb6e66adcabcba6cb4a58eafe96d6fc1
checksum: 37515aa8fa6cd85a7c3ca7e174017abffb558b8110f18f261bd8c2074b1496d764ac19b3015f9030d6dbe3235e259665bc36652a4aab8064ff75d7120887c7cc
languageName: node
linkType: hard
@@ -10699,10 +10711,10 @@ __metadata:
languageName: node
linkType: hard
"@remix-run/router@npm:1.12.0":
version: 1.12.0
resolution: "@remix-run/router@npm:1.12.0"
checksum: f984e42cfe855991e1d3067f686f857614f12e8c1c45168a2d98e3fc3a427e232fd0b6cf145173b7cd132faf070702b532c34230a825d933908c54c85077fc69
"@remix-run/router@npm:1.13.0":
version: 1.13.0
resolution: "@remix-run/router@npm:1.13.0"
checksum: bb173a012d2036c5ee69babfe30c73975b970c2e5a0edaba138c302ae80d255e238e462e77365ab4efe819b6397e1a7f3a416d6200d17f9655f0ca1c51c4f45e
languageName: node
linkType: hard
@@ -12234,11 +12246,11 @@ __metadata:
linkType: hard
"@storybook/csf@npm:^0.1.0, @storybook/csf@npm:^0.1.1":
version: 0.1.1
resolution: "@storybook/csf@npm:0.1.1"
version: 0.1.2
resolution: "@storybook/csf@npm:0.1.2"
dependencies:
type-fest: "npm:^2.19.0"
checksum: d79f9eca1184a3e3e6cf6b19b47ea2378db3fb2ec574686d311597b0de83cd8148a038311a891a348089edac2dc4dd7ceb65a87845a882546d9fc00146e3ce29
checksum: 11168df65e7b6bd0e5d31e7e805c8ba80397fc190cb33424e043b72bbd85d8f826dba082503992d7f606b72484337ab9d091eca947550613e241fbef57780d4c
languageName: node
linkType: hard
@@ -12981,12 +12993,12 @@ __metadata:
dependencies:
"@affine/debug": "workspace:*"
"@affine/env": "workspace:*"
"@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/editor": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/lit": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@testing-library/react": "npm:^14.0.0"
"@types/image-blob-reduce": "npm:^4.1.3"
"@types/lodash.debounce": "npm:^4.0.7"
@@ -13033,11 +13045,11 @@ __metadata:
"@affine-test/fixtures": "workspace:*"
"@affine/sdk": "workspace:*"
"@affine/templates": "workspace:*"
"@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/editor": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/lit": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@testing-library/react": "npm:^14.0.0"
async-call-rpc: "npm:^6.3.1"
electron: "link:../../frontend/electron/node_modules/electron"
@@ -13075,10 +13087,10 @@ __metadata:
languageName: unknown
linkType: soft
"@toeverything/theme@npm:^0.7.20, @toeverything/theme@npm:^0.7.24":
version: 0.7.26
resolution: "@toeverything/theme@npm:0.7.26"
checksum: be895af99318af5ea2d624a78b4c8b7d0ce3cf00e825a593e75d2e3c3c394fd0501213f99968424021462d10d89bdb3a66a7f50e2e6a6e1c8981513b4b15ad72
"@toeverything/theme@npm:^0.7.20, @toeverything/theme@npm:^0.7.24, @toeverything/theme@npm:^0.7.26":
version: 0.7.27
resolution: "@toeverything/theme@npm:0.7.27"
checksum: 9349d30256d33b5528441dbca09387819225449f97549585b92b33e354d7fbb3d0e5e35001c89b8a7e3033d8f51d3aee75ef9edaaf14fb7dd717dd66bfe96009
languageName: node
linkType: hard
@@ -13086,8 +13098,8 @@ __metadata:
version: 0.0.0-use.local
resolution: "@toeverything/y-indexeddb@workspace:packages/common/y-indexeddb"
dependencies:
"@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly"
"@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly"
fake-indexeddb: "npm:^5.0.0"
idb: "npm:^7.1.1"
nanoid: "npm:^5.0.3"
@@ -13885,20 +13897,20 @@ __metadata:
linkType: hard
"@types/node@npm:*, @types/node@npm:>=10.0.0, @types/node@npm:>=12.12.47, @types/node@npm:>=13.7.0, @types/node@npm:>=8.1.0, @types/node@npm:^20.9.3":
version: 20.9.4
resolution: "@types/node@npm:20.9.4"
version: 20.10.0
resolution: "@types/node@npm:20.10.0"
dependencies:
undici-types: "npm:~5.26.4"
checksum: d567855b48e453b443499c17fc6c939d154732b54319a05b9b31db6e475e6458f053838635b201b1bb493d349d9b1af0aecc58b28fd6062e564e9fbf593199eb
checksum: c7d5ddbdbf3491e2363135c9611eb6bfae90eda2957279237fa232bcb29cd0df1cc3ee149d6de9915b754262a531ee2d57d33c9ecd58d763e8ad4856113822f3
languageName: node
linkType: hard
"@types/node@npm:^18.0.0, @types/node@npm:^18.11.18, @types/node@npm:^18.11.9":
version: 18.18.12
resolution: "@types/node@npm:18.18.12"
version: 18.18.13
resolution: "@types/node@npm:18.18.13"
dependencies:
undici-types: "npm:~5.26.4"
checksum: bd32120a52e7bdbf38dafa0f9cdb0c322115fc7fe38890b6c151507199d307652905077b878f0173c375389c87400d78f25a12fc8517b8f829547c95df37ef4e
checksum: 5dcab799e39570a858741a13373a584529d0e6b81120c8a2118e158749d9ace291748644d760af554fe73ab3cebdd91500314bf1ecd17a746fabcae06ebf9eea
languageName: node
linkType: hard
@@ -15131,15 +15143,15 @@ __metadata:
linkType: hard
"@whatwg-node/node-fetch@npm:^0.5.0":
version: 0.5.0
resolution: "@whatwg-node/node-fetch@npm:0.5.0"
version: 0.5.1
resolution: "@whatwg-node/node-fetch@npm:0.5.1"
dependencies:
"@whatwg-node/events": "npm:^0.1.0"
busboy: "npm:^1.6.0"
fast-querystring: "npm:^1.1.1"
fast-url-parser: "npm:^1.1.3"
tslib: "npm:^2.3.1"
checksum: b779288b07296e0be60e90e338de46b9d0c892d2e38b99bd04d062bbd4acb429607afc92b9bfdbf10841cef6f4b531e63415197583677de69a07e7b2e39350b9
checksum: 7e630fe252dea7fb8c844e8643510e819d7b803649b709ee9ffd663870cc6715302f7a32b934c9cc6a9568f657d322173dfa66dd28ba2bd026014bf1f8bfcd33
languageName: node
linkType: hard
@@ -19564,9 +19576,9 @@ __metadata:
linkType: hard
"electron-to-chromium@npm:^1.4.535":
version: 1.4.590
resolution: "electron-to-chromium@npm:1.4.590"
checksum: 04f54be67d5465432363e6a7cf0813dbd95a23d115f55679662677508d9e068ce241b4a8e1224a53fccc4037094928d38946529c5118c1184090e2609c65cd94
version: 1.4.593
resolution: "electron-to-chromium@npm:1.4.593"
checksum: 836463303815c1e599183e4cd84549b76c1451602c68119bfd428f706202c7d2e799cdbf1408e2d154155f84bc85020491b3244b2f5a29614d96fa7d6967b114
languageName: node
linkType: hard
@@ -19616,15 +19628,15 @@ __metadata:
linkType: soft
"electron@npm:^27.1.0":
version: 27.1.0
resolution: "electron@npm:27.1.0"
version: 27.1.2
resolution: "electron@npm:27.1.2"
dependencies:
"@electron/get": "npm:^2.0.0"
"@types/node": "npm:^18.11.18"
extract-zip: "npm:^2.0.1"
bin:
electron: cli.js
checksum: 8eed880bbda6efd55041cc855b93f632897139557761a913dd56c68a9e24bcb1c222a0b335f70e3df97e9692997f659d62600276bbf3714b374d841fed75bab4
checksum: c24cbdee2ea7e219d2224e3b3f9760a248723490442dcdf28a5c5419c693127bc0ab169945bd6e7ff7db84c69b3ed4db65e2691dd2f7e15b2b0eea905a1e8863
languageName: node
linkType: hard
@@ -26196,9 +26208,9 @@ __metadata:
linkType: hard
"lru-cache@npm:^10.0.1, lru-cache@npm:^9.1.1 || ^10.0.0":
version: 10.0.3
resolution: "lru-cache@npm:10.0.3"
checksum: 2fd14d8f828760eb4f1e696d20383bf52bb58a02aebfd08cb8035a66876c6fc1ec244ffdcf93d5a201fdad6d9f8c59de62c4fc5ff5604d67a4f833441f5b5945
version: 10.1.0
resolution: "lru-cache@npm:10.1.0"
checksum: 207278d6fa711fb1f94a0835d4d4737441d2475302482a14785b10515e4c906a57ebf9f35bf060740c9560e91c7c1ad5a04fd7ed030972a9ba18bce2a228e95b
languageName: node
linkType: hard
@@ -30938,26 +30950,26 @@ __metadata:
linkType: hard
"react-router-dom@npm:^6.16.0, react-router-dom@npm:^6.19.0":
version: 6.19.0
resolution: "react-router-dom@npm:6.19.0"
version: 6.20.0
resolution: "react-router-dom@npm:6.20.0"
dependencies:
"@remix-run/router": "npm:1.12.0"
react-router: "npm:6.19.0"
"@remix-run/router": "npm:1.13.0"
react-router: "npm:6.20.0"
peerDependencies:
react: ">=16.8"
react-dom: ">=16.8"
checksum: 38312efc11d3ef688062301479a8257a1495a81cd8dd7039c1f81aba6774963df7d21aaee2ba1a3c152857b70f4fb9966a3ccff47aca12212e854dcd6fc4deab
checksum: 4b6741c545cedf5a5c4f996deb953679dcc985425e0664e27b97fdb9ab1387cbe1a6a12bfc7f7c38ec40b15759b4bf6396930ec26540a4a81ae16d154fd35049
languageName: node
linkType: hard
"react-router@npm:6.19.0":
version: 6.19.0
resolution: "react-router@npm:6.19.0"
"react-router@npm:6.20.0":
version: 6.20.0
resolution: "react-router@npm:6.20.0"
dependencies:
"@remix-run/router": "npm:1.12.0"
"@remix-run/router": "npm:1.13.0"
peerDependencies:
react: ">=16.8"
checksum: 5454f4a4d65401430ded8f1033cebe4ccca771c3c827e8329c77dcfd73618ca9a32488fb58722bf6a07afef7d8e7ef22a710aae0f3337e5c20962bf6473d81a3
checksum: 2cdac5ad8b7a7bc230173b26768bcf3f6a9abc0a19983fa7b76b9ffdbeb44bfbd88fcc2033e9062defafef144db207859eb3162a9c9742d70cfce4e7166ff1e5
languageName: node
linkType: hard
@@ -33864,9 +33876,9 @@ __metadata:
linkType: hard
"tocbot@npm:^4.20.1":
version: 4.22.0
resolution: "tocbot@npm:4.22.0"
checksum: 41ff72948464c3d1ce6f63845c9ba1ddad11f94f25f3c22cfbbac6339ab912efc874144046fd85501c1f2e7194da3750151d7846e07b68e7663fbaed703a2163
version: 4.23.0
resolution: "tocbot@npm:4.23.0"
checksum: 883c7ef6baa9cd04ebb0b3ce9109f952212d695eb2ed5a44a1013a62922325f7a9b8ca8a91ad744cd717cebcb75fd94c7969d841707ea123caba4447497557f2
languageName: node
linkType: hard
@@ -34363,11 +34375,11 @@ __metadata:
linkType: hard
"undici@npm:^5.22.1, undici@npm:^5.27.2":
version: 5.27.2
resolution: "undici@npm:5.27.2"
version: 5.28.0
resolution: "undici@npm:5.28.0"
dependencies:
"@fastify/busboy": "npm:^2.0.0"
checksum: 2bf96b102fb84568fb235bdf6e1e352e5d2bf99566b243cd1b13b41578bf9dd5c7c3d3d82192b20a3fec61fe7a528f9d80cd5b4555ce65405c06c69b023013de
checksum: 70afe17996fbb84a4febbda78b30ce627fc6db585afe0c5191426d277ff4162f62ae36ddbc252b8aaba5cc1fd4e09bc6db8a919ff59adb10008fb3882492a171
languageName: node
linkType: hard
@@ -35774,8 +35786,8 @@ __metadata:
linkType: hard
"wrangler@npm:^3.15.0":
version: 3.17.0
resolution: "wrangler@npm:3.17.0"
version: 3.17.1
resolution: "wrangler@npm:3.17.1"
dependencies:
"@cloudflare/kv-asset-handler": "npm:^0.2.0"
"@esbuild-plugins/node-globals-polyfill": "npm:^0.2.3"
@@ -35798,7 +35810,7 @@ __metadata:
bin:
wrangler: bin/wrangler.js
wrangler2: bin/wrangler.js
checksum: 62139687f1b7887158336a6c3caa37aac05305665252c5dd87e01fda7cfac92f8530986f0859dc4215411827f1961d8673d15bc6d6d182aa4258fdd070fbc0eb
checksum: 44ccfbaabe18e882d692791587966b372a0d38b7742c93a7d99242cd25287a675f452c8a3a21bbb6bea8294d9eaf13e9991a7cc2533608bf95dca744cacfa156
languageName: node
linkType: hard
@@ -36028,7 +36040,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "y-provider@workspace:packages/common/y-provider"
dependencies:
"@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly"
"@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly"
vite: "npm:^4.4.11"
vite-plugin-dts: "npm:3.6.0"
vitest: "npm:0.34.6"