mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-04 19:15:33 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 914e4baf82 | |||
| bd268044b4 |
@@ -3,13 +3,4 @@ DB_VERSION=16
|
|||||||
# database credentials
|
# database credentials
|
||||||
DB_PASSWORD=affine
|
DB_PASSWORD=affine
|
||||||
DB_USERNAME=affine
|
DB_USERNAME=affine
|
||||||
DB_DATABASE_NAME=affine
|
DB_DATABASE_NAME=affine
|
||||||
|
|
||||||
# elasticsearch env
|
|
||||||
# ELASTIC_VERSION=9.0.1
|
|
||||||
# enable for arm64, e.g.: macOS M1+
|
|
||||||
# ELASTIC_VERSION_ARM64=-arm64
|
|
||||||
# ELASTIC_PLATFORM=linux/arm64
|
|
||||||
|
|
||||||
# manticoresearch
|
|
||||||
MANTICORE_VERSION=9.2.14
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
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:
|
|
||||||
@@ -24,26 +24,8 @@ services:
|
|||||||
- 1025:1025
|
- 1025:1025
|
||||||
- 8025:8025
|
- 8025:8025
|
||||||
|
|
||||||
# https://manual.manticoresearch.com/Starting_the_server/Docker
|
|
||||||
manticoresearch:
|
|
||||||
image: manticoresearch/manticore:${MANTICORE_VERSION:-9.2.14}
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- 9308:9308
|
|
||||||
ulimits:
|
|
||||||
nproc: 65535
|
|
||||||
nofile:
|
|
||||||
soft: 65535
|
|
||||||
hard: 65535
|
|
||||||
memlock:
|
|
||||||
soft: -1
|
|
||||||
hard: -1
|
|
||||||
volumes:
|
|
||||||
- manticoresearch_data:/var/lib/manticore
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
dev:
|
dev:
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
manticoresearch_data:
|
|
||||||
|
|||||||
@@ -20,9 +20,4 @@ CONFIG_LOCATION=~/.affine/self-host/config
|
|||||||
# database credentials
|
# database credentials
|
||||||
DB_USERNAME=affine
|
DB_USERNAME=affine
|
||||||
DB_PASSWORD=
|
DB_PASSWORD=
|
||||||
DB_DATABASE=affine
|
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
|
|
||||||
@@ -10,8 +10,6 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
indexer:
|
|
||||||
condition: service_healthy
|
|
||||||
affine_migration:
|
affine_migration:
|
||||||
condition: service_completed_successfully
|
condition: service_completed_successfully
|
||||||
volumes:
|
volumes:
|
||||||
@@ -43,8 +41,6 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
redis:
|
redis:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
indexer:
|
|
||||||
condition: service_healthy
|
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis
|
image: redis
|
||||||
@@ -76,24 +72,3 @@ services:
|
|||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
restart: unless-stopped
|
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
|
|
||||||
|
|||||||
@@ -70,18 +70,6 @@
|
|||||||
"concurrency": 1
|
"concurrency": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"queues.indexer": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "The config for indexer job queue\n@default {\"concurrency\":1}",
|
|
||||||
"properties": {
|
|
||||||
"concurrency": {
|
|
||||||
"type": "number"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"default": {
|
|
||||||
"concurrency": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"queues.notification": {
|
"queues.notification": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The config for notification job queue\n@default {\"concurrency\":10}",
|
"description": "The config for notification job queue\n@default {\"concurrency\":10}",
|
||||||
@@ -135,6 +123,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"websocket": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Configuration for websocket module",
|
||||||
|
"properties": {
|
||||||
|
"transports": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "The enabled transports for accepting websocket traffics.\n@default [\"websocket\",\"polling\"]\n@link https://docs.nestjs.com/websockets/gateways#transports",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"websocket",
|
||||||
|
"polling"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": [
|
||||||
|
"websocket",
|
||||||
|
"polling"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"maxHttpBufferSize": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "How many bytes or characters a message can be, before closing the session (to avoid DoS).\n@default 100000000",
|
||||||
|
"default": 100000000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Configuration for auth module",
|
"description": "Configuration for auth module",
|
||||||
@@ -487,32 +501,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"websocket": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Configuration for websocket module",
|
|
||||||
"properties": {
|
|
||||||
"transports": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "The enabled transports for accepting websocket traffics.\n@default [\"websocket\",\"polling\"]\n@link https://docs.nestjs.com/websockets/gateways#transports",
|
|
||||||
"items": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"websocket",
|
|
||||||
"polling"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"default": [
|
|
||||||
"websocket",
|
|
||||||
"polling"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"maxHttpBufferSize": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "How many bytes or characters a message can be, before closing the session (to avoid DoS).\n@default 100000000",
|
|
||||||
"default": 100000000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"server": {
|
"server": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Configuration for server module",
|
"description": "Configuration for server module",
|
||||||
@@ -646,13 +634,6 @@
|
|||||||
"apiKey": ""
|
"apiKey": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"providers.anthropic": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "The config for the anthropic provider.\n@default {\"apiKey\":\"\"}",
|
|
||||||
"default": {
|
|
||||||
"apiKey": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"unsplash": {
|
"unsplash": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The config for the unsplash key.\n@default {\"key\":\"\"}",
|
"description": "The config for the unsplash key.\n@default {\"key\":\"\"}",
|
||||||
@@ -660,13 +641,6 @@
|
|||||||
"key": ""
|
"key": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"exa": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "The config for the exa web search key.\n@default {\"key\":\"\"}",
|
|
||||||
"default": {
|
|
||||||
"key": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"storage": {
|
"storage": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The config for the storage provider.\n@default {\"provider\":\"fs\",\"bucket\":\"copilot\",\"config\":{\"path\":\"~/.affine/storage\"}}",
|
"description": "The config for the storage provider.\n@default {\"provider\":\"fs\",\"bucket\":\"copilot\",\"config\":{\"path\":\"~/.affine/storage\"}}",
|
||||||
@@ -806,37 +780,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"indexer": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Configuration for indexer module",
|
|
||||||
"properties": {
|
|
||||||
"enabled": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Enable indexer plugin\n@default true",
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"provider.type": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Indexer search service provider name\n@default \"manticoresearch\"\n@environment `AFFINE_INDEXER_SEARCH_PROVIDER`",
|
|
||||||
"default": "manticoresearch"
|
|
||||||
},
|
|
||||||
"provider.endpoint": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Indexer search service endpoint\n@default \"http://localhost:9308\"\n@environment `AFFINE_INDEXER_SEARCH_ENDPOINT`",
|
|
||||||
"default": "http://localhost:9308"
|
|
||||||
},
|
|
||||||
"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",
|
|
||||||
"default": ""
|
|
||||||
},
|
|
||||||
"provider.password": {
|
|
||||||
"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": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"oauth": {
|
"oauth": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Configuration for oauth module",
|
"description": "Configuration for oauth module",
|
||||||
|
|||||||
@@ -16,10 +16,6 @@ const {
|
|||||||
REDIS_SERVER_HOST,
|
REDIS_SERVER_HOST,
|
||||||
REDIS_SERVER_PASSWORD,
|
REDIS_SERVER_PASSWORD,
|
||||||
STATIC_IP_NAME,
|
STATIC_IP_NAME,
|
||||||
AFFINE_INDEXER_SEARCH_PROVIDER,
|
|
||||||
AFFINE_INDEXER_SEARCH_ENDPOINT,
|
|
||||||
AFFINE_INDEXER_SEARCH_USERNAME,
|
|
||||||
AFFINE_INDEXER_SEARCH_PASSWORD,
|
|
||||||
} = process.env;
|
} = process.env;
|
||||||
|
|
||||||
const buildType = BUILD_TYPE || 'canary';
|
const buildType = BUILD_TYPE || 'canary';
|
||||||
@@ -85,12 +81,6 @@ const createHelmCommand = ({ isDryRun }) => {
|
|||||||
`--set-string global.redis.password="${REDIS_SERVER_PASSWORD}"`,
|
`--set-string global.redis.password="${REDIS_SERVER_PASSWORD}"`,
|
||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
const indexerOptions = [
|
|
||||||
`--set-string global.indexer.provider="${AFFINE_INDEXER_SEARCH_PROVIDER}"`,
|
|
||||||
`--set-string global.indexer.endpoint="${AFFINE_INDEXER_SEARCH_ENDPOINT}"`,
|
|
||||||
`--set-string global.indexer.username="${AFFINE_INDEXER_SEARCH_USERNAME}"`,
|
|
||||||
`--set-string global.indexer.password="${AFFINE_INDEXER_SEARCH_PASSWORD}"`,
|
|
||||||
];
|
|
||||||
const serviceAnnotations = [
|
const serviceAnnotations = [
|
||||||
`--set-json web.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
|
`--set-json web.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
|
||||||
`--set-json graphql.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
|
`--set-json graphql.serviceAccount.annotations="{ \\"iam.gke.io/gcp-service-account\\": \\"${APP_IAM_ACCOUNT}\\" }"`,
|
||||||
@@ -140,7 +130,6 @@ const createHelmCommand = ({ isDryRun }) => {
|
|||||||
`--set-string global.ingress.host="${host}"`,
|
`--set-string global.ingress.host="${host}"`,
|
||||||
`--set-string global.version="${APP_VERSION}"`,
|
`--set-string global.version="${APP_VERSION}"`,
|
||||||
...redisAndPostgres,
|
...redisAndPostgres,
|
||||||
...indexerOptions,
|
|
||||||
`--set web.replicaCount=${replica.web}`,
|
`--set web.replicaCount=${replica.web}`,
|
||||||
`--set-string web.image.tag="${imageTag}"`,
|
`--set-string web.image.tag="${imageTag}"`,
|
||||||
`--set graphql.replicaCount=${replica.graphql}`,
|
`--set graphql.replicaCount=${replica.graphql}`,
|
||||||
|
|||||||
@@ -4,11 +4,6 @@ description: 'Prepare Server Test Environment'
|
|||||||
runs:
|
runs:
|
||||||
using: 'composite'
|
using: 'composite'
|
||||||
steps:
|
steps:
|
||||||
- name: Bundle @affine/reader
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
yarn affine @affine/reader build
|
|
||||||
|
|
||||||
- name: Initialize database
|
- name: Initialize database
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -26,7 +21,6 @@ runs:
|
|||||||
yarn affine @affine/server prisma generate
|
yarn affine @affine/server prisma generate
|
||||||
yarn affine @affine/server prisma migrate deploy
|
yarn affine @affine/server prisma migrate deploy
|
||||||
yarn affine @affine/server data-migration run
|
yarn affine @affine/server data-migration run
|
||||||
|
|
||||||
- name: Import config
|
- name: Import config
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -69,17 +69,6 @@ spec:
|
|||||||
key: redis-password
|
key: redis-password
|
||||||
- name: REDIS_SERVER_DATABASE
|
- name: REDIS_SERVER_DATABASE
|
||||||
value: "{{ .Values.global.redis.database }}"
|
value: "{{ .Values.global.redis.database }}"
|
||||||
- name: AFFINE_INDEXER_SEARCH_PROVIDER
|
|
||||||
value: "{{ .Values.global.indexer.provider }}"
|
|
||||||
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
|
|
||||||
value: "{{ .Values.global.indexer.endpoint }}"
|
|
||||||
- name: AFFINE_INDEXER_SEARCH_USERNAME
|
|
||||||
value: "{{ .Values.global.indexer.username }}"
|
|
||||||
- name: AFFINE_INDEXER_SEARCH_PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: indexer
|
|
||||||
key: indexer-password
|
|
||||||
- name: AFFINE_SERVER_PORT
|
- name: AFFINE_SERVER_PORT
|
||||||
value: "{{ .Values.global.docService.port }}"
|
value: "{{ .Values.global.docService.port }}"
|
||||||
- name: AFFINE_SERVER_SUB_PATH
|
- name: AFFINE_SERVER_SUB_PATH
|
||||||
|
|||||||
@@ -67,17 +67,6 @@ spec:
|
|||||||
key: redis-password
|
key: redis-password
|
||||||
- name: REDIS_SERVER_DATABASE
|
- name: REDIS_SERVER_DATABASE
|
||||||
value: "{{ .Values.global.redis.database }}"
|
value: "{{ .Values.global.redis.database }}"
|
||||||
- name: AFFINE_INDEXER_SEARCH_PROVIDER
|
|
||||||
value: "{{ .Values.global.indexer.provider }}"
|
|
||||||
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
|
|
||||||
value: "{{ .Values.global.indexer.endpoint }}"
|
|
||||||
- name: AFFINE_INDEXER_SEARCH_USERNAME
|
|
||||||
value: "{{ .Values.global.indexer.username }}"
|
|
||||||
- name: AFFINE_INDEXER_SEARCH_PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: indexer
|
|
||||||
key: indexer-password
|
|
||||||
- name: AFFINE_SERVER_PORT
|
- name: AFFINE_SERVER_PORT
|
||||||
value: "{{ .Values.service.port }}"
|
value: "{{ .Values.service.port }}"
|
||||||
- name: AFFINE_SERVER_SUB_PATH
|
- name: AFFINE_SERVER_SUB_PATH
|
||||||
|
|||||||
@@ -44,17 +44,6 @@ spec:
|
|||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: redis
|
name: redis
|
||||||
key: redis-password
|
key: redis-password
|
||||||
- name: AFFINE_INDEXER_SEARCH_PROVIDER
|
|
||||||
value: "{{ .Values.global.indexer.provider }}"
|
|
||||||
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
|
|
||||||
value: "{{ .Values.global.indexer.endpoint }}"
|
|
||||||
- name: AFFINE_INDEXER_SEARCH_USERNAME
|
|
||||||
value: "{{ .Values.global.indexer.username }}"
|
|
||||||
- name: AFFINE_INDEXER_SEARCH_PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: indexer
|
|
||||||
key: indexer-password
|
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
cpu: '100m'
|
cpu: '100m'
|
||||||
|
|||||||
@@ -69,17 +69,6 @@ spec:
|
|||||||
key: redis-password
|
key: redis-password
|
||||||
- name: REDIS_SERVER_DATABASE
|
- name: REDIS_SERVER_DATABASE
|
||||||
value: "{{ .Values.global.redis.database }}"
|
value: "{{ .Values.global.redis.database }}"
|
||||||
- name: AFFINE_INDEXER_SEARCH_PROVIDER
|
|
||||||
value: "{{ .Values.global.indexer.provider }}"
|
|
||||||
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
|
|
||||||
value: "{{ .Values.global.indexer.endpoint }}"
|
|
||||||
- name: AFFINE_INDEXER_SEARCH_USERNAME
|
|
||||||
value: "{{ .Values.global.indexer.username }}"
|
|
||||||
- name: AFFINE_INDEXER_SEARCH_PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: indexer
|
|
||||||
key: indexer-password
|
|
||||||
- name: AFFINE_SERVER_PORT
|
- name: AFFINE_SERVER_PORT
|
||||||
value: "{{ .Values.service.port }}"
|
value: "{{ .Values.service.port }}"
|
||||||
- name: AFFINE_SERVER_SUB_PATH
|
- name: AFFINE_SERVER_SUB_PATH
|
||||||
|
|||||||
@@ -69,17 +69,6 @@ spec:
|
|||||||
key: redis-password
|
key: redis-password
|
||||||
- name: REDIS_SERVER_DATABASE
|
- name: REDIS_SERVER_DATABASE
|
||||||
value: "{{ .Values.global.redis.database }}"
|
value: "{{ .Values.global.redis.database }}"
|
||||||
- name: AFFINE_INDEXER_SEARCH_PROVIDER
|
|
||||||
value: "{{ .Values.global.indexer.provider }}"
|
|
||||||
- name: AFFINE_INDEXER_SEARCH_ENDPOINT
|
|
||||||
value: "{{ .Values.global.indexer.endpoint }}"
|
|
||||||
- name: AFFINE_INDEXER_SEARCH_USERNAME
|
|
||||||
value: "{{ .Values.global.indexer.username }}"
|
|
||||||
- name: AFFINE_INDEXER_SEARCH_PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: indexer
|
|
||||||
key: indexer-password
|
|
||||||
- name: AFFINE_SERVER_PORT
|
- name: AFFINE_SERVER_PORT
|
||||||
value: "{{ .Values.service.port }}"
|
value: "{{ .Values.service.port }}"
|
||||||
- name: AFFINE_SERVER_HOST
|
- name: AFFINE_SERVER_HOST
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
{{- if .Values.global.indexer.password -}}
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: indexer
|
|
||||||
annotations:
|
|
||||||
"helm.sh/hook": pre-install,pre-upgrade
|
|
||||||
"helm.sh/hook-weight": "-2"
|
|
||||||
"helm.sh/hook-delete-policy": before-hook-creation
|
|
||||||
type: Opaque
|
|
||||||
data:
|
|
||||||
indexer-password: {{ .Values.global.indexer.password | b64enc }}
|
|
||||||
{{- end }}
|
|
||||||
@@ -21,11 +21,6 @@ global:
|
|||||||
username: ''
|
username: ''
|
||||||
password: ''
|
password: ''
|
||||||
database: 0
|
database: 0
|
||||||
indexer:
|
|
||||||
provider: ''
|
|
||||||
endpoint: ''
|
|
||||||
username: ''
|
|
||||||
password: ''
|
|
||||||
docService:
|
docService:
|
||||||
name: 'affine-doc'
|
name: 'affine-doc'
|
||||||
port: 3020
|
port: 3020
|
||||||
|
|||||||
@@ -136,8 +136,6 @@ jobs:
|
|||||||
extra-flags: workspaces focus @affine/server-native
|
extra-flags: workspaces focus @affine/server-native
|
||||||
- name: Build Rust
|
- name: Build Rust
|
||||||
uses: ./.github/actions/build-rust
|
uses: ./.github/actions/build-rust
|
||||||
env:
|
|
||||||
AFFINE_PRO_PUBLIC_KEY: ${{ secrets.AFFINE_PRO_PUBLIC_KEY }}
|
|
||||||
with:
|
with:
|
||||||
target: ${{ matrix.targets.name }}
|
target: ${{ matrix.targets.name }}
|
||||||
package: '@affine/server-native'
|
package: '@affine/server-native'
|
||||||
@@ -174,8 +172,6 @@ jobs:
|
|||||||
path: ./packages/backend/native
|
path: ./packages/backend/native
|
||||||
- name: List server-native files
|
- name: List server-native files
|
||||||
run: ls -alh ./packages/backend/native
|
run: ls -alh ./packages/backend/native
|
||||||
- name: Build @affine/reader
|
|
||||||
run: yarn workspace @affine/reader build
|
|
||||||
- name: Build Server
|
- name: Build Server
|
||||||
run: yarn workspace @affine/server build
|
run: yarn workspace @affine/server build
|
||||||
- name: Upload server dist
|
- name: Upload server dist
|
||||||
@@ -257,9 +253,6 @@ jobs:
|
|||||||
- name: Generate Prisma client
|
- name: Generate Prisma client
|
||||||
run: yarn workspace @affine/server prisma generate
|
run: yarn workspace @affine/server prisma generate
|
||||||
|
|
||||||
- name: Mv node_modules
|
|
||||||
run: mv ./node_modules ./packages/backend/server
|
|
||||||
|
|
||||||
- name: Setup Version
|
- name: Setup Version
|
||||||
id: version
|
id: version
|
||||||
uses: ./.github/actions/setup-version
|
uses: ./.github/actions/setup-version
|
||||||
|
|||||||
@@ -157,7 +157,6 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
- optimize_ci
|
- optimize_ci
|
||||||
- build-server-native
|
|
||||||
if: needs.optimize_ci.outputs.skip == 'false'
|
if: needs.optimize_ci.outputs.skip == 'false'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -166,25 +165,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
full-cache: true
|
full-cache: true
|
||||||
|
|
||||||
- name: Download server-native.node
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: server-native.node
|
|
||||||
path: ./packages/backend/native
|
|
||||||
|
|
||||||
- name: Bundle @affine/reader
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
yarn workspace @affine/reader build
|
|
||||||
|
|
||||||
- name: Run Check
|
- name: Run Check
|
||||||
run: |
|
run: |
|
||||||
yarn affine init
|
yarn affine init
|
||||||
yarn affine gql build
|
yarn affine gql build
|
||||||
yarn affine i18n build
|
yarn affine i18n build
|
||||||
yarn affine server genconfig
|
|
||||||
git status --porcelain | grep . && {
|
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"
|
echo "Run 'yarn affine init && yarn affine gql build && yarn affine i18n build' and make sure all changes are submitted"
|
||||||
exit 1
|
exit 1
|
||||||
} || {
|
} || {
|
||||||
echo "All changes are submitted"
|
echo "All changes are submitted"
|
||||||
@@ -582,25 +569,7 @@ jobs:
|
|||||||
ports:
|
ports:
|
||||||
- 1025:1025
|
- 1025:1025
|
||||||
- 8025:8025
|
- 8025:8025
|
||||||
manticoresearch:
|
|
||||||
image: manticoresearch/manticore:9.2.14
|
|
||||||
ports:
|
|
||||||
- 9308:9308
|
|
||||||
steps:
|
steps:
|
||||||
# https://github.com/elastic/elastic-github-actions/blob/master/elasticsearch/README.md
|
|
||||||
- name: Configure sysctl limits for Elasticsearch
|
|
||||||
run: |
|
|
||||||
sudo swapoff -a
|
|
||||||
sudo sysctl -w vm.swappiness=1
|
|
||||||
sudo sysctl -w fs.file-max=262144
|
|
||||||
sudo sysctl -w vm.max_map_count=262144
|
|
||||||
|
|
||||||
- name: Runs Elasticsearch
|
|
||||||
uses: elastic/elastic-github-actions/elasticsearch@master
|
|
||||||
with:
|
|
||||||
stack-version: 9.0.1
|
|
||||||
security-enabled: false
|
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
@@ -662,10 +631,6 @@ jobs:
|
|||||||
image: redis
|
image: redis
|
||||||
ports:
|
ports:
|
||||||
- 6379:6379
|
- 6379:6379
|
||||||
indexer:
|
|
||||||
image: manticoresearch/manticore:9.2.14
|
|
||||||
ports:
|
|
||||||
- 9308:9308
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -1103,10 +1068,6 @@ jobs:
|
|||||||
ports:
|
ports:
|
||||||
- 1025:1025
|
- 1025:1025
|
||||||
- 8025:8025
|
- 8025:8025
|
||||||
indexer:
|
|
||||||
image: manticoresearch/manticore:9.2.14
|
|
||||||
ports:
|
|
||||||
- 9308:9308
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
|||||||
@@ -103,10 +103,6 @@ jobs:
|
|||||||
CLOUD_SQL_IAM_ACCOUNT: ${{ secrets.CLOUD_SQL_IAM_ACCOUNT }}
|
CLOUD_SQL_IAM_ACCOUNT: ${{ secrets.CLOUD_SQL_IAM_ACCOUNT }}
|
||||||
APP_IAM_ACCOUNT: ${{ secrets.APP_IAM_ACCOUNT }}
|
APP_IAM_ACCOUNT: ${{ secrets.APP_IAM_ACCOUNT }}
|
||||||
STATIC_IP_NAME: ${{ secrets.STATIC_IP_NAME }}
|
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_USERNAME: ${{ secrets.AFFINE_INDEXER_SEARCH_USERNAME }}
|
|
||||||
AFFINE_INDEXER_SEARCH_PASSWORD: ${{ secrets.AFFINE_INDEXER_SEARCH_PASSWORD }}
|
|
||||||
|
|
||||||
deploy-done:
|
deploy-done:
|
||||||
needs:
|
needs:
|
||||||
@@ -160,7 +156,7 @@ jobs:
|
|||||||
BLOCKSUITE_REPO_PATH: ${{ github.workspace }}/blocksuite
|
BLOCKSUITE_REPO_PATH: ${{ github.workspace }}/blocksuite
|
||||||
- name: Post Failed event to a Slack channel
|
- name: Post Failed event to a Slack channel
|
||||||
id: failed-slack
|
id: failed-slack
|
||||||
uses: slackapi/slack-github-action@v2.1.0
|
uses: slackapi/slack-github-action@v2.0.0
|
||||||
if: ${{ always() && contains(needs.*.result, 'failure') }}
|
if: ${{ always() && contains(needs.*.result, 'failure') }}
|
||||||
with:
|
with:
|
||||||
method: chat.postMessage
|
method: chat.postMessage
|
||||||
@@ -175,7 +171,7 @@ jobs:
|
|||||||
text: "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Backend deploy failed `${{ github.event.inputs.flavor }}`>"
|
text: "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Backend deploy failed `${{ github.event.inputs.flavor }}`>"
|
||||||
- name: Post Cancel event to a Slack channel
|
- name: Post Cancel event to a Slack channel
|
||||||
id: cancel-slack
|
id: cancel-slack
|
||||||
uses: slackapi/slack-github-action@v2.1.0
|
uses: slackapi/slack-github-action@v2.0.0
|
||||||
if: ${{ always() && contains(needs.*.result, 'cancelled') && !contains(needs.*.result, 'failure') }}
|
if: ${{ always() && contains(needs.*.result, 'cancelled') && !contains(needs.*.result, 'failure') }}
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.SLACK_BOT_TOKEN }}
|
token: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||||
|
|||||||
@@ -38,5 +38,3 @@ packages/frontend/apps/ios/App/**
|
|||||||
tests/blocksuite/snapshots
|
tests/blocksuite/snapshots
|
||||||
blocksuite/docs/api/**
|
blocksuite/docs/api/**
|
||||||
packages/frontend/admin/src/config.json
|
packages/frontend/admin/src/config.json
|
||||||
**/test-docs.json
|
|
||||||
**/test-blocks.json
|
|
||||||
|
|||||||
Generated
+342
-495
File diff suppressed because it is too large
Load Diff
@@ -43,7 +43,7 @@ _Special thanks to [Blaze](https://runblaze.dev) for their support of this proje
|
|||||||
<a href="https://affine.pro/redirect/discord">Discord</a> |
|
<a href="https://affine.pro/redirect/discord">Discord</a> |
|
||||||
<a href="https://app.affine.pro">Live Demo</a> |
|
<a href="https://app.affine.pro">Live Demo</a> |
|
||||||
<a href="https://affine.pro/blog/">Blog</a> |
|
<a href="https://affine.pro/blog/">Blog</a> |
|
||||||
<a href="https://docs.affine.pro/">Documentation</a>
|
<a href="https://docs.affine.pro/docs/">Documentation</a>
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ Star us, and you will receive all release notifications from GitHub without any
|
|||||||
|
|
||||||
**Self-host & Shape your own AFFiNE**
|
**Self-host & Shape your own AFFiNE**
|
||||||
|
|
||||||
- You have the freedom to manage, self-host, fork and build your own AFFiNE. Plugin community and third-party blocks are coming soon. More tractions on [Blocksuite](https://blocksuite.io). Check there to learn how to [self-host AFFiNE](https://docs.affine.pro/self-host-affine).
|
- You have the freedom to manage, self-host, fork and build your own AFFiNE. Plugin community and third-party blocks are coming soon. More tractions on [Blocksuite](https://blocksuite.io). Check there to learn how to [self-host AFFiNE](https://docs.affine.pro/docs/self-host-affine).
|
||||||
|
|
||||||
## Acknowledgement
|
## Acknowledgement
|
||||||
|
|
||||||
@@ -191,9 +191,7 @@ We would like to express our gratitude to all the individuals who have already c
|
|||||||
|
|
||||||
## Self-Host
|
## Self-Host
|
||||||
|
|
||||||
Begin with Docker to deploy your own feature-rich, unrestricted version of AFFiNE. Our team is diligently updating to the latest version. For more information on how to self-host AFFiNE, please refer to our [documentation](https://docs.affine.pro/self-host-affine).
|
Begin with Docker to deploy your own feature-rich, unrestricted version of AFFiNE. Our team is diligently updating to the latest version. For more information on how to self-host AFFiNE, please refer to our [documentation](https://docs.affine.pro/docs/self-host-affine).
|
||||||
|
|
||||||
[](https://template.run.claw.cloud/?openapp=system-fastdeploy%3FtemplateName%3Daffine)
|
|
||||||
|
|
||||||
## Hiring
|
## Hiring
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
"@blocksuite/affine-block-divider": "workspace:*",
|
"@blocksuite/affine-block-divider": "workspace:*",
|
||||||
"@blocksuite/affine-block-edgeless-text": "workspace:*",
|
"@blocksuite/affine-block-edgeless-text": "workspace:*",
|
||||||
"@blocksuite/affine-block-embed": "workspace:*",
|
"@blocksuite/affine-block-embed": "workspace:*",
|
||||||
"@blocksuite/affine-block-embed-doc": "workspace:*",
|
|
||||||
"@blocksuite/affine-block-frame": "workspace:*",
|
"@blocksuite/affine-block-frame": "workspace:*",
|
||||||
"@blocksuite/affine-block-image": "workspace:*",
|
"@blocksuite/affine-block-image": "workspace:*",
|
||||||
"@blocksuite/affine-block-latex": "workspace:*",
|
"@blocksuite/affine-block-latex": "workspace:*",
|
||||||
@@ -122,9 +121,6 @@
|
|||||||
"./blocks/embed": "./src/blocks/embed/index.ts",
|
"./blocks/embed": "./src/blocks/embed/index.ts",
|
||||||
"./blocks/embed/store": "./src/blocks/embed/store.ts",
|
"./blocks/embed/store": "./src/blocks/embed/store.ts",
|
||||||
"./blocks/embed/view": "./src/blocks/embed/view.ts",
|
"./blocks/embed/view": "./src/blocks/embed/view.ts",
|
||||||
"./blocks/embed-doc": "./src/blocks/embed-doc/index.ts",
|
|
||||||
"./blocks/embed-doc/store": "./src/blocks/embed-doc/store.ts",
|
|
||||||
"./blocks/embed-doc/view": "./src/blocks/embed-doc/view.ts",
|
|
||||||
"./blocks/frame": "./src/blocks/frame/index.ts",
|
"./blocks/frame": "./src/blocks/frame/index.ts",
|
||||||
"./blocks/frame/store": "./src/blocks/frame/store.ts",
|
"./blocks/frame/store": "./src/blocks/frame/store.ts",
|
||||||
"./blocks/frame/view": "./src/blocks/frame/view.ts",
|
"./blocks/frame/view": "./src/blocks/frame/view.ts",
|
||||||
@@ -143,9 +139,7 @@
|
|||||||
"./blocks/paragraph": "./src/blocks/paragraph/index.ts",
|
"./blocks/paragraph": "./src/blocks/paragraph/index.ts",
|
||||||
"./blocks/paragraph/store": "./src/blocks/paragraph/store.ts",
|
"./blocks/paragraph/store": "./src/blocks/paragraph/store.ts",
|
||||||
"./blocks/paragraph/view": "./src/blocks/paragraph/view.ts",
|
"./blocks/paragraph/view": "./src/blocks/paragraph/view.ts",
|
||||||
"./blocks/root": "./src/blocks/root/index.ts",
|
"./blocks/root": "./src/blocks/root.ts",
|
||||||
"./blocks/root/store": "./src/blocks/root/store.ts",
|
|
||||||
"./blocks/root/view": "./src/blocks/root/view.ts",
|
|
||||||
"./blocks/surface": "./src/blocks/surface/index.ts",
|
"./blocks/surface": "./src/blocks/surface/index.ts",
|
||||||
"./blocks/surface/store": "./src/blocks/surface/store.ts",
|
"./blocks/surface/store": "./src/blocks/surface/store.ts",
|
||||||
"./blocks/surface/view": "./src/blocks/surface/view.ts",
|
"./blocks/surface/view": "./src/blocks/surface/view.ts",
|
||||||
@@ -198,12 +192,9 @@
|
|||||||
"./widgets/viewport-overlay/view": "./src/widgets/viewport-overlay/view.ts",
|
"./widgets/viewport-overlay/view": "./src/widgets/viewport-overlay/view.ts",
|
||||||
"./widgets/page-dragging-area": "./src/widgets/page-dragging-area/index.ts",
|
"./widgets/page-dragging-area": "./src/widgets/page-dragging-area/index.ts",
|
||||||
"./widgets/page-dragging-area/view": "./src/widgets/page-dragging-area/view.ts",
|
"./widgets/page-dragging-area/view": "./src/widgets/page-dragging-area/view.ts",
|
||||||
"./fragments/doc-title": "./src/fragments/doc-title/index.ts",
|
"./fragments/doc-title": "./src/fragments/doc-title.ts",
|
||||||
"./fragments/doc-title/view": "./src/fragments/doc-title/view.ts",
|
"./fragments/frame-panel": "./src/fragments/frame-panel.ts",
|
||||||
"./fragments/frame-panel": "./src/fragments/frame-panel/index.ts",
|
"./fragments/outline": "./src/fragments/outline.ts",
|
||||||
"./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",
|
|
||||||
"./gfx/text": "./src/gfx/text/index.ts",
|
"./gfx/text": "./src/gfx/text/index.ts",
|
||||||
"./gfx/text/store": "./src/gfx/text/store.ts",
|
"./gfx/text/store": "./src/gfx/text/store.ts",
|
||||||
"./gfx/text/view": "./src/gfx/text/view.ts",
|
"./gfx/text/view": "./src/gfx/text/view.ts",
|
||||||
@@ -258,7 +249,6 @@
|
|||||||
"./components/toolbar": "./src/components/toolbar.ts",
|
"./components/toolbar": "./src/components/toolbar.ts",
|
||||||
"./components/view-dropdown-menu": "./src/components/view-dropdown-menu.ts",
|
"./components/view-dropdown-menu": "./src/components/view-dropdown-menu.ts",
|
||||||
"./components/tooltip-content-with-shortcut": "./src/components/tooltip-content-with-shortcut.ts",
|
"./components/tooltip-content-with-shortcut": "./src/components/tooltip-content-with-shortcut.ts",
|
||||||
"./components/resource": "./src/components/resource.ts",
|
|
||||||
"./rich-text": "./src/rich-text/index.ts",
|
"./rich-text": "./src/rich-text/index.ts",
|
||||||
"./rich-text/effects": "./src/rich-text/effects.ts",
|
"./rich-text/effects": "./src/rich-text/effects.ts",
|
||||||
"./shared/adapters": "./src/shared/adapters.ts",
|
"./shared/adapters": "./src/shared/adapters.ts",
|
||||||
@@ -274,9 +264,7 @@
|
|||||||
"./model": "./src/model/index.ts",
|
"./model": "./src/model/index.ts",
|
||||||
"./sync": "./src/sync/index.ts",
|
"./sync": "./src/sync/index.ts",
|
||||||
"./extensions/store": "./src/extensions/store.ts",
|
"./extensions/store": "./src/extensions/store.ts",
|
||||||
"./extensions/view": "./src/extensions/view.ts",
|
"./extensions/view": "./src/extensions/view.ts"
|
||||||
"./foundation/store": "./src/foundation/store.ts",
|
|
||||||
"./foundation/view": "./src/foundation/view.ts"
|
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"src",
|
"src",
|
||||||
@@ -287,6 +275,6 @@
|
|||||||
"version": "0.21.0",
|
"version": "0.21.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vanilla-extract/vite-plugin": "^5.0.0",
|
"@vanilla-extract/vite-plugin": "^5.0.0",
|
||||||
"vitest": "3.1.3"
|
"vitest": "3.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2360,65 +2360,6 @@ describe('html to snapshot', () => {
|
|||||||
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
|
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should preserve space in p', async () => {
|
|
||||||
const html = template(
|
|
||||||
`<p>A <b>bold text</b> followed by a <i>italic text</i></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: [
|
|
||||||
{
|
|
||||||
insert: 'A ',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
insert: 'bold text',
|
|
||||||
attributes: {
|
|
||||||
bold: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
insert: ' followed by a ',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
insert: 'italic text',
|
|
||||||
attributes: {
|
|
||||||
italic: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const htmlAdapter = new HtmlAdapter(createJob(), provider);
|
|
||||||
const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({
|
|
||||||
file: html,
|
|
||||||
});
|
|
||||||
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('span nested in p', async () => {
|
test('span nested in p', async () => {
|
||||||
const html = template(
|
const html = template(
|
||||||
`<p><span>aaa</span><span>bbb</span><span>ccc</span></p>`
|
`<p><span>aaa</span><span>bbb</span><span>ccc</span></p>`
|
||||||
|
|||||||
@@ -4,9 +4,6 @@ import {
|
|||||||
TableModelFlavour,
|
TableModelFlavour,
|
||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
import {
|
import {
|
||||||
CalloutAdmonitionType,
|
|
||||||
CalloutExportStyle,
|
|
||||||
calloutMarkdownExportMiddleware,
|
|
||||||
embedSyncedDocMiddleware,
|
embedSyncedDocMiddleware,
|
||||||
MarkdownAdapter,
|
MarkdownAdapter,
|
||||||
} from '@blocksuite/affine-shared/adapters';
|
} from '@blocksuite/affine-shared/adapters';
|
||||||
@@ -2451,290 +2448,6 @@ World!
|
|||||||
});
|
});
|
||||||
expect(target.file).toBe(markdown);
|
expect(target.file).toBe(markdown);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('callout', () => {
|
|
||||||
test('without export middleware', async () => {
|
|
||||||
const blockSnapshot: BlockSnapshot = {
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:vu6SK6WJpW',
|
|
||||||
flavour: 'affine:page',
|
|
||||||
props: {
|
|
||||||
title: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:Tk4gSPocAt',
|
|
||||||
flavour: 'affine:surface',
|
|
||||||
props: {
|
|
||||||
elements: {},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:WfnS5ZDCJT',
|
|
||||||
flavour: 'affine:note',
|
|
||||||
props: {
|
|
||||||
xywh: '[0,0,800,95]',
|
|
||||||
background: DefaultTheme.noteBackgrounColor,
|
|
||||||
index: 'a0',
|
|
||||||
hidden: false,
|
|
||||||
displayMode: NoteDisplayMode.DocAndEdgeless,
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:8hOLxad5Fv',
|
|
||||||
flavour: 'affine:callout',
|
|
||||||
props: {
|
|
||||||
emoji: '💡',
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:8hOLxad5Fv',
|
|
||||||
flavour: 'affine:paragraph',
|
|
||||||
props: {
|
|
||||||
type: 'text',
|
|
||||||
text: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [{ insert: 'First callout' }],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:8hOLxadvdv',
|
|
||||||
flavour: 'affine:callout',
|
|
||||||
props: {
|
|
||||||
emoji: '',
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:8hOLxad5Fv',
|
|
||||||
flavour: 'affine:paragraph',
|
|
||||||
props: {
|
|
||||||
type: 'text',
|
|
||||||
text: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [
|
|
||||||
{ insert: 'Warning second callout without emoji' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:8hOLxad5Fv',
|
|
||||||
flavour: 'affine:paragraph',
|
|
||||||
props: {
|
|
||||||
type: 'text',
|
|
||||||
text: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [{ insert: 'Text in second callout' }],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const markdown = `> \\[!💡]
|
|
||||||
>
|
|
||||||
> First callout
|
|
||||||
|
|
||||||
> \\[!]
|
|
||||||
>
|
|
||||||
> Warning second callout without emoji
|
|
||||||
>
|
|
||||||
> Text in second callout
|
|
||||||
`;
|
|
||||||
|
|
||||||
const mdAdapter = new MarkdownAdapter(createJob(), provider);
|
|
||||||
const target = await mdAdapter.fromBlockSnapshot({
|
|
||||||
snapshot: blockSnapshot,
|
|
||||||
});
|
|
||||||
expect(target.file).toBe(markdown);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('with export middleware', async () => {
|
|
||||||
const blockSnapshot: BlockSnapshot = {
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:vu6SK6WJpW',
|
|
||||||
flavour: 'affine:page',
|
|
||||||
props: {
|
|
||||||
title: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:Tk4gSPocAt',
|
|
||||||
flavour: 'affine:surface',
|
|
||||||
props: {
|
|
||||||
elements: {},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:WfnS5ZDCJT',
|
|
||||||
flavour: 'affine:note',
|
|
||||||
props: {
|
|
||||||
xywh: '[0,0,800,95]',
|
|
||||||
background: DefaultTheme.noteBackgrounColor,
|
|
||||||
index: 'a0',
|
|
||||||
hidden: false,
|
|
||||||
displayMode: NoteDisplayMode.DocAndEdgeless,
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:8hOLxad5Fv',
|
|
||||||
flavour: 'affine:callout',
|
|
||||||
props: {
|
|
||||||
emoji: '💡',
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:8hOLxad5Fv',
|
|
||||||
flavour: 'affine:paragraph',
|
|
||||||
props: {
|
|
||||||
type: 'text',
|
|
||||||
text: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [
|
|
||||||
{ insert: 'Callout that does not have a title' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:8hOLxadvdv',
|
|
||||||
flavour: 'affine:callout',
|
|
||||||
props: {
|
|
||||||
emoji: '',
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:8hOLxad5Fv',
|
|
||||||
flavour: 'affine:paragraph',
|
|
||||||
props: {
|
|
||||||
type: 'text',
|
|
||||||
text: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [
|
|
||||||
{
|
|
||||||
insert:
|
|
||||||
'Warning callout with custom title and multiple paragraphs',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:8hOLxad5Fv',
|
|
||||||
flavour: 'affine:paragraph',
|
|
||||||
props: {
|
|
||||||
type: 'text',
|
|
||||||
text: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [{ insert: 'Text in second callout' }],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:8hOLxad5Fv',
|
|
||||||
flavour: 'affine:callout',
|
|
||||||
props: {
|
|
||||||
emoji: '💡',
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'block:8hOLxad5Fv',
|
|
||||||
flavour: 'affine:paragraph',
|
|
||||||
props: {
|
|
||||||
type: 'text',
|
|
||||||
text: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [
|
|
||||||
{ insert: 'details' },
|
|
||||||
{ insert: ' ' },
|
|
||||||
{ insert: '\nText in details callout with new line' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const markdown = `::: info
|
|
||||||
|
|
||||||
Callout that does not have a title
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
::: warning callout with custom title and multiple paragraphs
|
|
||||||
|
|
||||||
Text in second callout
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
::: details
|
|
||||||
|
|
||||||
Text in details callout with new line
|
|
||||||
|
|
||||||
:::
|
|
||||||
`;
|
|
||||||
|
|
||||||
const mdAdapter = new MarkdownAdapter(
|
|
||||||
createJob([
|
|
||||||
calloutMarkdownExportMiddleware({
|
|
||||||
style: CalloutExportStyle.Admonitions,
|
|
||||||
admonitionType: CalloutAdmonitionType.Info,
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
provider
|
|
||||||
);
|
|
||||||
const target = await mdAdapter.fromBlockSnapshot({
|
|
||||||
snapshot: blockSnapshot,
|
|
||||||
});
|
|
||||||
expect(target.file).toBe(markdown);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('markdown to snapshot', () => {
|
describe('markdown to snapshot', () => {
|
||||||
@@ -3875,7 +3588,7 @@ bbb
|
|||||||
props: {
|
props: {
|
||||||
xywh: '[0,0,800,95]',
|
xywh: '[0,0,800,95]',
|
||||||
background: {
|
background: {
|
||||||
dark: '#252525',
|
dark: '#000000',
|
||||||
light: '#ffffff',
|
light: '#ffffff',
|
||||||
},
|
},
|
||||||
index: 'a0',
|
index: 'a0',
|
||||||
@@ -4469,62 +4182,4 @@ hhh
|
|||||||
});
|
});
|
||||||
expect(nanoidReplacement(rawSliceSnapshot!)).toEqual(sliceSnapshot);
|
expect(nanoidReplacement(rawSliceSnapshot!)).toEqual(sliceSnapshot);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('callout', () => {
|
|
||||||
const calloutBlockSnapshot: 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:callout',
|
|
||||||
props: {
|
|
||||||
emoji: '💬',
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'matchesReplaceMap[2]',
|
|
||||||
flavour: 'affine:paragraph',
|
|
||||||
props: {
|
|
||||||
type: 'text',
|
|
||||||
text: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [{ insert: 'This is a callout' }],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
test('callout start with escape character', async () => {
|
|
||||||
const markdown = '> \\[!💬]\n> This is a callout';
|
|
||||||
const mdAdapter = new MarkdownAdapter(createJob(), provider);
|
|
||||||
const rawBlockSnapshot = await mdAdapter.toBlockSnapshot({
|
|
||||||
file: markdown,
|
|
||||||
});
|
|
||||||
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(calloutBlockSnapshot);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('callout start without escape character', async () => {
|
|
||||||
const markdown = '> [!💬]\n> This is a callout';
|
|
||||||
const mdAdapter = new MarkdownAdapter(createJob(), provider);
|
|
||||||
const rawBlockSnapshot = await mdAdapter.toBlockSnapshot({
|
|
||||||
file: markdown,
|
|
||||||
});
|
|
||||||
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(calloutBlockSnapshot);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -106,65 +106,4 @@ describe('notion-text to snapshot', () => {
|
|||||||
});
|
});
|
||||||
expect(nanoidReplacement(target!)).toEqual(sliceSnapshot);
|
expect(nanoidReplacement(target!)).toEqual(sliceSnapshot);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('notion text with empty styles array', () => {
|
|
||||||
const notionText =
|
|
||||||
'{"blockType":"text","editing":[["a "],["bold text",[["b"]]],[" hello world"]],"selection":{"startIndex":0,"endIndex":23},"action":"copy"}';
|
|
||||||
|
|
||||||
const sliceSnapshot: SliceSnapshot = {
|
|
||||||
type: 'slice',
|
|
||||||
content: [
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'matchesReplaceMap[0]',
|
|
||||||
flavour: 'affine:note',
|
|
||||||
props: {
|
|
||||||
xywh: '[0,0,800,95]',
|
|
||||||
background: DefaultTheme.noteBackgrounColor,
|
|
||||||
index: 'a0',
|
|
||||||
hidden: false,
|
|
||||||
displayMode: 'both',
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: 'matchesReplaceMap[1]',
|
|
||||||
flavour: 'affine:paragraph',
|
|
||||||
props: {
|
|
||||||
type: 'text',
|
|
||||||
text: {
|
|
||||||
'$blocksuite:internal:text$': true,
|
|
||||||
delta: [
|
|
||||||
{
|
|
||||||
insert: 'a ',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
insert: 'bold text',
|
|
||||||
attributes: {
|
|
||||||
bold: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
insert: ' hello world',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
workspaceId: '',
|
|
||||||
pageId: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
const ntAdapter = new NotionTextAdapter(createJob(), provider);
|
|
||||||
const target = ntAdapter.toSliceSnapshot({
|
|
||||||
file: notionText,
|
|
||||||
workspaceId: '',
|
|
||||||
pageId: '',
|
|
||||||
});
|
|
||||||
expect(nanoidReplacement(target!)).toEqual(sliceSnapshot);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import {
|
||||||
|
HtmlInlineToDeltaAdapterExtensions,
|
||||||
|
InlineDeltaToHtmlAdapterExtensions,
|
||||||
|
InlineDeltaToMarkdownAdapterExtensions,
|
||||||
|
MarkdownInlineToDeltaAdapterExtensions,
|
||||||
|
NotionHtmlInlineToDeltaAdapterExtensions,
|
||||||
|
} from '@blocksuite/affine-inline-preset';
|
||||||
|
import {
|
||||||
|
AttachmentAdapterFactoryExtension,
|
||||||
|
HtmlAdapterFactoryExtension,
|
||||||
|
ImageAdapterFactoryExtension,
|
||||||
|
MarkdownAdapterFactoryExtension,
|
||||||
|
MixTextAdapterFactoryExtension,
|
||||||
|
NotionHtmlAdapterFactoryExtension,
|
||||||
|
NotionTextAdapterFactoryExtension,
|
||||||
|
PlainTextAdapterFactoryExtension,
|
||||||
|
} from '@blocksuite/affine-shared/adapters';
|
||||||
|
import type { ExtensionType } from '@blocksuite/store';
|
||||||
|
|
||||||
|
import { defaultBlockHtmlAdapterMatchers } from './html/block-matcher';
|
||||||
|
import { defaultBlockMarkdownAdapterMatchers } from './markdown/block-matcher';
|
||||||
|
import { defaultMarkdownPreprocessors } from './markdown/preprocessor';
|
||||||
|
import { defaultBlockNotionHtmlAdapterMatchers } from './notion-html/block-matcher';
|
||||||
|
import { defaultBlockPlainTextAdapterMatchers } from './plain-text/block-matcher';
|
||||||
|
|
||||||
|
export function getAdapterFactoryExtensions(): ExtensionType[] {
|
||||||
|
return [
|
||||||
|
AttachmentAdapterFactoryExtension,
|
||||||
|
ImageAdapterFactoryExtension,
|
||||||
|
MarkdownAdapterFactoryExtension,
|
||||||
|
PlainTextAdapterFactoryExtension,
|
||||||
|
HtmlAdapterFactoryExtension,
|
||||||
|
NotionTextAdapterFactoryExtension,
|
||||||
|
NotionHtmlAdapterFactoryExtension,
|
||||||
|
MixTextAdapterFactoryExtension,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getHtmlAdapterExtensions(): ExtensionType[] {
|
||||||
|
return [
|
||||||
|
...HtmlInlineToDeltaAdapterExtensions,
|
||||||
|
...defaultBlockHtmlAdapterMatchers,
|
||||||
|
...InlineDeltaToHtmlAdapterExtensions,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMarkdownAdapterExtensions(): ExtensionType[] {
|
||||||
|
return [
|
||||||
|
...MarkdownInlineToDeltaAdapterExtensions,
|
||||||
|
...defaultBlockMarkdownAdapterMatchers,
|
||||||
|
...InlineDeltaToMarkdownAdapterExtensions,
|
||||||
|
...defaultMarkdownPreprocessors,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNotionHtmlAdapterExtensions(): ExtensionType[] {
|
||||||
|
return [
|
||||||
|
...NotionHtmlInlineToDeltaAdapterExtensions,
|
||||||
|
...defaultBlockNotionHtmlAdapterMatchers,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPlainTextAdapterExtensions(): ExtensionType[] {
|
||||||
|
return [...defaultBlockPlainTextAdapterMatchers];
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { BookmarkBlockHtmlAdapterExtension } from '@blocksuite/affine-block-bookmark';
|
||||||
|
import { CodeBlockHtmlAdapterExtension } from '@blocksuite/affine-block-code';
|
||||||
|
import { DatabaseBlockHtmlAdapterExtension } from '@blocksuite/affine-block-database';
|
||||||
|
import { DividerBlockHtmlAdapterExtension } from '@blocksuite/affine-block-divider';
|
||||||
|
import {
|
||||||
|
EmbedFigmaBlockHtmlAdapterExtension,
|
||||||
|
EmbedGithubBlockHtmlAdapterExtension,
|
||||||
|
EmbedIframeBlockHtmlAdapterExtension,
|
||||||
|
EmbedLinkedDocHtmlAdapterExtension,
|
||||||
|
EmbedLoomBlockHtmlAdapterExtension,
|
||||||
|
EmbedSyncedDocBlockHtmlAdapterExtension,
|
||||||
|
EmbedYoutubeBlockHtmlAdapterExtension,
|
||||||
|
} from '@blocksuite/affine-block-embed';
|
||||||
|
import { ImageBlockHtmlAdapterExtension } from '@blocksuite/affine-block-image';
|
||||||
|
import { ListBlockHtmlAdapterExtension } from '@blocksuite/affine-block-list';
|
||||||
|
import { ParagraphBlockHtmlAdapterExtension } from '@blocksuite/affine-block-paragraph';
|
||||||
|
import { RootBlockHtmlAdapterExtension } from '@blocksuite/affine-block-root';
|
||||||
|
import { TableBlockHtmlAdapterExtension } from '@blocksuite/affine-block-table';
|
||||||
|
|
||||||
|
export const defaultBlockHtmlAdapterMatchers = [
|
||||||
|
ListBlockHtmlAdapterExtension,
|
||||||
|
ParagraphBlockHtmlAdapterExtension,
|
||||||
|
CodeBlockHtmlAdapterExtension,
|
||||||
|
DividerBlockHtmlAdapterExtension,
|
||||||
|
ImageBlockHtmlAdapterExtension,
|
||||||
|
RootBlockHtmlAdapterExtension,
|
||||||
|
EmbedYoutubeBlockHtmlAdapterExtension,
|
||||||
|
EmbedFigmaBlockHtmlAdapterExtension,
|
||||||
|
EmbedLoomBlockHtmlAdapterExtension,
|
||||||
|
EmbedGithubBlockHtmlAdapterExtension,
|
||||||
|
EmbedIframeBlockHtmlAdapterExtension,
|
||||||
|
BookmarkBlockHtmlAdapterExtension,
|
||||||
|
DatabaseBlockHtmlAdapterExtension,
|
||||||
|
TableBlockHtmlAdapterExtension,
|
||||||
|
EmbedLinkedDocHtmlAdapterExtension,
|
||||||
|
EmbedSyncedDocBlockHtmlAdapterExtension,
|
||||||
|
];
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export * from './extension.js';
|
||||||
|
export * from './html/block-matcher.js';
|
||||||
|
export * from './markdown/block-matcher.js';
|
||||||
|
export * from './markdown/preprocessor.js';
|
||||||
|
export * from './notion-html/block-matcher.js';
|
||||||
|
export * from './plain-text/block-matcher.js';
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { AttachmentBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-attachment';
|
||||||
|
import { BookmarkBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-bookmark';
|
||||||
|
import { CodeBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-code';
|
||||||
|
import { DatabaseBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-database';
|
||||||
|
import { DividerBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-divider';
|
||||||
|
import {
|
||||||
|
EmbedFigmaMarkdownAdapterExtension,
|
||||||
|
EmbedGithubMarkdownAdapterExtension,
|
||||||
|
EmbedIframeBlockMarkdownAdapterExtension,
|
||||||
|
EmbedLinkedDocMarkdownAdapterExtension,
|
||||||
|
EmbedLoomMarkdownAdapterExtension,
|
||||||
|
EmbedSyncedDocMarkdownAdapterExtension,
|
||||||
|
EmbedYoutubeMarkdownAdapterExtension,
|
||||||
|
} from '@blocksuite/affine-block-embed';
|
||||||
|
import { ImageBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-image';
|
||||||
|
import { LatexBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-latex';
|
||||||
|
import { ListBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-list';
|
||||||
|
import { DocNoteBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-note';
|
||||||
|
import { ParagraphBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-paragraph';
|
||||||
|
import { RootBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-root';
|
||||||
|
import { TableBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-table';
|
||||||
|
|
||||||
|
export const defaultBlockMarkdownAdapterMatchers = [
|
||||||
|
RootBlockMarkdownAdapterExtension,
|
||||||
|
DocNoteBlockMarkdownAdapterExtension,
|
||||||
|
EmbedFigmaMarkdownAdapterExtension,
|
||||||
|
EmbedGithubMarkdownAdapterExtension,
|
||||||
|
EmbedLinkedDocMarkdownAdapterExtension,
|
||||||
|
EmbedLoomMarkdownAdapterExtension,
|
||||||
|
EmbedSyncedDocMarkdownAdapterExtension,
|
||||||
|
EmbedYoutubeMarkdownAdapterExtension,
|
||||||
|
EmbedIframeBlockMarkdownAdapterExtension,
|
||||||
|
ListBlockMarkdownAdapterExtension,
|
||||||
|
ParagraphBlockMarkdownAdapterExtension,
|
||||||
|
BookmarkBlockMarkdownAdapterExtension,
|
||||||
|
CodeBlockMarkdownAdapterExtension,
|
||||||
|
DatabaseBlockMarkdownAdapterExtension,
|
||||||
|
TableBlockMarkdownAdapterExtension,
|
||||||
|
DividerBlockMarkdownAdapterExtension,
|
||||||
|
ImageBlockMarkdownAdapterExtension,
|
||||||
|
LatexBlockMarkdownAdapterExtension,
|
||||||
|
AttachmentBlockMarkdownAdapterExtension,
|
||||||
|
];
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { BookmarkBlockMarkdownPreprocessorExtension } from '@blocksuite/affine-block-bookmark';
|
||||||
|
import { CodeMarkdownPreprocessorExtension } from '@blocksuite/affine-block-code';
|
||||||
|
import { LatexMarkdownPreprocessorExtension } from '@blocksuite/affine-block-latex';
|
||||||
|
|
||||||
|
export const defaultMarkdownPreprocessors = [
|
||||||
|
LatexMarkdownPreprocessorExtension,
|
||||||
|
CodeMarkdownPreprocessorExtension,
|
||||||
|
BookmarkBlockMarkdownPreprocessorExtension,
|
||||||
|
];
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { AttachmentBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-attachment';
|
||||||
|
import { BookmarkBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-bookmark';
|
||||||
|
import { CodeBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-code';
|
||||||
|
import { DatabaseBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-database';
|
||||||
|
import { DividerBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-divider';
|
||||||
|
import {
|
||||||
|
EmbedFigmaBlockNotionHtmlAdapterExtension,
|
||||||
|
EmbedGithubBlockNotionHtmlAdapterExtension,
|
||||||
|
EmbedLoomBlockNotionHtmlAdapterExtension,
|
||||||
|
EmbedYoutubeBlockNotionHtmlAdapterExtension,
|
||||||
|
} from '@blocksuite/affine-block-embed';
|
||||||
|
import { ImageBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-image';
|
||||||
|
import { LatexBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-latex';
|
||||||
|
import { ListBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-list';
|
||||||
|
import { ParagraphBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-paragraph';
|
||||||
|
import { RootBlockNotionHtmlAdapterExtension } from '@blocksuite/affine-block-root';
|
||||||
|
import type { ExtensionType } from '@blocksuite/store';
|
||||||
|
|
||||||
|
export const defaultBlockNotionHtmlAdapterMatchers: ExtensionType[] = [
|
||||||
|
ListBlockNotionHtmlAdapterExtension,
|
||||||
|
ParagraphBlockNotionHtmlAdapterExtension,
|
||||||
|
CodeBlockNotionHtmlAdapterExtension,
|
||||||
|
DividerBlockNotionHtmlAdapterExtension,
|
||||||
|
ImageBlockNotionHtmlAdapterExtension,
|
||||||
|
RootBlockNotionHtmlAdapterExtension,
|
||||||
|
BookmarkBlockNotionHtmlAdapterExtension,
|
||||||
|
DatabaseBlockNotionHtmlAdapterExtension,
|
||||||
|
LatexBlockNotionHtmlAdapterExtension,
|
||||||
|
EmbedYoutubeBlockNotionHtmlAdapterExtension,
|
||||||
|
EmbedFigmaBlockNotionHtmlAdapterExtension,
|
||||||
|
EmbedGithubBlockNotionHtmlAdapterExtension,
|
||||||
|
EmbedLoomBlockNotionHtmlAdapterExtension,
|
||||||
|
AttachmentBlockNotionHtmlAdapterExtension,
|
||||||
|
];
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { BookmarkBlockPlainTextAdapterExtension } from '@blocksuite/affine-block-bookmark';
|
||||||
|
import { CodeBlockPlainTextAdapterExtension } from '@blocksuite/affine-block-code';
|
||||||
|
import { DatabaseBlockPlainTextAdapterExtension } from '@blocksuite/affine-block-database';
|
||||||
|
import { DividerBlockPlainTextAdapterExtension } from '@blocksuite/affine-block-divider';
|
||||||
|
import {
|
||||||
|
EmbedFigmaBlockPlainTextAdapterExtension,
|
||||||
|
EmbedGithubBlockPlainTextAdapterExtension,
|
||||||
|
EmbedIframeBlockPlainTextAdapterExtension,
|
||||||
|
EmbedLinkedDocBlockPlainTextAdapterExtension,
|
||||||
|
EmbedLoomBlockPlainTextAdapterExtension,
|
||||||
|
EmbedSyncedDocBlockPlainTextAdapterExtension,
|
||||||
|
EmbedYoutubeBlockPlainTextAdapterExtension,
|
||||||
|
} from '@blocksuite/affine-block-embed';
|
||||||
|
import { LatexBlockPlainTextAdapterExtension } from '@blocksuite/affine-block-latex';
|
||||||
|
import { ListBlockPlainTextAdapterExtension } from '@blocksuite/affine-block-list';
|
||||||
|
import { ParagraphBlockPlainTextAdapterExtension } from '@blocksuite/affine-block-paragraph';
|
||||||
|
import type { ExtensionType } from '@blocksuite/store';
|
||||||
|
|
||||||
|
export const defaultBlockPlainTextAdapterMatchers: ExtensionType[] = [
|
||||||
|
ParagraphBlockPlainTextAdapterExtension,
|
||||||
|
ListBlockPlainTextAdapterExtension,
|
||||||
|
DividerBlockPlainTextAdapterExtension,
|
||||||
|
CodeBlockPlainTextAdapterExtension,
|
||||||
|
BookmarkBlockPlainTextAdapterExtension,
|
||||||
|
EmbedFigmaBlockPlainTextAdapterExtension,
|
||||||
|
EmbedGithubBlockPlainTextAdapterExtension,
|
||||||
|
EmbedLoomBlockPlainTextAdapterExtension,
|
||||||
|
EmbedYoutubeBlockPlainTextAdapterExtension,
|
||||||
|
EmbedLinkedDocBlockPlainTextAdapterExtension,
|
||||||
|
EmbedSyncedDocBlockPlainTextAdapterExtension,
|
||||||
|
EmbedIframeBlockPlainTextAdapterExtension,
|
||||||
|
LatexBlockPlainTextAdapterExtension,
|
||||||
|
DatabaseBlockPlainTextAdapterExtension,
|
||||||
|
];
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from '@blocksuite/affine-block-embed-doc';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from '@blocksuite/affine-block-embed-doc/store';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from '@blocksuite/affine-block-embed-doc/view';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from '@blocksuite/affine-block-root/store';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from '@blocksuite/affine-block-root/view';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from '@blocksuite/affine-components/citation';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from '@blocksuite/affine-components/resource';
|
|
||||||
@@ -1,24 +1,92 @@
|
|||||||
import { type effects as blockRootEffects } from '@blocksuite/affine-block-root/effects';
|
import { effects as blockAttachmentEffects } from '@blocksuite/affine-block-attachment/effects';
|
||||||
import { type effects as componentCaptionEffects } from '@blocksuite/affine-components/caption';
|
import { effects as blockBookmarkEffects } from '@blocksuite/affine-block-bookmark/effects';
|
||||||
import { type effects as componentColorPickerEffects } from '@blocksuite/affine-components/color-picker';
|
import { effects as blockCalloutEffects } from '@blocksuite/affine-block-callout/effects';
|
||||||
import { type effects as componentContextMenuEffects } from '@blocksuite/affine-components/context-menu';
|
import { effects as blockCodeEffects } from '@blocksuite/affine-block-code/effects';
|
||||||
import { type effects as componentDatePickerEffects } from '@blocksuite/affine-components/date-picker';
|
import { effects as blockDataViewEffects } from '@blocksuite/affine-block-data-view/effects';
|
||||||
import { type effects as componentDropIndicatorEffects } from '@blocksuite/affine-components/drop-indicator';
|
import { effects as blockDatabaseEffects } from '@blocksuite/affine-block-database/effects';
|
||||||
import { type effects as componentEmbedCardModalEffects } from '@blocksuite/affine-components/embed-card-modal';
|
import { effects as blockDividerEffects } from '@blocksuite/affine-block-divider/effects';
|
||||||
import { type effects as componentHighlightDropdownMenuEffects } from '@blocksuite/affine-components/highlight-dropdown-menu';
|
import { effects as blockEdgelessTextEffects } from '@blocksuite/affine-block-edgeless-text/effects';
|
||||||
import { type effects as componentLinkPreviewEffects } from '@blocksuite/affine-components/link-preview';
|
import { effects as blockEmbedEffects } from '@blocksuite/affine-block-embed/effects';
|
||||||
import { type effects as componentLinkedDocTitleEffects } from '@blocksuite/affine-components/linked-doc-title';
|
import { effects as blockFrameEffects } from '@blocksuite/affine-block-frame/effects';
|
||||||
import { type effects as componentPortalEffects } from '@blocksuite/affine-components/portal';
|
import { effects as blockImageEffects } from '@blocksuite/affine-block-image/effects';
|
||||||
import { type effects as componentToggleButtonEffects } from '@blocksuite/affine-components/toggle-button';
|
import { effects as blockLatexEffects } from '@blocksuite/affine-block-latex/effects';
|
||||||
import { type effects as componentToolbarEffects } from '@blocksuite/affine-components/toolbar';
|
import { effects as blockListEffects } from '@blocksuite/affine-block-list/effects';
|
||||||
import { type effects as componentViewDropdownMenuEffects } from '@blocksuite/affine-components/view-dropdown-menu';
|
import { effects as blockNoteEffects } from '@blocksuite/affine-block-note/effects';
|
||||||
import { type effects as richTextEffects } from '@blocksuite/affine-rich-text/effects';
|
import { effects as blockParagraphEffects } from '@blocksuite/affine-block-paragraph/effects';
|
||||||
import { type effects as stdEffects } from '@blocksuite/std/effects';
|
import { effects as blockRootEffects } from '@blocksuite/affine-block-root/effects';
|
||||||
|
import { effects as blockSurfaceEffects } from '@blocksuite/affine-block-surface/effects';
|
||||||
|
import { effects as blockSurfaceRefEffects } from '@blocksuite/affine-block-surface-ref/effects';
|
||||||
|
import { effects as blockTableEffects } from '@blocksuite/affine-block-table/effects';
|
||||||
|
import { BlockSelection } from '@blocksuite/affine-components/block-selection';
|
||||||
|
import { BlockZeroWidth } from '@blocksuite/affine-components/block-zero-width';
|
||||||
|
import { effects as componentCaptionEffects } from '@blocksuite/affine-components/caption';
|
||||||
|
import { effects as componentCardStyleDropdownMenuEffects } from '@blocksuite/affine-components/card-style-dropdown-menu';
|
||||||
|
import { effects as componentCitationEffects } from '@blocksuite/affine-components/citation';
|
||||||
|
import { effects as componentColorPickerEffects } from '@blocksuite/affine-components/color-picker';
|
||||||
|
import { effects as componentContextMenuEffects } from '@blocksuite/affine-components/context-menu';
|
||||||
|
import { effects as componentDatePickerEffects } from '@blocksuite/affine-components/date-picker';
|
||||||
|
import { effects as componentDropIndicatorEffects } from '@blocksuite/affine-components/drop-indicator';
|
||||||
|
import { effects as componentEdgelessLineStylesEffects } from '@blocksuite/affine-components/edgeless-line-styles-panel';
|
||||||
|
import { effects as componentEdgelessLineWidthEffects } from '@blocksuite/affine-components/edgeless-line-width-panel';
|
||||||
|
import { effects as componentEdgelessShapeColorPickerEffects } from '@blocksuite/affine-components/edgeless-shape-color-picker';
|
||||||
|
import { effects as componentEmbedCardModalEffects } from '@blocksuite/affine-components/embed-card-modal';
|
||||||
|
import { FilterableListComponent } from '@blocksuite/affine-components/filterable-list';
|
||||||
|
import { effects as componentHighlightDropdownMenuEffects } from '@blocksuite/affine-components/highlight-dropdown-menu';
|
||||||
|
import { IconButton } from '@blocksuite/affine-components/icon-button';
|
||||||
|
import { effects as componentLinkPreviewEffects } from '@blocksuite/affine-components/link-preview';
|
||||||
|
import { effects as componentLinkedDocTitleEffects } from '@blocksuite/affine-components/linked-doc-title';
|
||||||
|
import { effects as componentOpenDocDropdownMenuEffects } from '@blocksuite/affine-components/open-doc-dropdown-menu';
|
||||||
|
import { effects as componentPortalEffects } from '@blocksuite/affine-components/portal';
|
||||||
|
import { effects as componentSizeDropdownMenuEffects } from '@blocksuite/affine-components/size-dropdown-menu';
|
||||||
|
import { SmoothCorner } from '@blocksuite/affine-components/smooth-corner';
|
||||||
|
import { effects as componentToggleButtonEffects } from '@blocksuite/affine-components/toggle-button';
|
||||||
|
import { ToggleSwitch } from '@blocksuite/affine-components/toggle-switch';
|
||||||
|
import { effects as componentToolbarEffects } from '@blocksuite/affine-components/toolbar';
|
||||||
|
import { effects as componentTooltipContentWithShortcutEffects } from '@blocksuite/affine-components/tooltip-content-with-shortcut';
|
||||||
|
import { effects as componentViewDropdownMenuEffects } from '@blocksuite/affine-components/view-dropdown-menu';
|
||||||
|
import { effects as fragmentDocTitleEffects } from '@blocksuite/affine-fragment-doc-title/effects';
|
||||||
|
import { effects as fragmentFramePanelEffects } from '@blocksuite/affine-fragment-frame-panel/effects';
|
||||||
|
import { effects as fragmentOutlineEffects } from '@blocksuite/affine-fragment-outline/effects';
|
||||||
|
import { effects as inlineFootnoteEffects } from '@blocksuite/affine-inline-footnote/effects';
|
||||||
|
import { effects as inlineLatexEffects } from '@blocksuite/affine-inline-latex/effects';
|
||||||
|
import { effects as inlineLinkEffects } from '@blocksuite/affine-inline-link/effects';
|
||||||
|
import { effects as inlineMentionEffects } from '@blocksuite/affine-inline-mention';
|
||||||
|
import { effects as inlinePresetEffects } from '@blocksuite/affine-inline-preset/effects';
|
||||||
|
import { effects as inlineReferenceEffects } from '@blocksuite/affine-inline-reference/effects';
|
||||||
|
import { effects as richTextEffects } from '@blocksuite/affine-rich-text/effects';
|
||||||
|
import { effects as widgetDragHandleEffects } from '@blocksuite/affine-widget-drag-handle/effects';
|
||||||
|
import { effects as widgetEdgelessAutoConnectEffects } from '@blocksuite/affine-widget-edgeless-auto-connect/effects';
|
||||||
|
import { effects as widgetFrameTitleEffects } from '@blocksuite/affine-widget-frame-title/effects';
|
||||||
|
import { effects as widgetRemoteSelectionEffects } from '@blocksuite/affine-widget-remote-selection/effects';
|
||||||
|
import { effects as widgetScrollAnchoringEffects } from '@blocksuite/affine-widget-scroll-anchoring/effects';
|
||||||
|
import { effects as widgetSlashMenuEffects } from '@blocksuite/affine-widget-slash-menu/effects';
|
||||||
|
import { effects as widgetToolbarEffects } from '@blocksuite/affine-widget-toolbar/effects';
|
||||||
|
import { effects as dataViewEffects } from '@blocksuite/data-view/effects';
|
||||||
|
import { effects as stdEffects } from '@blocksuite/std/effects';
|
||||||
|
|
||||||
export declare const _GLOBAL_:
|
export declare const _GLOBAL_:
|
||||||
| typeof stdEffects
|
| typeof stdEffects
|
||||||
|
| typeof dataViewEffects
|
||||||
| typeof richTextEffects
|
| typeof richTextEffects
|
||||||
|
| typeof blockNoteEffects
|
||||||
|
| typeof blockAttachmentEffects
|
||||||
|
| typeof blockBookmarkEffects
|
||||||
|
| typeof blockFrameEffects
|
||||||
|
| typeof blockListEffects
|
||||||
|
| typeof blockParagraphEffects
|
||||||
|
| typeof blockEmbedEffects
|
||||||
|
| typeof blockSurfaceEffects
|
||||||
|
| typeof blockImageEffects
|
||||||
|
| typeof blockDatabaseEffects
|
||||||
|
| typeof blockSurfaceRefEffects
|
||||||
|
| typeof blockLatexEffects
|
||||||
|
| typeof blockEdgelessTextEffects
|
||||||
|
| typeof blockDividerEffects
|
||||||
|
| typeof blockDataViewEffects
|
||||||
|
| typeof blockCodeEffects
|
||||||
|
| typeof blockTableEffects
|
||||||
| typeof blockRootEffects
|
| typeof blockRootEffects
|
||||||
|
| typeof blockCalloutEffects
|
||||||
| typeof componentCaptionEffects
|
| typeof componentCaptionEffects
|
||||||
| typeof componentContextMenuEffects
|
| typeof componentContextMenuEffects
|
||||||
| typeof componentDatePickerEffects
|
| typeof componentDatePickerEffects
|
||||||
@@ -31,4 +99,89 @@ export declare const _GLOBAL_:
|
|||||||
| typeof componentToolbarEffects
|
| typeof componentToolbarEffects
|
||||||
| typeof componentToggleButtonEffects
|
| typeof componentToggleButtonEffects
|
||||||
| typeof componentColorPickerEffects
|
| typeof componentColorPickerEffects
|
||||||
| typeof componentViewDropdownMenuEffects;
|
| typeof componentViewDropdownMenuEffects
|
||||||
|
| typeof widgetScrollAnchoringEffects
|
||||||
|
| typeof widgetFrameTitleEffects
|
||||||
|
| typeof widgetRemoteSelectionEffects
|
||||||
|
| typeof widgetDragHandleEffects
|
||||||
|
| typeof widgetEdgelessAutoConnectEffects
|
||||||
|
| typeof widgetToolbarEffects
|
||||||
|
| typeof widgetSlashMenuEffects
|
||||||
|
| typeof fragmentDocTitleEffects
|
||||||
|
| typeof fragmentFramePanelEffects
|
||||||
|
| typeof fragmentOutlineEffects;
|
||||||
|
|
||||||
|
export function effects() {
|
||||||
|
stdEffects();
|
||||||
|
|
||||||
|
dataViewEffects();
|
||||||
|
richTextEffects();
|
||||||
|
|
||||||
|
inlineReferenceEffects();
|
||||||
|
inlinePresetEffects();
|
||||||
|
inlineLinkEffects();
|
||||||
|
inlineFootnoteEffects();
|
||||||
|
inlineLatexEffects();
|
||||||
|
inlineMentionEffects();
|
||||||
|
|
||||||
|
blockNoteEffects();
|
||||||
|
blockAttachmentEffects();
|
||||||
|
blockBookmarkEffects();
|
||||||
|
blockFrameEffects();
|
||||||
|
blockListEffects();
|
||||||
|
blockParagraphEffects();
|
||||||
|
blockEmbedEffects();
|
||||||
|
blockSurfaceEffects();
|
||||||
|
blockImageEffects();
|
||||||
|
blockDatabaseEffects();
|
||||||
|
blockSurfaceRefEffects();
|
||||||
|
blockLatexEffects();
|
||||||
|
blockEdgelessTextEffects();
|
||||||
|
blockDividerEffects();
|
||||||
|
blockDataViewEffects();
|
||||||
|
blockCodeEffects();
|
||||||
|
blockTableEffects();
|
||||||
|
blockRootEffects();
|
||||||
|
blockCalloutEffects();
|
||||||
|
|
||||||
|
componentCaptionEffects();
|
||||||
|
componentContextMenuEffects();
|
||||||
|
componentDatePickerEffects();
|
||||||
|
componentPortalEffects();
|
||||||
|
componentToolbarEffects();
|
||||||
|
componentDropIndicatorEffects();
|
||||||
|
componentToggleButtonEffects();
|
||||||
|
componentColorPickerEffects();
|
||||||
|
componentEmbedCardModalEffects();
|
||||||
|
componentLinkPreviewEffects();
|
||||||
|
componentLinkedDocTitleEffects();
|
||||||
|
componentCitationEffects();
|
||||||
|
componentCardStyleDropdownMenuEffects();
|
||||||
|
componentHighlightDropdownMenuEffects();
|
||||||
|
componentViewDropdownMenuEffects();
|
||||||
|
componentTooltipContentWithShortcutEffects();
|
||||||
|
componentSizeDropdownMenuEffects();
|
||||||
|
componentEdgelessLineWidthEffects();
|
||||||
|
componentEdgelessLineStylesEffects();
|
||||||
|
componentEdgelessShapeColorPickerEffects();
|
||||||
|
componentOpenDocDropdownMenuEffects();
|
||||||
|
|
||||||
|
widgetScrollAnchoringEffects();
|
||||||
|
widgetFrameTitleEffects();
|
||||||
|
widgetRemoteSelectionEffects();
|
||||||
|
widgetDragHandleEffects();
|
||||||
|
widgetEdgelessAutoConnectEffects();
|
||||||
|
widgetSlashMenuEffects();
|
||||||
|
widgetToolbarEffects();
|
||||||
|
|
||||||
|
fragmentDocTitleEffects();
|
||||||
|
fragmentFramePanelEffects();
|
||||||
|
fragmentOutlineEffects();
|
||||||
|
|
||||||
|
customElements.define('icon-button', IconButton);
|
||||||
|
customElements.define('smooth-corner', SmoothCorner);
|
||||||
|
customElements.define('toggle-switch', ToggleSwitch);
|
||||||
|
customElements.define('affine-filterable-list', FilterableListComponent);
|
||||||
|
customElements.define('block-zero-width', BlockZeroWidth);
|
||||||
|
customElements.define('affine-block-selection', BlockSelection);
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { effects as blockRootEffects } from '@blocksuite/affine-block-root/effects';
|
||||||
|
import { effects as fragmentDocTitleEffects } from '@blocksuite/affine-fragment-doc-title/effects';
|
||||||
|
import { effects as fragmentFramePanelEffects } from '@blocksuite/affine-fragment-frame-panel/effects';
|
||||||
|
import { effects as fragmentOutlineEffects } from '@blocksuite/affine-fragment-outline/effects';
|
||||||
|
|
||||||
|
export function effects() {
|
||||||
|
blockRootEffects();
|
||||||
|
|
||||||
|
fragmentDocTitleEffects();
|
||||||
|
fragmentFramePanelEffects();
|
||||||
|
fragmentOutlineEffects();
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import {
|
||||||
|
RootBlockHtmlAdapterExtension,
|
||||||
|
RootBlockMarkdownAdapterExtension,
|
||||||
|
RootBlockNotionHtmlAdapterExtension,
|
||||||
|
} from '@blocksuite/affine-block-root';
|
||||||
|
import {
|
||||||
|
type StoreExtensionContext,
|
||||||
|
StoreExtensionProvider,
|
||||||
|
} from '@blocksuite/affine-ext-loader';
|
||||||
|
import { RootBlockSchemaExtension } from '@blocksuite/affine-model';
|
||||||
|
import type { ExtensionType } from '@blocksuite/store';
|
||||||
|
|
||||||
|
const defaultBlockHtmlAdapterMatchers = [RootBlockHtmlAdapterExtension];
|
||||||
|
|
||||||
|
const defaultBlockMarkdownAdapterMatchers = [RootBlockMarkdownAdapterExtension];
|
||||||
|
|
||||||
|
const defaultBlockNotionHtmlAdapterMatchers: ExtensionType[] = [
|
||||||
|
RootBlockNotionHtmlAdapterExtension,
|
||||||
|
];
|
||||||
|
|
||||||
|
function getHtmlAdapterExtensions(): ExtensionType[] {
|
||||||
|
return [...defaultBlockHtmlAdapterMatchers];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMarkdownAdapterExtensions(): ExtensionType[] {
|
||||||
|
return [...defaultBlockMarkdownAdapterMatchers];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNotionHtmlAdapterExtensions(): ExtensionType[] {
|
||||||
|
return [...defaultBlockNotionHtmlAdapterMatchers];
|
||||||
|
}
|
||||||
|
|
||||||
|
const MigratingStoreExtensions: ExtensionType[] = [
|
||||||
|
RootBlockSchemaExtension,
|
||||||
|
|
||||||
|
getHtmlAdapterExtensions(),
|
||||||
|
getMarkdownAdapterExtensions(),
|
||||||
|
getNotionHtmlAdapterExtensions(),
|
||||||
|
].flat();
|
||||||
|
|
||||||
|
export class MigratingStoreExtension extends StoreExtensionProvider {
|
||||||
|
override name = 'migrating';
|
||||||
|
|
||||||
|
override setup(context: StoreExtensionContext) {
|
||||||
|
super.setup(context);
|
||||||
|
context.register(MigratingStoreExtensions);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import {
|
||||||
|
type ViewExtensionContext,
|
||||||
|
ViewExtensionProvider,
|
||||||
|
} from '@blocksuite/affine-ext-loader';
|
||||||
|
|
||||||
|
import { effects } from './effects';
|
||||||
|
import {
|
||||||
|
MigratingEdgelessEditorBlockSpecs,
|
||||||
|
MigratingPageEditorBlockSpecs,
|
||||||
|
MigratingPreviewEdgelessEditorBlockSpecs,
|
||||||
|
MigratingPreviewPageEditorBlockSpecs,
|
||||||
|
} from './migrating';
|
||||||
|
|
||||||
|
export class MigratingViewExtension extends ViewExtensionProvider {
|
||||||
|
override name = 'migrating';
|
||||||
|
|
||||||
|
override effect() {
|
||||||
|
super.effect();
|
||||||
|
effects();
|
||||||
|
}
|
||||||
|
|
||||||
|
override setup(context: ViewExtensionContext) {
|
||||||
|
super.setup(context);
|
||||||
|
const scope = context.scope;
|
||||||
|
if (scope === 'preview-page') {
|
||||||
|
context.register(MigratingPreviewPageEditorBlockSpecs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (scope === 'preview-edgeless') {
|
||||||
|
context.register(MigratingPreviewEdgelessEditorBlockSpecs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (scope === 'page' || scope === 'mobile-page') {
|
||||||
|
context.register(MigratingPageEditorBlockSpecs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (scope === 'edgeless' || scope === 'mobile-edgeless') {
|
||||||
|
context.register(MigratingEdgelessEditorBlockSpecs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import {
|
||||||
|
EdgelessBuiltInSpecs,
|
||||||
|
PageRootBlockSpec,
|
||||||
|
PreviewEdgelessRootBlockSpec,
|
||||||
|
PreviewPageRootBlockSpec,
|
||||||
|
ReadOnlyClipboard,
|
||||||
|
} from '@blocksuite/affine-block-root';
|
||||||
|
import type { ExtensionType } from '@blocksuite/store';
|
||||||
|
|
||||||
|
export const MigratingEdgelessEditorBlockSpecs: ExtensionType[] = [
|
||||||
|
EdgelessBuiltInSpecs,
|
||||||
|
].flat();
|
||||||
|
|
||||||
|
export const MigratingPageEditorBlockSpecs: ExtensionType[] = [
|
||||||
|
PageRootBlockSpec,
|
||||||
|
].flat();
|
||||||
|
|
||||||
|
export const MigratingPreviewEdgelessEditorBlockSpecs: ExtensionType[] = [
|
||||||
|
PreviewEdgelessRootBlockSpec,
|
||||||
|
ReadOnlyClipboard,
|
||||||
|
].flat();
|
||||||
|
|
||||||
|
export const MigratingPreviewPageEditorBlockSpecs: ExtensionType[] = [
|
||||||
|
PreviewPageRootBlockSpec,
|
||||||
|
ReadOnlyClipboard,
|
||||||
|
].flat();
|
||||||
@@ -7,14 +7,12 @@ import { DatabaseStoreExtension } from '@blocksuite/affine-block-database/store'
|
|||||||
import { DividerStoreExtension } from '@blocksuite/affine-block-divider/store';
|
import { DividerStoreExtension } from '@blocksuite/affine-block-divider/store';
|
||||||
import { EdgelessTextStoreExtension } from '@blocksuite/affine-block-edgeless-text/store';
|
import { EdgelessTextStoreExtension } from '@blocksuite/affine-block-edgeless-text/store';
|
||||||
import { EmbedStoreExtension } from '@blocksuite/affine-block-embed/store';
|
import { EmbedStoreExtension } from '@blocksuite/affine-block-embed/store';
|
||||||
import { EmbedDocStoreExtension } from '@blocksuite/affine-block-embed-doc/store';
|
|
||||||
import { FrameStoreExtension } from '@blocksuite/affine-block-frame/store';
|
import { FrameStoreExtension } from '@blocksuite/affine-block-frame/store';
|
||||||
import { ImageStoreExtension } from '@blocksuite/affine-block-image/store';
|
import { ImageStoreExtension } from '@blocksuite/affine-block-image/store';
|
||||||
import { LatexStoreExtension } from '@blocksuite/affine-block-latex/store';
|
import { LatexStoreExtension } from '@blocksuite/affine-block-latex/store';
|
||||||
import { ListStoreExtension } from '@blocksuite/affine-block-list/store';
|
import { ListStoreExtension } from '@blocksuite/affine-block-list/store';
|
||||||
import { NoteStoreExtension } from '@blocksuite/affine-block-note/store';
|
import { NoteStoreExtension } from '@blocksuite/affine-block-note/store';
|
||||||
import { ParagraphStoreExtension } from '@blocksuite/affine-block-paragraph/store';
|
import { ParagraphStoreExtension } from '@blocksuite/affine-block-paragraph/store';
|
||||||
import { RootStoreExtension } from '@blocksuite/affine-block-root/store';
|
|
||||||
import { SurfaceStoreExtension } from '@blocksuite/affine-block-surface/store';
|
import { SurfaceStoreExtension } from '@blocksuite/affine-block-surface/store';
|
||||||
import { SurfaceRefStoreExtension } from '@blocksuite/affine-block-surface-ref/store';
|
import { SurfaceRefStoreExtension } from '@blocksuite/affine-block-surface-ref/store';
|
||||||
import { TableStoreExtension } from '@blocksuite/affine-block-table/store';
|
import { TableStoreExtension } from '@blocksuite/affine-block-table/store';
|
||||||
@@ -31,6 +29,8 @@ import { LinkStoreExtension } from '@blocksuite/affine-inline-link/store';
|
|||||||
import { InlinePresetStoreExtension } from '@blocksuite/affine-inline-preset/store';
|
import { InlinePresetStoreExtension } from '@blocksuite/affine-inline-preset/store';
|
||||||
import { ReferenceStoreExtension } from '@blocksuite/affine-inline-reference/store';
|
import { ReferenceStoreExtension } from '@blocksuite/affine-inline-reference/store';
|
||||||
|
|
||||||
|
import { MigratingStoreExtension } from './migrating-store';
|
||||||
|
|
||||||
export function getInternalStoreExtensions() {
|
export function getInternalStoreExtensions() {
|
||||||
return [
|
return [
|
||||||
FoundationStoreExtension,
|
FoundationStoreExtension,
|
||||||
@@ -44,7 +44,6 @@ export function getInternalStoreExtensions() {
|
|||||||
DividerStoreExtension,
|
DividerStoreExtension,
|
||||||
EdgelessTextStoreExtension,
|
EdgelessTextStoreExtension,
|
||||||
EmbedStoreExtension,
|
EmbedStoreExtension,
|
||||||
EmbedDocStoreExtension,
|
|
||||||
FrameStoreExtension,
|
FrameStoreExtension,
|
||||||
ImageStoreExtension,
|
ImageStoreExtension,
|
||||||
LatexStoreExtension,
|
LatexStoreExtension,
|
||||||
@@ -54,7 +53,6 @@ export function getInternalStoreExtensions() {
|
|||||||
SurfaceRefStoreExtension,
|
SurfaceRefStoreExtension,
|
||||||
TableStoreExtension,
|
TableStoreExtension,
|
||||||
SurfaceStoreExtension,
|
SurfaceStoreExtension,
|
||||||
RootStoreExtension,
|
|
||||||
|
|
||||||
FootnoteStoreExtension,
|
FootnoteStoreExtension,
|
||||||
LinkStoreExtension,
|
LinkStoreExtension,
|
||||||
@@ -68,5 +66,7 @@ export function getInternalStoreExtensions() {
|
|||||||
ConnectorStoreExtension,
|
ConnectorStoreExtension,
|
||||||
GroupStoreExtension,
|
GroupStoreExtension,
|
||||||
TextStoreExtension,
|
TextStoreExtension,
|
||||||
|
|
||||||
|
MigratingStoreExtension,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,21 +7,16 @@ import { DatabaseViewExtension } from '@blocksuite/affine-block-database/view';
|
|||||||
import { DividerViewExtension } from '@blocksuite/affine-block-divider/view';
|
import { DividerViewExtension } from '@blocksuite/affine-block-divider/view';
|
||||||
import { EdgelessTextViewExtension } from '@blocksuite/affine-block-edgeless-text/view';
|
import { EdgelessTextViewExtension } from '@blocksuite/affine-block-edgeless-text/view';
|
||||||
import { EmbedViewExtension } from '@blocksuite/affine-block-embed/view';
|
import { EmbedViewExtension } from '@blocksuite/affine-block-embed/view';
|
||||||
import { EmbedDocViewExtension } from '@blocksuite/affine-block-embed-doc/view';
|
|
||||||
import { FrameViewExtension } from '@blocksuite/affine-block-frame/view';
|
import { FrameViewExtension } from '@blocksuite/affine-block-frame/view';
|
||||||
import { ImageViewExtension } from '@blocksuite/affine-block-image/view';
|
import { ImageViewExtension } from '@blocksuite/affine-block-image/view';
|
||||||
import { LatexViewExtension } from '@blocksuite/affine-block-latex/view';
|
import { LatexViewExtension } from '@blocksuite/affine-block-latex/view';
|
||||||
import { ListViewExtension } from '@blocksuite/affine-block-list/view';
|
import { ListViewExtension } from '@blocksuite/affine-block-list/view';
|
||||||
import { NoteViewExtension } from '@blocksuite/affine-block-note/view';
|
import { NoteViewExtension } from '@blocksuite/affine-block-note/view';
|
||||||
import { ParagraphViewExtension } from '@blocksuite/affine-block-paragraph/view';
|
import { ParagraphViewExtension } from '@blocksuite/affine-block-paragraph/view';
|
||||||
import { RootViewExtension } from '@blocksuite/affine-block-root/view';
|
|
||||||
import { SurfaceViewExtension } from '@blocksuite/affine-block-surface/view';
|
import { SurfaceViewExtension } from '@blocksuite/affine-block-surface/view';
|
||||||
import { SurfaceRefViewExtension } from '@blocksuite/affine-block-surface-ref/view';
|
import { SurfaceRefViewExtension } from '@blocksuite/affine-block-surface-ref/view';
|
||||||
import { TableViewExtension } from '@blocksuite/affine-block-table/view';
|
import { TableViewExtension } from '@blocksuite/affine-block-table/view';
|
||||||
import { FoundationViewExtension } from '@blocksuite/affine-foundation/view';
|
import { FoundationViewExtension } from '@blocksuite/affine-foundation/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';
|
|
||||||
import { BrushViewExtension } from '@blocksuite/affine-gfx-brush/view';
|
import { BrushViewExtension } from '@blocksuite/affine-gfx-brush/view';
|
||||||
import { ConnectorViewExtension } from '@blocksuite/affine-gfx-connector/view';
|
import { ConnectorViewExtension } from '@blocksuite/affine-gfx-connector/view';
|
||||||
import { GroupViewExtension } from '@blocksuite/affine-gfx-group/view';
|
import { GroupViewExtension } from '@blocksuite/affine-gfx-group/view';
|
||||||
@@ -52,6 +47,8 @@ import { SlashMenuViewExtension } from '@blocksuite/affine-widget-slash-menu/vie
|
|||||||
import { ToolbarViewExtension } from '@blocksuite/affine-widget-toolbar/view';
|
import { ToolbarViewExtension } from '@blocksuite/affine-widget-toolbar/view';
|
||||||
import { ViewportOverlayViewExtension } from '@blocksuite/affine-widget-viewport-overlay/view';
|
import { ViewportOverlayViewExtension } from '@blocksuite/affine-widget-viewport-overlay/view';
|
||||||
|
|
||||||
|
import { MigratingViewExtension } from './migrating-view';
|
||||||
|
|
||||||
export function getInternalViewExtensions() {
|
export function getInternalViewExtensions() {
|
||||||
return [
|
return [
|
||||||
FoundationViewExtension,
|
FoundationViewExtension,
|
||||||
@@ -78,7 +75,6 @@ export function getInternalViewExtensions() {
|
|||||||
DividerViewExtension,
|
DividerViewExtension,
|
||||||
EdgelessTextViewExtension,
|
EdgelessTextViewExtension,
|
||||||
EmbedViewExtension,
|
EmbedViewExtension,
|
||||||
EmbedDocViewExtension,
|
|
||||||
FrameViewExtension,
|
FrameViewExtension,
|
||||||
ImageViewExtension,
|
ImageViewExtension,
|
||||||
LatexViewExtension,
|
LatexViewExtension,
|
||||||
@@ -88,7 +84,6 @@ export function getInternalViewExtensions() {
|
|||||||
SurfaceRefViewExtension,
|
SurfaceRefViewExtension,
|
||||||
TableViewExtension,
|
TableViewExtension,
|
||||||
SurfaceViewExtension,
|
SurfaceViewExtension,
|
||||||
RootViewExtension,
|
|
||||||
|
|
||||||
// Inline
|
// Inline
|
||||||
FootnoteViewExtension,
|
FootnoteViewExtension,
|
||||||
@@ -102,6 +97,7 @@ export function getInternalViewExtensions() {
|
|||||||
DragHandleViewExtension,
|
DragHandleViewExtension,
|
||||||
EdgelessAutoConnectViewExtension,
|
EdgelessAutoConnectViewExtension,
|
||||||
EdgelessToolbarViewExtension,
|
EdgelessToolbarViewExtension,
|
||||||
|
MigratingViewExtension,
|
||||||
FrameTitleViewExtension,
|
FrameTitleViewExtension,
|
||||||
KeyboardToolbarViewExtension,
|
KeyboardToolbarViewExtension,
|
||||||
LinkedDocViewExtension,
|
LinkedDocViewExtension,
|
||||||
@@ -112,10 +108,5 @@ export function getInternalViewExtensions() {
|
|||||||
ViewportOverlayViewExtension,
|
ViewportOverlayViewExtension,
|
||||||
EdgelessZoomToolbarViewExtension,
|
EdgelessZoomToolbarViewExtension,
|
||||||
PageDraggingAreaViewExtension,
|
PageDraggingAreaViewExtension,
|
||||||
|
|
||||||
// Fragment
|
|
||||||
DocTitleViewExtension,
|
|
||||||
FramePanelViewExtension,
|
|
||||||
OutlineViewExtension,
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export * from '@blocksuite/affine-foundation/store';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from '@blocksuite/affine-foundation/view';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from '@blocksuite/affine-fragment-doc-title/view';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from '@blocksuite/affine-fragment-frame-panel/view';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from '@blocksuite/affine-fragment-outline/view';
|
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
{ "path": "../blocks/divider" },
|
{ "path": "../blocks/divider" },
|
||||||
{ "path": "../blocks/edgeless-text" },
|
{ "path": "../blocks/edgeless-text" },
|
||||||
{ "path": "../blocks/embed" },
|
{ "path": "../blocks/embed" },
|
||||||
{ "path": "../blocks/embed-doc" },
|
|
||||||
{ "path": "../blocks/frame" },
|
{ "path": "../blocks/frame" },
|
||||||
{ "path": "../blocks/image" },
|
{ "path": "../blocks/image" },
|
||||||
{ "path": "../blocks/latex" },
|
{ "path": "../blocks/latex" },
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
"author": "toeverything",
|
"author": "toeverything",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@blocksuite/affine-block-embed": "workspace:*",
|
||||||
"@blocksuite/affine-block-surface": "workspace:*",
|
"@blocksuite/affine-block-surface": "workspace:*",
|
||||||
"@blocksuite/affine-components": "workspace:*",
|
"@blocksuite/affine-components": "workspace:*",
|
||||||
"@blocksuite/affine-ext-loader": "workspace:*",
|
"@blocksuite/affine-ext-loader": "workspace:*",
|
||||||
@@ -23,7 +24,7 @@
|
|||||||
"@floating-ui/dom": "^1.6.13",
|
"@floating-ui/dom": "^1.6.13",
|
||||||
"@lit/context": "^1.1.2",
|
"@lit/context": "^1.1.2",
|
||||||
"@preact/signals-core": "^1.8.0",
|
"@preact/signals-core": "^1.8.0",
|
||||||
"@toeverything/theme": "^1.1.14",
|
"@toeverything/theme": "^1.1.12",
|
||||||
"file-type": "^20.0.0",
|
"file-type": "^20.0.0",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.0.1",
|
"minimatch": "^10.0.1",
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
|
import { getEmbedCardIcons } from '@blocksuite/affine-block-embed';
|
||||||
import {
|
import {
|
||||||
CaptionedBlockComponent,
|
CaptionedBlockComponent,
|
||||||
SelectedStyle,
|
SelectedStyle,
|
||||||
} from '@blocksuite/affine-components/caption';
|
} from '@blocksuite/affine-components/caption';
|
||||||
import {
|
import { getAttachmentFileIcon } from '@blocksuite/affine-components/icons';
|
||||||
getAttachmentFileIcon,
|
|
||||||
getLoadingIconWith,
|
|
||||||
} from '@blocksuite/affine-components/icons';
|
|
||||||
import { Peekable } from '@blocksuite/affine-components/peek';
|
import { Peekable } from '@blocksuite/affine-components/peek';
|
||||||
import {
|
|
||||||
type ResolvedStateInfo,
|
|
||||||
ResourceController,
|
|
||||||
} from '@blocksuite/affine-components/resource';
|
|
||||||
import { toast } from '@blocksuite/affine-components/toast';
|
import { toast } from '@blocksuite/affine-components/toast';
|
||||||
import {
|
import {
|
||||||
type AttachmentBlockModel,
|
type AttachmentBlockModel,
|
||||||
@@ -28,26 +22,23 @@ import {
|
|||||||
WarningIcon,
|
WarningIcon,
|
||||||
} from '@blocksuite/icons/lit';
|
} from '@blocksuite/icons/lit';
|
||||||
import { BlockSelection } from '@blocksuite/std';
|
import { BlockSelection } from '@blocksuite/std';
|
||||||
import { nanoid, Slice } from '@blocksuite/store';
|
import { Slice } from '@blocksuite/store';
|
||||||
import { computed, signal } from '@preact/signals-core';
|
import { type BlobState } from '@blocksuite/sync';
|
||||||
|
import { effect, signal } from '@preact/signals-core';
|
||||||
import { html, type TemplateResult } from 'lit';
|
import { html, type TemplateResult } from 'lit';
|
||||||
|
import { property } from 'lit/decorators.js';
|
||||||
import { choose } from 'lit/directives/choose.js';
|
import { choose } from 'lit/directives/choose.js';
|
||||||
import { type ClassInfo, classMap } from 'lit/directives/class-map.js';
|
import { type ClassInfo, classMap } from 'lit/directives/class-map.js';
|
||||||
import { guard } from 'lit/directives/guard.js';
|
|
||||||
import { styleMap } from 'lit/directives/style-map.js';
|
import { styleMap } from 'lit/directives/style-map.js';
|
||||||
import { when } from 'lit/directives/when.js';
|
import { when } from 'lit/directives/when.js';
|
||||||
|
|
||||||
import { AttachmentEmbedProvider } from './embed';
|
import { AttachmentEmbedProvider } from './embed';
|
||||||
import { styles } from './styles';
|
import { styles } from './styles';
|
||||||
import { downloadAttachmentBlob, refreshData } from './utils';
|
import { downloadAttachmentBlob, refreshData } from './utils';
|
||||||
|
type State = 'loading' | 'uploading' | 'warning' | 'oversize' | 'none';
|
||||||
type AttachmentResolvedStateInfo = ResolvedStateInfo & {
|
|
||||||
kind?: TemplateResult;
|
|
||||||
};
|
|
||||||
|
|
||||||
@Peekable({
|
@Peekable({
|
||||||
enableOn: ({ model }: AttachmentBlockComponent) => {
|
enableOn: ({ model }: AttachmentBlockComponent) => {
|
||||||
return !model.store.readonly && model.props.type.endsWith('pdf');
|
return !model.doc.readonly && model.props.type.endsWith('pdf');
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export class AttachmentBlockComponent extends CaptionedBlockComponent<AttachmentBlockModel> {
|
export class AttachmentBlockComponent extends CaptionedBlockComponent<AttachmentBlockModel> {
|
||||||
@@ -55,13 +46,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
|||||||
|
|
||||||
blockDraggable = true;
|
blockDraggable = true;
|
||||||
|
|
||||||
resourceController = new ResourceController(
|
blobState$ = signal<Partial<BlobState>>({});
|
||||||
computed(() => this.model.props.sourceId$.value)
|
|
||||||
);
|
|
||||||
|
|
||||||
get blobUrl() {
|
|
||||||
return this.resourceController.blobUrl$.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected containerStyleMap = styleMap({
|
protected containerStyleMap = styleMap({
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
@@ -84,7 +69,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
|||||||
};
|
};
|
||||||
|
|
||||||
copy = () => {
|
copy = () => {
|
||||||
const slice = Slice.fromModels(this.store, [this.model]);
|
const slice = Slice.fromModels(this.doc, [this.model]);
|
||||||
this.std.clipboard.copySlice(slice).catch(console.error);
|
this.std.clipboard.copySlice(slice).catch(console.error);
|
||||||
toast(this.host, 'Copied to clipboard');
|
toast(this.host, 'Copied to clipboard');
|
||||||
};
|
};
|
||||||
@@ -108,23 +93,33 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
|||||||
window.open(blobUrl, '_blank');
|
window.open(blobUrl, '_blank');
|
||||||
};
|
};
|
||||||
|
|
||||||
// Refreshes data.
|
|
||||||
refreshData = () => {
|
refreshData = () => {
|
||||||
refreshData(this).catch(console.error);
|
refreshData(this.std, this).catch(console.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly _refreshKey$ = signal<string | null>(null);
|
updateBlobState(state: Partial<BlobState>) {
|
||||||
|
this.blobState$.value = { ...this.blobState$.value, ...state };
|
||||||
|
}
|
||||||
|
|
||||||
// Refreshes the embed component.
|
determineState = (
|
||||||
reload = () => {
|
loading: boolean,
|
||||||
if (this.model.props.embed) {
|
uploading: boolean,
|
||||||
this._refreshKey$.value = nanoid();
|
overSize: boolean,
|
||||||
return;
|
error: boolean
|
||||||
}
|
): State => {
|
||||||
|
if (overSize) return 'oversize';
|
||||||
this.refreshData();
|
if (error) return 'warning';
|
||||||
|
if (uploading) return 'uploading';
|
||||||
|
if (loading) return 'loading';
|
||||||
|
return 'none';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected get embedView() {
|
||||||
|
return this.std
|
||||||
|
.get(AttachmentEmbedProvider)
|
||||||
|
.render(this.model, this.blobUrl ?? undefined, this._maxFileSize);
|
||||||
|
}
|
||||||
|
|
||||||
private _selectBlock() {
|
private _selectBlock() {
|
||||||
const selectionManager = this.host.selection;
|
const selectionManager = this.host.selection;
|
||||||
const blockSelection = selectionManager.create(BlockSelection, {
|
const blockSelection = selectionManager.create(BlockSelection, {
|
||||||
@@ -138,22 +133,46 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
|||||||
|
|
||||||
this.contentEditable = 'false';
|
this.contentEditable = 'false';
|
||||||
|
|
||||||
this.resourceController.setEngine(this.std.store.blobSync);
|
|
||||||
|
|
||||||
this.disposables.add(this.resourceController.subscribe());
|
|
||||||
this.disposables.add(this.resourceController);
|
|
||||||
|
|
||||||
this.refreshData();
|
this.refreshData();
|
||||||
|
|
||||||
if (!this.model.props.style && !this.store.readonly) {
|
this.disposables.add(
|
||||||
this.store.withoutTransact(() => {
|
effect(() => {
|
||||||
this.store.updateBlock(this.model, {
|
const blobId = this.model.props.sourceId$.value;
|
||||||
|
if (!blobId) return;
|
||||||
|
|
||||||
|
const blobState$ = this.std.store.blobSync.blobState$(blobId);
|
||||||
|
if (!blobState$) return;
|
||||||
|
|
||||||
|
const subscription = blobState$.subscribe(state => {
|
||||||
|
if (state.overSize || state.errorMessage) {
|
||||||
|
state.uploading = false;
|
||||||
|
state.downloading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateBlobState(state);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => subscription.unsubscribe();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!this.model.props.style && !this.doc.readonly) {
|
||||||
|
this.doc.withoutTransact(() => {
|
||||||
|
this.doc.updateBlock(this.model, {
|
||||||
style: AttachmentBlockStyles[1],
|
style: AttachmentBlockStyles[1],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override disconnectedCallback() {
|
||||||
|
const blobUrl = this.blobUrl;
|
||||||
|
if (blobUrl) {
|
||||||
|
URL.revokeObjectURL(blobUrl);
|
||||||
|
}
|
||||||
|
super.disconnectedCallback();
|
||||||
|
}
|
||||||
|
|
||||||
override firstUpdated() {
|
override firstUpdated() {
|
||||||
// lazy bindings
|
// lazy bindings
|
||||||
this.disposables.addFromEvent(this, 'click', this.onClick);
|
this.disposables.addFromEvent(this, 'click', this.onClick);
|
||||||
@@ -207,135 +226,128 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
|||||||
|
|
||||||
protected renderWithHorizontal(
|
protected renderWithHorizontal(
|
||||||
classInfo: ClassInfo,
|
classInfo: ClassInfo,
|
||||||
{ icon, title, description, kind, state }: AttachmentResolvedStateInfo
|
icon: TemplateResult,
|
||||||
|
title: string,
|
||||||
|
description: string,
|
||||||
|
kind: TemplateResult,
|
||||||
|
state: State
|
||||||
) {
|
) {
|
||||||
return html`
|
return html`<div class=${classMap(classInfo)}>
|
||||||
<div class=${classMap(classInfo)}>
|
<div class="affine-attachment-content">
|
||||||
<div class="affine-attachment-content">
|
<div class="affine-attachment-content-title">
|
||||||
<div class="affine-attachment-content-title">
|
<div class="affine-attachment-content-title-icon">${icon}</div>
|
||||||
<div class="affine-attachment-content-title-icon">${icon}</div>
|
|
||||||
<div class="affine-attachment-content-title-text truncate">
|
|
||||||
${title}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="affine-attachment-content-description">
|
<div class="affine-attachment-content-title-text truncate">
|
||||||
<div class="affine-attachment-content-info truncate">
|
${title}
|
||||||
${description}
|
|
||||||
</div>
|
|
||||||
${choose(state, [
|
|
||||||
['error', this.renderReloadButton],
|
|
||||||
['error:oversize', this.renderUpgradeButton],
|
|
||||||
])}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="affine-attachment-banner">${kind}</div>
|
<div class="affine-attachment-content-description">
|
||||||
|
<div class="affine-attachment-content-info truncate">
|
||||||
|
${description}
|
||||||
|
</div>
|
||||||
|
${choose(state, [
|
||||||
|
['oversize', this.renderUpgradeButton],
|
||||||
|
['warning', this.renderReloadButton],
|
||||||
|
])}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
|
||||||
|
<div class="affine-attachment-banner">${kind}</div>
|
||||||
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected renderWithVertical(
|
protected renderWithVertical(
|
||||||
classInfo: ClassInfo,
|
classInfo: ClassInfo,
|
||||||
{ icon, title, description, kind, state }: AttachmentResolvedStateInfo
|
icon: TemplateResult,
|
||||||
|
title: string,
|
||||||
|
description: string,
|
||||||
|
kind: TemplateResult,
|
||||||
|
state?: State
|
||||||
) {
|
) {
|
||||||
return html`
|
return html`<div class=${classMap(classInfo)}>
|
||||||
<div class=${classMap(classInfo)}>
|
<div class="affine-attachment-content">
|
||||||
<div class="affine-attachment-content">
|
<div class="affine-attachment-content-title">
|
||||||
<div class="affine-attachment-content-title">
|
<div class="affine-attachment-content-title-icon">${icon}</div>
|
||||||
<div class="affine-attachment-content-title-icon">${icon}</div>
|
|
||||||
<div class="affine-attachment-content-title-text truncate">
|
|
||||||
${title}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="affine-attachment-content-info truncate">
|
<div class="affine-attachment-content-title-text truncate">
|
||||||
${description}
|
${title}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="affine-attachment-banner">
|
<div class="affine-attachment-content-info truncate">
|
||||||
${kind}
|
${description}
|
||||||
${choose(state, [
|
|
||||||
['error', this.renderReloadButton],
|
|
||||||
['error:oversize', this.renderUpgradeButton],
|
|
||||||
])}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
|
||||||
|
<div class="affine-attachment-banner">
|
||||||
|
${kind}
|
||||||
|
${choose(state, [
|
||||||
|
['oversize', this.renderUpgradeButton],
|
||||||
|
['warning', this.renderReloadButton],
|
||||||
|
])}
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected resolvedState$ = computed<AttachmentResolvedStateInfo>(() => {
|
protected renderCard = () => {
|
||||||
|
const { name, size, style } = this.model.props;
|
||||||
|
const cardStyle = style ?? AttachmentBlockStyles[1];
|
||||||
|
|
||||||
const theme = this.std.get(ThemeProvider).theme$.value;
|
const theme = this.std.get(ThemeProvider).theme$.value;
|
||||||
const loadingIcon = getLoadingIconWith(theme);
|
const { LoadingIcon } = getEmbedCardIcons(theme);
|
||||||
|
|
||||||
const size = this.model.props.size;
|
const blobState = this.blobState$.value;
|
||||||
const name = this.model.props.name$.value;
|
const {
|
||||||
const kind = getAttachmentFileIcon(name.split('.').pop() ?? '');
|
uploading = false,
|
||||||
|
downloading = false,
|
||||||
const resolvedState = this.resourceController.resolveStateWith({
|
overSize = false,
|
||||||
loadingIcon,
|
errorMessage,
|
||||||
errorIcon: WarningIcon(),
|
} = blobState;
|
||||||
icon: AttachmentIcon(),
|
const warning = !overSize && Boolean(errorMessage);
|
||||||
title: name,
|
const error = overSize || warning;
|
||||||
description: humanFileSize(size),
|
const loading = !error && downloading;
|
||||||
});
|
const state = this.determineState(loading, uploading, overSize, error);
|
||||||
|
|
||||||
return { ...resolvedState, kind };
|
|
||||||
});
|
|
||||||
|
|
||||||
protected renderCardView = () => {
|
|
||||||
const resolvedState = this.resolvedState$.value;
|
|
||||||
const cardStyle = this.model.props.style$.value ?? AttachmentBlockStyles[1];
|
|
||||||
|
|
||||||
const classInfo = {
|
const classInfo = {
|
||||||
'affine-attachment-card': true,
|
'affine-attachment-card': true,
|
||||||
[cardStyle]: true,
|
[cardStyle]: true,
|
||||||
loading: resolvedState.loading,
|
error,
|
||||||
error: resolvedState.error,
|
loading,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const icon = loading
|
||||||
|
? LoadingIcon
|
||||||
|
: error
|
||||||
|
? WarningIcon()
|
||||||
|
: AttachmentIcon();
|
||||||
|
const title = uploading ? 'Uploading...' : loading ? 'Loading...' : name;
|
||||||
|
const description = errorMessage || humanFileSize(size);
|
||||||
|
const kind = getAttachmentFileIcon(name.split('.').pop() ?? '');
|
||||||
|
|
||||||
return when(
|
return when(
|
||||||
cardStyle === 'cubeThick',
|
cardStyle === 'cubeThick',
|
||||||
() => this.renderWithVertical(classInfo, resolvedState),
|
() =>
|
||||||
() => this.renderWithHorizontal(classInfo, resolvedState)
|
this.renderWithVertical(
|
||||||
|
classInfo,
|
||||||
|
icon,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
kind,
|
||||||
|
state
|
||||||
|
),
|
||||||
|
() =>
|
||||||
|
this.renderWithHorizontal(
|
||||||
|
classInfo,
|
||||||
|
icon,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
kind,
|
||||||
|
state
|
||||||
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
protected renderEmbedView = () => {
|
|
||||||
const { model, blobUrl } = this;
|
|
||||||
if (!model.props.embed || !blobUrl) return null;
|
|
||||||
|
|
||||||
const { std, _maxFileSize } = this;
|
|
||||||
const provider = std.get(AttachmentEmbedProvider);
|
|
||||||
|
|
||||||
const render = provider.getRender(model, _maxFileSize);
|
|
||||||
if (!render) return null;
|
|
||||||
|
|
||||||
const enabled = provider.shouldShowStatus(model);
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<div class="affine-attachment-embed-container">
|
|
||||||
${guard([this._refreshKey$.value], () => render(model, blobUrl))}
|
|
||||||
</div>
|
|
||||||
${when(enabled, () => {
|
|
||||||
const resolvedState = this.resolvedState$.value;
|
|
||||||
if (resolvedState.state !== 'error') return null;
|
|
||||||
// It should be an error messge.
|
|
||||||
const message = resolvedState.description;
|
|
||||||
if (!message) return null;
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<affine-resource-status
|
|
||||||
class="affine-attachment-embed-status"
|
|
||||||
.message=${message}
|
|
||||||
.reload=${() => this.reload()}
|
|
||||||
></affine-resource-status>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
`;
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly _renderCitation = () => {
|
private readonly _renderCitation = () => {
|
||||||
const { name, footnoteIdentifier } = this.model.props;
|
const { name, footnoteIdentifier } = this.model.props;
|
||||||
const fileType = name.split('.').pop() ?? '';
|
const fileType = name.split('.').pop() ?? '';
|
||||||
@@ -360,12 +372,23 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
|||||||
${when(
|
${when(
|
||||||
this.isCitation,
|
this.isCitation,
|
||||||
() => this._renderCitation(),
|
() => this._renderCitation(),
|
||||||
() => this.renderEmbedView() ?? this.renderCardView()
|
() =>
|
||||||
|
when(
|
||||||
|
this.embedView,
|
||||||
|
() =>
|
||||||
|
html`<div class="affine-attachment-embed-container">
|
||||||
|
${this.embedView}
|
||||||
|
</div>`,
|
||||||
|
this.renderCard
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor blobUrl: string | null = null;
|
||||||
|
|
||||||
override accessor selectedStyle = SelectedStyle.Border;
|
override accessor selectedStyle = SelectedStyle.Border;
|
||||||
|
|
||||||
override accessor useCaptionEditor = true;
|
override accessor useCaptionEditor = true;
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
|
import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
|
||||||
import {
|
import { AttachmentBlockStyles } from '@blocksuite/affine-model';
|
||||||
AttachmentBlockSchema,
|
|
||||||
AttachmentBlockStyles,
|
|
||||||
} from '@blocksuite/affine-model';
|
|
||||||
import {
|
import {
|
||||||
EMBED_CARD_HEIGHT,
|
EMBED_CARD_HEIGHT,
|
||||||
EMBED_CARD_WIDTH,
|
EMBED_CARD_WIDTH,
|
||||||
} from '@blocksuite/affine-shared/consts';
|
} from '@blocksuite/affine-shared/consts';
|
||||||
import { toGfxBlockComponent } from '@blocksuite/std';
|
import { toGfxBlockComponent } from '@blocksuite/std';
|
||||||
import { GfxViewInteractionExtension } from '@blocksuite/std/gfx';
|
|
||||||
import { styleMap } from 'lit/directives/style-map.js';
|
import { styleMap } from 'lit/directives/style-map.js';
|
||||||
|
|
||||||
import { AttachmentBlockComponent } from './attachment-block.js';
|
import { AttachmentBlockComponent } from './attachment-block.js';
|
||||||
@@ -52,21 +48,3 @@ declare global {
|
|||||||
'affine-edgeless-attachment': AttachmentEdgelessBlockComponent;
|
'affine-edgeless-attachment': AttachmentEdgelessBlockComponent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AttachmentBlockInteraction = GfxViewInteractionExtension(
|
|
||||||
AttachmentBlockSchema.model.flavour,
|
|
||||||
{
|
|
||||||
resizeConstraint: {
|
|
||||||
lockRatio: true,
|
|
||||||
},
|
|
||||||
handleRotate: () => {
|
|
||||||
return {
|
|
||||||
beforeRotate: context => {
|
|
||||||
context.set({
|
|
||||||
rotatable: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { AttachmentBlockSchema } from '@blocksuite/affine-model';
|
||||||
|
import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu';
|
||||||
|
import { BlockViewExtension, FlavourExtension } from '@blocksuite/std';
|
||||||
|
import type { ExtensionType } from '@blocksuite/store';
|
||||||
|
import { literal } from 'lit/static-html.js';
|
||||||
|
|
||||||
|
import { AttachmentBlockAdapterExtensions } from './adapters/extension.js';
|
||||||
|
import { AttachmentDropOption } from './attachment-service.js';
|
||||||
|
import { attachmentSlashMenuConfig } from './configs/slash-menu.js';
|
||||||
|
import { createBuiltinToolbarConfigExtension } from './configs/toolbar';
|
||||||
|
import {
|
||||||
|
AttachmentEmbedConfigExtension,
|
||||||
|
AttachmentEmbedService,
|
||||||
|
} from './embed';
|
||||||
|
|
||||||
|
const flavour = AttachmentBlockSchema.model.flavour;
|
||||||
|
|
||||||
|
export const AttachmentBlockSpec: ExtensionType[] = [
|
||||||
|
FlavourExtension(flavour),
|
||||||
|
BlockViewExtension(flavour, model => {
|
||||||
|
return model.parent?.flavour === 'affine:surface'
|
||||||
|
? literal`affine-edgeless-attachment`
|
||||||
|
: literal`affine-attachment`;
|
||||||
|
}),
|
||||||
|
AttachmentDropOption,
|
||||||
|
AttachmentEmbedConfigExtension(),
|
||||||
|
AttachmentEmbedService,
|
||||||
|
AttachmentBlockAdapterExtensions,
|
||||||
|
createBuiltinToolbarConfigExtension(flavour),
|
||||||
|
SlashMenuConfigExtension(flavour, attachmentSlashMenuConfig),
|
||||||
|
].flat();
|
||||||
@@ -41,7 +41,7 @@ export const RenameModal = ({
|
|||||||
toast(editorHost, 'File name cannot be empty');
|
toast(editorHost, 'File name cannot be empty');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
model.store.updateBlock(model, {
|
model.doc.updateBlock(model, {
|
||||||
name: newFileName,
|
name: newFileName,
|
||||||
});
|
});
|
||||||
abort();
|
abort();
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export const attachmentSlashMenuConfig: SlashMenuConfig = {
|
|||||||
searchAlias: ['file'],
|
searchAlias: ['file'],
|
||||||
group: '4_Content & Media@3',
|
group: '4_Content & Media@3',
|
||||||
when: ({ model }) =>
|
when: ({ model }) =>
|
||||||
model.store.schema.flavourSchemaMap.has('affine:attachment'),
|
model.doc.schema.flavourSchemaMap.has('affine:attachment'),
|
||||||
action: ({ std, model }) => {
|
action: ({ std, model }) => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const file = await openFileOrFiles();
|
const file = await openFileOrFiles();
|
||||||
@@ -41,7 +41,7 @@ export const attachmentSlashMenuConfig: SlashMenuConfig = {
|
|||||||
},
|
},
|
||||||
group: '4_Content & Media@4',
|
group: '4_Content & Media@4',
|
||||||
when: ({ model }) =>
|
when: ({ model }) =>
|
||||||
model.store.schema.flavourSchemaMap.has('affine:attachment'),
|
model.doc.schema.flavourSchemaMap.has('affine:attachment'),
|
||||||
action: ({ std, model }) => {
|
action: ({ std, model }) => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const file = await openFileOrFiles();
|
const file = await openFileOrFiles();
|
||||||
|
|||||||
@@ -77,19 +77,13 @@ export const attachmentViewDropdownMenu = {
|
|||||||
const model = ctx.getCurrentModelByType(AttachmentBlockModel);
|
const model = ctx.getCurrentModelByType(AttachmentBlockModel);
|
||||||
if (!model) return;
|
if (!model) return;
|
||||||
|
|
||||||
const provider = ctx.std.get(AttachmentEmbedProvider);
|
if (!ctx.hasSelectedSurfaceModels) {
|
||||||
|
|
||||||
// TODO(@fundon): should auto focus image block.
|
|
||||||
if (
|
|
||||||
provider.shouldBeConverted(model) &&
|
|
||||||
!ctx.hasSelectedSurfaceModels
|
|
||||||
) {
|
|
||||||
// Clears
|
// Clears
|
||||||
ctx.reset();
|
ctx.reset();
|
||||||
ctx.select('note');
|
ctx.select('note');
|
||||||
}
|
}
|
||||||
|
|
||||||
provider.convertTo(model);
|
ctx.std.get(AttachmentEmbedProvider).convertTo(model);
|
||||||
|
|
||||||
ctx.track('SelectedView', {
|
ctx.track('SelectedView', {
|
||||||
...trackBaseProps,
|
...trackBaseProps,
|
||||||
@@ -100,32 +94,18 @@ export const attachmentViewDropdownMenu = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
content(ctx) {
|
content(ctx) {
|
||||||
const block = ctx.getCurrentBlockByType(AttachmentBlockComponent);
|
const model = ctx.getCurrentModelByType(AttachmentBlockModel);
|
||||||
if (!block) return null;
|
if (!model) return null;
|
||||||
|
|
||||||
const model = block.model;
|
|
||||||
const embedProvider = ctx.std.get(AttachmentEmbedProvider);
|
const embedProvider = ctx.std.get(AttachmentEmbedProvider);
|
||||||
const actions = computed(() => {
|
const actions = this.actions.map(action => ({ ...action }));
|
||||||
const [cardAction, embedAction] = this.actions.map(action => ({
|
const viewType$ = computed(() => {
|
||||||
...action,
|
const [cardAction, embedAction] = actions;
|
||||||
}));
|
|
||||||
|
|
||||||
const ok = block.resourceController.resolvedState$.value.state === 'none';
|
|
||||||
const sourceId = Boolean(model.props.sourceId$.value);
|
|
||||||
const embed = model.props.embed$.value ?? false;
|
const embed = model.props.embed$.value ?? false;
|
||||||
// 1. Check whether `sourceId` exists.
|
|
||||||
// 2. Check if `embedded` is allowed.
|
|
||||||
// 3. Check `blobState$`
|
|
||||||
const allowed = ok && sourceId && embedProvider.embedded(model) && !embed;
|
|
||||||
|
|
||||||
cardAction.disabled = !embed;
|
cardAction.disabled = !embed;
|
||||||
embedAction.disabled = !allowed;
|
embedAction.disabled = embed && embedProvider.embedded(model);
|
||||||
|
|
||||||
return [cardAction, embedAction];
|
|
||||||
});
|
|
||||||
const viewType$ = computed(() => {
|
|
||||||
const [cardAction, embedAction] = actions.value;
|
|
||||||
const embed = model.props.embed$.value ?? false;
|
|
||||||
return embed ? embedAction.label : cardAction.label;
|
return embed ? embedAction.label : cardAction.label;
|
||||||
});
|
});
|
||||||
const onToggle = (e: CustomEvent<boolean>) => {
|
const onToggle = (e: CustomEvent<boolean>) => {
|
||||||
@@ -143,7 +123,7 @@ export const attachmentViewDropdownMenu = {
|
|||||||
model,
|
model,
|
||||||
html`<affine-view-dropdown-menu
|
html`<affine-view-dropdown-menu
|
||||||
@toggle=${onToggle}
|
@toggle=${onToggle}
|
||||||
.actions=${actions.value}
|
.actions=${actions}
|
||||||
.context=${ctx}
|
.context=${ctx}
|
||||||
.viewType$=${viewType$}
|
.viewType$=${viewType$}
|
||||||
></affine-view-dropdown-menu>`
|
></affine-view-dropdown-menu>`
|
||||||
@@ -263,7 +243,7 @@ const builtinToolbarConfig = {
|
|||||||
icon: ResetIcon(),
|
icon: ResetIcon(),
|
||||||
run(ctx) {
|
run(ctx) {
|
||||||
const block = ctx.getCurrentBlockByType(AttachmentBlockComponent);
|
const block = ctx.getCurrentBlockByType(AttachmentBlockComponent);
|
||||||
block?.reload();
|
block?.refreshData();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,10 +3,6 @@ import {
|
|||||||
type ImageBlockProps,
|
type ImageBlockProps,
|
||||||
MAX_IMAGE_WIDTH,
|
MAX_IMAGE_WIDTH,
|
||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
import {
|
|
||||||
EMBED_CARD_HEIGHT,
|
|
||||||
EMBED_CARD_WIDTH,
|
|
||||||
} from '@blocksuite/affine-shared/consts';
|
|
||||||
import { FileSizeLimitProvider } from '@blocksuite/affine-shared/services';
|
import { FileSizeLimitProvider } from '@blocksuite/affine-shared/services';
|
||||||
import {
|
import {
|
||||||
readImageSize,
|
readImageSize,
|
||||||
@@ -21,7 +17,6 @@ import type { ExtensionType } from '@blocksuite/store';
|
|||||||
import { Extension } from '@blocksuite/store';
|
import { Extension } from '@blocksuite/store';
|
||||||
import type { TemplateResult } from 'lit';
|
import type { TemplateResult } from 'lit';
|
||||||
import { html } from 'lit';
|
import { html } from 'lit';
|
||||||
import { styleMap } from 'lit/directives/style-map.js';
|
|
||||||
|
|
||||||
import { getAttachmentBlob } from './utils';
|
import { getAttachmentBlob } from './utils';
|
||||||
|
|
||||||
@@ -39,22 +34,9 @@ export type AttachmentEmbedConfig = {
|
|||||||
std: BlockStdScope
|
std: BlockStdScope
|
||||||
) => Promise<void> | void;
|
) => Promise<void> | void;
|
||||||
/**
|
/**
|
||||||
* Renders the embed view.
|
* The template will be used to render the embed view.
|
||||||
*/
|
*/
|
||||||
render?: (
|
template?: (model: AttachmentBlockModel, blobUrl: string) => TemplateResult;
|
||||||
model: AttachmentBlockModel,
|
|
||||||
blobUrl: string
|
|
||||||
) => TemplateResult | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should show status when turned on.
|
|
||||||
*/
|
|
||||||
shouldShowStatus?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should block type conversion be required.
|
|
||||||
*/
|
|
||||||
shouldBeConverted?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Single embed config.
|
// Single embed config.
|
||||||
@@ -115,53 +97,39 @@ export class AttachmentEmbedService extends Extension {
|
|||||||
// Converts to embed view.
|
// Converts to embed view.
|
||||||
convertTo(model: AttachmentBlockModel, maxFileSize = this._maxFileSize) {
|
convertTo(model: AttachmentBlockModel, maxFileSize = this._maxFileSize) {
|
||||||
const config = this.values.find(config => config.check(model, maxFileSize));
|
const config = this.values.find(config => config.check(model, maxFileSize));
|
||||||
|
if (!config?.action) {
|
||||||
if (config?.action) {
|
model.doc.updateBlock(model, { embed: true });
|
||||||
config.action(model, this.std)?.catch(console.error);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
config.action(model, this.std)?.catch(console.error);
|
||||||
model.store.updateBlock(model, { embed: true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
embedded(model: AttachmentBlockModel, maxFileSize = this._maxFileSize) {
|
embedded(model: AttachmentBlockModel, maxFileSize = this._maxFileSize) {
|
||||||
return this.values.some(config => config.check(model, maxFileSize));
|
return this.values.some(config => config.check(model, maxFileSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
getRender(model: AttachmentBlockModel, maxFileSize = this._maxFileSize) {
|
render(
|
||||||
return (
|
|
||||||
this.values.find(config => config.check(model, maxFileSize))?.render ??
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldShowStatus(
|
|
||||||
model: AttachmentBlockModel,
|
model: AttachmentBlockModel,
|
||||||
|
blobUrl?: string,
|
||||||
maxFileSize = this._maxFileSize
|
maxFileSize = this._maxFileSize
|
||||||
) {
|
) {
|
||||||
return (
|
if (!model.props.embed || !blobUrl) return;
|
||||||
this.values.find(config => config.check(model, maxFileSize))
|
|
||||||
?.shouldShowStatus ?? false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldBeConverted(
|
const config = this.values.find(config => config.check(model, maxFileSize));
|
||||||
model: AttachmentBlockModel,
|
if (!config || !config.template) {
|
||||||
maxFileSize = this._maxFileSize
|
console.error('No embed view template found!', model, model.props.type);
|
||||||
) {
|
return;
|
||||||
return (
|
}
|
||||||
this.values.find(config => config.check(model, maxFileSize))
|
|
||||||
?.shouldBeConverted ?? false
|
return config.template(model, blobUrl);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const embedConfig: AttachmentEmbedConfig[] = [
|
const embedConfig: AttachmentEmbedConfig[] = [
|
||||||
{
|
{
|
||||||
name: 'image',
|
name: 'image',
|
||||||
shouldBeConverted: true,
|
|
||||||
check: model =>
|
check: model =>
|
||||||
model.store.schema.flavourSchemaMap.has('affine:image') &&
|
model.doc.schema.flavourSchemaMap.has('affine:image') &&
|
||||||
model.props.type.startsWith('image/'),
|
model.props.type.startsWith('image/'),
|
||||||
async action(model, std) {
|
async action(model, std) {
|
||||||
const component = std.view.getBlock(model.id);
|
const component = std.view.getBlock(model.id);
|
||||||
@@ -172,30 +140,16 @@ const embedConfig: AttachmentEmbedConfig[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'pdf',
|
name: 'pdf',
|
||||||
shouldShowStatus: true,
|
|
||||||
check: (model, maxFileSize) =>
|
check: (model, maxFileSize) =>
|
||||||
model.props.type === 'application/pdf' && model.props.size <= maxFileSize,
|
model.props.type === 'application/pdf' && model.props.size <= maxFileSize,
|
||||||
action: model => {
|
template: (_, blobUrl) => {
|
||||||
const bound = Bound.deserialize(model.props.xywh);
|
|
||||||
bound.w = EMBED_CARD_WIDTH.pdf;
|
|
||||||
bound.h = EMBED_CARD_HEIGHT.pdf;
|
|
||||||
model.store.updateBlock(model, {
|
|
||||||
embed: true,
|
|
||||||
style: 'pdf',
|
|
||||||
xywh: bound.serialize(),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
render: (_, blobUrl) => {
|
|
||||||
// More options: https://tinytip.co/tips/html-pdf-params/
|
// More options: https://tinytip.co/tips/html-pdf-params/
|
||||||
// https://chromium.googlesource.com/chromium/src/+/refs/tags/121.0.6153.1/chrome/browser/resources/pdf/open_pdf_params_parser.ts
|
// https://chromium.googlesource.com/chromium/src/+/refs/tags/121.0.6153.1/chrome/browser/resources/pdf/open_pdf_params_parser.ts
|
||||||
const parameters = '#toolbar=0';
|
const parameters = '#toolbar=0';
|
||||||
return html`
|
return html`
|
||||||
<iframe
|
<iframe
|
||||||
style=${styleMap({
|
style="width: 100%; color-scheme: auto;"
|
||||||
width: '100%',
|
height="480"
|
||||||
minHeight: '480px',
|
|
||||||
colorScheme: 'auto',
|
|
||||||
})}
|
|
||||||
src=${blobUrl + parameters}
|
src=${blobUrl + parameters}
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
scrolling="no"
|
scrolling="no"
|
||||||
@@ -203,7 +157,6 @@ const embedConfig: AttachmentEmbedConfig[] = [
|
|||||||
allowTransparency
|
allowTransparency
|
||||||
allowfullscreen
|
allowfullscreen
|
||||||
type="application/pdf"
|
type="application/pdf"
|
||||||
credentialless
|
|
||||||
></iframe>
|
></iframe>
|
||||||
<div class="affine-attachment-embed-event-mask"></div>
|
<div class="affine-attachment-embed-event-mask"></div>
|
||||||
`;
|
`;
|
||||||
@@ -211,44 +164,23 @@ const embedConfig: AttachmentEmbedConfig[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'video',
|
name: 'video',
|
||||||
shouldShowStatus: true,
|
|
||||||
check: (model, maxFileSize) =>
|
check: (model, maxFileSize) =>
|
||||||
model.props.type.startsWith('video/') && model.props.size <= maxFileSize,
|
model.props.type.startsWith('video/') && model.props.size <= maxFileSize,
|
||||||
action: model => {
|
template: (_, blobUrl) =>
|
||||||
const bound = Bound.deserialize(model.props.xywh);
|
|
||||||
bound.w = EMBED_CARD_WIDTH.video;
|
|
||||||
bound.h = EMBED_CARD_HEIGHT.video;
|
|
||||||
model.store.updateBlock(model, {
|
|
||||||
embed: true,
|
|
||||||
style: 'video',
|
|
||||||
xywh: bound.serialize(),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
render: (_, blobUrl) =>
|
|
||||||
html`<video
|
html`<video
|
||||||
style=${styleMap({
|
style="max-height: max-content;"
|
||||||
display: 'flex',
|
width="100%;"
|
||||||
objectFit: 'cover',
|
height="480"
|
||||||
backgroundSize: 'cover',
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
})}
|
|
||||||
src=${blobUrl}
|
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
controls
|
controls
|
||||||
|
src=${blobUrl}
|
||||||
></video>`,
|
></video>`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'audio',
|
name: 'audio',
|
||||||
check: (model, maxFileSize) =>
|
check: (model, maxFileSize) =>
|
||||||
model.props.type.startsWith('audio/') && model.props.size <= maxFileSize,
|
model.props.type.startsWith('audio/') && model.props.size <= maxFileSize,
|
||||||
render: (_, blobUrl) =>
|
template: (_, blobUrl) =>
|
||||||
html`<audio
|
html`<audio controls src=${blobUrl} style="margin: 4px;"></audio>`,
|
||||||
style=${styleMap({ margin: '4px' })}
|
|
||||||
src=${blobUrl}
|
|
||||||
controls
|
|
||||||
></audio>`,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -256,7 +188,7 @@ const embedConfig: AttachmentEmbedConfig[] = [
|
|||||||
* Turn the attachment block into an image block.
|
* Turn the attachment block into an image block.
|
||||||
*/
|
*/
|
||||||
async function turnIntoImageBlock(model: AttachmentBlockModel) {
|
async function turnIntoImageBlock(model: AttachmentBlockModel) {
|
||||||
if (!model.store.schema.flavourSchemaMap.has('affine:image')) {
|
if (!model.doc.schema.flavourSchemaMap.has('affine:image')) {
|
||||||
console.error('The image flavour is not supported!');
|
console.error('The image flavour is not supported!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export * from './adapters';
|
export * from './adapters';
|
||||||
export * from './attachment-block';
|
export * from './attachment-block';
|
||||||
export * from './attachment-service';
|
export * from './attachment-service';
|
||||||
|
export * from './attachment-spec';
|
||||||
export { attachmentViewDropdownMenu } from './configs/toolbar';
|
export { attachmentViewDropdownMenu } from './configs/toolbar';
|
||||||
export * from './edgeless-clipboard-config';
|
export * from './edgeless-clipboard-config';
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ export const styles = css`
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
overflow: hidden;
|
|
||||||
border: 1px solid ${unsafeCSSVarV2('layer/background/tertiary')};
|
border: 1px solid ${unsafeCSSVarV2('layer/background/tertiary')};
|
||||||
background: ${unsafeCSSVarV2('layer/background/primary')};
|
background: ${unsafeCSSVarV2('layer/background/primary')};
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
&.focused {
|
&.focused {
|
||||||
border-color: ${unsafeCSSVarV2('layer/insideBorder/primaryBorder')};
|
border-color: ${unsafeCSSVarV2('layer/insideBorder/primaryBorder')};
|
||||||
@@ -30,13 +30,6 @@ export const styles = css`
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.truncate {
|
|
||||||
align-self: stretch;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.affine-attachment-content-title {
|
.affine-attachment-content-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -54,6 +47,13 @@ export const styles = css`
|
|||||||
color: var(--affine-text-primary-color);
|
color: var(--affine-text-primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.truncate {
|
||||||
|
align-self: stretch;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.affine-attachment-content-title-text {
|
.affine-attachment-content-title-text {
|
||||||
color: var(--affine-text-primary-color);
|
color: var(--affine-text-primary-color);
|
||||||
font-family: var(--affine-font-family);
|
font-family: var(--affine-font-family);
|
||||||
@@ -117,11 +117,6 @@ export const styles = css`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.affine-attachment-card.loading,
|
|
||||||
.affine-attachment-card.error {
|
|
||||||
background: ${unsafeCSSVarV2('layer/background/secondary')};
|
|
||||||
}
|
|
||||||
|
|
||||||
.affine-attachment-card.cubeThick {
|
.affine-attachment-card.cubeThick {
|
||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
|
|
||||||
@@ -143,12 +138,6 @@ export const styles = css`
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.affine-attachment-embed-status {
|
|
||||||
position: absolute;
|
|
||||||
left: 14px;
|
|
||||||
bottom: 64px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.affine-attachment-embed-event-mask {
|
.affine-attachment-embed-event-mask {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
|
|||||||
@@ -22,13 +22,15 @@ import type { BlockModel } from '@blocksuite/store';
|
|||||||
import type { AttachmentBlockComponent } from './attachment-block';
|
import type { AttachmentBlockComponent } from './attachment-block';
|
||||||
|
|
||||||
export async function getAttachmentBlob(model: AttachmentBlockModel) {
|
export async function getAttachmentBlob(model: AttachmentBlockModel) {
|
||||||
const { sourceId$, type$ } = model.props;
|
const {
|
||||||
const sourceId = sourceId$.peek();
|
sourceId$: { value: sourceId },
|
||||||
const type = type$.peek();
|
type$: { value: type },
|
||||||
|
} = model.props;
|
||||||
if (!sourceId) return null;
|
if (!sourceId) return null;
|
||||||
|
|
||||||
const doc = model.store;
|
const doc = model.doc;
|
||||||
const blob = await doc.blobSync.get(sourceId);
|
let blob = await doc.blobSync.get(sourceId);
|
||||||
|
|
||||||
if (!blob) return null;
|
if (!blob) return null;
|
||||||
|
|
||||||
return new Blob([blob], { type });
|
return new Blob([blob], { type });
|
||||||
@@ -39,9 +41,9 @@ export async function getAttachmentBlob(model: AttachmentBlockModel) {
|
|||||||
* the download process may take a long time!
|
* the download process may take a long time!
|
||||||
*/
|
*/
|
||||||
export function downloadAttachmentBlob(block: AttachmentBlockComponent) {
|
export function downloadAttachmentBlob(block: AttachmentBlockComponent) {
|
||||||
const { host, model, blobUrl, resourceController } = block;
|
const { host, model, blobUrl, blobState$ } = block;
|
||||||
|
|
||||||
if (resourceController.state$.peek().downloading) {
|
if (blobState$.peek().downloading) {
|
||||||
toast(host, 'Download in progress...');
|
toast(host, 'Download in progress...');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -54,7 +56,7 @@ export function downloadAttachmentBlob(block: AttachmentBlockComponent) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceController.updateState({ downloading: true });
|
block.updateBlobState({ downloading: true });
|
||||||
|
|
||||||
toast(host, `Downloading ${shortName}`);
|
toast(host, `Downloading ${shortName}`);
|
||||||
|
|
||||||
@@ -65,14 +67,34 @@ export function downloadAttachmentBlob(block: AttachmentBlockComponent) {
|
|||||||
tmpLink.dispatchEvent(event);
|
tmpLink.dispatchEvent(event);
|
||||||
tmpLink.remove();
|
tmpLink.remove();
|
||||||
|
|
||||||
resourceController.updateState({ downloading: false });
|
block.updateBlobState({ downloading: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function refreshData(block: AttachmentBlockComponent) {
|
export async function refreshData(
|
||||||
|
std: BlockStdScope,
|
||||||
|
block: AttachmentBlockComponent
|
||||||
|
) {
|
||||||
const model = block.model;
|
const model = block.model;
|
||||||
|
const sourceId = model.props.sourceId$.peek();
|
||||||
|
if (!sourceId) return;
|
||||||
|
|
||||||
|
const blobUrl = block.blobUrl;
|
||||||
|
if (blobUrl) {
|
||||||
|
URL.revokeObjectURL(blobUrl);
|
||||||
|
block.blobUrl = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let blob = await std.store.blobSync.get(sourceId);
|
||||||
|
if (!blob) {
|
||||||
|
block.updateBlobState({ errorMessage: 'File not found' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const type = model.props.type$.peek();
|
const type = model.props.type$.peek();
|
||||||
|
|
||||||
await block.resourceController.refreshUrlWith(type);
|
blob = new Blob([blob], { type });
|
||||||
|
|
||||||
|
block.blobUrl = URL.createObjectURL(blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getFileType(file: File) {
|
export async function getFileType(file: File) {
|
||||||
@@ -82,7 +104,7 @@ export async function getFileType(file: File) {
|
|||||||
const buffer = await file.arrayBuffer();
|
const buffer = await file.arrayBuffer();
|
||||||
const FileType = await import('file-type');
|
const FileType = await import('file-type');
|
||||||
const fileType = await FileType.fileTypeFromBuffer(buffer);
|
const fileType = await FileType.fileTypeFromBuffer(buffer);
|
||||||
return fileType?.mime ?? '';
|
return fileType ? fileType.mime : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasExceeded(
|
function hasExceeded(
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu';
|
|||||||
import { BlockViewExtension, FlavourExtension } from '@blocksuite/std';
|
import { BlockViewExtension, FlavourExtension } from '@blocksuite/std';
|
||||||
import { literal } from 'lit/static-html.js';
|
import { literal } from 'lit/static-html.js';
|
||||||
|
|
||||||
import { AttachmentBlockInteraction } from './attachment-edgeless-block.js';
|
|
||||||
import { AttachmentDropOption } from './attachment-service.js';
|
import { AttachmentDropOption } from './attachment-service.js';
|
||||||
import { attachmentSlashMenuConfig } from './configs/slash-menu.js';
|
import { attachmentSlashMenuConfig } from './configs/slash-menu.js';
|
||||||
import { createBuiltinToolbarConfigExtension } from './configs/toolbar';
|
import { createBuiltinToolbarConfigExtension } from './configs/toolbar';
|
||||||
@@ -45,7 +44,6 @@ export class AttachmentViewExtension extends ViewExtensionProvider {
|
|||||||
]);
|
]);
|
||||||
if (this.isEdgeless(context.scope)) {
|
if (this.isEdgeless(context.scope)) {
|
||||||
context.register(EdgelessClipboardAttachmentConfig);
|
context.register(EdgelessClipboardAttachmentConfig);
|
||||||
context.register(AttachmentBlockInteraction);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
},
|
},
|
||||||
"include": ["./src"],
|
"include": ["./src"],
|
||||||
"references": [
|
"references": [
|
||||||
|
{ "path": "../embed" },
|
||||||
{ "path": "../surface" },
|
{ "path": "../surface" },
|
||||||
{ "path": "../../components" },
|
{ "path": "../../components" },
|
||||||
{ "path": "../../ext-loader" },
|
{ "path": "../../ext-loader" },
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@blocksuite/affine-block-embed": "workspace:*",
|
"@blocksuite/affine-block-embed": "workspace:*",
|
||||||
"@blocksuite/affine-block-embed-doc": "workspace:*",
|
|
||||||
"@blocksuite/affine-block-surface": "workspace:*",
|
"@blocksuite/affine-block-surface": "workspace:*",
|
||||||
"@blocksuite/affine-components": "workspace:*",
|
"@blocksuite/affine-components": "workspace:*",
|
||||||
"@blocksuite/affine-ext-loader": "workspace:*",
|
"@blocksuite/affine-ext-loader": "workspace:*",
|
||||||
@@ -24,7 +23,7 @@
|
|||||||
"@blocksuite/store": "workspace:*",
|
"@blocksuite/store": "workspace:*",
|
||||||
"@lit/context": "^1.1.2",
|
"@lit/context": "^1.1.2",
|
||||||
"@preact/signals-core": "^1.8.0",
|
"@preact/signals-core": "^1.8.0",
|
||||||
"@toeverything/theme": "^1.1.14",
|
"@toeverything/theme": "^1.1.12",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.0.1",
|
"minimatch": "^10.0.1",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
@@ -32,7 +31,7 @@
|
|||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vitest": "3.1.3"
|
"vitest": "3.1.1"
|
||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts",
|
".": "./src/index.ts",
|
||||||
|
|||||||
@@ -140,16 +140,4 @@ Some text in between
|
|||||||
`.trim();
|
`.trim();
|
||||||
expect(footnoteUrlPreprocessor(input)).toBe(expected);
|
expect(footnoteUrlPreprocessor(input)).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should encode partial encoded URLs in footnote definitions', () => {
|
|
||||||
const input = `
|
|
||||||
[^ref]: {"type":"url","url":"https://zh.wikipedia.org/zh-hans/%E5%B0%8F%E7%B1%B3SU7"}
|
|
||||||
[^ref2]: {"type":"url","url":"https://www.dw.com/zh/%E5%B0%8F%E7%B1%B3%E9%A6%96%E6%AC%BE%E6%B1%BD%E8%BD%A6%E5%8F%91%E5%B8%83-su7%E8%B5%B7%E4%BB%B72159%E4%B8%87%E5%85%83/a-68693432"}
|
|
||||||
`.trim();
|
|
||||||
const expected = `
|
|
||||||
[^ref]: {"type":"url","url":"https%3A%2F%2Fzh.wikipedia.org%2Fzh-hans%2F%25E5%25B0%258F%25E7%25B1%25B3SU7"}
|
|
||||||
[^ref2]: {"type":"url","url":"https%3A%2F%2Fwww.dw.com%2Fzh%2F%25E5%25B0%258F%25E7%25B1%25B3%25E9%25A6%2596%25E6%25AC%25BE%25E6%25B1%25BD%25E8%25BD%25A6%25E5%258F%2591%25E5%25B8%2583-su7%25E8%25B5%25B7%25E4%25BB%25B72159%25E4%25B8%2587%25E5%2585%2583%2Fa-68693432"}
|
|
||||||
`.trim();
|
|
||||||
expect(footnoteUrlPreprocessor(input)).toBe(expected);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,17 +23,12 @@ type FootnoteDefinition = {
|
|||||||
content: FootNoteReferenceParams;
|
content: FootNoteReferenceParams;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
// Check if a URL is already encoded with encodeURIComponent
|
||||||
* Check if a URL is already encoded with encodeURIComponent to avoid markdown link parsing
|
function isEncoded(uri: string): boolean {
|
||||||
* @example
|
|
||||||
* https://example.com/path%20with%20spaces should return false
|
|
||||||
* https://example.com/ should return false
|
|
||||||
* https%3A%2F%2Fexample.com%2F should return true
|
|
||||||
*/
|
|
||||||
function isFullyEncoded(uri: string): boolean {
|
|
||||||
try {
|
try {
|
||||||
// Should check if the components of the URI are fully encoded
|
// If decoding produces a different result than the original,
|
||||||
return uri === encodeURIComponent(decodeURIComponent(uri));
|
// then the URI contains encoded characters
|
||||||
|
return uri !== decodeURIComponent(uri);
|
||||||
} catch {
|
} catch {
|
||||||
// If decoding fails, the URI contains invalid percent-encoding
|
// If decoding fails, the URI contains invalid percent-encoding
|
||||||
return true;
|
return true;
|
||||||
@@ -199,10 +194,10 @@ class FootnoteParser {
|
|||||||
// Process URLs in footnote content
|
// Process URLs in footnote content
|
||||||
private processUrls(footnote: FootnoteDefinition): FootnoteDefinition {
|
private processUrls(footnote: FootnoteDefinition): FootnoteDefinition {
|
||||||
const content = footnote.content;
|
const content = footnote.content;
|
||||||
if (content.url && !isFullyEncoded(content.url)) {
|
if (content.url && !isEncoded(content.url)) {
|
||||||
content.url = encodeURIComponent(content.url);
|
content.url = encodeURIComponent(content.url);
|
||||||
}
|
}
|
||||||
if (content.favicon && !isFullyEncoded(content.favicon)) {
|
if (content.favicon && !isEncoded(content.favicon)) {
|
||||||
content.favicon = encodeURIComponent(content.favicon);
|
content.favicon = encodeURIComponent(content.favicon);
|
||||||
}
|
}
|
||||||
return footnote;
|
return footnote;
|
||||||
|
|||||||
@@ -2,17 +2,10 @@ import {
|
|||||||
CaptionedBlockComponent,
|
CaptionedBlockComponent,
|
||||||
SelectedStyle,
|
SelectedStyle,
|
||||||
} from '@blocksuite/affine-components/caption';
|
} from '@blocksuite/affine-components/caption';
|
||||||
import type {
|
import type { BookmarkBlockModel } from '@blocksuite/affine-model';
|
||||||
BookmarkBlockModel,
|
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||||
LinkPreviewData,
|
|
||||||
} from '@blocksuite/affine-model';
|
|
||||||
import { ImageProxyService } from '@blocksuite/affine-shared/adapters';
|
|
||||||
import {
|
|
||||||
DocModeProvider,
|
|
||||||
LinkPreviewServiceIdentifier,
|
|
||||||
} from '@blocksuite/affine-shared/services';
|
|
||||||
import { BlockSelection } from '@blocksuite/std';
|
import { BlockSelection } from '@blocksuite/std';
|
||||||
import { computed, type ReadonlySignal, signal } from '@preact/signals-core';
|
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||||
import { html } from 'lit';
|
import { html } from 'lit';
|
||||||
import { property, query } from 'lit/decorators.js';
|
import { property, query } from 'lit/decorators.js';
|
||||||
import { type ClassInfo, classMap } from 'lit/directives/class-map.js';
|
import { type ClassInfo, classMap } from 'lit/directives/class-map.js';
|
||||||
@@ -35,62 +28,6 @@ export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBloc
|
|||||||
|
|
||||||
protected containerStyleMap!: ReturnType<typeof styleMap>;
|
protected containerStyleMap!: ReturnType<typeof styleMap>;
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Local link preview data
|
|
||||||
* When the doc is in readonly mode, and the link preview data are not provided (stored in the block model),
|
|
||||||
* We will use the local link preview data fetched from the link previewer service to render the block.
|
|
||||||
*/
|
|
||||||
private readonly _localLinkPreview$ = signal<LinkPreviewData>({
|
|
||||||
icon: null,
|
|
||||||
title: null,
|
|
||||||
description: null,
|
|
||||||
image: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Link preview data for actual rendering
|
|
||||||
* When the doc is not in readonly mode, and the link preview data are provided (stored in the block model),
|
|
||||||
* We will use the model props to render the block.
|
|
||||||
* Otherwise, we will use the local link preview data to render the block.
|
|
||||||
*/
|
|
||||||
linkPreview$ = computed(() => {
|
|
||||||
const modelProps = this.model.props;
|
|
||||||
const local = this._localLinkPreview$.value;
|
|
||||||
return {
|
|
||||||
icon: modelProps.icon$.value ?? local.icon ?? null,
|
|
||||||
title: modelProps.title$.value ?? local.title ?? null,
|
|
||||||
description: modelProps.description$.value ?? local.description ?? null,
|
|
||||||
image: modelProps.image$.value ?? local.image ?? null,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
private readonly _updateLocalLinkPreview = () => {
|
|
||||||
// cancel any inflight request
|
|
||||||
this._fetchAbortController?.abort();
|
|
||||||
this._fetchAbortController = new AbortController();
|
|
||||||
|
|
||||||
this.loading = true;
|
|
||||||
this.error = false;
|
|
||||||
|
|
||||||
this.std
|
|
||||||
.get(LinkPreviewServiceIdentifier)
|
|
||||||
.query(this.model.props.url, this._fetchAbortController.signal)
|
|
||||||
.then(data => {
|
|
||||||
this._localLinkPreview$.value = {
|
|
||||||
icon: data.icon ?? null,
|
|
||||||
title: data.title ?? null,
|
|
||||||
description: data.description ?? null,
|
|
||||||
image: data.image ?? null,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.error = true;
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
selectBlock = () => {
|
selectBlock = () => {
|
||||||
const selectionManager = this.std.selection;
|
const selectionManager = this.std.selection;
|
||||||
const blockSelection = selectionManager.create(BlockSelection, {
|
const blockSelection = selectionManager.create(BlockSelection, {
|
||||||
@@ -120,38 +57,17 @@ export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBloc
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get imageProxyService() {
|
|
||||||
return this.std.get(ImageProxyService);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClick = (event: MouseEvent) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
|
|
||||||
if (
|
|
||||||
this.model.parent?.flavour !== 'affine:surface' &&
|
|
||||||
!this.store.readonly
|
|
||||||
) {
|
|
||||||
this.selectBlock();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleDoubleClick = (event: MouseEvent) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
this.open();
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly _renderCitationView = () => {
|
private readonly _renderCitationView = () => {
|
||||||
const { url, footnoteIdentifier } = this.model.props;
|
const { title, description, url, icon, footnoteIdentifier } =
|
||||||
const { icon, title, description } = this.linkPreview$.value;
|
this.model.props;
|
||||||
const iconSrc = icon ? this.imageProxyService.buildUrl(icon) : undefined;
|
|
||||||
return html`
|
return html`
|
||||||
<affine-citation-card
|
<affine-citation-card
|
||||||
.icon=${iconSrc}
|
.icon=${icon}
|
||||||
.citationTitle=${title || url}
|
.citationTitle=${title || url}
|
||||||
.citationContent=${description}
|
.citationContent=${description}
|
||||||
.citationIdentifier=${footnoteIdentifier}
|
.citationIdentifier=${footnoteIdentifier}
|
||||||
.onClickCallback=${this.handleClick}
|
.onClickCallback=${this.selectBlock}
|
||||||
.onDoubleClickCallback=${this.handleDoubleClick}
|
.onDoubleClickCallback=${this.open}
|
||||||
.active=${this.selected$.value}
|
.active=${this.selected$.value}
|
||||||
></affine-citation-card>
|
></affine-citation-card>
|
||||||
`;
|
`;
|
||||||
@@ -181,17 +97,10 @@ export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBloc
|
|||||||
|
|
||||||
this.contentEditable = 'false';
|
this.contentEditable = 'false';
|
||||||
|
|
||||||
if (
|
if (!this.model.props.description && !this.model.props.title) {
|
||||||
(!this.model.props.description && !this.model.props.title) ||
|
if (this.doc.readonly) {
|
||||||
(!this.model.props.image && this.model.props.style === 'vertical')
|
|
||||||
) {
|
|
||||||
// When the doc is readonly, and the preview data not provided
|
|
||||||
// We should fetch the preview data and update the local link preview data
|
|
||||||
if (this.store.readonly) {
|
|
||||||
this._updateLocalLinkPreview();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Otherwise, we should refresh the data to the model props
|
|
||||||
this.refreshData();
|
this.refreshData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { BookmarkBlockSchema } from '@blocksuite/affine-model';
|
|
||||||
import {
|
import {
|
||||||
EMBED_CARD_HEIGHT,
|
EMBED_CARD_HEIGHT,
|
||||||
EMBED_CARD_WIDTH,
|
EMBED_CARD_WIDTH,
|
||||||
} from '@blocksuite/affine-shared/consts';
|
} from '@blocksuite/affine-shared/consts';
|
||||||
import { toGfxBlockComponent } from '@blocksuite/std';
|
import { toGfxBlockComponent } from '@blocksuite/std';
|
||||||
import { GfxViewInteractionExtension } from '@blocksuite/std/gfx';
|
|
||||||
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
|
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
|
||||||
|
|
||||||
import { BookmarkBlockComponent } from './bookmark-block.js';
|
import { BookmarkBlockComponent } from './bookmark-block.js';
|
||||||
@@ -52,24 +50,6 @@ export class BookmarkEdgelessBlockComponent extends toGfxBlockComponent(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BookmarkBlockInteraction = GfxViewInteractionExtension(
|
|
||||||
BookmarkBlockSchema.model.flavour,
|
|
||||||
{
|
|
||||||
resizeConstraint: {
|
|
||||||
lockRatio: true,
|
|
||||||
},
|
|
||||||
handleRotate: () => {
|
|
||||||
return {
|
|
||||||
beforeRotate(context) {
|
|
||||||
context.set({
|
|
||||||
rotatable: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
'affine-edgeless-bookmark': BookmarkEdgelessBlockComponent;
|
'affine-edgeless-bookmark': BookmarkEdgelessBlockComponent;
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { BookmarkBlockSchema } from '@blocksuite/affine-model';
|
||||||
|
import { BlockViewExtension, FlavourExtension } from '@blocksuite/std';
|
||||||
|
import type { ExtensionType } from '@blocksuite/store';
|
||||||
|
import { literal } from 'lit/static-html.js';
|
||||||
|
|
||||||
|
import { BookmarkBlockAdapterExtensions } from './adapters/extension';
|
||||||
|
import { BookmarkSlashMenuConfigExtension } from './configs/slash-menu';
|
||||||
|
import { createBuiltinToolbarConfigExtension } from './configs/toolbar';
|
||||||
|
|
||||||
|
const flavour = BookmarkBlockSchema.model.flavour;
|
||||||
|
|
||||||
|
export const BookmarkBlockSpec: ExtensionType[] = [
|
||||||
|
FlavourExtension(flavour),
|
||||||
|
BlockViewExtension(flavour, model => {
|
||||||
|
return model.parent?.flavour === 'affine:surface'
|
||||||
|
? literal`affine-edgeless-bookmark`
|
||||||
|
: literal`affine-bookmark`;
|
||||||
|
}),
|
||||||
|
BookmarkBlockAdapterExtensions,
|
||||||
|
createBuiltinToolbarConfigExtension(flavour),
|
||||||
|
BookmarkSlashMenuConfigExtension,
|
||||||
|
].flat();
|
||||||
@@ -1,15 +1,10 @@
|
|||||||
import { insertEmbedIframeWithUrlCommand } from '@blocksuite/affine-block-embed';
|
|
||||||
import {
|
import {
|
||||||
type InsertedLinkType,
|
type InsertedLinkType,
|
||||||
|
insertEmbedIframeWithUrlCommand,
|
||||||
insertEmbedLinkedDocCommand,
|
insertEmbedLinkedDocCommand,
|
||||||
insertEmbedSyncedDocCommand,
|
|
||||||
type LinkableFlavour,
|
type LinkableFlavour,
|
||||||
} from '@blocksuite/affine-block-embed-doc';
|
} from '@blocksuite/affine-block-embed';
|
||||||
import {
|
import { QuickSearchProvider } from '@blocksuite/affine-shared/services';
|
||||||
DocModeProvider,
|
|
||||||
EditorSettingProvider,
|
|
||||||
QuickSearchProvider,
|
|
||||||
} from '@blocksuite/affine-shared/services';
|
|
||||||
import type { Command } from '@blocksuite/std';
|
import type { Command } from '@blocksuite/std';
|
||||||
|
|
||||||
import { insertBookmarkCommand } from './insert-bookmark';
|
import { insertBookmarkCommand } from './insert-bookmark';
|
||||||
@@ -31,26 +26,12 @@ export const insertLinkByQuickSearchCommand: Command<
|
|||||||
|
|
||||||
// add linked doc
|
// add linked doc
|
||||||
if ('docId' in result) {
|
if ('docId' in result) {
|
||||||
const editorMode = std.get(DocModeProvider).getEditorMode();
|
std.command.exec(insertEmbedLinkedDocCommand, {
|
||||||
const editorSettings = std.get(EditorSettingProvider);
|
|
||||||
let flavour: LinkableFlavour = 'affine:embed-linked-doc';
|
|
||||||
if (editorMode === 'edgeless') {
|
|
||||||
flavour =
|
|
||||||
editorSettings.setting$.value.docCanvasPreferView ?? flavour;
|
|
||||||
}
|
|
||||||
|
|
||||||
const insertCommand =
|
|
||||||
flavour === 'affine:embed-linked-doc'
|
|
||||||
? insertEmbedLinkedDocCommand
|
|
||||||
: insertEmbedSyncedDocCommand;
|
|
||||||
|
|
||||||
std.command.exec(insertCommand, {
|
|
||||||
docId: result.docId,
|
docId: result.docId,
|
||||||
params: result.params,
|
params: result.params,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
flavour,
|
flavour: 'affine:embed-linked-doc',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,20 @@ export class BookmarkCard extends SignalWatcher(
|
|||||||
) {
|
) {
|
||||||
static override styles = styles;
|
static override styles = styles;
|
||||||
|
|
||||||
|
private _handleClick(event: MouseEvent) {
|
||||||
|
event.stopPropagation();
|
||||||
|
const model = this.bookmark.model;
|
||||||
|
|
||||||
|
if (model.parent?.flavour !== 'affine:surface') {
|
||||||
|
this.bookmark.selectBlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleDoubleClick(event: MouseEvent) {
|
||||||
|
event.stopPropagation();
|
||||||
|
this.bookmark.open();
|
||||||
|
}
|
||||||
|
|
||||||
override connectedCallback(): void {
|
override connectedCallback(): void {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
||||||
@@ -35,9 +49,8 @@ export class BookmarkCard extends SignalWatcher(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override render() {
|
override render() {
|
||||||
const { url, style } = this.bookmark.model.props;
|
const { icon, title, url, description, image, style } =
|
||||||
const { icon, title, description, image } =
|
this.bookmark.model.props;
|
||||||
this.bookmark.linkPreview$.value;
|
|
||||||
|
|
||||||
const cardClassMap = classMap({
|
const cardClassMap = classMap({
|
||||||
loading: this.loading,
|
loading: this.loading,
|
||||||
@@ -61,7 +74,7 @@ export class BookmarkCard extends SignalWatcher(
|
|||||||
|
|
||||||
const theme = this.bookmark.std.get(ThemeProvider).theme;
|
const theme = this.bookmark.std.get(ThemeProvider).theme;
|
||||||
const { LoadingIcon, EmbedCardBannerIcon } = getEmbedCardIcons(theme);
|
const { LoadingIcon, EmbedCardBannerIcon } = getEmbedCardIcons(theme);
|
||||||
const imageProxyService = this.bookmark.store.get(ImageProxyService);
|
const imageProxyService = this.bookmark.doc.get(ImageProxyService);
|
||||||
|
|
||||||
const titleIcon = this.loading
|
const titleIcon = this.loading
|
||||||
? LoadingIcon
|
? LoadingIcon
|
||||||
@@ -85,8 +98,8 @@ export class BookmarkCard extends SignalWatcher(
|
|||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
class="affine-bookmark-card ${cardClassMap}"
|
class="affine-bookmark-card ${cardClassMap}"
|
||||||
@click=${this.bookmark.handleClick}
|
@click=${this._handleClick}
|
||||||
@dblclick=${this.bookmark.handleDoubleClick}
|
@dblclick=${this._handleDoubleClick}
|
||||||
>
|
>
|
||||||
<div class="affine-bookmark-content">
|
<div class="affine-bookmark-content">
|
||||||
<div class="affine-bookmark-content-title">
|
<div class="affine-bookmark-content-title">
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { DefaultTool } from '@blocksuite/affine-block-surface';
|
|
||||||
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
|
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
|
||||||
import { BookmarkBlockSchema } from '@blocksuite/affine-model';
|
import { BookmarkBlockSchema } from '@blocksuite/affine-model';
|
||||||
import {
|
import {
|
||||||
@@ -6,7 +5,6 @@ import {
|
|||||||
SlashMenuConfigIdentifier,
|
SlashMenuConfigIdentifier,
|
||||||
} from '@blocksuite/affine-widget-slash-menu';
|
} from '@blocksuite/affine-widget-slash-menu';
|
||||||
import { LinkIcon } from '@blocksuite/icons/lit';
|
import { LinkIcon } from '@blocksuite/icons/lit';
|
||||||
import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
|
|
||||||
import type { ExtensionType } from '@blocksuite/store';
|
import type { ExtensionType } from '@blocksuite/store';
|
||||||
|
|
||||||
import { LinkTooltip } from './tooltips';
|
import { LinkTooltip } from './tooltips';
|
||||||
@@ -23,10 +21,10 @@ const bookmarkSlashMenuConfig: SlashMenuConfig = {
|
|||||||
},
|
},
|
||||||
group: '4_Content & Media@2',
|
group: '4_Content & Media@2',
|
||||||
when: ({ model }) =>
|
when: ({ model }) =>
|
||||||
model.store.schema.flavourSchemaMap.has('affine:bookmark'),
|
model.doc.schema.flavourSchemaMap.has('affine:bookmark'),
|
||||||
action: ({ std, model }) => {
|
action: ({ std, model }) => {
|
||||||
const { host } = std;
|
const { host } = std;
|
||||||
const parentModel = host.store.getParent(model);
|
const parentModel = host.doc.getParent(model);
|
||||||
if (!parentModel) {
|
if (!parentModel) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -35,17 +33,11 @@ const bookmarkSlashMenuConfig: SlashMenuConfig = {
|
|||||||
host,
|
host,
|
||||||
'Links',
|
'Links',
|
||||||
'The added link will be displayed as a card view.',
|
'The added link will be displayed as a card view.',
|
||||||
{ mode: 'page', parentModel, index },
|
{ mode: 'page', parentModel, index }
|
||||||
({ mode }) => {
|
|
||||||
if (mode === 'edgeless') {
|
|
||||||
const gfx = std.get(GfxControllerIdentifier);
|
|
||||||
gfx.tool.setTool(DefaultTool);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (model.text?.length === 0) {
|
if (model.text?.length === 0) {
|
||||||
model.store.deleteBlock(model);
|
model.doc.deleteBlock(model);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export * from './adapters';
|
export * from './adapters';
|
||||||
export * from './bookmark-block';
|
export * from './bookmark-block';
|
||||||
|
export * from './bookmark-spec';
|
||||||
export * from './commands';
|
export * from './commands';
|
||||||
export * from './components';
|
export * from './components';
|
||||||
export { BookmarkSlashMenuConfigIdentifier } from './configs/slash-menu';
|
export { BookmarkSlashMenuConfigIdentifier } from './configs/slash-menu';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { LinkPreviewServiceIdentifier } from '@blocksuite/affine-shared/services';
|
import { LinkPreviewerService } from '@blocksuite/affine-shared/services';
|
||||||
import { isAbortError } from '@blocksuite/affine-shared/utils';
|
import { isAbortError } from '@blocksuite/affine-shared/utils';
|
||||||
|
|
||||||
import type { BookmarkBlockComponent } from './bookmark-block.js';
|
import type { BookmarkBlockComponent } from './bookmark-block.js';
|
||||||
@@ -15,7 +15,7 @@ export async function refreshBookmarkUrlData(
|
|||||||
try {
|
try {
|
||||||
bookmarkElement.loading = true;
|
bookmarkElement.loading = true;
|
||||||
|
|
||||||
const linkPreviewer = bookmarkElement.std.get(LinkPreviewServiceIdentifier);
|
const linkPreviewer = bookmarkElement.doc.get(LinkPreviewerService);
|
||||||
const bookmarkUrlData = await linkPreviewer.query(
|
const bookmarkUrlData = await linkPreviewer.query(
|
||||||
bookmarkElement.model.props.url,
|
bookmarkElement.model.props.url,
|
||||||
signal
|
signal
|
||||||
@@ -32,7 +32,7 @@ export async function refreshBookmarkUrlData(
|
|||||||
|
|
||||||
if (signal?.aborted) return;
|
if (signal?.aborted) return;
|
||||||
|
|
||||||
bookmarkElement.store.updateBlock(bookmarkElement.model, {
|
bookmarkElement.doc.updateBlock(bookmarkElement.model, {
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
icon,
|
icon,
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { BookmarkBlockSchema } from '@blocksuite/affine-model';
|
|||||||
import { BlockViewExtension, FlavourExtension } from '@blocksuite/std';
|
import { BlockViewExtension, FlavourExtension } from '@blocksuite/std';
|
||||||
import { literal } from 'lit/static-html.js';
|
import { literal } from 'lit/static-html.js';
|
||||||
|
|
||||||
import { BookmarkBlockInteraction } from './bookmark-edgeless-block';
|
|
||||||
import { BookmarkSlashMenuConfigExtension } from './configs/slash-menu';
|
import { BookmarkSlashMenuConfigExtension } from './configs/slash-menu';
|
||||||
import { createBuiltinToolbarConfigExtension } from './configs/toolbar';
|
import { createBuiltinToolbarConfigExtension } from './configs/toolbar';
|
||||||
import { EdgelessClipboardBookmarkConfig } from './edgeless-clipboard-config';
|
import { EdgelessClipboardBookmarkConfig } from './edgeless-clipboard-config';
|
||||||
@@ -37,7 +36,6 @@ export class BookmarkViewExtension extends ViewExtensionProvider {
|
|||||||
const isEdgeless = this.isEdgeless(context.scope);
|
const isEdgeless = this.isEdgeless(context.scope);
|
||||||
if (isEdgeless) {
|
if (isEdgeless) {
|
||||||
context.register(EdgelessClipboardBookmarkConfig);
|
context.register(EdgelessClipboardBookmarkConfig);
|
||||||
context.register(BookmarkBlockInteraction);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
"include": ["./src"],
|
"include": ["./src"],
|
||||||
"references": [
|
"references": [
|
||||||
{ "path": "../embed" },
|
{ "path": "../embed" },
|
||||||
{ "path": "../embed-doc" },
|
|
||||||
{ "path": "../surface" },
|
{ "path": "../surface" },
|
||||||
{ "path": "../../components" },
|
{ "path": "../../components" },
|
||||||
{ "path": "../../ext-loader" },
|
{ "path": "../../ext-loader" },
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
"@floating-ui/dom": "^1.6.10",
|
"@floating-ui/dom": "^1.6.10",
|
||||||
"@lit/context": "^1.1.2",
|
"@lit/context": "^1.1.2",
|
||||||
"@preact/signals-core": "^1.8.0",
|
"@preact/signals-core": "^1.8.0",
|
||||||
"@toeverything/theme": "^1.1.14",
|
"@toeverything/theme": "^1.1.12",
|
||||||
"@types/mdast": "^4.0.4",
|
"@types/mdast": "^4.0.4",
|
||||||
"emoji-mart": "^5.6.0",
|
"emoji-mart": "^5.6.0",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
|
|||||||
@@ -1,207 +0,0 @@
|
|||||||
import { CalloutBlockSchema } from '@blocksuite/affine-model';
|
|
||||||
import {
|
|
||||||
BlockMarkdownAdapterExtension,
|
|
||||||
type BlockMarkdownAdapterMatcher,
|
|
||||||
CALLOUT_MARKDOWN_EXPORT_OPTIONS_KEY,
|
|
||||||
type CalloutAdmonitionType,
|
|
||||||
CalloutAdmonitionTypeSet,
|
|
||||||
CalloutExportStyle,
|
|
||||||
type CalloutMarkdownExportOptions,
|
|
||||||
calloutMarkdownExportOptionsSchema,
|
|
||||||
DEFAULT_ADMONITION_TYPE,
|
|
||||||
getCalloutEmoji,
|
|
||||||
isCalloutNode,
|
|
||||||
} from '@blocksuite/affine-shared/adapters';
|
|
||||||
import { type DeltaInsert, nanoid } from '@blocksuite/store';
|
|
||||||
|
|
||||||
// Currently, the callout block children can only be paragraph block or list block
|
|
||||||
// In mdast, the node types are `paragraph`, `list`, `heading`, `blockquote`
|
|
||||||
const CALLOUT_BLOCK_CHILDREN_TYPES = new Set([
|
|
||||||
'paragraph',
|
|
||||||
'list',
|
|
||||||
'heading',
|
|
||||||
'blockquote',
|
|
||||||
]);
|
|
||||||
|
|
||||||
const ADMONITION_SYMBOL = ':::';
|
|
||||||
const DEFAULT_OPTIONS: CalloutMarkdownExportOptions = {
|
|
||||||
style: CalloutExportStyle.GFM,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the callout export options from the configs
|
|
||||||
* @param configs - The configs of the callout block
|
|
||||||
* @returns The callout export options
|
|
||||||
*/
|
|
||||||
function getCalloutExportOptions(
|
|
||||||
configs: Map<string, unknown>
|
|
||||||
): CalloutMarkdownExportOptions {
|
|
||||||
let exportOptions: CalloutMarkdownExportOptions = DEFAULT_OPTIONS;
|
|
||||||
try {
|
|
||||||
const options = configs.get(CALLOUT_MARKDOWN_EXPORT_OPTIONS_KEY);
|
|
||||||
if (options) {
|
|
||||||
exportOptions = calloutMarkdownExportOptionsSchema.parse(options);
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
return exportOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const calloutBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher = {
|
|
||||||
flavour: CalloutBlockSchema.model.flavour,
|
|
||||||
toMatch: o => isCalloutNode(o.node),
|
|
||||||
fromMatch: o => o.node.flavour === CalloutBlockSchema.model.flavour,
|
|
||||||
toBlockSnapshot: {
|
|
||||||
enter: (o, context) => {
|
|
||||||
if (!o.node.data || !isCalloutNode(o.node)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Currently, the callout block children can only be a paragraph or a list
|
|
||||||
// So we should filter out the other children
|
|
||||||
o.node.children = o.node.children.filter(child =>
|
|
||||||
CALLOUT_BLOCK_CHILDREN_TYPES.has(child.type)
|
|
||||||
);
|
|
||||||
|
|
||||||
const { walkerContext } = context;
|
|
||||||
const calloutEmoji = getCalloutEmoji(o.node);
|
|
||||||
walkerContext.openNode(
|
|
||||||
{
|
|
||||||
type: 'block',
|
|
||||||
id: nanoid(),
|
|
||||||
flavour: CalloutBlockSchema.model.flavour,
|
|
||||||
props: {
|
|
||||||
emoji: calloutEmoji,
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
'children'
|
|
||||||
);
|
|
||||||
},
|
|
||||||
leave: (o, context) => {
|
|
||||||
const { walkerContext } = context;
|
|
||||||
if (isCalloutNode(o.node)) {
|
|
||||||
walkerContext.closeNode();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fromBlockSnapshot: {
|
|
||||||
enter: (o, context) => {
|
|
||||||
const emoji = o.node.props.emoji as string;
|
|
||||||
const { walkerContext, configs } = context;
|
|
||||||
|
|
||||||
const exportOptions = getCalloutExportOptions(configs);
|
|
||||||
const { style, admonitionType } = exportOptions;
|
|
||||||
// If the style is admonitions, we should handle the first child
|
|
||||||
if (style === CalloutExportStyle.Admonitions) {
|
|
||||||
let type = admonitionType ?? DEFAULT_ADMONITION_TYPE;
|
|
||||||
let customTitle = '';
|
|
||||||
let restOfText = '';
|
|
||||||
|
|
||||||
const firstChild = o.node.children[0];
|
|
||||||
const isTextNode = !!firstChild.props.text;
|
|
||||||
// If the first child is a text block, we should get the type and custom title from the first line of the text
|
|
||||||
// And remove the first child from the children
|
|
||||||
// Otherwise, we should use the default admonition type as the type
|
|
||||||
if (isTextNode) {
|
|
||||||
const textDelta = (firstChild.props.text ?? { delta: [] }) as {
|
|
||||||
delta: DeltaInsert[];
|
|
||||||
};
|
|
||||||
// Get the text of the first child
|
|
||||||
const text = textDelta.delta.reduce((acc, delta) => {
|
|
||||||
if (delta.insert) {
|
|
||||||
acc += delta.insert;
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, '');
|
|
||||||
|
|
||||||
// If the text is not empty, we should try to get type and custom title from the text
|
|
||||||
if (text) {
|
|
||||||
// Get the first line of the text
|
|
||||||
const firstLine = text.includes('\n') ? text.split('\n')[0] : text;
|
|
||||||
// Get the rest of the text besides the first line
|
|
||||||
restOfText = text.split('\n').slice(1).join('\n');
|
|
||||||
// Get the possible type from the first line
|
|
||||||
const possibleType = firstLine.split(' ')[0].toLowerCase();
|
|
||||||
// If the type is a valid admonition type, we should use it as the type
|
|
||||||
if (CalloutAdmonitionTypeSet.has(possibleType)) {
|
|
||||||
type = possibleType as CalloutAdmonitionType;
|
|
||||||
// Get the custom title from the first line
|
|
||||||
customTitle = firstLine.split(' ').slice(1).join(' ').trim();
|
|
||||||
// Remove the first child from the children
|
|
||||||
o.node.children = o.node.children.slice(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an admonition symbol paragraph to the start of the children
|
|
||||||
const admonitionSymbol =
|
|
||||||
`${ADMONITION_SYMBOL} ${type} ${customTitle}`.trim();
|
|
||||||
walkerContext
|
|
||||||
.openNode({
|
|
||||||
type: 'paragraph',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
value: admonitionSymbol,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
.closeNode();
|
|
||||||
|
|
||||||
// Add the rest of the text to the children content
|
|
||||||
if (restOfText) {
|
|
||||||
walkerContext
|
|
||||||
.openNode({
|
|
||||||
type: 'paragraph',
|
|
||||||
children: [{ type: 'text', value: `${restOfText}` }],
|
|
||||||
})
|
|
||||||
.closeNode();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
walkerContext
|
|
||||||
.openNode(
|
|
||||||
{
|
|
||||||
type: 'blockquote',
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
'children'
|
|
||||||
)
|
|
||||||
.openNode({
|
|
||||||
type: 'paragraph',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
value: `[!${emoji}]`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
.closeNode();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
leave: (_, context) => {
|
|
||||||
const { walkerContext, configs } = context;
|
|
||||||
const exportOptions = getCalloutExportOptions(configs);
|
|
||||||
const { style } = exportOptions;
|
|
||||||
// If the style is admonitions, we should add an admonition symbol paragraph to the end of the children
|
|
||||||
if (style === CalloutExportStyle.Admonitions) {
|
|
||||||
walkerContext
|
|
||||||
.openNode({
|
|
||||||
type: 'paragraph',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
value: ADMONITION_SYMBOL,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
.closeNode();
|
|
||||||
} else {
|
|
||||||
// If the style is gfm, we should close the outer blockquote node
|
|
||||||
walkerContext.closeNode();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const CalloutBlockMarkdownAdapterExtension =
|
|
||||||
BlockMarkdownAdapterExtension(calloutBlockMarkdownAdapterMatcher);
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu';
|
||||||
|
import { BlockViewExtension, FlavourExtension } from '@blocksuite/std';
|
||||||
|
import type { ExtensionType } from '@blocksuite/store';
|
||||||
|
import { literal } from 'lit/static-html.js';
|
||||||
|
|
||||||
|
import { CalloutKeymapExtension } from './callout-keymap';
|
||||||
|
import { calloutSlashMenuConfig } from './configs/slash-menu';
|
||||||
|
|
||||||
|
export const CalloutBlockSpec: ExtensionType[] = [
|
||||||
|
FlavourExtension('affine:callout'),
|
||||||
|
BlockViewExtension('affine:callout', literal`affine-callout`),
|
||||||
|
CalloutKeymapExtension,
|
||||||
|
SlashMenuConfigExtension('affine:callout', calloutSlashMenuConfig),
|
||||||
|
];
|
||||||
@@ -33,24 +33,19 @@ export const calloutSlashMenuConfig: SlashMenuConfig = {
|
|||||||
when: ({ std, model }) => {
|
when: ({ std, model }) => {
|
||||||
return (
|
return (
|
||||||
std.get(FeatureFlagService).getFlag('enable_callout') &&
|
std.get(FeatureFlagService).getFlag('enable_callout') &&
|
||||||
!isInsideBlockByFlavour(model.store, model, 'affine:edgeless-text')
|
!isInsideBlockByFlavour(model.doc, model, 'affine:edgeless-text')
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
action: ({ model, std }) => {
|
action: ({ model, std }) => {
|
||||||
const { store } = model;
|
const { doc } = model;
|
||||||
const parent = store.getParent(model);
|
const parent = doc.getParent(model);
|
||||||
if (!parent) return;
|
if (!parent) return;
|
||||||
|
|
||||||
const index = parent.children.indexOf(model);
|
const index = parent.children.indexOf(model);
|
||||||
if (index === -1) return;
|
if (index === -1) return;
|
||||||
const calloutId = store.addBlock(
|
const calloutId = doc.addBlock('affine:callout', {}, parent, index + 1);
|
||||||
'affine:callout',
|
|
||||||
{},
|
|
||||||
parent,
|
|
||||||
index + 1
|
|
||||||
);
|
|
||||||
if (!calloutId) return;
|
if (!calloutId) return;
|
||||||
const paragraphId = store.addBlock('affine:paragraph', {}, calloutId);
|
const paragraphId = doc.addBlock('affine:paragraph', {}, calloutId);
|
||||||
if (!paragraphId) return;
|
if (!paragraphId) return;
|
||||||
std.host.updateComplete
|
std.host.updateComplete
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from './callout-block.js';
|
export * from './callout-block.js';
|
||||||
|
export * from './callout-spec.js';
|
||||||
export * from './effects.js';
|
export * from './effects.js';
|
||||||
|
|||||||
@@ -4,14 +4,11 @@ import {
|
|||||||
} from '@blocksuite/affine-ext-loader';
|
} from '@blocksuite/affine-ext-loader';
|
||||||
import { CalloutBlockSchemaExtension } from '@blocksuite/affine-model';
|
import { CalloutBlockSchemaExtension } from '@blocksuite/affine-model';
|
||||||
|
|
||||||
import { CalloutBlockMarkdownAdapterExtension } from './adapters/markdown';
|
|
||||||
|
|
||||||
export class CalloutStoreExtension extends StoreExtensionProvider {
|
export class CalloutStoreExtension extends StoreExtensionProvider {
|
||||||
override name = 'affine-callout-block';
|
override name = 'affine-callout-block';
|
||||||
|
|
||||||
override setup(context: StoreExtensionContext) {
|
override setup(context: StoreExtensionContext) {
|
||||||
super.setup(context);
|
super.setup(context);
|
||||||
context.register(CalloutBlockSchemaExtension);
|
context.register(CalloutBlockSchemaExtension);
|
||||||
context.register(CalloutBlockMarkdownAdapterExtension);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
"@floating-ui/dom": "^1.6.13",
|
"@floating-ui/dom": "^1.6.13",
|
||||||
"@lit/context": "^1.1.2",
|
"@lit/context": "^1.1.2",
|
||||||
"@preact/signals-core": "^1.8.0",
|
"@preact/signals-core": "^1.8.0",
|
||||||
"@toeverything/theme": "^1.1.14",
|
"@toeverything/theme": "^1.1.12",
|
||||||
"@types/mdast": "^4.0.4",
|
"@types/mdast": "^4.0.4",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.0.1",
|
"minimatch": "^10.0.1",
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu';
|
||||||
|
import {
|
||||||
|
BlockViewExtension,
|
||||||
|
FlavourExtension,
|
||||||
|
WidgetViewExtension,
|
||||||
|
} from '@blocksuite/std';
|
||||||
|
import type { ExtensionType } from '@blocksuite/store';
|
||||||
|
import { literal, unsafeStatic } from 'lit/static-html.js';
|
||||||
|
|
||||||
|
import { CodeBlockAdapterExtensions } from './adapters/extension.js';
|
||||||
|
import { getCodeClipboardExtensions } from './clipboard/index.js';
|
||||||
|
import {
|
||||||
|
CodeBlockInlineManagerExtension,
|
||||||
|
CodeBlockUnitSpecExtension,
|
||||||
|
} from './code-block-inline.js';
|
||||||
|
import { CodeBlockHighlighter } from './code-block-service.js';
|
||||||
|
import { CodeKeymapExtension } from './code-keymap.js';
|
||||||
|
import { AFFINE_CODE_TOOLBAR_WIDGET } from './code-toolbar/index.js';
|
||||||
|
import { codeSlashMenuConfig } from './configs/slash-menu.js';
|
||||||
|
|
||||||
|
export const codeToolbarWidget = WidgetViewExtension(
|
||||||
|
'affine:code',
|
||||||
|
AFFINE_CODE_TOOLBAR_WIDGET,
|
||||||
|
literal`${unsafeStatic(AFFINE_CODE_TOOLBAR_WIDGET)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
export const CodeBlockSpec: ExtensionType[] = [
|
||||||
|
FlavourExtension('affine:code'),
|
||||||
|
CodeBlockHighlighter,
|
||||||
|
BlockViewExtension('affine:code', literal`affine-code`),
|
||||||
|
codeToolbarWidget,
|
||||||
|
CodeBlockInlineManagerExtension,
|
||||||
|
CodeBlockUnitSpecExtension,
|
||||||
|
CodeBlockAdapterExtensions,
|
||||||
|
SlashMenuConfigExtension('affine:code', codeSlashMenuConfig),
|
||||||
|
CodeKeymapExtension,
|
||||||
|
...getCodeClipboardExtensions(),
|
||||||
|
].flat();
|
||||||
@@ -26,13 +26,11 @@ import { computed, effect, type Signal, signal } from '@preact/signals-core';
|
|||||||
import { html, nothing, type TemplateResult } from 'lit';
|
import { html, nothing, type TemplateResult } from 'lit';
|
||||||
import { query } from 'lit/decorators.js';
|
import { query } from 'lit/decorators.js';
|
||||||
import { classMap } from 'lit/directives/class-map.js';
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
import { styleMap } from 'lit/directives/style-map.js';
|
|
||||||
import { bundledLanguagesInfo, type ThemedToken } from 'shiki';
|
import { bundledLanguagesInfo, type ThemedToken } from 'shiki';
|
||||||
|
|
||||||
import { CodeBlockConfigExtension } from './code-block-config.js';
|
import { CodeBlockConfigExtension } from './code-block-config.js';
|
||||||
import { CodeBlockInlineManagerExtension } from './code-block-inline.js';
|
import { CodeBlockInlineManagerExtension } from './code-block-inline.js';
|
||||||
import { CodeBlockHighlighter } from './code-block-service.js';
|
import { CodeBlockHighlighter } from './code-block-service.js';
|
||||||
import { CodeBlockPreviewIdentifier } from './code-preview-extension.js';
|
|
||||||
import { codeBlockStyles } from './styles.js';
|
import { codeBlockStyles } from './styles.js';
|
||||||
|
|
||||||
export class CodeBlockComponent extends CaptionedBlockComponent<CodeBlockModel> {
|
export class CodeBlockComponent extends CaptionedBlockComponent<CodeBlockModel> {
|
||||||
@@ -68,7 +66,7 @@ export class CodeBlockComponent extends CaptionedBlockComponent<CodeBlockModel>
|
|||||||
}
|
}
|
||||||
|
|
||||||
get readonly() {
|
get readonly() {
|
||||||
return this.store.readonly;
|
return this.doc.readonly;
|
||||||
}
|
}
|
||||||
|
|
||||||
get langs() {
|
get langs() {
|
||||||
@@ -226,7 +224,7 @@ export class CodeBlockComponent extends CaptionedBlockComponent<CodeBlockModel>
|
|||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
Tab: ctx => {
|
Tab: ctx => {
|
||||||
if (this.store.readonly) return;
|
if (this.doc.readonly) return;
|
||||||
const state = ctx.get('keyboardState');
|
const state = ctx.get('keyboardState');
|
||||||
const event = state.raw;
|
const event = state.raw;
|
||||||
const inlineEditor = this.inlineEditor;
|
const inlineEditor = this.inlineEditor;
|
||||||
@@ -334,10 +332,10 @@ export class CodeBlockComponent extends CaptionedBlockComponent<CodeBlockModel>
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
Delete: () => {
|
Delete: () => {
|
||||||
return;
|
return true;
|
||||||
},
|
},
|
||||||
Enter: () => {
|
Enter: () => {
|
||||||
this.store.captureSync();
|
this.doc.captureSync();
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
'Mod-Enter': () => {
|
'Mod-Enter': () => {
|
||||||
@@ -348,16 +346,11 @@ export class CodeBlockComponent extends CaptionedBlockComponent<CodeBlockModel>
|
|||||||
if (!inlineRange || !inlineEditor) return;
|
if (!inlineRange || !inlineEditor) return;
|
||||||
const isEnd = model.props.text.length === inlineRange.index;
|
const isEnd = model.props.text.length === inlineRange.index;
|
||||||
if (!isEnd) return;
|
if (!isEnd) return;
|
||||||
const parent = this.store.getParent(model);
|
const parent = this.doc.getParent(model);
|
||||||
if (!parent) return;
|
if (!parent) return;
|
||||||
const index = parent.children.indexOf(model);
|
const index = parent.children.indexOf(model);
|
||||||
if (index === -1) return;
|
if (index === -1) return;
|
||||||
const id = this.store.addBlock(
|
const id = this.doc.addBlock('affine:paragraph', {}, parent, index + 1);
|
||||||
'affine:paragraph',
|
|
||||||
{},
|
|
||||||
parent,
|
|
||||||
index + 1
|
|
||||||
);
|
|
||||||
focusTextModel(std, id);
|
focusTextModel(std, id);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
@@ -368,7 +361,7 @@ export class CodeBlockComponent extends CaptionedBlockComponent<CodeBlockModel>
|
|||||||
|
|
||||||
copyCode() {
|
copyCode() {
|
||||||
const model = this.model;
|
const model = this.model;
|
||||||
const slice = Slice.fromModels(model.store, [model]);
|
const slice = Slice.fromModels(model.doc, [model]);
|
||||||
this.std.clipboard
|
this.std.clipboard
|
||||||
.copySlice(slice)
|
.copySlice(slice)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@@ -391,12 +384,6 @@ export class CodeBlockComponent extends CaptionedBlockComponent<CodeBlockModel>
|
|||||||
this.std.getOptional(CodeBlockConfigExtension.identifier)
|
this.std.getOptional(CodeBlockConfigExtension.identifier)
|
||||||
?.showLineNumbers ?? true;
|
?.showLineNumbers ?? true;
|
||||||
|
|
||||||
const preview = !!this.model.props.preview;
|
|
||||||
const previewContext = this.std.getOptional(
|
|
||||||
CodeBlockPreviewIdentifier(this.model.props.language ?? '')
|
|
||||||
);
|
|
||||||
const shouldRenderPreview = preview && previewContext;
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
@@ -406,15 +393,12 @@ export class CodeBlockComponent extends CaptionedBlockComponent<CodeBlockModel>
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<rich-text
|
<rich-text
|
||||||
style=${styleMap({
|
|
||||||
display: shouldRenderPreview ? 'none' : undefined,
|
|
||||||
})}
|
|
||||||
.yText=${this.model.props.text.yText}
|
.yText=${this.model.props.text.yText}
|
||||||
.inlineEventSource=${this.topContenteditableElement ?? nothing}
|
.inlineEventSource=${this.topContenteditableElement ?? nothing}
|
||||||
.undoManager=${this.store.history.undoManager}
|
.undoManager=${this.doc.history}
|
||||||
.attributesSchema=${this.inlineManager.getSchema()}
|
.attributesSchema=${this.inlineManager.getSchema()}
|
||||||
.attributeRenderer=${this.inlineManager.getRenderer()}
|
.attributeRenderer=${this.inlineManager.getRenderer()}
|
||||||
.readonly=${this.store.readonly}
|
.readonly=${this.doc.readonly}
|
||||||
.inlineRangeProvider=${this._inlineRangeProvider}
|
.inlineRangeProvider=${this._inlineRangeProvider}
|
||||||
.enableClipboard=${false}
|
.enableClipboard=${false}
|
||||||
.enableUndoRedo=${false}
|
.enableUndoRedo=${false}
|
||||||
@@ -432,22 +416,14 @@ export class CodeBlockComponent extends CaptionedBlockComponent<CodeBlockModel>
|
|||||||
: undefined}
|
: undefined}
|
||||||
>
|
>
|
||||||
</rich-text>
|
</rich-text>
|
||||||
<div
|
|
||||||
style=${styleMap({
|
|
||||||
display: shouldRenderPreview ? undefined : 'none',
|
|
||||||
})}
|
|
||||||
contenteditable="false"
|
|
||||||
class="affine-code-block-preview"
|
|
||||||
>
|
|
||||||
${previewContext?.renderer(this.model)}
|
|
||||||
</div>
|
|
||||||
${this.renderChildren(this.model)} ${Object.values(this.widgets)}
|
${this.renderChildren(this.model)} ${Object.values(this.widgets)}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
setWrap(wrap: boolean) {
|
setWrap(wrap: boolean) {
|
||||||
this.store.updateBlock(this.model, { wrap });
|
this.doc.updateBlock(this.model, { wrap });
|
||||||
}
|
}
|
||||||
|
|
||||||
@query('rich-text')
|
@query('rich-text')
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
import type { CodeBlockModel } from '@blocksuite/affine-model';
|
|
||||||
import { createIdentifier } from '@blocksuite/global/di';
|
|
||||||
import type { ExtensionType } from '@blocksuite/store';
|
|
||||||
import type { HTMLTemplateResult } from 'lit';
|
|
||||||
|
|
||||||
export type CodeBlockPreviewRenderer = (
|
|
||||||
model: CodeBlockModel
|
|
||||||
) => HTMLTemplateResult | null;
|
|
||||||
|
|
||||||
export type CodeBlockPreviewContext = {
|
|
||||||
renderer: CodeBlockPreviewRenderer;
|
|
||||||
lang: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const CodeBlockPreviewIdentifier =
|
|
||||||
createIdentifier<CodeBlockPreviewContext>('CodeBlockPreview');
|
|
||||||
|
|
||||||
export function CodeBlockPreviewExtension(
|
|
||||||
lang: string,
|
|
||||||
renderer: CodeBlockPreviewRenderer
|
|
||||||
): ExtensionType {
|
|
||||||
return {
|
|
||||||
setup: di => {
|
|
||||||
di.addImpl(CodeBlockPreviewIdentifier(lang), { renderer, lang });
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -30,6 +30,7 @@ export class AffineCodeToolbar extends WithDisposable(LitElement) {
|
|||||||
padding: 4px;
|
padding: 4px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.code-toolbar-button {
|
.code-toolbar-button {
|
||||||
@@ -38,10 +39,6 @@ export class AffineCodeToolbar extends WithDisposable(LitElement) {
|
|||||||
box-shadow: var(--affine-shadow-1);
|
box-shadow: var(--affine-shadow-1);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy-code {
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
private _currentOpenMenu: AbortController | null = null;
|
private _currentOpenMenu: AbortController | null = null;
|
||||||
|
|||||||
@@ -18,13 +18,16 @@ export class LanguageListButton extends WithDisposable(
|
|||||||
SignalWatcher(LitElement)
|
SignalWatcher(LitElement)
|
||||||
) {
|
) {
|
||||||
static override styles = css`
|
static override styles = css`
|
||||||
|
:host {
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.lang-button {
|
.lang-button {
|
||||||
background-color: var(--affine-background-primary-color);
|
background-color: var(--affine-background-primary-color);
|
||||||
box-shadow: var(--affine-shadow-1);
|
box-shadow: var(--affine-shadow-1);
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
height: 28px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.lang-button:hover {
|
.lang-button:hover {
|
||||||
@@ -50,7 +53,7 @@ export class LanguageListButton extends WithDisposable(
|
|||||||
private _abortController?: AbortController;
|
private _abortController?: AbortController;
|
||||||
|
|
||||||
private readonly _clickLangBtn = () => {
|
private readonly _clickLangBtn = () => {
|
||||||
if (this.blockComponent.store.readonly) return;
|
if (this.blockComponent.doc.readonly) return;
|
||||||
if (this._abortController) {
|
if (this._abortController) {
|
||||||
// Close the language list if it's already opened.
|
// Close the language list if it's already opened.
|
||||||
this._abortController.abort();
|
this._abortController.abort();
|
||||||
@@ -72,7 +75,7 @@ export class LanguageListButton extends WithDisposable(
|
|||||||
sortedBundledLanguages.splice(index, 1);
|
sortedBundledLanguages.splice(index, 1);
|
||||||
sortedBundledLanguages.unshift(item);
|
sortedBundledLanguages.unshift(item);
|
||||||
}
|
}
|
||||||
this.blockComponent.store.transact(() => {
|
this.blockComponent.doc.transact(() => {
|
||||||
this.blockComponent.model.props.language$.value = item.name;
|
this.blockComponent.model.props.language$.value = item.name;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -135,10 +138,10 @@ export class LanguageListButton extends WithDisposable(
|
|||||||
</div>`}
|
</div>`}
|
||||||
height="24px"
|
height="24px"
|
||||||
@click=${this._clickLangBtn}
|
@click=${this._clickLangBtn}
|
||||||
?disabled=${this.blockComponent.store.readonly}
|
?disabled=${this.blockComponent.doc.readonly}
|
||||||
>
|
>
|
||||||
<span class="lang-button-icon" slot="suffix">
|
<span class="lang-button-icon" slot="suffix">
|
||||||
${!this.blockComponent.store.readonly ? ArrowDownIcon : nothing}
|
${!this.blockComponent.doc.readonly ? ArrowDownIcon : nothing}
|
||||||
</span>
|
</span>
|
||||||
</icon-button> `;
|
</icon-button> `;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
|
||||||
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
|
|
||||||
import { css, html, LitElement, nothing } from 'lit';
|
|
||||||
import { property } from 'lit/decorators.js';
|
|
||||||
import { classMap } from 'lit/directives/class-map.js';
|
|
||||||
|
|
||||||
import type { CodeBlockComponent } from '../../code-block';
|
|
||||||
import { CodeBlockPreviewIdentifier } from '../../code-preview-extension';
|
|
||||||
|
|
||||||
export class PreviewButton extends WithDisposable(SignalWatcher(LitElement)) {
|
|
||||||
static override styles = css`
|
|
||||||
.preview-toggle-container {
|
|
||||||
display: flex;
|
|
||||||
padding: 2px;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
background: ${unsafeCSSVarV2('segment/background')};
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle-button {
|
|
||||||
display: flex;
|
|
||||||
padding: 0px 4px;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
color: ${unsafeCSSVarV2('text/primary')};
|
|
||||||
font-family: Inter;
|
|
||||||
font-size: 12px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle-button:hover {
|
|
||||||
background: ${unsafeCSSVarV2('layer/background/hoverOverlay')};
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle-button.active {
|
|
||||||
background: ${unsafeCSSVarV2('segment/button')};
|
|
||||||
box-shadow:
|
|
||||||
var(--Shadow-buttonShadow-1-x, 0px) var(--Shadow-buttonShadow-1-y, 0px)
|
|
||||||
var(--Shadow-buttonShadow-1-blur, 1px) 0px
|
|
||||||
var(--Shadow-buttonShadow-1-color, rgba(0, 0, 0, 0.12)),
|
|
||||||
var(--Shadow-buttonShadow-2-x, 0px) var(--Shadow-buttonShadow-2-y, 1px)
|
|
||||||
var(--Shadow-buttonShadow-2-blur, 5px) 0px
|
|
||||||
var(--Shadow-buttonShadow-2-color, rgba(0, 0, 0, 0.12));
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
private readonly _toggle = (value: boolean) => {
|
|
||||||
if (this.blockComponent.store.readonly) return;
|
|
||||||
|
|
||||||
this.blockComponent.store.updateBlock(this.blockComponent.model, {
|
|
||||||
preview: value,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
get preview() {
|
|
||||||
return !!this.blockComponent.model.props.preview$.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
override render() {
|
|
||||||
const lang = this.blockComponent.model.props.language$.value ?? '';
|
|
||||||
const previewContext = this.blockComponent.std.getOptional(
|
|
||||||
CodeBlockPreviewIdentifier(lang)
|
|
||||||
);
|
|
||||||
if (!previewContext) return nothing;
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<div class="preview-toggle-container">
|
|
||||||
<div
|
|
||||||
class=${classMap({
|
|
||||||
'toggle-button': true,
|
|
||||||
active: !this.preview,
|
|
||||||
})}
|
|
||||||
@click=${() => this._toggle(false)}
|
|
||||||
>
|
|
||||||
Code
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class=${classMap({
|
|
||||||
'toggle-button': true,
|
|
||||||
active: this.preview,
|
|
||||||
})}
|
|
||||||
@click=${() => this._toggle(true)}
|
|
||||||
>
|
|
||||||
Preview
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
accessor blockComponent!: CodeBlockComponent;
|
|
||||||
}
|
|
||||||
@@ -42,18 +42,6 @@ export const PRIMARY_GROUPS: MenuItemGroup<CodeBlockToolbarContext>[] = [
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: 'preview',
|
|
||||||
generate: ({ blockComponent }) => {
|
|
||||||
return {
|
|
||||||
action: noop,
|
|
||||||
render: () => html`
|
|
||||||
<preview-button .blockComponent=${blockComponent}>
|
|
||||||
</preview-button>
|
|
||||||
`,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: 'copy-code',
|
type: 'copy-code',
|
||||||
label: 'Copy code',
|
label: 'Copy code',
|
||||||
@@ -122,28 +110,16 @@ export const clipboardGroup: MenuItemGroup<CodeBlockToolbarContext> = {
|
|||||||
{
|
{
|
||||||
type: 'wrap',
|
type: 'wrap',
|
||||||
generate: ({ blockComponent, close }) => {
|
generate: ({ blockComponent, close }) => {
|
||||||
|
const wrapped = blockComponent.model.props.wrap;
|
||||||
|
const label = wrapped ? 'Cancel wrap' : 'Wrap';
|
||||||
|
const icon = wrapped ? CancelWrapIcon : WrapIcon;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
action: () => {},
|
label,
|
||||||
render: () => {
|
icon,
|
||||||
const wrapped = blockComponent.model.props.wrap;
|
action: () => {
|
||||||
const label = wrapped ? 'Cancel wrap' : 'Wrap';
|
blockComponent.setWrap(!wrapped);
|
||||||
const icon = wrapped ? CancelWrapIcon : WrapIcon;
|
close();
|
||||||
return html`
|
|
||||||
<editor-menu-action
|
|
||||||
@click=${() => {
|
|
||||||
blockComponent.setWrap(!wrapped);
|
|
||||||
close();
|
|
||||||
}}
|
|
||||||
aria-label=${label}
|
|
||||||
>
|
|
||||||
${icon}
|
|
||||||
<span class="label">${label}</span>
|
|
||||||
<toggle-switch
|
|
||||||
style="margin-left: auto;"
|
|
||||||
.on="${wrapped}"
|
|
||||||
></toggle-switch>
|
|
||||||
</editor-menu-action>
|
|
||||||
`;
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export class CodeBlockToolbarContext extends MenuContext {
|
|||||||
};
|
};
|
||||||
|
|
||||||
get doc() {
|
get doc() {
|
||||||
return this.blockComponent.store;
|
return this.blockComponent.doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
get host() {
|
get host() {
|
||||||
|
|||||||
@@ -12,5 +12,5 @@ export const duplicateCodeBlock = (model: CodeBlockModel) => {
|
|||||||
...duplicateProps,
|
...duplicateProps,
|
||||||
};
|
};
|
||||||
|
|
||||||
return model.store.addSiblingBlocks(model, [newProps])[0];
|
return model.doc.addSiblingBlocks(model, [newProps])[0];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
} from './code-toolbar';
|
} from './code-toolbar';
|
||||||
import { AffineCodeToolbar } from './code-toolbar/components/code-toolbar';
|
import { AffineCodeToolbar } from './code-toolbar/components/code-toolbar';
|
||||||
import { LanguageListButton } from './code-toolbar/components/lang-button';
|
import { LanguageListButton } from './code-toolbar/components/lang-button';
|
||||||
import { PreviewButton } from './code-toolbar/components/preview-button';
|
|
||||||
import { AffineCodeUnit } from './highlight/affine-code-unit';
|
import { AffineCodeUnit } from './highlight/affine-code-unit';
|
||||||
|
|
||||||
export function effects() {
|
export function effects() {
|
||||||
@@ -14,14 +13,12 @@ export function effects() {
|
|||||||
customElements.define(AFFINE_CODE_TOOLBAR_WIDGET, AffineCodeToolbarWidget);
|
customElements.define(AFFINE_CODE_TOOLBAR_WIDGET, AffineCodeToolbarWidget);
|
||||||
customElements.define('affine-code-unit', AffineCodeUnit);
|
customElements.define('affine-code-unit', AffineCodeUnit);
|
||||||
customElements.define('affine-code', CodeBlockComponent);
|
customElements.define('affine-code', CodeBlockComponent);
|
||||||
customElements.define('preview-button', PreviewButton);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
'language-list-button': LanguageListButton;
|
'language-list-button': LanguageListButton;
|
||||||
'affine-code-toolbar': AffineCodeToolbar;
|
'affine-code-toolbar': AffineCodeToolbar;
|
||||||
'preview-button': PreviewButton;
|
|
||||||
[AFFINE_CODE_TOOLBAR_WIDGET]: AffineCodeToolbarWidget;
|
[AFFINE_CODE_TOOLBAR_WIDGET]: AffineCodeToolbarWidget;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ export * from './adapters';
|
|||||||
export * from './clipboard';
|
export * from './clipboard';
|
||||||
export * from './code-block';
|
export * from './code-block';
|
||||||
export * from './code-block-config';
|
export * from './code-block-config';
|
||||||
export * from './code-preview-extension';
|
export * from './code-block-spec';
|
||||||
export * from './code-toolbar';
|
export * from './code-toolbar';
|
||||||
export * from './turbo/code-layout-handler';
|
export * from './turbo/code-layout-handler';
|
||||||
export * from './turbo/code-painter.worker';
|
export * from './turbo/code-painter.worker';
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user