Compare commits

..

3 Commits

Author SHA1 Message Date
DarkSky
1a758b5e67 chore(server): improve transcript stability (#13821)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Enhanced audio/video detection for MP4 files to better distinguish
audio-only vs. video.

* **Dependencies**
* Added MP4 parsing dependency and updated AI provider libraries
(Anthropic, Google, OpenAI, etc.).

* **Bug Fixes**
  * Tightened authentication state validation for magic-link/OTP flows.
* Stricter space-join validation to reject invalid client
types/versions.
  * Improved transcript entry deduplication and data handling.

* **API**
* Transcript submit payload now requires infos and removes deprecated
url/mimeType fields.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-30 16:39:18 +08:00
DarkSky
a8194cdad3 fix(server): avoid a snowball effect of email sending failures (#13818)
fix #13802
2025-10-30 16:39:11 +08:00
DarkSky
7b05bff9c6 feat(server): expose mail server name config in admin dashboard (#13792)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
  * Added SMTP name configuration field to notification settings.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-30 16:38:50 +08:00
257 changed files with 2929 additions and 10311 deletions

View File

@@ -5,14 +5,7 @@ rustflags = ["-C", "target-feature=+crt-static"]
[target.'cfg(target_os = "linux")']
rustflags = ["-C", "link-args=-Wl,--warn-unresolved-symbols"]
[target.'cfg(target_os = "macos")']
rustflags = [
"-C",
"link-args=-Wl,-undefined,dynamic_lookup,-no_fixup_chains",
"-C",
"link-args=-all_load",
"-C",
"link-args=-weak_framework ScreenCaptureKit",
]
rustflags = ["-C", "link-args=-Wl,-undefined,dynamic_lookup,-no_fixup_chains", "-C", "link-args=-all_load", "-C", "link-args=-weak_framework ScreenCaptureKit"]
# https://sourceware.org/bugzilla/show_bug.cgi?id=21032
# https://sourceware.org/bugzilla/show_bug.cgi?id=21031
# https://github.com/rust-lang/rust/issues/134820

View File

@@ -2,8 +2,6 @@ version: '3.8'
services:
app:
security_opt:
- no-new-privileges:true
image: mcr.microsoft.com/devcontainers/base:bookworm
volumes:
- ../..:/workspaces:cached

View File

@@ -222,12 +222,12 @@
},
"SMTP.sender": {
"type": "string",
"description": "Sender of all the emails (e.g. \"AFFiNE Self Hosted <noreply@example.com>\")\n@default \"AFFiNE Self Hosted <noreply@example.com>\"\n@environment `MAILER_SENDER`",
"default": "AFFiNE Self Hosted <noreply@example.com>"
"description": "Sender of all the emails (e.g. \"AFFiNE Team <noreply@affine.pro>\")\n@default \"\"\n@environment `MAILER_SENDER`",
"default": ""
},
"SMTP.ignoreTLS": {
"type": "boolean",
"description": "Whether ignore email server's TLS certificate verification. Enable it for self-signed certificates.\n@default false\n@environment `MAILER_IGNORE_TLS`",
"description": "Whether ignore email server's TSL certification verification. Enable it for self-signed certificates.\n@default false\n@environment `MAILER_IGNORE_TLS`",
"default": false
},
"fallbackDomains": {
@@ -262,12 +262,12 @@
},
"fallbackSMTP.sender": {
"type": "string",
"description": "Sender of all the emails (e.g. \"AFFiNE Self Hosted <noreply@example.com>\")\n@default \"\"",
"description": "Sender of all the emails (e.g. \"AFFiNE Team <noreply@affine.pro>\")\n@default \"\"",
"default": ""
},
"fallbackSMTP.ignoreTLS": {
"type": "boolean",
"description": "Whether ignore email server's TLS certificate verification. Enable it for self-signed certificates.\n@default false",
"description": "Whether ignore email server's TSL certification verification. Enable it for self-signed certificates.\n@default false",
"default": false
}
}

View File

@@ -3,4 +3,4 @@ name: affine
description: AFFiNE cloud chart
type: application
version: 0.0.0
appVersion: "0.25.5"
appVersion: "0.22.4"

View File

@@ -3,7 +3,7 @@ name: doc
description: AFFiNE doc server
type: application
version: 0.0.0
appVersion: "0.25.5"
appVersion: "0.22.4"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -3,7 +3,7 @@ name: graphql
description: AFFiNE GraphQL server
type: application
version: 0.0.0
appVersion: "0.25.5"
appVersion: "0.22.4"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -3,7 +3,7 @@ name: renderer
description: AFFiNE renderer server
type: application
version: 0.0.0
appVersion: "0.25.5"
appVersion: "0.22.4"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -3,7 +3,7 @@ name: sync
description: AFFiNE Sync Server
type: application
version: 0.0.0
appVersion: "0.25.5"
appVersion: "0.22.4"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -19,7 +19,7 @@ env:
APP_NAME: affine
AFFINE_ENV: dev
COVERAGE: true
MACOSX_DEPLOYMENT_TARGET: '11.6'
MACOSX_DEPLOYMENT_TARGET: '10.13'
DEPLOYMENT_TYPE: affine
AFFINE_INDEXER_ENABLED: true

View File

@@ -25,7 +25,7 @@ env:
RELEASE_VERSION: ${{ inputs.app-version }}
DEBUG: 'affine:*,napi:*'
APP_NAME: affine
MACOSX_DEPLOYMENT_TARGET: '11.6'
MACOSX_DEPLOYMENT_TARGET: '10.13'
jobs:
before-make:
@@ -165,7 +165,7 @@ jobs:
mv packages/frontend/apps/electron/out/*/make/zip/linux/${{ matrix.spec.arch }}/*.zip ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.zip
mv packages/frontend/apps/electron/out/*/make/*.AppImage ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.appimage
mv packages/frontend/apps/electron/out/*/make/deb/${{ matrix.spec.arch }}/*.deb ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.deb
# mv packages/frontend/apps/electron/out/*/make/flatpak/*/*.flatpak ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.flatpak
mv packages/frontend/apps/electron/out/*/make/flatpak/*/*.flatpak ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.flatpak
- uses: actions/attest-build-provenance@v2
if: ${{ matrix.spec.platform == 'darwin' }}
@@ -466,4 +466,6 @@ jobs:
draft: ${{ inputs.build-type == 'stable' }}
prerelease: ${{ inputs.build-type != 'stable' }}
tag_name: v${{ env.RELEASE_VERSION}}
files: ./release/*
files: |
./release/*
./release/.env.example

3
.gitignore vendored
View File

@@ -33,9 +33,6 @@ node_modules
!.vscode/launch.template.json
!.vscode/extensions.json
# Kiro
.kiro
# misc
/.sass-cache
/connect.lock

View File

@@ -1,7 +1,7 @@
exclude = ["node_modules/**/*.toml", "target/**/*.toml"]
include = ["./*.toml", "./packages/**/*.toml"]
# https://taplo.tamasfe.dev/configuration/formatter-options.html
[formatting]
align_entries = true
indent_tables = true
reorder_keys = true
align_entries = true
column_width = 180
reorder_arrays = true
reorder_keys = true

154
Cargo.lock generated
View File

@@ -40,7 +40,6 @@ dependencies = [
name = "affine_common"
version = "0.1.0"
dependencies = [
"assert-json-diff",
"cc",
"chrono",
"criterion2",
@@ -51,7 +50,6 @@ dependencies = [
"rand 0.9.1",
"rayon",
"readability",
"serde",
"serde_json",
"sha3",
"strum_macros",
@@ -71,7 +69,6 @@ dependencies = [
"tree-sitter-scala",
"tree-sitter-typescript",
"url",
"y-octo",
]
[[package]]
@@ -445,6 +442,12 @@ dependencies = [
"num-traits",
]
[[package]]
name = "atomic_refcell"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c"
[[package]]
name = "auto_enums"
version = "0.8.7"
@@ -581,6 +584,18 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "bitvec"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
@@ -1597,6 +1612,12 @@ dependencies = [
"autocfg",
]
[[package]]
name = "funty"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "futf"
version = "0.1.5"
@@ -1703,6 +1724,19 @@ dependencies = [
"version_check",
]
[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
"wasm-bindgen",
]
[[package]]
name = "getrandom"
version = "0.2.16"
@@ -1800,6 +1834,10 @@ name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "hashbrown"
@@ -2217,6 +2255,16 @@ dependencies = [
"cpufeatures",
]
[[package]]
name = "lasso"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e14eda50a3494b3bf7b9ce51c52434a761e383d7238ce1dd5dcec2fbc13e9fb"
dependencies = [
"dashmap",
"hashbrown 0.14.5",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -3232,6 +3280,25 @@ version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "radium"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom 0.1.16",
"libc",
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc",
]
[[package]]
name = "rand"
version = "0.8.5"
@@ -3253,6 +3320,16 @@ dependencies = [
"rand_core 0.9.3",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core 0.5.1",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
@@ -3273,6 +3350,15 @@ dependencies = [
"rand_core 0.9.3",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom 0.1.16",
]
[[package]]
name = "rand_core"
version = "0.6.4"
@@ -3301,6 +3387,15 @@ dependencies = [
"rand 0.9.1",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rand_xorshift"
version = "0.3.0"
@@ -4382,6 +4477,12 @@ dependencies = [
"syn 2.0.101",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "tempfile"
version = "3.20.0"
@@ -5097,6 +5198,12 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@@ -5701,6 +5808,15 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
[[package]]
name = "wyz"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
]
[[package]]
name = "xml5ever"
version = "0.17.0"
@@ -5726,8 +5842,10 @@ dependencies = [
"arbitrary",
"assert-json-diff",
"async-lock",
"bitvec",
"byteorder",
"criterion",
"lasso",
"lib0",
"log",
"loom",
@@ -5744,7 +5862,7 @@ dependencies = [
"serde_json",
"smol_str",
"thiserror 2.0.12",
"yrs",
"yrs 0.23.4",
]
[[package]]
@@ -5774,7 +5892,18 @@ dependencies = [
"rand_chacha 0.9.0",
"regex",
"y-octo",
"yrs",
"y-sync",
"yrs 0.23.4",
]
[[package]]
name = "y-sync"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52e3675a497cde881a71e7e5c2ae1d087dfc7733ddece9b24a9a61408e969d3b"
dependencies = [
"thiserror 1.0.69",
"yrs 0.17.4",
]
[[package]]
@@ -5801,6 +5930,21 @@ dependencies = [
"synstructure",
]
[[package]]
name = "yrs"
version = "0.17.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4830316bfee4bec0044fe34a001cda783506d5c4c0852f8433c6041dfbfce51"
dependencies = [
"atomic_refcell",
"rand 0.7.3",
"serde",
"serde_json",
"smallstr",
"smallvec",
"thiserror 1.0.69",
]
[[package]]
name = "yrs"
version = "0.23.4"

View File

@@ -13,122 +13,109 @@ members = [
]
resolver = "3"
[workspace.package]
edition = "2024"
[workspace.package]
edition = "2024"
[workspace.dependencies]
affine_common = { path = "./packages/common/native" }
affine_nbstore = { path = "./packages/frontend/native/nbstore" }
ahash = "0.8"
anyhow = "1"
arbitrary = { version = "1.3", features = ["derive"] }
assert-json-diff = "2.0"
async-lock = { version = "3.4.0", features = ["loom"] }
base64-simd = "0.8"
bitvec = "1.0"
block2 = "0.6"
byteorder = "1.5"
chrono = "0.4"
clap = { version = "4.4", features = ["derive"] }
core-foundation = "0.10"
coreaudio-rs = "0.12"
cpal = "0.15"
criterion = { version = "0.5", features = ["html_reports"] }
criterion2 = { version = "3", default-features = false }
crossbeam-channel = "0.5"
dispatch2 = "0.3"
docx-parser = { git = "https://github.com/toeverything/docx-parser" }
dotenvy = "0.15"
file-format = { version = "0.28", features = ["reader"] }
homedir = "0.3"
infer = { version = "0.19.0" }
lasso = { version = "0.7", features = ["multi-threaded"] }
lib0 = { version = "0.16", features = ["lib0-serde"] }
libc = "0.2"
log = "0.4"
loom = { version = "0.7", features = ["checkpoint"] }
mimalloc = "0.1"
mp4parse = "0.17"
nanoid = "0.4"
napi = { version = "3.0.0-beta.3", features = [
"async",
"chrono_date",
"error_anyhow",
"napi9",
"serde",
] }
napi-build = { version = "2" }
napi-derive = { version = "3.0.0-beta.3" }
nom = "8"
notify = { version = "8", features = ["serde"] }
objc2 = "0.6"
objc2-foundation = "0.3"
once_cell = "1"
ordered-float = "5"
parking_lot = "0.12"
path-ext = "0.1.2"
pdf-extract = { git = "https://github.com/toeverything/pdf-extract", branch = "darksky/improve-font-decoding" }
phf = { version = "0.11", features = ["macros"] }
proptest = "1.3"
proptest-derive = "0.5"
rand = "0.9"
rand_chacha = "0.9"
rand_distr = "0.5"
rayon = "1.10"
readability = { version = "0.3.0", default-features = false }
regex = "1.10"
rubato = "0.16"
screencapturekit = "0.3"
serde = "1"
serde_json = "1"
sha3 = "0.10"
smol_str = "0.3"
sqlx = { version = "0.8", default-features = false, features = [
"chrono",
"macros",
"migrate",
"runtime-tokio",
"sqlite",
"tls-rustls",
] }
strum_macros = "0.27.0"
symphonia = { version = "0.5", features = ["all", "opt-simd"] }
text-splitter = "0.27"
thiserror = "2"
tiktoken-rs = "0.7"
tokio = "1.45"
tree-sitter = { version = "0.25" }
tree-sitter-c = { version = "0.24" }
tree-sitter-c-sharp = { version = "0.23" }
tree-sitter-cpp = { version = "0.23" }
tree-sitter-go = { version = "0.23" }
tree-sitter-java = { version = "0.23" }
tree-sitter-javascript = { version = "0.23" }
tree-sitter-kotlin-ng = { version = "1.1" }
tree-sitter-python = { version = "0.23" }
tree-sitter-rust = { version = "0.24" }
tree-sitter-scala = { version = "0.24" }
tree-sitter-typescript = { version = "0.23" }
uniffi = "0.29"
url = { version = "2.5" }
uuid = "1.8"
v_htmlescape = "0.15"
windows = { version = "0.61", features = [
"Win32_Devices_FunctionDiscovery",
"Win32_Foundation",
"Win32_Media_Audio",
"Win32_System_Com",
"Win32_System_Com_StructuredStorage",
"Win32_System_Diagnostics_ToolHelp",
"Win32_System_ProcessStatus",
"Win32_System_Threading",
"Win32_System_Variant",
"Win32_UI_Shell_PropertiesSystem",
] }
windows-core = { version = "0.61" }
y-octo = { path = "./packages/common/y-octo/core" }
y-sync = { version = "0.4" }
yrs = "0.23.0"
[workspace.dependencies]
affine_common = { path = "./packages/common/native" }
affine_nbstore = { path = "./packages/frontend/native/nbstore" }
ahash = "0.8"
anyhow = "1"
arbitrary = { version = "1.3", features = ["derive"] }
assert-json-diff = "2.0"
async-lock = { version = "3.4.0", features = ["loom"] }
base64-simd = "0.8"
bitvec = "1.0"
block2 = "0.6"
byteorder = "1.5"
cpal = "0.15"
chrono = "0.4"
clap = { version = "4.4", features = ["derive"] }
core-foundation = "0.10"
coreaudio-rs = "0.12"
criterion = { version = "0.5", features = ["html_reports"] }
criterion2 = { version = "3", default-features = false }
crossbeam-channel = "0.5"
dispatch2 = "0.3"
docx-parser = { git = "https://github.com/toeverything/docx-parser" }
dotenvy = "0.15"
file-format = { version = "0.28", features = ["reader"] }
homedir = "0.3"
infer = { version = "0.19.0" }
lasso = { version = "0.7", features = ["multi-threaded"] }
lib0 = { version = "0.16", features = ["lib0-serde"] }
libc = "0.2"
log = "0.4"
loom = { version = "0.7", features = ["checkpoint"] }
mimalloc = "0.1"
mp4parse = "0.17"
nanoid = "0.4"
napi = { version = "3.0.0-beta.3", features = ["async", "chrono_date", "error_anyhow", "napi9", "serde"] }
napi-build = { version = "2" }
napi-derive = { version = "3.0.0-beta.3" }
nom = "8"
notify = { version = "8", features = ["serde"] }
objc2 = "0.6"
objc2-foundation = "0.3"
once_cell = "1"
ordered-float = "5"
parking_lot = "0.12"
path-ext = "0.1.2"
pdf-extract = { git = "https://github.com/toeverything/pdf-extract", branch = "darksky/improve-font-decoding" }
phf = { version = "0.11", features = ["macros"] }
proptest = "1.3"
proptest-derive = "0.5"
rand = "0.9"
rand_chacha = "0.9"
rand_distr = "0.5"
rayon = "1.10"
readability = { version = "0.3.0", default-features = false }
regex = "1.10"
rubato = "0.16"
screencapturekit = "0.3"
serde = "1"
serde_json = "1"
sha3 = "0.10"
smol_str = "0.3"
sqlx = { version = "0.8", default-features = false, features = ["chrono", "macros", "migrate", "runtime-tokio", "sqlite", "tls-rustls"] }
strum_macros = "0.27.0"
symphonia = { version = "0.5", features = ["all", "opt-simd"] }
text-splitter = "0.27"
thiserror = "2"
tiktoken-rs = "0.7"
tokio = "1.45"
tree-sitter = { version = "0.25" }
tree-sitter-c = { version = "0.24" }
tree-sitter-c-sharp = { version = "0.23" }
tree-sitter-cpp = { version = "0.23" }
tree-sitter-go = { version = "0.23" }
tree-sitter-java = { version = "0.23" }
tree-sitter-javascript = { version = "0.23" }
tree-sitter-kotlin-ng = { version = "1.1" }
tree-sitter-python = { version = "0.23" }
tree-sitter-rust = { version = "0.24" }
tree-sitter-scala = { version = "0.24" }
tree-sitter-typescript = { version = "0.23" }
uniffi = "0.29"
url = { version = "2.5" }
uuid = "1.8"
v_htmlescape = "0.15"
windows = { version = "0.61", features = [
"Win32_Devices_FunctionDiscovery",
"Win32_UI_Shell_PropertiesSystem",
"Win32_Media_Audio",
"Win32_System_Variant",
"Win32_System_Com_StructuredStorage",
"Win32_System_Threading",
"Win32_System_ProcessStatus",
"Win32_Foundation",
"Win32_System_Com",
"Win32_System_Diagnostics_ToolHelp",
] }
windows-core = { version = "0.61" }
y-octo = { path = "./packages/common/y-octo/core" }
y-sync = { version = "0.4" }
yrs = "0.23.0"
[profile.dev.package.sqlx-macros]
opt-level = 3
@@ -139,6 +126,6 @@ lto = true
opt-level = 3
strip = "symbols"
# android uniffi bindgen requires symbols
[profile.release.package.affine_mobile_native]
strip = "none"
# android uniffi bindgen requires symbols
[profile.release.package.affine_mobile_native]
strip = "none"

View File

@@ -6,7 +6,7 @@
<br>
</h1>
<a href="https://affine.pro/download">
<img alt="affine logo" src="https://cdn.affine.pro/Github_hero_image2.png" style="width: 100%">
<img alt="affine logo" src="https://cdn.affine.pro/Github_hero_image1.png" style="width: 100%">
</a>
<br/>
<p align="center">

View File

@@ -296,7 +296,7 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5",
"version": "0.22.4",
"devDependencies": {
"@vanilla-extract/vite-plugin": "^5.0.0",
"msw": "^2.8.4",

View File

@@ -41,5 +41,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -1,4 +1,5 @@
import { focusBlockEnd } from '@blocksuite/affine-shared/commands';
import { FeatureFlagService } from '@blocksuite/affine-shared/services';
import { isInsideBlockByFlavour } from '@blocksuite/affine-shared/utils';
import { type SlashMenuConfig } from '@blocksuite/affine-widget-slash-menu';
import { FontIcon } from '@blocksuite/icons/lit';
@@ -17,11 +18,10 @@ export const calloutSlashMenuConfig: SlashMenuConfig = {
},
searchAlias: ['callout'],
group: '0_Basic@9',
when: ({ model }) => {
return !isInsideBlockByFlavour(
model.store,
model,
'affine:edgeless-text'
when: ({ std, model }) => {
return (
std.get(FeatureFlagService).getFlag('enable_callout') &&
!isInsideBlockByFlavour(model.store, model, 'affine:edgeless-text')
);
},
action: ({ model, std }) => {

View File

@@ -48,5 +48,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -48,5 +48,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -176,7 +176,7 @@ export class DatabaseTitle extends SignalWatcher(
private readonly isFocus$ = signal(false);
private onPressEnterKey() {
this.input.blur();
this.dataViewLogic.addRow?.('start');
}
get readonly$() {

View File

@@ -39,5 +39,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -43,5 +43,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -49,5 +49,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -323,8 +323,7 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
private readonly _renderEmbedView = () => {
const linkedDoc = this.linkedDoc;
const trash = linkedDoc?.meta?.trash;
const isDeleted = trash || !linkedDoc;
const isDeleted = !linkedDoc;
const isLoading = this._loading;
const isError = this.isError;
const isEmpty = this._isDocEmpty() && this.isBannerEmpty;
@@ -522,6 +521,11 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
);
this._setDocUpdatedAt();
this.disposables.add(
this.store.workspace.slots.docListUpdated.subscribe(() => {
this._setDocUpdatedAt();
})
);
if (this._referenceToNode) {
this._linkedDocMode = this.model.props.params?.mode ?? 'page';
@@ -550,13 +554,6 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
})
);
this.disposables.add(
this.store.workspace.slots.docListUpdated.subscribe(() => {
this._setDocUpdatedAt();
this.refreshData();
})
);
this._trackCitationDeleteEvent();
}

View File

@@ -56,9 +56,6 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
// Caches total bounds, includes all blocks and elements.
private _cachedBounds: Bound | null = null;
private _hasRenderedSyncedView = false;
private _hasInitedFitEffect = false;
private readonly _initEdgelessFitEffect = () => {
const fitToContent = () => {
if (this.isPageMode) return;
@@ -360,14 +357,10 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
};
refreshData = () => {
this._load()
.then(() => {
this._isEmptySyncedDoc = isEmptyDoc(this.syncedDoc, this.editorMode);
})
.catch(e => {
console.error(e);
this._error = true;
});
this._load().catch(e => {
console.error(e);
this._error = true;
});
};
title$ = computed(() => {
@@ -452,8 +445,7 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
this._cycle = false;
const syncedDoc = this.syncedDoc;
const trash = syncedDoc?.meta?.trash;
if (trash || !syncedDoc) {
if (!syncedDoc) {
this._deleted = true;
this._loading = false;
return;
@@ -529,7 +521,6 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
this.disposables.add(
this.store.workspace.slots.docListUpdated.subscribe(() => {
this._setDocUpdatedAt();
this.refreshData();
})
);
@@ -561,6 +552,8 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
this._selectBlock();
}
});
this._initEdgelessFitEffect();
}
override renderBlock() {
@@ -588,21 +581,12 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
);
}
!this._hasRenderedSyncedView && (this._hasRenderedSyncedView = true);
return this._renderSyncedView();
}
override updated(changedProperties: PropertyValues) {
super.updated(changedProperties);
this.syncedDocCard?.requestUpdate();
if (!this._hasInitedFitEffect && this._hasRenderedSyncedView) {
/* Register the resizeObserver AFTER syncdView viewport's own resizeObserver
* so that viewport.onResize() use up-to-date boundingClientRect values */
this._hasInitedFitEffect = true;
this._initEdgelessFitEffect();
}
}
@state()

View File

@@ -49,5 +49,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -46,5 +46,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -46,5 +46,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -49,5 +49,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -46,7 +46,7 @@
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@types/lodash-es": "^4.17.12",
"dompurify": "^3.3.0",
"dompurify": "^3.2.4",
"html2canvas": "^1.4.1",
"lit": "^3.2.0",
"lodash-es": "^4.17.21",
@@ -67,5 +67,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -46,5 +46,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -113,7 +113,7 @@ export class CanvasRenderer {
* It is not recommended to set width and height to 100%.
*/
private _canvasSizeUpdater(dpr = window.devicePixelRatio) {
const { width, height, viewScale } = this.viewport;
const { width, height } = this.viewport;
const actualWidth = Math.ceil(width * dpr);
const actualHeight = Math.ceil(height * dpr);
@@ -124,8 +124,6 @@ export class CanvasRenderer {
update(canvas: HTMLCanvasElement) {
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
canvas.style.transform = `scale(${1 / viewScale})`;
canvas.style.transformOrigin = `top left`;
canvas.width = actualWidth;
canvas.height = actualHeight;
},

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -82,5 +82,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -229,9 +229,9 @@ export class DatePicker extends WithDisposable(LitElement) {
private _modeDecade(offset: number) {
this._yearCursor = clamp(
this._yearCursor + offset,
this._minYear,
this._maxYear
this._maxYear,
this._yearCursor + offset
);
this._getYearMatrix();
}

View File

@@ -46,5 +46,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -1,36 +0,0 @@
import { describe, expect, test } from 'vitest';
import { mobileEffects } from '../view-presets/table/mobile/effect.js';
import type { MobileTableGroup } from '../view-presets/table/mobile/group.js';
import { pcEffects } from '../view-presets/table/pc/effect.js';
import type { TableGroup } from '../view-presets/table/pc/group.js';
/** @vitest-environment happy-dom */
describe('TableGroup', () => {
test('toggle collapse on pc', () => {
pcEffects();
const group = document.createElement(
'affine-data-view-table-group'
) as TableGroup;
expect(group.collapsed$.value).toBe(false);
(group as any)._toggleCollapse();
expect(group.collapsed$.value).toBe(true);
(group as any)._toggleCollapse();
expect(group.collapsed$.value).toBe(false);
});
test('toggle collapse on mobile', () => {
mobileEffects();
const group = document.createElement(
'mobile-table-group'
) as MobileTableGroup;
expect(group.collapsed$.value).toBe(false);
(group as any)._toggleCollapse();
expect(group.collapsed$.value).toBe(true);
(group as any)._toggleCollapse();
expect(group.collapsed$.value).toBe(false);
});
});

View File

@@ -26,10 +26,13 @@ const GroupTitleMobile = (
const type = groupData.tType;
if (!type) return nothing;
const icon = html` <uni-lit
class="group-header-icon"
.uni="${groupData.property.icon}"
></uni-lit>`;
const icon =
groupData.value == null
? ''
: html` <uni-lit
class="group-header-icon"
.uni="${groupData.property.icon}"
></uni-lit>`;
const props: GroupRenderProps = {
group: groupData,
readonly: ops.readonly,
@@ -123,10 +126,13 @@ export const GroupTitle = (
const type = groupData.tType;
if (!type) return nothing;
const icon = html` <uni-lit
class="group-header-icon"
.uni="${groupData.property.icon}"
></uni-lit>`;
const icon =
groupData.value == null
? ''
: html` <uni-lit
class="group-header-icon"
.uni="${groupData.property.icon}"
></uni-lit>`;
const props: GroupRenderProps = {
group: groupData,
readonly: ops.readonly,

View File

@@ -1,44 +0,0 @@
/**
* Shared utility for managing table group collapsed state in sessionStorage.
* Used by both PC and mobile table group implementations.
*/
/**
* Gets the collapsed state for a specific table group from sessionStorage.
* @param viewId - The ID of the table view
* @param groupKey - The key of the group
* @returns The collapsed state as a boolean, or false if not found or invalid
*/
export function getCollapsedState(viewId: string, groupKey: string): boolean {
try {
const value = sessionStorage.getItem(
`affine:table-group:${viewId}:${groupKey}:collapsed`
);
if (!value) return false;
const parsed = JSON.parse(value);
return typeof parsed === 'boolean' ? parsed : false;
} catch {
return false;
}
}
/**
* Sets the collapsed state for a specific table group in sessionStorage.
* @param viewId - The ID of the table view
* @param groupKey - The key of the group
* @param collapsed - The collapsed state to store
*/
export function setCollapsedState(
viewId: string,
groupKey: string,
collapsed: boolean
): void {
try {
sessionStorage.setItem(
`affine:table-group:${viewId}:${groupKey}:collapsed`,
JSON.stringify(collapsed)
);
} catch {
// ignore
}
}

View File

@@ -4,22 +4,16 @@ import {
popupTargetFromElement,
} from '@blocksuite/affine-components/context-menu';
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
import {
PlusIcon,
ToggleDownIcon,
ToggleRightIcon,
} from '@blocksuite/icons/lit';
import { PlusIcon } from '@blocksuite/icons/lit';
import { ShadowlessElement } from '@blocksuite/std';
import { signal } from '@preact/signals-core';
import { cssVarV2 } from '@toeverything/theme/v2';
import { css, html, nothing, unsafeCSS } from 'lit';
import { css, html, unsafeCSS } from 'lit';
import { property } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import { GroupTitle } from '../../../core/group-by/group-title.js';
import type { Group } from '../../../core/group-by/trait.js';
import type { Row } from '../../../core/index.js';
import { getCollapsedState, setCollapsedState } from '../collapsed-state.js';
import { LEFT_TOOL_BAR_WIDTH } from '../consts.js';
import type { MobileTableViewUILogic } from './table-view-ui-logic.js';
@@ -48,28 +42,6 @@ const styles = css`
line-height: 20px;
color: var(--affine-text-secondary-color);
}
.group-toggle-btn {
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
cursor: pointer;
transition: background-color 150ms cubic-bezier(0.42, 0, 1, 1);
}
.group-toggle-btn:hover {
background: var(--affine-hover-color);
}
.group-toggle-btn svg {
width: 16px;
height: 16px;
flex-shrink: 0;
user-select: none;
}
`;
export class MobileTableGroup extends SignalWatcher(
@@ -77,29 +49,6 @@ export class MobileTableGroup extends SignalWatcher(
) {
static override styles = styles;
collapsed$ = signal(false);
private storageLoaded = false;
private _loadCollapsedState() {
if (this.storageLoaded) return;
this.storageLoaded = true;
const view = this.tableViewLogic?.view;
if (!view) return;
const value = getCollapsedState(view.id, this.group?.key ?? 'all');
this.collapsed$.value = value;
}
private readonly _toggleCollapse = (e?: MouseEvent) => {
e?.stopPropagation();
const next = !this.collapsed$.value;
this.collapsed$.value = next;
const view = this.tableViewLogic?.view;
if (view) {
setCollapsedState(view.id, this.group?.key ?? 'all', next);
}
};
private readonly clickAddRow = () => {
this.view.rowAdd('end', this.group?.key);
this.requestUpdate();
@@ -144,27 +93,6 @@ export class MobileTableGroup extends SignalWatcher(
<div
style="position: sticky;left: 0;width: max-content;padding: 6px 0;margin-bottom: 4px;display:flex;align-items:center;gap: 12px;max-width: 400px"
>
<div
class=${`group-toggle-btn ${this.collapsed$.value ? '' : 'expanded'}`}
role="button"
aria-expanded=${this.collapsed$.value ? 'false' : 'true'}
aria-label=${this.collapsed$.value
? 'Expand group'
: 'Collapse group'}
tabindex="0"
@click=${this._toggleCollapse}
@keydown=${(e: KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
this._toggleCollapse();
}
}}
>
${this.collapsed$.value
? ToggleRightIcon({ width: '16px', height: '16px' })
: ToggleDownIcon({ width: '16px', height: '16px' })}
</div>
${GroupTitle(this.group, {
readonly: this.view.readonly$.value,
clickAdd: this.clickAddRowInStart,
@@ -181,6 +109,7 @@ export class MobileTableGroup extends SignalWatcher(
private renderRows(rows: Row[]) {
return html`
<mobile-table-header
.renderGroupHeader="${this.renderGroupHeader}"
.tableViewManager="${this.view}"
></mobile-table-header>
<div class="mobile-affine-table-body">
@@ -215,26 +144,8 @@ export class MobileTableGroup extends SignalWatcher(
`;
}
override willUpdate(changed: Map<PropertyKey, unknown>): void {
super.willUpdate(changed);
if (
!this.storageLoaded &&
(changed.has('group') || changed.has('tableViewLogic'))
) {
this._loadCollapsedState();
}
}
override connectedCallback(): void {
super.connectedCallback();
this._loadCollapsedState();
}
override render() {
return html`
${this.collapsed$.value ? this.renderGroupHeader() : nothing}
${this.collapsed$.value ? nothing : this.renderRows(this.rows)}
`;
return this.renderRows(this.rows);
}
@property({ attribute: false })

View File

@@ -57,12 +57,12 @@ const styles = css`
margin-inline: 5px;
}
.stats-cell .label {
.label {
text-transform: uppercase;
color: var(--affine-text-secondary-color);
}
.stats-cell .value {
.value {
color: var(--affine-text-primary-color);
}
`;

View File

@@ -4,15 +4,11 @@ import {
popupTargetFromElement,
} from '@blocksuite/affine-components/context-menu';
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
import {
PlusIcon,
ToggleDownIcon,
ToggleRightIcon,
} from '@blocksuite/icons/lit';
import { PlusIcon } from '@blocksuite/icons/lit';
import { ShadowlessElement } from '@blocksuite/std';
import { effect, signal } from '@preact/signals-core';
import { effect } from '@preact/signals-core';
import { cssVarV2 } from '@toeverything/theme/v2';
import { css, html, nothing, unsafeCSS } from 'lit';
import { css, html, unsafeCSS } from 'lit';
import { property, query } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
@@ -22,7 +18,6 @@ import type { Row } from '../../../core/index.js';
import { createDndContext } from '../../../core/utils/wc-dnd/dnd-context.js';
import { defaultActivators } from '../../../core/utils/wc-dnd/sensors/index.js';
import { linearMove } from '../../../core/utils/wc-dnd/utils/linear-move.js';
import { getCollapsedState, setCollapsedState } from '../collapsed-state.js';
import { LEFT_TOOL_BAR_WIDTH } from '../consts.js';
import { TableViewAreaSelection } from '../selection';
import { DataViewColumnPreview } from './header/column-renderer.js';
@@ -35,12 +30,6 @@ const styles = css`
opacity: 1;
}
affine-data-view-table-group {
margin-top: 4px;
padding-top: 4px;
border-top: 1px solid var(--affine-border-color);
}
.data-view-table-group-add-row {
display: flex;
width: 100%;
@@ -53,10 +42,6 @@ const styles = css`
border-bottom: 1px solid ${unsafeCSS(cssVarV2.layer.insideBorder.border)};
}
.affine-data-view-table-group:hover svg {
fill: var(--affine-icon-color);
}
@media print {
.data-view-table-group-add-row {
display: none;
@@ -75,28 +60,6 @@ const styles = css`
line-height: 20px;
color: var(--affine-text-secondary-color);
}
.group-toggle-btn {
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
cursor: pointer;
transition: background-color 150ms cubic-bezier(0.42, 0, 1, 1);
}
.group-toggle-btn:hover {
background: var(--affine-hover-color);
}
.group-toggle-btn svg {
width: 16px;
height: 16px;
flex-shrink: 0;
user-select: none;
}
`;
export class TableGroup extends SignalWatcher(
@@ -104,29 +67,6 @@ export class TableGroup extends SignalWatcher(
) {
static override styles = styles;
collapsed$ = signal(false);
private storageLoaded = false;
private _loadCollapsedState() {
if (this.storageLoaded) return;
this.storageLoaded = true;
const view = this.tableViewLogic?.view;
if (!view) return;
const value = getCollapsedState(view.id, this.group?.key ?? 'all');
this.collapsed$.value = value;
}
private readonly _toggleCollapse = (e?: MouseEvent) => {
e?.stopPropagation();
const next = !this.collapsed$.value;
this.collapsed$.value = next;
const view = this.tableViewLogic?.view;
if (view) {
setCollapsedState(view.id, this.group?.key ?? 'all', next);
}
};
private readonly clickAddRow = () => {
this.view.rowAdd('end', this.group?.key);
const selectionController = this.tableViewLogic.selectionController;
@@ -197,32 +137,10 @@ export class TableGroup extends SignalWatcher(
if (!this.group) {
return null;
}
return html`
<div
style="position: sticky;left: 0;width: max-content;padding: 6px 0;margin-bottom: 4px;display:flex;align-items:center;gap: 8px;max-width: 400px"
style="position: sticky;left: 0;width: max-content;padding: 6px 0;margin-bottom: 4px;display:flex;align-items:center;gap: 12px;max-width: 400px"
>
<div
class=${`group-toggle-btn ${this.collapsed$.value ? '' : 'expanded'}`}
role="button"
aria-expanded=${this.collapsed$.value ? 'false' : 'true'}
aria-label=${this.collapsed$.value
? 'Expand group'
: 'Collapse group'}
tabindex="0"
@click=${this._toggleCollapse}
@keydown=${(e: KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
this._toggleCollapse();
}
}}
>
${this.collapsed$.value
? ToggleRightIcon({ width: '16px', height: '16px' })
: ToggleDownIcon({ width: '16px', height: '16px' })}
</div>
${GroupTitle(this.group, {
readonly: this.view.readonly$.value,
clickAdd: this.clickAddRowInStart,
@@ -326,8 +244,8 @@ export class TableGroup extends SignalWatcher(
private renderRows(rows: Row[]) {
return html`
<affine-database-column-header
.renderGroupHeader=${this.renderGroupHeader}
.tableViewLogic=${this.tableViewLogic}
.renderGroupHeader="${this.renderGroupHeader}"
.tableViewLogic="${this.tableViewLogic}"
></affine-database-column-header>
<div class="affine-database-block-rows">
${repeat(
@@ -366,27 +284,13 @@ export class TableGroup extends SignalWatcher(
`;
}
override willUpdate(changed: Map<PropertyKey, unknown>): void {
super.willUpdate(changed);
if (
!this.storageLoaded &&
(changed.has('group') || changed.has('tableViewLogic'))
) {
this._loadCollapsedState();
}
}
override connectedCallback(): void {
super.connectedCallback();
this._loadCollapsedState();
this.showIndicator();
}
override render() {
return html`
${this.collapsed$.value ? this.renderGroupHeader() : nothing}
${this.collapsed$.value ? nothing : this.renderRows(this.rows)}
`;
return this.renderRows(this.rows);
}
@query('.affine-database-block-rows')

View File

@@ -57,12 +57,12 @@ const styles = css`
margin-inline: 5px;
}
.stats-cell .label {
.label {
text-transform: uppercase;
color: var(--affine-text-secondary-color);
}
.stats-cell .value {
.value {
color: var(--affine-text-primary-color);
}
`;

View File

@@ -26,5 +26,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -35,5 +35,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -40,5 +40,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -41,5 +41,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -43,5 +43,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -51,5 +51,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -60,20 +60,12 @@ export class PanTool extends BaseTool<PanToolOption> {
return;
}
const currentTool = this.controller.currentToolOption$.peek();
const { toolType, options: originalToolOptions } = currentTool;
if (toolType?.toolName === PanTool.toolName) {
return;
}
evt.raw.preventDefault();
const selectionToRestore = this.gfx.selection.surfaceSelections.slice();
const currentTool = this.controller.currentToolOption$.peek();
const restoreToPrevious = () => {
this.gfx.selection.set(selectionToRestore);
const { toolType, options: originalToolOptions } = currentTool;
const selectionToRestore = this.gfx.selection.surfaceSelections;
if (!toolType) return;
// restore to DefaultTool if previous tool is CopilotTool
if (toolType.toolName === 'copilot') {
@@ -96,18 +88,21 @@ export class PanTool extends BaseTool<PanToolOption> {
} as RestorablePresentToolOptions;
}
this.controller.setTool(toolType, finalOptions);
this.gfx.selection.set(selectionToRestore);
};
// If in presentation mode, disable black background after middle mouse drag
if (toolType?.toolName === 'frameNavigator') {
if (currentTool.toolType?.toolName === 'frameNavigator') {
const slots = this.std.get(EdgelessLegacySlotIdentifier);
slots.navigatorSettingUpdated.next({
blackBackground: false,
});
}
this.controller.setTool(PanTool, {
panning: true,
requestAnimationFrame(() => {
this.controller.setTool(PanTool, {
panning: true,
});
});
const dispose = on(document, 'pointerup', evt => {

View File

@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -43,5 +43,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -25,5 +25,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.21.0"
}

View File

@@ -47,5 +47,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -50,5 +50,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -56,5 +56,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -43,5 +43,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -30,5 +30,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -41,5 +41,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -24,7 +24,7 @@
"@types/lodash-es": "^4.17.12",
"@types/mdast": "^4.0.4",
"bytes": "^3.1.2",
"dompurify": "^3.3.0",
"dompurify": "^3.2.4",
"fractional-indexing": "^3.2.0",
"lit": "^3.2.0",
"lodash-es": "^4.17.21",
@@ -75,5 +75,5 @@
"devDependencies": {
"vitest": "3.1.3"
},
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -20,7 +20,6 @@ import {
type ToDocSnapshotPayload,
type Transformer,
} from '@blocksuite/store';
import DOMPurify from 'dompurify';
import type { Root } from 'hast';
import rehypeParse from 'rehype-parse';
import rehypeStringify from 'rehype-stringify';
@@ -298,8 +297,7 @@ export class HtmlAdapter extends BaseAdapter<Html> {
override async toDocSnapshot(
payload: ToDocSnapshotPayload<string>
): Promise<DocSnapshot> {
const sanitized = DOMPurify.sanitize(payload.file);
const htmlAst = this._htmlToAst(sanitized);
const htmlAst = this._htmlToAst(payload.file);
const titleAst = HastUtils.querySelector(htmlAst, 'title');
const blockSnapshotRoot = {
type: 'block',

View File

@@ -17,6 +17,7 @@ export interface BlockSuiteFlags {
enable_mobile_linked_doc_menu: boolean;
enable_mobile_database_editing: boolean;
enable_block_meta: boolean;
enable_callout: boolean;
enable_edgeless_scribbled_style: boolean;
enable_table_virtual_scroll: boolean;
enable_turbo_renderer: boolean;
@@ -42,6 +43,7 @@ export class FeatureFlagService extends StoreExtension {
enable_mobile_linked_doc_menu: false,
enable_block_meta: true,
enable_mobile_database_editing: false,
enable_callout: false,
enable_edgeless_scribbled_style: false,
enable_table_virtual_scroll: false,
enable_turbo_renderer: false,

View File

@@ -92,13 +92,6 @@ const FileTypes: NonNullable<OpenFilePickerOptions['types']> = [
'application/zip': ['.zip'],
},
},
{
description: 'Docx',
accept: {
'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
['.docx'],
},
},
{
description: 'MindMap',
accept: {
@@ -118,7 +111,6 @@ type AcceptTypes =
| 'Markdown'
| 'Html'
| 'Zip'
| 'Docx'
| 'MindMap';
export async function openFilesWith(

View File

@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -34,5 +34,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -36,5 +36,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -40,5 +40,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -38,5 +38,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -36,5 +36,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -34,5 +34,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -55,5 +55,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -47,7 +47,10 @@ import {
getTextSelectionCommand,
} from '@blocksuite/affine-shared/commands';
import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts';
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
import {
FeatureFlagService,
TelemetryProvider,
} from '@blocksuite/affine-shared/services';
import type { AffineTextStyleAttributes } from '@blocksuite/affine-shared/types';
import {
createDefaultDoc,
@@ -287,11 +290,10 @@ const textToolActionItems: KeyboardToolbarActionItem[] = [
{
name: 'Callout',
icon: FontIcon(),
showWhen: ({ rootComponent: { model } }) => {
return !isInsideBlockByFlavour(
model.store,
model,
'affine:edgeless-text'
showWhen: ({ std, rootComponent: { model } }) => {
return (
std.get(FeatureFlagService).getFlag('enable_callout') &&
!isInsideBlockByFlavour(model.store, model, 'affine:edgeless-text')
);
},
action: ({ rootComponent: { model }, std }) => {

View File

@@ -28,7 +28,6 @@
"fflate": "^0.8.2",
"lit": "^3.2.0",
"lodash-es": "^4.17.21",
"mammoth": "^1.11.0",
"rxjs": "^7.8.1",
"zod": "^3.23.8"
},
@@ -42,5 +41,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -134,7 +134,7 @@ export class ImportDoc extends WithDisposable(LitElement) {
);
return;
}
this._onImportSuccess(entryId ? [entryId] : [], {
this._onImportSuccess([entryId], {
isWorkspaceFile,
importedCount: pageIds.length,
});

View File

@@ -181,10 +181,6 @@ export class LinkedDocPopover extends SignalWatcher(
target: eventSource,
signal: keydownObserverAbortController.signal,
interceptor: (event, next) => {
if (event.key === 'GroupNext' || event.key === 'GroupPrevious') {
event.stopPropagation();
return;
}
if (event.key === 'ArrowRight' || event.key === 'ArrowLeft') {
event.preventDefault();
event.stopPropagation();

View File

@@ -1,47 +0,0 @@
import type { ExtensionType, Schema, Workspace } from '@blocksuite/store';
// @ts-ignore
import { convertToHtml } from 'mammoth/mammoth.browser';
import { HtmlTransformer } from './html';
type ImportDocxOptions = {
collection: Workspace;
schema: Schema;
imported: Blob;
extensions: ExtensionType[];
};
/**
* Imports a .docx file into a doc.
*
* @param options - The import options.
* @param options.collection - The target doc collection.
* @param options.schema - The schema of the target doc collection.
* @param options.imported - The .docx file as a Blob.
* @returns A Promise that resolves to the ID of the newly created doc, or undefined if import fails.
*/
async function importDocx({
collection,
schema,
imported,
extensions,
}: ImportDocxOptions) {
try {
const { value } = await convertToHtml({
arrayBuffer: await imported.arrayBuffer(),
});
return await HtmlTransformer.importHTMLToDoc({
collection,
schema,
html: value,
extensions,
});
} catch (e) {
console.error('Failed to import .docx file:', e);
return undefined;
}
}
export const DocxTransformer = {
importDocx,
};

View File

@@ -1,4 +1,3 @@
export { DocxTransformer } from './docx.js';
export { HtmlTransformer } from './html.js';
export { MarkdownTransformer } from './markdown.js';
export { NotionHtmlTransformer } from './notion-html.js';

View File

@@ -21,28 +21,6 @@ type ImportNotionZipOptions = {
extensions: ExtensionType[];
};
type PageIcon = {
type: 'emoji' | 'image';
content: string; // emoji unicode or image URL/data
};
type FolderHierarchy = {
name: string;
path: string;
children: Map<string, FolderHierarchy>;
pageId?: string;
parentPath?: string;
icon?: PageIcon;
};
type ImportNotionZipResult = {
entryId: string | undefined;
pageIds: string[];
isWorkspaceFile: boolean;
hasMarkdown: boolean;
folderHierarchy?: FolderHierarchy;
};
function getProvider(extensions: ExtensionType[]) {
const container = new Container();
extensions.forEach(ext => {
@@ -51,197 +29,6 @@ function getProvider(extensions: ExtensionType[]) {
return container.provider();
}
function parseFolderPath(filePath: string): {
folderParts: string[];
fileName: string;
} {
const parts = filePath.split('/');
const fileName = parts.pop() || '';
return { folderParts: parts.filter(part => part.length > 0), fileName };
}
function extractPageIcon(doc: Document): PageIcon | undefined {
// Look for Notion page icon in the HTML
// Notion export format: <div class="page-header-icon undefined"><span class="icon">✅</span></div>
console.log('=== Extracting page icon ===');
// Check if there's a head section with title for debugging
const headTitle = doc.querySelector('head title');
if (headTitle) {
console.log('Page title from head:', headTitle.textContent);
}
// Look for the exact Notion export structure: .page-header-icon .icon
const notionIconSpan = doc.querySelector('.page-header-icon .icon');
if (notionIconSpan && notionIconSpan.textContent) {
const iconContent = notionIconSpan.textContent.trim();
console.log('Found Notion icon (.page-header-icon .icon):', iconContent);
if (/\p{Emoji}/u.test(iconContent)) {
return {
type: 'emoji',
content: iconContent,
};
}
}
// Look for page header area for debugging
const pageHeader = doc.querySelector('.page-header-icon');
if (pageHeader) {
console.log(
'Found .page-header-icon:',
pageHeader.outerHTML.substring(0, 300) + '...'
);
}
// Fallback: try to find emoji icons with older selectors
const emojiIcon = doc.querySelector('.page-header-icon .notion-emoji');
if (emojiIcon && emojiIcon.textContent) {
console.log(
'Found emoji icon (.page-header-icon .notion-emoji):',
emojiIcon.textContent
);
return {
type: 'emoji',
content: emojiIcon.textContent.trim(),
};
}
// Try alternative emoji selectors
const altEmojiIcon = doc.querySelector('[role="img"][aria-label]');
if (
altEmojiIcon &&
altEmojiIcon.textContent &&
/\p{Emoji}/u.test(altEmojiIcon.textContent)
) {
console.log(
'Found emoji icon ([role="img"][aria-label]):',
altEmojiIcon.textContent
);
return {
type: 'emoji',
content: altEmojiIcon.textContent.trim(),
};
}
// Look for image icons in the page header
const imageIcon = doc.querySelector('.page-header-icon img');
if (imageIcon) {
const src = imageIcon.getAttribute('src');
console.log('Found image icon (.page-header-icon img):', src);
if (src) {
return {
type: 'image',
content: src,
};
}
}
// Fallback: Look for any span with emoji class "icon" in page header area
const iconSpans = doc.querySelectorAll('span.icon');
for (const span of iconSpans) {
if (span.textContent && /\p{Emoji}/u.test(span.textContent.trim())) {
const parent = span.parentElement;
console.log(
'Found emoji in span.icon:',
span.textContent,
'parent classes:',
parent?.className
);
// Check if this is in a page header context
if (
parent &&
(parent.classList.contains('page-header-icon') ||
parent.closest('.page-header-icon'))
) {
console.log(
'Using emoji from span.icon in page header:',
span.textContent
);
return {
type: 'emoji',
content: span.textContent.trim(),
};
}
}
}
// Fallback: Try to find icons in the page title area that might contain emoji
const pageTitle = doc.querySelector('.page-title, h1');
if (pageTitle && pageTitle.textContent) {
console.log('Page title element found:', pageTitle.textContent);
const text = pageTitle.textContent.trim();
// Check if the title starts with an emoji
const emojiMatch = text.match(/^(\p{Emoji}+)/u);
if (emojiMatch) {
console.log('Found emoji in title:', emojiMatch[1]);
return {
type: 'emoji',
content: emojiMatch[1],
};
}
}
console.log('No page icon found');
return undefined;
}
function buildFolderHierarchy(
pagePaths: Array<{ path: string; pageId: string; icon?: PageIcon }>
): FolderHierarchy {
const root: FolderHierarchy = {
name: '',
path: '',
children: new Map(),
};
for (const { path, pageId, icon } of pagePaths) {
const { folderParts, fileName } = parseFolderPath(path);
let current = root;
let currentPath = '';
// Navigate/create folder structure
for (const folderName of folderParts) {
const parentPath = currentPath;
currentPath = currentPath ? `${currentPath}/${folderName}` : folderName;
if (!current.children.has(folderName)) {
current.children.set(folderName, {
name: folderName,
path: currentPath,
parentPath: parentPath || undefined,
children: new Map(),
});
}
current = current.children.get(folderName)!;
}
// If this is a page file, associate it with the current folder
if (fileName.endsWith('.html') && !fileName.startsWith('index.html')) {
const pageName = fileName.replace(/\.html$/, '');
if (!current.children.has(pageName)) {
current.children.set(pageName, {
name: pageName,
path: path,
parentPath: current.path || undefined,
children: new Map(),
pageId: pageId,
icon: icon,
});
} else {
// Update existing entry with pageId and icon
const existingPage = current.children.get(pageName)!;
existingPage.pageId = pageId;
if (icon) {
existingPage.icon = icon;
}
}
}
}
return root;
}
/**
* Imports a Notion zip file into the BlockSuite collection.
*
@@ -255,24 +42,18 @@ function buildFolderHierarchy(
* - pageIds: An array of imported page IDs.
* - isWorkspaceFile: Whether the imported file is a workspace file.
* - hasMarkdown: Whether the zip contains markdown files.
* - folderHierarchy: The parsed folder hierarchy from the Notion export.
*/
async function importNotionZip({
collection,
schema,
imported,
extensions,
}: ImportNotionZipOptions): Promise<ImportNotionZipResult> {
}: ImportNotionZipOptions) {
const provider = getProvider(extensions);
const pageIds: string[] = [];
let isWorkspaceFile = false;
let hasMarkdown = false;
let entryId: string | undefined;
const pagePathsWithIds: Array<{
path: string;
pageId: string;
icon?: PageIcon;
}> = [];
const parseZipFile = async (path: File | Blob) => {
const unzip = new Unzip();
await unzip.load(path);
@@ -299,8 +80,6 @@ async function importNotionZip({
isWorkspaceFile = true;
continue;
}
let pageIcon: PageIcon | undefined;
if (lastSplitIndex !== -1) {
const text = await content.text();
const doc = new DOMParser().parseFromString(text, 'text/html');
@@ -309,10 +88,7 @@ async function importNotionZip({
// Skip empty pages
continue;
}
// Extract page icon from the HTML
pageIcon = extractPageIcon(doc);
}
const id = collection.idGenerator();
const splitPath = path.split('/');
while (splitPath.length > 0) {
@@ -320,7 +96,6 @@ async function importNotionZip({
splitPath.shift();
}
pagePaths.push(path);
pagePathsWithIds.push({ path, pageId: id, icon: pageIcon });
if (entryId === undefined && lastSplitIndex === -1) {
entryId = id;
}
@@ -391,14 +166,7 @@ async function importNotionZip({
const allPromises = await parseZipFile(imported);
await Promise.all(allPromises.flat());
entryId = entryId ?? pageIds[0];
// Build folder hierarchy from collected paths
const folderHierarchy =
pagePathsWithIds.length > 0
? buildFolderHierarchy(pagePathsWithIds)
: undefined;
return { entryId, pageIds, isWorkspaceFile, hasMarkdown, folderHierarchy };
return { entryId, pageIds, isWorkspaceFile, hasMarkdown };
}
export const NotionHtmlTransformer = {

View File

@@ -37,5 +37,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -37,5 +37,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

View File

@@ -35,5 +35,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.5"
"version": "0.22.4"
}

Some files were not shown because too many files have changed in this diff Show More