Compare commits

..

1 Commits

Author SHA1 Message Date
flrande e363ba5f4f feat(editor): support edgeless code block 2025-05-19 18:15:55 +08:00
1193 changed files with 17574 additions and 38548 deletions
-1
View File
@@ -6,7 +6,6 @@ yarn install
# Build Server Dependencies
yarn affine @affine/server-native build
yarn affine @affine/reader build
# Create database
yarn affine @affine/server prisma migrate reset -f
-15
View File
@@ -10,7 +10,6 @@ services:
environment:
DATABASE_URL: postgresql://affine:affine@db:5432/affine
REDIS_SERVER_HOST: redis
AFFINE_INDEXER_SEARCH_ENDPOINT: http://indexer:9308
db:
image: pgvector/pgvector:pg16
@@ -24,19 +23,5 @@ services:
redis:
image: redis
indexer:
image: manticoresearch/manticore:${MANTICORE_VERSION:-9.3.2}
ulimits:
nproc: 65535
nofile:
soft: 65535
hard: 65535
memlock:
soft: -1
hard: -1
volumes:
- manticoresearch_data:/var/lib/manticore
volumes:
postgres-data:
manticoresearch_data:
+1 -1
View File
@@ -12,4 +12,4 @@ DB_DATABASE_NAME=affine
# ELASTIC_PLATFORM=linux/arm64
# manticoresearch
MANTICORE_VERSION=9.3.2
MANTICORE_VERSION=9.2.14
+1 -4
View File
@@ -1,6 +1,3 @@
postgres
.env
compose.yml
certs/*
!certs/.gitkeep
nginx/conf.d/*
compose.yml
-27
View File
@@ -1,27 +0,0 @@
# Dev containers
## Develop with domain
> MacOs only, OrbStack only
### 1. Generate and install Root CA
```bash
# the root ca file will be located at `./.docker/dev/certs/ca`
yarn affine cert --install
```
### 2. Generate domain certs
```bash
# certificates will be located at `./.docker/dev/certs/${domain}`
yarn affine cert --domain dev.affine.fail
```
### 3. Enable dns and nginx service in compose.yml
### 4. Add custom dns server
```bash
echo "nameserver 127.0.0.1" | sudo tee /etc/resolver/dev.affine.fail
```
View File
@@ -0,0 +1,65 @@
name: affine_dev_services
services:
postgres:
env_file:
- .env
image: pgvector/pgvector:pg${DB_VERSION:-16}
ports:
- 5432:5432
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:latest
ports:
- 6379:6379
mailhog:
image: mailhog/mailhog:latest
ports:
- 1025:1025
- 8025:8025
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:${ELASTIC_VERSION:-9.0.1}${ELASTIC_VERSION_ARM64}
platform: ${ELASTIC_PLATFORM}
labels:
co.elastic.logs/module: elasticsearch
volumes:
- elasticsearch_data:/usr/share/elasticsearch/data
ports:
- ${ES_PORT:-9200}:9200
environment:
- node.name=es01
- cluster.name=affine-dev
- discovery.type=single-node
- bootstrap.memory_lock=true
- xpack.security.enabled=false
- xpack.security.http.ssl.enabled=false
- xpack.security.transport.ssl.enabled=false
- xpack.license.self_generated.type=basic
mem_limit: ${ES_MEM_LIMIT:-1073741824}
ulimits:
memlock:
soft: -1
hard: -1
healthcheck:
test:
[
"CMD-SHELL",
"curl -s http://localhost:9200 | grep -q 'affine-dev'",
]
interval: 10s
timeout: 10s
retries: 120
networks:
dev:
volumes:
postgres_data:
elasticsearch_data:
+2 -54
View File
@@ -26,7 +26,8 @@ services:
# https://manual.manticoresearch.com/Starting_the_server/Docker
manticoresearch:
image: manticoresearch/manticore:${MANTICORE_VERSION:-9.3.2}
image: manticoresearch/manticore:${MANTICORE_VERSION:-9.2.14}
restart: always
ports:
- 9308:9308
ulimits:
@@ -39,58 +40,6 @@ services:
hard: -1
volumes:
- manticoresearch_data:/var/lib/manticore
# elasticsearch:
# image: docker.elastic.co/elasticsearch/elasticsearch:${ELASTIC_VERSION:-9.0.1}${ELASTIC_VERSION_ARM64}
# platform: ${ELASTIC_PLATFORM}
# labels:
# co.elastic.logs/module: elasticsearch
# volumes:
# - elasticsearch_data:/usr/share/elasticsearch/data
# ports:
# - ${ES_PORT:-9200}:9200
# environment:
# - node.name=es01
# - cluster.name=affine-dev
# - discovery.type=single-node
# - bootstrap.memory_lock=true
# - xpack.security.enabled=false
# - xpack.security.http.ssl.enabled=false
# - xpack.security.transport.ssl.enabled=false
# - xpack.license.self_generated.type=basic
# mem_limit: ${ES_MEM_LIMIT:-1073741824}
# ulimits:
# memlock:
# soft: -1
# hard: -1
# healthcheck:
# test:
# [
# "CMD-SHELL",
# "curl -s http://localhost:9200 | grep -q 'affine-dev'",
# ]
# interval: 10s
# timeout: 10s
# retries: 120
# dns:
# image: strm/dnsmasq
# volumes:
# - ./dnsmasq.conf:/etc/dnsmasq.d/local.conf
# ports:
# - "53:53/udp"
# cap_add:
# - NET_ADMIN
# depends_on:
# - nginx
# nginx:
# image: nginx:alpine
# volumes:
# - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
# - ./nginx/conf.d:/etc/nginx/conf.d:ro
# - ./certs:/etc/nginx/certs:ro
# network_mode: host
networks:
dev:
@@ -98,4 +47,3 @@ networks:
volumes:
postgres_data:
manticoresearch_data:
elasticsearch_data:
-2
View File
@@ -1,2 +0,0 @@
log-queries
address=/dev.affine.fail/127.0.0.1
-28
View File
@@ -1,28 +0,0 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 512M;
server_names_hash_bucket_size 128;
ssi on;
gzip on;
include "/etc/nginx/conf.d/*";
}
-27
View File
@@ -1,27 +0,0 @@
server {
listen 80;
server_name DEV_DOMAIN;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
http2 on;
ssl_certificate /etc/nginx/certs/$host/crt;
ssl_certificate_key /etc/nginx/certs/$host/key;
server_name DEV_DOMAIN;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
resolver 127.0.0.1;
}
}
-25
View File
@@ -1,25 +0,0 @@
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
[req_distinguished_name]
countryName = Country Name (2 letter code)
countryName_default = US
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = MN
localityName = Locality Name (eg, city)
localityName_default = Minneapolis
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = Domain Control Validated
commonName = Internet Widgits Ltd
commonName_max = 64
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = DEV_DOMAIN
DNS.2 = *.DEV_DOMAIN
+5
View File
@@ -21,3 +21,8 @@ CONFIG_LOCATION=~/.affine/self-host/config
DB_USERNAME=affine
DB_PASSWORD=
DB_DATABASE=affine
# indexer search provider manticoresearch version
MANTICORE_VERSION=9.2.14
# position of the manticoresearch data to persist
MANTICORE_DATA_LOCATION=~/.affine/self-host/manticore
+27 -2
View File
@@ -10,6 +10,8 @@ services:
condition: service_healthy
postgres:
condition: service_healthy
indexer:
condition: service_healthy
affine_migration:
condition: service_completed_successfully
volumes:
@@ -21,7 +23,7 @@ services:
environment:
- REDIS_SERVER_HOST=redis
- DATABASE_URL=postgresql://${DB_USERNAME}:${DB_PASSWORD}@postgres:5432/${DB_DATABASE:-affine}
- AFFINE_INDEXER_ENABLED=false
- AFFINE_INDEXER_SEARCH_ENDPOINT=http://indexer:9308
restart: unless-stopped
affine_migration:
@@ -37,12 +39,14 @@ services:
environment:
- REDIS_SERVER_HOST=redis
- DATABASE_URL=postgresql://${DB_USERNAME}:${DB_PASSWORD}@postgres:5432/${DB_DATABASE:-affine}
- AFFINE_INDEXER_ENABLED=false
- AFFINE_INDEXER_SEARCH_ENDPOINT=http://indexer:9308
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
indexer:
condition: service_healthy
redis:
image: redis
@@ -74,3 +78,24 @@ services:
timeout: 5s
retries: 5
restart: unless-stopped
indexer:
image: manticoresearch/manticore:${MANTICORE_VERSION:-9.2.14}
container_name: affine_indexer
volumes:
- ${MANTICORE_DATA_LOCATION}:/var/lib/manticore
ulimits:
nproc: 65535
nofile:
soft: 65535
hard: 65535
memlock:
soft: -1
hard: -1
healthcheck:
test:
['CMD', 'wget', '-O-', 'http://127.0.0.1:9308']
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
+5 -119
View File
@@ -31,13 +31,9 @@
"properties": {
"queue": {
"type": "object",
"description": "The config for job queues\n@default {\"attempts\":5,\"backoff\":{\"type\":\"exponential\",\"delay\":1000},\"removeOnComplete\":true,\"removeOnFail\":{\"age\":86400,\"count\":500}}\n@link https://api.docs.bullmq.io/interfaces/v5.QueueOptions.html",
"description": "The config for job queues\n@default {\"attempts\":5,\"removeOnComplete\":true,\"removeOnFail\":{\"age\":86400,\"count\":500}}\n@link https://api.docs.bullmq.io/interfaces/v5.QueueOptions.html",
"default": {
"attempts": 5,
"backoff": {
"type": "exponential",
"delay": 1000
},
"removeOnComplete": true,
"removeOnFail": {
"age": 86400,
@@ -52,14 +48,14 @@
},
"queues.copilot": {
"type": "object",
"description": "The config for copilot job queue\n@default {\"concurrency\":10}",
"description": "The config for copilot job queue\n@default {\"concurrency\":1}",
"properties": {
"concurrency": {
"type": "number"
}
},
"default": {
"concurrency": 10
"concurrency": 1
}
},
"queues.doc": {
@@ -643,41 +639,6 @@
"apiKey": ""
}
},
"providers.geminiVertex": {
"type": "object",
"description": "The config for the google vertex provider.\n@default {}",
"properties": {
"location": {
"type": "string",
"description": "The location of the google vertex provider."
},
"project": {
"type": "string",
"description": "The project name of the google vertex provider."
},
"googleAuthOptions": {
"type": "object",
"description": "The google auth options for the google vertex provider.",
"properties": {
"credentials": {
"type": "object",
"description": "The credentials for the google vertex provider.",
"properties": {
"client_email": {
"type": "string",
"description": "The client email for the google vertex provider."
},
"private_key": {
"type": "string",
"description": "The private key for the google vertex provider."
}
}
}
}
}
},
"default": {}
},
"providers.perplexity": {
"type": "object",
"description": "The config for the perplexity provider.\n@default {\"apiKey\":\"\"}",
@@ -692,41 +653,6 @@
"apiKey": ""
}
},
"providers.anthropicVertex": {
"type": "object",
"description": "The config for the google vertex provider.\n@default {}",
"properties": {
"location": {
"type": "string",
"description": "The location of the google vertex provider."
},
"project": {
"type": "string",
"description": "The project name of the google vertex provider."
},
"googleAuthOptions": {
"type": "object",
"description": "The google auth options for the google vertex provider.",
"properties": {
"credentials": {
"type": "object",
"description": "The credentials for the google vertex provider.",
"properties": {
"client_email": {
"type": "string",
"description": "The client email for the google vertex provider."
},
"private_key": {
"type": "string",
"description": "The private key for the google vertex provider."
}
}
}
}
}
},
"default": {}
},
"unsplash": {
"type": "object",
"description": "The config for the unsplash key.\n@default {\"key\":\"\"}",
@@ -886,7 +812,7 @@
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable indexer plugin\n@default true\n@environment `AFFINE_INDEXER_ENABLED`",
"description": "Enable indexer plugin\n@default true",
"default": true
},
"provider.type": {
@@ -899,11 +825,6 @@
"description": "Indexer search service endpoint\n@default \"http://localhost:9308\"\n@environment `AFFINE_INDEXER_SEARCH_ENDPOINT`",
"default": "http://localhost:9308"
},
"provider.apiKey": {
"type": "string",
"description": "Indexer search service api key. Optional for elasticsearch\n@default \"\"\n@environment `AFFINE_INDEXER_SEARCH_API_KEY`\n@link https://www.elastic.co/guide/server/current/api-key.html",
"default": ""
},
"provider.username": {
"type": "string",
"description": "Indexer search service auth username, if not set, basic auth will be disabled. Optional for elasticsearch\n@default \"\"\n@environment `AFFINE_INDEXER_SEARCH_USERNAME`\n@link https://www.elastic.co/guide/en/elasticsearch/reference/current/http-clients.html",
@@ -913,11 +834,6 @@
"type": "string",
"description": "Indexer search service auth password, if not set, basic auth will be disabled. Optional for elasticsearch\n@default \"\"\n@environment `AFFINE_INDEXER_SEARCH_PASSWORD`",
"default": ""
},
"autoIndex.batchSize": {
"type": "number",
"description": "Number of workspaces automatically indexed per batch\n@default 10",
"default": 10
}
}
},
@@ -965,43 +881,13 @@
},
"providers.oidc": {
"type": "object",
"description": "OIDC OAuth provider config\n@default {\"clientId\":\"\",\"clientSecret\":\"\",\"issuer\":\"\",\"args\":{}}\n@link https://openid.net/specs/openid-connect-core-1_0.html",
"properties": {
"clientId": {
"type": "string"
},
"clientSecret": {
"type": "string"
},
"args": {
"type": "object"
}
},
"description": "OIDC OAuth provider config\n@default {\"clientId\":\"\",\"clientSecret\":\"\",\"issuer\":\"\",\"args\":{}}",
"default": {
"clientId": "",
"clientSecret": "",
"issuer": "",
"args": {}
}
},
"providers.apple": {
"type": "object",
"description": "Apple OAuth provider config\n@default {\"clientId\":\"\",\"clientSecret\":\"\"}\n@link https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/implementing_sign_in_with_apple_in_your_app",
"properties": {
"clientId": {
"type": "string"
},
"clientSecret": {
"type": "string"
},
"args": {
"type": "object"
}
},
"default": {
"clientId": "",
"clientSecret": ""
}
}
}
},
+4 -2
View File
@@ -18,7 +18,8 @@ const {
STATIC_IP_NAME,
AFFINE_INDEXER_SEARCH_PROVIDER,
AFFINE_INDEXER_SEARCH_ENDPOINT,
AFFINE_INDEXER_SEARCH_API_KEY,
AFFINE_INDEXER_SEARCH_USERNAME,
AFFINE_INDEXER_SEARCH_PASSWORD,
} = process.env;
const buildType = BUILD_TYPE || 'canary';
@@ -87,7 +88,8 @@ const createHelmCommand = ({ isDryRun }) => {
const indexerOptions = [
`--set-string global.indexer.provider="${AFFINE_INDEXER_SEARCH_PROVIDER}"`,
`--set-string global.indexer.endpoint="${AFFINE_INDEXER_SEARCH_ENDPOINT}"`,
`--set-string global.indexer.apiKey="${AFFINE_INDEXER_SEARCH_API_KEY}"`,
`--set-string global.indexer.username="${AFFINE_INDEXER_SEARCH_USERNAME}"`,
`--set-string global.indexer.password="${AFFINE_INDEXER_SEARCH_PASSWORD}"`,
];
const serviceAnnotations = [
`--set-json web.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
+7 -3
View File
@@ -29,7 +29,11 @@ runs:
- name: Import config
shell: bash
env:
DEFAULT_CONFIG: '{}'
run: |
printf '%s\n' "${SERVER_CONFIG:-$DEFAULT_CONFIG}" > ./packages/backend/server/config.json
printf '{"copilot":{"enabled":true,"providers.fal":{"apiKey":"%s"},"providers.gemini":{"apiKey":"%s"},"providers.openai":{"apiKey":"%s"},"providers.perplexity":{"apiKey":"%s"},"providers.anthropic":{"apiKey":"%s"},"exa":{"key":"%s"}}}' \
"$COPILOT_FAL_API_KEY" \
"$COPILOT_GOOGLE_API_KEY" \
"$COPILOT_OPENAI_API_KEY" \
"$COPILOT_PERPLEXITY_API_KEY" \
"$COPILOT_ANTHROPIC_API_KEY" \
"$COPILOT_EXA_API_KEY" > ./packages/backend/server/config.json
@@ -73,11 +73,13 @@ spec:
value: "{{ .Values.global.indexer.provider }}"
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
value: "{{ .Values.global.indexer.endpoint }}"
- name: AFFINE_INDEXER_SEARCH_API_KEY
- name: AFFINE_INDEXER_SEARCH_USERNAME
value: "{{ .Values.global.indexer.username }}"
- name: AFFINE_INDEXER_SEARCH_PASSWORD
valueFrom:
secretKeyRef:
name: indexer
key: indexer-apiKey
key: indexer-password
- name: AFFINE_SERVER_PORT
value: "{{ .Values.global.docService.port }}"
- name: AFFINE_SERVER_SUB_PATH
@@ -71,11 +71,13 @@ spec:
value: "{{ .Values.global.indexer.provider }}"
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
value: "{{ .Values.global.indexer.endpoint }}"
- name: AFFINE_INDEXER_SEARCH_API_KEY
- name: AFFINE_INDEXER_SEARCH_USERNAME
value: "{{ .Values.global.indexer.username }}"
- name: AFFINE_INDEXER_SEARCH_PASSWORD
valueFrom:
secretKeyRef:
name: indexer
key: indexer-apiKey
key: indexer-password
- name: AFFINE_SERVER_PORT
value: "{{ .Values.service.port }}"
- name: AFFINE_SERVER_SUB_PATH
@@ -48,11 +48,13 @@ spec:
value: "{{ .Values.global.indexer.provider }}"
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
value: "{{ .Values.global.indexer.endpoint }}"
- name: AFFINE_INDEXER_SEARCH_API_KEY
- name: AFFINE_INDEXER_SEARCH_USERNAME
value: "{{ .Values.global.indexer.username }}"
- name: AFFINE_INDEXER_SEARCH_PASSWORD
valueFrom:
secretKeyRef:
name: indexer
key: indexer-apiKey
key: indexer-password
resources:
requests:
cpu: '100m'
@@ -73,11 +73,13 @@ spec:
value: "{{ .Values.global.indexer.provider }}"
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
value: "{{ .Values.global.indexer.endpoint }}"
- name: AFFINE_INDEXER_SEARCH_API_KEY
- name: AFFINE_INDEXER_SEARCH_USERNAME
value: "{{ .Values.global.indexer.username }}"
- name: AFFINE_INDEXER_SEARCH_PASSWORD
valueFrom:
secretKeyRef:
name: indexer
key: indexer-apiKey
key: indexer-password
- name: AFFINE_SERVER_PORT
value: "{{ .Values.service.port }}"
- name: AFFINE_SERVER_SUB_PATH
@@ -73,11 +73,13 @@ spec:
value: "{{ .Values.global.indexer.provider }}"
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
value: "{{ .Values.global.indexer.endpoint }}"
- name: AFFINE_INDEXER_SEARCH_API_KEY
- name: AFFINE_INDEXER_SEARCH_USERNAME
value: "{{ .Values.global.indexer.username }}"
- name: AFFINE_INDEXER_SEARCH_PASSWORD
valueFrom:
secretKeyRef:
name: indexer
key: indexer-apiKey
key: indexer-password
- name: AFFINE_SERVER_PORT
value: "{{ .Values.service.port }}"
- name: AFFINE_SERVER_HOST
@@ -1,4 +1,4 @@
{{- if .Values.global.indexer.apiKey -}}
{{- if .Values.global.indexer.password -}}
apiVersion: v1
kind: Secret
metadata:
@@ -9,5 +9,5 @@ metadata:
"helm.sh/hook-delete-policy": before-hook-creation
type: Opaque
data:
indexer-apiKey: {{ .Values.global.indexer.apiKey | b64enc }}
indexer-password: {{ .Values.global.indexer.password | b64enc }}
{{- end }}
@@ -1,13 +0,0 @@
{{- if eq .Values.global.deployment.platform "gcp" -}}
apiVersion: monitoring.googleapis.com/v1
kind: PodMonitoring
metadata:
name: "{{ .Release.Name }}-monitoring"
spec:
selector:
matchLabels:
app.kubernetes.io/instance: {{ .Release.Name }}
endpoints:
- port: 9464
interval: 30s
{{- end }}
-1
View File
@@ -138,7 +138,6 @@ jobs:
uses: ./.github/actions/build-rust
env:
AFFINE_PRO_PUBLIC_KEY: ${{ secrets.AFFINE_PRO_PUBLIC_KEY }}
AFFINE_PRO_LICENSE_AES_KEY: ${{ secrets.AFFINE_PRO_LICENSE_AES_KEY }}
with:
target: ${{ matrix.targets.name }}
package: '@affine/server-native'
+19 -31
View File
@@ -125,7 +125,6 @@ jobs:
- name: Run BS Docs Build
run: |
yarn affine bs-docs build
git checkout packages/frontend/i18n/src/i18n-completenesses.json
git status --porcelain | grep . && {
echo "Run 'yarn typecheck && yarn affine bs-docs build' and make sure all changes are submitted"
exit 1
@@ -151,8 +150,7 @@ jobs:
- name: Clippy
run: |
rustup component add clippy
cargo clippy --workspace --exclude affine_server_native --all-targets --all-features -- -D warnings
cargo clippy -p affine_server_native --all-targets --all-features -- -D warnings
cargo clippy --all-targets --all-features -- -D warnings
check-git-status:
name: Check Git Status
@@ -185,7 +183,6 @@ jobs:
yarn affine gql build
yarn affine i18n build
yarn affine server genconfig
git checkout packages/frontend/i18n/src/i18n-completenesses.json
git status --porcelain | grep . && {
echo "Run 'yarn affine init && yarn affine gql build && yarn affine i18n build && yarn affine server genconfig' and make sure all changes are submitted"
exit 1
@@ -586,7 +583,7 @@ jobs:
- 1025:1025
- 8025:8025
indexer:
image: manticoresearch/manticore:9.3.2
image: manticoresearch/manticore:9.2.14
ports:
- 9308:9308
steps:
@@ -736,7 +733,7 @@ jobs:
ports:
- 6379:6379
indexer:
image: manticoresearch/manticore:9.3.2
image: manticoresearch/manticore:9.2.14
ports:
- 9308:9308
steps:
@@ -924,7 +921,7 @@ jobs:
uses: taiki-e/install-action@nextest
- name: Run tests
run: cargo nextest run --workspace --exclude affine_server_native --features use-as-lib --release --no-fail-fast
run: cargo nextest run --release --no-fail-fast
copilot-api-test:
name: Server Copilot Api Test
@@ -960,7 +957,7 @@ jobs:
- 1025:1025
- 8025:8025
indexer:
image: manticoresearch/manticore:9.3.2
image: manticoresearch/manticore:9.2.14
ports:
- 9308:9308
steps:
@@ -1002,7 +999,12 @@ jobs:
- name: Prepare Server Test Environment
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.apifilter.outputs.changed == 'true' }}
env:
SERVER_CONFIG: ${{ secrets.TEST_SERVER_CONFIG }}
COPILOT_OPENAI_API_KEY: ${{ secrets.COPILOT_OPENAI_API_KEY }}
COPILOT_GOOGLE_API_KEY: ${{ secrets.COPILOT_GOOGLE_API_KEY }}
COPILOT_FAL_API_KEY: ${{ secrets.COPILOT_FAL_API_KEY }}
COPILOT_PERPLEXITY_API_KEY: ${{ secrets.COPILOT_PERPLEXITY_API_KEY }}
COPILOT_ANTHROPIC_API_KEY: ${{ secrets.COPILOT_ANTHROPIC_API_KEY }}
COPILOT_EXA_API_KEY: ${{ secrets.COPILOT_EXA_API_KEY }}
uses: ./.github/actions/server-test-env
- name: Run server tests
@@ -1054,7 +1056,7 @@ jobs:
ports:
- 6379:6379
indexer:
image: manticoresearch/manticore:9.3.2
image: manticoresearch/manticore:9.2.14
ports:
- 9308:9308
steps:
@@ -1101,7 +1103,12 @@ jobs:
- name: Prepare Server Test Environment
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.e2efilter.outputs.changed == 'true' }}
env:
SERVER_CONFIG: ${{ secrets.TEST_SERVER_CONFIG }}
COPILOT_OPENAI_API_KEY: ${{ secrets.COPILOT_OPENAI_API_KEY }}
COPILOT_GOOGLE_API_KEY: ${{ secrets.COPILOT_GOOGLE_API_KEY }}
COPILOT_FAL_API_KEY: ${{ secrets.COPILOT_FAL_API_KEY }}
COPILOT_PERPLEXITY_API_KEY: ${{ secrets.COPILOT_PERPLEXITY_API_KEY }}
COPILOT_ANTHROPIC_API_KEY: ${{ secrets.COPILOT_ANTHROPIC_API_KEY }}
COPILOT_EXA_API_KEY: ${{ secrets.COPILOT_EXA_API_KEY }}
uses: ./.github/actions/server-test-env
- name: Run Copilot E2E Test ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
@@ -1176,7 +1183,7 @@ jobs:
- 1025:1025
- 8025:8025
indexer:
image: manticoresearch/manticore:9.3.2
image: manticoresearch/manticore:9.2.14
ports:
- 9308:9308
steps:
@@ -1348,13 +1355,6 @@ jobs:
target: x86_64-unknown-linux-gnu,
test: true,
}
- {
os: windows-latest,
platform: windows,
arch: x64,
target: x86_64-pc-windows-msvc,
test: true,
}
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
@@ -1395,18 +1395,6 @@ jobs:
HOIST_NODE_MODULES: 1
run: yarn affine @affine/electron package --platform=darwin --arch=arm64
- name: Make Bundle (Windows)
if: ${{ matrix.spec.target == 'x86_64-pc-windows-msvc' }}
shell: bash
env:
SKIP_BUNDLE: true
SKIP_WEB_BUILD: true
HOIST_NODE_MODULES: 1
run: |
rm -rf packages/frontend/apps/electron/node_modules/@affine/nbstore/node_modules/@blocksuite/affine/node_modules
rm -rf packages/frontend/apps/electron/node_modules/@affine/native/node_modules
yarn affine @affine/electron package --platform=win32 --arch=x64
- name: Make Bundle (Linux)
run: |
sudo add-apt-repository universe
+14 -4
View File
@@ -60,7 +60,7 @@ jobs:
- 1025:1025
- 8025:8025
indexer:
image: manticoresearch/manticore:9.3.2
image: manticoresearch/manticore:9.2.14
ports:
- 9308:9308
steps:
@@ -81,7 +81,12 @@ jobs:
- name: Prepare Server Test Environment
env:
SERVER_CONFIG: ${{ secrets.TEST_SERVER_CONFIG }}
COPILOT_OPENAI_API_KEY: ${{ secrets.COPILOT_OPENAI_API_KEY }}
COPILOT_FAL_API_KEY: ${{ secrets.COPILOT_FAL_API_KEY }}
COPILOT_GOOGLE_API_KEY: ${{ secrets.COPILOT_GOOGLE_API_KEY }}
COPILOT_PERPLEXITY_API_KEY: ${{ secrets.COPILOT_PERPLEXITY_API_KEY }}
COPILOT_ANTHROPIC_API_KEY: ${{ secrets.COPILOT_ANTHROPIC_API_KEY }}
COPILOT_EXA_API_KEY: ${{ secrets.COPILOT_EXA_API_KEY }}
uses: ./.github/actions/server-test-env
- name: Run server tests
@@ -130,7 +135,7 @@ jobs:
ports:
- 6379:6379
indexer:
image: manticoresearch/manticore:9.3.2
image: manticoresearch/manticore:9.2.14
ports:
- 9308:9308
steps:
@@ -151,7 +156,12 @@ jobs:
- name: Prepare Server Test Environment
env:
SERVER_CONFIG: ${{ secrets.TEST_SERVER_CONFIG }}
COPILOT_OPENAI_API_KEY: ${{ secrets.COPILOT_OPENAI_API_KEY }}
COPILOT_FAL_API_KEY: ${{ secrets.COPILOT_FAL_API_KEY }}
COPILOT_GOOGLE_API_KEY: ${{ secrets.COPILOT_GOOGLE_API_KEY }}
COPILOT_PERPLEXITY_API_KEY: ${{ secrets.COPILOT_PERPLEXITY_API_KEY }}
COPILOT_ANTHROPIC_API_KEY: ${{ secrets.COPILOT_ANTHROPIC_API_KEY }}
COPILOT_EXA_API_KEY: ${{ secrets.COPILOT_EXA_API_KEY }}
uses: ./.github/actions/server-test-env
- name: Run Copilot E2E Test ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
+2 -1
View File
@@ -105,7 +105,8 @@ jobs:
STATIC_IP_NAME: ${{ secrets.STATIC_IP_NAME }}
AFFINE_INDEXER_SEARCH_PROVIDER: ${{ secrets.AFFINE_INDEXER_SEARCH_PROVIDER }}
AFFINE_INDEXER_SEARCH_ENDPOINT: ${{ secrets.AFFINE_INDEXER_SEARCH_ENDPOINT }}
AFFINE_INDEXER_SEARCH_API_KEY: ${{ secrets.AFFINE_INDEXER_SEARCH_API_KEY }}
AFFINE_INDEXER_SEARCH_USERNAME: ${{ secrets.AFFINE_INDEXER_SEARCH_USERNAME }}
AFFINE_INDEXER_SEARCH_PASSWORD: ${{ secrets.AFFINE_INDEXER_SEARCH_PASSWORD }}
deploy-done:
needs:
+1 -1
View File
@@ -252,7 +252,7 @@ jobs:
shell: bash
# node_modules of nbstore is not needed for building, and it will make the build process out of memory
run: |
rm -rf packages/frontend/apps/electron/node_modules/@affine/nbstore/node_modules/@blocksuite/affine/node_modules
rm -rf packages/frontend/apps/electron/node_modules/@affine/nbstore/node_modules/@blocksuite
rm -rf packages/frontend/apps/electron/node_modules/@affine/native/node_modules
- name: package
+24 -2
View File
@@ -117,10 +117,31 @@ jobs:
name: android
path: packages/frontend/apps/android/dist
ios:
runs-on: ${{ github.ref_name == 'canary' && 'macos-latest' || 'blaze/macos-14' }}
determine-ios-runner:
runs-on: ubuntu-latest
needs:
- build-ios-web
outputs:
RUNNER: ${{ steps.runner.outputs.RUNNER }}
steps:
- name: Determine Runner
id: runner
# Randomly pick runner with 80% chance for blaze/macos-14 and 20% chance for namespace-profile-macos
# blaze/macos-14 is free but has limited concurrency
run: |
RANDOM_NUMBER=$(( $RANDOM % 100 + 1 ))
if [ $RANDOM_NUMBER -le 20 ]; then
echo "Selected namespace-profile-macos (20% probability)"
echo "RUNNER=namespace-profile-macos" >> $GITHUB_OUTPUT
else
echo "Selected blaze/macos-14 (80% probability)"
echo "RUNNER=blaze/macos-14" >> $GITHUB_OUTPUT
fi
ios:
runs-on: ${{ github.ref_name == 'canary' && 'macos-latest' || needs.determine-ios-runner.outputs.RUNNER }}
needs:
- determine-ios-runner
steps:
- uses: actions/checkout@v4
- name: Download mobile artifact
@@ -159,6 +180,7 @@ jobs:
- name: Testflight
if: ${{ env.BUILD_TYPE != 'stable' }}
working-directory: packages/frontend/apps/ios/App
continue-on-error: true
run: |
echo -n "${{ env.BUILD_PROVISION_PROFILE }}" | base64 --decode -o $PP_PATH
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
+1 -1
View File
@@ -1 +1 @@
22.16.0
22.15.1
Generated
+233 -208
View File
@@ -20,7 +20,8 @@ checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "adobe-cmap-parser"
version = "0.4.1"
source = "git+https://github.com/darkskygit/adobe-cmap-parser#610513ae6035c63eab69f33299b86c43693cabb4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae8abfa9a4688de8fc9f42b3f013b6fffec18ed8a554f5f113577e0b9b3212a3"
dependencies = [
"pom",
]
@@ -281,12 +282,12 @@ dependencies = [
[[package]]
name = "anstyle-wincon"
version = "3.0.8"
version = "3.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa"
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
dependencies = [
"anstyle",
"once_cell_polyfill",
"once_cell",
"windows-sys 0.59.0",
]
@@ -496,7 +497,7 @@ version = "0.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
dependencies = [
"bitflags 2.9.1",
"bitflags 2.9.0",
"cexpr",
"clang-sys",
"itertools 0.13.0",
@@ -546,9 +547,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.9.1"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
dependencies = [
"serde",
]
@@ -603,9 +604,9 @@ dependencies = [
[[package]]
name = "bpaf"
version = "0.9.20"
version = "0.9.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "473976d7a8620bb1e06dcdd184407c2363fe4fec8e983ee03ed9197222634a31"
checksum = "4848ed5727d39a7573551c205bcb1ccd88c8cad4ed2c80f62e2316f208196b8d"
[[package]]
name = "bstr"
@@ -697,9 +698,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.24"
version = "1.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7"
checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1"
dependencies = [
"shlex",
]
@@ -815,9 +816,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.39"
version = "4.5.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f"
checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
dependencies = [
"clap_builder",
"clap_derive",
@@ -825,9 +826,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.39"
version = "4.5.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51"
checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
dependencies = [
"anstream",
"anstyle",
@@ -901,9 +902,9 @@ dependencies = [
[[package]]
name = "core-foundation"
version = "0.10.1"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63"
dependencies = [
"core-foundation-sys",
"libc",
@@ -922,7 +923,7 @@ version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
dependencies = [
"bitflags 2.9.1",
"bitflags 2.9.0",
"core-foundation",
"core-graphics-types",
"foreign-types",
@@ -935,7 +936,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
dependencies = [
"bitflags 2.9.1",
"bitflags 2.9.0",
"core-foundation",
"libc",
]
@@ -1241,7 +1242,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
dependencies = [
"bitflags 2.9.1",
"bitflags 2.9.0",
"block2",
"libc",
"objc2",
@@ -1272,9 +1273,9 @@ dependencies = [
[[package]]
name = "docx-rust"
version = "0.1.10"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75463cd42bd579fdf76cf10ff1c4fb5988568c726f8ed4c2ed3921ddb6ef5c89"
checksum = "e6a8e9647d314f66d467a705715111d045955949b5dbcf2dc7aee89e078af83d"
dependencies = [
"derive_more",
"hard-xml",
@@ -1339,9 +1340,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.12"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
dependencies = [
"libc",
"windows-sys 0.59.0",
@@ -1595,16 +1596,15 @@ dependencies = [
[[package]]
name = "generator"
version = "0.8.5"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827"
checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd"
dependencies = [
"cc",
"cfg-if",
"libc",
"log",
"rustversion",
"windows 0.61.1",
"windows 0.58.0",
]
[[package]]
@@ -1690,9 +1690,9 @@ dependencies = [
[[package]]
name = "hard-xml"
version = "1.41.0"
version = "1.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b07b8ba970e18a03dbb79f6786b6e4d6f198a0ac839aa5182017001bb8dee17"
checksum = "18b4f798dc293307dc5445b47403a317adc8131782963f3ad4700a836bd26c93"
dependencies = [
"hard-xml-derive",
"jetscii",
@@ -1703,11 +1703,11 @@ dependencies = [
[[package]]
name = "hard-xml-derive"
version = "1.41.0"
version = "1.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c43e7c3212bd992c11b6b9796563388170950521ae8487f5cdf6f6e792f1c8"
checksum = "5ef0ad18601a1563729b311c9d49e5d46b1c987dc0353742cb8219f61f4339a6"
dependencies = [
"bitflags 2.9.1",
"bitflags 2.9.0",
"proc-macro2",
"quote",
"syn 1.0.109",
@@ -1826,7 +1826,7 @@ dependencies = [
"js-sys",
"log",
"wasm-bindgen",
"windows-core 0.57.0",
"windows-core 0.61.0",
]
[[package]]
@@ -1911,9 +1911,9 @@ checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
[[package]]
name = "icu_properties"
version = "2.0.1"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a"
dependencies = [
"displaydoc",
"icu_collections 2.0.0",
@@ -1927,9 +1927,9 @@ dependencies = [
[[package]]
name = "icu_properties_data"
version = "2.0.1"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04"
[[package]]
name = "icu_provider"
@@ -2050,14 +2050,15 @@ dependencies = [
[[package]]
name = "io-surface"
version = "0.16.1"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "554b8c5d64ec09a3a520fe58e4d48a73e00ff32899cdcbe32a4877afd4968b8e"
checksum = "8283575d5f0b2e7447ec0840363879d71c0fa325d4c699d5b45208ea4a51f45e"
dependencies = [
"cgl",
"core-foundation",
"core-foundation-sys",
"leaky-cow",
"libc",
]
[[package]]
@@ -2188,12 +2189,12 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "libloading"
version = "0.8.8"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c"
dependencies = [
"cfg-if",
"windows-targets 0.48.5",
"windows-targets 0.53.0",
]
[[package]]
@@ -2279,7 +2280,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59fa2559e99ba0f26a12458aabc754432c805bbb8cba516c427825a997af1fb7"
dependencies = [
"aes",
"bitflags 2.9.1",
"bitflags 2.9.0",
"cbc",
"ecb",
"encoding_rs",
@@ -2390,13 +2391,13 @@ dependencies = [
[[package]]
name = "mio"
version = "1.0.4"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -2410,12 +2411,12 @@ dependencies = [
[[package]]
name = "napi"
version = "3.0.0-beta.3"
version = "3.0.0-alpha.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a5c343e6e1fb57bf3ea3386638c4affb394ee932708128840a56aaac3d6a8ab"
checksum = "c857a2b38c994db8bec785554ab4216d45ad63469832070c86a992be0b5491ad"
dependencies = [
"anyhow",
"bitflags 2.9.1",
"bitflags 2.9.0",
"chrono",
"ctor",
"napi-build",
@@ -2426,18 +2427,17 @@ dependencies = [
[[package]]
name = "napi-build"
version = "2.2.0"
version = "2.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03acbfa4f156a32188bfa09b86dc11a431b5725253fc1fc6f6df5bed273382c4"
checksum = "e28acfa557c083f6e254a786e01ba253fc56f18ee000afcd4f79af735f73a6da"
[[package]]
name = "napi-derive"
version = "3.0.0-beta.3"
version = "3.0.0-alpha.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d23065ee795a4b1a8755fdf4a39c2a229679f01f923a8feea33f045d6d96cb"
checksum = "c7165d931d54f68115e651330d5fe0ae0081133d3f4ee3ab55b0b808f0c23f71"
dependencies = [
"convert_case 0.8.0",
"ctor",
"napi-derive-backend",
"proc-macro2",
"quote",
@@ -2446,9 +2446,9 @@ dependencies = [
[[package]]
name = "napi-derive-backend"
version = "2.0.0-beta.3"
version = "2.0.0-alpha.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "348aaac2c51b5d11cf90cf7670b470c7f4d1607d15c338efd4d3db361003e4f5"
checksum = "ce3f36354262054df8e1c3a73bdcd36ea13f130feb1e4d86b67cab9e10d6ef6d"
dependencies = [
"convert_case 0.8.0",
"proc-macro2",
@@ -2459,9 +2459,9 @@ dependencies = [
[[package]]
name = "napi-sys"
version = "3.0.0-alpha.2"
version = "3.0.0-alpha.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b443b980b2258dbaa31b99115e74da6c0866e537278309d566b4672a2f8df516"
checksum = "4cc061b99c514ad4b7abc99d4db1ca24b9542b7ff48b4760bd9f82b24611534d"
dependencies = [
"libloading",
]
@@ -2478,7 +2478,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
"bitflags 2.9.1",
"bitflags 2.9.0",
"cfg-if",
"cfg_aliases",
"libc",
@@ -2626,7 +2626,7 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
dependencies = [
"bitflags 2.9.1",
"bitflags 2.9.0",
"dispatch2",
"objc2",
]
@@ -2643,7 +2643,7 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c"
dependencies = [
"bitflags 2.9.1",
"bitflags 2.9.0",
"block2",
"libc",
"objc2",
@@ -2674,12 +2674,6 @@ version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "once_cell_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
[[package]]
name = "oorandom"
version = "11.1.5"
@@ -2743,9 +2737,9 @@ dependencies = [
[[package]]
name = "path-ext"
version = "0.1.2"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7603010004b5cdecf8006605bf7b6f07b0e59d3003010f52b767e91bf2582a45"
checksum = "0de7a86239a8b87b5094977b64893fcf0ed768072744dd4ee0df237686b2d815"
dependencies = [
"path-slash",
"walkdir",
@@ -2760,7 +2754,7 @@ checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42"
[[package]]
name = "pdf-extract"
version = "0.8.2"
source = "git+https://github.com/toeverything/pdf-extract?branch=darksky%2Fimprove-font-decoding#040751a61aba51e7a28217b758c18db4415c3ee4"
source = "git+https://github.com/toeverything/pdf-extract?branch=darksky%2Fimprove-font-decoding#e74beed894e1b8dc228c2bf078ed92814b27759f"
dependencies = [
"adobe-cmap-parser",
"cff-parser",
@@ -2769,7 +2763,6 @@ dependencies = [
"log",
"lopdf",
"postscript",
"rust-embed",
"type1-encoding-parser",
"unicode-normalization",
]
@@ -2950,12 +2943,9 @@ checksum = "60f6ce597ecdcc9a098e7fddacb1065093a3d66446fa16c675e7e71d1b5c28e6"
[[package]]
name = "postscript"
version = "0.19.0"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2238e788cf2c9b6edc23b83cf8ccdd4a6380cc9bf0598cc220fac42a55def6"
dependencies = [
"typeface",
]
checksum = "78451badbdaebaf17f053fd9152b3ffb33b516104eacb45e7864aaa9c712f306"
[[package]]
name = "potential_utf"
@@ -3016,7 +3006,7 @@ checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50"
dependencies = [
"bit-set 0.8.0",
"bit-vec 0.8.0",
"bitflags 2.9.1",
"bitflags 2.9.0",
"lazy_static",
"num-traits",
"rand 0.8.5",
@@ -3045,7 +3035,7 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0"
dependencies = [
"bitflags 2.9.1",
"bitflags 2.9.0",
"memchr",
"unicase",
]
@@ -3250,7 +3240,7 @@ version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
dependencies = [
"bitflags 2.9.1",
"bitflags 2.9.0",
]
[[package]]
@@ -3343,40 +3333,6 @@ dependencies = [
"realfft",
]
[[package]]
name = "rust-embed"
version = "8.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
"walkdir",
]
[[package]]
name = "rust-embed-impl"
version = "8.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c"
dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
"syn 2.0.101",
"walkdir",
]
[[package]]
name = "rust-embed-utils"
version = "8.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594"
dependencies = [
"sha2",
"walkdir",
]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
@@ -3425,7 +3381,7 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
dependencies = [
"bitflags 2.9.1",
"bitflags 2.9.0",
"errno",
"libc",
"linux-raw-sys",
@@ -3468,9 +3424,9 @@ dependencies = [
[[package]]
name = "rustversion"
version = "1.0.21"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
[[package]]
name = "rusty-fork"
@@ -3724,9 +3680,9 @@ dependencies = [
[[package]]
name = "socket2"
version = "0.5.10"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
dependencies = [
"libc",
"windows-sys 0.52.0",
@@ -3753,9 +3709,9 @@ dependencies = [
[[package]]
name = "sqlx"
version = "0.8.6"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc"
checksum = "f3c3a85280daca669cfd3bcb68a337882a8bc57ec882f72c5d13a430613a738e"
dependencies = [
"sqlx-core",
"sqlx-macros",
@@ -3766,9 +3722,9 @@ dependencies = [
[[package]]
name = "sqlx-core"
version = "0.8.6"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6"
checksum = "f743f2a3cea30a58cd479013f75550e879009e3a02f616f18ca699335aa248c3"
dependencies = [
"base64 0.22.1",
"bytes",
@@ -3803,9 +3759,9 @@ dependencies = [
[[package]]
name = "sqlx-macros"
version = "0.8.6"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d"
checksum = "7f4200e0fde19834956d4252347c12a083bdcb237d7a1a1446bffd8768417dce"
dependencies = [
"proc-macro2",
"quote",
@@ -3816,9 +3772,9 @@ dependencies = [
[[package]]
name = "sqlx-macros-core"
version = "0.8.6"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b"
checksum = "882ceaa29cade31beca7129b6beeb05737f44f82dbe2a9806ecea5a7093d00b7"
dependencies = [
"dotenvy",
"either",
@@ -3835,19 +3791,20 @@ dependencies = [
"sqlx-postgres",
"sqlx-sqlite",
"syn 2.0.101",
"tempfile",
"tokio",
"url",
]
[[package]]
name = "sqlx-mysql"
version = "0.8.6"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526"
checksum = "0afdd3aa7a629683c2d750c2df343025545087081ab5942593a5288855b1b7a7"
dependencies = [
"atoi",
"base64 0.22.1",
"bitflags 2.9.1",
"bitflags 2.9.0",
"byteorder",
"bytes",
"chrono",
@@ -3884,13 +3841,13 @@ dependencies = [
[[package]]
name = "sqlx-postgres"
version = "0.8.6"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46"
checksum = "a0bedbe1bbb5e2615ef347a5e9d8cd7680fb63e77d9dafc0f29be15e53f1ebe6"
dependencies = [
"atoi",
"base64 0.22.1",
"bitflags 2.9.1",
"bitflags 2.9.0",
"byteorder",
"chrono",
"crc",
@@ -3922,9 +3879,9 @@ dependencies = [
[[package]]
name = "sqlx-sqlite"
version = "0.8.6"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea"
checksum = "c26083e9a520e8eb87a06b12347679b142dc2ea29e6e409f805644a7a979a5bc"
dependencies = [
"atoi",
"chrono",
@@ -4438,9 +4395,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.45.1"
version = "1.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
dependencies = [
"backtrace",
"bytes",
@@ -4576,9 +4533,9 @@ dependencies = [
[[package]]
name = "tree-sitter"
version = "0.25.5"
version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac5fff5c47490dfdf473b5228039bfacad9d765d9b6939d26bf7cc064c1c7822"
checksum = "69aff09fea9a41fb061ae6b206cb87cac1b8db07df31be3ba271fbc26760f213"
dependencies = [
"cc",
"regex",
@@ -4713,12 +4670,6 @@ dependencies = [
"pom",
]
[[package]]
name = "typeface"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f6b49e025f4dc953a29b83e4f5a905089117d09fa53491015d7678951b8be1"
[[package]]
name = "typenum"
version = "1.18.0"
@@ -4941,13 +4892,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.17.0"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
dependencies = [
"getrandom 0.3.3",
"js-sys",
"wasm-bindgen",
]
[[package]]
@@ -5123,9 +5072,9 @@ dependencies = [
[[package]]
name = "weezl"
version = "0.1.10"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3"
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
[[package]]
name = "whoami"
@@ -5165,7 +5114,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -5186,24 +5135,12 @@ dependencies = [
[[package]]
name = "windows"
version = "0.61.1"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
dependencies = [
"windows-collections",
"windows-core 0.61.2",
"windows-future",
"windows-link",
"windows-numerics",
]
[[package]]
name = "windows-collections"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
dependencies = [
"windows-core 0.61.2",
"windows-core 0.58.0",
"windows-targets 0.52.6",
]
[[package]]
@@ -5220,26 +5157,28 @@ dependencies = [
[[package]]
name = "windows-core"
version = "0.61.2"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
dependencies = [
"windows-implement 0.58.0",
"windows-interface 0.58.0",
"windows-result 0.2.0",
"windows-strings 0.1.0",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-core"
version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [
"windows-implement 0.60.0",
"windows-interface 0.59.1",
"windows-link",
"windows-result 0.3.4",
"windows-strings",
]
[[package]]
name = "windows-future"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
dependencies = [
"windows-core 0.61.2",
"windows-link",
"windows-threading",
"windows-result 0.3.2",
"windows-strings 0.4.0",
]
[[package]]
@@ -5253,6 +5192,17 @@ dependencies = [
"syn 2.0.101",
]
[[package]]
name = "windows-implement"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
@@ -5275,6 +5225,17 @@ dependencies = [
"syn 2.0.101",
]
[[package]]
name = "windows-interface"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
@@ -5292,16 +5253,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
[[package]]
name = "windows-numerics"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
dependencies = [
"windows-core 0.61.2",
"windows-link",
]
[[package]]
name = "windows-result"
version = "0.1.2"
@@ -5313,18 +5264,37 @@ dependencies = [
[[package]]
name = "windows-result"
version = "0.3.4"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-result"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.4.2"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
dependencies = [
"windows-result 0.2.0",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-strings"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
dependencies = [
"windows-link",
]
@@ -5380,7 +5350,7 @@ dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
@@ -5388,12 +5358,19 @@ dependencies = [
]
[[package]]
name = "windows-threading"
version = "0.1.0"
name = "windows-targets"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b"
dependencies = [
"windows-link",
"windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0",
"windows_i686_gnullvm 0.53.0",
"windows_i686_msvc 0.53.0",
"windows_x86_64_gnu 0.53.0",
"windows_x86_64_gnullvm 0.53.0",
"windows_x86_64_msvc 0.53.0",
]
[[package]]
@@ -5408,6 +5385,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
@@ -5420,6 +5403,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
@@ -5432,12 +5421,24 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
@@ -5450,6 +5451,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
@@ -5462,6 +5469,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
@@ -5474,6 +5487,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
@@ -5486,6 +5505,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "winnow"
version = "0.7.10"
@@ -5501,7 +5526,7 @@ version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags 2.9.1",
"bitflags 2.9.0",
]
[[package]]
@@ -5570,7 +5595,7 @@ dependencies = [
"serde_json",
"smol_str",
"thiserror 2.0.12",
"yrs 0.23.4",
"yrs 0.23.1",
]
[[package]]
@@ -5601,7 +5626,7 @@ dependencies = [
"regex",
"y-octo",
"y-sync",
"yrs 0.23.4",
"yrs 0.23.1",
]
[[package]]
@@ -5679,9 +5704,9 @@ dependencies = [
[[package]]
name = "yrs"
version = "0.23.4"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb1d740a98b12112352f05ddfc06c1505b66fca116601f9388b84be45d21f84d"
checksum = "4a7cab84724ae7f361a8c92465f5160922cbb941a499e1a8cacd103351ab9c78"
dependencies = [
"arc-swap",
"async-lock",
@@ -5692,7 +5717,7 @@ dependencies = [
"serde_json",
"smallstr",
"smallvec",
"thiserror 2.0.12",
"thiserror 1.0.69",
]
[[package]]
+4 -4
View File
@@ -47,9 +47,9 @@ log = "0.4"
loom = { version = "0.7", features = ["checkpoint"] }
mimalloc = "0.1"
nanoid = "0.4"
napi = { version = "3.0.0-beta.3", features = ["async", "chrono_date", "error_anyhow", "napi9", "serde"] }
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-beta.3" }
napi-derive = { version = "3.0.0-alpha.28" }
nom = "8"
notify = { version = "8", features = ["serde"] }
objc2 = "0.6"
@@ -57,7 +57,7 @@ objc2-foundation = "0.3"
once_cell = "1"
ordered-float = "5"
parking_lot = "0.12"
path-ext = "0.1.2"
path-ext = "0.1.1"
pdf-extract = { git = "https://github.com/toeverything/pdf-extract", branch = "darksky/improve-font-decoding" }
phf = { version = "0.11", features = ["macros"] }
proptest = "1.3"
@@ -80,7 +80,7 @@ symphonia = { version = "0.5", features = ["all", "opt-simd"] }
text-splitter = "0.25"
thiserror = "2"
tiktoken-rs = "0.6"
tokio = "1.45"
tokio = "1.37"
tree-sitter = { version = "0.25" }
tree-sitter-c = { version = "0.23" }
tree-sitter-c-sharp = { version = "0.23" }
-4
View File
@@ -33,7 +33,6 @@
"@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-ext-loader": "workspace:*",
"@blocksuite/affine-foundation": "workspace:*",
"@blocksuite/affine-fragment-adapter-panel": "workspace:*",
"@blocksuite/affine-fragment-doc-title": "workspace:*",
"@blocksuite/affine-fragment-frame-panel": "workspace:*",
"@blocksuite/affine-fragment-outline": "workspace:*",
@@ -210,8 +209,6 @@
"./fragments/frame-panel/view": "./src/fragments/frame-panel/view.ts",
"./fragments/outline": "./src/fragments/outline/index.ts",
"./fragments/outline/view": "./src/fragments/outline/view.ts",
"./fragments/adapter-panel": "./src/fragments/adapter-panel/index.ts",
"./fragments/adapter-panel/view": "./src/fragments/adapter-panel/view.ts",
"./gfx/text": "./src/gfx/text/index.ts",
"./gfx/text/store": "./src/gfx/text/store.ts",
"./gfx/text/view": "./src/gfx/text/view.ts",
@@ -295,7 +292,6 @@
"version": "0.21.0",
"devDependencies": {
"@vanilla-extract/vite-plugin": "^5.0.0",
"msw": "^2.8.4",
"vitest": "3.1.3"
}
}
@@ -2697,335 +2697,4 @@ describe('html to snapshot', () => {
});
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
});
test('block level element in b should not be treated as inline', async () => {
const html = template(`<b><p><span>aaa</span></p></b>`);
const blockSnapshot: BlockSnapshot = {
type: 'block',
id: 'matchesReplaceMap[0]',
flavour: 'affine:note',
props: {
xywh: '[0,0,800,95]',
background: DefaultTheme.noteBackgrounColor,
index: 'a0',
hidden: false,
displayMode: NoteDisplayMode.DocAndEdgeless,
},
children: [
{
type: 'block',
id: 'matchesReplaceMap[1]',
flavour: 'affine:paragraph',
props: {
type: 'text',
text: {
'$blocksuite:internal:text$': true,
delta: [
{
insert: 'aaa',
},
],
},
},
children: [],
},
],
};
const htmlAdapter = new HtmlAdapter(createJob(), provider);
const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({
file: html,
});
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
});
describe('strong element', () => {
test('should not be bold when font-weight is normal', async () => {
const html = template(`<span style="font-weight: normal;">aaa</span>`);
const blockSnapshot: BlockSnapshot = {
type: 'block',
id: 'matchesReplaceMap[0]',
flavour: 'affine:note',
props: {
xywh: '[0,0,800,95]',
background: DefaultTheme.noteBackgrounColor,
index: 'a0',
hidden: false,
displayMode: NoteDisplayMode.DocAndEdgeless,
},
children: [
{
type: 'block',
id: 'matchesReplaceMap[1]',
flavour: 'affine:paragraph',
props: {
type: 'text',
text: {
'$blocksuite:internal:text$': true,
delta: [
{
insert: 'aaa',
},
],
},
},
children: [],
},
],
};
const htmlAdapter = new HtmlAdapter(createJob(), provider);
const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({
file: html,
});
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
});
test('should be bold when font-weight is bold or 500-900 ', async () => {
const html = template(
`<p><span style="font-weight: bold;">aaa</span><span style="font-weight: 100;">aaa</span><span style="font-weight: 500;">bbb</span><span style="font-weight: 200;">bbb</span><span style="font-weight: 600;">ccc</span><span style="font-weight: 300;">ccc</span><span style="font-weight: 700;">ddd</span></p>`
);
const blockSnapshot: BlockSnapshot = {
type: 'block',
id: 'matchesReplaceMap[0]',
flavour: 'affine:note',
props: {
xywh: '[0,0,800,95]',
background: DefaultTheme.noteBackgrounColor,
index: 'a0',
hidden: false,
displayMode: NoteDisplayMode.DocAndEdgeless,
},
children: [
{
type: 'block',
id: 'matchesReplaceMap[1]',
flavour: 'affine:paragraph',
props: {
type: 'text',
text: {
'$blocksuite:internal:text$': true,
delta: [
{
attributes: {
bold: true,
},
insert: 'aaa',
},
{
insert: 'aaa',
},
{
attributes: {
bold: true,
},
insert: 'bbb',
},
{
insert: 'bbb',
},
{
attributes: {
bold: true,
},
insert: 'ccc',
},
{
insert: 'ccc',
},
{
attributes: {
bold: true,
},
insert: 'ddd',
},
],
},
},
children: [],
},
],
};
const htmlAdapter = new HtmlAdapter(createJob(), provider);
const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({
file: html,
});
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
});
});
test('should be italic when tag is i or em or span with style font-style: italic', async () => {
const html = template(
`<p><i>aaa</i><span>aaa</span><em>bbb</em><span>bbb</span><span style="font-style: italic;">ccc</span></p>`
);
const blockSnapshot: BlockSnapshot = {
type: 'block',
id: 'matchesReplaceMap[0]',
flavour: 'affine:note',
props: {
xywh: '[0,0,800,95]',
background: DefaultTheme.noteBackgrounColor,
index: 'a0',
hidden: false,
displayMode: NoteDisplayMode.DocAndEdgeless,
},
children: [
{
type: 'block',
id: 'matchesReplaceMap[1]',
flavour: 'affine:paragraph',
props: {
type: 'text',
text: {
'$blocksuite:internal:text$': true,
delta: [
{
attributes: {
italic: true,
},
insert: 'aaa',
},
{
insert: 'aaa',
},
{
attributes: {
italic: true,
},
insert: 'bbb',
},
{
insert: 'bbb',
},
{
attributes: {
italic: true,
},
insert: 'ccc',
},
],
},
},
children: [],
},
],
};
const htmlAdapter = new HtmlAdapter(createJob(), provider);
const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({
file: html,
});
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
});
test('should be underline when tag is u or span with style text-decoration: underline', async () => {
const html = template(
`<p><u>aaa</u><span>aaa</span><span style="text-decoration: underline;">bbb</span></p>`
);
const blockSnapshot: BlockSnapshot = {
type: 'block',
id: 'matchesReplaceMap[0]',
flavour: 'affine:note',
props: {
xywh: '[0,0,800,95]',
background: DefaultTheme.noteBackgrounColor,
index: 'a0',
hidden: false,
displayMode: NoteDisplayMode.DocAndEdgeless,
},
children: [
{
type: 'block',
id: 'matchesReplaceMap[1]',
flavour: 'affine:paragraph',
props: {
type: 'text',
text: {
'$blocksuite:internal:text$': true,
delta: [
{
attributes: {
underline: true,
},
insert: 'aaa',
},
{
insert: 'aaa',
},
{
attributes: {
underline: true,
},
insert: 'bbb',
},
],
},
},
children: [],
},
],
};
const htmlAdapter = new HtmlAdapter(createJob(), provider);
const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({
file: html,
});
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
});
test('should be strike when tag is del or span with style text-decoration: line-through', async () => {
const html = template(
`<p><del>aaa</del><span>aaa</span><span style="text-decoration: line-through;">bbb</span></p>`
);
const blockSnapshot: BlockSnapshot = {
type: 'block',
id: 'matchesReplaceMap[0]',
flavour: 'affine:note',
props: {
xywh: '[0,0,800,95]',
background: DefaultTheme.noteBackgrounColor,
index: 'a0',
hidden: false,
displayMode: NoteDisplayMode.DocAndEdgeless,
},
children: [
{
type: 'block',
id: 'matchesReplaceMap[1]',
flavour: 'affine:paragraph',
props: {
type: 'text',
text: {
'$blocksuite:internal:text$': true,
delta: [
{
attributes: {
strike: true,
},
insert: 'aaa',
},
{
insert: 'aaa',
},
{
attributes: {
strike: true,
},
insert: 'bbb',
},
],
},
},
children: [],
},
],
};
const htmlAdapter = new HtmlAdapter(createJob(), provider);
const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({
file: html,
});
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
});
});
@@ -4393,61 +4393,6 @@ hhh
},
children: [],
},
{
type: 'block',
id: 'matchesReplaceMap[2]',
flavour: 'affine:paragraph',
props: {
type: 'h6',
text: {
'$blocksuite:internal:text$': true,
delta: [
{
insert: 'Sources',
},
],
},
collapsed: true,
},
children: [],
},
{
type: 'block',
id: 'matchesReplaceMap[3]',
flavour: 'affine:bookmark',
props: {
style: 'citation',
url,
title,
description,
icon: favicon,
footnoteIdentifier: '1',
},
children: [],
},
{
type: 'block',
id: 'matchesReplaceMap[4]',
flavour: 'affine:embed-linked-doc',
props: {
style: 'citation',
pageId: 'deadbeef',
footnoteIdentifier: '2',
},
children: [],
},
{
type: 'block',
id: 'matchesReplaceMap[5]',
flavour: 'affine:attachment',
props: {
name: 'test.txt',
sourceId: 'abcdefg',
footnoteIdentifier: '3',
style: 'citation',
},
children: [],
},
],
};
@@ -4472,101 +4417,6 @@ hhh
});
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
});
test('should handle footnote reference with url prefix', async () => {
const blockSnapshot = {
type: 'block',
id: 'matchesReplaceMap[0]',
flavour: 'affine:note',
props: {
xywh: '[0,0,800,95]',
background: DefaultTheme.noteBackgrounColor,
index: 'a0',
hidden: false,
displayMode: NoteDisplayMode.DocAndEdgeless,
},
children: [
{
type: 'block',
id: 'matchesReplaceMap[1]',
flavour: 'affine:paragraph',
props: {
type: 'text',
text: {
'$blocksuite:internal:text$': true,
delta: [
{
insert: 'https://example.com',
attributes: {
link: 'https://example.com',
},
},
{
insert: ' ',
},
{
insert: ' ',
attributes: {
footnote: {
label: '1',
reference: {
type: 'url',
url,
favicon,
title,
description,
},
},
},
},
],
},
},
children: [],
},
{
type: 'block',
id: 'matchesReplaceMap[2]',
flavour: 'affine:paragraph',
props: {
type: 'h6',
text: {
'$blocksuite:internal:text$': true,
delta: [
{
insert: 'Sources',
},
],
},
collapsed: true,
},
children: [],
},
{
type: 'block',
id: 'matchesReplaceMap[3]',
flavour: 'affine:bookmark',
props: {
style: 'citation',
url,
title,
description,
icon: favicon,
footnoteIdentifier: '1',
},
children: [],
},
],
};
const markdown = `https://example.com[^1]\n\n[^1]: {"type":"url","url":"${url}","favicon":"${favicon}","title":"${title}","description":"${description}"}\n`;
const mdAdapter = new MarkdownAdapter(createJob(), provider);
const rawBlockSnapshot = await mdAdapter.toBlockSnapshot({
file: markdown,
});
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
});
});
test('should not wrap url with angle brackets if it is not a url', async () => {
@@ -1,14 +1,11 @@
import { DefaultTheme, NoteDisplayMode } from '@blocksuite/affine-model';
import { NotionHtmlAdapter } from '@blocksuite/affine-shared/adapters';
import { DEFAULT_IMAGE_PROXY_ENDPOINT } from '@blocksuite/affine-shared/consts';
import {
AssetsManager,
type BlockSnapshot,
MemoryBlobCRUD,
} from '@blocksuite/store';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
import { afterAll, afterEach, beforeAll, describe, expect, test } from 'vitest';
import { describe, expect, test } from 'vitest';
import { createJob } from '../utils/create-job.js';
import { getProvider } from '../utils/get-provider.js';
@@ -1198,71 +1195,43 @@ describe('notion html to snapshot', () => {
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
});
describe('image', () => {
const originalUrl =
'https://raw.githubusercontent.com/toeverything/blocksuite/master/assets/logo.svg';
test('image', async () => {
const html = `<div class="page-body">
<figure id="ed3d2ae9-62f5-433a-9049-9ddbd1c81ac5" class="image"><a
href="https://raw.githubusercontent.com/toeverything/blocksuite/master/assets/logo.svg"><img src="https://raw.githubusercontent.com/toeverything/blocksuite/master/assets/logo.svg" /></a>
</figure>
</div>`;
const imageProxy = DEFAULT_IMAGE_PROXY_ENDPOINT;
const imageUrl = `${imageProxy}?url=${encodeURIComponent(originalUrl)}`;
// Mock the image request
const imageRequestHandlers = [
http.get(imageUrl.toString(), async () => {
// Return a mock image blob
const mockImageBlob = new Blob(['mock image data'], {
type: 'image/svg+xml',
});
return new HttpResponse(mockImageBlob, {
headers: {
'Content-Type': 'image/svg+xml',
const blockSnapshot: BlockSnapshot = {
type: 'block',
id: 'matchesReplaceMap[0]',
flavour: 'affine:note',
props: {
xywh: '[0,0,800,95]',
background: DefaultTheme.noteBackgrounColor,
index: 'a0',
hidden: false,
displayMode: NoteDisplayMode.DocAndEdgeless,
},
children: [
{
type: 'block',
id: 'matchesReplaceMap[1]',
flavour: 'affine:image',
props: {
sourceId: 'matchesReplaceMap[2]',
},
});
}),
];
const server = setupServer(...imageRequestHandlers);
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterAll(() => server.close());
afterEach(() => server.resetHandlers());
test('network image resource', async () => {
const html = `<div class="page-body">
<figure id="ed3d2ae9-62f5-433a-9049-9ddbd1c81ac5" class="image"><a
href="${originalUrl}"><img src="${originalUrl}" /></a>
</figure>
</div>`;
const blockSnapshot: BlockSnapshot = {
type: 'block',
id: 'matchesReplaceMap[0]',
flavour: 'affine:note',
props: {
xywh: '[0,0,800,95]',
background: DefaultTheme.noteBackgrounColor,
index: 'a0',
hidden: false,
displayMode: NoteDisplayMode.DocAndEdgeless,
children: [],
},
children: [
{
type: 'block',
id: 'matchesReplaceMap[1]',
flavour: 'affine:image',
props: {
sourceId: 'matchesReplaceMap[2]',
},
children: [],
},
],
};
],
};
const adapter = new NotionHtmlAdapter(createJob(), provider);
const rawBlockSnapshot = await adapter.toBlockSnapshot({
file: html,
assets: new AssetsManager({ blob: new MemoryBlobCRUD() }),
});
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
const adapter = new NotionHtmlAdapter(createJob(), provider);
const rawBlockSnapshot = await adapter.toBlockSnapshot({
file: html,
assets: new AssetsManager({ blob: new MemoryBlobCRUD() }),
});
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
});
test('bookmark', async () => {
+1 -4
View File
@@ -19,7 +19,6 @@ import { SurfaceViewExtension } from '@blocksuite/affine-block-surface/view';
import { SurfaceRefViewExtension } from '@blocksuite/affine-block-surface-ref/view';
import { TableViewExtension } from '@blocksuite/affine-block-table/view';
import { FoundationViewExtension } from '@blocksuite/affine-foundation/view';
import { AdapterPanelViewExtension } from '@blocksuite/affine-fragment-adapter-panel/view';
import { DocTitleViewExtension } from '@blocksuite/affine-fragment-doc-title/view';
import { FramePanelViewExtension } from '@blocksuite/affine-fragment-frame-panel/view';
import { OutlineViewExtension } from '@blocksuite/affine-fragment-outline/view';
@@ -103,9 +102,9 @@ export function getInternalViewExtensions() {
InlinePresetViewExtension,
// Widget
// order will affect the z-index of the widget
DragHandleViewExtension,
EdgelessAutoConnectViewExtension,
EdgelessToolbarViewExtension,
FrameTitleViewExtension,
KeyboardToolbarViewExtension,
LinkedDocViewExtension,
@@ -119,12 +118,10 @@ export function getInternalViewExtensions() {
EdgelessSelectedRectViewExtension,
EdgelessDraggingAreaViewExtension,
NoteSlicerViewExtension,
EdgelessToolbarViewExtension,
// Fragment
DocTitleViewExtension,
FramePanelViewExtension,
OutlineViewExtension,
AdapterPanelViewExtension,
];
}
@@ -1 +0,0 @@
export * from '@blocksuite/affine-fragment-adapter-panel';
@@ -1 +0,0 @@
export * from '@blocksuite/affine-fragment-adapter-panel/view';
-1
View File
@@ -30,7 +30,6 @@
{ "path": "../components" },
{ "path": "../ext-loader" },
{ "path": "../foundation" },
{ "path": "../fragments/adapter-panel" },
{ "path": "../fragments/doc-title" },
{ "path": "../fragments/frame-panel" },
{ "path": "../fragments/outline" },
@@ -23,8 +23,8 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.15",
"file-type": "^21.0.0",
"@toeverything/theme": "^1.1.14",
"file-type": "^20.0.0",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
@@ -10,6 +10,7 @@ import {
isFootnoteDefinitionNode,
type MarkdownAST,
} from '@blocksuite/affine-shared/adapters';
import { FeatureFlagService } from '@blocksuite/affine-shared/services';
import { nanoid } from '@blocksuite/store';
const isAttachmentFootnoteDefinitionNode = (node: MarkdownAST) => {
@@ -35,7 +36,15 @@ export const attachmentBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher
fromMatch: o => o.node.flavour === AttachmentBlockSchema.model.flavour,
toBlockSnapshot: {
enter: (o, context) => {
if (!isFootnoteDefinitionNode(o.node)) {
const { provider } = context;
let enableCitation = false;
try {
const featureFlagService = provider?.get(FeatureFlagService);
enableCitation = !!featureFlagService?.getFlag('enable_citation');
} catch {
enableCitation = false;
}
if (!isFootnoteDefinitionNode(o.node) || !enableCitation) {
return;
}
@@ -64,7 +73,6 @@ export const attachmentBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher
name: fileName,
sourceId: blobId,
footnoteIdentifier,
style: 'citation',
},
children: [],
},
@@ -4,7 +4,7 @@ import {
} from '@blocksuite/affine-components/caption';
import {
getAttachmentFileIcon,
LoadingIcon,
getLoadingIconWith,
} from '@blocksuite/affine-components/icons';
import { Peekable } from '@blocksuite/affine-components/peek';
import {
@@ -20,8 +20,9 @@ import {
DocModeProvider,
FileSizeLimitProvider,
TelemetryProvider,
ThemeProvider,
} from '@blocksuite/affine-shared/services';
import { formatSize } from '@blocksuite/affine-shared/utils';
import { humanFileSize } from '@blocksuite/affine-shared/utils';
import {
AttachmentIcon,
ResetIcon,
@@ -64,11 +65,6 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
return this.resourceController.blobUrl$.value;
}
get filetype() {
const name = this.model.props.name$.value;
return name.split('.').pop() ?? '';
}
protected containerStyleMap = styleMap({
position: 'relative',
width: '100%',
@@ -217,23 +213,13 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
);
};
protected renderNormalButton = (needUpload: boolean) => {
const label = needUpload ? 'retry' : 'reload';
const run = async () => {
if (needUpload) {
await this.resourceController.upload();
return;
}
this.refreshData();
};
protected renderReloadButton = () => {
return html`
<button
class="affine-attachment-content-button"
@click=${(event: MouseEvent) => {
event.stopPropagation();
run().catch(console.error);
this.refreshData();
{
const mode =
@@ -245,28 +231,21 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
segment,
page: `${segment} editor`,
module: 'attachment',
control: label,
control: 'reload',
category: 'card',
type: this.filetype,
type: this.model.props.name.split('.').pop() ?? '',
});
}
}}
>
${ResetIcon()} ${label}
${ResetIcon()} Reload
</button>
`;
};
protected renderWithHorizontal(
classInfo: ClassInfo,
{
icon,
title,
description,
kind,
state,
needUpload,
}: AttachmentResolvedStateInfo
{ icon, title, description, kind, state }: AttachmentResolvedStateInfo
) {
return html`
<div class=${classMap(classInfo)}>
@@ -283,7 +262,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
${description}
</div>
${choose(state, [
['error', () => this.renderNormalButton(needUpload)],
['error', this.renderReloadButton],
['error:oversize', this.renderUpgradeButton],
])}
</div>
@@ -296,14 +275,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
protected renderWithVertical(
classInfo: ClassInfo,
{
icon,
title,
description,
kind,
state,
needUpload,
}: AttachmentResolvedStateInfo
{ icon, title, description, kind, state }: AttachmentResolvedStateInfo
) {
return html`
<div class=${classMap(classInfo)}>
@@ -323,7 +295,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
<div class="affine-attachment-banner">
${kind}
${choose(state, [
['error', () => this.renderNormalButton(needUpload)],
['error', this.renderReloadButton],
['error:oversize', this.renderUpgradeButton],
])}
</div>
@@ -332,16 +304,19 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
}
protected resolvedState$ = computed<AttachmentResolvedStateInfo>(() => {
const theme = this.std.get(ThemeProvider).theme$.value;
const loadingIcon = getLoadingIconWith(theme);
const size = this.model.props.size;
const name = this.model.props.name$.value;
const kind = getAttachmentFileIcon(this.filetype);
const kind = getAttachmentFileIcon(name.split('.').pop() ?? '');
const resolvedState = this.resourceController.resolveStateWith({
loadingIcon: LoadingIcon(),
loadingIcon,
errorIcon: WarningIcon(),
icon: AttachmentIcon(),
title: name,
description: formatSize(size),
description: humanFileSize(size),
});
return { ...resolvedState, kind };
@@ -388,16 +363,11 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
const message = resolvedState.description;
if (!message) return null;
const needUpload = resolvedState.needUpload;
const action = () =>
needUpload ? this.resourceController.upload() : this.reload();
return html`
<affine-resource-status
class="affine-attachment-embed-status"
.message=${message}
.needUpload=${needUpload}
.action=${action}
.reload=${() => this.reload()}
></affine-resource-status>
`;
})}
@@ -406,10 +376,10 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
private readonly _renderCitation = () => {
const { name, footnoteIdentifier } = this.model.props;
const icon = getAttachmentFileIcon(this.filetype);
const fileType = name.split('.').pop() ?? '';
const fileTypeIcon = getAttachmentFileIcon(fileType);
return html`<affine-citation-card
.icon=${icon}
.icon=${fileTypeIcon}
.citationTitle=${name}
.citationIdentifier=${footnoteIdentifier}
.active=${this.selected$.value}
@@ -1,4 +1,4 @@
import { openSingleFileWith } from '@blocksuite/affine-shared/utils';
import { openFileOrFiles } from '@blocksuite/affine-shared/utils';
import { type SlashMenuConfig } from '@blocksuite/affine-widget-slash-menu';
import { ExportToPdfIcon, FileIcon } from '@blocksuite/icons/lit';
@@ -21,7 +21,7 @@ export const attachmentSlashMenuConfig: SlashMenuConfig = {
model.store.schema.flavourSchemaMap.has('affine:attachment'),
action: ({ std, model }) => {
(async () => {
const file = await openSingleFileWith();
const file = await openFileOrFiles();
if (!file) return;
await addSiblingAttachmentBlocks(std, [file], model);
@@ -44,7 +44,7 @@ export const attachmentSlashMenuConfig: SlashMenuConfig = {
model.store.schema.flavourSchemaMap.has('affine:attachment'),
action: ({ std, model }) => {
(async () => {
const file = await openSingleFileWith();
const file = await openFileOrFiles();
if (!file) return;
await addSiblingAttachmentBlocks(std, [file], model);
@@ -47,10 +47,11 @@ export const styles = css`
.affine-attachment-content-title-icon {
display: flex;
width: 16px;
height: 16px;
align-items: center;
justify-content: center;
color: var(--affine-text-primary-color);
font-size: 16px;
}
.affine-attachment-content-title-text {
@@ -91,7 +92,6 @@ export const styles = css`
font-size: var(--affine-font-xs);
font-style: normal;
font-weight: 500;
text-transform: capitalize;
line-height: 20px;
svg {
@@ -107,7 +107,7 @@ export const styles = css`
.affine-attachment-card.loading {
.affine-attachment-content-title-text {
color: ${unsafeCSSVarV2('text/placeholder')};
color: var(--affine-placeholder-color);
}
}
@@ -13,7 +13,7 @@ import {
FileSizeLimitProvider,
TelemetryProvider,
} from '@blocksuite/affine-shared/services';
import { formatSize } from '@blocksuite/affine-shared/utils';
import { humanFileSize } from '@blocksuite/affine-shared/utils';
import { Bound, type IVec, Vec } from '@blocksuite/global/gfx';
import type { BlockStdScope } from '@blocksuite/std';
import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
@@ -93,7 +93,7 @@ function hasExceeded(
const exceeded = files.some(file => file.size > maxFileSize);
if (exceeded) {
const size = formatSize(maxFileSize);
const size = humanFileSize(maxFileSize, true, 0);
toast(std.host, `You can only upload files less than ${size}`);
}
@@ -24,7 +24,7 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.15",
"@toeverything/theme": "^1.1.14",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
@@ -10,6 +10,7 @@ import {
isFootnoteDefinitionNode,
type MarkdownAST,
} from '@blocksuite/affine-shared/adapters';
import { FeatureFlagService } from '@blocksuite/affine-shared/services';
import { nanoid } from '@blocksuite/store';
const isUrlFootnoteDefinitionNode = (node: MarkdownAST) => {
@@ -32,7 +33,15 @@ export const bookmarkBlockMarkdownAdapterMatcher =
toMatch: o => isUrlFootnoteDefinitionNode(o.node),
toBlockSnapshot: {
enter: (o, context) => {
if (!isFootnoteDefinitionNode(o.node)) {
const { provider } = context;
let enableCitation = false;
try {
const featureFlagService = provider?.get(FeatureFlagService);
enableCitation = !!featureFlagService?.getFlag('enable_citation');
} catch {
enableCitation = false;
}
if (!isFootnoteDefinitionNode(o.node) || !enableCitation) {
return;
}
@@ -11,7 +11,6 @@ import {
DocModeProvider,
LinkPreviewServiceIdentifier,
} from '@blocksuite/affine-shared/services';
import { normalizeUrl } from '@blocksuite/affine-shared/utils';
import { BlockSelection } from '@blocksuite/std';
import { computed, type ReadonlySignal, signal } from '@preact/signals-core';
import { html } from 'lit';
@@ -100,12 +99,12 @@ export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBloc
selectionManager.setGroup('note', [blockSelection]);
};
get link() {
return normalizeUrl(this.model.props.url);
}
open = () => {
window.open(this.link, '_blank');
let link = this.model.props.url;
if (!link.match(/^[a-zA-Z]+:\/\//)) {
link = 'https://' + link;
}
window.open(link, '_blank');
};
refreshData = () => {
@@ -29,15 +29,6 @@ export class BookmarkEdgelessBlockComponent extends toGfxBlockComponent(
};
}
override connectedCallback(): void {
super.connectedCallback();
this.disposables.add(
this.gfx.selection.slots.updated.subscribe(() => {
this.requestUpdate();
})
);
}
override renderGfxBlock() {
const style = this.model.props.style$.value;
const width = EMBED_CARD_WIDTH[style];
@@ -45,14 +36,12 @@ export class BookmarkEdgelessBlockComponent extends toGfxBlockComponent(
const bound = this.model.elementBound;
const scaleX = bound.w / width;
const scaleY = bound.h / height;
const isSelected = this.gfx.selection.has(this.model.id);
this.containerStyleMap = styleMap({
width: `100%`,
height: `100%`,
transform: `scale(${scaleX}, ${scaleY})`,
transformOrigin: '0 0',
pointerEvents: isSelected ? 'auto' : 'none',
});
return this.renderPageContent();
@@ -1,5 +1,5 @@
import { getEmbedCardIcons } from '@blocksuite/affine-block-embed';
import { LoadingIcon, WebIcon16 } from '@blocksuite/affine-components/icons';
import { WebIcon16 } from '@blocksuite/affine-components/icons';
import { ImageProxyService } from '@blocksuite/affine-shared/adapters';
import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { getHostName } from '@blocksuite/affine-shared/utils';
@@ -60,11 +60,11 @@ export class BookmarkCard extends SignalWatcher(
: title;
const theme = this.bookmark.std.get(ThemeProvider).theme;
const { EmbedCardBannerIcon } = getEmbedCardIcons(theme);
const { LoadingIcon, EmbedCardBannerIcon } = getEmbedCardIcons(theme);
const imageProxyService = this.bookmark.store.get(ImageProxyService);
const titleIcon = this.loading
? LoadingIcon()
? LoadingIcon
: icon
? html`<img src=${imageProxyService.buildUrl(icon)} alt="icon" />`
: WebIcon16;
@@ -407,7 +407,7 @@ const builtinSurfaceToolbarConfig = {
if (options?.viewType !== 'embed') return;
const { flavour, styles } = options;
let style: EmbedCardStyle = model.props.style;
let { style } = model.props;
if (!styles.includes(style)) {
style = styles[0];
@@ -482,26 +482,24 @@ const builtinSurfaceToolbarConfig = {
} satisfies ToolbarActionGroup<ToolbarAction>,
{
id: 'b.style',
actions: (
[
{
id: 'horizontal',
label: 'Large horizontal style',
},
{
id: 'list',
label: 'Small horizontal style',
},
{
id: 'vertical',
label: 'Large vertical style',
},
{
id: 'cube',
label: 'Small vertical style',
},
] as const
).filter(action => BookmarkStyles.includes(action.id)),
actions: [
{
id: 'horizontal',
label: 'Large horizontal style',
},
{
id: 'list',
label: 'Small horizontal style',
},
{
id: 'vertical',
label: 'Large vertical style',
},
{
id: 'cube',
label: 'Small vertical style',
},
].filter(action => BookmarkStyles.includes(action.id as EmbedCardStyle)),
content(ctx) {
const model = ctx.getCurrentModelByType(BookmarkBlockModel);
if (!model) return null;
@@ -25,7 +25,7 @@
"@floating-ui/dom": "^1.6.10",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.15",
"@toeverything/theme": "^1.1.14",
"@types/mdast": "^4.0.4",
"emoji-mart": "^5.6.0",
"lit": "^3.2.0",
@@ -12,7 +12,6 @@ import type { BlockComponent } from '@blocksuite/std';
import { flip, offset } from '@floating-ui/dom';
import { css, html } from 'lit';
import { query } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js';
export class CalloutBlockComponent extends CaptionedBlockComponent<CalloutBlockModel> {
static override styles = css`
:host {
@@ -110,18 +109,14 @@ export class CalloutBlockComponent extends CaptionedBlockComponent<CalloutBlockM
}
override renderBlock() {
const emoji = this.model.props.emoji$.value;
return html`
<div class="affine-callout-block-container">
<div
@click=${this._toggleEmojiMenu}
contenteditable="false"
class="affine-callout-emoji-container"
style=${styleMap({
display: emoji.length === 0 ? 'none' : undefined,
})}
>
<span class="affine-callout-emoji">${emoji}</span>
<span class="affine-callout-emoji">${this.model.props.emoji$}</span>
</div>
<div class="affine-callout-children">
${this.renderChildren(this.model)}
+1 -1
View File
@@ -27,7 +27,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.15",
"@toeverything/theme": "^1.1.14",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
@@ -48,11 +48,7 @@ const codePreprocessor: MarkdownAdapterPreprocessor = {
}
trimmedLine = trimmedLine.trimEnd();
if (
!trimmedLine.startsWith('<') &&
!trimmedLine.endsWith('>') &&
!trimmedLine.includes(' ')
) {
if (!trimmedLine.startsWith('<') && !trimmedLine.endsWith('>')) {
// check if it is a url link and wrap it with the angle brackets
// sometimes the url includes emphasis `_` that will break URL parsing
//
@@ -0,0 +1,13 @@
import { toGfxBlockComponent } from '@blocksuite/std';
import { CodeBlockComponent } from './code-block.js';
export class CodeEdgelessBlockComponent extends toGfxBlockComponent(
CodeBlockComponent
) {}
declare global {
interface HTMLElementTagNameMap {
'affine-edgeless-code': CodeEdgelessBlockComponent;
}
}
@@ -4,7 +4,6 @@ import type {
MenuItemGroup,
} from '@blocksuite/affine-components/toolbar';
import { renderGroups } from '@blocksuite/affine-components/toolbar';
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { WithDisposable } from '@blocksuite/global/lit';
import { noop } from '@blocksuite/global/utils';
import { MoreVerticalIcon } from '@blocksuite/icons/lit';
@@ -34,11 +33,15 @@ export class AffineCodeToolbar extends WithDisposable(LitElement) {
}
.code-toolbar-button {
color: ${unsafeCSSVarV2('icon/primary')};
background-color: ${unsafeCSSVarV2('button/secondary')};
color: var(--affine-icon-color);
background-color: var(--affine-background-primary-color);
box-shadow: var(--affine-shadow-1);
border-radius: 4px;
}
.copy-code {
margin-left: auto;
}
`;
private _currentOpenMenu: AbortController | null = null;
@@ -4,10 +4,6 @@ import {
showPopFilterableList,
} from '@blocksuite/affine-components/filterable-list';
import { ArrowDownIcon } from '@blocksuite/affine-components/icons';
import {
DocModeProvider,
TelemetryProvider,
} from '@blocksuite/affine-shared/services';
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
import { noop } from '@blocksuite/global/utils';
@@ -23,6 +19,8 @@ export class LanguageListButton extends WithDisposable(
) {
static override styles = css`
.lang-button {
background-color: var(--affine-background-primary-color);
box-shadow: var(--affine-shadow-1);
display: flex;
gap: 4px;
padding: 2px 4px;
@@ -30,11 +28,11 @@ export class LanguageListButton extends WithDisposable(
}
.lang-button:hover {
background: ${unsafeCSSVarV2('layer/background/hoverOverlay')};
background: var(--affine-hover-color-filled);
}
.lang-button[hover] {
background: ${unsafeCSSVarV2('layer/background/hoverOverlay')};
background: var(--affine-hover-color-filled);
}
.lang-button-icon {
@@ -77,18 +75,6 @@ export class LanguageListButton extends WithDisposable(
this.blockComponent.store.transact(() => {
this.blockComponent.model.props.language$.value = item.name;
});
const std = this.blockComponent.std;
const mode =
std.getOptional(DocModeProvider)?.getEditorMode() ?? 'page';
const telemetryService = std.getOptional(TelemetryProvider);
if (!telemetryService) return;
telemetryService.track('codeBlockLanguageSelect', {
page: mode,
segment: 'code block',
module: 'language selector',
control: item.name,
});
},
active: item => item.name === this.blockComponent.model.props.language,
items: this._sortedBundledLanguages,
@@ -1,7 +1,3 @@
import {
DocModeProvider,
TelemetryProvider,
} from '@blocksuite/affine-shared/services';
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
import { css, html, LitElement, nothing } from 'lit';
@@ -13,10 +9,6 @@ import { CodeBlockPreviewIdentifier } from '../../code-preview-extension';
export class PreviewButton extends WithDisposable(SignalWatcher(LitElement)) {
static override styles = css`
:host {
margin-right: auto;
}
.preview-toggle-container {
display: flex;
padding: 2px;
@@ -63,17 +55,6 @@ export class PreviewButton extends WithDisposable(SignalWatcher(LitElement)) {
this.blockComponent.store.updateBlock(this.blockComponent.model, {
preview: value,
});
const std = this.blockComponent.std;
const mode = std.getOptional(DocModeProvider)?.getEditorMode() ?? 'page';
const telemetryService = std.getOptional(TelemetryProvider);
if (!telemetryService) return;
telemetryService.track('htmlBlockTogglePreview', {
page: mode,
segment: 'code block',
module: 'code toolbar container',
control: 'preview toggle button',
});
};
get preview() {
@@ -117,12 +117,13 @@ export const PRIMARY_GROUPS: MenuItemGroup<CodeBlockToolbarContext>[] = [
},
];
export const toggleGroup: MenuItemGroup<CodeBlockToolbarContext> = {
type: 'toggle',
// Clipboard Group
export const clipboardGroup: MenuItemGroup<CodeBlockToolbarContext> = {
type: 'clipboard',
items: [
{
type: 'wrap',
generate: ({ blockComponent }) => {
generate: ({ blockComponent, close }) => {
return {
action: () => {},
render: () => {
@@ -133,6 +134,7 @@ export const toggleGroup: MenuItemGroup<CodeBlockToolbarContext> = {
<editor-menu-action
@click=${() => {
blockComponent.setWrap(!wrapped);
close();
}}
aria-label=${label}
>
@@ -153,7 +155,7 @@ export const toggleGroup: MenuItemGroup<CodeBlockToolbarContext> = {
when: ({ std }) =>
std.getOptional(CodeBlockConfigExtension.identifier)?.showLineNumbers ??
true,
generate: ({ blockComponent }) => {
generate: ({ blockComponent, close }) => {
return {
action: () => {},
render: () => {
@@ -165,6 +167,8 @@ export const toggleGroup: MenuItemGroup<CodeBlockToolbarContext> = {
blockComponent.store.updateBlock(blockComponent.model, {
lineNumber: !lineNumber,
});
close();
}}
aria-label=${label}
>
@@ -180,13 +184,6 @@ export const toggleGroup: MenuItemGroup<CodeBlockToolbarContext> = {
};
},
},
],
};
// Clipboard Group
export const clipboardGroup: MenuItemGroup<CodeBlockToolbarContext> = {
type: 'clipboard',
items: [
{
type: 'duplicate',
label: 'Duplicate',
@@ -236,7 +233,6 @@ export const deleteGroup: MenuItemGroup<CodeBlockToolbarContext> = {
};
export const MORE_GROUPS: MenuItemGroup<CodeBlockToolbarContext>[] = [
toggleGroup,
clipboardGroup,
deleteGroup,
];
@@ -1,4 +1,5 @@
import { CodeBlockComponent } from './code-block';
import { CodeEdgelessBlockComponent } from './code-edgeless-block';
import {
AFFINE_CODE_TOOLBAR_WIDGET,
AffineCodeToolbarWidget,
@@ -14,6 +15,7 @@ export function effects() {
customElements.define(AFFINE_CODE_TOOLBAR_WIDGET, AffineCodeToolbarWidget);
customElements.define('affine-code-unit', AffineCodeUnit);
customElements.define('affine-code', CodeBlockComponent);
customElements.define('affine-edgeless-code', CodeEdgelessBlockComponent);
customElements.define('preview-button', PreviewButton);
}
@@ -2,10 +2,6 @@ import { scrollbarStyle } from '@blocksuite/affine-shared/styles';
import { css } from 'lit';
export const codeBlockStyles = css`
affine-code {
display: block;
}
.affine-code-block-container {
font-size: var(--affine-font-xs);
line-height: var(--affine-line-height);
+5 -1
View File
@@ -41,7 +41,11 @@ export class CodeBlockViewExtension extends ViewExtensionProvider {
context.register([
FlavourExtension('affine:code'),
CodeBlockHighlighter,
BlockViewExtension('affine:code', literal`affine-code`),
BlockViewExtension('affine:code', model => {
return model.parent?.flavour === 'affine:surface'
? literal`affine-edgeless-code`
: literal`affine-code`;
}),
SlashMenuConfigExtension('affine:code', codeSlashMenuConfig),
CodeKeymapExtension,
...getCodeClipboardExtensions(),
@@ -24,7 +24,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.15",
"@toeverything/theme": "^1.1.14",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
@@ -23,9 +23,9 @@ import {
createRecordDetail,
createUniComponentFromWebComponent,
type DataSource,
DataView,
dataViewCommonStyle,
type DataViewProps,
DataViewRootUILogic,
type DataViewSelection,
type DataViewWidget,
type DataViewWidgetProps,
@@ -133,6 +133,8 @@ export class DataViewBlockComponent extends CaptionedBlockComponent<DataViewBloc
private _dataSource?: DataSource;
private readonly dataView = new DataView();
_bindHotkey: DataViewProps['bindHotkey'] = hotkeys => {
return {
dispose: this.host.event.bindHotkey(hotkeys, {
@@ -230,6 +232,10 @@ export class DataViewBlockComponent extends CaptionedBlockComponent<DataViewBloc
return this.rootComponent;
}
get view() {
return this.dataView.expose;
}
private renderDatabaseOps() {
if (this.store.readonly) {
return nothing;
@@ -244,68 +250,68 @@ export class DataViewBlockComponent extends CaptionedBlockComponent<DataViewBloc
this.setAttribute(RANGE_SYNC_EXCLUDE_ATTR, 'true');
}
private readonly dataViewRootLogic = new DataViewRootUILogic({
virtualPadding$: signal(0),
bindHotkey: this._bindHotkey,
handleEvent: this._handleEvent,
selection$: this.selection$,
setSelection: this.setSelection,
dataSource: this.dataSource,
headerWidget: this.headerWidget,
clipboard: this.std.clipboard,
notification: {
toast: message => {
const notification = this.std.getOptional(NotificationProvider);
if (notification) {
notification.toast(message);
} else {
toast(this.host, message);
}
},
},
eventTrace: (key, params) => {
const telemetryService = this.std.getOptional(TelemetryProvider);
telemetryService?.track(key, {
...(params as TelemetryEventMap[typeof key]),
blockId: this.blockId,
});
},
detailPanelConfig: {
openDetailPanel: (target, data) => {
const peekViewService = this.std.getOptional(PeekViewProvider);
if (peekViewService) {
const template = createRecordDetail({
...data,
openDoc: () => {},
detail: {
header: uniMap(
createUniComponentFromWebComponent(BlockRenderer),
props => ({
...props,
host: this.host,
})
),
note: uniMap(
createUniComponentFromWebComponent(NoteRenderer),
props => ({
...props,
model: this.model,
host: this.host,
})
),
},
});
return peekViewService.peek({ target, template });
} else {
return Promise.resolve();
}
},
},
});
override renderBlock() {
const peekViewService = this.std.getOptional(PeekViewProvider);
const telemetryService = this.std.getOptional(TelemetryProvider);
return html`
<div contenteditable="false" style="position: relative">
${this.dataViewRootLogic.render()}
${this.dataView.render({
virtualPadding$: signal(0),
bindHotkey: this._bindHotkey,
handleEvent: this._handleEvent,
selection$: this.selection$,
setSelection: this.setSelection,
dataSource: this.dataSource,
headerWidget: this.headerWidget,
clipboard: this.std.clipboard,
notification: {
toast: message => {
const notification = this.std.getOptional(NotificationProvider);
if (notification) {
notification.toast(message);
} else {
toast(this.host, message);
}
},
},
eventTrace: (key, params) => {
telemetryService?.track(key, {
...(params as TelemetryEventMap[typeof key]),
blockId: this.blockId,
});
},
detailPanelConfig: {
openDetailPanel: (target, data) => {
if (peekViewService) {
const template = createRecordDetail({
...data,
openDoc: () => {},
detail: {
header: uniMap(
createUniComponentFromWebComponent(BlockRenderer),
props => ({
...props,
host: this.host,
})
),
note: uniMap(
createUniComponentFromWebComponent(NoteRenderer),
props => ({
...props,
model: this.model,
host: this.host,
})
),
},
});
return peekViewService.peek({ target, template });
} else {
return Promise.resolve();
}
},
},
})}
</div>
`;
}
@@ -28,7 +28,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.15",
"@toeverything/theme": "^1.1.14",
"@types/mdast": "^4.0.4",
"date-fns": "^4.0.0",
"lit": "^3.2.0",
@@ -1,19 +1,15 @@
import { stopPropagation } from '@blocksuite/affine-shared/utils';
import type { DataViewUILogicBase } from '@blocksuite/data-view';
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
import { WithDisposable } from '@blocksuite/global/lit';
import { ShadowlessElement } from '@blocksuite/std';
import type { Text } from '@blocksuite/store';
import { signal } from '@preact/signals-core';
import { css, html } from 'lit';
import { property, query } from 'lit/decorators.js';
import { property, query, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { styleMap } from 'lit/directives/style-map.js';
import type { DatabaseBlockComponent } from '../../database-block.js';
export class DatabaseTitle extends SignalWatcher(
WithDisposable(ShadowlessElement)
) {
export class DatabaseTitle extends WithDisposable(ShadowlessElement) {
static override styles = css`
.affine-database-title {
position: relative;
@@ -75,23 +71,22 @@ export class DatabaseTitle extends SignalWatcher(
`;
private readonly compositionEnd = () => {
this.isComposing$.value = false;
this.titleText.replace(0, this.titleText.length, this.input.value);
};
private readonly onBlur = () => {
this.isFocus$.value = false;
this.isFocus = false;
};
private readonly onFocus = () => {
this.isFocus$.value = true;
if (this.dataViewLogic.selection$.value) {
this.dataViewLogic.setSelection(undefined);
this.isFocus = true;
if (this.database?.viewSelection$?.value) {
this.database?.setSelection(undefined);
}
};
private readonly onInput = (e: InputEvent) => {
this.text$.value = this.input.value;
this.text = this.input.value;
if (!e.isComposing) {
this.titleText.replace(0, this.titleText.length, this.input.value);
}
@@ -107,9 +102,9 @@ export class DatabaseTitle extends SignalWatcher(
};
updateText = () => {
if (!this.isFocus$.value) {
if (!this.isFocus) {
this.input.value = this.titleText.toString();
this.text$.value = this.input.value;
this.text = this.input.value;
}
};
@@ -129,25 +124,25 @@ export class DatabaseTitle extends SignalWatcher(
}
override render() {
const isEmpty = !this.text$.value;
const isEmpty = !this.text;
const classList = classMap({
'affine-database-title': true,
ellipsis: !this.isFocus$.value,
ellipsis: !this.isFocus,
});
const untitledStyle = styleMap({
height: isEmpty ? 'auto' : 0,
opacity: isEmpty && !this.isFocus$.value ? 1 : 0,
opacity: isEmpty && !this.isFocus ? 1 : 0,
});
return html` <div
class="${classList}"
data-title-empty="${isEmpty}"
data-title-focus="${this.isFocus$.value}"
data-title-focus="${this.isFocus}"
>
<div class="text" style="${untitledStyle}">Untitled</div>
<div class="text">${this.text$.value}</div>
<div class="text">${this.text}</div>
<textarea
.disabled="${this.readonly$.value}"
.disabled="${this.readonly}"
@input="${this.onInput}"
@keydown="${this.onKeyDown}"
@copy="${stopPropagation}"
@@ -164,24 +159,23 @@ export class DatabaseTitle extends SignalWatcher(
@query('textarea')
private accessor input!: HTMLTextAreaElement;
private readonly isComposing$ = signal(false);
private readonly isFocus$ = signal(false);
@state()
accessor isComposing = false;
private onPressEnterKey() {
this.dataViewLogic.addRow?.('start');
}
@state()
private accessor isFocus = false;
get readonly$() {
return this.dataViewLogic.view.readonly$;
}
@property({ attribute: false })
accessor onPressEnterKey: (() => void) | undefined = undefined;
private readonly text$ = signal('');
@property({ attribute: false })
accessor readonly!: boolean;
@state()
private accessor text = '';
@property({ attribute: false })
accessor titleText!: Text;
@property({ attribute: false })
accessor dataViewLogic!: DataViewUILogicBase;
}
declare global {
@@ -1,73 +0,0 @@
import { css } from '@emotion/css';
import { cssVarV2 } from '@toeverything/theme/v2';
export const databaseBlockStyles = css({
display: 'block',
borderRadius: '8px',
backgroundColor: 'var(--affine-background-primary-color)',
padding: '8px',
margin: '8px -8px -8px',
});
export const databaseBlockSelectedStyles = css({
backgroundColor: 'var(--affine-hover-color)',
borderRadius: '4px',
});
export const databaseOpsStyles = css({
padding: '2px',
borderRadius: '4px',
display: 'flex',
cursor: 'pointer',
alignItems: 'center',
height: 'max-content',
fontSize: '16px',
color: cssVarV2.icon.primary,
':hover': {
backgroundColor: 'var(--affine-hover-color)',
},
'@media print': {
display: 'none',
},
});
export const databaseHeaderBarStyles = css({
'@media print': {
display: 'none !important',
},
});
export const databaseTitleStyles = css({
overflow: 'hidden',
});
export const databaseHeaderContainerStyles = css({
marginBottom: '16px',
display: 'flex',
flexDirection: 'column',
});
export const databaseTitleRowStyles = css({
display: 'flex',
gap: '12px',
marginBottom: '8px',
alignItems: 'center',
});
export const databaseToolbarRowStyles = css({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
gap: '12px',
});
export const databaseViewBarContainerStyles = css({
flex: 1,
});
export const databaseContentStyles = css({
position: 'relative',
backgroundColor: 'var(--affine-background-primary-color)',
borderRadius: '4px',
});
@@ -19,14 +19,15 @@ import { getDropResult } from '@blocksuite/affine-widget-drag-handle';
import {
createRecordDetail,
createUniComponentFromWebComponent,
DataViewRootUILogic,
DataView,
dataViewCommonStyle,
type DataViewInstance,
type DataViewProps,
type DataViewSelection,
type DataViewUILogicBase,
type DataViewWidget,
type DataViewWidgetProps,
defineUniComponent,
ExternalGroupByConfigProvider,
lazy,
renderUniLit,
type SingleView,
uniMap,
@@ -43,23 +44,12 @@ import { RANGE_SYNC_EXCLUDE_ATTR } from '@blocksuite/std/inline';
import { Slice } from '@blocksuite/store';
import { autoUpdate } from '@floating-ui/dom';
import { computed, signal } from '@preact/signals-core';
import { html, nothing } from 'lit';
import { css, html, nothing, unsafeCSS } from 'lit';
import { popSideDetail } from './components/layout.js';
import { DatabaseConfigExtension } from './config.js';
import { EditorHostKey } from './context/host-context.js';
import { DatabaseBlockDataSource } from './data-source.js';
import {
databaseBlockStyles,
databaseContentStyles,
databaseHeaderBarStyles,
databaseHeaderContainerStyles,
databaseOpsStyles,
databaseTitleRowStyles,
databaseTitleStyles,
databaseToolbarRowStyles,
databaseViewBarContainerStyles,
} from './database-block-styles.js';
import { BlockRenderer } from './detail-panel/block-renderer.js';
import { NoteRenderer } from './detail-panel/note-renderer.js';
import { DatabaseSelection } from './selection.js';
@@ -68,7 +58,52 @@ import { getSingleDocIdFromText } from './utils/title-doc.js';
import type { DatabaseViewExtensionOptions } from './view';
export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBlockModel> {
private readonly clickDatabaseOps = (e: MouseEvent) => {
static override styles = css`
${unsafeCSS(dataViewCommonStyle('affine-database'))}
affine-database {
display: block;
border-radius: 8px;
background-color: var(--affine-background-primary-color);
padding: 8px;
margin: 8px -8px -8px;
}
.database-block-selected {
background-color: var(--affine-hover-color);
border-radius: 4px;
}
.database-ops {
padding: 2px;
border-radius: 4px;
display: flex;
cursor: pointer;
align-items: center;
height: max-content;
}
.database-ops svg {
width: 16px;
height: 16px;
color: var(--affine-icon-color);
}
.database-ops:hover {
background-color: var(--affine-hover-color);
}
@media print {
.database-ops {
display: none;
}
.database-header-bar {
display: none !important;
}
}
`;
private readonly _clickDatabaseOps = (e: MouseEvent) => {
const options = this.optionsConfig.configure(this.model, {
items: [
menu.input({
@@ -120,33 +155,36 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
});
};
private readonly dataSource = lazy(() => {
const dataSource = new DatabaseBlockDataSource(this.model, dataSource => {
dataSource.serviceSet(EditorHostKey, this.host);
this.std.provider
.getAll(ExternalGroupByConfigProvider)
.forEach(config => {
dataSource.serviceSet(
ExternalGroupByConfigProvider(config.name),
config
);
});
});
const id = currentViewStorage.getCurrentView(this.model.id);
if (id && dataSource.viewManager.viewGet(id)) {
dataSource.viewManager.setCurrentView(id);
}
return dataSource;
});
private _dataSource?: DatabaseBlockDataSource;
private readonly renderTitle = (dataViewLogic: DataViewUILogicBase) => {
private readonly dataView = new DataView();
private readonly renderTitle = (dataViewMethod: DataViewInstance) => {
const addRow = () => dataViewMethod.addRow?.('start');
return html` <affine-database-title
class="${databaseTitleStyles}"
style="overflow: hidden"
.titleText="${this.model.props.title}"
.dataViewLogic="${dataViewLogic}"
.readonly="${this.dataSource.readonly$.value}"
.onPressEnterKey="${addRow}"
></affine-database-title>`;
};
_bindHotkey: DataViewProps['bindHotkey'] = hotkeys => {
return {
dispose: this.host.event.bindHotkey(hotkeys, {
blockId: this.topContenteditableElement?.blockId ?? this.blockId,
}),
};
};
_handleEvent: DataViewProps['handleEvent'] = (name, handler) => {
return {
dispose: this.host.event.add(name, handler, {
blockId: this.blockId,
}),
};
};
createTemplate = (
data: {
view: SingleView;
@@ -180,12 +218,18 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
headerWidget: DataViewWidget = defineUniComponent(
(props: DataViewWidgetProps) => {
return html`
<div class="${databaseHeaderContainerStyles}">
<div class="${databaseTitleRowStyles}">
${this.renderTitle(props.dataViewLogic)} ${this.renderDatabaseOps()}
<div style="margin-bottom: 16px;display:flex;flex-direction: column">
<div
style="display:flex;gap:12px;margin-bottom: 8px;align-items: center"
>
${this.renderTitle(props.dataViewInstance)}
${this.renderDatabaseOps()}
</div>
<div class="${databaseToolbarRowStyles} ${databaseHeaderBarStyles}">
<div class="${databaseViewBarContainerStyles}">
<div
style="display:flex;align-items:center;justify-content: space-between;gap: 12px"
class="database-header-bar"
>
<div style="flex:1">
${renderUniLit(widgetPresets.viewBar, {
...props,
onChangeView: id => {
@@ -240,9 +284,7 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
return () => {};
};
private readonly setSelection = (
selection: DataViewSelection | undefined
) => {
setSelection = (selection: DataViewSelection | undefined) => {
if (selection) {
getSelection()?.removeAllRanges();
}
@@ -259,7 +301,7 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
);
};
private readonly toolsWidget: DataViewWidget = widgetPresets.createTools({
toolsWidget: DataViewWidget = widgetPresets.createTools({
table: [
widgetPresets.tools.filter,
widgetPresets.tools.sort,
@@ -276,7 +318,7 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
],
});
private readonly viewSelection$ = computed(() => {
viewSelection$ = computed(() => {
const databaseSelection = this.selection.value.find(
(selection): selection is DatabaseSelection => {
if (selection.blockId !== this.blockId) {
@@ -288,7 +330,28 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
return databaseSelection?.viewSelection;
});
private readonly virtualPadding$ = signal(0);
virtualPadding$ = signal(0);
get dataSource(): DatabaseBlockDataSource {
if (!this._dataSource) {
this._dataSource = new DatabaseBlockDataSource(this.model, dataSource => {
dataSource.serviceSet(EditorHostKey, this.host);
this.std.provider
.getAll(ExternalGroupByConfigProvider)
.forEach(config => {
dataSource.serviceSet(
ExternalGroupByConfigProvider(config.name),
config
);
});
});
const id = currentViewStorage.getCurrentView(this.model.id);
if (id && this.dataSource.viewManager.viewGet(id)) {
this.dataSource.viewManager.setCurrentView(id);
}
}
return this._dataSource;
}
get optionsConfig(): DatabaseViewExtensionOptions {
return {
@@ -306,15 +369,15 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
return this.rootComponent;
}
get view() {
return this.dataView.expose;
}
private renderDatabaseOps() {
if (this.dataSource.value.readonly$.value) {
if (this.dataSource.readonly$.value) {
return nothing;
}
return html` <div
data-testid="database-ops"
class="${databaseOpsStyles}"
@click="${this.clickDatabaseOps}"
>
return html` <div class="database-ops" @click="${this._clickDatabaseOps}">
${MoreHorizontalIcon()}
</div>`;
}
@@ -323,7 +386,6 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
super.connectedCallback();
this.setAttribute(RANGE_SYNC_EXCLUDE_ATTR, 'true');
this.classList.add(databaseBlockStyles);
this.listenFullWidthChange();
}
@@ -340,97 +402,85 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
})
);
}
private readonly dataViewRootLogic = lazy(
() =>
new DataViewRootUILogic({
virtualPadding$: this.virtualPadding$,
bindHotkey: hotkeys => {
return {
dispose: this.host.event.bindHotkey(hotkeys, {
blockId: this.topContenteditableElement?.blockId ?? this.blockId,
}),
};
},
handleEvent: (name, handler) => {
return {
dispose: this.host.event.add(name, handler, {
blockId: this.blockId,
}),
};
},
selection$: this.viewSelection$,
setSelection: this.setSelection,
dataSource: this.dataSource.value,
headerWidget: this.headerWidget,
onDrag: this.onDrag,
clipboard: this.std.clipboard,
notification: {
toast: message => {
const notification = this.std.getOptional(NotificationProvider);
if (notification) {
notification.toast(message);
} else {
toast(this.host, message);
}
},
},
eventTrace: (key, params) => {
const telemetryService = this.std.getOptional(TelemetryProvider);
telemetryService?.track(key, {
...(params as TelemetryEventMap[typeof key]),
blockId: this.blockId,
});
},
detailPanelConfig: {
openDetailPanel: (target, data) => {
const peekViewService = this.std.getOptional(PeekViewProvider);
if (peekViewService) {
const openDoc = (docId: string) => {
return peekViewService.peek({
docId,
databaseId: this.blockId,
databaseDocId: this.model.store.id,
databaseRowId: data.rowId,
target: this,
});
};
const doc = getSingleDocIdFromText(
this.model.store.getBlock(data.rowId)?.model?.text
);
if (doc) {
return openDoc(doc);
}
const abort = new AbortController();
return new Promise<void>(focusBack => {
peekViewService
.peek(
{
target,
template: this.createTemplate(data, docId => {
// abort.abort();
openDoc(docId).then(focusBack).catch(focusBack);
}),
},
{ abortSignal: abort.signal }
)
.then(focusBack)
.catch(focusBack);
});
} else {
return popSideDetail(
this.createTemplate(data, () => {
//
})
);
}
},
},
})
);
override renderBlock() {
const peekViewService = this.std.getOptional(PeekViewProvider);
const telemetryService = this.std.getOptional(TelemetryProvider);
return html`
<div contenteditable="false" class="${databaseContentStyles}">
${this.dataViewRootLogic.value.render()}
<div
contenteditable="false"
style="position: relative;background-color: var(--affine-background-primary-color);border-radius: 4px"
>
${this.dataView.render({
virtualPadding$: this.virtualPadding$,
bindHotkey: this._bindHotkey,
handleEvent: this._handleEvent,
selection$: this.viewSelection$,
setSelection: this.setSelection,
dataSource: this.dataSource,
headerWidget: this.headerWidget,
onDrag: this.onDrag,
clipboard: this.std.clipboard,
notification: {
toast: message => {
const notification = this.std.getOptional(NotificationProvider);
if (notification) {
notification.toast(message);
} else {
toast(this.host, message);
}
},
},
eventTrace: (key, params) => {
telemetryService?.track(key, {
...(params as TelemetryEventMap[typeof key]),
blockId: this.blockId,
});
},
detailPanelConfig: {
openDetailPanel: (target, data) => {
if (peekViewService) {
const openDoc = (docId: string) => {
return peekViewService.peek({
docId,
databaseId: this.blockId,
databaseDocId: this.model.store.id,
databaseRowId: data.rowId,
target: this,
});
};
const doc = getSingleDocIdFromText(
this.model.store.getBlock(data.rowId)?.model?.text
);
if (doc) {
return openDoc(doc);
}
const abort = new AbortController();
return new Promise<void>(focusBack => {
peekViewService
.peek(
{
target,
template: this.createTemplate(data, docId => {
// abort.abort();
openDoc(docId).then(focusBack).catch(focusBack);
}),
},
{ abortSignal: abort.signal }
)
.then(focusBack)
.catch(focusBack);
});
} else {
return popSideDetail(
this.createTemplate(data, () => {
//
})
);
}
},
},
})}
</div>
`;
}
@@ -20,7 +20,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.15",
"@toeverything/theme": "^1.1.14",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
@@ -26,7 +26,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.15",
"@toeverything/theme": "^1.1.14",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
@@ -22,8 +22,10 @@ import {
GfxBlockComponent,
TextSelection,
} from '@blocksuite/std';
import { GfxViewInteractionExtension } from '@blocksuite/std/gfx';
import { computed } from '@preact/signals-core';
import {
GfxViewInteractionExtension,
type SelectedContext,
} from '@blocksuite/std/gfx';
import { css, html } from 'lit';
import { query, state } from 'lit/decorators.js';
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
@@ -80,23 +82,6 @@ export class EdgelessTextBlockComponent extends GfxBlockComponent<EdgelessTextBl
});
}
private readonly _style$ = computed(() => {
const {
color$: { value: color },
fontFamily$: { value: fontFamily },
fontStyle$: { value: fontStyle },
fontWeight$: { value: fontWeight },
textAlign$: { value: textAlign },
} = this.model.props;
return {
color,
fontFamily,
fontStyle,
fontWeight,
textAlign,
};
});
checkWidthOverflow(width: number) {
let wValid = true;
@@ -279,6 +264,69 @@ export class EdgelessTextBlockComponent extends GfxBlockComponent<EdgelessTextBl
};
}
override onSelected(context: SelectedContext): void | boolean {
const { selected, multiSelect, event: e } = context;
const { editing } = this.gfx.selection;
const alreadySelected = this.gfx.selection.has(this.model.id);
if (!multiSelect && selected && (alreadySelected || editing)) {
if (this.model.isLocked()) return;
if (alreadySelected && editing) {
return;
}
this.gfx.selection.set({
elements: [this.model.id],
editing: true,
});
this.updateComplete
.then(() => {
if (!this.isConnected) {
return;
}
if (this.model.children.length === 0) {
const blockId = this.store.addBlock(
'affine:paragraph',
{ type: 'text' },
this.model.id
);
if (blockId) {
focusTextModel(this.std, blockId);
}
} else {
const rect = this.querySelector(
'.affine-block-children-container'
)?.getBoundingClientRect();
if (rect) {
const offsetY = 8 * this.gfx.viewport.zoom;
const offsetX = 2 * this.gfx.viewport.zoom;
const x = clamp(
e.clientX,
rect.left + offsetX,
rect.right - offsetX
);
const y = clamp(
e.clientY,
rect.top + offsetY,
rect.bottom - offsetY
);
handleNativeRangeAtPoint(x, y);
} else {
handleNativeRangeAtPoint(e.clientX, e.clientY);
}
}
})
.catch(console.error);
} else {
return super.onSelected(context);
}
}
override renderGfxBlock() {
const { model } = this;
const { rotate, hasMaxWidth } = model.props;
@@ -317,7 +365,7 @@ export class EdgelessTextBlockComponent extends GfxBlockComponent<EdgelessTextBl
override renderPageContent() {
const { color, fontFamily, fontStyle, fontWeight, textAlign } =
this._style$.value;
this.model.props;
const themeProvider = this.std.get(ThemeProvider);
const textColor = themeProvider.generateColorProperty(
color,
@@ -440,73 +488,5 @@ export const EdgelessTextInteraction =
},
};
},
handleSelection: context => {
const { gfx, std, view, model } = context;
return {
onSelect(context) {
const { selected, multiSelect, event: e } = context;
const { editing } = gfx.selection;
const alreadySelected = gfx.selection.has(model.id);
if (!multiSelect && selected && (alreadySelected || editing)) {
if (model.isLocked()) return;
if (alreadySelected && editing) {
return;
}
gfx.selection.set({
elements: [model.id],
editing: true,
});
view.updateComplete
.then(() => {
if (!view.isConnected) {
return;
}
if (model.children.length === 0) {
const blockId = std.store.addBlock(
'affine:paragraph',
{ type: 'text' },
model.id
);
if (blockId) {
focusTextModel(std, blockId);
}
} else {
const rect = view
.querySelector('.affine-block-children-container')
?.getBoundingClientRect();
if (rect) {
const offsetY = 8 * gfx.viewport.zoom;
const offsetX = 2 * gfx.viewport.zoom;
const x = clamp(
e.clientX,
rect.left + offsetX,
rect.right - offsetX
);
const y = clamp(
e.clientY,
rect.top + offsetY,
rect.bottom - offsetY
);
handleNativeRangeAtPoint(x, y);
} else {
handleNativeRangeAtPoint(e.clientX, e.clientY);
}
}
})
.catch(console.error);
} else {
return context.default(context);
}
},
};
},
}
);
@@ -26,7 +26,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.15",
"@toeverything/theme": "^1.1.14",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.21",
@@ -11,6 +11,7 @@ import {
isFootnoteDefinitionNode,
type MarkdownAST,
} from '@blocksuite/affine-shared/adapters';
import { FeatureFlagService } from '@blocksuite/affine-shared/services';
import { nanoid } from '@blocksuite/store';
const isLinkedDocFootnoteDefinitionNode = (node: MarkdownAST) => {
@@ -35,7 +36,15 @@ export const embedLinkedDocBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatc
fromMatch: o => o.node.flavour === EmbedLinkedDocBlockSchema.model.flavour,
toBlockSnapshot: {
enter: (o, context) => {
if (!isFootnoteDefinitionNode(o.node)) {
const { provider } = context;
let enableCitation = false;
try {
const featureFlagService = provider?.get(FeatureFlagService);
enableCitation = !!featureFlagService?.getFlag('enable_citation');
} catch {
enableCitation = false;
}
if (!isFootnoteDefinitionNode(o.node) || !enableCitation) {
return;
}
@@ -13,6 +13,7 @@ import {
ActionPlacement,
DocDisplayMetaProvider,
EditorSettingProvider,
FeatureFlagService,
type LinkEventType,
type OpenDocMode,
type ToolbarAction,
@@ -215,7 +216,12 @@ const conversionsActionGroup = {
run(ctx) {
const block = ctx.getCurrentBlockByType(EmbedLinkedDocBlockComponent);
if (isGfxBlockComponent(block)) {
if (
ctx.std
.get(FeatureFlagService)
.getFlag('enable_embed_doc_with_alias') &&
isGfxBlockComponent(block)
) {
const editorSetting = ctx.std.getOptional(EditorSettingProvider);
editorSetting?.set?.(
'docCanvasPreferView',
@@ -259,18 +265,18 @@ const builtinToolbarConfig = {
conversionsActionGroup,
{
id: 'c.style',
actions: (
[
{
id: 'horizontal',
label: 'Large horizontal style',
},
{
id: 'list',
label: 'Small horizontal style',
},
] as const
).filter(action => EmbedLinkedDocStyles.includes(action.id)),
actions: [
{
id: 'horizontal',
label: 'Large horizontal style',
},
{
id: 'list',
label: 'Small horizontal style',
},
].filter(action =>
EmbedLinkedDocStyles.includes(action.id as EmbedCardStyle)
),
content(ctx) {
const model = ctx.getCurrentModelByType(EmbedLinkedDocModel);
if (!model) return null;
@@ -368,26 +374,26 @@ const builtinSurfaceToolbarConfig = {
conversionsActionGroup,
{
id: 'c.style',
actions: (
[
{
id: 'horizontal',
label: 'Large horizontal style',
},
{
id: 'list',
label: 'Small horizontal style',
},
{
id: 'vertical',
label: 'Large vertical style',
},
{
id: 'cube',
label: 'Small vertical style',
},
] as const
).filter(action => EmbedLinkedDocStyles.includes(action.id)),
actions: [
{
id: 'horizontal',
label: 'Large horizontal style',
},
{
id: 'list',
label: 'Small horizontal style',
},
{
id: 'vertical',
label: 'Large vertical style',
},
{
id: 'cube',
label: 'Small vertical style',
},
].filter(action =>
EmbedLinkedDocStyles.includes(action.id as EmbedCardStyle)
),
content(ctx) {
const model = ctx.getCurrentModelByType(EmbedLinkedDocModel);
if (!model) return null;
@@ -3,7 +3,6 @@ import {
RENDER_CARD_THROTTLE_MS,
} from '@blocksuite/affine-block-embed';
import { SurfaceBlockModel } from '@blocksuite/affine-block-surface';
import { LoadingIcon } from '@blocksuite/affine-components/icons';
import { isPeekable, Peekable } from '@blocksuite/affine-components/peek';
import { RefNodeSlotsProvider } from '@blocksuite/affine-inline-reference';
import type {
@@ -32,7 +31,6 @@ import {
referenceToNode,
} from '@blocksuite/affine-shared/utils';
import { Bound } from '@blocksuite/global/gfx';
import { ResetIcon } from '@blocksuite/icons/lit';
import { BlockSelection } from '@blocksuite/std';
import { Text } from '@blocksuite/store';
import { computed } from '@preact/signals-core';
@@ -261,7 +259,7 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
);
}
private readonly _handleDoubleClick = (event: MouseEvent) => {
private _handleDoubleClick(event: MouseEvent) {
event.stopPropagation();
const openDocService = this.std.get(OpenDocExtensionIdentifier);
const shouldOpenInPeek =
@@ -272,7 +270,7 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
: 'open-in-active-view',
event,
});
};
}
private _isDocEmpty() {
const linkedDoc = this.linkedDoc;
@@ -313,7 +311,6 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
.citationIdentifier=${footnoteIdentifier}
.active=${this.selected$.value}
.onClickCallback=${this._handleClick}
.onDoubleClickCallback=${this._handleDoubleClick}
></affine-citation-card>
</div> `;
};
@@ -339,6 +336,8 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
const theme = this.std.get(ThemeProvider).theme;
const {
LoadingIcon,
ReloadIcon,
LinkedDocDeletedBanner,
LinkedDocEmptyBanner,
SyncedDocErrorBanner,
@@ -347,7 +346,7 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
const icon = isError
? SyncedDocErrorIcon
: isLoading
? LoadingIcon()
? LoadingIcon
: this.icon$.value;
const title = isLoading ? 'Loading...' : this.title$;
const description = this.model.props.description$;
@@ -384,6 +383,10 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
() => html`
<div
class="affine-embed-linked-doc-block ${cardClassMap}"
style=${styleMap({
transform: `scale(${this._scale})`,
transformOrigin: '0 0',
})}
@click=${this._handleClick}
@dblclick=${this._handleDoubleClick}
>
@@ -429,7 +432,7 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
class="affine-embed-linked-doc-card-content-reload-button"
@click=${this.refreshData}
>
${ResetIcon()} <span>Reload</span>
${ReloadIcon} <span>Reload</span>
</div>
</div>
`
@@ -124,11 +124,11 @@ export const styles = css`
align-items: center;
gap: 4px;
cursor: pointer;
color: ${unsafeCSSVarV2('button/primary')};
}
.affine-embed-linked-doc-card-content-reload-button svg {
width: 12px;
height: 12px;
fill: var(--affine-background-primary-color);
}
.affine-embed-linked-doc-card-content-reload-button > span {
display: -webkit-box;
@@ -138,6 +138,7 @@ export const styles = css`
white-space: normal;
overflow: hidden;
text-overflow: ellipsis;
color: var(--affine-brand-color);
font-family: var(--affine-font-family);
font-size: var(--affine-font-xs);
font-style: normal;
@@ -304,6 +305,7 @@ export const styles = css`
.affine-embed-linked-doc-content-note {
-webkit-line-clamp: 16;
max-height: 320px;
}
.affine-embed-linked-doc-content-date {
@@ -1,6 +1,8 @@
import {
EmbedEdgelessIcon,
EmbedPageIcon,
getLoadingIconWith,
ReloadIcon,
} from '@blocksuite/affine-components/icons';
import {
ColorScheme,
@@ -33,6 +35,8 @@ import {
} from './styles.js';
type EmbedCardImages = {
LoadingIcon: TemplateResult<1>;
ReloadIcon: TemplateResult<1>;
LinkedDocIcon: TemplateResult<1>;
LinkedDocDeletedIcon: TemplateResult<1>;
LinkedDocEmptyBanner: TemplateResult<1>;
@@ -46,9 +50,12 @@ export function getEmbedLinkedDocIcons(
style: (typeof EmbedLinkedDocStyles)[number]
): EmbedCardImages {
const small = style !== 'vertical';
const LoadingIcon = getLoadingIconWith(theme);
if (editorMode === 'page') {
if (theme === ColorScheme.Light) {
return {
LoadingIcon,
ReloadIcon,
LinkedDocIcon: EmbedPageIcon,
LinkedDocDeletedIcon,
LinkedDocEmptyBanner: small
@@ -61,6 +68,8 @@ export function getEmbedLinkedDocIcons(
};
} else {
return {
ReloadIcon,
LoadingIcon,
LinkedDocIcon: EmbedPageIcon,
LinkedDocDeletedIcon,
LinkedDocEmptyBanner: small
@@ -75,6 +84,8 @@ export function getEmbedLinkedDocIcons(
} else {
if (theme === ColorScheme.Light) {
return {
ReloadIcon,
LoadingIcon,
LinkedDocIcon: EmbedEdgelessIcon,
LinkedDocDeletedIcon,
LinkedDocEmptyBanner: small
@@ -87,6 +98,8 @@ export function getEmbedLinkedDocIcons(
};
} else {
return {
ReloadIcon,
LoadingIcon,
LinkedDocIcon: EmbedEdgelessIcon,
LinkedDocDeletedIcon,
LinkedDocEmptyBanner: small
@@ -1,8 +1,6 @@
import { RENDER_CARD_THROTTLE_MS } from '@blocksuite/affine-block-embed';
import { LoadingIcon } from '@blocksuite/affine-components/icons';
import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { WithDisposable } from '@blocksuite/global/lit';
import { ResetIcon } from '@blocksuite/icons/lit';
import {
BlockSelection,
isGfxBlockComponent,
@@ -150,7 +148,9 @@ export class EmbedSyncedDocCard extends WithDisposable(ShadowlessElement) {
const theme = this.std.get(ThemeProvider).theme;
const {
LoadingIcon,
SyncedDocErrorIcon,
ReloadIcon,
SyncedDocEmptyBanner,
SyncedDocErrorBanner,
SyncedDocDeletedBanner,
@@ -159,7 +159,7 @@ export class EmbedSyncedDocCard extends WithDisposable(ShadowlessElement) {
const icon = error
? SyncedDocErrorIcon
: isLoading
? LoadingIcon()
? LoadingIcon
: this.block.icon$.value;
const title = isLoading ? 'Loading...' : this.block.title$;
@@ -216,7 +216,7 @@ export class EmbedSyncedDocCard extends WithDisposable(ShadowlessElement) {
class="affine-embed-synced-doc-card-content-reload-button"
@click=${() => this.block.refreshData()}
>
${ResetIcon()} <span>Reload</span>
${ReloadIcon} <span>Reload</span>
</div>
</div>
`
@@ -17,6 +17,7 @@ import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts';
import {
ActionPlacement,
EditorSettingProvider,
FeatureFlagService,
type LinkEventType,
type OpenDocMode,
type ToolbarAction,
@@ -162,7 +163,12 @@ const conversionsActionGroup = {
label: 'Card view',
run(ctx) {
const block = ctx.getCurrentBlockByType(EmbedSyncedDocBlockComponent);
if (isGfxBlockComponent(block)) {
if (
ctx.std
.get(FeatureFlagService)
.getFlag('enable_embed_doc_with_alias') &&
isGfxBlockComponent(block)
) {
const editorSetting = ctx.std.getOptional(EditorSettingProvider);
editorSetting?.set?.(
'docCanvasPreferView',
@@ -290,6 +296,8 @@ const builtinSurfaceToolbarConfig = {
label: 'Insert to page',
tooltip: 'Insert to page',
icon: InsertIntoPageIcon(),
when: ({ std }) =>
std.get(FeatureFlagService).getFlag('enable_embed_doc_with_alias'),
run: ctx => {
const model = ctx.getCurrentModelByType(EmbedSyncedDocModel);
if (!model) return;
@@ -326,6 +334,8 @@ const builtinSurfaceToolbarConfig = {
tooltip:
'Duplicate as note to create an editable copy, the original remains unchanged.',
icon: DuplicateIcon(),
when: ({ std }) =>
std.get(FeatureFlagService).getFlag('enable_embed_doc_with_alias'),
run: ctx => {
const { gfx } = ctx;
@@ -20,6 +20,7 @@ import { choose } from 'lit/directives/choose.js';
import { classMap } from 'lit/directives/class-map.js';
import { guard } from 'lit/directives/guard.js';
import { styleMap } from 'lit/directives/style-map.js';
import { when } from 'lit/directives/when.js';
import { EmbedSyncedDocConfigExtension } from './configs';
import { EmbedSyncedDocBlockComponent } from './embed-synced-doc-block';
@@ -122,18 +123,22 @@ export class EmbedEdgelessSyncedDocBlockComponent extends toEdgelessEmbedBlock(
<div class="affine-embed-synced-doc-edgeless-header-wrapper">
${header}
</div>
<div class="affine-embed-synced-doc-editor">
${this.isPageMode && this._isEmptySyncedDoc
? html`
<div class="affine-embed-synced-doc-editor-empty">
<span>
This is a linked doc, you can add content here.
</span>
</div>
`
: guard([editorMode, syncedDoc], renderEditor)}
</div>
<div class="affine-embed-synced-doc-editor-overlay"></div>
${when(
!this.model.isFolded,
() =>
html`<div class="affine-embed-synced-doc-editor">
${this.isPageMode && this._isEmptySyncedDoc
? html`
<div class="affine-embed-synced-doc-editor-empty">
<span>
This is a linked doc, you can add content here.
</span>
</div>
`
: guard([editorMode, syncedDoc], renderEditor)}
</div>
<div class="affine-embed-synced-doc-editor-overlay"></div>`
)}
</div>
`
);
@@ -303,11 +303,11 @@ export const cardStyles = css`
align-items: center;
gap: 4px;
cursor: pointer;
color: ${unsafeCSSVarV2('button/primary')};
}
.affine-embed-synced-doc-card-content-reload-button svg {
width: 12px;
height: 12px;
fill: var(--affine-background-primary-color);
}
.affine-embed-synced-doc-card-content-reload-button > span {
display: -webkit-box;
@@ -317,6 +317,7 @@ export const cardStyles = css`
white-space: normal;
overflow: hidden;
text-overflow: ellipsis;
color: var(--affine-brand-color);
font-family: var(--affine-font-family);
font-size: var(--affine-font-xs);
font-style: normal;
@@ -1,6 +1,8 @@
import {
EmbedEdgelessIcon,
EmbedPageIcon,
getLoadingIconWith,
ReloadIcon,
} from '@blocksuite/affine-components/icons';
import { ColorScheme } from '@blocksuite/affine-model';
import type { BlockComponent } from '@blocksuite/std';
@@ -19,9 +21,11 @@ import {
} from './styles.js';
type SyncedCardImages = {
LoadingIcon: TemplateResult<1>;
SyncedDocIcon: TemplateResult<1>;
SyncedDocErrorIcon: TemplateResult<1>;
SyncedDocDeletedIcon: TemplateResult<1>;
ReloadIcon: TemplateResult<1>;
SyncedDocEmptyBanner: TemplateResult<1>;
SyncedDocErrorBanner: TemplateResult<1>;
SyncedDocDeletedBanner: TemplateResult<1>;
@@ -31,20 +35,25 @@ export function getSyncedDocIcons(
theme: ColorScheme,
editorMode: 'page' | 'edgeless'
): SyncedCardImages {
const LoadingIcon = getLoadingIconWith(theme);
if (theme === ColorScheme.Light) {
return {
LoadingIcon,
SyncedDocIcon: editorMode === 'page' ? EmbedPageIcon : EmbedEdgelessIcon,
SyncedDocErrorIcon,
SyncedDocDeletedIcon,
ReloadIcon,
SyncedDocEmptyBanner: LightSyncedDocEmptyBanner,
SyncedDocErrorBanner: LightSyncedDocErrorBanner,
SyncedDocDeletedBanner: LightSyncedDocDeletedBanner,
};
} else {
return {
LoadingIcon,
SyncedDocIcon: editorMode === 'page' ? EmbedPageIcon : EmbedEdgelessIcon,
SyncedDocErrorIcon,
SyncedDocDeletedIcon,
ReloadIcon,
SyncedDocEmptyBanner: DarkSyncedDocEmptyBanner,
SyncedDocErrorBanner: DarkSyncedDocErrorBanner,
SyncedDocDeletedBanner: DarkSyncedDocDeletedBanner,
+1 -1
View File
@@ -26,7 +26,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.15",
"@toeverything/theme": "^1.1.14",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.21",
@@ -50,6 +50,12 @@ export class EmbedBlockComponent<
_cardStyle: EmbedCardStyle = 'horizontal';
/**
* The actual rendered scale of the embed card.
* By default, it is set to 1.
*/
protected _scale = 1;
blockDraggable = true;
/**
@@ -68,6 +68,7 @@ export function toEdgelessEmbedBlock<
this.blockContainerStyles = {
width: `${bound.w}px`,
};
this._scale = bound.w / this._cardWidth;
return this.renderPageContent();
}
@@ -9,11 +9,13 @@ import {
EmbedCardLightHorizontalIcon,
EmbedCardLightListIcon,
EmbedCardLightVerticalIcon,
getLoadingIconWith,
} from '@blocksuite/affine-components/icons';
import { ColorScheme } from '@blocksuite/affine-model';
import type { TemplateResult } from 'lit';
type EmbedCardIcons = {
LoadingIcon: TemplateResult<1>;
EmbedCardBannerIcon: TemplateResult<1>;
EmbedCardHorizontalIcon: TemplateResult<1>;
EmbedCardListIcon: TemplateResult<1>;
@@ -22,8 +24,11 @@ type EmbedCardIcons = {
};
export function getEmbedCardIcons(theme: ColorScheme): EmbedCardIcons {
const LoadingIcon = getLoadingIconWith(theme);
if (theme === ColorScheme.Light) {
return {
LoadingIcon,
EmbedCardBannerIcon: EmbedCardLightBannerIcon,
EmbedCardHorizontalIcon: EmbedCardLightHorizontalIcon,
EmbedCardListIcon: EmbedCardLightListIcon,
@@ -32,6 +37,7 @@ export function getEmbedCardIcons(theme: ColorScheme): EmbedCardIcons {
};
} else {
return {
LoadingIcon,
EmbedCardBannerIcon: EmbedCardDarkBannerIcon,
EmbedCardHorizontalIcon: EmbedCardDarkHorizontalIcon,
EmbedCardListIcon: EmbedCardDarkListIcon,
@@ -153,7 +153,7 @@ function createBuiltinToolbarConfigForExternal(
.get(EmbedOptionProvider)
.getEmbedBlockOptions(url);
let style: EmbedCardStyle = model.props.style;
let { style } = model.props;
let flavour = 'affine:bookmark';
if (options?.viewType === 'card') {
@@ -227,7 +227,7 @@ function createBuiltinToolbarConfigForExternal(
if (options?.viewType !== 'embed') return;
const { flavour, styles } = options;
let style: EmbedCardStyle = model.props.style;
let { style } = model.props;
if (!styles.includes(style)) {
style =
@@ -441,11 +441,7 @@ const createBuiltinSurfaceToolbarConfigForExternal = (
let { style } = model.props;
let flavour = 'affine:bookmark';
if (
!BookmarkStyles.includes(
style as (typeof BookmarkStyles)[number]
)
) {
if (!BookmarkStyles.includes(style)) {
style = BookmarkStyles[0];
}
@@ -521,26 +517,26 @@ const createBuiltinSurfaceToolbarConfigForExternal = (
} satisfies ToolbarActionGroup<ToolbarAction>,
{
id: 'c.style',
actions: (
[
{
id: 'horizontal',
label: 'Large horizontal style',
},
{
id: 'list',
label: 'Small horizontal style',
},
{
id: 'vertical',
label: 'Large vertical style',
},
{
id: 'cube',
label: 'Small vertical style',
},
] as const
).filter(action => EmbedGithubStyles.includes(action.id)),
actions: [
{
id: 'horizontal',
label: 'Large horizontal style',
},
{
id: 'list',
label: 'Small horizontal style',
},
{
id: 'vertical',
label: 'Large vertical style',
},
{
id: 'cube',
label: 'Small vertical style',
},
].filter(action =>
EmbedGithubStyles.includes(action.id as EmbedCardStyle)
),
when(ctx) {
return Boolean(ctx.getCurrentModelByType(EmbedGithubModel));
},
@@ -6,6 +6,7 @@ import type {
import { BlockSelection } from '@blocksuite/std';
import { html, nothing } from 'lit';
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 { FigmaIcon, styles } from './styles.js';
@@ -75,6 +76,10 @@ export class EmbedFigmaBlockComponent extends EmbedBlockComponent<EmbedFigmaMode
'affine-embed-figma-block': true,
selected: this.selected$.value,
})}
style=${styleMap({
transform: `scale(${this._scale})`,
transformOrigin: '0 0',
})}
@click=${this._handleClick}
@dblclick=${this._handleDoubleClick}
>
@@ -1,4 +1,4 @@
import { LoadingIcon, OpenIcon } from '@blocksuite/affine-components/icons';
import { OpenIcon } from '@blocksuite/affine-components/icons';
import type {
EmbedGithubModel,
EmbedGithubStyles,
@@ -133,8 +133,8 @@ export class EmbedGithubBlockComponent extends EmbedBlockComponent<
const loading = this.loading;
const theme = this.std.get(ThemeProvider).theme;
const imageProxyService = this.store.get(ImageProxyService);
const { EmbedCardBannerIcon } = getEmbedCardIcons(theme);
const titleIcon = loading ? LoadingIcon() : GithubIcon;
const { LoadingIcon, EmbedCardBannerIcon } = getEmbedCardIcons(theme);
const titleIcon = loading ? LoadingIcon : GithubIcon;
const statusIcon = status
? getGithubStatusIcon(githubType, status, statusReason)
: nothing;
@@ -1,4 +1,4 @@
import { LoadingIcon } from '@blocksuite/affine-components/icons';
import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { EmbedIcon } from '@blocksuite/icons/lit';
import { type BlockStdScope } from '@blocksuite/std';
@@ -7,6 +7,7 @@ import { property } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { styleMap } from 'lit/directives/style-map.js';
import { getEmbedCardIcons } from '../../common/utils';
import { LOADING_CARD_DEFAULT_HEIGHT } from '../consts';
import type { EmbedIframeStatusCardOptions } from '../types';
@@ -155,6 +156,9 @@ export class EmbedIframeLoadingCard extends LitElement {
`;
override render() {
const theme = this.std.get(ThemeProvider).theme;
const { LoadingIcon } = getEmbedCardIcons(theme);
const { layout, width, height } = this.options;
const cardClasses = classMap({
'affine-embed-iframe-loading-card': true,
@@ -172,7 +176,7 @@ export class EmbedIframeLoadingCard extends LitElement {
return html`
<div class=${cardClasses} style=${cardStyle}>
<div class="loading-content">
<div class="loading-spinner">${LoadingIcon()}</div>
<div class="loading-spinner">${LoadingIcon}</div>
<div class="loading-text">Loading...</div>
</div>
<div class="loading-banner">
@@ -1,4 +1,4 @@
import { LoadingIcon, OpenIcon } from '@blocksuite/affine-components/icons';
import { OpenIcon } from '@blocksuite/affine-components/icons';
import type { EmbedLoomModel, EmbedLoomStyles } from '@blocksuite/affine-model';
import { ImageProxyService } from '@blocksuite/affine-shared/adapters';
import { ThemeProvider } from '@blocksuite/affine-shared/services';
@@ -94,8 +94,8 @@ export class EmbedLoomBlockComponent extends EmbedBlockComponent<
const loading = this.loading;
const theme = this.std.get(ThemeProvider).theme;
const imageProxyService = this.store.get(ImageProxyService);
const { EmbedCardBannerIcon } = getEmbedCardIcons(theme);
const titleIcon = loading ? LoadingIcon() : LoomIcon;
const { LoadingIcon, EmbedCardBannerIcon } = getEmbedCardIcons(theme);
const titleIcon = loading ? LoadingIcon : LoomIcon;
const titleText = loading ? 'Loading...' : title;
const descriptionText = loading ? '' : description;
const bannerImage =
@@ -112,6 +112,7 @@ export class EmbedLoomBlockComponent extends EmbedBlockComponent<
selected: this.selected$.value,
})}
style=${styleMap({
transform: `scale(${this._scale})`,
transformOrigin: '0 0',
})}
@click=${this._handleClick}
@@ -1,4 +1,4 @@
import { LoadingIcon, OpenIcon } from '@blocksuite/affine-components/icons';
import { OpenIcon } from '@blocksuite/affine-components/icons';
import type {
EmbedYoutubeModel,
EmbedYoutubeStyles,
@@ -108,8 +108,8 @@ export class EmbedYoutubeBlockComponent extends EmbedBlockComponent<
const loading = this.loading;
const theme = this.std.get(ThemeProvider).theme;
const imageProxyService = this.store.get(ImageProxyService);
const { EmbedCardBannerIcon } = getEmbedCardIcons(theme);
const titleIcon = loading ? LoadingIcon() : YoutubeIcon;
const { LoadingIcon, EmbedCardBannerIcon } = getEmbedCardIcons(theme);
const titleIcon = loading ? LoadingIcon : YoutubeIcon;
const titleText = loading ? 'Loading...' : title;
const descriptionText = loading ? null : description;
const bannerImage =
+1 -1
View File
@@ -25,7 +25,7 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.15",
"@toeverything/theme": "^1.1.14",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
@@ -205,11 +205,10 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
!forceMove
) {
// Clear the flag so future navigations behave normally
// Here we modify the tool's activated option to avoid triggering setTool update
const currentTool = this.gfx.tool.currentTool$.peek();
if (currentTool?.activatedOption) {
currentTool.activatedOption.restoredAfterPan = false;
}
this.gfx.tool.setTool(PresentTool, {
...toolOptions,
restoredAfterPan: false,
});
return;
}
@@ -3,7 +3,6 @@ import {
DefaultTheme,
type FrameBlockModel,
FrameBlockSchema,
isTransparent,
} from '@blocksuite/affine-model';
import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { Bound } from '@blocksuite/global/gfx';
@@ -12,11 +11,11 @@ import {
type BoxSelectionContext,
getTopElements,
GfxViewInteractionExtension,
type SelectedContext,
} from '@blocksuite/std/gfx';
import { cssVarV2 } from '@toeverything/theme/v2';
import { html } from 'lit';
import { state } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import { styleMap } from 'lit/directives/style-map.js';
import {
@@ -69,6 +68,22 @@ export class FrameBlockComponent extends GfxBlockComponent<FrameBlockModel> {
};
}
override onSelected(context: SelectedContext): boolean | void {
const { x, y } = context.position;
if (
!context.fallback &&
// if the frame is selected by title, then ignore it because the title selection is handled by the title widget
(this.model.externalBound?.containsPoint([x, y]) ||
// otherwise if the frame has title, then ignore it because in this case the frame cannot be selected by frame body
this.model.props.title.length)
) {
return false;
}
return super.onSelected(context);
}
override onBoxSelected(context: BoxSelectionContext) {
const { box } = context;
const bound = new Bound(box.x, box.y, box.w, box.h);
@@ -88,12 +103,6 @@ export class FrameBlockComponent extends GfxBlockComponent<FrameBlockModel> {
this.gfx.tool.currentToolName$.value === 'frameNavigator';
const frameIndex = this.gfx.layer.getZIndex(model);
const widgets = html`${repeat(
Object.entries(this.widgets),
([id]) => id,
([_, widget]) => widget
)}`;
return html`
<div
class="affine-frame-container"
@@ -109,7 +118,6 @@ export class FrameBlockComponent extends GfxBlockComponent<FrameBlockModel> {
: `1px solid ${cssVarV2('edgeless/frame/border/default')}`,
})}
></div>
${widgets}
`;
}
@@ -181,28 +189,5 @@ export const FrameBlockInteraction =
},
};
},
handleSelection: () => {
return {
selectable(context) {
const { model } = context;
const onTitle =
model.externalBound?.containsPoint([
context.position.x,
context.position.y,
]) ?? false;
return (
context.default(context) &&
(model.isLocked() ||
!isTransparent(model.props.background) ||
onTitle)
);
},
onSelect(context) {
return context.default(context);
},
};
},
}
);
@@ -241,35 +241,20 @@ export class EdgelessFrameManager extends GfxExtension {
surfaceModel.elementAdded.subscribe(({ id, local }) => {
const element = surfaceModel.getElementById(id);
if (element && local) {
// The entire frame detection logic must be in microtask for timing reasons:
//
// 1. For connectors: When elementAdded fires, connectors have invalid bounds [0,0,0,0]
// because their path/bounds are calculated in a separate microtask of updateConnectorPath by connector-watcher.
// We need to wait for that calculation to complete before frame detection.
//
// 2. For shapes: Although they have valid bounds immediately, processing them in microtask
// ensures consistent timing and allows other initialization to complete first.
//
// 3. Group compatibility: Some elements may need to establish their group relationships
// before being considered for frame membership.
//
// By embedding the entire logic in microtask, we ensure:
// - Connectors have proper bounds calculated (not [0,0,0,0])
// - getFrameFromPoint() works correctly with valid element centers
// - All element initialization is complete before frame detection
const frame = this.getFrameFromPoint(element.elementBound.center);
// if the container created with a frame, skip it.
if (
isGfxGroupCompatibleModel(element) &&
frame &&
element.hasChild(frame)
) {
return;
}
// new element may intended to be added to other group
// so we need to wait for the next microtask to check if the element can be added to the frame
queueMicrotask(() => {
const frame = this.getFrameFromPoint(element.elementBound.center);
// if the container created with a frame, skip it.
if (
isGfxGroupCompatibleModel(element) &&
frame &&
element.hasChild(frame)
) {
return;
}
// Only add elements that aren't already grouped and have a valid frame
if (!element.group && frame) {
this.addElementsToFrame(frame, [element]);
}
@@ -160,30 +160,19 @@ const builtinSurfaceToolbarConfig = {
background => resolveColor(background, theme)
) ?? DefaultTheme.transparent;
const onPick = (e: PickColorEvent) => {
switch (e.type) {
case 'pick':
{
const color = e.detail.value;
const props = packColor(field, color);
const crud = ctx.std.get(EdgelessCRUDIdentifier);
models.forEach(model => {
crud.updateElement(model.id, props);
});
}
break;
case 'start':
ctx.store.captureSync();
models.forEach(model => {
model.stash(field);
});
break;
case 'end':
ctx.store.transact(() => {
models.forEach(model => {
model.pop(field);
});
});
break;
if (e.type === 'pick') {
const color = e.detail.value;
for (const model of models) {
const props = packColor(field, color);
ctx.std
.get(EdgelessCRUDIdentifier)
.updateElement(model.id, props);
}
return;
}
for (const model of models) {
model[e.type === 'start' ? 'stash' : 'pop'](field);
}
};

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