mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-08 02:23:43 +00:00
Compare commits
100 Commits
fix/checkb
...
v0.20.5-ca
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22924c767c | ||
|
|
c418e89fb9 | ||
|
|
1c29d0e269 | ||
|
|
321247a2fa | ||
|
|
8e5d7858c4 | ||
|
|
37fbfbcf3e | ||
|
|
889625cdaa | ||
|
|
4c3c953a36 | ||
|
|
899a957fab | ||
|
|
2cd83be621 | ||
|
|
170066050e | ||
|
|
abad289783 | ||
|
|
5d3c365d97 | ||
|
|
86c449319b | ||
|
|
0e24ea3ac5 | ||
|
|
0e26498e6a | ||
|
|
a587abca85 | ||
|
|
3711e13e0e | ||
|
|
18bdf830b4 | ||
|
|
73fa04e249 | ||
|
|
4eae3cc66a | ||
|
|
629aea48df | ||
|
|
bd1b26a230 | ||
|
|
fdde818ddd | ||
|
|
f23f29610c | ||
|
|
6353e72078 | ||
|
|
7527d36547 | ||
|
|
2b0c91b58b | ||
|
|
b05315c6fa | ||
|
|
0f66f9dff1 | ||
|
|
87ca6b1981 | ||
|
|
815b437025 | ||
|
|
9ca8fc895a | ||
|
|
54bf4b05f4 | ||
|
|
4012732058 | ||
|
|
5c16b803b7 | ||
|
|
91e00f08d0 | ||
|
|
bab4a07c9f | ||
|
|
61541a2d15 | ||
|
|
b59f60c60b | ||
|
|
008fdfc234 | ||
|
|
5c5c9f8dcd | ||
|
|
12acf7e4a0 | ||
|
|
82dffcbf67 | ||
|
|
04b1239a25 | ||
|
|
6b76037e39 | ||
|
|
7227b7f8f6 | ||
|
|
22191caa82 | ||
|
|
b9b3c0db66 | ||
|
|
27518e1fb1 | ||
|
|
d476d3b1df | ||
|
|
f1df774188 | ||
|
|
90b37ce65c | ||
|
|
27fc4afc35 | ||
|
|
e90c00c3b7 | ||
|
|
1d865ad883 | ||
|
|
cd0bec5d31 | ||
|
|
59526080d4 | ||
|
|
9e0cae58d7 | ||
|
|
c50184bee6 | ||
|
|
d7d33868d4 | ||
|
|
b19c1df43e | ||
|
|
985906aa13 | ||
|
|
d50860eee2 | ||
|
|
b995b4f965 | ||
|
|
1bb5dd0eda | ||
|
|
fc4a716ef1 | ||
|
|
4c736bc190 | ||
|
|
272d41e32d | ||
|
|
d57ef5c5b3 | ||
|
|
1fd3d618be | ||
|
|
7c8ba13aad | ||
|
|
b3821ad619 | ||
|
|
caa4dfedfc | ||
|
|
18dfad28d7 | ||
|
|
f43a848e18 | ||
|
|
8cec22cc64 | ||
|
|
be94f3fc17 | ||
|
|
e9484e8e15 | ||
|
|
f25266ec88 | ||
|
|
3252dd7a31 | ||
|
|
903d260880 | ||
|
|
2c79d7229f | ||
|
|
fd6d96a38e | ||
|
|
1c5e360d7e | ||
|
|
589622043c | ||
|
|
ce87dcf58e | ||
|
|
2732b96d00 | ||
|
|
0f8c837fbe | ||
|
|
c058f94e15 | ||
|
|
d25b216311 | ||
|
|
e1fd8f5d80 | ||
|
|
866b096304 | ||
|
|
e38e59d4e5 | ||
|
|
7dbc1e300d | ||
|
|
1a9bfeaa2c | ||
|
|
97cc814a22 | ||
|
|
d63f16da5e | ||
|
|
0e4a79959f | ||
|
|
f3911b1b5e |
10
.github/actions/build-rust/action.yml
vendored
10
.github/actions/build-rust/action.yml
vendored
@@ -44,12 +44,14 @@ runs:
|
||||
RUSTUP_HOME: ${{ env.DEV_DRIVE }}/.rustup
|
||||
|
||||
- name: Set CC
|
||||
if: ${{ contains(inputs.target, 'linux') && inputs.package != '@affine/native' && inputs.no-build != 'true' }}
|
||||
if: ${{ contains(inputs.target, 'linux') && inputs.no-build != 'true' }}
|
||||
working-directory: ${{ env.DEV_DRIVE_WORKSPACE || github.workspace }}
|
||||
shell: bash
|
||||
# https://github.com/tree-sitter/tree-sitter/issues/4186
|
||||
# pass -D_BSD_SOURCE to clang to fix the tree-sitter build issue
|
||||
run: |
|
||||
echo "CC=clang" >> "$GITHUB_ENV"
|
||||
echo "TARGET_CC=clang" >> "$GITHUB_ENV"
|
||||
echo "CC=clang -D_BSD_SOURCE" >> "$GITHUB_ENV"
|
||||
echo "TARGET_CC=clang -D_BSD_SOURCE" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@v2
|
||||
@@ -82,7 +84,7 @@ runs:
|
||||
shell: bash
|
||||
if: ${{ runner.os == 'Windows' && inputs.no-build != 'true' }}
|
||||
run: |
|
||||
yarn workspace ${{ inputs.package }} build --target ${{ inputs.target }} --use-napi-cross
|
||||
yarn workspace ${{ inputs.package }} build --target ${{ inputs.target }}
|
||||
env:
|
||||
DEBUG: 'napi:*'
|
||||
CARGO_HOME: ${{ env.DEV_DRIVE }}/.cargo
|
||||
|
||||
23
.github/actions/deploy/deploy.mjs
vendored
23
.github/actions/deploy/deploy.mjs
vendored
@@ -25,6 +25,7 @@ const {
|
||||
AFFINE_GOOGLE_CLIENT_ID,
|
||||
AFFINE_GOOGLE_CLIENT_SECRET,
|
||||
CLOUD_SQL_IAM_ACCOUNT,
|
||||
APP_IAM_ACCOUNT,
|
||||
GCLOUD_CONNECTION_NAME,
|
||||
GCLOUD_CLOUD_SQL_INTERNAL_ENDPOINT,
|
||||
REDIS_HOST,
|
||||
@@ -99,16 +100,22 @@ const createHelmCommand = ({ isDryRun }) => {
|
||||
`--set-string global.redis.password="${REDIS_PASSWORD}"`,
|
||||
]
|
||||
: [];
|
||||
const serviceAnnotations =
|
||||
const serviceAnnotations = [
|
||||
`--set-json web.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
|
||||
`--set-json graphql.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
|
||||
`--set-json sync.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
|
||||
`--set-json doc.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
|
||||
].concat(
|
||||
isProduction || isBeta || isInternal
|
||||
? [
|
||||
`--set-json web.service.annotations=\"{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }\"`,
|
||||
`--set-json graphql.service.annotations=\"{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }\"`,
|
||||
`--set-json sync.service.annotations=\"{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }\"`,
|
||||
`--set-json cloud-sql-proxy.serviceAccount.annotations=\"{ \\"iam.gke.io/gcp-service-account\\": \\"${CLOUD_SQL_IAM_ACCOUNT}\\" }\"`,
|
||||
`--set-json cloud-sql-proxy.nodeSelector=\"{ \\"iam.gke.io/gke-metadata-server-enabled\\": \\"true\\" }\"`,
|
||||
`--set-json web.service.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
|
||||
`--set-json graphql.service.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
|
||||
`--set-json sync.service.annotations="{ \\"cloud.google.com/neg\\": \\"{\\\\\\"ingress\\\\\\": true}\\" }"`,
|
||||
`--set-json cloud-sql-proxy.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${CLOUD_SQL_IAM_ACCOUNT}\\" }"`,
|
||||
`--set-json cloud-sql-proxy.nodeSelector="{ \\"iam.gke.io/gke-metadata-server-enabled\\": \\"true\\" }"`,
|
||||
]
|
||||
: [];
|
||||
: []
|
||||
);
|
||||
|
||||
const cpu = cpuConfig[buildType];
|
||||
const resources = cpu
|
||||
@@ -136,7 +143,7 @@ const createHelmCommand = ({ isDryRun }) => {
|
||||
`--namespace ${namespace}`,
|
||||
`--set-string global.app.buildType="${buildType}"`,
|
||||
`--set global.ingress.enabled=true`,
|
||||
`--set-json global.ingress.annotations=\"{ \\"kubernetes.io/ingress.class\\": \\"gce\\", \\"kubernetes.io/ingress.allow-http\\": \\"true\\", \\"kubernetes.io/ingress.global-static-ip-name\\": \\"${STATIC_IP_NAME}\\" }\"`,
|
||||
`--set-json global.ingress.annotations="{ \\"kubernetes.io/ingress.class\\": \\"gce\\", \\"kubernetes.io/ingress.allow-http\\": \\"true\\", \\"kubernetes.io/ingress.global-static-ip-name\\": \\"${STATIC_IP_NAME}\\" }"`,
|
||||
`--set-string global.ingress.host="${host}"`,
|
||||
`--set global.objectStorage.r2.enabled=true`,
|
||||
`--set-string global.objectStorage.r2.accountId="${R2_ACCOUNT_ID}"`,
|
||||
|
||||
2
.github/helm/affine/charts/doc/values.yaml
vendored
2
.github/helm/affine/charts/doc/values.yaml
vendored
@@ -26,7 +26,7 @@ podSecurityContext:
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: '2'
|
||||
cpu: '1'
|
||||
memory: 4Gi
|
||||
|
||||
probe:
|
||||
|
||||
@@ -36,7 +36,7 @@ spec:
|
||||
- name: NODE_ENV
|
||||
value: "{{ .Values.env }}"
|
||||
- name: NODE_OPTIONS
|
||||
value: "--max-old-space-size=4096"
|
||||
value: "--max-old-space-size=2048"
|
||||
- name: NO_COLOR
|
||||
value: "1"
|
||||
- name: DEPLOYMENT_TYPE
|
||||
|
||||
@@ -71,8 +71,8 @@ podSecurityContext:
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: '4'
|
||||
memory: 4Gi
|
||||
cpu: '2'
|
||||
memory: 2Gi
|
||||
|
||||
probe:
|
||||
initialDelaySeconds: 20
|
||||
|
||||
@@ -36,7 +36,7 @@ spec:
|
||||
- name: NODE_ENV
|
||||
value: "{{ .Values.env }}"
|
||||
- name: NODE_OPTIONS
|
||||
value: "--max-old-space-size=4096"
|
||||
value: "--max-old-space-size=2048"
|
||||
- name: NO_COLOR
|
||||
value: "1"
|
||||
- name: DEPLOYMENT_TYPE
|
||||
|
||||
@@ -27,8 +27,8 @@ podSecurityContext:
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: '4'
|
||||
memory: 4Gi
|
||||
cpu: '1'
|
||||
memory: 2Gi
|
||||
|
||||
probe:
|
||||
initialDelaySeconds: 20
|
||||
|
||||
6
.github/helm/affine/charts/sync/values.yaml
vendored
6
.github/helm/affine/charts/sync/values.yaml
vendored
@@ -24,11 +24,11 @@ podSecurityContext:
|
||||
|
||||
resources:
|
||||
limits:
|
||||
cpu: '4'
|
||||
memory: 8Gi
|
||||
requests:
|
||||
cpu: '2'
|
||||
memory: 4Gi
|
||||
requests:
|
||||
cpu: '1'
|
||||
memory: 2Gi
|
||||
|
||||
probe:
|
||||
initialDelaySeconds: 20
|
||||
|
||||
27
.github/workflows/build-test.yml
vendored
27
.github/workflows/build-test.yml
vendored
@@ -309,6 +309,10 @@ jobs:
|
||||
with:
|
||||
workspace-copy: true
|
||||
drive-size: 8GB
|
||||
drive-format: NTFS
|
||||
env-mapping: |
|
||||
CARGO_HOME,{{ DEV_DRIVE }}/.cargo
|
||||
RUSTUP_HOME,{{ DEV_DRIVE }}/.rustup
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
@@ -387,6 +391,28 @@ jobs:
|
||||
path: dist.tar.gz
|
||||
if-no-files-found: error
|
||||
|
||||
native-unit-test:
|
||||
name: Native Unit Test
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- optimize_ci
|
||||
- build-native
|
||||
if: needs.optimize_ci.outputs.skip == 'false'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
extra-flags: workspaces focus @affine-tools/cli @affine/monorepo @affine/native
|
||||
electron-install: false
|
||||
- name: Download affine.linux-x64-gnu.node
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: affine.linux-x64-gnu.node
|
||||
path: ./packages/frontend/native
|
||||
- name: Unit Test
|
||||
run: yarn affine @affine/native test
|
||||
|
||||
server-test:
|
||||
name: Server Test
|
||||
runs-on: ubuntu-latest
|
||||
@@ -897,6 +923,7 @@ jobs:
|
||||
- build-native
|
||||
- build-server-native
|
||||
- build-electron-renderer
|
||||
- native-unit-test
|
||||
- server-test
|
||||
- rust-test
|
||||
- copilot-api-test
|
||||
|
||||
1
.github/workflows/deploy.yml
vendored
1
.github/workflows/deploy.yml
vendored
@@ -116,6 +116,7 @@ jobs:
|
||||
REDIS_HOST: ${{ secrets.REDIS_HOST }}
|
||||
REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }}
|
||||
CLOUD_SQL_IAM_ACCOUNT: ${{ secrets.CLOUD_SQL_IAM_ACCOUNT }}
|
||||
APP_IAM_ACCOUNT: ${{ secrets.APP_IAM_ACCOUNT }}
|
||||
STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }}
|
||||
STRIPE_WEBHOOK_KEY: ${{ secrets.STRIPE_WEBHOOK_KEY }}
|
||||
STATIC_IP_NAME: ${{ secrets.STATIC_IP_NAME }}
|
||||
|
||||
2
.github/workflows/sync-i18n.yml
vendored
2
.github/workflows/sync-i18n.yml
vendored
@@ -53,7 +53,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: l10n_crowdin_translations
|
||||
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# we will make this file shared by prettier|eslint|oxlint
|
||||
**/node_modules
|
||||
.yarn
|
||||
.github
|
||||
.github/helm
|
||||
.vscode
|
||||
.yarnrc.yml
|
||||
.docker
|
||||
|
||||
445
Cargo.lock
generated
445
Cargo.lock
generated
@@ -44,7 +44,7 @@ dependencies = [
|
||||
"sha3",
|
||||
"strum_macros",
|
||||
"text-splitter",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.11",
|
||||
"tiktoken-rs",
|
||||
"tree-sitter",
|
||||
"tree-sitter-c",
|
||||
@@ -69,6 +69,8 @@ dependencies = [
|
||||
"core-foundation",
|
||||
"coreaudio-rs",
|
||||
"dispatch2",
|
||||
"libc",
|
||||
"mp3lame-encoder",
|
||||
"napi",
|
||||
"napi-build",
|
||||
"napi-derive",
|
||||
@@ -76,6 +78,7 @@ dependencies = [
|
||||
"objc2-foundation",
|
||||
"rubato",
|
||||
"screencapturekit",
|
||||
"symphonia",
|
||||
"thiserror 2.0.11",
|
||||
"uuid",
|
||||
]
|
||||
@@ -273,9 +276,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.95"
|
||||
version = "1.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||
checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
@@ -286,6 +289,12 @@ dependencies = [
|
||||
"derive_arbitrary",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "async-compat"
|
||||
version = "0.2.4"
|
||||
@@ -338,6 +347,15 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "autotools"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef941527c41b0fc0dd48511a8154cd5fc7e29200a0ff8b7203c5d777dbc795cf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.74"
|
||||
@@ -500,6 +518,12 @@ version = "3.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
@@ -552,9 +576,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.12"
|
||||
version = "1.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2"
|
||||
checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
@@ -608,16 +632,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.39"
|
||||
version = "0.4.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
||||
checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.52.6",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -660,9 +684,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.28"
|
||||
version = "4.5.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff"
|
||||
checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -670,9 +694,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.27"
|
||||
version = "4.5.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7"
|
||||
checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -885,9 +909,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "criterion2"
|
||||
version = "2.0.0"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09db22066fd79bd628faf416dac96e44054deb00531601bcc20c6d12506b3701"
|
||||
checksum = "7b43b9cdbf592c78d882f2a3b9e6ebe8aedc749ef84915103a0248802ce2f6b3"
|
||||
dependencies = [
|
||||
"anes",
|
||||
"bpaf",
|
||||
@@ -952,9 +976,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.3.0"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f06b1425736ba96096116f063c9d10be2352a7cde0cbea829a717008e114aec9"
|
||||
checksum = "21d960ecacd0a1bf55e73144b72de745e7bf275c7952c50e36e8af0a0cb7ab1f"
|
||||
dependencies = [
|
||||
"ctor-proc-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor-proc-macro"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c426d2ba3e525b39c1f0a9ba41b9fe61878dee11fa4e4a76b6ab440f46c5db5d"
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
@@ -1099,9 +1132,9 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -1117,9 +1150,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
@@ -1173,6 +1206,12 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "extended"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af9673d8203fcb076b19dfd17e38b3d4ae9f44959416ea532ce72415a6020365"
|
||||
|
||||
[[package]]
|
||||
name = "fancy-regex"
|
||||
version = "0.13.0"
|
||||
@@ -1198,9 +1237,9 @@ checksum = "e7ef3d5e8ae27277c8285ac43ed153158178ef0f79567f32024ca8140a0c7cd8"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.35"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
|
||||
checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
@@ -1874,9 +1913,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
version = "0.2.170"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
@@ -1923,9 +1962,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
|
||||
checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
@@ -1939,9 +1978,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.25"
|
||||
version = "0.4.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
|
||||
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
|
||||
|
||||
[[package]]
|
||||
name = "loom"
|
||||
@@ -2075,9 +2114,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.3"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
|
||||
checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
@@ -2093,6 +2132,27 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mp3lame-encoder"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc8c8b5cdbe788ccd1098c3d3635298a011cffdebdd3460c9ca5060a7551557b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mp3lame-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mp3lame-sys"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acaec8842b2ebd61692a6c8c2b9f3edbf5c36e5e5c4677b5911430eaf859377c"
|
||||
dependencies = [
|
||||
"autotools",
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanoid"
|
||||
version = "0.4.0"
|
||||
@@ -2104,9 +2164,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "napi"
|
||||
version = "3.0.0-alpha.28"
|
||||
version = "3.0.0-alpha.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dd957e2cc4bd62b730b10ff1f35775f8a81dac84a3bfac273b0ec4336f53ab8"
|
||||
checksum = "b1911b4f0d33fbcb5f46ff68319ec053ab8a655f3a17440eae1246a23ba2ad78"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.8.0",
|
||||
@@ -2120,15 +2180,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "napi-build"
|
||||
version = "2.1.4"
|
||||
version = "2.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db836caddef23662b94e16bf1f26c40eceb09d6aee5d5b06a7ac199320b69b19"
|
||||
checksum = "40685973218af4aa4b42486652692c294c44b5a67e4b2202df721c9063f2e51c"
|
||||
|
||||
[[package]]
|
||||
name = "napi-derive"
|
||||
version = "3.0.0-alpha.26"
|
||||
version = "3.0.0-alpha.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0f0b6f3f77925d8fd2030855af659ce428a7bb6e10e94852e226f509186ba7c"
|
||||
checksum = "c8097918a9af1976700eac6944b120b65ad17bf6d38906703d2b68e17ee89256"
|
||||
dependencies = [
|
||||
"convert_case 0.7.1",
|
||||
"napi-derive-backend",
|
||||
@@ -2139,9 +2199,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "napi-derive-backend"
|
||||
version = "2.0.0-alpha.26"
|
||||
version = "2.0.0-alpha.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c694bb49a2fa84dd9542d51eece39a57519f9cf1fc2deefa9d119ab8181e374d"
|
||||
checksum = "8e5adc92fcdec3aa09f591bd2b139d7c669399f34b8211fe653641b52d40d3b3"
|
||||
dependencies = [
|
||||
"convert_case 0.7.1",
|
||||
"proc-macro2",
|
||||
@@ -2629,9 +2689,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.12.2"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14"
|
||||
checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"memchr",
|
||||
@@ -2671,8 +2731,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.0",
|
||||
"zerocopy 0.8.16",
|
||||
"rand_core 0.9.2",
|
||||
"zerocopy 0.8.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2692,7 +2752,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.0",
|
||||
"rand_core 0.9.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2706,12 +2766,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.0"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff"
|
||||
checksum = "7a509b1a2ffbe92afab0e55c8fd99dea1c280e8171bd2d88682bb20bc41cbc2c"
|
||||
dependencies = [
|
||||
"getrandom 0.3.1",
|
||||
"zerocopy 0.8.16",
|
||||
"zerocopy 0.8.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2774,9 +2834,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.8"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
||||
checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
]
|
||||
@@ -2827,15 +2887,14 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.8"
|
||||
version = "0.17.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
|
||||
checksum = "da5349ae27d3887ca812fb375b45a4fbb36d8d12d2df394968cd86e35683fe73"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom 0.2.15",
|
||||
"libc",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
@@ -2968,9 +3027,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.22"
|
||||
version = "0.23.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fb9263ab4eb695e42321db096e3b8fbd715a59b154d5c88d82db2175b681ba7"
|
||||
checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ring",
|
||||
@@ -3086,18 +3145,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.217"
|
||||
version = "1.0.218"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||
checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.217"
|
||||
version = "1.0.218"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3106,9 +3165,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.138"
|
||||
version = "1.0.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949"
|
||||
checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
@@ -3218,9 +3277,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -3503,9 +3562,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "string_cache_codegen"
|
||||
version = "0.5.3"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "244292f3441c89febe5b5bdfbb6863aeaf4f64da810ea3050fd927b27b8d92ce"
|
||||
checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0"
|
||||
dependencies = [
|
||||
"phf_generator 0.11.3",
|
||||
"phf_shared 0.11.3",
|
||||
@@ -3532,18 +3591,18 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.26.3"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||
checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.26.4"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||
checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -3558,6 +3617,202 @@ version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "symphonia"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "815c942ae7ee74737bb00f965fa5b5a2ac2ce7b6c01c0cc169bbeaf7abd5f5a9"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"symphonia-bundle-flac",
|
||||
"symphonia-bundle-mp3",
|
||||
"symphonia-codec-aac",
|
||||
"symphonia-codec-adpcm",
|
||||
"symphonia-codec-alac",
|
||||
"symphonia-codec-pcm",
|
||||
"symphonia-codec-vorbis",
|
||||
"symphonia-core",
|
||||
"symphonia-format-caf",
|
||||
"symphonia-format-isomp4",
|
||||
"symphonia-format-mkv",
|
||||
"symphonia-format-ogg",
|
||||
"symphonia-format-riff",
|
||||
"symphonia-metadata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-bundle-flac"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72e34f34298a7308d4397a6c7fbf5b84c5d491231ce3dd379707ba673ab3bd97"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
"symphonia-utils-xiph",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-bundle-mp3"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c01c2aae70f0f1fb096b6f0ff112a930b1fb3626178fba3ae68b09dce71706d4"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-codec-aac"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdbf25b545ad0d3ee3e891ea643ad115aff4ca92f6aec472086b957a58522f70"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
"symphonia-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-codec-adpcm"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c94e1feac3327cd616e973d5be69ad36b3945f16b06f19c6773fc3ac0b426a0f"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-codec-alac"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d8a6666649a08412906476a8b0efd9b9733e241180189e9f92b09c08d0e38f3"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-codec-pcm"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f395a67057c2ebc5e84d7bb1be71cce1a7ba99f64e0f0f0e303a03f79116f89b"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-codec-vorbis"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a98765fb46a0a6732b007f7e2870c2129b6f78d87db7987e6533c8f164a9f30"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-utils-xiph",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-core"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "798306779e3dc7d5231bd5691f5a813496dc79d3f56bf82e25789f2094e022c3"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags 1.3.2",
|
||||
"bytemuck",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"rustfft",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-format-caf"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e43c99c696a388295a29fe71b133079f5d8b18041cf734c5459c35ad9097af50"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-format-isomp4"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abfdf178d697e50ce1e5d9b982ba1b94c47218e03ec35022d9f0e071a16dc844"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
"symphonia-utils-xiph",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-format-mkv"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bb43471a100f7882dc9937395bd5ebee8329298e766250b15b3875652fe3d6f"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
"symphonia-utils-xiph",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-format-ogg"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ada3505789516bcf00fc1157c67729eded428b455c27ca370e41f4d785bfa931"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
"symphonia-utils-xiph",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-format-riff"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f7be232f962f937f4b7115cbe62c330929345434c834359425e043bfd15f50"
|
||||
dependencies = [
|
||||
"extended",
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-metadata"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc622b9841a10089c5b18e99eb904f4341615d5aa55bbf4eedde1be721a4023c"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"symphonia-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-utils-xiph"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "484472580fa49991afda5f6550ece662237b00c6f562c7d9638d1b086ed010fe"
|
||||
dependencies = [
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
@@ -3599,9 +3854,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.16.0"
|
||||
version = "3.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
|
||||
checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
@@ -3624,9 +3879,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "text-splitter"
|
||||
version = "0.22.0"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5cb76f2930deed7b89fd345fff5361813fb8feb7b6f0b80d26c4aba391819dd"
|
||||
checksum = "698b22fc8ce5bef13475143a43e87df82440e66b2a18d7655d1425dd36580a53"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"auto_enums",
|
||||
@@ -3912,9 +4167,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter"
|
||||
version = "0.25.1"
|
||||
version = "0.25.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a802c93485fb6781d27e27cb5927f6b00ff8d26b56c70af87267be7e99def97"
|
||||
checksum = "5168a515fe492af54c5cc8800ff8c840be09fa5168de45838afaecd3e008bce4"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"regex",
|
||||
@@ -3996,9 +4251,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-language"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38eee4db33814de3d004de9d8d825627ed3320d0989cce0dea30efaf5be4736c"
|
||||
checksum = "c4013970217383f67b18aef68f6fb2e8d409bc5755227092d32efb0422ba24b8"
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-python"
|
||||
@@ -4051,9 +4306,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
@@ -4069,9 +4324,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.16"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||
checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
@@ -4251,9 +4506,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.13.1"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0"
|
||||
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
|
||||
dependencies = [
|
||||
"getrandom 0.3.1",
|
||||
]
|
||||
@@ -4546,6 +4801,12 @@ dependencies = [
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.2"
|
||||
@@ -4724,9 +4985,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603"
|
||||
checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -4839,11 +5100,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.16"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b8c07a70861ce02bad1607b5753ecb2501f67847b9f9ada7c160fff0ec6300c"
|
||||
checksum = "dcf01143b2dd5d134f11f545cf9f1431b13b749695cb33bcce051e7568f99478"
|
||||
dependencies = [
|
||||
"zerocopy-derive 0.8.16",
|
||||
"zerocopy-derive 0.8.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4859,9 +5120,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.16"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5226bc9a9a9836e7428936cde76bb6b22feea1a8bfdbc0d241136e4d13417e25"
|
||||
checksum = "712c8386f4f4299382c9abee219bee7084f78fb939d88b6840fcc1320d5f6da2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4870,18 +5131,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
|
||||
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
|
||||
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
102
Cargo.toml
102
Cargo.toml
@@ -8,46 +8,72 @@ members = [
|
||||
"./packages/frontend/native/schema",
|
||||
"./packages/frontend/native/sqlite_v1",
|
||||
]
|
||||
resolver = "2"
|
||||
resolver = "3"
|
||||
|
||||
[workspace.package]
|
||||
edition = "2024"
|
||||
|
||||
[workspace.dependencies]
|
||||
affine_common = { path = "./packages/common/native" }
|
||||
affine_nbstore = { path = "./packages/frontend/native/nbstore" }
|
||||
anyhow = "1"
|
||||
base64-simd = "0.8"
|
||||
block2 = "0.6"
|
||||
chrono = "0.4"
|
||||
core-foundation = "0.10"
|
||||
coreaudio-rs = "0.12"
|
||||
criterion2 = { version = "2", default-features = false }
|
||||
dispatch2 = "0.2"
|
||||
dotenvy = "0.15"
|
||||
file-format = { version = "0.26", features = ["reader"] }
|
||||
homedir = "0.3"
|
||||
mimalloc = "0.1"
|
||||
napi = { version = "3.0.0-alpha.12", features = ["async", "chrono_date", "error_anyhow", "napi9", "serde"] }
|
||||
napi-build = { version = "2" }
|
||||
napi-derive = { version = "3.0.0-alpha.12" }
|
||||
notify = { version = "8", features = ["serde"] }
|
||||
objc2 = "0.6"
|
||||
objc2-foundation = "0.3"
|
||||
once_cell = "1"
|
||||
parking_lot = "0.12"
|
||||
rand = "0.9"
|
||||
rayon = "1.10"
|
||||
rubato = "0.16"
|
||||
screencapturekit = "0.3"
|
||||
serde = "1"
|
||||
serde_json = "1"
|
||||
sha3 = "0.10"
|
||||
sqlx = { version = "0.8", default-features = false, features = ["chrono", "macros", "migrate", "runtime-tokio", "sqlite", "tls-rustls"] }
|
||||
thiserror = "2"
|
||||
tiktoken-rs = "0.6"
|
||||
tokio = "1.37"
|
||||
uniffi = "0.29"
|
||||
uuid = "1.8"
|
||||
v_htmlescape = "0.15"
|
||||
y-octo = { git = "https://github.com/y-crdt/y-octo.git", branch = "main" }
|
||||
affine_common = { path = "./packages/common/native" }
|
||||
affine_nbstore = { path = "./packages/frontend/native/nbstore" }
|
||||
anyhow = "1"
|
||||
base64-simd = "0.8"
|
||||
block2 = "0.6"
|
||||
chrono = "0.4"
|
||||
core-foundation = "0.10"
|
||||
coreaudio-rs = "0.12"
|
||||
criterion2 = { version = "3", default-features = false }
|
||||
dispatch2 = "0.2"
|
||||
docx-parser = { git = "https://github.com/toeverything/docx-parser" }
|
||||
dotenvy = "0.15"
|
||||
file-format = { version = "0.26", features = ["reader"] }
|
||||
homedir = "0.3"
|
||||
infer = { version = "0.19.0" }
|
||||
libc = "0.2"
|
||||
mimalloc = "0.1"
|
||||
mp3lame-encoder = "0.2"
|
||||
napi = { version = "3.0.0-alpha.31", features = ["async", "chrono_date", "error_anyhow", "napi9", "serde"] }
|
||||
napi-build = { version = "2" }
|
||||
napi-derive = { version = "3.0.0-alpha.28" }
|
||||
notify = { version = "8", features = ["serde"] }
|
||||
objc2 = "0.6"
|
||||
objc2-foundation = "0.3"
|
||||
once_cell = "1"
|
||||
parking_lot = "0.12"
|
||||
path-ext = "0.1.1"
|
||||
pdf-extract = "0.8.2"
|
||||
rand = "0.9"
|
||||
rayon = "1.10"
|
||||
readability = { version = "0.3.0", default-features = false }
|
||||
rubato = "0.16"
|
||||
screencapturekit = "0.3"
|
||||
serde = "1"
|
||||
serde_json = "1"
|
||||
sha3 = "0.10"
|
||||
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.24"
|
||||
thiserror = "2"
|
||||
tiktoken-rs = "0.6"
|
||||
tokio = "1.37"
|
||||
tree-sitter = { version = "0.25" }
|
||||
tree-sitter-c = { version = "0.23" }
|
||||
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.23" }
|
||||
tree-sitter-scala = { version = "0.23" }
|
||||
tree-sitter-typescript = { version = "0.23" }
|
||||
uniffi = "0.29"
|
||||
url = { version = "2.5" }
|
||||
uuid = "1.8"
|
||||
v_htmlescape = "0.15"
|
||||
y-octo = { git = "https://github.com/y-crdt/y-octo.git", branch = "main" }
|
||||
|
||||
[profile.dev.package.sqlx-macros]
|
||||
opt-level = 3
|
||||
|
||||
@@ -160,6 +160,7 @@ We would also like to give thanks to open-source projects that make AFFiNE possi
|
||||
- [Jotai](https://github.com/pmndrs/jotai) - Primitive and flexible state management for React.
|
||||
- [async-call-rpc](https://github.com/Jack-Works/async-call-rpc) - A lightweight JSON RPC client & server.
|
||||
- [Vite](https://github.com/vitejs/vite) - Next generation frontend tooling.
|
||||
- [lame](https://lame.sourceforge.io/) - High quality MPEG Audio Layer III (MP3) encoder.
|
||||
- Other upstream [dependencies](https://github.com/toeverything/AFFiNE/network/dependencies).
|
||||
|
||||
Thanks a lot to the community for providing such powerful and simple libraries, so that we can focus more on the implementation of the product logic, and we hope that in the future our projects will also provide a more easy-to-use knowledge base for everyone.
|
||||
|
||||
@@ -30,7 +30,11 @@ import { AttachmentEmbedProvider } from './embed.js';
|
||||
import { styles } from './styles.js';
|
||||
import { checkAttachmentBlob, downloadAttachmentBlob } from './utils.js';
|
||||
|
||||
@Peekable()
|
||||
@Peekable({
|
||||
enableOn: ({ model }: AttachmentBlockComponent) => {
|
||||
return model.type.endsWith('pdf');
|
||||
},
|
||||
})
|
||||
export class AttachmentBlockComponent extends CaptionedBlockComponent<AttachmentBlockModel> {
|
||||
static override styles = styles;
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"@types/mdast": "^4.0.4",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"shiki": "^2.0.0",
|
||||
"shiki": "^3.0.0",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"exports": {
|
||||
|
||||
@@ -182,17 +182,17 @@ export class CodeBlockComponent extends CaptionedBlockComponent<
|
||||
// TODO: move to service for better performance
|
||||
this.bindHotKey({
|
||||
Backspace: ctx => {
|
||||
const state = ctx.get('keyboardState');
|
||||
const event = ctx.get('defaultState').event;
|
||||
const textSelection = selectionManager.find(TextSelection);
|
||||
if (!textSelection) {
|
||||
state.raw.preventDefault();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
const from = textSelection.from;
|
||||
|
||||
if (from.index === 0 && from.length === 0) {
|
||||
state.raw.preventDefault();
|
||||
event.preventDefault();
|
||||
selectionManager.setGroup('note', [
|
||||
selectionManager.create(BlockSelection, { blockId: this.blockId }),
|
||||
]);
|
||||
@@ -215,7 +215,7 @@ export class CodeBlockComponent extends CaptionedBlockComponent<
|
||||
index: index,
|
||||
length: 0,
|
||||
});
|
||||
state.raw.preventDefault();
|
||||
event.preventDefault();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
getMoreMenuConfig,
|
||||
} from '@blocksuite/affine-components/toolbar';
|
||||
import type { CodeBlockModel } from '@blocksuite/affine-model';
|
||||
import { PAGE_HEADER_HEIGHT } from '@blocksuite/affine-shared/consts';
|
||||
import {
|
||||
BlockSelection,
|
||||
TextSelection,
|
||||
@@ -92,7 +91,6 @@ export class AffineCodeToolbarWidget extends WidgetComponent<
|
||||
shift({
|
||||
crossAxis: true,
|
||||
padding: {
|
||||
top: PAGE_HEADER_HEIGHT + 12,
|
||||
bottom: 12,
|
||||
right: 12,
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { affineTextStyles } from '@blocksuite/affine-components/rich-text';
|
||||
import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
|
||||
import { ShadowlessElement } from '@blocksuite/block-std';
|
||||
import { type DeltaInsert, ZERO_WIDTH_SPACE } from '@blocksuite/inline';
|
||||
@@ -16,7 +17,25 @@ export class AffineCodeUnit extends ShadowlessElement {
|
||||
}
|
||||
|
||||
override render() {
|
||||
const plainContent = html`<span
|
||||
if (this.delta.attributes?.link && this.codeBlock) {
|
||||
return html`<affine-link
|
||||
.std=${this.codeBlock.std}
|
||||
.delta=${this.delta}
|
||||
></affine-link>`;
|
||||
}
|
||||
|
||||
let style = this.delta.attributes
|
||||
? affineTextStyles(this.delta.attributes)
|
||||
: {};
|
||||
if (this.delta.attributes?.code) {
|
||||
style = {
|
||||
...style,
|
||||
'font-size': 'calc(var(--affine-font-base) - 3px)',
|
||||
padding: '0px 4px 2px',
|
||||
};
|
||||
}
|
||||
|
||||
const plainContent = html`<span style=${styleMap(style)}
|
||||
><v-text .str=${this.delta.insert}></v-text
|
||||
></span>`;
|
||||
|
||||
@@ -53,12 +72,13 @@ export class AffineCodeUnit extends ShadowlessElement {
|
||||
endOffset - token.offset
|
||||
);
|
||||
|
||||
return html`<v-text
|
||||
.str=${content}
|
||||
return html`<span
|
||||
style=${styleMap({
|
||||
color: token.color,
|
||||
...style,
|
||||
})}
|
||||
></v-text>`;
|
||||
><v-text .str=${content}></v-text
|
||||
></span>`;
|
||||
} else {
|
||||
const firstToken = includedTokens[0];
|
||||
const lastToken = includedTokens[includedTokens.length - 1];
|
||||
@@ -79,6 +99,7 @@ export class AffineCodeUnit extends ShadowlessElement {
|
||||
.str=${token.content}
|
||||
style=${styleMap({
|
||||
color: token.color,
|
||||
...style,
|
||||
})}
|
||||
></v-text>`;
|
||||
});
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { css } from 'lit';
|
||||
|
||||
export const codeBlockStyles = css`
|
||||
affine-code {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.affine-code-block-container {
|
||||
font-size: var(--affine-font-xs);
|
||||
line-height: var(--affine-line-height);
|
||||
|
||||
@@ -5,7 +5,11 @@ import {
|
||||
type InsertToPosition,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import type { DataViewDataType } from '@blocksuite/data-view';
|
||||
import { BlockModel, defineBlockSchema } from '@blocksuite/store';
|
||||
import {
|
||||
BlockModel,
|
||||
BlockSchemaExtension,
|
||||
defineBlockSchema,
|
||||
} from '@blocksuite/store';
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
@@ -93,3 +97,6 @@ export const DataViewBlockSchema = defineBlockSchema({
|
||||
return new DataViewBlockModel();
|
||||
},
|
||||
});
|
||||
|
||||
export const DataViewBlockSchemaExtension =
|
||||
BlockSchemaExtension(DataViewBlockSchema);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { clamp } from '@blocksuite/affine-shared/utils';
|
||||
import {
|
||||
createPropertyConvert,
|
||||
getTagColor,
|
||||
@@ -6,6 +5,7 @@ import {
|
||||
} from '@blocksuite/data-view';
|
||||
import { presetPropertyConverts } from '@blocksuite/data-view/property-presets';
|
||||
import { propertyModelPresets } from '@blocksuite/data-view/property-pure-presets';
|
||||
import { clamp } from '@blocksuite/global/utils';
|
||||
import { nanoid, Text } from '@blocksuite/store';
|
||||
|
||||
import { richTextPropertyModelConfig } from './rich-text/define.js';
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vitest": "3.0.6"
|
||||
"vitest": "3.0.7"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
EMBED_CARD_WIDTH,
|
||||
} from '@blocksuite/affine-shared/consts';
|
||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { findAncestorModel } from '@blocksuite/affine-shared/utils';
|
||||
import type { BlockService } from '@blocksuite/block-std';
|
||||
import type { GfxCompatibleProps } from '@blocksuite/block-std/gfx';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
@@ -57,7 +58,15 @@ export class EmbedBlockComponent<
|
||||
) {
|
||||
this.style.display = 'block';
|
||||
|
||||
if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
|
||||
const insideNote = findAncestorModel(
|
||||
this.model,
|
||||
m => m.flavour === 'affine:note'
|
||||
);
|
||||
|
||||
if (
|
||||
!insideNote &&
|
||||
this.std.get(DocModeProvider).getEditorMode() === 'edgeless'
|
||||
) {
|
||||
this.style.minWidth = `${EMBED_CARD_MIN_WIDTH}px`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,11 +304,12 @@ export function getDocContentWithMaxLength(doc: Store, maxlength = 500) {
|
||||
|
||||
export function getTitleFromSelectedModels(selectedModels: DraftModel[]) {
|
||||
const firstBlock = selectedModels[0];
|
||||
if (
|
||||
matchModels(firstBlock, [ParagraphBlockModel]) &&
|
||||
firstBlock.type.startsWith('h')
|
||||
) {
|
||||
return firstBlock.text.toString();
|
||||
const isParagraph = (
|
||||
model: DraftModel
|
||||
): model is DraftModel<ParagraphBlockModel> =>
|
||||
model.flavour === 'affine:paragraph';
|
||||
if (isParagraph(firstBlock) && firstBlock.type.startsWith('h')) {
|
||||
return firstBlock.text?.toString();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -394,7 +395,7 @@ export async function convertSelectedBlocksToLinkedDoc(
|
||||
'before'
|
||||
);
|
||||
// delete selected elements
|
||||
models.forEach(model => doc.deleteBlock(model));
|
||||
models.forEach(model => doc.deleteBlock(model.id));
|
||||
return linkedDoc;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,13 +10,9 @@ import { classMap } from 'lit/directives/class-map.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { EmbedBlockComponent } from '../common/embed-block-element.js';
|
||||
import type { EmbedFigmaBlockService } from './embed-figma-service.js';
|
||||
import { FigmaIcon, styles } from './styles.js';
|
||||
|
||||
export class EmbedFigmaBlockComponent extends EmbedBlockComponent<
|
||||
EmbedFigmaModel,
|
||||
EmbedFigmaBlockService
|
||||
> {
|
||||
export class EmbedFigmaBlockComponent extends EmbedBlockComponent<EmbedFigmaModel> {
|
||||
static override styles = styles;
|
||||
|
||||
override _cardStyle: (typeof EmbedFigmaStyles)[number] = 'figma';
|
||||
|
||||
@@ -2,22 +2,13 @@ import {
|
||||
EmbedFigmaBlockSchema,
|
||||
EmbedFigmaStyles,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { EmbedOptionProvider } from '@blocksuite/affine-shared/services';
|
||||
import { BlockService } from '@blocksuite/block-std';
|
||||
import { EmbedOptionConfig } from '@blocksuite/affine-shared/services';
|
||||
|
||||
import { figmaUrlRegex } from './embed-figma-model.js';
|
||||
|
||||
export class EmbedFigmaBlockService extends BlockService {
|
||||
static override readonly flavour = EmbedFigmaBlockSchema.model.flavour;
|
||||
|
||||
override mounted() {
|
||||
super.mounted();
|
||||
|
||||
this.std.get(EmbedOptionProvider).registerEmbedBlockOptions({
|
||||
flavour: this.flavour,
|
||||
urlRegex: figmaUrlRegex,
|
||||
styles: EmbedFigmaStyles,
|
||||
viewType: 'embed',
|
||||
});
|
||||
}
|
||||
}
|
||||
export const EmbedFigmaBlockOptionConfig = EmbedOptionConfig({
|
||||
flavour: EmbedFigmaBlockSchema.model.flavour,
|
||||
urlRegex: figmaUrlRegex,
|
||||
styles: EmbedFigmaStyles,
|
||||
viewType: 'embed',
|
||||
});
|
||||
|
||||
@@ -3,15 +3,15 @@ import type { ExtensionType } from '@blocksuite/store';
|
||||
import { literal } from 'lit/static-html.js';
|
||||
|
||||
import { EmbedFigmaBlockAdapterExtensions } from './adapters/extension.js';
|
||||
import { EmbedFigmaBlockService } from './embed-figma-service.js';
|
||||
import { EmbedFigmaBlockOptionConfig } from './embed-figma-service.js';
|
||||
|
||||
export const EmbedFigmaBlockSpec: ExtensionType[] = [
|
||||
FlavourExtension('affine:embed-figma'),
|
||||
EmbedFigmaBlockService,
|
||||
BlockViewExtension('affine:embed-figma', model => {
|
||||
return model.parent?.flavour === 'affine:surface'
|
||||
? literal`affine-embed-edgeless-figma-block`
|
||||
: literal`affine-embed-figma-block`;
|
||||
}),
|
||||
EmbedFigmaBlockAdapterExtensions,
|
||||
EmbedFigmaBlockOptionConfig,
|
||||
].flat();
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
EmbedGithubStyles,
|
||||
} from '@blocksuite/affine-model';
|
||||
import {
|
||||
EmbedOptionProvider,
|
||||
EmbedOptionConfig,
|
||||
LinkPreviewerService,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { BlockService } from '@blocksuite/block-std';
|
||||
@@ -26,15 +26,11 @@ export class EmbedGithubBlockService extends BlockService {
|
||||
signal
|
||||
);
|
||||
};
|
||||
|
||||
override mounted() {
|
||||
super.mounted();
|
||||
|
||||
this.std.get(EmbedOptionProvider).registerEmbedBlockOptions({
|
||||
flavour: this.flavour,
|
||||
urlRegex: githubUrlRegex,
|
||||
styles: EmbedGithubStyles,
|
||||
viewType: 'card',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const EmbedGithubBlockOptionConfig = EmbedOptionConfig({
|
||||
flavour: EmbedGithubBlockSchema.model.flavour,
|
||||
urlRegex: githubUrlRegex,
|
||||
styles: EmbedGithubStyles,
|
||||
viewType: 'card',
|
||||
});
|
||||
|
||||
@@ -3,7 +3,10 @@ import type { ExtensionType } from '@blocksuite/store';
|
||||
import { literal } from 'lit/static-html.js';
|
||||
|
||||
import { EmbedGithubBlockAdapterExtensions } from './adapters/extension.js';
|
||||
import { EmbedGithubBlockService } from './embed-github-service.js';
|
||||
import {
|
||||
EmbedGithubBlockOptionConfig,
|
||||
EmbedGithubBlockService,
|
||||
} from './embed-github-service.js';
|
||||
|
||||
export const EmbedGithubBlockSpec: ExtensionType[] = [
|
||||
FlavourExtension('affine:embed-github'),
|
||||
@@ -14,4 +17,5 @@ export const EmbedGithubBlockSpec: ExtensionType[] = [
|
||||
: literal`affine-embed-github-block`;
|
||||
}),
|
||||
EmbedGithubBlockAdapterExtensions,
|
||||
EmbedGithubBlockOptionConfig,
|
||||
].flat();
|
||||
|
||||
@@ -2,6 +2,7 @@ import { css, html } from 'lit';
|
||||
|
||||
export const styles = css`
|
||||
.affine-embed-github-block {
|
||||
container: affine-embed-github-block / inline-size;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
@@ -24,6 +25,7 @@ export const styles = css`
|
||||
padding: 12px;
|
||||
border-radius: var(--1, 0px);
|
||||
opacity: var(--add, 1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.affine-embed-github-content-title {
|
||||
@@ -376,6 +378,15 @@ export const styles = css`
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@container affine-embed-github-block (width < 375px) {
|
||||
.affine-embed-github-content {
|
||||
width: 100%;
|
||||
}
|
||||
.affine-embed-github-banner {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GithubIcon = html`<svg
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
EMBED_CARD_HEIGHT,
|
||||
EMBED_CARD_WIDTH,
|
||||
} from '@blocksuite/affine-shared/consts';
|
||||
import { FeatureFlagService } from '@blocksuite/affine-shared/services';
|
||||
import {
|
||||
cloneReferenceInfoWithoutAliases,
|
||||
isNewTabTrigger,
|
||||
@@ -23,14 +22,6 @@ export class EmbedEdgelessLinkedDocBlockComponent extends toEdgelessEmbedBlock(
|
||||
override convertToEmbed = () => {
|
||||
const { id, doc, caption, xywh } = this.model;
|
||||
|
||||
// synced doc entry controlled by flag
|
||||
const isSyncedDocEnabled = doc
|
||||
.get(FeatureFlagService)
|
||||
.getFlag('enable_synced_doc_block');
|
||||
if (!isSyncedDocEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const style = 'syncedDoc';
|
||||
const bound = Bound.deserialize(xywh);
|
||||
bound.w = EMBED_CARD_WIDTH[style];
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
import {
|
||||
DocDisplayMetaProvider,
|
||||
DocModeProvider,
|
||||
FeatureFlagService,
|
||||
OpenDocExtensionIdentifier,
|
||||
type OpenDocMode,
|
||||
ThemeProvider,
|
||||
@@ -131,14 +130,6 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
|
||||
|
||||
const { doc, caption } = this.model;
|
||||
|
||||
// synced doc entry controlled by flag
|
||||
const isSyncedDocEnabled = doc
|
||||
.get(FeatureFlagService)
|
||||
.getFlag('enable_synced_doc_block');
|
||||
if (!isSyncedDocEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parent = doc.getParent(this.model);
|
||||
if (!parent) {
|
||||
return;
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
type EmbedLoomModel,
|
||||
EmbedLoomStyles,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { EmbedOptionProvider } from '@blocksuite/affine-shared/services';
|
||||
import { EmbedOptionConfig } from '@blocksuite/affine-shared/services';
|
||||
import { BlockService } from '@blocksuite/block-std';
|
||||
|
||||
import { loomUrlRegex } from './embed-loom-model.js';
|
||||
@@ -15,15 +15,11 @@ export class EmbedLoomBlockService extends BlockService {
|
||||
queryUrlData = (embedLoomModel: EmbedLoomModel, signal?: AbortSignal) => {
|
||||
return queryEmbedLoomData(embedLoomModel, signal);
|
||||
};
|
||||
|
||||
override mounted() {
|
||||
super.mounted();
|
||||
|
||||
this.std.get(EmbedOptionProvider).registerEmbedBlockOptions({
|
||||
flavour: this.flavour,
|
||||
urlRegex: loomUrlRegex,
|
||||
styles: EmbedLoomStyles,
|
||||
viewType: 'embed',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const EmbedLoomBlockOptionConfig = EmbedOptionConfig({
|
||||
flavour: EmbedLoomBlockSchema.model.flavour,
|
||||
urlRegex: loomUrlRegex,
|
||||
styles: EmbedLoomStyles,
|
||||
viewType: 'embed',
|
||||
});
|
||||
|
||||
@@ -3,7 +3,10 @@ import type { ExtensionType } from '@blocksuite/store';
|
||||
import { literal } from 'lit/static-html.js';
|
||||
|
||||
import { EmbedLoomBlockAdapterExtensions } from './adapters/extension.js';
|
||||
import { EmbedLoomBlockService } from './embed-loom-service.js';
|
||||
import {
|
||||
EmbedLoomBlockOptionConfig,
|
||||
EmbedLoomBlockService,
|
||||
} from './embed-loom-service.js';
|
||||
|
||||
export const EmbedLoomBlockSpec: ExtensionType[] = [
|
||||
FlavourExtension('affine:embed-loom'),
|
||||
@@ -14,4 +17,5 @@ export const EmbedLoomBlockSpec: ExtensionType[] = [
|
||||
: literal`affine-embed-loom-block`;
|
||||
}),
|
||||
EmbedLoomBlockAdapterExtensions,
|
||||
EmbedLoomBlockOptionConfig,
|
||||
].flat();
|
||||
|
||||
@@ -26,9 +26,9 @@ import {
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import {
|
||||
BlockSelection,
|
||||
BlockServiceWatcher,
|
||||
BlockStdScope,
|
||||
type EditorHost,
|
||||
LifeCycleWatcher,
|
||||
} from '@blocksuite/block-std';
|
||||
import {
|
||||
GfxControllerIdentifier,
|
||||
@@ -124,27 +124,31 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
|
||||
this.std.getOptional(EditorSettingProvider) ??
|
||||
signal(GeneralSettingSchema.parse({}));
|
||||
|
||||
class EmbedSyncedDocWatcher extends BlockServiceWatcher {
|
||||
static override readonly flavour = 'affine:embed-synced-doc';
|
||||
class EmbedSyncedDocWatcher extends LifeCycleWatcher {
|
||||
static override key = 'embed-synced-doc-watcher';
|
||||
|
||||
override mounted() {
|
||||
const disposableGroup = this.blockService.disposables;
|
||||
const slots = this.blockService.specSlots;
|
||||
disposableGroup.add(
|
||||
slots.viewConnected.on(({ component }) => {
|
||||
const nextComponent = component as EmbedSyncedDocBlockComponent;
|
||||
override mounted(): void {
|
||||
const { view } = this.std;
|
||||
view.viewUpdated.on(payload => {
|
||||
if (
|
||||
payload.type !== 'block' ||
|
||||
payload.view.model.flavour !== 'affine:embed-synced-doc'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const nextComponent = payload.view as EmbedSyncedDocBlockComponent;
|
||||
if (payload.method === 'add') {
|
||||
nextComponent.depth = nextDepth;
|
||||
currentDisposables.add(() => {
|
||||
nextComponent.depth = 0;
|
||||
});
|
||||
})
|
||||
);
|
||||
disposableGroup.add(
|
||||
slots.viewDisconnected.on(({ component }) => {
|
||||
const nextComponent = component as EmbedSyncedDocBlockComponent;
|
||||
return;
|
||||
}
|
||||
if (payload.method === 'delete') {
|
||||
nextComponent.depth = 0;
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,6 +235,7 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
|
||||
[theme]: true,
|
||||
surface: false,
|
||||
selected: this.selected$.value,
|
||||
'show-hover-border': true,
|
||||
})}
|
||||
@click=${this._handleClick}
|
||||
style=${containerStyleMap}
|
||||
|
||||
@@ -57,10 +57,13 @@ export const blockStyles = css`
|
||||
}
|
||||
|
||||
.affine-embed-synced-doc-container {
|
||||
border: 1px solid var(--affine-border-color);
|
||||
border: 1px solid transparent;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.affine-embed-synced-doc-container.show-hover-border:hover {
|
||||
border-color: var(--affine-border-color);
|
||||
}
|
||||
.affine-embed-synced-doc-container.page {
|
||||
display: block;
|
||||
width: 100%;
|
||||
@@ -151,7 +154,12 @@ export const blockStyles = css`
|
||||
}
|
||||
|
||||
.affine-embed-synced-doc-container.surface {
|
||||
border-color: var(--affine-border-color);
|
||||
background: var(--affine-background-primary-color);
|
||||
|
||||
affine-preview-root {
|
||||
padding: 0 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.affine-embed-synced-doc-container
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
EmbedYoutubeStyles,
|
||||
} from '@blocksuite/affine-model';
|
||||
import {
|
||||
EmbedOptionProvider,
|
||||
EmbedOptionConfig,
|
||||
LinkPreviewerService,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { BlockService } from '@blocksuite/block-std';
|
||||
@@ -25,15 +25,11 @@ export class EmbedYoutubeBlockService extends BlockService {
|
||||
signal
|
||||
);
|
||||
};
|
||||
|
||||
override mounted() {
|
||||
super.mounted();
|
||||
|
||||
this.std.get(EmbedOptionProvider).registerEmbedBlockOptions({
|
||||
flavour: this.flavour,
|
||||
urlRegex: youtubeUrlRegex,
|
||||
styles: EmbedYoutubeStyles,
|
||||
viewType: 'embed',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const EmbedYoutubeBlockOptionConfig = EmbedOptionConfig({
|
||||
flavour: EmbedYoutubeBlockSchema.model.flavour,
|
||||
urlRegex: youtubeUrlRegex,
|
||||
styles: EmbedYoutubeStyles,
|
||||
viewType: 'embed',
|
||||
});
|
||||
|
||||
@@ -3,7 +3,10 @@ import type { ExtensionType } from '@blocksuite/store';
|
||||
import { literal } from 'lit/static-html.js';
|
||||
|
||||
import { EmbedYoutubeBlockAdapterExtensions } from './adapters/extension.js';
|
||||
import { EmbedYoutubeBlockService } from './embed-youtube-service.js';
|
||||
import {
|
||||
EmbedYoutubeBlockOptionConfig,
|
||||
EmbedYoutubeBlockService,
|
||||
} from './embed-youtube-service.js';
|
||||
|
||||
export const EmbedYoutubeBlockSpec: ExtensionType[] = [
|
||||
FlavourExtension('affine:embed-youtube'),
|
||||
@@ -14,4 +17,5 @@ export const EmbedYoutubeBlockSpec: ExtensionType[] = [
|
||||
: literal`affine-embed-youtube-block`;
|
||||
}),
|
||||
EmbedYoutubeBlockAdapterExtensions,
|
||||
EmbedYoutubeBlockOptionConfig,
|
||||
].flat();
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vitest": "3.0.6"
|
||||
"vitest": "3.0.7"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
|
||||
@@ -105,7 +105,7 @@ export const ListKeymapExtension = KeymapExtension(
|
||||
const isStart = isCollapsed && text.from.index === 0;
|
||||
if (!isStart) return false;
|
||||
|
||||
ctx.get('keyboardState').raw.preventDefault();
|
||||
ctx.get('defaultState').event.preventDefault();
|
||||
std.command
|
||||
.chain()
|
||||
.pipe(listToParagraphCommand, {
|
||||
|
||||
@@ -4,14 +4,23 @@ import {
|
||||
TextSelection,
|
||||
} from '@blocksuite/block-std';
|
||||
|
||||
/**
|
||||
* Focus the end of the block
|
||||
* @param focusBlock - The block to focus
|
||||
* @param force - If set to true, the selection will be cleared.
|
||||
* It is useful when the selection is same.
|
||||
*/
|
||||
export const focusBlockEnd: Command<{
|
||||
focusBlock?: BlockComponent;
|
||||
force?: boolean;
|
||||
}> = (ctx, next) => {
|
||||
const { focusBlock, std } = ctx;
|
||||
const { focusBlock, force, std } = ctx;
|
||||
if (!focusBlock || !focusBlock.model.text) return;
|
||||
|
||||
const { selection } = std;
|
||||
|
||||
if (force) selection.clear();
|
||||
|
||||
selection.setGroup('note', [
|
||||
selection.create(TextSelection, {
|
||||
from: {
|
||||
|
||||
@@ -2,10 +2,11 @@ import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
|
||||
import type { DocTitle } from '@blocksuite/affine-components/doc-title';
|
||||
import { NoteDisplayMode } from '@blocksuite/affine-model';
|
||||
import { EDGELESS_BLOCK_CHILD_PADDING } from '@blocksuite/affine-shared/consts';
|
||||
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
|
||||
import { stopPropagation } from '@blocksuite/affine-shared/utils';
|
||||
import { toGfxBlockComponent } from '@blocksuite/block-std';
|
||||
import { Bound } from '@blocksuite/global/utils';
|
||||
import { html, nothing } from 'lit';
|
||||
import { html, nothing, type PropertyValues } from 'lit';
|
||||
import { query, state } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
@@ -173,6 +174,15 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
||||
}
|
||||
}
|
||||
|
||||
override updated(changedProperties: PropertyValues) {
|
||||
if (changedProperties.has('_editing') && this._editing) {
|
||||
this.std.getOptional(TelemetryProvider)?.track('EdgelessNoteEditing', {
|
||||
page: 'edgeless',
|
||||
segment: this.model.isPageBlock() ? 'page' : 'note',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
override getRenderingRect() {
|
||||
const { xywh, edgeless } = this.model;
|
||||
const { collapse, scale = 1 } = edgeless;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
getSelectedModelsCommand,
|
||||
} from '@blocksuite/affine-shared/commands';
|
||||
import type { BlockStdScope } from '@blocksuite/block-std';
|
||||
import { toDraftModel } from '@blocksuite/store';
|
||||
|
||||
export interface QuickActionConfig {
|
||||
id: string;
|
||||
@@ -45,7 +46,9 @@ export const quickActionConfig: QuickActionConfig[] = [
|
||||
std.selection.clear();
|
||||
|
||||
const doc = std.store;
|
||||
const autofill = getTitleFromSelectedModels(selectedModels);
|
||||
const autofill = getTitleFromSelectedModels(
|
||||
selectedModels.map(toDraftModel)
|
||||
);
|
||||
promptDocTitle(std, autofill)
|
||||
.then(title => {
|
||||
if (title === null) return;
|
||||
|
||||
@@ -42,7 +42,7 @@ export const ParagraphKeymapExtension = KeymapExtension(
|
||||
const model = store.getBlock(text.from.blockId)?.model;
|
||||
if (!model || !matchModels(model, [ParagraphBlockModel])) return;
|
||||
|
||||
const event = ctx.get('keyboardState').raw;
|
||||
const event = ctx.get('defaultState').event;
|
||||
event.preventDefault();
|
||||
|
||||
// When deleting at line start of a paragraph block,
|
||||
|
||||
@@ -64,7 +64,6 @@ import {
|
||||
BlockSnapshotSchema,
|
||||
fromJSON,
|
||||
type SliceSnapshot,
|
||||
Transformer,
|
||||
} from '@blocksuite/store';
|
||||
import DOMPurify from 'dompurify';
|
||||
import * as Y from 'yjs';
|
||||
@@ -373,15 +372,7 @@ export class EdgelessClipboardController extends PageClipboard {
|
||||
if (mayBeSurfaceDataJson !== undefined) {
|
||||
const elementsRawData = JSON.parse(mayBeSurfaceDataJson);
|
||||
const { snapshot, blobs } = elementsRawData;
|
||||
const job = new Transformer({
|
||||
schema: this.std.workspace.schema,
|
||||
blobCRUD: this.std.workspace.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => this.std.workspace.createDoc({ id }),
|
||||
get: (id: string) => this.std.workspace.getDoc(id),
|
||||
delete: (id: string) => this.std.workspace.removeDoc(id),
|
||||
},
|
||||
});
|
||||
const job = this.std.store.getTransformer();
|
||||
const map = job.assetsManager.getAssets();
|
||||
decodeClipboardBlobs(blobs, map);
|
||||
for (const blobId of map.keys()) {
|
||||
@@ -1377,15 +1368,7 @@ export async function prepareClipboardData(
|
||||
selectedAll: GfxModel[],
|
||||
std: BlockStdScope
|
||||
) {
|
||||
const job = new Transformer({
|
||||
schema: std.workspace.schema,
|
||||
blobCRUD: std.workspace.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => std.workspace.createDoc({ id }),
|
||||
get: (id: string) => std.workspace.getDoc(id),
|
||||
delete: (id: string) => std.workspace.removeDoc(id),
|
||||
},
|
||||
});
|
||||
const job = std.store.getTransformer();
|
||||
const selected = await Promise.all(
|
||||
selectedAll.map(async selected => {
|
||||
const data = serializeElement(selected, selectedAll, job);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { FrameBlockModel } from '@blocksuite/affine-model';
|
||||
import { SpecProvider } from '@blocksuite/affine-shared/utils';
|
||||
import {
|
||||
BlockServiceWatcher,
|
||||
BlockStdScope,
|
||||
type EditorHost,
|
||||
LifeCycleWatcher,
|
||||
ShadowlessElement,
|
||||
} from '@blocksuite/block-std';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||
@@ -117,22 +117,26 @@ export class FramePreview extends WithDisposable(ShadowlessElement) {
|
||||
|
||||
private _initSpec() {
|
||||
const refreshViewport = this._refreshViewport.bind(this);
|
||||
class FramePreviewWatcher extends BlockServiceWatcher {
|
||||
static override readonly flavour = 'affine:page';
|
||||
class FramePreviewWatcher extends LifeCycleWatcher {
|
||||
static override key = 'frame-preview-watcher';
|
||||
|
||||
override mounted() {
|
||||
const blockService = this.blockService;
|
||||
blockService.disposables.add(
|
||||
blockService.specSlots.viewConnected.on(({ component }) => {
|
||||
const edgelessBlock =
|
||||
component as EdgelessRootPreviewBlockComponent;
|
||||
|
||||
edgelessBlock.editorViewportSelector = 'frame-preview-viewport';
|
||||
edgelessBlock.service.viewport.sizeUpdated.once(() => {
|
||||
refreshViewport();
|
||||
});
|
||||
})
|
||||
);
|
||||
const { view } = this.std;
|
||||
view.viewUpdated.on(payload => {
|
||||
if (
|
||||
payload.type !== 'block' ||
|
||||
payload.method !== 'add' ||
|
||||
payload.view.model.flavour !== 'affine:page'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const edgelessBlock =
|
||||
payload.view as EdgelessRootPreviewBlockComponent;
|
||||
edgelessBlock.editorViewportSelector = 'frame-preview-viewport';
|
||||
edgelessBlock.service.viewport.sizeUpdated.once(() => {
|
||||
refreshViewport();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
this._previewSpec.extend([FramePreviewWatcher]);
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import type { Color, ColorScheme, Palette } from '@blocksuite/affine-model';
|
||||
import { isTransparent, resolveColor } from '@blocksuite/affine-model';
|
||||
import {
|
||||
DefaultTheme,
|
||||
isTransparent,
|
||||
resolveColor,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||
import { ColorEvent } from '@blocksuite/affine-shared/utils';
|
||||
import { css, html, LitElement, nothing, svg, type TemplateResult } from 'lit';
|
||||
@@ -253,7 +257,7 @@ export class EdgelessColorPanel extends LitElement {
|
||||
accessor openColorPicker!: (e: MouseEvent) => void;
|
||||
|
||||
@property({ type: Array })
|
||||
accessor palettes: readonly Palette[] = [];
|
||||
accessor palettes: readonly Palette[] = DefaultTheme.Palettes;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor theme!: ColorScheme;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { LINE_WIDTHS, LineWidth } from '@blocksuite/affine-model';
|
||||
import { clamp, on, once } from '@blocksuite/affine-shared/utils';
|
||||
import { on, once } from '@blocksuite/affine-shared/utils';
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import { css, html, LitElement, nothing, type PropertyValues } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import clamp from 'lodash-es/clamp';
|
||||
|
||||
interface Config {
|
||||
width: number;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { clamp, stopPropagation } from '@blocksuite/affine-shared/utils';
|
||||
import { stopPropagation } from '@blocksuite/affine-shared/utils';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import clamp from 'lodash-es/clamp';
|
||||
|
||||
const MIN_SCALE = 0;
|
||||
const MAX_SCALE = 400;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { clamp, stopPropagation } from '@blocksuite/affine-shared/utils';
|
||||
import { stopPropagation } from '@blocksuite/affine-shared/utils';
|
||||
import { DoneIcon } from '@blocksuite/icons/lit';
|
||||
import { css, html, LitElement, nothing } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import clamp from 'lodash-es/clamp';
|
||||
|
||||
const MIN_SIZE = 1;
|
||||
const MAX_SIZE = 200;
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import {
|
||||
type ColorScheme,
|
||||
DefaultTheme,
|
||||
type StrokeStyle,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { type ColorScheme, type StrokeStyle } from '@blocksuite/affine-model';
|
||||
import type { ColorEvent } from '@blocksuite/affine-shared/utils';
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
@@ -44,7 +40,6 @@ export class StrokeStylePanel extends WithDisposable(LitElement) {
|
||||
aria-label="Border colors"
|
||||
.value=${this.strokeColor}
|
||||
.theme=${this.theme}
|
||||
.palettes=${DefaultTheme.Palettes}
|
||||
.hollowCircle=${this.hollowCircle}
|
||||
@select=${(e: ColorEvent) => this.setStrokeColor(e)}
|
||||
>
|
||||
|
||||
@@ -35,7 +35,6 @@ import {
|
||||
import { EMBED_CARD_HEIGHT } from '@blocksuite/affine-shared/consts';
|
||||
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||
import {
|
||||
clamp,
|
||||
getElementsWithoutGroup,
|
||||
getSelectedRect,
|
||||
requestThrottledConnectedFrame,
|
||||
@@ -68,6 +67,7 @@ import { css, html, nothing } from 'lit';
|
||||
import { state } from 'lit/decorators.js';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import clamp from 'lodash-es/clamp';
|
||||
|
||||
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
|
||||
import {
|
||||
|
||||
@@ -65,7 +65,7 @@ export class EdgelessBrushMenu extends EdgelessToolbarToolMixin(
|
||||
class="one-way"
|
||||
.value=${this._props$.value.color}
|
||||
.theme=${this._theme$.value}
|
||||
.palettes=${DefaultTheme.StrokeColorPalettes}
|
||||
.palettes=${DefaultTheme.StrokeColorShortPalettes}
|
||||
.hasTransparent=${!this.edgeless.doc
|
||||
.get(FeatureFlagService)
|
||||
.getFlag('enable_color_picker')}
|
||||
|
||||
@@ -133,7 +133,7 @@ export class EdgelessConnectorMenu extends EdgelessToolbarToolMixin(
|
||||
class="one-way"
|
||||
.value=${stroke}
|
||||
.theme=${this._theme$.value}
|
||||
.palettes=${DefaultTheme.StrokeColorPalettes}
|
||||
.palettes=${DefaultTheme.StrokeColorShortPalettes}
|
||||
.hasTransparent=${!this.edgeless.doc
|
||||
.get(FeatureFlagService)
|
||||
.getFlag('enable_color_picker')}
|
||||
|
||||
@@ -75,9 +75,10 @@ export class EdgelessShapeMenu extends SignalWatcher(
|
||||
const filled = !isTransparent(value);
|
||||
const fillColor = value;
|
||||
const strokeColor = filled
|
||||
? DefaultTheme.StrokeColorPalettes.find(palette => palette.key === key)
|
||||
?.value
|
||||
: DefaultTheme.StrokeColorMap.Grey;
|
||||
? DefaultTheme.StrokeColorShortPalettes.find(
|
||||
palette => palette.key === key
|
||||
)?.value
|
||||
: DefaultTheme.StrokeColorShortMap.Grey;
|
||||
|
||||
const { shapeName } = this._props$.value;
|
||||
this.edgeless.std
|
||||
@@ -173,7 +174,7 @@ export class EdgelessShapeMenu extends SignalWatcher(
|
||||
class="one-way"
|
||||
.value=${fillColor}
|
||||
.theme=${this._theme$.value}
|
||||
.palettes=${DefaultTheme.FillColorPalettes}
|
||||
.palettes=${DefaultTheme.FillColorShortPalettes}
|
||||
.hasTransparent=${!this.edgeless.doc
|
||||
.get(FeatureFlagService)
|
||||
.getFlag('enable_color_picker')}
|
||||
|
||||
@@ -33,7 +33,7 @@ export class EdgelessTextMenu extends EdgelessToolbarToolMixin(LitElement) {
|
||||
class="one-way"
|
||||
.value=${this.color}
|
||||
.theme=${this._theme$.value}
|
||||
.palettes=${DefaultTheme.StrokeColorPalettes}
|
||||
.palettes=${DefaultTheme.StrokeColorShortPalettes}
|
||||
@select=${(e: ColorEvent) => this.onChange({ color: e.detail })}
|
||||
></edgeless-color-panel>
|
||||
</div>
|
||||
|
||||
@@ -21,7 +21,7 @@ import { ShapeTool } from './gfx-tool/shape-tool.js';
|
||||
import { TemplateTool } from './gfx-tool/template-tool.js';
|
||||
import { TextTool } from './gfx-tool/text-tool.js';
|
||||
import { EditPropsMiddlewareBuilder } from './middlewares/base.js';
|
||||
import { EdgelessSnapManager } from './utils/snap-manager.js';
|
||||
import { SnapManager } from './utils/snap-manager.js';
|
||||
|
||||
export const EdgelessToolExtension: ExtensionType[] = [
|
||||
DefaultTool,
|
||||
@@ -43,7 +43,7 @@ export const EdgelessBuiltInManager: ExtensionType[] = [
|
||||
ConnectionOverlay,
|
||||
FrameOverlay,
|
||||
MindMapIndicatorOverlay,
|
||||
EdgelessSnapManager,
|
||||
SnapManager,
|
||||
EdgelessFrameManager,
|
||||
EditPropsMiddlewareBuilder,
|
||||
];
|
||||
|
||||
@@ -447,7 +447,7 @@ export class EdgelessRootBlockComponent extends BlockComponent<
|
||||
);
|
||||
|
||||
const zoom = normalizeWheelDeltaY(e.deltaY, viewport.zoom);
|
||||
viewport.setZoom(zoom, new Point(baseX, baseY));
|
||||
viewport.setZoom(zoom, new Point(baseX, baseY), true);
|
||||
e.stopPropagation();
|
||||
}
|
||||
// pan
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
MindmapElementModel,
|
||||
RootBlockSchema,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { clamp } from '@blocksuite/affine-shared/utils';
|
||||
import type { BlockStdScope } from '@blocksuite/block-std';
|
||||
import type {
|
||||
GfxController,
|
||||
@@ -35,6 +34,7 @@ import {
|
||||
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
|
||||
import { Bound, getCommonBound } from '@blocksuite/global/utils';
|
||||
import { effect } from '@preact/signals-core';
|
||||
import clamp from 'lodash-es/clamp';
|
||||
|
||||
import { RootService } from '../root-service.js';
|
||||
import { TemplateJob } from './services/template.js';
|
||||
|
||||
@@ -2,11 +2,14 @@ import { autoConnectWidget } from '@blocksuite/affine-widget-edgeless-auto-conne
|
||||
import { frameTitleWidget } from '@blocksuite/affine-widget-frame-title';
|
||||
import { edgelessRemoteSelectionWidget } from '@blocksuite/affine-widget-remote-selection';
|
||||
import {
|
||||
BlockServiceWatcher,
|
||||
BlockViewExtension,
|
||||
LifeCycleWatcher,
|
||||
WidgetViewExtension,
|
||||
} from '@blocksuite/block-std';
|
||||
import { ToolController } from '@blocksuite/block-std/gfx';
|
||||
import {
|
||||
GfxControllerIdentifier,
|
||||
ToolController,
|
||||
} from '@blocksuite/block-std/gfx';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
import { literal, unsafeStatic } from 'lit/static-html.js';
|
||||
|
||||
@@ -56,17 +59,12 @@ export const edgelessToolbarWidget = WidgetViewExtension(
|
||||
literal`${unsafeStatic(EDGELESS_TOOLBAR_WIDGET)}`
|
||||
);
|
||||
|
||||
class EdgelessLocker extends BlockServiceWatcher {
|
||||
static override readonly flavour = 'affine:page';
|
||||
class EdgelessLocker extends LifeCycleWatcher {
|
||||
static override key = 'edgeless-locker';
|
||||
|
||||
override mounted() {
|
||||
const service = this.blockService;
|
||||
service.disposables.add(
|
||||
service.specSlots.viewConnected.on(({ service }) => {
|
||||
// Does not allow the user to move and zoom.
|
||||
(service as EdgelessRootService).locked = true;
|
||||
})
|
||||
);
|
||||
const { viewport } = this.std.get(GfxControllerIdentifier);
|
||||
viewport.locked = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import {
|
||||
TelemetryProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import {
|
||||
clamp,
|
||||
handleNativeRangeAtPoint,
|
||||
resetNativeSelection,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
@@ -52,13 +51,14 @@ import {
|
||||
Vec,
|
||||
} from '@blocksuite/global/utils';
|
||||
import { effect } from '@preact/signals-core';
|
||||
import clamp from 'lodash-es/clamp';
|
||||
|
||||
import type { EdgelessRootBlockComponent } from '../edgeless-root-block.js';
|
||||
import { prepareCloneData } from '../utils/clone-utils.js';
|
||||
import { isSingleMindMapNode } from '../utils/mindmap.js';
|
||||
import { calPanDelta } from '../utils/panning-utils.js';
|
||||
import { isCanvasElement, isEdgelessTextBlock } from '../utils/query.js';
|
||||
import type { EdgelessSnapManager } from '../utils/snap-manager.js';
|
||||
import type { SnapManager } from '../utils/snap-manager.js';
|
||||
import {
|
||||
addText,
|
||||
mountConnectorLabelEditor,
|
||||
@@ -277,9 +277,7 @@ export class DefaultTool extends BaseTool {
|
||||
}
|
||||
|
||||
get snapOverlay() {
|
||||
return this.std.get(
|
||||
OverlayIdentifier('snap-manager')
|
||||
) as EdgelessSnapManager;
|
||||
return this.std.get(OverlayIdentifier('snap-manager')) as SnapManager;
|
||||
}
|
||||
|
||||
private _addEmptyParagraphBlock(
|
||||
@@ -580,7 +578,7 @@ export class DefaultTool extends BaseTool {
|
||||
) {
|
||||
const mindmap = this._toBeMoved[0].group as MindmapElementModel;
|
||||
|
||||
this._alignBound = this.snapOverlay.setupAlignables(this._toBeMoved, [
|
||||
this._alignBound = this.snapOverlay.setMovingElements(this._toBeMoved, [
|
||||
mindmap,
|
||||
...(mindmap?.childElements || []),
|
||||
]);
|
||||
@@ -640,7 +638,7 @@ export class DefaultTool extends BaseTool {
|
||||
);
|
||||
}
|
||||
|
||||
this._alignBound = this.snapOverlay.setupAlignables(
|
||||
this._alignBound = this.snapOverlay.setMovingElements(
|
||||
this._toBeMoved
|
||||
);
|
||||
|
||||
@@ -882,7 +880,7 @@ export class DefaultTool extends BaseTool {
|
||||
if (this.edgelessSelectionManager.editing) return;
|
||||
|
||||
this._selectedBounds = [];
|
||||
this.snapOverlay.cleanupAlignables();
|
||||
this.snapOverlay.clear();
|
||||
this.frameOverlay.clear();
|
||||
this._toBeMoved = [];
|
||||
this._selectedConnector = null;
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
type DocSnapshot,
|
||||
DocSnapshotSchema,
|
||||
type SnapshotNode,
|
||||
Transformer,
|
||||
type Transformer,
|
||||
} from '@blocksuite/store';
|
||||
import type * as Y from 'yjs';
|
||||
/**
|
||||
@@ -90,16 +90,7 @@ export class TemplateJob {
|
||||
type: TemplateType;
|
||||
|
||||
constructor({ model, type, middlewares }: TemplateJobConfig) {
|
||||
this.job = new Transformer({
|
||||
schema: model.doc.workspace.schema,
|
||||
blobCRUD: model.doc.workspace.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => model.doc.workspace.createDoc({ id }),
|
||||
get: (id: string) => model.doc.workspace.getDoc(id),
|
||||
delete: (id: string) => model.doc.workspace.removeDoc(id),
|
||||
},
|
||||
middlewares: [],
|
||||
});
|
||||
this.job = model.doc.getTransformer();
|
||||
this.model = model;
|
||||
this.type = TEMPLATE_TYPES.includes(type as TemplateType)
|
||||
? (type as TemplateType)
|
||||
@@ -320,8 +311,7 @@ export class TemplateJob {
|
||||
from: Record<string, Record<string, unknown>>,
|
||||
to: Y.Map<Y.Map<unknown>>
|
||||
) {
|
||||
const schema =
|
||||
this.model.doc.workspace.schema.flavourSchemaMap.get('affine:surface');
|
||||
const schema = this.model.doc.schema.get('affine:surface');
|
||||
const surfaceTransformer = schema?.transformer?.(
|
||||
new Map()
|
||||
) as SurfaceBlockTransformer;
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
isGfxGroupCompatibleModel,
|
||||
type SerializedElement,
|
||||
} from '@blocksuite/block-std/gfx';
|
||||
import { type BlockSnapshot, Transformer } from '@blocksuite/store';
|
||||
import type { BlockSnapshot, Transformer } from '@blocksuite/store';
|
||||
|
||||
/**
|
||||
* return all elements in the tree of the elements
|
||||
@@ -40,15 +40,7 @@ export function getSortedCloneElements(elements: GfxModel[]) {
|
||||
|
||||
export function prepareCloneData(elements: GfxModel[], std: BlockStdScope) {
|
||||
elements = sortEdgelessElements(elements);
|
||||
const job = new Transformer({
|
||||
schema: std.workspace.schema,
|
||||
blobCRUD: std.workspace.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => std.workspace.createDoc({ id }),
|
||||
get: (id: string) => std.workspace.getDoc(id),
|
||||
delete: (id: string) => std.workspace.removeDoc(id),
|
||||
},
|
||||
});
|
||||
const job = std.store.getTransformer();
|
||||
const res = elements.map(element => {
|
||||
const data = serializeElement(element, elements, job);
|
||||
return data;
|
||||
|
||||
@@ -1,30 +1,49 @@
|
||||
import type {
|
||||
SurfaceBlockComponent,
|
||||
SurfaceBlockModel,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import { getSurfaceBlock, Overlay } from '@blocksuite/affine-block-surface';
|
||||
import type { ConnectorElementModel } from '@blocksuite/affine-model';
|
||||
import type { GfxController, GfxModel } from '@blocksuite/block-std/gfx';
|
||||
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
|
||||
import { Bound, Point } from '@blocksuite/global/utils';
|
||||
|
||||
import { isConnectable } from '../utils/query.js';
|
||||
import { Overlay } from '@blocksuite/affine-block-surface';
|
||||
import { ConnectorElementModel } from '@blocksuite/affine-model';
|
||||
import type { GfxModel } from '@blocksuite/block-std/gfx';
|
||||
import { almostEqual, Bound, Point } from '@blocksuite/global/utils';
|
||||
|
||||
interface Distance {
|
||||
absXDistance: number;
|
||||
absYDistance: number;
|
||||
xDistance: number;
|
||||
yDistance: number;
|
||||
indexX: number;
|
||||
indexY: number;
|
||||
horiz?: {
|
||||
/**
|
||||
* the minimum x moving distance to align with other bound
|
||||
*/
|
||||
distance: number;
|
||||
|
||||
/**
|
||||
* the indices of the align position
|
||||
*/
|
||||
alignPositionIndices: number[];
|
||||
};
|
||||
|
||||
vert?: {
|
||||
/**
|
||||
* the minimum y moving distance to align with other bound
|
||||
*/
|
||||
distance: number;
|
||||
|
||||
/**
|
||||
* the indices of the align position
|
||||
*/
|
||||
alignPositionIndices: number[];
|
||||
};
|
||||
}
|
||||
|
||||
const ALIGN_THRESHOLD = 5;
|
||||
const ALIGN_THRESHOLD = 8;
|
||||
const DISTRIBUTION_LINE_OFFSET = 1;
|
||||
|
||||
export class EdgelessSnapManager extends Overlay {
|
||||
export class SnapManager extends Overlay {
|
||||
static override overlayName: string = 'snap-manager';
|
||||
|
||||
private _alignableBounds: Bound[] = [];
|
||||
private _referenceBounds: {
|
||||
vertical: Bound[];
|
||||
horizontal: Bound[];
|
||||
all: Bound[];
|
||||
} = {
|
||||
vertical: [],
|
||||
horizontal: [],
|
||||
all: [],
|
||||
};
|
||||
|
||||
/**
|
||||
* This variable contains reference lines that are
|
||||
@@ -44,28 +63,16 @@ export class EdgelessSnapManager extends Overlay {
|
||||
*/
|
||||
private _intraGraphicAlignLines: [Point, Point][] = [];
|
||||
|
||||
cleanupAlignables = () => {
|
||||
this._alignableBounds = [];
|
||||
override clear() {
|
||||
super.clear();
|
||||
|
||||
this._referenceBounds = {
|
||||
vertical: [],
|
||||
horizontal: [],
|
||||
all: [],
|
||||
};
|
||||
this._intraGraphicAlignLines = [];
|
||||
this._distributedAlignLines = [];
|
||||
// FIXME: not sure why renderer can be undefined sometimes
|
||||
this._surface.renderer?.removeOverlay(this);
|
||||
};
|
||||
|
||||
private get _surface() {
|
||||
const surfaceModel = getSurfaceBlock(this.gfx.doc);
|
||||
if (!surfaceModel) {
|
||||
throw new BlockSuiteError(
|
||||
ErrorCode.ValueNotExists,
|
||||
'Surface block not found in doc when creating snap manager'
|
||||
);
|
||||
}
|
||||
|
||||
return this.gfx.std.view.getBlock(surfaceModel.id) as SurfaceBlockComponent;
|
||||
}
|
||||
|
||||
constructor(gfx: GfxController) {
|
||||
super(gfx);
|
||||
}
|
||||
|
||||
private _alignDistributeHorizontally(
|
||||
@@ -75,31 +82,52 @@ export class EdgelessSnapManager extends Overlay {
|
||||
viewport: { zoom: number }
|
||||
) {
|
||||
const wBoxes: Bound[] = [];
|
||||
this._alignableBounds.forEach(box => {
|
||||
this._referenceBounds.horizontal.forEach(box => {
|
||||
if (box.isHorizontalCross(bound)) {
|
||||
wBoxes.push(box);
|
||||
}
|
||||
});
|
||||
|
||||
wBoxes.sort((a, b) => a.center[0] - b.center[0]);
|
||||
|
||||
let dif = Infinity;
|
||||
let min = Infinity;
|
||||
let aveDis = Number.MAX_SAFE_INTEGER;
|
||||
let curBound!: {
|
||||
leftIdx: number;
|
||||
rightIdx: number;
|
||||
spacing: number;
|
||||
points: [Point, Point][];
|
||||
};
|
||||
for (let i = 0; i < wBoxes.length; i++) {
|
||||
for (let j = i + 1; j < wBoxes.length; j++) {
|
||||
let lb = wBoxes[i],
|
||||
rb = wBoxes[j];
|
||||
// it means these bound need to be horizontally across
|
||||
if (!lb.isHorizontalCross(rb)) continue;
|
||||
if (lb.isIntersectWithBound(rb)) continue;
|
||||
if (!lb.isHorizontalCross(rb) || lb.isIntersectWithBound(rb)) continue;
|
||||
|
||||
let switchFlag = false;
|
||||
// exchange lb and rb to make sure lb is on the left of rb
|
||||
if (rb.maxX < lb.minX) {
|
||||
const temp = rb;
|
||||
rb = lb;
|
||||
lb = temp;
|
||||
switchFlag = true;
|
||||
}
|
||||
/** align middle */
|
||||
|
||||
let _centerX = 0;
|
||||
const updateDif = () => {
|
||||
dif = Math.abs(bound.center[0] - _centerX);
|
||||
if (dif <= threshold && dif < min) {
|
||||
const curAveDis =
|
||||
(Math.abs(lb.center[0] - bound.center[0]) +
|
||||
Math.abs(rb.center[0] - bound.center[0])) /
|
||||
2;
|
||||
if (
|
||||
dif <= threshold &&
|
||||
(dif < min || (almostEqual(dif, min) && curAveDis < aveDis))
|
||||
) {
|
||||
min = dif;
|
||||
aveDis = curAveDis;
|
||||
rst.dx = _centerX - bound.center[0];
|
||||
/**
|
||||
* calculate points to draw
|
||||
@@ -108,37 +136,102 @@ export class EdgelessSnapManager extends Overlay {
|
||||
(a, b) => a - b
|
||||
);
|
||||
const y = (ys[1] + ys[2]) / 2;
|
||||
const offset = 2 / viewport.zoom;
|
||||
const offset = DISTRIBUTION_LINE_OFFSET / viewport.zoom;
|
||||
const xs = [
|
||||
_centerX - bound.w / 2 - offset,
|
||||
_centerX + bound.w / 2 + offset,
|
||||
_centerX - bound.w / 2,
|
||||
_centerX + bound.w / 2,
|
||||
rb.minX,
|
||||
rb.maxX,
|
||||
lb.minX,
|
||||
lb.maxX,
|
||||
].sort((a, b) => a - b);
|
||||
this._distributedAlignLines[0] = [
|
||||
new Point(xs[1], y),
|
||||
new Point(xs[2], y),
|
||||
];
|
||||
this._distributedAlignLines[1] = [
|
||||
new Point(xs[3], y),
|
||||
new Point(xs[4], y),
|
||||
];
|
||||
|
||||
curBound = {
|
||||
leftIdx: switchFlag ? j : i,
|
||||
rightIdx: switchFlag ? i : j,
|
||||
spacing: xs[2] - xs[1],
|
||||
points: [
|
||||
[new Point(xs[1] + offset, y), new Point(xs[2] - offset, y)],
|
||||
[new Point(xs[3] + offset, y), new Point(xs[4] - offset, y)],
|
||||
],
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* align between left and right bound
|
||||
*/
|
||||
if (lb.horizontalDistance(rb) > bound.w) {
|
||||
_centerX = (lb.maxX + rb.minX) / 2;
|
||||
updateDif();
|
||||
}
|
||||
/** align left */
|
||||
|
||||
/**
|
||||
* align to the left bounds
|
||||
*/
|
||||
_centerX = lb.minX - (rb.minX - lb.maxX) - bound.w / 2;
|
||||
updateDif();
|
||||
|
||||
/** align right */
|
||||
_centerX = rb.minX - lb.maxX + rb.maxX + bound.w / 2;
|
||||
updateDif();
|
||||
}
|
||||
}
|
||||
|
||||
// find the boxes that has same spacing
|
||||
if (curBound) {
|
||||
const { leftIdx, rightIdx, spacing, points } = curBound;
|
||||
|
||||
this._distributedAlignLines.push(...points);
|
||||
|
||||
{
|
||||
let curLeftBound = wBoxes[leftIdx];
|
||||
|
||||
for (let i = leftIdx - 1; i >= 0; i--) {
|
||||
if (almostEqual(wBoxes[i].maxX, curLeftBound.minX - spacing)) {
|
||||
const targetBound = wBoxes[i];
|
||||
const ys = [
|
||||
targetBound.minY,
|
||||
targetBound.maxY,
|
||||
curLeftBound.minY,
|
||||
curLeftBound.maxY,
|
||||
].sort((a, b) => a - b);
|
||||
const y = (ys[1] + ys[2]) / 2;
|
||||
|
||||
this._distributedAlignLines.push([
|
||||
new Point(wBoxes[i].maxX, y),
|
||||
new Point(curLeftBound.minX, y),
|
||||
]);
|
||||
|
||||
curLeftBound = wBoxes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let curRightBound = wBoxes[rightIdx];
|
||||
|
||||
for (let i = rightIdx + 1; i < wBoxes.length; i++) {
|
||||
if (almostEqual(wBoxes[i].minX, curRightBound.maxX + spacing)) {
|
||||
const targetBound = wBoxes[i];
|
||||
const ys = [
|
||||
targetBound.minY,
|
||||
targetBound.maxY,
|
||||
curRightBound.minY,
|
||||
curRightBound.maxY,
|
||||
].sort((a, b) => a - b);
|
||||
const y = (ys[1] + ys[2]) / 2;
|
||||
|
||||
this._distributedAlignLines.push([
|
||||
new Point(curRightBound.maxX, y),
|
||||
new Point(wBoxes[i].minX, y),
|
||||
]);
|
||||
|
||||
curRightBound = wBoxes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _alignDistributeVertically(
|
||||
@@ -148,29 +241,50 @@ export class EdgelessSnapManager extends Overlay {
|
||||
viewport: { zoom: number }
|
||||
) {
|
||||
const hBoxes: Bound[] = [];
|
||||
this._alignableBounds.forEach(box => {
|
||||
this._referenceBounds.vertical.forEach(box => {
|
||||
if (box.isVerticalCross(bound)) {
|
||||
hBoxes.push(box);
|
||||
}
|
||||
});
|
||||
|
||||
hBoxes.sort((a, b) => a.center[0] - b.center[0]);
|
||||
|
||||
let dif = Infinity;
|
||||
let min = Infinity;
|
||||
let aveDis = Number.MAX_SAFE_INTEGER;
|
||||
let curBound!: {
|
||||
upperIdx: number;
|
||||
lowerIdx: number;
|
||||
spacing: number;
|
||||
points: [Point, Point][];
|
||||
};
|
||||
for (let i = 0; i < hBoxes.length; i++) {
|
||||
for (let j = i + 1; j < hBoxes.length; j++) {
|
||||
let ub = hBoxes[i],
|
||||
db = hBoxes[j];
|
||||
if (!ub.isVerticalCross(db)) continue;
|
||||
if (ub.isIntersectWithBound(db)) continue;
|
||||
if (!ub.isVerticalCross(db) || ub.isIntersectWithBound(db)) continue;
|
||||
|
||||
let switchFlag = false;
|
||||
if (db.maxY < ub.minX) {
|
||||
const temp = ub;
|
||||
ub = db;
|
||||
db = temp;
|
||||
switchFlag = true;
|
||||
}
|
||||
|
||||
/** align middle */
|
||||
let _centerY = 0;
|
||||
const updateDiff = () => {
|
||||
dif = Math.abs(bound.center[1] - _centerY);
|
||||
if (dif <= threshold && dif < min) {
|
||||
const curAveDis =
|
||||
(Math.abs(ub.center[1] - bound.center[1]) +
|
||||
Math.abs(db.center[1] - bound.center[1])) /
|
||||
2;
|
||||
|
||||
if (
|
||||
dif <= threshold &&
|
||||
(dif < min || (almostEqual(dif, min) && curAveDis < aveDis))
|
||||
) {
|
||||
min = dif;
|
||||
rst.dy = _centerY - bound.center[1];
|
||||
/**
|
||||
@@ -180,29 +294,33 @@ export class EdgelessSnapManager extends Overlay {
|
||||
(a, b) => a - b
|
||||
);
|
||||
const x = (xs[1] + xs[2]) / 2;
|
||||
const offset = 2 / viewport.zoom;
|
||||
const offset = DISTRIBUTION_LINE_OFFSET / viewport.zoom;
|
||||
const ys = [
|
||||
_centerY - bound.h / 2 - offset,
|
||||
_centerY + bound.h / 2 + offset,
|
||||
_centerY - bound.h / 2,
|
||||
_centerY + bound.h / 2,
|
||||
db.minY,
|
||||
db.maxY,
|
||||
ub.minY,
|
||||
ub.maxY,
|
||||
].sort((a, b) => a - b);
|
||||
this._distributedAlignLines[3] = [
|
||||
new Point(x, ys[1]),
|
||||
new Point(x, ys[2]),
|
||||
];
|
||||
this._distributedAlignLines[4] = [
|
||||
new Point(x, ys[3]),
|
||||
new Point(x, ys[4]),
|
||||
];
|
||||
|
||||
curBound = {
|
||||
upperIdx: switchFlag ? j : i,
|
||||
lowerIdx: switchFlag ? i : j,
|
||||
spacing: ys[2] - ys[1],
|
||||
points: [
|
||||
[new Point(x, ys[1] + offset), new Point(x, ys[2] - offset)],
|
||||
[new Point(x, ys[3] + offset), new Point(x, ys[4] - offset)],
|
||||
],
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
if (ub.verticalDistance(db) > bound.h) {
|
||||
_centerY = (ub.maxY + db.minY) / 2;
|
||||
updateDiff();
|
||||
}
|
||||
|
||||
/** align upper */
|
||||
_centerY = ub.minY - (db.minY - ub.maxY) - bound.h / 2;
|
||||
updateDiff();
|
||||
@@ -211,6 +329,61 @@ export class EdgelessSnapManager extends Overlay {
|
||||
updateDiff();
|
||||
}
|
||||
}
|
||||
|
||||
// find the boxes that has same spacing
|
||||
if (curBound) {
|
||||
const { upperIdx, lowerIdx, spacing, points } = curBound;
|
||||
|
||||
this._distributedAlignLines.push(...points);
|
||||
|
||||
{
|
||||
let curUpperBound = hBoxes[upperIdx];
|
||||
|
||||
for (let i = upperIdx - 1; i >= 0; i--) {
|
||||
if (almostEqual(hBoxes[i].maxY, curUpperBound.minY - spacing)) {
|
||||
const targetBound = hBoxes[i];
|
||||
const xs = [
|
||||
targetBound.minX,
|
||||
targetBound.maxX,
|
||||
curUpperBound.minX,
|
||||
curUpperBound.maxX,
|
||||
].sort((a, b) => a - b);
|
||||
const x = (xs[1] + xs[2]) / 2;
|
||||
|
||||
this._distributedAlignLines.push([
|
||||
new Point(x, hBoxes[i].maxY),
|
||||
new Point(x, curUpperBound.minY),
|
||||
]);
|
||||
|
||||
curUpperBound = hBoxes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let curLowerBound = hBoxes[lowerIdx];
|
||||
|
||||
for (let i = lowerIdx + 1; i < hBoxes.length; i++) {
|
||||
if (almostEqual(hBoxes[i].minY, curLowerBound.maxY + spacing)) {
|
||||
const targetBound = hBoxes[i];
|
||||
const xs = [
|
||||
targetBound.minX,
|
||||
targetBound.maxX,
|
||||
curLowerBound.minX,
|
||||
curLowerBound.maxX,
|
||||
].sort((a, b) => a - b);
|
||||
const x = (xs[1] + xs[2]) / 2;
|
||||
|
||||
this._distributedAlignLines.push([
|
||||
new Point(x, curLowerBound.maxY),
|
||||
new Point(x, hBoxes[i].minY),
|
||||
]);
|
||||
|
||||
curLowerBound = hBoxes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _calculateClosestDistances(bound: Bound, other: Bound): Distance {
|
||||
@@ -263,94 +436,150 @@ export class EdgelessSnapManager extends Overlay {
|
||||
const closestX = Math.min(...xDistancesAbs);
|
||||
const closestY = Math.min(...yDistancesAbs);
|
||||
|
||||
const indexX = xDistancesAbs.indexOf(closestX);
|
||||
const indexY = yDistancesAbs.indexOf(closestY);
|
||||
const threshold = ALIGN_THRESHOLD / this.gfx.viewport.zoom;
|
||||
|
||||
// the x and y distances will be useful for locating the align point
|
||||
return {
|
||||
absXDistance: closestX,
|
||||
absYDistance: closestY,
|
||||
xDistance: xDistances[indexX],
|
||||
yDistance: yDistances[indexY],
|
||||
indexX,
|
||||
indexY,
|
||||
horiz:
|
||||
closestX <= threshold
|
||||
? {
|
||||
distance: xDistances[xDistancesAbs.indexOf(closestX)],
|
||||
get alignPositionIndices() {
|
||||
const indices: number[] = [];
|
||||
xDistancesAbs.forEach(
|
||||
(val, idx) => almostEqual(val, closestX) && indices.push(idx)
|
||||
);
|
||||
return indices;
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
vert:
|
||||
closestY <= threshold
|
||||
? {
|
||||
distance: yDistances[yDistancesAbs.indexOf(closestY)],
|
||||
get alignPositionIndices() {
|
||||
const indices: number[] = [];
|
||||
yDistancesAbs.forEach(
|
||||
(val, idx) => almostEqual(val, closestY) && indices.push(idx)
|
||||
);
|
||||
return indices;
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
private _draw() {
|
||||
this._surface.refresh();
|
||||
}
|
||||
|
||||
// Update X align point
|
||||
/**
|
||||
* Update horizontal moving distance `rst.dx` to align with other bound.
|
||||
* Also, update the align points to draw.
|
||||
* @param rst
|
||||
* @param bound
|
||||
* @param other
|
||||
* @param distance
|
||||
*/
|
||||
private _updateXAlignPoint(
|
||||
rst: { dx: number; dy: number },
|
||||
bound: Bound,
|
||||
other: Bound,
|
||||
distance: Distance
|
||||
) {
|
||||
const index = distance.indexX;
|
||||
rst.dx = distance.xDistance;
|
||||
const alignPointX = [
|
||||
if (!distance.horiz) return;
|
||||
|
||||
const { distance: dx, alignPositionIndices: distanceIndices } =
|
||||
distance.horiz;
|
||||
const alignXPosition = [
|
||||
other.center[0],
|
||||
other.minX,
|
||||
other.maxX,
|
||||
bound.minX + rst.dx,
|
||||
bound.minX + rst.dx,
|
||||
bound.maxX + rst.dx,
|
||||
bound.maxX + rst.dx,
|
||||
][index];
|
||||
this._intraGraphicAlignLines[0] = [
|
||||
new Point(alignPointX, bound.center[1]),
|
||||
new Point(alignPointX, other.center[1]),
|
||||
bound.minX + dx,
|
||||
bound.minX + dx,
|
||||
bound.maxX + dx,
|
||||
bound.maxX + dx,
|
||||
];
|
||||
|
||||
rst.dx = dx;
|
||||
|
||||
const dy = distance.vert?.distance ?? 0;
|
||||
const top = Math.min(bound.minY + dy, other.minY);
|
||||
const down = Math.max(bound.maxY + dy, other.maxY);
|
||||
|
||||
this._intraGraphicAlignLines.push(
|
||||
...distanceIndices.map(
|
||||
idx =>
|
||||
[
|
||||
new Point(alignXPosition[idx], top),
|
||||
new Point(alignXPosition[idx], down),
|
||||
] as [Point, Point]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Update Y align point
|
||||
/**
|
||||
* Update vertical moving distance `rst.dy` to align with other bound.
|
||||
* Also, update the align points to draw.
|
||||
* @param rst
|
||||
* @param bound
|
||||
* @param other
|
||||
* @param distance
|
||||
*/
|
||||
private _updateYAlignPoint(
|
||||
rst: { dx: number; dy: number },
|
||||
bound: Bound,
|
||||
other: Bound,
|
||||
distance: Distance
|
||||
) {
|
||||
const index = distance.indexY;
|
||||
rst.dy = distance.yDistance;
|
||||
const alignPointY = [
|
||||
if (!distance.vert) return;
|
||||
|
||||
const { distance: dy, alignPositionIndices } = distance.vert;
|
||||
const alignXPosition = [
|
||||
other.center[1],
|
||||
other.minY,
|
||||
other.maxY,
|
||||
bound.minY + rst.dy,
|
||||
bound.minY + rst.dy,
|
||||
bound.maxY + rst.dy,
|
||||
bound.maxY + rst.dy,
|
||||
][index];
|
||||
this._intraGraphicAlignLines[1] = [
|
||||
new Point(bound.center[0], alignPointY),
|
||||
new Point(other.center[0], alignPointY),
|
||||
bound.minY + dy,
|
||||
bound.minY + dy,
|
||||
bound.maxY + dy,
|
||||
bound.maxY + dy,
|
||||
];
|
||||
|
||||
rst.dy = dy;
|
||||
|
||||
const dx = distance.horiz?.distance ?? 0;
|
||||
const left = Math.min(bound.minX + dx, other.minX);
|
||||
const right = Math.max(bound.maxX + dx, other.maxX);
|
||||
|
||||
this._intraGraphicAlignLines.push(
|
||||
...alignPositionIndices.map(
|
||||
idx =>
|
||||
[
|
||||
new Point(left, alignXPosition[idx]),
|
||||
new Point(right, alignXPosition[idx]),
|
||||
] as [Point, Point]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
align(bound: Bound): { dx: number; dy: number } {
|
||||
const rst = { dx: 0, dy: 0 };
|
||||
const threshold = ALIGN_THRESHOLD;
|
||||
const threshold = ALIGN_THRESHOLD / this.gfx.viewport.zoom;
|
||||
|
||||
const { viewport } = this.gfx;
|
||||
|
||||
this._intraGraphicAlignLines = [];
|
||||
this._distributedAlignLines = [];
|
||||
|
||||
for (const other of this._alignableBounds) {
|
||||
for (const other of this._referenceBounds.all) {
|
||||
const closestDistances = this._calculateClosestDistances(bound, other);
|
||||
|
||||
if (closestDistances.absXDistance < threshold) {
|
||||
if (closestDistances.horiz) {
|
||||
this._updateXAlignPoint(rst, bound, other, closestDistances);
|
||||
}
|
||||
|
||||
if (closestDistances.absYDistance < threshold) {
|
||||
if (closestDistances.vert) {
|
||||
this._updateYAlignPoint(rst, bound, other, closestDistances);
|
||||
}
|
||||
}
|
||||
|
||||
// point align prority is higher than distribute align
|
||||
// point align priority is higher than distribute align
|
||||
if (rst.dx === 0) {
|
||||
this._alignDistributeHorizontally(rst, bound, threshold, viewport);
|
||||
}
|
||||
@@ -358,7 +587,9 @@ export class EdgelessSnapManager extends Overlay {
|
||||
if (rst.dy === 0) {
|
||||
this._alignDistributeVertically(rst, bound, threshold, viewport);
|
||||
}
|
||||
this._draw();
|
||||
|
||||
this._renderer?.refresh();
|
||||
|
||||
return rst;
|
||||
}
|
||||
|
||||
@@ -369,9 +600,9 @@ export class EdgelessSnapManager extends Overlay {
|
||||
)
|
||||
return;
|
||||
const { viewport } = this.gfx;
|
||||
const strokeWidth = 1 / viewport.zoom;
|
||||
const offset = 5 / viewport.zoom;
|
||||
ctx.strokeStyle = '#1672F3';
|
||||
const strokeWidth = 2 / viewport.zoom;
|
||||
|
||||
ctx.strokeStyle = '#8B5CF6';
|
||||
ctx.lineWidth = strokeWidth;
|
||||
ctx.beginPath();
|
||||
|
||||
@@ -381,30 +612,31 @@ export class EdgelessSnapManager extends Overlay {
|
||||
const x = line[0].x;
|
||||
const minY = Math.min(line[0].y, line[1].y);
|
||||
const maxY = Math.max(line[0].y, line[1].y);
|
||||
d = `M${x},${minY - offset}L${x},${maxY}`;
|
||||
d = `M${x},${minY}L${x},${maxY}`;
|
||||
} else {
|
||||
const y = line[0].y;
|
||||
const minX = Math.min(line[0].x, line[1].x);
|
||||
const maxX = Math.max(line[0].x, line[1].x);
|
||||
d = `M${minX - offset},${y}L${maxX + offset},${y}`;
|
||||
d = `M${minX},${y}L${maxX},${y}`;
|
||||
}
|
||||
ctx.stroke(new Path2D(d));
|
||||
});
|
||||
|
||||
ctx.strokeStyle = '#CC4187';
|
||||
this._distributedAlignLines.forEach(line => {
|
||||
const bar = 10 / viewport.zoom;
|
||||
let d = '';
|
||||
if (line[0].x === line[1].x) {
|
||||
const x = line[0].x;
|
||||
const minY = Math.min(line[0].y, line[1].y) + offset;
|
||||
const maxY = Math.max(line[0].y, line[1].y) - offset;
|
||||
const minY = Math.min(line[0].y, line[1].y);
|
||||
const maxY = Math.max(line[0].y, line[1].y);
|
||||
d = `M${x},${minY}L${x},${maxY}
|
||||
M${x - bar},${minY}L${x + bar},${minY}
|
||||
M${x - bar},${maxY}L${x + bar},${maxY} `;
|
||||
} else {
|
||||
const y = line[0].y;
|
||||
const minX = Math.min(line[0].x, line[1].x) + offset;
|
||||
const maxX = Math.max(line[0].x, line[1].x) - offset;
|
||||
const minX = Math.min(line[0].x, line[1].x);
|
||||
const maxX = Math.max(line[0].x, line[1].x);
|
||||
d = `M${minX},${y}L${maxX},${y}
|
||||
M${minX},${y - bar}L${minX},${y + bar}
|
||||
M${maxX},${y - bar}L${maxX},${y + bar}`;
|
||||
@@ -413,42 +645,68 @@ export class EdgelessSnapManager extends Overlay {
|
||||
});
|
||||
}
|
||||
|
||||
setupAlignables(alignables: GfxModel[], exclude: GfxModel[] = []): Bound {
|
||||
if (alignables.length === 0) return new Bound();
|
||||
setMovingElements(
|
||||
movingElements: GfxModel[],
|
||||
excludes: GfxModel[] = []
|
||||
): Bound {
|
||||
if (movingElements.length === 0) return new Bound();
|
||||
|
||||
const connectors = alignables.filter(isConnectable).reduce((prev, el) => {
|
||||
const connectors = (this.gfx.surface as SurfaceBlockModel).getConnectors(
|
||||
el.id
|
||||
);
|
||||
const skipped = new Set(movingElements);
|
||||
excludes.forEach(e => skipped.add(e));
|
||||
|
||||
if (connectors.length > 0) {
|
||||
prev = prev.concat(connectors);
|
||||
}
|
||||
|
||||
return prev;
|
||||
}, [] as ConnectorElementModel[]);
|
||||
|
||||
const { viewport } = this.gfx;
|
||||
const viewportBounds = Bound.from(viewport.viewportBounds);
|
||||
this._surface.renderer.addOverlay(this);
|
||||
const canvasElements = this.gfx.layer.canvasElements;
|
||||
const excludes = new Set([...alignables, ...exclude, ...connectors]);
|
||||
this._alignableBounds = [];
|
||||
([...this.gfx.layer.blocks, ...canvasElements] as GfxModel[]).forEach(
|
||||
alignable => {
|
||||
const bounds = alignable.elementBound;
|
||||
if (
|
||||
viewportBounds.isOverlapWithBound(bounds) &&
|
||||
!excludes.has(alignable)
|
||||
) {
|
||||
this._alignableBounds.push(bounds);
|
||||
}
|
||||
}
|
||||
const viewportBound = this.gfx.viewport.viewportBounds;
|
||||
const movingBound = movingElements
|
||||
.reduce(
|
||||
(prev, element) => prev.unite(element.elementBound),
|
||||
movingElements[0].elementBound
|
||||
)
|
||||
.expand(ALIGN_THRESHOLD * this.gfx.viewport.zoom);
|
||||
const horizAreaBound = new Bound(
|
||||
Math.min(movingBound.x, viewportBound.x),
|
||||
movingBound.y,
|
||||
Math.max(movingBound.w, viewportBound.w),
|
||||
movingBound.h
|
||||
);
|
||||
const vertAreaBound = new Bound(
|
||||
movingBound.x,
|
||||
Math.min(movingBound.y, viewportBound.y),
|
||||
movingBound.w,
|
||||
Math.max(movingBound.h, viewportBound.h)
|
||||
);
|
||||
|
||||
return alignables.reduce((prev, element) => {
|
||||
const bounds = element.elementBound;
|
||||
return prev.unite(bounds);
|
||||
}, Bound.deserialize(alignables[0].xywh));
|
||||
const vertCandidates = this.gfx.grid.search(vertAreaBound, {
|
||||
useSet: true,
|
||||
});
|
||||
const horizCandidates = this.gfx.grid.search(horizAreaBound, {
|
||||
useSet: true,
|
||||
});
|
||||
const verticalBounds: Bound[] = [];
|
||||
const horizBounds: Bound[] = [];
|
||||
const allBounds: Bound[] = [];
|
||||
|
||||
vertCandidates.forEach(candidate => {
|
||||
if (skipped.has(candidate) || candidate instanceof ConnectorElementModel)
|
||||
return;
|
||||
verticalBounds.push(candidate.elementBound);
|
||||
allBounds.push(candidate.elementBound);
|
||||
});
|
||||
|
||||
horizCandidates.forEach(candidate => {
|
||||
if (skipped.has(candidate) || candidate instanceof ConnectorElementModel)
|
||||
return;
|
||||
horizBounds.push(candidate.elementBound);
|
||||
allBounds.push(candidate.elementBound);
|
||||
});
|
||||
|
||||
this._referenceBounds = {
|
||||
horizontal: horizBounds,
|
||||
vertical: verticalBounds,
|
||||
all: allBounds,
|
||||
};
|
||||
|
||||
return movingElements.reduce(
|
||||
(prev, element) => prev.unite(element.elementBound),
|
||||
Bound.deserialize(movingElements[0].xywh)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,11 @@ import {
|
||||
type UIEventHandler,
|
||||
} from '@blocksuite/block-std';
|
||||
import { IS_MAC, IS_WINDOWS } from '@blocksuite/global/env';
|
||||
import { toDraftModel } from '@blocksuite/store';
|
||||
|
||||
export class PageKeyboardManager {
|
||||
private readonly _handleDelete: UIEventHandler = ctx => {
|
||||
const event = ctx.get('keyboardState').raw;
|
||||
const event = ctx.get('defaultState').event;
|
||||
const blockSelections = this._currentSelection.filter(sel =>
|
||||
sel.is(BlockSelection)
|
||||
);
|
||||
@@ -143,7 +144,9 @@ export class PageKeyboardManager {
|
||||
}
|
||||
|
||||
const doc = rootComponent.host.doc;
|
||||
const autofill = getTitleFromSelectedModels(selectedModels);
|
||||
const autofill = getTitleFromSelectedModels(
|
||||
selectedModels.map(toDraftModel)
|
||||
);
|
||||
promptDocTitle(rootComponent.std, autofill)
|
||||
.then(title => {
|
||||
if (title === null) return;
|
||||
|
||||
@@ -10,7 +10,6 @@ export class PreviewRootBlockComponent extends BlockComponent {
|
||||
static override styles = css`
|
||||
affine-preview-root {
|
||||
display: block;
|
||||
padding: 0 24px;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -8,19 +8,21 @@ import {
|
||||
import { SpecProvider } from '@blocksuite/affine-shared/utils';
|
||||
import { Container } from '@blocksuite/global/di';
|
||||
import { sha } from '@blocksuite/global/utils';
|
||||
import type { Store, Workspace } from '@blocksuite/store';
|
||||
import type { Schema, Store, Workspace } from '@blocksuite/store';
|
||||
import { extMimeMap, Transformer } from '@blocksuite/store';
|
||||
|
||||
import { createAssetsArchive, download, Unzip } from './utils.js';
|
||||
|
||||
type ImportHTMLToDocOptions = {
|
||||
collection: Workspace;
|
||||
schema: Schema;
|
||||
html: string;
|
||||
fileName?: string;
|
||||
};
|
||||
|
||||
type ImportHTMLZipOptions = {
|
||||
collection: Workspace;
|
||||
schema: Schema;
|
||||
imported: Blob;
|
||||
};
|
||||
|
||||
@@ -41,19 +43,10 @@ function getProvider() {
|
||||
*/
|
||||
async function exportDoc(doc: Store) {
|
||||
const provider = getProvider();
|
||||
const job = new Transformer({
|
||||
schema: doc.schema,
|
||||
blobCRUD: doc.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => doc.workspace.createDoc({ id }),
|
||||
get: (id: string) => doc.workspace.getDoc(id),
|
||||
delete: (id: string) => doc.workspace.removeDoc(id),
|
||||
},
|
||||
middlewares: [
|
||||
docLinkBaseURLMiddleware(doc.workspace.id),
|
||||
titleMiddleware(doc.workspace.meta.docMetas),
|
||||
],
|
||||
});
|
||||
const job = doc.getTransformer([
|
||||
docLinkBaseURLMiddleware(doc.workspace.id),
|
||||
titleMiddleware(doc.workspace.meta.docMetas),
|
||||
]);
|
||||
const snapshot = job.docToSnapshot(doc);
|
||||
const adapter = new HtmlAdapter(job, provider);
|
||||
if (!snapshot) {
|
||||
@@ -87,18 +80,20 @@ async function exportDoc(doc: Store) {
|
||||
*
|
||||
* @param options - The import options.
|
||||
* @param options.collection - The target doc collection.
|
||||
* @param options.schema - The schema of the target doc collection.
|
||||
* @param options.html - The HTML content to import.
|
||||
* @param options.fileName - Optional filename for the imported doc.
|
||||
* @returns A Promise that resolves to the ID of the newly created doc, or undefined if import fails.
|
||||
*/
|
||||
async function importHTMLToDoc({
|
||||
collection,
|
||||
schema,
|
||||
html,
|
||||
fileName,
|
||||
}: ImportHTMLToDocOptions) {
|
||||
const provider = getProvider();
|
||||
const job = new Transformer({
|
||||
schema: collection.schema,
|
||||
schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
@@ -127,10 +122,15 @@ async function importHTMLToDoc({
|
||||
*
|
||||
* @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 zip file as a Blob.
|
||||
* @returns A Promise that resolves to an array of IDs of the newly created docs.
|
||||
*/
|
||||
async function importHTMLZip({ collection, imported }: ImportHTMLZipOptions) {
|
||||
async function importHTMLZip({
|
||||
collection,
|
||||
schema,
|
||||
imported,
|
||||
}: ImportHTMLZipOptions) {
|
||||
const provider = getProvider();
|
||||
const unzip = new Unzip();
|
||||
await unzip.load(imported);
|
||||
@@ -161,7 +161,7 @@ async function importHTMLZip({ collection, imported }: ImportHTMLZipOptions) {
|
||||
htmlBlobs.map(async ([fileName, blob]) => {
|
||||
const fileNameWithoutExt = fileName.replace(/\.[^/.]+$/, '');
|
||||
const job = new Transformer({
|
||||
schema: collection.schema,
|
||||
schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
|
||||
@@ -9,7 +9,7 @@ import { SpecProvider } from '@blocksuite/affine-shared/utils';
|
||||
import { Container } from '@blocksuite/global/di';
|
||||
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
|
||||
import { assertExists, sha } from '@blocksuite/global/utils';
|
||||
import type { Store, Workspace } from '@blocksuite/store';
|
||||
import type { Schema, Store, Workspace } from '@blocksuite/store';
|
||||
import { extMimeMap, Transformer } from '@blocksuite/store';
|
||||
|
||||
import { createAssetsArchive, download, Unzip } from './utils.js';
|
||||
@@ -31,12 +31,14 @@ type ImportMarkdownToBlockOptions = {
|
||||
|
||||
type ImportMarkdownToDocOptions = {
|
||||
collection: Workspace;
|
||||
schema: Schema;
|
||||
markdown: string;
|
||||
fileName?: string;
|
||||
};
|
||||
|
||||
type ImportMarkdownZipOptions = {
|
||||
collection: Workspace;
|
||||
schema: Schema;
|
||||
imported: Blob;
|
||||
};
|
||||
|
||||
@@ -47,19 +49,10 @@ type ImportMarkdownZipOptions = {
|
||||
*/
|
||||
async function exportDoc(doc: Store) {
|
||||
const provider = getProvider();
|
||||
const job = new Transformer({
|
||||
schema: doc.schema,
|
||||
blobCRUD: doc.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => doc.workspace.createDoc({ id }),
|
||||
get: (id: string) => doc.workspace.getDoc(id),
|
||||
delete: (id: string) => doc.workspace.removeDoc(id),
|
||||
},
|
||||
middlewares: [
|
||||
docLinkBaseURLMiddleware(doc.workspace.id),
|
||||
titleMiddleware(doc.workspace.meta.docMetas),
|
||||
],
|
||||
});
|
||||
const job = doc.getTransformer([
|
||||
docLinkBaseURLMiddleware(doc.workspace.id),
|
||||
titleMiddleware(doc.workspace.meta.docMetas),
|
||||
]);
|
||||
const snapshot = job.docToSnapshot(doc);
|
||||
|
||||
const adapter = new MarkdownAdapter(job, provider);
|
||||
@@ -107,19 +100,10 @@ async function importMarkdownToBlock({
|
||||
blockId,
|
||||
}: ImportMarkdownToBlockOptions) {
|
||||
const provider = getProvider();
|
||||
const job = new Transformer({
|
||||
schema: doc.schema,
|
||||
blobCRUD: doc.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => doc.workspace.createDoc({ id }),
|
||||
get: (id: string) => doc.workspace.getDoc(id),
|
||||
delete: (id: string) => doc.workspace.removeDoc(id),
|
||||
},
|
||||
middlewares: [
|
||||
defaultImageProxyMiddleware,
|
||||
docLinkBaseURLMiddleware(doc.workspace.id),
|
||||
],
|
||||
});
|
||||
const job = doc.getTransformer([
|
||||
defaultImageProxyMiddleware,
|
||||
docLinkBaseURLMiddleware(doc.workspace.id),
|
||||
]);
|
||||
const adapter = new MarkdownAdapter(job, provider);
|
||||
const snapshot = await adapter.toSliceSnapshot({
|
||||
file: markdown,
|
||||
@@ -143,18 +127,20 @@ async function importMarkdownToBlock({
|
||||
* Imports Markdown content into a new doc within a collection.
|
||||
* @param options Object containing import options
|
||||
* @param options.collection The target doc collection
|
||||
* @param options.schema The schema of the target doc collection
|
||||
* @param options.markdown The Markdown content to import
|
||||
* @param options.fileName Optional filename for the imported doc
|
||||
* @returns A Promise that resolves to the ID of the newly created doc, or undefined if import fails
|
||||
*/
|
||||
async function importMarkdownToDoc({
|
||||
collection,
|
||||
schema,
|
||||
markdown,
|
||||
fileName,
|
||||
}: ImportMarkdownToDocOptions) {
|
||||
const provider = getProvider();
|
||||
const job = new Transformer({
|
||||
schema: collection.schema,
|
||||
schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
@@ -182,11 +168,13 @@ async function importMarkdownToDoc({
|
||||
* Imports a zip file containing Markdown files and assets into a collection.
|
||||
* @param options Object containing import options
|
||||
* @param options.collection The target doc collection
|
||||
* @param options.schema The schema of the target doc collection
|
||||
* @param options.imported The zip file as a Blob
|
||||
* @returns A Promise that resolves to an array of IDs of the newly created docs
|
||||
*/
|
||||
async function importMarkdownZip({
|
||||
collection,
|
||||
schema,
|
||||
imported,
|
||||
}: ImportMarkdownZipOptions) {
|
||||
const provider = getProvider();
|
||||
@@ -219,7 +207,7 @@ async function importMarkdownZip({
|
||||
markdownBlobs.map(async ([fileName, blob]) => {
|
||||
const fileNameWithoutExt = fileName.replace(/\.[^/.]+$/, '');
|
||||
const job = new Transformer({
|
||||
schema: collection.schema,
|
||||
schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
|
||||
@@ -3,12 +3,18 @@ import { NotionHtmlAdapter } from '@blocksuite/affine-shared/adapters';
|
||||
import { SpecProvider } from '@blocksuite/affine-shared/utils';
|
||||
import { Container } from '@blocksuite/global/di';
|
||||
import { sha } from '@blocksuite/global/utils';
|
||||
import { extMimeMap, Transformer, type Workspace } from '@blocksuite/store';
|
||||
import {
|
||||
extMimeMap,
|
||||
type Schema,
|
||||
Transformer,
|
||||
type Workspace,
|
||||
} from '@blocksuite/store';
|
||||
|
||||
import { Unzip } from './utils.js';
|
||||
|
||||
type ImportNotionZipOptions = {
|
||||
collection: Workspace;
|
||||
schema: Schema;
|
||||
imported: Blob;
|
||||
};
|
||||
|
||||
@@ -26,6 +32,7 @@ function getProvider() {
|
||||
*
|
||||
* @param options - The options for importing.
|
||||
* @param options.collection - The BlockSuite document collection.
|
||||
* @param options.schema - The schema of the BlockSuite document collection.
|
||||
* @param options.imported - The imported zip file as a Blob.
|
||||
*
|
||||
* @returns A promise that resolves to an object containing:
|
||||
@@ -36,6 +43,7 @@ function getProvider() {
|
||||
*/
|
||||
async function importNotionZip({
|
||||
collection,
|
||||
schema,
|
||||
imported,
|
||||
}: ImportNotionZipOptions) {
|
||||
const provider = getProvider();
|
||||
@@ -117,7 +125,7 @@ async function importNotionZip({
|
||||
}
|
||||
const pagePromises = Array.from(pagePaths).map(async path => {
|
||||
const job = new Transformer({
|
||||
schema: collection.schema,
|
||||
schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
|
||||
@@ -3,15 +3,19 @@ import {
|
||||
titleMiddleware,
|
||||
} from '@blocksuite/affine-shared/adapters';
|
||||
import { sha } from '@blocksuite/global/utils';
|
||||
import type { DocSnapshot, Store, Workspace } from '@blocksuite/store';
|
||||
import type { DocSnapshot, Schema, Store, Workspace } from '@blocksuite/store';
|
||||
import { extMimeMap, getAssetName, Transformer } from '@blocksuite/store';
|
||||
|
||||
import { download, Unzip, Zip } from '../transformers/utils.js';
|
||||
|
||||
async function exportDocs(collection: Workspace, docs: Store[]) {
|
||||
async function exportDocs(
|
||||
collection: Workspace,
|
||||
schema: Schema,
|
||||
docs: Store[]
|
||||
) {
|
||||
const zip = new Zip();
|
||||
const job = new Transformer({
|
||||
schema: collection.schema,
|
||||
schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
@@ -70,7 +74,11 @@ async function exportDocs(collection: Workspace, docs: Store[]) {
|
||||
return download(downloadBlob, `${collection.id}.bs.zip`);
|
||||
}
|
||||
|
||||
async function importDocs(collection: Workspace, imported: Blob) {
|
||||
async function importDocs(
|
||||
collection: Workspace,
|
||||
schema: Schema,
|
||||
imported: Blob
|
||||
) {
|
||||
const unzip = new Unzip();
|
||||
await unzip.load(imported);
|
||||
|
||||
@@ -98,7 +106,7 @@ async function importDocs(collection: Workspace, imported: Blob) {
|
||||
}
|
||||
|
||||
const job = new Transformer({
|
||||
schema: collection.schema,
|
||||
schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { clamp } from '@blocksuite/affine-shared/utils';
|
||||
import clamp from 'lodash-es/clamp';
|
||||
|
||||
type CollisionBox = {
|
||||
/**
|
||||
|
||||
@@ -134,13 +134,12 @@ export class EdgelessChangeBrushButton extends WithDisposable(LitElement) {
|
||||
return html`
|
||||
<edgeless-color-picker-button
|
||||
class="color"
|
||||
.label=${'Color'}
|
||||
.label="${'Color'}"
|
||||
.pick=${this.pickColor}
|
||||
.color=${selectedColor}
|
||||
.colors=${colors}
|
||||
.colorType=${type}
|
||||
.theme=${colorScheme}
|
||||
.palettes=${DefaultTheme.Palettes}
|
||||
>
|
||||
</edgeless-color-picker-button>
|
||||
`;
|
||||
@@ -159,7 +158,6 @@ export class EdgelessChangeBrushButton extends WithDisposable(LitElement) {
|
||||
<edgeless-color-panel
|
||||
.value=${selectedColor}
|
||||
.theme=${colorScheme}
|
||||
.palettes=${DefaultTheme.Palettes}
|
||||
@select=${this._setBrushColor}
|
||||
>
|
||||
</edgeless-color-panel>
|
||||
|
||||
@@ -373,13 +373,12 @@ export class EdgelessChangeConnectorButton extends WithDisposable(LitElement) {
|
||||
return html`
|
||||
<edgeless-color-picker-button
|
||||
class="stroke-color"
|
||||
.label=${'Stroke style'}
|
||||
.label="${'Stroke style'}"
|
||||
.pick=${this.pickColor}
|
||||
.color=${selectedColor}
|
||||
.colors=${colors}
|
||||
.colorType=${type}
|
||||
.theme=${colorScheme}
|
||||
.palettes=${DefaultTheme.Palettes}
|
||||
.hollowCircle=${true}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -43,7 +43,6 @@ import {
|
||||
import {
|
||||
EmbedOptionProvider,
|
||||
type EmbedOptions,
|
||||
FeatureFlagService,
|
||||
GenerateDocUrlProvider,
|
||||
type GenerateDocUrlService,
|
||||
type LinkEventType,
|
||||
@@ -419,16 +418,6 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) {
|
||||
private get _canConvertToEmbedView() {
|
||||
const block = this._blockComponent;
|
||||
|
||||
// synced doc entry controlled by awareness flag
|
||||
if (!!block && isEmbedLinkedDocBlock(block.model)) {
|
||||
const isSyncedDocEnabled = block.doc
|
||||
.get(FeatureFlagService)
|
||||
.getFlag('enable_synced_doc_block');
|
||||
if (!isSyncedDocEnabled) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
(block && 'convertToEmbed' in block) ||
|
||||
this._embedOptions?.viewType === 'embed'
|
||||
|
||||
@@ -13,7 +13,6 @@ import { renderToolbarSeparator } from '@blocksuite/affine-components/toolbar';
|
||||
import {
|
||||
type ColorScheme,
|
||||
DEFAULT_NOTE_HEIGHT,
|
||||
DefaultTheme,
|
||||
type FrameBlockModel,
|
||||
NoteBlockModel,
|
||||
NoteDisplayMode,
|
||||
@@ -201,13 +200,12 @@ export class EdgelessChangeFrameButton extends WithDisposable(LitElement) {
|
||||
return html`
|
||||
<edgeless-color-picker-button
|
||||
class="background"
|
||||
.label=${'Background'}
|
||||
.label="${'Background'}"
|
||||
.pick=${this.pickColor}
|
||||
.color=${background}
|
||||
.colors=${colors}
|
||||
.colorType=${type}
|
||||
.theme=${colorScheme}
|
||||
.palettes=${DefaultTheme.Palettes}
|
||||
>
|
||||
</edgeless-color-picker-button>
|
||||
`;
|
||||
@@ -229,7 +227,6 @@ export class EdgelessChangeFrameButton extends WithDisposable(LitElement) {
|
||||
<edgeless-color-panel
|
||||
.value=${background}
|
||||
.theme=${colorScheme}
|
||||
.palettes=${DefaultTheme.Palettes}
|
||||
@select=${this._setFrameBackground}
|
||||
>
|
||||
</edgeless-color-panel>
|
||||
|
||||
@@ -338,7 +338,6 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) {
|
||||
.colors=${colors}
|
||||
.colorType=${type}
|
||||
.theme=${colorScheme}
|
||||
.palettes=${DefaultTheme.Palettes}
|
||||
>
|
||||
</edgeless-color-picker-button>
|
||||
`;
|
||||
@@ -362,7 +361,6 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) {
|
||||
aria-label="Fill colors"
|
||||
.value=${selectedFillColor}
|
||||
.theme=${colorScheme}
|
||||
.palettes=${DefaultTheme.Palettes}
|
||||
@select=${this._setShapeFillColor}
|
||||
>
|
||||
</edgeless-color-panel>
|
||||
@@ -390,7 +388,6 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) {
|
||||
.colors=${colors}
|
||||
.colorType=${type}
|
||||
.theme=${colorScheme}
|
||||
.palettes=${DefaultTheme.Palettes}
|
||||
.hollowCircle=${true}
|
||||
>
|
||||
<div
|
||||
@@ -453,8 +450,8 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) {
|
||||
() => html`
|
||||
<editor-icon-button
|
||||
aria-label="Add text"
|
||||
.tooltip=${'Add text'}
|
||||
.iconSize=${'20px'}
|
||||
.tooltip="${'Add text'}"
|
||||
.iconSize="${'20px'}"
|
||||
@click=${this._addText}
|
||||
>
|
||||
${AddTextIcon()}
|
||||
@@ -465,7 +462,7 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) {
|
||||
'menu',
|
||||
() => html`
|
||||
<edgeless-change-text-menu
|
||||
.elementType=${'shape'}
|
||||
.elementType="${'shape'}"
|
||||
.elements=${elements}
|
||||
.edgeless=${this.edgeless}
|
||||
></edgeless-change-text-menu>
|
||||
|
||||
@@ -344,6 +344,10 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) {
|
||||
matchFontFaces.length === 1 &&
|
||||
matchFontFaces[0].style === selectedFontStyle &&
|
||||
matchFontFaces[0].weight === selectedFontWeight;
|
||||
const palettes =
|
||||
this.elementType === 'shape'
|
||||
? DefaultTheme.ShapeTextColorPalettes
|
||||
: DefaultTheme.Palettes;
|
||||
|
||||
return join(
|
||||
[
|
||||
@@ -389,14 +393,14 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) {
|
||||
return html`
|
||||
<edgeless-color-picker-button
|
||||
class="text-color"
|
||||
.label=${'Text color'}
|
||||
.label="${'Text color'}"
|
||||
.pick=${this.pickColor}
|
||||
.isText=${true}
|
||||
.color=${selectedColor}
|
||||
.colors=${colors}
|
||||
.colorType=${type}
|
||||
.theme=${colorScheme}
|
||||
.palettes=${DefaultTheme.Palettes}
|
||||
.palettes=${palettes}
|
||||
>
|
||||
</edgeless-color-picker-button>
|
||||
`;
|
||||
@@ -418,7 +422,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) {
|
||||
<edgeless-color-panel
|
||||
.value=${selectedColor}
|
||||
.theme=${colorScheme}
|
||||
.palettes=${DefaultTheme.Palettes}
|
||||
.palettes=${palettes}
|
||||
@select=${this._setTextColor}
|
||||
></edgeless-color-panel>
|
||||
</editor-menu-button>
|
||||
|
||||
@@ -44,7 +44,6 @@ import {
|
||||
import {
|
||||
EmbedOptionProvider,
|
||||
type EmbedOptions,
|
||||
FeatureFlagService,
|
||||
GenerateDocUrlProvider,
|
||||
type GenerateDocUrlService,
|
||||
type LinkEventType,
|
||||
@@ -236,16 +235,6 @@ export class EmbedCardToolbar extends WidgetComponent<
|
||||
cloneGroups(BUILT_IN_GROUPS);
|
||||
|
||||
private get _canConvertToEmbedView() {
|
||||
// synced doc entry controlled by awareness flag
|
||||
if (this.focusModel && isEmbedLinkedDocBlock(this.focusModel)) {
|
||||
const isSyncedDocEnabled = this.doc
|
||||
.get(FeatureFlagService)
|
||||
.getFlag('enable_synced_doc_block');
|
||||
if (!isSyncedDocEnabled) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.focusBlock) return false;
|
||||
|
||||
return (
|
||||
|
||||
@@ -64,7 +64,7 @@ import type {
|
||||
import { tableViewMeta } from '@blocksuite/data-view/view-presets';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import { MoreVerticalIcon } from '@blocksuite/icons/lit';
|
||||
import { Slice } from '@blocksuite/store';
|
||||
import { Slice, toDraftModel } from '@blocksuite/store';
|
||||
import { html, type TemplateResult } from 'lit';
|
||||
|
||||
import { FormatBarContext } from './context.js';
|
||||
@@ -230,7 +230,9 @@ export function toolbarDefaultConfig(toolbar: AffineFormatBarWidget) {
|
||||
host.selection.clear();
|
||||
|
||||
const doc = host.doc;
|
||||
const autofill = getTitleFromSelectedModels(selectedModels);
|
||||
const autofill = getTitleFromSelectedModels(
|
||||
selectedModels.map(toDraftModel)
|
||||
);
|
||||
promptDocTitle(std, autofill)
|
||||
.then(async title => {
|
||||
if (title === null) return;
|
||||
|
||||
@@ -113,12 +113,6 @@ import {
|
||||
|
||||
export type KeyboardToolbarConfig = {
|
||||
items: KeyboardToolbarItem[];
|
||||
/**
|
||||
* @description Whether to use the screen height as the keyboard height when the virtual keyboard API is not supported.
|
||||
* It is useful when the app is running in a webview and the keyboard is not overlaid on the content.
|
||||
* @default false
|
||||
*/
|
||||
useScreenHeight?: boolean;
|
||||
};
|
||||
|
||||
export type KeyboardToolbarItem =
|
||||
@@ -1106,5 +1100,4 @@ export const defaultKeyboardToolbarConfig: KeyboardToolbarConfig = {
|
||||
},
|
||||
},
|
||||
],
|
||||
useScreenHeight: false,
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { getDocTitleByEditorHost } from '@blocksuite/affine-components/doc-title';
|
||||
import type { RootBlockModel } from '@blocksuite/affine-model';
|
||||
import { FeatureFlagService } from '@blocksuite/affine-shared/services';
|
||||
import { WidgetComponent } from '@blocksuite/block-std';
|
||||
import { IS_MOBILE } from '@blocksuite/global/env';
|
||||
import { assertType } from '@blocksuite/global/utils';
|
||||
import { signal } from '@preact/signals-core';
|
||||
import { html, nothing } from 'lit';
|
||||
|
||||
@@ -20,10 +20,10 @@ export class AffineKeyboardToolbarWidget extends WidgetComponent<
|
||||
> {
|
||||
private readonly _close = (blur: boolean) => {
|
||||
if (blur) {
|
||||
if (document.activeElement === this._docTitle) {
|
||||
this._docTitle?.blur();
|
||||
if (document.activeElement === this._docTitle?.inlineEditorContainer) {
|
||||
this._docTitle?.inlineEditor?.setInlineRange(null);
|
||||
} else if (document.activeElement === this.block.rootComponent) {
|
||||
this.block.rootComponent?.blur();
|
||||
this.std.selection.clear();
|
||||
}
|
||||
}
|
||||
this._show$.value = false;
|
||||
@@ -31,12 +31,8 @@ export class AffineKeyboardToolbarWidget extends WidgetComponent<
|
||||
|
||||
private readonly _show$ = signal(false);
|
||||
|
||||
private get _docTitle(): HTMLDivElement | null {
|
||||
const docTitle = this.std.host
|
||||
.closest('.affine-page-viewport')
|
||||
?.querySelector('doc-title rich-text .inline-editor');
|
||||
assertType<HTMLDivElement | null>(docTitle);
|
||||
return docTitle;
|
||||
private get _docTitle() {
|
||||
return getDocTitleByEditorHost(this.std.host);
|
||||
}
|
||||
|
||||
get config() {
|
||||
@@ -61,10 +57,11 @@ export class AffineKeyboardToolbarWidget extends WidgetComponent<
|
||||
}
|
||||
|
||||
if (this._docTitle) {
|
||||
this.disposables.addFromEvent(this._docTitle, 'focus', () => {
|
||||
const { inlineEditorContainer } = this._docTitle;
|
||||
this.disposables.addFromEvent(inlineEditorContainer, 'focus', () => {
|
||||
this._show$.value = true;
|
||||
});
|
||||
this.disposables.addFromEvent(this._docTitle, 'blur', () => {
|
||||
this.disposables.addFromEvent(inlineEditorContainer, 'blur', () => {
|
||||
this._show$.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import {
|
||||
VirtualKeyboardController,
|
||||
type VirtualKeyboardControllerConfig,
|
||||
} from '@blocksuite/affine-components/virtual-keyboard';
|
||||
import { getSelectedModelsCommand } from '@blocksuite/affine-shared/commands';
|
||||
import { VirtualKeyboardProvider } from '@blocksuite/affine-shared/services';
|
||||
import {
|
||||
PropTypes,
|
||||
requiredProperties,
|
||||
@@ -17,20 +14,21 @@ import { repeat } from 'lit/directives/repeat.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { when } from 'lit/directives/when.js';
|
||||
|
||||
import { PageRootBlockComponent } from '../../page/page-root-block.js';
|
||||
import { PageRootBlockComponent } from '../../page/page-root-block';
|
||||
import type {
|
||||
KeyboardIconType,
|
||||
KeyboardToolbarConfig,
|
||||
KeyboardToolbarContext,
|
||||
KeyboardToolbarItem,
|
||||
KeyboardToolPanelConfig,
|
||||
} from './config.js';
|
||||
import { keyboardToolbarStyles, TOOLBAR_HEIGHT } from './styles.js';
|
||||
} from './config';
|
||||
import { PositionController } from './position-controller';
|
||||
import { keyboardToolbarStyles } from './styles';
|
||||
import {
|
||||
isKeyboardSubToolBarConfig,
|
||||
isKeyboardToolBarActionItem,
|
||||
isKeyboardToolPanelConfig,
|
||||
} from './utils.js';
|
||||
} from './utils';
|
||||
|
||||
export const AFFINE_KEYBOARD_TOOLBAR = 'affine-keyboard-toolbar';
|
||||
|
||||
@@ -43,11 +41,28 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
||||
) {
|
||||
static override styles = keyboardToolbarStyles;
|
||||
|
||||
/** This field records the panel static height same as the virtual keyboard height */
|
||||
panelHeight$ = signal(0);
|
||||
|
||||
positionController = new PositionController(this);
|
||||
|
||||
get std() {
|
||||
return this.rootComponent.std;
|
||||
}
|
||||
|
||||
get keyboard() {
|
||||
return this._context.std.get(VirtualKeyboardProvider);
|
||||
}
|
||||
|
||||
get panelOpened() {
|
||||
return this._currentPanelIndex$.value !== -1;
|
||||
}
|
||||
|
||||
private readonly _closeToolPanel = () => {
|
||||
if (!this._isPanelOpened) return;
|
||||
if (!this.panelOpened) return;
|
||||
|
||||
this._currentPanelIndex$.value = -1;
|
||||
this._keyboardController.show();
|
||||
this.keyboard.show();
|
||||
};
|
||||
|
||||
private readonly _currentPanelIndex$ = signal(-1);
|
||||
@@ -55,7 +70,7 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
||||
private readonly _goPrevToolbar = () => {
|
||||
if (!this._isSubToolbarOpened) return;
|
||||
|
||||
if (this._isPanelOpened) this._closeToolPanel();
|
||||
if (this.panelOpened) this._closeToolPanel();
|
||||
|
||||
this._path$.value = this._path$.value.slice(0, -1);
|
||||
};
|
||||
@@ -75,31 +90,25 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
||||
this._closeToolPanel();
|
||||
} else {
|
||||
this._currentPanelIndex$.value = index;
|
||||
this._keyboardController.hide();
|
||||
this.scrollCurrentBlockIntoView();
|
||||
this.keyboard.hide();
|
||||
this._scrollCurrentBlockIntoView();
|
||||
}
|
||||
}
|
||||
this._lastActiveItem$.value = item;
|
||||
};
|
||||
|
||||
private readonly _keyboardController = new VirtualKeyboardController(this);
|
||||
|
||||
private readonly _lastActiveItem$ = signal<KeyboardToolbarItem | null>(null);
|
||||
|
||||
/** This field records the panel static height, which dose not aim to control the panel opening */
|
||||
private readonly _panelHeight$ = signal(0);
|
||||
|
||||
private readonly _path$ = signal<number[]>([]);
|
||||
|
||||
private readonly scrollCurrentBlockIntoView = () => {
|
||||
const { std } = this.rootComponent;
|
||||
std.command
|
||||
private readonly _scrollCurrentBlockIntoView = () => {
|
||||
this.std.command
|
||||
.chain()
|
||||
.pipe(getSelectedModelsCommand)
|
||||
.pipe(({ selectedModels }) => {
|
||||
if (!selectedModels?.length) return;
|
||||
|
||||
const block = std.view.getBlock(selectedModels[0].id);
|
||||
const block = this.std.view.getBlock(selectedModels[0].id);
|
||||
if (!block) return;
|
||||
|
||||
const { y: y1 } = this.getBoundingClientRect();
|
||||
@@ -118,7 +127,7 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
||||
|
||||
private get _context(): KeyboardToolbarContext {
|
||||
return {
|
||||
std: this.rootComponent.std,
|
||||
std: this.std,
|
||||
rootComponent: this.rootComponent,
|
||||
closeToolbar: (blur = false) => {
|
||||
this.close(blur);
|
||||
@@ -130,7 +139,7 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
||||
}
|
||||
|
||||
private get _currentPanelConfig(): KeyboardToolPanelConfig | null {
|
||||
if (!this._isPanelOpened) return null;
|
||||
if (!this.panelOpened) return null;
|
||||
|
||||
const result = this._currentToolbarItems[this._currentPanelIndex$.value];
|
||||
|
||||
@@ -139,9 +148,7 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
||||
|
||||
private get _currentToolbarItems(): KeyboardToolbarItem[] {
|
||||
let items = this.config.items;
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
||||
for (let i = 0; i < this._path$.value.length; i++) {
|
||||
const index = this._path$.value[i];
|
||||
for (const index of this._path$.value) {
|
||||
if (isKeyboardSubToolBarConfig(items[index])) {
|
||||
items = items[index].items;
|
||||
} else {
|
||||
@@ -156,21 +163,10 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
||||
);
|
||||
}
|
||||
|
||||
private get _isPanelOpened() {
|
||||
return this._currentPanelIndex$.value !== -1;
|
||||
}
|
||||
|
||||
private get _isSubToolbarOpened() {
|
||||
return this._path$.value.length > 0;
|
||||
}
|
||||
|
||||
get virtualKeyboardControllerConfig(): VirtualKeyboardControllerConfig {
|
||||
return {
|
||||
useScreenHeight: this.config.useScreenHeight ?? false,
|
||||
inputElement: this.rootComponent,
|
||||
};
|
||||
}
|
||||
|
||||
private _renderIcon(icon: KeyboardIconType) {
|
||||
return typeof icon === 'function' ? icon(this._context) : icon;
|
||||
}
|
||||
@@ -252,35 +248,13 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
this.disposables.add(
|
||||
effect(() => {
|
||||
if (this._keyboardController.opened) {
|
||||
this._panelHeight$.value = this._keyboardController.keyboardHeight;
|
||||
} else if (this._isPanelOpened && this._panelHeight$.peek() === 0) {
|
||||
this._panelHeight$.value = 260;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.disposables.add(
|
||||
effect(() => {
|
||||
if (this._keyboardController.opened && !this.config.useScreenHeight) {
|
||||
document.body.style.paddingBottom = `${this._keyboardController.keyboardHeight + TOOLBAR_HEIGHT}px`;
|
||||
} else if (this._isPanelOpened) {
|
||||
document.body.style.paddingBottom = `${this._panelHeight$.value + TOOLBAR_HEIGHT}px`;
|
||||
} else {
|
||||
document.body.style.paddingBottom = '';
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.disposables.add(
|
||||
effect(() => {
|
||||
const std = this.rootComponent.std;
|
||||
std.selection.value;
|
||||
// wait cursor updated
|
||||
requestAnimationFrame(() => {
|
||||
this.scrollCurrentBlockIntoView();
|
||||
this._scrollCurrentBlockIntoView();
|
||||
});
|
||||
})
|
||||
);
|
||||
@@ -328,24 +302,14 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
||||
);
|
||||
}
|
||||
|
||||
override disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
document.body.style.paddingBottom = '';
|
||||
}
|
||||
|
||||
override firstUpdated() {
|
||||
// workaround for the virtual keyboard showing transition animation
|
||||
setTimeout(() => {
|
||||
this.scrollCurrentBlockIntoView();
|
||||
this._scrollCurrentBlockIntoView();
|
||||
}, 700);
|
||||
}
|
||||
|
||||
override render() {
|
||||
this.style.bottom =
|
||||
this.config.useScreenHeight && this._keyboardController.opened
|
||||
? `${-this._panelHeight$.value}px`
|
||||
: '0px';
|
||||
|
||||
return html`
|
||||
<div class="keyboard-toolbar">
|
||||
${this._renderItems()}
|
||||
@@ -355,7 +319,7 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
||||
<affine-keyboard-tool-panel
|
||||
.config=${this._currentPanelConfig}
|
||||
.context=${this._context}
|
||||
height=${this._panelHeight$.value}
|
||||
height=${this.panelHeight$.value}
|
||||
></affine-keyboard-tool-panel>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { type VirtualKeyboardProvider } from '@blocksuite/affine-shared/services';
|
||||
import type { BlockStdScope, ShadowlessElement } from '@blocksuite/block-std';
|
||||
import { DisposableGroup } from '@blocksuite/global/utils';
|
||||
import { effect, type Signal } from '@preact/signals-core';
|
||||
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
||||
|
||||
import { TOOLBAR_HEIGHT } from './styles';
|
||||
|
||||
/**
|
||||
* This controller is used to control the keyboard toolbar position
|
||||
*/
|
||||
export class PositionController implements ReactiveController {
|
||||
private readonly _disposables = new DisposableGroup();
|
||||
|
||||
host: ReactiveControllerHost &
|
||||
ShadowlessElement & {
|
||||
std: BlockStdScope;
|
||||
panelHeight$: Signal<number>;
|
||||
keyboard: VirtualKeyboardProvider;
|
||||
panelOpened: boolean;
|
||||
};
|
||||
|
||||
constructor(host: PositionController['host']) {
|
||||
(this.host = host).addController(this);
|
||||
}
|
||||
|
||||
hostConnected() {
|
||||
const { keyboard, panelOpened } = this.host;
|
||||
|
||||
this._disposables.add(
|
||||
effect(() => {
|
||||
if (keyboard.visible$.value) {
|
||||
this.host.panelHeight$.value = keyboard.height$.value;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.host.style.bottom = '0px';
|
||||
this._disposables.add(
|
||||
effect(() => {
|
||||
if (keyboard.visible$.value) {
|
||||
document.body.style.paddingBottom = `${keyboard.height$.value + TOOLBAR_HEIGHT}px`;
|
||||
} else if (panelOpened) {
|
||||
document.body.style.paddingBottom = `${this.host.panelHeight$.peek() + TOOLBAR_HEIGHT}px`;
|
||||
} else {
|
||||
document.body.style.paddingBottom = '';
|
||||
}
|
||||
})
|
||||
);
|
||||
this._disposables.add(() => {
|
||||
document.body.style.paddingBottom = '';
|
||||
});
|
||||
}
|
||||
|
||||
hostDisconnected() {
|
||||
this._disposables.dispose();
|
||||
}
|
||||
}
|
||||
@@ -55,15 +55,15 @@ export interface LinkedWidgetConfig {
|
||||
*
|
||||
* If the return value is not null, no action will be taken.
|
||||
*/
|
||||
autoFocusedItem?: (
|
||||
autoFocusedItemKey?: (
|
||||
menus: LinkedMenuGroup[],
|
||||
query: string,
|
||||
currentActiveKey: string | null,
|
||||
editorHost: EditorHost,
|
||||
inlineEditor: AffineInlineEditor
|
||||
) => LinkedMenuItem | null;
|
||||
) => string | null;
|
||||
|
||||
mobile: {
|
||||
useScreenHeight?: boolean;
|
||||
/**
|
||||
* The linked doc menu widget will scroll the container to make sure the input cursor is visible in viewport.
|
||||
* It accepts a selector string, HTMLElement or Window
|
||||
@@ -101,8 +101,6 @@ export type LinkedMenuGroup = {
|
||||
loading?: boolean | Signal<boolean>;
|
||||
// copywriting when display quantity exceeds
|
||||
overflowText?: string | Signal<string>;
|
||||
// loading text
|
||||
loadingText?: string | Signal<string>;
|
||||
};
|
||||
|
||||
export type LinkedDocContext = {
|
||||
@@ -233,6 +231,7 @@ export function createNewDocMenuGroup(
|
||||
};
|
||||
showImportModal({
|
||||
collection: doc.workspace,
|
||||
schema: doc.schema,
|
||||
onSuccess,
|
||||
onFail,
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from '@blocksuite/affine-components/icons';
|
||||
import { openFileOrFiles } from '@blocksuite/affine-shared/utils';
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import type { Workspace } from '@blocksuite/store';
|
||||
import type { Schema, Workspace } from '@blocksuite/store';
|
||||
import { html, LitElement, type PropertyValues } from 'lit';
|
||||
import { query, state } from 'lit/decorators.js';
|
||||
|
||||
@@ -31,6 +31,7 @@ export class ImportDoc extends WithDisposable(LitElement) {
|
||||
|
||||
constructor(
|
||||
private readonly collection: Workspace,
|
||||
private readonly schema: Schema,
|
||||
private readonly onSuccess?: OnSuccessHandler,
|
||||
private readonly onFail?: OnFailHandler,
|
||||
private readonly abortController = new AbortController()
|
||||
@@ -63,6 +64,7 @@ export class ImportDoc extends WithDisposable(LitElement) {
|
||||
}
|
||||
const pageId = await HtmlTransformer.importHTMLToDoc({
|
||||
collection: this.collection,
|
||||
schema: this.schema,
|
||||
html: text,
|
||||
fileName,
|
||||
});
|
||||
@@ -93,6 +95,7 @@ export class ImportDoc extends WithDisposable(LitElement) {
|
||||
}
|
||||
const pageId = await MarkdownTransformer.importMarkdownToDoc({
|
||||
collection: this.collection,
|
||||
schema: this.schema,
|
||||
markdown: text,
|
||||
fileName,
|
||||
});
|
||||
@@ -117,6 +120,7 @@ export class ImportDoc extends WithDisposable(LitElement) {
|
||||
const { entryId, pageIds, isWorkspaceFile, hasMarkdown } =
|
||||
await NotionHtmlTransformer.importNotionZip({
|
||||
collection: this.collection,
|
||||
schema: this.schema,
|
||||
imported: file,
|
||||
});
|
||||
needLoading && this.abortController.abort();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Workspace } from '@blocksuite/store';
|
||||
import type { Schema, Workspace } from '@blocksuite/store';
|
||||
|
||||
import {
|
||||
ImportDoc,
|
||||
@@ -7,12 +7,14 @@ import {
|
||||
} from './import-doc.js';
|
||||
|
||||
export function showImportModal({
|
||||
schema,
|
||||
collection,
|
||||
onSuccess,
|
||||
onFail,
|
||||
container = document.body,
|
||||
abortController = new AbortController(),
|
||||
}: {
|
||||
schema: Schema;
|
||||
collection: Workspace;
|
||||
onSuccess?: OnSuccessHandler;
|
||||
onFail?: OnFailHandler;
|
||||
@@ -22,6 +24,7 @@ export function showImportModal({
|
||||
}) {
|
||||
const importDoc = new ImportDoc(
|
||||
collection,
|
||||
schema,
|
||||
onSuccess,
|
||||
onFail,
|
||||
abortController
|
||||
|
||||
@@ -20,10 +20,8 @@ import { choose } from 'lit/directives/choose.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import {
|
||||
type PageRootBlockComponent,
|
||||
RootBlockConfigExtension,
|
||||
} from '../../index.js';
|
||||
import type { PageRootBlockComponent } from '../../page/page-root-block.js';
|
||||
import { RootBlockConfigExtension } from '../../root-config.js';
|
||||
import {
|
||||
type AFFINE_LINKED_DOC_WIDGET,
|
||||
getMenus,
|
||||
@@ -216,7 +214,6 @@ export class AffineLinkedDocWidget extends WidgetComponent<
|
||||
convertTriggerKey: true,
|
||||
getMenus,
|
||||
mobile: {
|
||||
useScreenHeight: false,
|
||||
scrollContainer: getViewportElement(this.std.host) ?? window,
|
||||
scrollTopOffset: 46,
|
||||
},
|
||||
|
||||
@@ -75,23 +75,33 @@ export class LinkedDocPopover extends SignalWatcher(
|
||||
// need to rebind the effect because this._linkedDocGroup has changed.
|
||||
this._menusItemsEffectCleanup = effect(() => {
|
||||
this._updateAutoFocusedItem();
|
||||
|
||||
// wait for the next tick to ensure the items are rendered to DOM
|
||||
setTimeout(() => {
|
||||
this.scrollToFocusedItem();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
private readonly _updateAutoFocusedItem = () => {
|
||||
if (!this._query) {
|
||||
return;
|
||||
}
|
||||
const autoFocusedItem = this.context.config.autoFocusedItem?.(
|
||||
// Get the auto-focused item key from the config
|
||||
const autoFocusedItemKey = this.context.config.autoFocusedItemKey?.(
|
||||
this._linkedDocGroup,
|
||||
this._query,
|
||||
this._query || '',
|
||||
this._activatedItemKey,
|
||||
this.context.std.host,
|
||||
this.context.inlineEditor
|
||||
);
|
||||
if (autoFocusedItem) {
|
||||
this._activatedItemIndex = this._flattenActionList.findIndex(
|
||||
item => item.key === autoFocusedItem.key
|
||||
);
|
||||
|
||||
if (autoFocusedItemKey) {
|
||||
this._activatedItemKey = autoFocusedItemKey;
|
||||
return;
|
||||
}
|
||||
|
||||
// If no auto-focused item key is returned from the config and no item is currently focused,
|
||||
// focus the first item in the flattened action list
|
||||
if (!this._activatedItemKey && this._flattenActionList.length > 0) {
|
||||
this._activatedItemKey = this._flattenActionList[0].key;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -126,19 +136,9 @@ export class LinkedDocPopover extends SignalWatcher(
|
||||
let items = resolveSignal(group.items);
|
||||
|
||||
const isOverflow = !!group.maxDisplay && items.length > group.maxDisplay;
|
||||
const isLoading = resolveSignal(group.loading);
|
||||
|
||||
items = isExpanded ? items : items.slice(0, group.maxDisplay);
|
||||
|
||||
if (isLoading) {
|
||||
items = items.concat({
|
||||
key: 'loading',
|
||||
name: resolveSignal(group.loadingText) || 'loading',
|
||||
icon: LoadingIcon,
|
||||
action: () => {},
|
||||
});
|
||||
}
|
||||
|
||||
if (isOverflow && !isExpanded && group.maxDisplay) {
|
||||
items = items.concat({
|
||||
key: `${group.name} More`,
|
||||
@@ -183,7 +183,6 @@ export class LinkedDocPopover extends SignalWatcher(
|
||||
target: eventSource,
|
||||
signal: keydownObserverAbortController.signal,
|
||||
onInput: isComposition => {
|
||||
this._activatedItemIndex = 0;
|
||||
if (isComposition) {
|
||||
this._updateLinkedDocGroup().catch(console.error);
|
||||
} else {
|
||||
@@ -193,7 +192,6 @@ export class LinkedDocPopover extends SignalWatcher(
|
||||
}
|
||||
},
|
||||
onPaste: () => {
|
||||
this._activatedItemIndex = 0;
|
||||
setTimeout(() => {
|
||||
this._updateLinkedDocGroup().catch(console.error);
|
||||
}, 50);
|
||||
@@ -206,33 +204,18 @@ export class LinkedDocPopover extends SignalWatcher(
|
||||
if (curRange.index < this.context.startRange.index) {
|
||||
this.context.close();
|
||||
}
|
||||
this._activatedItemIndex = 0;
|
||||
this.context.inlineEditor.slots.renderComplete.once(
|
||||
this._updateLinkedDocGroup
|
||||
);
|
||||
},
|
||||
onMove: step => {
|
||||
const itemLen = this._flattenActionList.length;
|
||||
this._activatedItemIndex =
|
||||
(itemLen + this._activatedItemIndex + step) % itemLen;
|
||||
|
||||
// Scroll to the active item
|
||||
const item = this._flattenActionList[this._activatedItemIndex];
|
||||
const shadowRoot = this.shadowRoot;
|
||||
if (!shadowRoot) {
|
||||
console.warn('Failed to find the shadow root!', this);
|
||||
return;
|
||||
const nextIndex = (itemLen + this._activatedItemIndex + step) % itemLen;
|
||||
const item = this._flattenActionList[nextIndex];
|
||||
if (item) {
|
||||
this._activatedItemKey = item.key;
|
||||
}
|
||||
const ele = shadowRoot.querySelector(
|
||||
`icon-button[data-id="${item.key}"]`
|
||||
);
|
||||
if (!ele) {
|
||||
console.warn('Failed to find the active item!', item);
|
||||
return;
|
||||
}
|
||||
ele.scrollIntoView({
|
||||
block: 'nearest',
|
||||
});
|
||||
this.scrollToFocusedItem();
|
||||
},
|
||||
onConfirm: () => {
|
||||
this._flattenActionList[this._activatedItemIndex]
|
||||
@@ -261,19 +244,29 @@ export class LinkedDocPopover extends SignalWatcher(
|
||||
visibility: 'hidden',
|
||||
});
|
||||
|
||||
// XXX This is a side effect
|
||||
let accIdx = 0;
|
||||
const actionGroups = this._actionGroup.map(group => {
|
||||
// Check if the group is loading
|
||||
const isLoading = resolveSignal(group.loading);
|
||||
return {
|
||||
...group,
|
||||
isLoading,
|
||||
};
|
||||
});
|
||||
|
||||
return html`<div class="linked-doc-popover" style="${style}">
|
||||
${this._actionGroup
|
||||
.filter(group => group.items.length)
|
||||
${actionGroups
|
||||
.filter(group => group.items.length || group.isLoading)
|
||||
.map((group, idx) => {
|
||||
return html`
|
||||
<div class="divider" ?hidden=${idx === 0}></div>
|
||||
<div class="group-title">${group.name}</div>
|
||||
<div class="group-title">
|
||||
${group.name}
|
||||
${group.isLoading
|
||||
? html`<span class="loading-icon">${LoadingIcon}</span>`
|
||||
: nothing}
|
||||
</div>
|
||||
<div class="group" style=${group.styles ?? ''}>
|
||||
${group.items.map(({ key, name, icon, action }) => {
|
||||
accIdx++;
|
||||
const curIdx = accIdx - 1;
|
||||
const tooltip = this._showTooltip
|
||||
? html`<affine-tooltip
|
||||
tip-position=${'right'}
|
||||
@@ -290,13 +283,13 @@ export class LinkedDocPopover extends SignalWatcher(
|
||||
height="30px"
|
||||
data-id=${key}
|
||||
.text=${name}
|
||||
hover=${this._activatedItemIndex === curIdx}
|
||||
hover=${this._activatedItemKey === key}
|
||||
@click=${() => {
|
||||
action()?.catch(console.error);
|
||||
}}
|
||||
@mousemove=${() => {
|
||||
// Use `mousemove` instead of `mouseover` to avoid navigate conflict with keyboard
|
||||
this._activatedItemIndex = curIdx;
|
||||
this._activatedItemKey = key;
|
||||
// show tooltip whether text length overflows
|
||||
for (const button of this.iconButtons.values()) {
|
||||
if (button.dataset.id == key && button.textElement) {
|
||||
@@ -348,8 +341,40 @@ export class LinkedDocPopover extends SignalWatcher(
|
||||
}
|
||||
}
|
||||
|
||||
private scrollToFocusedItem() {
|
||||
const shadowRoot = this.shadowRoot;
|
||||
if (!shadowRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If there's no active item key, don't try to scroll
|
||||
if (!this._activatedItemKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ele = shadowRoot.querySelector(
|
||||
`icon-button[data-id="${this._activatedItemKey}"]`
|
||||
);
|
||||
|
||||
// If the element doesn't exist, don't log a warning
|
||||
if (!ele) {
|
||||
return;
|
||||
}
|
||||
|
||||
ele.scrollIntoView({
|
||||
block: 'nearest',
|
||||
});
|
||||
}
|
||||
|
||||
get _activatedItemIndex() {
|
||||
const index = this._flattenActionList.findIndex(
|
||||
item => item.key === this._activatedItemKey
|
||||
);
|
||||
return index === -1 ? 0 : index;
|
||||
}
|
||||
|
||||
@state()
|
||||
private accessor _activatedItemIndex = 0;
|
||||
private accessor _activatedItemKey: string | null = null;
|
||||
|
||||
@state()
|
||||
private accessor _linkedDocGroup: LinkedMenuGroup[] = [];
|
||||
|
||||
@@ -2,10 +2,7 @@ import {
|
||||
cleanSpecifiedTail,
|
||||
getTextContentFromInlineRange,
|
||||
} from '@blocksuite/affine-components/rich-text';
|
||||
import {
|
||||
VirtualKeyboardController,
|
||||
type VirtualKeyboardControllerConfig,
|
||||
} from '@blocksuite/affine-components/virtual-keyboard';
|
||||
import { VirtualKeyboardProvider } from '@blocksuite/affine-shared/services';
|
||||
import {
|
||||
createKeydownObserver,
|
||||
getViewportElement,
|
||||
@@ -43,8 +40,6 @@ export class AffineMobileLinkedDocMenu extends SignalWatcher(
|
||||
|
||||
private _firstActionItem: LinkedMenuItem | null = null;
|
||||
|
||||
private readonly _keyboardController = new VirtualKeyboardController(this);
|
||||
|
||||
private readonly _linkedDocGroup$ = signal<LinkedMenuGroup[]>([]);
|
||||
|
||||
private readonly _renderGroup = (group: LinkedMenuGroup) => {
|
||||
@@ -159,11 +154,8 @@ export class AffineMobileLinkedDocMenu extends SignalWatcher(
|
||||
);
|
||||
}
|
||||
|
||||
get virtualKeyboardControllerConfig(): VirtualKeyboardControllerConfig {
|
||||
return {
|
||||
useScreenHeight: this.context.config.mobile.useScreenHeight ?? false,
|
||||
inputElement: this.rootComponent,
|
||||
};
|
||||
get keyboard() {
|
||||
return this.context.std.get(VirtualKeyboardProvider);
|
||||
}
|
||||
|
||||
override connectedCallback() {
|
||||
@@ -230,8 +222,8 @@ export class AffineMobileLinkedDocMenu extends SignalWatcher(
|
||||
}
|
||||
|
||||
override firstUpdated() {
|
||||
if (!this._keyboardController.opened) {
|
||||
this._keyboardController.show();
|
||||
if (!this.keyboard.visible$.value) {
|
||||
this.keyboard.show();
|
||||
}
|
||||
this._scrollInputToTop();
|
||||
}
|
||||
@@ -244,11 +236,7 @@ export class AffineMobileLinkedDocMenu extends SignalWatcher(
|
||||
|
||||
this._firstActionItem = resolveSignal(groups[0].items)[0];
|
||||
|
||||
this.style.bottom =
|
||||
this.context.config.mobile.useScreenHeight &&
|
||||
this._keyboardController.opened
|
||||
? '0px'
|
||||
: `max(0px, ${this._keyboardController.keyboardHeight}px)`;
|
||||
this.style.bottom = `${this.keyboard.height$.value}px`;
|
||||
|
||||
return html`
|
||||
${join(groups.map(this._renderGroup), html`<div class="divider"></div>`)}
|
||||
|
||||
@@ -51,6 +51,18 @@ export const linkedDocPopoverStyles = css`
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
font-weight: 500;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.linked-doc-popover .group-title .loading-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.linked-doc-popover .group-title .loading-icon svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.linked-doc-popover .divider {
|
||||
|
||||
@@ -423,8 +423,9 @@ export class SurfaceRefBlockComponent extends BlockComponent<SurfaceRefBlockMode
|
||||
|
||||
override mounted() {
|
||||
const disposable = this.std.view.viewUpdated.on(payload => {
|
||||
if (payload.type !== 'block') return;
|
||||
if (
|
||||
payload.type === 'add' &&
|
||||
payload.method === 'add' &&
|
||||
matchModels(payload.view.model, [RootBlockModel])
|
||||
) {
|
||||
disposable.dispose();
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash.chunk": "^4.2.9",
|
||||
"vitest": "3.0.6"
|
||||
"vitest": "3.0.7"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
|
||||
@@ -50,7 +50,11 @@ export {
|
||||
} from './adapters/index.js';
|
||||
export type { SurfaceContext } from './surface-block.js';
|
||||
export { SurfaceBlockComponent } from './surface-block.js';
|
||||
export { SurfaceBlockModel, SurfaceBlockSchema } from './surface-model.js';
|
||||
export {
|
||||
SurfaceBlockModel,
|
||||
SurfaceBlockSchema,
|
||||
SurfaceBlockSchemaExtension,
|
||||
} from './surface-model.js';
|
||||
export type { SurfaceBlockService } from './surface-service.js';
|
||||
export {
|
||||
EdgelessSurfaceBlockSpec,
|
||||
|
||||
@@ -5,7 +5,7 @@ import type {
|
||||
import type { SurfaceBlockProps } from '@blocksuite/block-std/gfx';
|
||||
import { SurfaceBlockModel as BaseSurfaceModel } from '@blocksuite/block-std/gfx';
|
||||
import { DisposableGroup } from '@blocksuite/global/utils';
|
||||
import { defineBlockSchema } from '@blocksuite/store';
|
||||
import { BlockSchemaExtension, defineBlockSchema } from '@blocksuite/store';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import { elementsCtorMap } from './element-model/index.js';
|
||||
@@ -36,6 +36,9 @@ export const SurfaceBlockSchema = defineBlockSchema({
|
||||
toModel: () => new SurfaceBlockModel(),
|
||||
});
|
||||
|
||||
export const SurfaceBlockSchemaExtension =
|
||||
BlockSchemaExtension(SurfaceBlockSchema);
|
||||
|
||||
export type SurfaceMiddleware = (surface: SurfaceBlockModel) => () => void;
|
||||
|
||||
export class SurfaceBlockModel extends BaseSurfaceModel {
|
||||
|
||||
@@ -246,6 +246,7 @@ export class AddButton extends SignalWatcher(
|
||||
this.hoverColumnIndex$.value === this.columns$.value.length - 1;
|
||||
const dragging = this.columnDragging$.value;
|
||||
return html` <div
|
||||
data-testid="add-column-button"
|
||||
class="${classMap({
|
||||
[addColumnButtonStyle]: true,
|
||||
active: dragging,
|
||||
@@ -269,6 +270,7 @@ export class AddButton extends SignalWatcher(
|
||||
const hovered = this.hoverRowIndex$.value === this.rows$.value.length - 1;
|
||||
const dragging = this.rowDragging$.value;
|
||||
return html` <div
|
||||
data-testid="add-row-button"
|
||||
class="${classMap({
|
||||
[addRowButtonStyle]: true,
|
||||
active: dragging,
|
||||
|
||||
@@ -180,14 +180,16 @@ export class TableCell extends SignalWatcher(
|
||||
name: 'Insert Left',
|
||||
prefix: InsertLeftIcon(),
|
||||
select: () => {
|
||||
this.dataManager.insertColumn(columnIndex - 1);
|
||||
this.dataManager.insertColumn(
|
||||
columnIndex > 0 ? columnIndex - 1 : undefined
|
||||
);
|
||||
},
|
||||
}),
|
||||
menu.action({
|
||||
name: 'Insert Right',
|
||||
prefix: InsertRightIcon(),
|
||||
select: () => {
|
||||
this.dataManager.insertColumn(columnIndex + 1);
|
||||
this.dataManager.insertColumn(columnIndex);
|
||||
},
|
||||
}),
|
||||
menu.action({
|
||||
@@ -304,14 +306,16 @@ export class TableCell extends SignalWatcher(
|
||||
name: 'Insert Above',
|
||||
prefix: InsertAboveIcon(),
|
||||
select: () => {
|
||||
this.dataManager.insertRow(rowIndex - 1);
|
||||
this.dataManager.insertRow(
|
||||
rowIndex > 0 ? rowIndex - 1 : undefined
|
||||
);
|
||||
},
|
||||
}),
|
||||
menu.action({
|
||||
name: 'Insert Below',
|
||||
prefix: InsertBelowIcon(),
|
||||
select: () => {
|
||||
this.dataManager.insertRow(rowIndex + 1);
|
||||
this.dataManager.insertRow(rowIndex);
|
||||
},
|
||||
}),
|
||||
menu.action({
|
||||
@@ -454,6 +458,7 @@ export class TableCell extends SignalWatcher(
|
||||
};
|
||||
return html`<div class=${columnOptionsCellStyle}>
|
||||
<div
|
||||
data-testid="drag-column-handle"
|
||||
data-drag-column-id=${column.columnId}
|
||||
class=${classMap({
|
||||
[columnOptionsStyle]: true,
|
||||
@@ -478,6 +483,7 @@ export class TableCell extends SignalWatcher(
|
||||
};
|
||||
return html`<div class=${rowOptionsCellStyle}>
|
||||
<div
|
||||
data-testid="drag-row-handle"
|
||||
data-drag-row-id=${row.rowId}
|
||||
class=${classMap({
|
||||
[rowOptionsStyle]: true,
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"lit-html": "^3.2.1",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"remark-math": "^6.0.0",
|
||||
"shiki": "^2.0.0",
|
||||
"shiki": "^3.0.0",
|
||||
"yjs": "^13.6.21",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
@@ -55,7 +55,6 @@
|
||||
"./date-picker": "./src/date-picker/index.ts",
|
||||
"./drop-indicator": "./src/drop-indicator/index.ts",
|
||||
"./filterable-list": "./src/filterable-list/index.ts",
|
||||
"./virtual-keyboard": "./src/virtual-keyboard/index.ts",
|
||||
"./toggle-button": "./src/toggle-button/index.ts",
|
||||
"./toggle-switch": "./src/toggle-switch/index.ts",
|
||||
"./notification": "./src/notification/index.ts",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ColorScheme, Palette } from '@blocksuite/affine-model';
|
||||
import { resolveColor } from '@blocksuite/affine-model';
|
||||
import { DefaultTheme, resolveColor } from '@blocksuite/affine-model';
|
||||
import type { ColorEvent } from '@blocksuite/affine-shared/utils';
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import { html, LitElement } from 'lit';
|
||||
@@ -188,7 +188,7 @@ export class EdgelessColorPickerButton extends WithDisposable(LitElement) {
|
||||
accessor menuButton!: EditorMenuButton;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor palettes: Palette[] = [];
|
||||
accessor palettes: Palette[] = DefaultTheme.Palettes;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor pick!: (event: PickColorEvent) => void;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user