mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-05 00:54:56 +00:00
Compare commits
5 Commits
zzj/fix/ta
...
v0.19.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b14f1cdb7c | ||
|
|
22a8694972 | ||
|
|
bbe88c57b7 | ||
|
|
a3b502fad7 | ||
|
|
28f0dad3f9 |
@@ -1,6 +1,5 @@
|
||||
FROM mcr.microsoft.com/devcontainers/base:bookworm
|
||||
|
||||
USER vscode
|
||||
# Install Homebrew For Linux
|
||||
RUN /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" && \
|
||||
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" && \
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
DATABASE_LOCATION=./postgres
|
||||
DB_PASSWORD=affine
|
||||
DB_USERNAME=affine
|
||||
DB_DATABASE_NAME=affine
|
||||
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -1,2 +1 @@
|
||||
/blocksuite/ @toeverything/blocksuite-core
|
||||
/packages/frontend/core/src/blocksuite @toeverything/blocksuite-core
|
||||
|
||||
1
.github/actions/copilot-test/action.yml
vendored
1
.github/actions/copilot-test/action.yml
vendored
@@ -26,7 +26,6 @@ runs:
|
||||
DEV_SERVER_URL: http://localhost:8080
|
||||
COPILOT_OPENAI_API_KEY: ${{ inputs.openai-key }}
|
||||
COPILOT_FAL_API_KEY: ${{ inputs.fal-key }}
|
||||
COPILOT_PERPLEXITY_API_KEY: ${{ inputs.perplexity-key }}
|
||||
|
||||
- name: Upload test results
|
||||
if: ${{ failure() }}
|
||||
|
||||
11
.github/actions/deploy/deploy.mjs
vendored
11
.github/actions/deploy/deploy.mjs
vendored
@@ -17,7 +17,6 @@ const {
|
||||
METRICS_CUSTOMER_IO_TOKEN,
|
||||
COPILOT_OPENAI_API_KEY,
|
||||
COPILOT_FAL_API_KEY,
|
||||
COPILOT_PERPLEXITY_API_KEY,
|
||||
COPILOT_UNSPLASH_API_KEY,
|
||||
MAILER_SENDER,
|
||||
MAILER_USER,
|
||||
@@ -47,21 +46,18 @@ const replicaConfig = {
|
||||
graphql: Number(process.env.PRODUCTION_GRAPHQL_REPLICA) || 3,
|
||||
sync: Number(process.env.PRODUCTION_SYNC_REPLICA) || 3,
|
||||
renderer: Number(process.env.PRODUCTION_RENDERER_REPLICA) || 3,
|
||||
doc: Number(process.env.PRODUCTION_DOC_REPLICA) || 3,
|
||||
},
|
||||
beta: {
|
||||
web: 2,
|
||||
graphql: Number(process.env.BETA_GRAPHQL_REPLICA) || 2,
|
||||
sync: Number(process.env.BETA_SYNC_REPLICA) || 2,
|
||||
renderer: Number(process.env.BETA_RENDERER_REPLICA) || 2,
|
||||
doc: Number(process.env.BETA_DOC_REPLICA) || 2,
|
||||
},
|
||||
canary: {
|
||||
web: 2,
|
||||
graphql: 2,
|
||||
sync: 2,
|
||||
renderer: 2,
|
||||
doc: 2,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -70,14 +66,12 @@ const cpuConfig = {
|
||||
web: '300m',
|
||||
graphql: '1',
|
||||
sync: '1',
|
||||
doc: '1',
|
||||
renderer: '300m',
|
||||
},
|
||||
canary: {
|
||||
web: '300m',
|
||||
graphql: '1',
|
||||
sync: '1',
|
||||
doc: '1',
|
||||
renderer: '300m',
|
||||
},
|
||||
};
|
||||
@@ -116,7 +110,6 @@ const createHelmCommand = ({ isDryRun }) => {
|
||||
`--set web.resources.requests.cpu="${cpu.web}"`,
|
||||
`--set graphql.resources.requests.cpu="${cpu.graphql}"`,
|
||||
`--set sync.resources.requests.cpu="${cpu.sync}"`,
|
||||
`--set doc.resources.requests.cpu="${cpu.doc}"`,
|
||||
]
|
||||
: [];
|
||||
|
||||
@@ -154,7 +147,6 @@ const createHelmCommand = ({ isDryRun }) => {
|
||||
`--set graphql.app.copilot.enabled=true`,
|
||||
`--set-string graphql.app.copilot.openai.key="${COPILOT_OPENAI_API_KEY}"`,
|
||||
`--set-string graphql.app.copilot.fal.key="${COPILOT_FAL_API_KEY}"`,
|
||||
`--set-string graphql.app.copilot.perplexity.key="${COPILOT_PERPLEXITY_API_KEY}"`,
|
||||
`--set-string graphql.app.copilot.unsplash.key="${COPILOT_UNSPLASH_API_KEY}"`,
|
||||
`--set-string graphql.app.mailer.sender="${MAILER_SENDER}"`,
|
||||
`--set-string graphql.app.mailer.user="${MAILER_USER}"`,
|
||||
@@ -174,9 +166,6 @@ const createHelmCommand = ({ isDryRun }) => {
|
||||
`--set-string renderer.image.tag="${imageTag}"`,
|
||||
`--set renderer.app.host=${host}`,
|
||||
`--set renderer.replicaCount=${replica.renderer}`,
|
||||
`--set-string doc.image.tag="${imageTag}"`,
|
||||
`--set doc.app.host=${host}`,
|
||||
`--set doc.replicaCount=${replica.doc}`,
|
||||
...serviceAnnotations,
|
||||
...resources,
|
||||
`--timeout 10m`,
|
||||
|
||||
2
.github/deployment/node/Dockerfile
vendored
2
.github/deployment/node/Dockerfile
vendored
@@ -1,4 +1,4 @@
|
||||
FROM node:22-bookworm-slim
|
||||
FROM node:20-bookworm-slim
|
||||
|
||||
COPY ./packages/backend/server /app
|
||||
COPY ./packages/frontend/apps/web/dist /app/static
|
||||
|
||||
2
.github/helm/affine/Chart.yaml
vendored
2
.github/helm/affine/Chart.yaml
vendored
@@ -3,4 +3,4 @@ name: affine
|
||||
description: AFFiNE cloud chart
|
||||
type: application
|
||||
version: 0.0.0
|
||||
appVersion: "0.20.0"
|
||||
appVersion: "0.19.0"
|
||||
|
||||
11
.github/helm/affine/charts/doc/Chart.yaml
vendored
11
.github/helm/affine/charts/doc/Chart.yaml
vendored
@@ -1,11 +0,0 @@
|
||||
apiVersion: v2
|
||||
name: doc
|
||||
description: AFFiNE doc server
|
||||
type: application
|
||||
version: 0.0.0
|
||||
appVersion: "0.20.0"
|
||||
dependencies:
|
||||
- name: gcloud-sql-proxy
|
||||
version: 0.0.0
|
||||
repository: "file://../gcloud-sql-proxy"
|
||||
condition: .global.database.gcloud.enabled
|
||||
@@ -1,16 +0,0 @@
|
||||
1. Get the application URL by running these commands:
|
||||
{{- if contains "NodePort" .Values.service.type }}
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "doc.fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo http://$NODE_IP:$NODE_PORT
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "doc.fullname" . }}'
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "doc.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
|
||||
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "doc.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
|
||||
{{- end }}
|
||||
@@ -1,63 +0,0 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "doc.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "doc.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "doc.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "doc.labels" -}}
|
||||
helm.sh/chart: {{ include "doc.chart" . }}
|
||||
{{ include "doc.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
monitoring: enabled
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "doc.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "doc.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "doc.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "doc.fullname" .) .Values.global.docService.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.global.docService.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -1,105 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "doc.fullname" . }}
|
||||
labels:
|
||||
{{- include "doc.labels" . | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "doc.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "doc.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "doc.serviceAccountName" . }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
env:
|
||||
- name: AFFINE_PRIVATE_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "{{ .Values.global.secret.secretName }}"
|
||||
key: key
|
||||
- name: NODE_ENV
|
||||
value: "{{ .Values.env }}"
|
||||
- name: NODE_OPTIONS
|
||||
value: "--max-old-space-size=4096"
|
||||
- name: NO_COLOR
|
||||
value: "1"
|
||||
- name: DEPLOYMENT_TYPE
|
||||
value: "affine"
|
||||
- name: SERVER_FLAVOR
|
||||
value: "doc"
|
||||
- name: AFFINE_ENV
|
||||
value: "{{ .Release.Namespace }}"
|
||||
- name: DATABASE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: pg-postgresql
|
||||
key: postgres-password
|
||||
- name: DATABASE_URL
|
||||
value: postgres://{{ .Values.global.database.user }}:$(DATABASE_PASSWORD)@{{ .Values.global.database.url }}:{{ .Values.global.database.port }}/{{ .Values.global.database.name }}
|
||||
- name: REDIS_SERVER_ENABLED
|
||||
value: "true"
|
||||
- name: REDIS_SERVER_HOST
|
||||
value: "{{ .Values.global.redis.host }}"
|
||||
- name: REDIS_SERVER_PORT
|
||||
value: "{{ .Values.global.redis.port }}"
|
||||
- name: REDIS_SERVER_USER
|
||||
value: "{{ .Values.global.redis.username }}"
|
||||
- name: REDIS_SERVER_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: redis
|
||||
key: redis-password
|
||||
- name: REDIS_SERVER_DATABASE
|
||||
value: "{{ .Values.global.redis.database }}"
|
||||
- name: AFFINE_SERVER_PORT
|
||||
value: "{{ .Values.global.docService.port }}"
|
||||
- name: AFFINE_SERVER_SUB_PATH
|
||||
value: "{{ .Values.app.path }}"
|
||||
- name: AFFINE_SERVER_HOST
|
||||
value: "{{ .Values.app.host }}"
|
||||
- name: AFFINE_SERVER_HTTPS
|
||||
value: "{{ .Values.app.https }}"
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.global.docService.port }}
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /info
|
||||
port: http
|
||||
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /info
|
||||
port: http
|
||||
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
@@ -1,19 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "doc.fullname" . }}
|
||||
labels:
|
||||
{{- include "doc.labels" . | nindent 4 }}
|
||||
{{- with .Values.service.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.global.docService.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
{{- include "doc.selectorLabels" . | nindent 4 }}
|
||||
@@ -1,12 +0,0 @@
|
||||
{{- if .Values.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "doc.serviceAccountName" . }}
|
||||
labels:
|
||||
{{- include "doc.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -1,15 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: "{{ include "doc.fullname" . }}-test-connection"
|
||||
labels:
|
||||
{{- include "doc.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
"helm.sh/hook": test
|
||||
spec:
|
||||
containers:
|
||||
- name: wget
|
||||
image: busybox
|
||||
command: ['wget']
|
||||
args: ['{{ include "doc.fullname" . }}:{{ .Values.global.docService.port }}']
|
||||
restartPolicy: Never
|
||||
37
.github/helm/affine/charts/doc/values.yaml
vendored
37
.github/helm/affine/charts/doc/values.yaml
vendored
@@ -1,37 +0,0 @@
|
||||
replicaCount: 1
|
||||
image:
|
||||
repository: ghcr.io/toeverything/affine-graphql
|
||||
pullPolicy: IfNotPresent
|
||||
tag: ''
|
||||
|
||||
imagePullSecrets: []
|
||||
nameOverride: ''
|
||||
fullnameOverride: ''
|
||||
# map to NODE_ENV environment variable
|
||||
env: 'production'
|
||||
app:
|
||||
# AFFINE_SERVER_SUB_PATH
|
||||
path: ''
|
||||
# AFFINE_SERVER_HOST
|
||||
host: '0.0.0.0'
|
||||
https: true
|
||||
serviceAccount:
|
||||
create: true
|
||||
annotations: {}
|
||||
|
||||
podAnnotations: {}
|
||||
|
||||
podSecurityContext:
|
||||
fsGroup: 2000
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: '2'
|
||||
memory: 4Gi
|
||||
|
||||
probe:
|
||||
initialDelaySeconds: 20
|
||||
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
@@ -3,7 +3,7 @@ name: graphql
|
||||
description: AFFiNE GraphQL server
|
||||
type: application
|
||||
version: 0.0.0
|
||||
appVersion: "0.20.0"
|
||||
appVersion: "0.19.0"
|
||||
dependencies:
|
||||
- name: gcloud-sql-proxy
|
||||
version: 0.0.0
|
||||
|
||||
@@ -7,6 +7,5 @@ type: Opaque
|
||||
data:
|
||||
openaiSecret: {{ .Values.app.copilot.openai.key | b64enc }}
|
||||
falSecret: {{ .Values.app.copilot.fal.key | b64enc }}
|
||||
perplexitySecret: {{ .Values.app.copilot.perplexity.key | b64enc }}
|
||||
unsplashSecret: {{ .Values.app.copilot.unsplash.key | b64enc }}
|
||||
{{- end }}
|
||||
|
||||
@@ -116,8 +116,8 @@ spec:
|
||||
secretKeyRef:
|
||||
name: "{{ .Values.app.payment.stripe.secretName }}"
|
||||
key: stripeWebhookKey
|
||||
- name: DOC_SERVICE_ENDPOINT
|
||||
value: "http://{{ .Values.global.docService.name }}:{{ .Values.global.docService.port }}"
|
||||
- name: DOC_MERGE_INTERVAL
|
||||
value: "{{ .Values.app.doc.mergeInterval }}"
|
||||
{{ if .Values.app.experimental.enableJwstCodec }}
|
||||
- name: DOC_MERGE_USE_JWST_CODEC
|
||||
value: "true"
|
||||
@@ -157,11 +157,6 @@ spec:
|
||||
secretKeyRef:
|
||||
name: "{{ .Values.app.copilot.secretName }}"
|
||||
key: falSecret
|
||||
- name: COPILOT_PERPLEXITY_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: "{{ .Values.app.copilot.secretName }}"
|
||||
key: perplexitySecret
|
||||
- name: COPILOT_UNSPLASH_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
|
||||
@@ -17,6 +17,8 @@ app:
|
||||
# AFFINE_SERVER_HOST
|
||||
host: '0.0.0.0'
|
||||
https: true
|
||||
doc:
|
||||
mergeInterval: "3000"
|
||||
captcha:
|
||||
enabled: false
|
||||
secretName: captcha
|
||||
|
||||
@@ -94,8 +94,6 @@ spec:
|
||||
name: "{{ .Values.global.objectStorage.r2.secretName }}"
|
||||
key: secretAccessKey
|
||||
{{ end }}
|
||||
- name: DOC_SERVICE_ENDPOINT
|
||||
value: "http://{{ .Values.global.docService.name }}:{{ .Values.global.docService.port }}"
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.service.port }}
|
||||
|
||||
2
.github/helm/affine/charts/sync/Chart.yaml
vendored
2
.github/helm/affine/charts/sync/Chart.yaml
vendored
@@ -3,7 +3,7 @@ name: sync
|
||||
description: AFFiNE Sync Server
|
||||
type: application
|
||||
version: 0.0.0
|
||||
appVersion: "0.20.0"
|
||||
appVersion: "0.19.0"
|
||||
dependencies:
|
||||
- name: gcloud-sql-proxy
|
||||
version: 0.0.0
|
||||
|
||||
@@ -73,8 +73,6 @@ spec:
|
||||
value: "{{ .Values.service.port }}"
|
||||
- name: AFFINE_SERVER_HOST
|
||||
value: "{{ .Values.app.host }}"
|
||||
- name: DOC_SERVICE_ENDPOINT
|
||||
value: "http://{{ .Values.global.docService.name }}:{{ .Values.global.docService.port }}"
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.service.port }}
|
||||
|
||||
9
.github/helm/affine/values.yaml
vendored
9
.github/helm/affine/values.yaml
vendored
@@ -39,9 +39,6 @@ global:
|
||||
secretAccessKey: ''
|
||||
gke:
|
||||
enabled: true
|
||||
docService:
|
||||
name: 'affine-doc'
|
||||
port: 3020
|
||||
|
||||
graphql:
|
||||
service:
|
||||
@@ -64,12 +61,6 @@ renderer:
|
||||
annotations:
|
||||
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
|
||||
|
||||
doc:
|
||||
service:
|
||||
type: ClusterIP
|
||||
annotations:
|
||||
cloud.google.com/backend-config: '{"default": "affine-api-backendconfig"}'
|
||||
|
||||
web:
|
||||
service:
|
||||
type: ClusterIP
|
||||
|
||||
4
.github/workflows/build-images.yml
vendored
4
.github/workflows/build-images.yml
vendored
@@ -27,9 +27,7 @@ jobs:
|
||||
electron-install: false
|
||||
extra-flags: workspaces focus @affine/server
|
||||
- name: Build Server
|
||||
run: |
|
||||
find packages/backend/server -type d -name "__tests__" -exec rm -rf {} +
|
||||
yarn workspace @affine/server build
|
||||
run: yarn workspace @affine/server build
|
||||
- name: Upload server dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
||||
53
.github/workflows/build-test.yml
vendored
53
.github/workflows/build-test.yml
vendored
@@ -118,26 +118,6 @@ jobs:
|
||||
- name: Run Type Check
|
||||
run: yarn typecheck
|
||||
|
||||
lint-rust:
|
||||
name: Lint Rust
|
||||
runs-on: ubuntu-latest
|
||||
needs: optimize_ci
|
||||
if: needs.optimize_ci.outputs.skip == 'false'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/build-rust
|
||||
with:
|
||||
no-build: 'true'
|
||||
- name: fmt check
|
||||
run: |
|
||||
rustup toolchain add nightly
|
||||
rustup component add --toolchain nightly-x86_64-unknown-linux-gnu rustfmt
|
||||
cargo +nightly fmt --all -- --check
|
||||
- name: Clippy
|
||||
run: |
|
||||
rustup component add clippy
|
||||
cargo clippy --all-targets --all-features -- -D warnings
|
||||
|
||||
check-yarn-binary:
|
||||
name: Check yarn binary
|
||||
runs-on: ubuntu-latest
|
||||
@@ -175,7 +155,7 @@ jobs:
|
||||
run: yarn workspace @blocksuite/legacy-e2e test --forbid-only --shard=${{ matrix.shard }}/${{ strategy.job-total }}
|
||||
|
||||
- name: Upload test results
|
||||
if: always()
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-results-e2e-legacy-bs-${{ matrix.shard }}
|
||||
@@ -207,7 +187,7 @@ jobs:
|
||||
run: yarn affine @affine-test/affine-local e2e --forbid-only --shard=${{ matrix.shard }}/${{ strategy.job-total }}
|
||||
|
||||
- name: Upload test results
|
||||
if: always()
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-results-e2e-${{ matrix.shard }}
|
||||
@@ -239,7 +219,7 @@ jobs:
|
||||
run: yarn affine @affine-test/affine-mobile e2e --forbid-only --shard=${{ matrix.shard }}/${{ strategy.job-total }}
|
||||
|
||||
- name: Upload test results
|
||||
if: always()
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-results-e2e-mobile-${{ matrix.shard }}
|
||||
@@ -328,7 +308,6 @@ jobs:
|
||||
package: '@affine/native'
|
||||
- name: Upload ${{ steps.filename.outputs.filename }}
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: ${{ steps.filename.outputs.filename }}
|
||||
path: ${{ env.DEV_DRIVE_WORKSPACE || github.workspace }}/packages/frontend/native/${{ steps.filename.outputs.filename }}
|
||||
@@ -355,7 +334,6 @@ jobs:
|
||||
package: '@affine/server-native'
|
||||
- name: Upload server-native.node
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: server-native.node
|
||||
path: ./packages/backend/native/server-native.node
|
||||
@@ -381,7 +359,6 @@ jobs:
|
||||
run: tar -czf dist.tar.gz --directory=packages/frontend/apps/electron-renderer/dist .
|
||||
- name: Upload web artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: web
|
||||
path: dist.tar.gz
|
||||
@@ -394,11 +371,6 @@ jobs:
|
||||
- optimize_ci
|
||||
- build-server-native
|
||||
if: needs.optimize_ci.outputs.skip == 'false'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node_index: [0, 1, 2]
|
||||
total_nodes: [3]
|
||||
env:
|
||||
NODE_ENV: test
|
||||
DISTRIBUTION: web
|
||||
@@ -448,8 +420,6 @@ jobs:
|
||||
env:
|
||||
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
|
||||
COPILOT_OPENAI_API_KEY: 'use_fake_openai_api_key'
|
||||
CI_NODE_INDEX: ${{ matrix.node_index }}
|
||||
CI_NODE_TOTAL: ${{ matrix.total_nodes }}
|
||||
|
||||
- name: Upload server test coverage results
|
||||
uses: codecov/codecov-action@v5
|
||||
@@ -561,7 +531,6 @@ jobs:
|
||||
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
|
||||
COPILOT_OPENAI_API_KEY: ${{ secrets.COPILOT_OPENAI_API_KEY }}
|
||||
COPILOT_FAL_API_KEY: ${{ secrets.COPILOT_FAL_API_KEY }}
|
||||
COPILOT_PERPLEXITY_API_KEY: ${{ secrets.COPILOT_PERPLEXITY_API_KEY }}
|
||||
|
||||
- name: Upload server test coverage results
|
||||
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.apifilter.outputs.changed == 'true' }}
|
||||
@@ -624,7 +593,8 @@ jobs:
|
||||
with:
|
||||
filters: |
|
||||
changed:
|
||||
- 'packages/frontend/core/src/blocksuite/ai/**'
|
||||
- 'packages/frontend/core/src/blocksuite/presets/ai/**'
|
||||
- 'packages/frontend/core/src/components/blocksuite/block-suite-editor/ai/**'
|
||||
- 'tests/affine-cloud-copilot/**'
|
||||
|
||||
- name: Setup Node.js
|
||||
@@ -649,7 +619,6 @@ jobs:
|
||||
script: yarn affine @affine-test/affine-cloud-copilot e2e --forbid-only --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||
openai-key: ${{ secrets.COPILOT_OPENAI_API_KEY }}
|
||||
fal-key: ${{ secrets.COPILOT_FAL_API_KEY }}
|
||||
perplexity-key: ${{ secrets.COPILOT_PERPLEXITY_API_KEY }}
|
||||
|
||||
server-e2e-test:
|
||||
name: ${{ matrix.tests.name }}
|
||||
@@ -669,16 +638,12 @@ jobs:
|
||||
matrix:
|
||||
tests:
|
||||
- name: 'Server E2E Test 1/3'
|
||||
shard: 1
|
||||
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=1/3
|
||||
- name: 'Server E2E Test 2/3'
|
||||
shard: 2
|
||||
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=2/3
|
||||
- name: 'Server E2E Test 3/3'
|
||||
shard: 3
|
||||
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=3/3
|
||||
- name: 'Server Desktop E2E Test'
|
||||
shard: desktop
|
||||
script: |
|
||||
yarn affine @affine/electron build:dev
|
||||
# Workaround for Electron apps failing to initialize on Ubuntu 24.04 due to AppArmor restrictions
|
||||
@@ -738,13 +703,12 @@ jobs:
|
||||
DEV_SERVER_URL: http://localhost:8080
|
||||
COPILOT_OPENAI_API_KEY: 1
|
||||
COPILOT_FAL_API_KEY: 1
|
||||
COPILOT_PERPLEXITY_API_KEY: 1
|
||||
|
||||
- name: Upload test results
|
||||
if: always()
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-results-e2e-server-${{ matrix.tests.shard }}
|
||||
name: test-results-e2e-server
|
||||
path: ./test-results
|
||||
if-no-files-found: ignore
|
||||
|
||||
@@ -866,7 +830,7 @@ jobs:
|
||||
yarn affine @affine/electron node ./scripts/macos-arm64-output-check.ts
|
||||
|
||||
- name: Upload test results
|
||||
if: always()
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-results-e2e-${{ matrix.spec.os }}-${{ matrix.spec.arch }}
|
||||
@@ -888,7 +852,6 @@ jobs:
|
||||
needs:
|
||||
- analyze
|
||||
- lint
|
||||
- lint-rust
|
||||
- check-yarn-binary
|
||||
- e2e-test
|
||||
- e2e-legacy-blocksuite-test
|
||||
|
||||
2
.github/workflows/copilot-test.yml
vendored
2
.github/workflows/copilot-test.yml
vendored
@@ -84,7 +84,6 @@ jobs:
|
||||
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
|
||||
COPILOT_OPENAI_API_KEY: ${{ secrets.COPILOT_OPENAI_API_KEY }}
|
||||
COPILOT_FAL_API_KEY: ${{ secrets.COPILOT_FAL_API_KEY }}
|
||||
COPILOT_PERPLEXITY_API_KEY: ${{ secrets.COPILOT_PERPLEXITY_API_KEY }}
|
||||
|
||||
- name: Upload server test coverage results
|
||||
uses: codecov/codecov-action@v5
|
||||
@@ -148,7 +147,6 @@ jobs:
|
||||
script: yarn affine @affine-test/affine-cloud-copilot e2e --forbid-only --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||
openai-key: ${{ secrets.COPILOT_OPENAI_API_KEY }}
|
||||
fal-key: ${{ secrets.COPILOT_FAL_API_KEY }}
|
||||
perplexity-key: ${{ secrets.COPILOT_PERPLEXITY_API_KEY }}
|
||||
|
||||
test-done:
|
||||
needs:
|
||||
|
||||
1
.github/workflows/deploy.yml
vendored
1
.github/workflows/deploy.yml
vendored
@@ -98,7 +98,6 @@ jobs:
|
||||
CAPTCHA_TURNSTILE_SECRET: ${{ secrets.CAPTCHA_TURNSTILE_SECRET }}
|
||||
COPILOT_OPENAI_API_KEY: ${{ secrets.COPILOT_OPENAI_API_KEY }}
|
||||
COPILOT_FAL_API_KEY: ${{ secrets.COPILOT_FAL_API_KEY }}
|
||||
COPILOT_PERPLEXITY_API_KEY: ${{ secrets.COPILOT_PERPLEXITY_API_KEY }}
|
||||
COPILOT_UNSPLASH_API_KEY: ${{ secrets.COPILOT_UNSPLASH_API_KEY }}
|
||||
METRICS_CUSTOMER_IO_TOKEN: ${{ secrets.METRICS_CUSTOMER_IO_TOKEN }}
|
||||
MAILER_SENDER: ${{ secrets.OAUTH_EMAIL_SENDER }}
|
||||
|
||||
10
.github/workflows/release-mobile.yml
vendored
10
.github/workflows/release-mobile.yml
vendored
@@ -139,7 +139,7 @@ jobs:
|
||||
enableScripts: false
|
||||
- uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: 16.1
|
||||
xcode-version: latest-stable
|
||||
- name: Cap sync
|
||||
run: yarn workspace @affine/ios cap sync
|
||||
- name: Signing By Apple Developer ID
|
||||
@@ -191,7 +191,7 @@ jobs:
|
||||
uses: ./.github/actions/setup-node
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
extra-flags: workspaces focus @affine/monorepo @affine-tools/cli @affine/android @affine/playstore-auto-bump
|
||||
extra-flags: workspaces focus @affine/android @affine/playstore-auto-bump
|
||||
playwright-install: false
|
||||
electron-install: false
|
||||
hard-link-nm: false
|
||||
@@ -220,7 +220,7 @@ jobs:
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '21'
|
||||
java-version: '17'
|
||||
cache: 'gradle'
|
||||
- name: Auto increment version code
|
||||
id: bump
|
||||
@@ -231,7 +231,7 @@ jobs:
|
||||
- name: Build
|
||||
run: |
|
||||
echo -n "${{ env.AFFINE_ANDROID_SIGN_KEYSTORE }}" | base64 --decode > packages/frontend/apps/android/affine.keystore
|
||||
yarn workspace @affine/android cap build android --flavor ${{ env.BUILD_TYPE }} --androidreleasetype AAB
|
||||
yarn workspace @affine/android cap build android
|
||||
env:
|
||||
AFFINE_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.AFFINE_ANDROID_KEYSTORE_PASSWORD }}
|
||||
AFFINE_ANDROID_KEYSTORE_ALIAS_PASSWORD: ${{ secrets.AFFINE_ANDROID_KEYSTORE_ALIAS_PASSWORD }}
|
||||
@@ -243,7 +243,7 @@ jobs:
|
||||
with:
|
||||
serviceAccountJson: ${{ steps.auth.outputs.credentials_file_path }}
|
||||
packageName: app.affine.pro
|
||||
releaseFiles: packages/frontend/apps/android/App/app/build/outputs/bundle/${{ env.BUILD_TYPE }}Release/app-${{ env.BUILD_TYPE }}-release-signed.aab
|
||||
releaseFiles: packages/frontend/apps/android/App/app/build/outputs/bundle/release/app-release-signed.aab
|
||||
track: internal
|
||||
status: draft
|
||||
existingEditId: ${{ steps.bump.outputs.EDIT_ID }}
|
||||
|
||||
34
.github/workflows/sync-i18n.yml
vendored
34
.github/workflows/sync-i18n.yml
vendored
@@ -11,6 +11,7 @@ on:
|
||||
jobs:
|
||||
synchronize-with-crowdin:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
@@ -20,11 +21,10 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Crowdin action
|
||||
id: crowdin
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
upload_sources: true
|
||||
upload_translations: false
|
||||
upload_translations: true
|
||||
download_translations: true
|
||||
auto_approve_imported: true
|
||||
import_eq_suggestions: true
|
||||
@@ -40,33 +40,3 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
i18n-codegen:
|
||||
needs: synchronize-with-crowdin
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: l10n_crowdin_translations
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
with:
|
||||
electron-install: false
|
||||
full-cache: true
|
||||
|
||||
- name: Run i18n codegen
|
||||
run: yarn affine @affine/i18n build
|
||||
|
||||
- name: Commit changes
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add .
|
||||
git commit -m "chore(i18n): i18n codegen"
|
||||
git push origin l10n_crowdin_translations
|
||||
|
||||
23
.github/workflows/workers.yml
vendored
Normal file
23
.github/workflows/workers.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Deploy Cloudflare Worker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- canary
|
||||
paths:
|
||||
- tools/workers/**
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
environment: stable
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Publish
|
||||
uses: cloudflare/wrangler-action@v3.13.0
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
workingDirectory: 'tools/workers'
|
||||
packageManager: 'yarn'
|
||||
@@ -28,8 +28,6 @@ test-results
|
||||
# per files
|
||||
tools/cli/src/webpack/error-handler.js
|
||||
packages/backend/native/index.d.ts
|
||||
packages/backend/server/src/__tests__/__snapshots__
|
||||
packages/common/native/fixtures/**
|
||||
packages/frontend/native/index.d.ts
|
||||
packages/frontend/native/index.js
|
||||
packages/frontend/graphql/src/graphql/index.ts
|
||||
|
||||
23
.vscode/launch.template.json
vendored
23
.vscode/launch.template.json
vendored
@@ -7,11 +7,7 @@
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "yarn",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"runtimeArgs": [
|
||||
"affine",
|
||||
"@affine/server",
|
||||
"dev"
|
||||
]
|
||||
"runtimeArgs": ["affine", "@affine/server", "dev"]
|
||||
},
|
||||
{
|
||||
"name": "Lanuch AFFiNE Web",
|
||||
@@ -19,20 +15,7 @@
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "yarn",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"runtimeArgs": [
|
||||
"affine",
|
||||
"@affine/web",
|
||||
"dev"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Debug AFFiNE Web",
|
||||
"url": "http://localhost:8080",
|
||||
"sourceMapPathOverrides": {
|
||||
"webpack://affine/blocksuite/*": "${workspaceFolder}/blocksuite/*"
|
||||
}
|
||||
"runtimeArgs": ["affine", "@affine/web", "dev"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
1931
Cargo.lock
generated
1931
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
20
Cargo.toml
20
Cargo.toml
@@ -15,29 +15,23 @@ affine_common = { path = "./packages/common/native" }
|
||||
affine_nbstore = { path = "./packages/frontend/native/nbstore" }
|
||||
anyhow = "1"
|
||||
base64-simd = "0.8"
|
||||
block2 = "0.6"
|
||||
chrono = "0.4"
|
||||
core-foundation = "0.10"
|
||||
coreaudio-rs = "0.12"
|
||||
criterion2 = { version = "2", default-features = false }
|
||||
dispatch2 = "0.2"
|
||||
dashmap = "6"
|
||||
dotenvy = "0.15"
|
||||
file-format = { version = "0.26", features = ["reader"] }
|
||||
homedir = "0.3"
|
||||
libc = "0.2"
|
||||
mimalloc = "0.1"
|
||||
napi = { version = "3.0.0-alpha.12", features = ["async", "chrono_date", "error_anyhow", "napi9", "serde"] }
|
||||
napi-build = { version = "2" }
|
||||
napi-derive = { version = "3.0.0-alpha.12" }
|
||||
notify = { version = "8", features = ["serde"] }
|
||||
objc2 = "0.6"
|
||||
objc2-foundation = "0.3"
|
||||
notify = { version = "7", features = ["serde"] }
|
||||
objc2 = "0.5.2"
|
||||
objc2-foundation = "0.2.2"
|
||||
once_cell = "1"
|
||||
parking_lot = "0.12"
|
||||
rand = "0.9"
|
||||
homedir = "0.3"
|
||||
rand = "0.8"
|
||||
rayon = "1.10"
|
||||
rubato = "0.16"
|
||||
screencapturekit = "0.3"
|
||||
serde = "1"
|
||||
serde_json = "1"
|
||||
sha3 = "0.10"
|
||||
@@ -45,7 +39,7 @@ sqlx = { version = "0.8", default-features = false, features = ["chr
|
||||
thiserror = "2"
|
||||
tiktoken-rs = "0.6"
|
||||
tokio = "1.37"
|
||||
uniffi = "0.29"
|
||||
uniffi = "0.28"
|
||||
uuid = "1.8"
|
||||
v_htmlescape = "0.15"
|
||||
y-octo = { git = "https://github.com/y-crdt/y-octo.git", branch = "main" }
|
||||
|
||||
@@ -127,8 +127,6 @@ AFFiNE now provides pre-built [templates](https://affine.pro/templates) from our
|
||||
Welcome to the AFFiNE blog section! Here, you’ll find the latest insights, tips, and guides on how to maximize your experience with AFFiNE and AFFiNE AI, the leading Canvas AI tool for flexible note-taking and creative organization.
|
||||
|
||||
- [vision board template](https://affine.pro/blog/8-free-printable-vision-board-templates-examples-2023)
|
||||
- [ai homework helper](https://affine.pro/blog/ai-homework-helper)
|
||||
- [vision board maker](https://affine.pro/blog/vision-board-maker)
|
||||
- [itinerary template](https://affine.pro/blog/free-customized-travel-itinerary-planner-templates)
|
||||
- [one pager template](https://affine.pro/blog/top-12-one-pager-examples-how-to-create-your-own)
|
||||
- [cornell notes template](https://affine.pro/blog/the-cornell-notes-template-and-system-learning-tips)
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
"@blocksuite/blocks": "workspace:*",
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/inline": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@blocksuite/sync": "workspace:*"
|
||||
"@blocksuite/presets": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
@@ -32,13 +32,12 @@
|
||||
"./global/di": "./src/global/di.ts",
|
||||
"./global/types": "./src/global/types.ts",
|
||||
"./store": "./src/store/index.ts",
|
||||
"./store/test": "./src/store/test.ts",
|
||||
"./inline": "./src/inline/index.ts",
|
||||
"./inline/consts": "./src/inline/consts.ts",
|
||||
"./inline/types": "./src/inline/types.ts",
|
||||
"./presets": "./src/presets/index.ts",
|
||||
"./blocks": "./src/blocks/index.ts",
|
||||
"./blocks/schemas": "./src/blocks/schemas.ts",
|
||||
"./sync": "./src/sync/index.ts"
|
||||
"./blocks/schemas": "./src/blocks/schemas.ts"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
@@ -81,14 +80,14 @@
|
||||
"inline/types": [
|
||||
"dist/inline/types.d.ts"
|
||||
],
|
||||
"presets": [
|
||||
"dist/presets/index.d.ts"
|
||||
],
|
||||
"blocks": [
|
||||
"dist/blocks/index.d.ts"
|
||||
],
|
||||
"blocks/schemas": [
|
||||
"dist/blocks/schemas.d.ts"
|
||||
],
|
||||
"sync": [
|
||||
"dist/sync/index.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -98,5 +97,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.20.0"
|
||||
"version": "0.19.0"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { effects as blocksEffects } from '@blocksuite/blocks/effects';
|
||||
import { effects as presetsEffects } from '@blocksuite/presets/effects';
|
||||
|
||||
export function effects() {
|
||||
blocksEffects();
|
||||
presetsEffects();
|
||||
}
|
||||
|
||||
1
blocksuite/affine/all/src/presets/index.ts
Normal file
1
blocksuite/affine/all/src/presets/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from '@blocksuite/presets';
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-restricted-imports */
|
||||
|
||||
// oxlint-disable-next-line
|
||||
// @ts-ignore FIXME: typecheck error
|
||||
export * from '@blocksuite/store';
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
export {
|
||||
createAutoIncrementIdGenerator,
|
||||
TestDoc,
|
||||
TestMeta,
|
||||
TestWorkspace,
|
||||
} from '@blocksuite/store/test';
|
||||
@@ -1 +0,0 @@
|
||||
export * from '@blocksuite/sync';
|
||||
@@ -1,17 +1,20 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
||||
"rootDir": "./src/",
|
||||
"outDir": "./dist/",
|
||||
"noEmit": false
|
||||
},
|
||||
"include": ["./src"],
|
||||
"references": [
|
||||
{ "path": "../../framework/block-std" },
|
||||
{ "path": "../../blocks" },
|
||||
{ "path": "../../framework/global" },
|
||||
{ "path": "../../framework/inline" },
|
||||
{ "path": "../../framework/store" },
|
||||
{ "path": "../../framework/sync" }
|
||||
{
|
||||
"path": "../../framework"
|
||||
},
|
||||
{
|
||||
"path": "../../blocks"
|
||||
},
|
||||
{
|
||||
"path": "../../presets"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
30
blocksuite/affine/all/vitest.config.ts
Normal file
30
blocksuite/affine/all/vitest.config.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
esbuild: {
|
||||
target: 'es2018',
|
||||
},
|
||||
test: {
|
||||
globalSetup: '../../../scripts/vitest-global.js',
|
||||
include: ['src/__tests__/**/*.unit.spec.ts'],
|
||||
testTimeout: 1000,
|
||||
coverage: {
|
||||
provider: 'istanbul', // or 'c8'
|
||||
reporter: ['lcov'],
|
||||
reportsDirectory: '../../../.coverage/affine',
|
||||
},
|
||||
/**
|
||||
* Custom handler for console.log in tests.
|
||||
*
|
||||
* Return `false` to ignore the log.
|
||||
*/
|
||||
onConsoleLog(log, type) {
|
||||
if (log.includes('https://lit.dev/msg/dev-mode')) {
|
||||
return false;
|
||||
}
|
||||
console.warn(`Unexpected ${type} log`, log);
|
||||
throw new Error(log);
|
||||
},
|
||||
environment: 'happy-dom',
|
||||
},
|
||||
});
|
||||
@@ -20,14 +20,14 @@
|
||||
"@blocksuite/affine-shared": "workspace:*",
|
||||
"@blocksuite/block-std": "workspace:*",
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/icons": "^2.2.1",
|
||||
"@blocksuite/icons": "^2.1.75",
|
||||
"@blocksuite/inline": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.12",
|
||||
"file-type": "^20.0.0",
|
||||
"@toeverything/theme": "^1.1.3",
|
||||
"file-type": "^19.5.0",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"zod": "^3.23.8"
|
||||
@@ -42,5 +42,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.20.0"
|
||||
"version": "0.19.0"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption';
|
||||
import { HoverController } from '@blocksuite/affine-components/hover';
|
||||
import {
|
||||
AttachmentIcon16,
|
||||
getAttachmentFileIcon,
|
||||
getAttachmentFileIcons,
|
||||
} from '@blocksuite/affine-components/icons';
|
||||
import { Peekable } from '@blocksuite/affine-components/peek';
|
||||
import { toast } from '@blocksuite/affine-components/toast';
|
||||
@@ -11,12 +11,8 @@ import {
|
||||
type AttachmentBlockModel,
|
||||
AttachmentBlockStyles,
|
||||
} from '@blocksuite/affine-model';
|
||||
import {
|
||||
FileSizeLimitService,
|
||||
ThemeProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { humanFileSize } from '@blocksuite/affine-shared/utils';
|
||||
import { BlockSelection, TextSelection } from '@blocksuite/block-std';
|
||||
import { Slice } from '@blocksuite/store';
|
||||
import { flip, offset } from '@floating-ui/dom';
|
||||
import { html, nothing } from 'lit';
|
||||
@@ -25,24 +21,30 @@ import { classMap } from 'lit/directives/class-map.js';
|
||||
import { ref } from 'lit/directives/ref.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import type { AttachmentBlockService } from './attachment-service.js';
|
||||
import { AttachmentOptionsTemplate } from './components/options.js';
|
||||
import { AttachmentEmbedProvider } from './embed.js';
|
||||
import { styles } from './styles.js';
|
||||
import { checkAttachmentBlob, downloadAttachmentBlob } from './utils.js';
|
||||
|
||||
@Peekable()
|
||||
export class AttachmentBlockComponent extends CaptionedBlockComponent<AttachmentBlockModel> {
|
||||
export class AttachmentBlockComponent extends CaptionedBlockComponent<
|
||||
AttachmentBlockModel,
|
||||
AttachmentBlockService
|
||||
> {
|
||||
static override styles = styles;
|
||||
|
||||
protected _isDragging = false;
|
||||
|
||||
protected _isResizing = false;
|
||||
|
||||
protected _isSelected = false;
|
||||
|
||||
protected _whenHover: HoverController | null = new HoverController(
|
||||
this,
|
||||
({ abortController }) => {
|
||||
const selection = this.host.selection;
|
||||
const textSelection = selection.find(TextSelection);
|
||||
const textSelection = selection.find('text');
|
||||
if (
|
||||
!!textSelection &&
|
||||
(!!textSelection.to || !!textSelection.from.length)
|
||||
@@ -50,7 +52,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
||||
return null;
|
||||
}
|
||||
|
||||
const blockSelections = selection.filter(BlockSelection);
|
||||
const blockSelections = selection.filter('block');
|
||||
if (
|
||||
blockSelections.length > 1 ||
|
||||
(blockSelections.length === 1 &&
|
||||
@@ -83,14 +85,10 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
||||
margin: '18px 0px',
|
||||
});
|
||||
|
||||
private get _maxFileSize() {
|
||||
return this.std.store.get(FileSizeLimitService).maxFileSize;
|
||||
}
|
||||
|
||||
convertTo = () => {
|
||||
return this.std
|
||||
.get(AttachmentEmbedProvider)
|
||||
.convertTo(this.model, this._maxFileSize);
|
||||
.convertTo(this.model, this.service.maxFileSize);
|
||||
};
|
||||
|
||||
copy = () => {
|
||||
@@ -106,7 +104,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
||||
embedded = () => {
|
||||
return this.std
|
||||
.get(AttachmentEmbedProvider)
|
||||
.embedded(this.model, this._maxFileSize);
|
||||
.embedded(this.model, this.service.maxFileSize);
|
||||
};
|
||||
|
||||
open = () => {
|
||||
@@ -123,12 +121,12 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
||||
protected get embedView() {
|
||||
return this.std
|
||||
.get(AttachmentEmbedProvider)
|
||||
.render(this.model, this.blobUrl, this._maxFileSize);
|
||||
.render(this.model, this.blobUrl, this.service.maxFileSize);
|
||||
}
|
||||
|
||||
private _selectBlock() {
|
||||
const selectionManager = this.host.selection;
|
||||
const blockSelection = selectionManager.create(BlockSelection, {
|
||||
const blockSelection = selectionManager.create('block', {
|
||||
blockId: this.blockId,
|
||||
});
|
||||
selectionManager.setGroup('note', [blockSelection]);
|
||||
@@ -167,21 +165,25 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
||||
|
||||
// this is required to prevent iframe from capturing pointer events
|
||||
this.disposables.add(
|
||||
this.selected$.subscribe(selected => {
|
||||
this._showOverlay = this._isResizing || this._isDragging || !selected;
|
||||
this.std.selection.slots.changed.on(() => {
|
||||
this._isSelected =
|
||||
!!this.selected?.is('block') || !!this.selected?.is('surface');
|
||||
|
||||
this._showOverlay =
|
||||
this._isResizing || this._isDragging || !this._isSelected;
|
||||
})
|
||||
);
|
||||
// this is required to prevent iframe from capturing pointer events
|
||||
this.handleEvent('dragStart', () => {
|
||||
this._isDragging = true;
|
||||
this._showOverlay =
|
||||
this._isResizing || this._isDragging || !this.selected$.peek();
|
||||
this._isResizing || this._isDragging || !this._isSelected;
|
||||
});
|
||||
|
||||
this.handleEvent('dragEnd', () => {
|
||||
this._isDragging = false;
|
||||
this._showOverlay =
|
||||
this._isResizing || this._isDragging || !this.selected$.peek();
|
||||
this._isResizing || this._isDragging || !this._isSelected;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -218,7 +220,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
||||
const infoText = this.error ? 'File loading failed.' : humanFileSize(size);
|
||||
|
||||
const fileType = name.split('.').pop() ?? '';
|
||||
const FileTypeIcon = getAttachmentFileIcon(fileType);
|
||||
const FileTypeIcon = getAttachmentFileIcons(fileType);
|
||||
|
||||
const embedView = this.embedView;
|
||||
|
||||
@@ -226,6 +228,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
||||
<div
|
||||
${this._whenHover ? ref(this._whenHover.setReference) : nothing}
|
||||
class="affine-attachment-container"
|
||||
draggable="${this.blockDraggable ? 'true' : 'false'}"
|
||||
style=${this.containerStyleMap}
|
||||
>
|
||||
${embedView
|
||||
|
||||
@@ -35,7 +35,7 @@ export class AttachmentEdgelessBlockComponent extends toGfxBlockComponent(
|
||||
this.slots.elementResizeEnd.on(() => {
|
||||
this._isResizing = false;
|
||||
this._showOverlay =
|
||||
this._isResizing || this._isDragging || !this.selected$.peek();
|
||||
this._isResizing || this._isDragging || !this._isSelected;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,36 +1,42 @@
|
||||
import { SurfaceBlockModel } from '@blocksuite/affine-block-surface';
|
||||
import { FileDropConfigExtension } from '@blocksuite/affine-components/drop-indicator';
|
||||
import { FileDropConfigExtension } from '@blocksuite/affine-components/drag-indicator';
|
||||
import { AttachmentBlockSchema } from '@blocksuite/affine-model';
|
||||
import {
|
||||
FileSizeLimitService,
|
||||
TelemetryProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
|
||||
import {
|
||||
isInsideEdgelessEditor,
|
||||
matchModels,
|
||||
matchFlavours,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import { BlockService } from '@blocksuite/block-std';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||
|
||||
import { addAttachments, addSiblingAttachmentBlocks } from './utils.js';
|
||||
|
||||
// bytes.parse('2GB')
|
||||
const maxFileSize = 2147483648;
|
||||
|
||||
export class AttachmentBlockService extends BlockService {
|
||||
static override readonly flavour = AttachmentBlockSchema.model.flavour;
|
||||
|
||||
maxFileSize = maxFileSize;
|
||||
}
|
||||
|
||||
export const AttachmentDropOption = FileDropConfigExtension({
|
||||
flavour: AttachmentBlockSchema.model.flavour,
|
||||
onDrop: ({ files, targetModel, placement, point, std }) => {
|
||||
onDrop: ({ files, targetModel, place, point, std }) => {
|
||||
// generic attachment block for all files except images
|
||||
const attachmentFiles = files.filter(
|
||||
file => !file.type.startsWith('image/')
|
||||
);
|
||||
|
||||
if (!attachmentFiles.length) return false;
|
||||
|
||||
const maxFileSize = std.store.get(FileSizeLimitService).maxFileSize;
|
||||
|
||||
if (targetModel && !matchModels(targetModel, [SurfaceBlockModel])) {
|
||||
if (targetModel && !matchFlavours(targetModel, ['affine:surface'])) {
|
||||
addSiblingAttachmentBlocks(
|
||||
std.host,
|
||||
attachmentFiles,
|
||||
// TODO: use max file size from service
|
||||
maxFileSize,
|
||||
targetModel,
|
||||
placement
|
||||
place
|
||||
).catch(console.error);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
import {
|
||||
BlockViewExtension,
|
||||
type ExtensionType,
|
||||
FlavourExtension,
|
||||
} from '@blocksuite/block-std';
|
||||
import { literal } from 'lit/static-html.js';
|
||||
|
||||
import { AttachmentBlockNotionHtmlAdapterExtension } from './adapters/notion-html.js';
|
||||
import { AttachmentDropOption } from './attachment-service.js';
|
||||
import {
|
||||
AttachmentBlockService,
|
||||
AttachmentDropOption,
|
||||
} from './attachment-service.js';
|
||||
import {
|
||||
AttachmentEmbedConfigExtension,
|
||||
AttachmentEmbedService,
|
||||
@@ -11,6 +17,7 @@ import {
|
||||
|
||||
export const AttachmentBlockSpec: ExtensionType[] = [
|
||||
FlavourExtension('affine:attachment'),
|
||||
AttachmentBlockService,
|
||||
BlockViewExtension('affine:attachment', model => {
|
||||
return model.parent?.flavour === 'affine:surface'
|
||||
? literal`affine-edgeless-attachment`
|
||||
|
||||
@@ -2,6 +2,8 @@ import {
|
||||
CaptionIcon,
|
||||
DownloadIcon,
|
||||
EditIcon,
|
||||
MoreVerticalIcon,
|
||||
SmallArrowDownIcon,
|
||||
} from '@blocksuite/affine-components/icons';
|
||||
import { createLitPortal } from '@blocksuite/affine-components/portal';
|
||||
import {
|
||||
@@ -19,7 +21,6 @@ import {
|
||||
EMBED_CARD_WIDTH,
|
||||
} from '@blocksuite/affine-shared/consts';
|
||||
import { Bound } from '@blocksuite/global/utils';
|
||||
import { ArrowDownSmallIcon, MoreVerticalIcon } from '@blocksuite/icons/lit';
|
||||
import { flip, offset } from '@floating-ui/dom';
|
||||
import { html, nothing } from 'lit';
|
||||
import { join } from 'lit/directives/join.js';
|
||||
@@ -87,7 +88,7 @@ export function attachmentViewToggleMenu({
|
||||
<span style="text-transform: capitalize">${viewType}</span>
|
||||
view
|
||||
</div>
|
||||
${ArrowDownSmallIcon({ width: '16px', height: '16px' })}
|
||||
${SmallArrowDownIcon}
|
||||
</editor-icon-button>
|
||||
`}
|
||||
>
|
||||
@@ -199,12 +200,8 @@ export function AttachmentOptionsTemplate({
|
||||
<editor-menu-button
|
||||
.contentPadding=${'8px'}
|
||||
.button=${html`
|
||||
<editor-icon-button
|
||||
aria-label="More"
|
||||
.tooltip=${'More'}
|
||||
.iconSize=${'20px'}
|
||||
>
|
||||
${MoreVerticalIcon()}
|
||||
<editor-icon-button aria-label="More" .tooltip=${'More'}>
|
||||
${MoreVerticalIcon}
|
||||
</editor-icon-button>
|
||||
`}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AttachmentBlockComponent } from './attachment-block';
|
||||
import { AttachmentEdgelessBlockComponent } from './attachment-edgeless-block';
|
||||
import type { AttachmentBlockService } from './attachment-service';
|
||||
|
||||
export function effects() {
|
||||
customElements.define(
|
||||
@@ -8,3 +9,11 @@ export function effects() {
|
||||
);
|
||||
customElements.define('affine-attachment', AttachmentBlockComponent);
|
||||
}
|
||||
|
||||
declare global {
|
||||
namespace BlockSuite {
|
||||
interface BlockServices {
|
||||
'affine:attachment': AttachmentBlockService;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,18 @@
|
||||
import {
|
||||
type AttachmentBlockModel,
|
||||
type ImageBlockProps,
|
||||
MAX_IMAGE_WIDTH,
|
||||
import type {
|
||||
AttachmentBlockModel,
|
||||
ImageBlockProps,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { FileSizeLimitService } from '@blocksuite/affine-shared/services';
|
||||
import {
|
||||
readImageSize,
|
||||
transformModel,
|
||||
withTempBlobData,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import { type BlockStdScope, StdIdentifier } from '@blocksuite/block-std';
|
||||
import type { ExtensionType } from '@blocksuite/block-std';
|
||||
import { Extension } from '@blocksuite/block-std';
|
||||
import type { Container } from '@blocksuite/global/di';
|
||||
import { createIdentifier } from '@blocksuite/global/di';
|
||||
import { Bound } from '@blocksuite/global/utils';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
import { Extension } from '@blocksuite/store';
|
||||
import type { TemplateResult } from 'lit';
|
||||
import { html } from 'lit';
|
||||
|
||||
import { getAttachmentBlob } from './utils';
|
||||
|
||||
export type AttachmentEmbedConfig = {
|
||||
name: string;
|
||||
/**
|
||||
@@ -29,10 +22,7 @@ export type AttachmentEmbedConfig = {
|
||||
/**
|
||||
* The action will be executed when the 「Turn into embed view」 button is clicked.
|
||||
*/
|
||||
action?: (
|
||||
model: AttachmentBlockModel,
|
||||
std: BlockStdScope
|
||||
) => Promise<void> | void;
|
||||
action?: (model: AttachmentBlockModel) => Promise<void> | void;
|
||||
/**
|
||||
* The template will be used to render the embed view.
|
||||
*/
|
||||
@@ -67,9 +57,8 @@ export const AttachmentEmbedProvider = createIdentifier<AttachmentEmbedService>(
|
||||
);
|
||||
|
||||
export class AttachmentEmbedService extends Extension {
|
||||
private get _maxFileSize() {
|
||||
return this.std.store.get(FileSizeLimitService).maxFileSize;
|
||||
}
|
||||
// 10MB
|
||||
static MAX_EMBED_SIZE = 10 * 1024 * 1024;
|
||||
|
||||
get keys() {
|
||||
return this.configs.keys();
|
||||
@@ -79,11 +68,7 @@ export class AttachmentEmbedService extends Extension {
|
||||
return this.configs.values();
|
||||
}
|
||||
|
||||
get configs(): Map<string, AttachmentEmbedConfig> {
|
||||
return this.std.get(AttachmentEmbedConfigMapIdentifier);
|
||||
}
|
||||
|
||||
constructor(private readonly std: BlockStdScope) {
|
||||
constructor(private readonly configs: Map<string, AttachmentEmbedConfig>) {
|
||||
super();
|
||||
}
|
||||
|
||||
@@ -91,27 +76,35 @@ export class AttachmentEmbedService extends Extension {
|
||||
di.addImpl(AttachmentEmbedConfigMapIdentifier, provider =>
|
||||
provider.getAll(AttachmentEmbedConfigIdentifier)
|
||||
);
|
||||
di.addImpl(AttachmentEmbedProvider, this, [StdIdentifier]);
|
||||
di.addImpl(AttachmentEmbedProvider, AttachmentEmbedService, [
|
||||
AttachmentEmbedConfigMapIdentifier,
|
||||
]);
|
||||
}
|
||||
|
||||
// Converts to embed view.
|
||||
convertTo(model: AttachmentBlockModel, maxFileSize = this._maxFileSize) {
|
||||
convertTo(
|
||||
model: AttachmentBlockModel,
|
||||
maxFileSize = AttachmentEmbedService.MAX_EMBED_SIZE
|
||||
) {
|
||||
const config = this.values.find(config => config.check(model, maxFileSize));
|
||||
if (!config?.action) {
|
||||
if (!config || !config.action) {
|
||||
model.doc.updateBlock(model, { embed: true });
|
||||
return;
|
||||
}
|
||||
config.action(model, this.std)?.catch(console.error);
|
||||
config.action(model)?.catch(console.error);
|
||||
}
|
||||
|
||||
embedded(model: AttachmentBlockModel, maxFileSize = this._maxFileSize) {
|
||||
embedded(
|
||||
model: AttachmentBlockModel,
|
||||
maxFileSize = AttachmentEmbedService.MAX_EMBED_SIZE
|
||||
) {
|
||||
return this.values.some(config => config.check(model, maxFileSize));
|
||||
}
|
||||
|
||||
render(
|
||||
model: AttachmentBlockModel,
|
||||
blobUrl?: string,
|
||||
maxFileSize = this._maxFileSize
|
||||
maxFileSize = AttachmentEmbedService.MAX_EMBED_SIZE
|
||||
) {
|
||||
if (!model.embed || !blobUrl) return;
|
||||
|
||||
@@ -131,12 +124,7 @@ const embedConfig: AttachmentEmbedConfig[] = [
|
||||
check: model =>
|
||||
model.doc.schema.flavourSchemaMap.has('affine:image') &&
|
||||
model.type.startsWith('image/'),
|
||||
async action(model, std) {
|
||||
const component = std.view.getBlock(model.id);
|
||||
if (!component) return;
|
||||
|
||||
await turnIntoImageBlock(model);
|
||||
},
|
||||
action: model => turnIntoImageBlock(model),
|
||||
},
|
||||
{
|
||||
name: 'pdf',
|
||||
@@ -164,13 +152,7 @@ const embedConfig: AttachmentEmbedConfig[] = [
|
||||
check: (model, maxFileSize) =>
|
||||
model.type.startsWith('video/') && model.size <= maxFileSize,
|
||||
template: (_, blobUrl) =>
|
||||
html`<video
|
||||
style="max-height: max-content;"
|
||||
width="100%;"
|
||||
height="480"
|
||||
controls
|
||||
src=${blobUrl}
|
||||
></video>`,
|
||||
html`<video width="100%;" height="480" controls src=${blobUrl}></video>`,
|
||||
},
|
||||
{
|
||||
name: 'audio',
|
||||
@@ -184,7 +166,7 @@ const embedConfig: AttachmentEmbedConfig[] = [
|
||||
/**
|
||||
* Turn the attachment block into an image block.
|
||||
*/
|
||||
export async function turnIntoImageBlock(model: AttachmentBlockModel) {
|
||||
export function turnIntoImageBlock(model: AttachmentBlockModel) {
|
||||
if (!model.doc.schema.flavourSchemaMap.has('affine:image')) {
|
||||
console.error('The image flavour is not supported!');
|
||||
return;
|
||||
@@ -196,37 +178,15 @@ export async function turnIntoImageBlock(model: AttachmentBlockModel) {
|
||||
const { saveAttachmentData, getImageData } = withTempBlobData();
|
||||
saveAttachmentData(sourceId, { name: model.name });
|
||||
|
||||
let imageSize = model.sourceId ? getImageData(model.sourceId) : undefined;
|
||||
|
||||
const bounds = model.xywh
|
||||
? Bound.fromXYWH(model.deserializedXYWH)
|
||||
const imageConvertData = model.sourceId
|
||||
? getImageData(model.sourceId)
|
||||
: undefined;
|
||||
|
||||
if (bounds) {
|
||||
if (!imageSize?.width || !imageSize?.height) {
|
||||
const blob = await getAttachmentBlob(model);
|
||||
if (blob) {
|
||||
imageSize = await readImageSize(blob);
|
||||
}
|
||||
}
|
||||
|
||||
if (imageSize?.width && imageSize?.height) {
|
||||
const p = imageSize.height / imageSize.width;
|
||||
imageSize.width = Math.min(imageSize.width, MAX_IMAGE_WIDTH);
|
||||
imageSize.height = imageSize.width * p;
|
||||
bounds.w = imageSize.width;
|
||||
bounds.h = imageSize.height;
|
||||
}
|
||||
}
|
||||
|
||||
const others = bounds ? { xywh: bounds.serialize() } : undefined;
|
||||
|
||||
const imageProp: Partial<ImageBlockProps> = {
|
||||
sourceId,
|
||||
caption: model.caption,
|
||||
size: model.size,
|
||||
...imageSize,
|
||||
...others,
|
||||
...imageConvertData,
|
||||
};
|
||||
transformModel(model, 'affine:image', imageProp);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import type * as SurfaceEffects from '@blocksuite/affine-block-surface/effects';
|
||||
|
||||
declare type _GLOBAL_ = typeof SurfaceEffects;
|
||||
|
||||
export * from './adapters/notion-html';
|
||||
export * from './attachment-block';
|
||||
export * from './attachment-service';
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import {
|
||||
EMBED_CARD_HEIGHT,
|
||||
EMBED_CARD_WIDTH,
|
||||
} from '@blocksuite/affine-shared/consts';
|
||||
import { css } from 'lit';
|
||||
|
||||
export const styles = css`
|
||||
@@ -8,7 +12,7 @@ export const styles = css`
|
||||
gap: 12px;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
height: ${EMBED_CARD_HEIGHT.horizontalThin}px;
|
||||
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
@@ -117,6 +121,9 @@ export const styles = css`
|
||||
}
|
||||
|
||||
.affine-attachment-card.cubeThick {
|
||||
width: ${EMBED_CARD_WIDTH.cubeThick}px;
|
||||
height: ${EMBED_CARD_HEIGHT.cubeThick}px;
|
||||
|
||||
flex-direction: column-reverse;
|
||||
|
||||
.affine-attachment-content {
|
||||
|
||||
@@ -8,10 +8,7 @@ import {
|
||||
EMBED_CARD_HEIGHT,
|
||||
EMBED_CARD_WIDTH,
|
||||
} from '@blocksuite/affine-shared/consts';
|
||||
import {
|
||||
FileSizeLimitService,
|
||||
TelemetryProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
|
||||
import { humanFileSize } from '@blocksuite/affine-shared/utils';
|
||||
import type { BlockStdScope, EditorHost } from '@blocksuite/block-std';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||
@@ -97,7 +94,7 @@ export async function uploadAttachmentBlob(
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAttachmentBlob(model: AttachmentBlockModel) {
|
||||
async function getAttachmentBlob(model: AttachmentBlockModel) {
|
||||
const sourceId = model.sourceId;
|
||||
if (!sourceId) {
|
||||
return null;
|
||||
@@ -215,8 +212,7 @@ export async function addSiblingAttachmentBlocks(
|
||||
files: File[],
|
||||
maxFileSize: number,
|
||||
targetModel: BlockModel,
|
||||
place: 'before' | 'after' = 'after',
|
||||
isEmbed?: boolean
|
||||
place: 'before' | 'after' = 'after'
|
||||
) {
|
||||
if (!files.length) {
|
||||
return;
|
||||
@@ -246,7 +242,6 @@ export async function addSiblingAttachmentBlocks(
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
type: types[index],
|
||||
embed: isEmbed,
|
||||
}));
|
||||
|
||||
const blockIds = doc.addSiblingBlocks(
|
||||
@@ -266,13 +261,18 @@ export async function addSiblingAttachmentBlocks(
|
||||
export async function addAttachments(
|
||||
std: BlockStdScope,
|
||||
files: File[],
|
||||
point?: IVec,
|
||||
transformPoint?: boolean // determines whether we should use `toModelCoord` to convert the point
|
||||
point?: IVec
|
||||
): Promise<string[]> {
|
||||
if (!files.length) return [];
|
||||
|
||||
const attachmentService = std.getService('affine:attachment');
|
||||
const gfx = std.get(GfxControllerIdentifier);
|
||||
const maxFileSize = std.store.get(FileSizeLimitService).maxFileSize;
|
||||
|
||||
if (!attachmentService) {
|
||||
console.error('Attachment service not found');
|
||||
return [];
|
||||
}
|
||||
const maxFileSize = attachmentService.maxFileSize;
|
||||
const isSizeExceeded = files.some(file => file.size > maxFileSize);
|
||||
if (isSizeExceeded) {
|
||||
toast(
|
||||
@@ -287,14 +287,7 @@ export async function addAttachments(
|
||||
}
|
||||
|
||||
let { x, y } = gfx.viewport.center;
|
||||
if (point) {
|
||||
let transform = transformPoint ?? true;
|
||||
if (transform) {
|
||||
[x, y] = gfx.viewport.toModelCoord(...point);
|
||||
} else {
|
||||
[x, y] = point;
|
||||
}
|
||||
}
|
||||
if (point) [x, y] = gfx.viewport.toModelCoord(...point);
|
||||
|
||||
const CARD_STACK_GAP = 32;
|
||||
|
||||
@@ -310,7 +303,7 @@ export async function addAttachments(
|
||||
EMBED_CARD_WIDTH.cubeThick,
|
||||
EMBED_CARD_HEIGHT.cubeThick
|
||||
);
|
||||
const blockId = std.store.addBlock(
|
||||
const blockId = std.doc.addBlock(
|
||||
'affine:attachment',
|
||||
{
|
||||
name: file.name,
|
||||
|
||||
@@ -1,20 +1,29 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
||||
"rootDir": "./src/",
|
||||
"outDir": "./dist/",
|
||||
"noEmit": false
|
||||
},
|
||||
"include": ["./src"],
|
||||
"references": [
|
||||
{ "path": "../block-embed" },
|
||||
{ "path": "../block-surface" },
|
||||
{ "path": "../components" },
|
||||
{ "path": "../model" },
|
||||
{ "path": "../shared" },
|
||||
{ "path": "../../framework/block-std" },
|
||||
{ "path": "../../framework/global" },
|
||||
{ "path": "../../framework/inline" },
|
||||
{ "path": "../../framework/store" }
|
||||
{
|
||||
"path": "../../framework"
|
||||
},
|
||||
{
|
||||
"path": "../model"
|
||||
},
|
||||
{
|
||||
"path": "../components"
|
||||
},
|
||||
{
|
||||
"path": "../shared"
|
||||
},
|
||||
{
|
||||
"path": "../block-embed"
|
||||
},
|
||||
{
|
||||
"path": "../block-surface"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -19,13 +19,13 @@
|
||||
"@blocksuite/affine-shared": "workspace:*",
|
||||
"@blocksuite/block-std": "workspace:*",
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/icons": "^2.2.1",
|
||||
"@blocksuite/icons": "^2.1.75",
|
||||
"@blocksuite/inline": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.12",
|
||||
"@toeverything/theme": "^1.1.3",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"zod": "^3.23.8"
|
||||
@@ -40,5 +40,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.20.0"
|
||||
"version": "0.19.0"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
import type { ExtensionType } from '@blocksuite/block-std';
|
||||
|
||||
import { BookmarkBlockHtmlAdapterExtension } from './html.js';
|
||||
import { BookmarkBlockMarkdownAdapterExtension } from './markdown.js';
|
||||
|
||||
@@ -4,23 +4,20 @@ import {
|
||||
} from '@blocksuite/affine-components/caption';
|
||||
import type { BookmarkBlockModel } from '@blocksuite/affine-model';
|
||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||
import { html } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { type ClassInfo, classMap } from 'lit/directives/class-map.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import type { BookmarkBlockService } from './bookmark-service.js';
|
||||
import { refreshBookmarkUrlData } from './utils.js';
|
||||
|
||||
export const BOOKMARK_MIN_WIDTH = 450;
|
||||
|
||||
export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBlockModel> {
|
||||
selectedStyle$: ReadonlySignal<ClassInfo> | null = computed<ClassInfo>(
|
||||
() => ({
|
||||
'selected-style': this.selected$.value,
|
||||
})
|
||||
);
|
||||
|
||||
export class BookmarkBlockComponent extends CaptionedBlockComponent<
|
||||
BookmarkBlockModel,
|
||||
BookmarkBlockService
|
||||
> {
|
||||
private _fetchAbortController?: AbortController;
|
||||
|
||||
blockDraggable = true;
|
||||
@@ -76,12 +73,13 @@ export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBloc
|
||||
}
|
||||
|
||||
override renderBlock() {
|
||||
const selected = !!this.selected?.is('block');
|
||||
return html`
|
||||
<div
|
||||
draggable="${this.blockDraggable ? 'true' : 'false'}"
|
||||
class=${classMap({
|
||||
'affine-bookmark-container': true,
|
||||
...this.selectedStyle$?.value,
|
||||
'selected-style': selected,
|
||||
})}
|
||||
style=${this.containerStyleMap}
|
||||
>
|
||||
|
||||
@@ -3,15 +3,13 @@ import {
|
||||
EMBED_CARD_WIDTH,
|
||||
} from '@blocksuite/affine-shared/consts';
|
||||
import { toGfxBlockComponent } from '@blocksuite/block-std';
|
||||
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { BookmarkBlockComponent } from './bookmark-block.js';
|
||||
|
||||
export class BookmarkEdgelessBlockComponent extends toGfxBlockComponent(
|
||||
BookmarkBlockComponent
|
||||
) {
|
||||
override selectedStyle$ = null;
|
||||
|
||||
override blockDraggable = false;
|
||||
|
||||
override getRenderingRect() {
|
||||
@@ -45,9 +43,7 @@ export class BookmarkEdgelessBlockComponent extends toGfxBlockComponent(
|
||||
return this.renderPageContent();
|
||||
}
|
||||
|
||||
protected override accessor blockContainerStyles: StyleInfo = {
|
||||
height: '100%',
|
||||
};
|
||||
protected override accessor blockContainerStyles = {};
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
16
blocksuite/affine/block-bookmark/src/bookmark-service.ts
Normal file
16
blocksuite/affine/block-bookmark/src/bookmark-service.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { LinkPreviewer } from '@blocksuite/affine-block-embed';
|
||||
import { BookmarkBlockSchema } from '@blocksuite/affine-model';
|
||||
import { BlockService } from '@blocksuite/block-std';
|
||||
|
||||
export class BookmarkBlockService extends BlockService {
|
||||
static override readonly flavour = BookmarkBlockSchema.model.flavour;
|
||||
|
||||
private static readonly linkPreviewer = new LinkPreviewer();
|
||||
|
||||
static setLinkPreviewEndpoint =
|
||||
BookmarkBlockService.linkPreviewer.setEndpoint;
|
||||
|
||||
queryUrlData = (url: string, signal?: AbortSignal) => {
|
||||
return BookmarkBlockService.linkPreviewer.query(url, signal);
|
||||
};
|
||||
}
|
||||
@@ -1,11 +1,19 @@
|
||||
import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
import {
|
||||
BlockViewExtension,
|
||||
CommandExtension,
|
||||
type ExtensionType,
|
||||
FlavourExtension,
|
||||
} from '@blocksuite/block-std';
|
||||
import { literal } from 'lit/static-html.js';
|
||||
|
||||
import { BookmarkBlockAdapterExtensions } from './adapters/extension.js';
|
||||
import { BookmarkBlockService } from './bookmark-service.js';
|
||||
import { commands } from './commands/index.js';
|
||||
|
||||
export const BookmarkBlockSpec: ExtensionType[] = [
|
||||
FlavourExtension('affine:bookmark'),
|
||||
BookmarkBlockService,
|
||||
CommandExtension(commands),
|
||||
BlockViewExtension('affine:bookmark', model => {
|
||||
return model.parent?.flavour === 'affine:surface'
|
||||
? literal`affine-edgeless-bookmark`
|
||||
|
||||
@@ -1,2 +1,9 @@
|
||||
export { insertBookmarkCommand } from './insert-bookmark.js';
|
||||
export { insertLinkByQuickSearchCommand } from './insert-link-by-quick-search.js';
|
||||
import type { BlockCommands } from '@blocksuite/block-std';
|
||||
|
||||
import { insertBookmarkCommand } from './insert-bookmark.js';
|
||||
import { insertLinkByQuickSearchCommand } from './insert-link-by-quick-search.js';
|
||||
|
||||
export const commands: BlockCommands = {
|
||||
insertBookmark: insertBookmarkCommand,
|
||||
insertLinkByQuickSearch: insertLinkByQuickSearchCommand,
|
||||
};
|
||||
|
||||
@@ -5,7 +5,11 @@ import type { EmbedCardStyle } from '@blocksuite/affine-model';
|
||||
import { EmbedOptionProvider } from '@blocksuite/affine-shared/services';
|
||||
import type { Command } from '@blocksuite/block-std';
|
||||
|
||||
export const insertBookmarkCommand: Command<{ url: string }> = (ctx, next) => {
|
||||
export const insertBookmarkCommand: Command<
|
||||
never,
|
||||
'insertedLinkType',
|
||||
{ url: string }
|
||||
> = (ctx, next) => {
|
||||
const { url, std } = ctx;
|
||||
const embedOptions = std.get(EmbedOptionProvider).getEmbedBlockOptions(url);
|
||||
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
import {
|
||||
type InsertedLinkType,
|
||||
insertEmbedLinkedDocCommand,
|
||||
} from '@blocksuite/affine-block-embed';
|
||||
import type { InsertedLinkType } from '@blocksuite/affine-block-embed';
|
||||
import { QuickSearchProvider } from '@blocksuite/affine-shared/services';
|
||||
import type { Command } from '@blocksuite/block-std';
|
||||
|
||||
import { insertBookmarkCommand } from './insert-bookmark';
|
||||
|
||||
export const insertLinkByQuickSearchCommand: Command<
|
||||
{},
|
||||
{ insertedLinkType: Promise<InsertedLinkType> }
|
||||
never,
|
||||
'insertedLinkType'
|
||||
> = (ctx, next) => {
|
||||
const { std } = ctx;
|
||||
const quickSearchService = std.getOptional(QuickSearchProvider);
|
||||
@@ -25,7 +20,7 @@ export const insertLinkByQuickSearchCommand: Command<
|
||||
|
||||
// add linked doc
|
||||
if ('docId' in result) {
|
||||
std.command.exec(insertEmbedLinkedDocCommand, {
|
||||
std.command.exec('insertEmbedLinkedDoc', {
|
||||
docId: result.docId,
|
||||
params: result.params,
|
||||
});
|
||||
@@ -36,7 +31,7 @@ export const insertLinkByQuickSearchCommand: Command<
|
||||
|
||||
// add normal link;
|
||||
if ('externalUrl' in result) {
|
||||
std.command.exec(insertBookmarkCommand, { url: result.externalUrl });
|
||||
std.command.exec('insertBookmark', { url: result.externalUrl });
|
||||
return {
|
||||
flavour: 'affine:bookmark',
|
||||
};
|
||||
|
||||
@@ -2,11 +2,11 @@ import { getEmbedCardIcons } from '@blocksuite/affine-block-embed';
|
||||
import { WebIcon16 } from '@blocksuite/affine-components/icons';
|
||||
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { getHostName } from '@blocksuite/affine-shared/utils';
|
||||
import { BlockSelection, ShadowlessElement } from '@blocksuite/block-std';
|
||||
import { ShadowlessElement } from '@blocksuite/block-std';
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import { OpenInNewIcon } from '@blocksuite/icons/lit';
|
||||
import { html } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { property, state } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
|
||||
import type { BookmarkBlockComponent } from '../bookmark-block.js';
|
||||
@@ -31,7 +31,7 @@ export class BookmarkCard extends WithDisposable(ShadowlessElement) {
|
||||
|
||||
private _selectBlock() {
|
||||
const selectionManager = this.bookmark.host.selection;
|
||||
const blockSelection = selectionManager.create(BlockSelection, {
|
||||
const blockSelection = selectionManager.create('block', {
|
||||
blockId: this.bookmark.blockId,
|
||||
});
|
||||
selectionManager.setGroup('note', [blockSelection]);
|
||||
@@ -51,6 +51,14 @@ export class BookmarkCard extends WithDisposable(ShadowlessElement) {
|
||||
.get(ThemeProvider)
|
||||
.theme$.subscribe(() => this.requestUpdate())
|
||||
);
|
||||
|
||||
this.disposables.add(
|
||||
this.bookmark.selection.slots.changed.on(() => {
|
||||
this._isSelected =
|
||||
!!this.bookmark.selected?.is('block') ||
|
||||
!!this.bookmark.selected?.is('surface');
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
override render() {
|
||||
@@ -60,7 +68,7 @@ export class BookmarkCard extends WithDisposable(ShadowlessElement) {
|
||||
loading: this.loading,
|
||||
error: this.error,
|
||||
[style]: true,
|
||||
selected: this.bookmark.selected$.value,
|
||||
selected: this._isSelected,
|
||||
});
|
||||
|
||||
const domainName = url.match(
|
||||
@@ -136,6 +144,9 @@ export class BookmarkCard extends WithDisposable(ShadowlessElement) {
|
||||
`;
|
||||
}
|
||||
|
||||
@state()
|
||||
private accessor _isSelected = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor bookmark!: BookmarkBlockComponent;
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { stopPropagation } from '@blocksuite/affine-shared/utils';
|
||||
import { type BlockComponent, ShadowlessElement } from '@blocksuite/block-std';
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
@@ -49,9 +48,6 @@ export class EmbedCardEditCaptionEditModal extends WithDisposable(
|
||||
.catch(console.error);
|
||||
|
||||
this.disposables.addFromEvent(this, 'keydown', this._onKeydown);
|
||||
this.disposables.addFromEvent(this, 'cut', stopPropagation);
|
||||
this.disposables.addFromEvent(this, 'copy', stopPropagation);
|
||||
this.disposables.addFromEvent(this, 'paste', stopPropagation);
|
||||
}
|
||||
|
||||
override render() {
|
||||
@@ -1,15 +1,21 @@
|
||||
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
|
||||
import { toast } from '@blocksuite/affine-components/toast';
|
||||
import type { EmbedCardStyle } from '@blocksuite/affine-model';
|
||||
import {
|
||||
EMBED_CARD_HEIGHT,
|
||||
EMBED_CARD_WIDTH,
|
||||
} from '@blocksuite/affine-shared/consts';
|
||||
import { EmbedOptionProvider } from '@blocksuite/affine-shared/services';
|
||||
import { isValidUrl, stopPropagation } from '@blocksuite/affine-shared/utils';
|
||||
import { isValidUrl } from '@blocksuite/affine-shared/utils';
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import { ShadowlessElement } from '@blocksuite/block-std';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import { Bound, Vec, WithDisposable } from '@blocksuite/global/utils';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
import { html } from 'lit';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
|
||||
import { toast } from '../toast';
|
||||
import { embedCardModalStyles } from './styles.js';
|
||||
|
||||
export class EmbedCardCreateModal extends WithDisposable(ShadowlessElement) {
|
||||
@@ -49,13 +55,37 @@ export class EmbedCardCreateModal extends WithDisposable(ShadowlessElement) {
|
||||
index
|
||||
);
|
||||
} else if (mode === 'edgeless') {
|
||||
let flavour = 'affine:bookmark',
|
||||
targetStyle: EmbedCardStyle = 'vertical';
|
||||
|
||||
if (embedOptions) {
|
||||
flavour = embedOptions.flavour;
|
||||
targetStyle = embedOptions.styles[0];
|
||||
}
|
||||
|
||||
const gfx = this.host.std.get(GfxControllerIdentifier);
|
||||
const crud = this.host.std.get(EdgelessCRUDIdentifier);
|
||||
|
||||
const viewport = gfx.viewport;
|
||||
const surfaceModel = gfx.surface;
|
||||
if (!surfaceModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.createOptions.onSave(url);
|
||||
const center = Vec.toVec(viewport.center);
|
||||
crud.addBlock(
|
||||
flavour,
|
||||
{
|
||||
url,
|
||||
xywh: Bound.fromCenter(
|
||||
center,
|
||||
EMBED_CARD_WIDTH[targetStyle],
|
||||
EMBED_CARD_HEIGHT[targetStyle]
|
||||
).serialize(),
|
||||
style: targetStyle,
|
||||
},
|
||||
surfaceModel
|
||||
);
|
||||
|
||||
gfx.tool.setTool(
|
||||
// @ts-expect-error FIXME: resolve after gfx tool refactor
|
||||
@@ -92,9 +122,6 @@ export class EmbedCardCreateModal extends WithDisposable(ShadowlessElement) {
|
||||
})
|
||||
.catch(console.error);
|
||||
this.disposables.addFromEvent(this, 'keydown', this._onDocumentKeydown);
|
||||
this.disposables.addFromEvent(this, 'cut', stopPropagation);
|
||||
this.disposables.addFromEvent(this, 'copy', stopPropagation);
|
||||
this.disposables.addFromEvent(this, 'paste', stopPropagation);
|
||||
}
|
||||
|
||||
override render() {
|
||||
@@ -150,7 +177,6 @@ export class EmbedCardCreateModal extends WithDisposable(ShadowlessElement) {
|
||||
}
|
||||
| {
|
||||
mode: 'edgeless';
|
||||
onSave: (url: string) => void;
|
||||
};
|
||||
|
||||
@property({ attribute: false })
|
||||
@@ -181,7 +207,6 @@ export async function toggleEmbedCardCreateModal(
|
||||
}
|
||||
| {
|
||||
mode: 'edgeless';
|
||||
onSave: (url: string) => void;
|
||||
}
|
||||
): Promise<void> {
|
||||
host.selection.clear();
|
||||
@@ -1,8 +1,16 @@
|
||||
import type { AliasInfo, LinkableEmbedModel } from '@blocksuite/affine-model';
|
||||
import {
|
||||
EmbedLinkedDocBlockComponent,
|
||||
EmbedSyncedDocBlockComponent,
|
||||
} from '@blocksuite/affine-block-embed';
|
||||
import {
|
||||
notifyLinkedDocClearedAliases,
|
||||
notifyLinkedDocSwitchedToCard,
|
||||
} from '@blocksuite/affine-components/notification';
|
||||
import { toast } from '@blocksuite/affine-components/toast';
|
||||
import type { AliasInfo } from '@blocksuite/affine-model';
|
||||
import {
|
||||
EmbedLinkedDocModel,
|
||||
EmbedSyncedDocModel,
|
||||
isInternalEmbedModel,
|
||||
} from '@blocksuite/affine-model';
|
||||
import {
|
||||
type LinkEventType,
|
||||
@@ -29,7 +37,8 @@ import { choose } from 'lit/directives/choose.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { live } from 'lit/directives/live.js';
|
||||
|
||||
import { toast } from '../toast';
|
||||
import type { LinkableEmbedModel } from './type.js';
|
||||
import { isInternalEmbedModel } from './type.js';
|
||||
|
||||
export class EmbedCardEditModal extends SignalWatcher(
|
||||
WithDisposable(LitElement)
|
||||
@@ -162,8 +171,14 @@ export class EmbedCardEditModal extends SignalWatcher(
|
||||
|
||||
this.model.doc.updateBlock(this.model, { title: null, description: null });
|
||||
|
||||
this.onReset?.(std, blockComponent);
|
||||
if (
|
||||
this.isEmbedLinkedDocModel &&
|
||||
blockComponent instanceof EmbedLinkedDocBlockComponent
|
||||
) {
|
||||
blockComponent.refreshData();
|
||||
|
||||
notifyLinkedDocClearedAliases(std);
|
||||
}
|
||||
blockComponent.requestUpdate();
|
||||
|
||||
track(std, this.model, this.viewType, 'ResetedAlias', { control: 'reset' });
|
||||
@@ -192,7 +207,17 @@ export class EmbedCardEditModal extends SignalWatcher(
|
||||
const props: AliasInfo = { title };
|
||||
if (description) props.description = description;
|
||||
|
||||
this.onSave?.(std, blockComponent, props);
|
||||
if (
|
||||
this.isEmbedSyncedDocModel &&
|
||||
blockComponent instanceof EmbedSyncedDocBlockComponent
|
||||
) {
|
||||
blockComponent.convertToCard(props);
|
||||
|
||||
notifyLinkedDocSwitchedToCard(std);
|
||||
} else {
|
||||
this.model.doc.updateBlock(this.model, props);
|
||||
blockComponent.requestUpdate();
|
||||
}
|
||||
|
||||
track(std, this.model, this.viewType, 'SavedAlias', { control: 'save' });
|
||||
|
||||
@@ -254,7 +279,6 @@ export class EmbedCardEditModal extends SignalWatcher(
|
||||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
this.disposables.add(this.host.slots.unmounted.on(this._hide));
|
||||
this._updateInfo();
|
||||
}
|
||||
|
||||
@@ -281,9 +305,6 @@ export class EmbedCardEditModal extends SignalWatcher(
|
||||
this.disposables.add(listenClickAway(this, this._hide));
|
||||
this.disposables.addFromEvent(this, 'keydown', this._onKeydown);
|
||||
this.disposables.addFromEvent(this, 'pointerdown', stopPropagation);
|
||||
this.disposables.addFromEvent(this, 'cut', stopPropagation);
|
||||
this.disposables.addFromEvent(this, 'copy', stopPropagation);
|
||||
this.disposables.addFromEvent(this, 'paste', stopPropagation);
|
||||
|
||||
this.titleInput.focus();
|
||||
this.titleInput.select();
|
||||
@@ -368,20 +389,6 @@ export class EmbedCardEditModal extends SignalWatcher(
|
||||
@property({ attribute: false })
|
||||
accessor originalDocInfo: AliasInfo | undefined = undefined;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor onReset:
|
||||
| ((std: BlockStdScope, component: BlockComponent) => void)
|
||||
| undefined = undefined;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor onSave:
|
||||
| ((
|
||||
std: BlockStdScope,
|
||||
component: BlockComponent,
|
||||
props: AliasInfo
|
||||
) => void)
|
||||
| undefined = undefined;
|
||||
|
||||
accessor resetButtonDisabled$ = computed<boolean>(
|
||||
() =>
|
||||
!(
|
||||
@@ -407,13 +414,7 @@ export function toggleEmbedCardEditModal(
|
||||
host: EditorHost,
|
||||
embedCardModel: LinkableEmbedModel,
|
||||
viewType: string,
|
||||
originalDocInfo?: AliasInfo,
|
||||
onReset?: (std: BlockStdScope, component: BlockComponent) => void,
|
||||
onSave?: (
|
||||
std: BlockStdScope,
|
||||
component: BlockComponent,
|
||||
props: AliasInfo
|
||||
) => void
|
||||
originalDocInfo?: AliasInfo
|
||||
) {
|
||||
document.body.querySelector('embed-card-edit-modal')?.remove();
|
||||
|
||||
@@ -422,8 +423,6 @@ export function toggleEmbedCardEditModal(
|
||||
embedCardEditModal.host = host;
|
||||
embedCardEditModal.viewType = viewType;
|
||||
embedCardEditModal.originalDocInfo = originalDocInfo;
|
||||
embedCardEditModal.onReset = onReset;
|
||||
embedCardEditModal.onSave = onSave;
|
||||
document.body.append(embedCardEditModal);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './embed-card-caption-edit-modal';
|
||||
export * from './embed-card-create-modal';
|
||||
export * from './embed-card-edit-modal';
|
||||
export * from './type';
|
||||
@@ -1,4 +1,3 @@
|
||||
import { BookmarkBlockComponent } from '@blocksuite/affine-block-bookmark';
|
||||
import {
|
||||
EmbedFigmaBlockComponent,
|
||||
EmbedGithubBlockComponent,
|
||||
@@ -8,8 +7,22 @@ import {
|
||||
EmbedSyncedDocBlockComponent,
|
||||
EmbedYoutubeBlockComponent,
|
||||
} from '@blocksuite/affine-block-embed';
|
||||
import type {
|
||||
BookmarkBlockModel,
|
||||
EmbedFigmaModel,
|
||||
EmbedGithubModel,
|
||||
EmbedHtmlModel,
|
||||
EmbedLoomModel,
|
||||
EmbedYoutubeModel,
|
||||
} from '@blocksuite/affine-model';
|
||||
import {
|
||||
EmbedLinkedDocModel,
|
||||
EmbedSyncedDocModel,
|
||||
} from '@blocksuite/affine-model';
|
||||
import type { BlockComponent } from '@blocksuite/block-std';
|
||||
|
||||
import { BookmarkBlockComponent } from '../../bookmark-block';
|
||||
|
||||
export type ExternalEmbedBlockComponent =
|
||||
| BookmarkBlockComponent
|
||||
| EmbedFigmaBlockComponent
|
||||
@@ -29,6 +42,19 @@ export type BuiltInEmbedBlockComponent =
|
||||
| LinkableEmbedBlockComponent
|
||||
| EmbedHtmlBlockComponent;
|
||||
|
||||
export type ExternalEmbedModel =
|
||||
| BookmarkBlockModel
|
||||
| EmbedFigmaModel
|
||||
| EmbedGithubModel
|
||||
| EmbedLoomModel
|
||||
| EmbedYoutubeModel;
|
||||
|
||||
export type InternalEmbedModel = EmbedLinkedDocModel | EmbedSyncedDocModel;
|
||||
|
||||
export type LinkableEmbedModel = ExternalEmbedModel | InternalEmbedModel;
|
||||
|
||||
export type BuiltInEmbedModel = LinkableEmbedModel | EmbedHtmlModel;
|
||||
|
||||
export function isEmbedCardBlockComponent(
|
||||
block: BlockComponent
|
||||
): block is BuiltInEmbedBlockComponent {
|
||||
@@ -43,3 +69,11 @@ export function isEmbedCardBlockComponent(
|
||||
block instanceof EmbedSyncedDocBlockComponent
|
||||
);
|
||||
}
|
||||
|
||||
export function isInternalEmbedModel(
|
||||
model: BuiltInEmbedModel
|
||||
): model is InternalEmbedModel {
|
||||
return (
|
||||
model instanceof EmbedLinkedDocModel || model instanceof EmbedSyncedDocModel
|
||||
);
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export * from './bookmark-card';
|
||||
export * from './embed-card-modal';
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import { BookmarkBlockComponent } from './bookmark-block';
|
||||
import { BookmarkEdgelessBlockComponent } from './bookmark-edgeless-block';
|
||||
import type { BookmarkBlockService } from './bookmark-service';
|
||||
import type { insertBookmarkCommand } from './commands/insert-bookmark';
|
||||
import type { insertLinkByQuickSearchCommand } from './commands/insert-link-by-quick-search';
|
||||
import { BookmarkCard } from './components/bookmark-card';
|
||||
import {
|
||||
EmbedCardCreateModal,
|
||||
EmbedCardEditCaptionEditModal,
|
||||
EmbedCardEditModal,
|
||||
} from './components/embed-card-modal';
|
||||
|
||||
export function effects() {
|
||||
customElements.define(
|
||||
@@ -9,4 +17,23 @@ export function effects() {
|
||||
);
|
||||
customElements.define('affine-bookmark', BookmarkBlockComponent);
|
||||
customElements.define('bookmark-card', BookmarkCard);
|
||||
|
||||
customElements.define('embed-card-create-modal', EmbedCardCreateModal);
|
||||
customElements.define('embed-card-edit-modal', EmbedCardEditModal);
|
||||
customElements.define(
|
||||
'embed-card-caption-edit-modal',
|
||||
EmbedCardEditCaptionEditModal
|
||||
);
|
||||
}
|
||||
|
||||
declare global {
|
||||
namespace BlockSuite {
|
||||
interface Commands {
|
||||
insertBookmark: typeof insertBookmarkCommand;
|
||||
insertLinkByQuickSearch: typeof insertLinkByQuickSearchCommand;
|
||||
}
|
||||
interface BlockServices {
|
||||
'affine:bookmark': BookmarkBlockService;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export * from './adapters';
|
||||
export * from './bookmark-block';
|
||||
export * from './bookmark-service';
|
||||
export * from './bookmark-spec';
|
||||
export * from './commands';
|
||||
export * from './components';
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import {
|
||||
EMBED_CARD_HEIGHT,
|
||||
EMBED_CARD_WIDTH,
|
||||
} from '@blocksuite/affine-shared/consts';
|
||||
import { unsafeCSSVar } from '@blocksuite/affine-shared/theme';
|
||||
import { baseTheme } from '@toeverything/theme';
|
||||
import { css, unsafeCSS } from 'lit';
|
||||
|
||||
export const styles = css`
|
||||
bookmark-card {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.affine-bookmark-card {
|
||||
container: affine-bookmark-card / inline-size;
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: ${EMBED_CARD_HEIGHT.horizontal}px;
|
||||
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--affine-background-tertiary-color);
|
||||
@@ -188,6 +187,8 @@ export const styles = css`
|
||||
}
|
||||
|
||||
.affine-bookmark-card.list {
|
||||
height: ${EMBED_CARD_HEIGHT.list}px;
|
||||
|
||||
.affine-bookmark-content {
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
@@ -214,8 +215,9 @@ export const styles = css`
|
||||
}
|
||||
|
||||
.affine-bookmark-card.vertical {
|
||||
width: ${EMBED_CARD_WIDTH.vertical}px;
|
||||
height: ${EMBED_CARD_HEIGHT.vertical}px;
|
||||
flex-direction: column-reverse;
|
||||
height: 100%;
|
||||
|
||||
.affine-bookmark-content {
|
||||
width: 100%;
|
||||
@@ -246,6 +248,9 @@ export const styles = css`
|
||||
}
|
||||
|
||||
.affine-bookmark-card.cube {
|
||||
width: ${EMBED_CARD_WIDTH.cube}px;
|
||||
height: ${EMBED_CARD_HEIGHT.cube}px;
|
||||
|
||||
.affine-bookmark-content {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LinkPreviewerService } from '@blocksuite/affine-shared/services';
|
||||
import { isAbortError } from '@blocksuite/affine-shared/utils';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
|
||||
import type { BookmarkBlockComponent } from './bookmark-block.js';
|
||||
|
||||
@@ -15,8 +15,10 @@ export async function refreshBookmarkUrlData(
|
||||
try {
|
||||
bookmarkElement.loading = true;
|
||||
|
||||
const linkPreviewer = bookmarkElement.doc.get(LinkPreviewerService);
|
||||
const bookmarkUrlData = await linkPreviewer.query(
|
||||
const queryUrlData = bookmarkElement.service?.queryUrlData;
|
||||
assertExists(queryUrlData);
|
||||
|
||||
const bookmarkUrlData = await queryUrlData(
|
||||
bookmarkElement.model.url,
|
||||
signal
|
||||
);
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
||||
"rootDir": "./src/",
|
||||
"outDir": "./dist/",
|
||||
"noEmit": false
|
||||
},
|
||||
"include": ["./src"],
|
||||
"references": [
|
||||
{ "path": "../block-embed" },
|
||||
{ "path": "../components" },
|
||||
{ "path": "../model" },
|
||||
{ "path": "../shared" },
|
||||
{ "path": "../../framework/block-std" },
|
||||
{ "path": "../../framework/global" },
|
||||
{ "path": "../../framework/inline" },
|
||||
{ "path": "../../framework/store" }
|
||||
{
|
||||
"path": "../../framework"
|
||||
},
|
||||
{
|
||||
"path": "../model"
|
||||
},
|
||||
{
|
||||
"path": "../components"
|
||||
},
|
||||
{
|
||||
"path": "../shared"
|
||||
},
|
||||
{
|
||||
"path": "../block-embed"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -18,17 +18,16 @@
|
||||
"@blocksuite/affine-shared": "workspace:*",
|
||||
"@blocksuite/block-std": "workspace:*",
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/icons": "^2.2.3",
|
||||
"@blocksuite/inline": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.12",
|
||||
"@toeverything/theme": "^1.1.3",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"shiki": "^2.0.0",
|
||||
"shiki": "^1.14.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"exports": {
|
||||
@@ -41,5 +40,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.20.0"
|
||||
"version": "0.19.0"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
import type { ExtensionType } from '@blocksuite/block-std';
|
||||
|
||||
import { CodeBlockHtmlAdapterExtension } from './html.js';
|
||||
import { CodeBlockMarkdownAdapterExtension } from './markdown.js';
|
||||
|
||||
@@ -2,7 +2,6 @@ import { CodeBlockSchema } from '@blocksuite/affine-model';
|
||||
import {
|
||||
BlockHtmlAdapterExtension,
|
||||
type BlockHtmlAdapterMatcher,
|
||||
CODE_BLOCK_WRAP_KEY,
|
||||
HastUtils,
|
||||
} from '@blocksuite/affine-shared/adapters';
|
||||
import type { DeltaInsert } from '@blocksuite/inline';
|
||||
@@ -38,8 +37,7 @@ export const codeBlockHtmlAdapterMatcher: BlockHtmlAdapterMatcher = {
|
||||
? codeLang.replace('code-', '')
|
||||
: undefined;
|
||||
|
||||
const { walkerContext, deltaConverter, configs } = context;
|
||||
const wrap = configs.get(CODE_BLOCK_WRAP_KEY) === 'true';
|
||||
const { walkerContext, deltaConverter } = context;
|
||||
walkerContext
|
||||
.openNode(
|
||||
{
|
||||
@@ -48,7 +46,6 @@ export const codeBlockHtmlAdapterMatcher: BlockHtmlAdapterMatcher = {
|
||||
flavour: 'affine:code',
|
||||
props: {
|
||||
language: codeLang ?? 'Plain Text',
|
||||
wrap,
|
||||
text: {
|
||||
'$blocksuite:internal:text$': true,
|
||||
delta: deltaConverter.astToDelta(codeText, {
|
||||
|
||||
@@ -2,7 +2,6 @@ import { CodeBlockSchema } from '@blocksuite/affine-model';
|
||||
import {
|
||||
BlockMarkdownAdapterExtension,
|
||||
type BlockMarkdownAdapterMatcher,
|
||||
CODE_BLOCK_WRAP_KEY,
|
||||
type MarkdownAST,
|
||||
} from '@blocksuite/affine-shared/adapters';
|
||||
import type { DeltaInsert } from '@blocksuite/inline';
|
||||
@@ -20,8 +19,7 @@ export const codeBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher = {
|
||||
if (!isCodeNode(o.node)) {
|
||||
return;
|
||||
}
|
||||
const { walkerContext, configs } = context;
|
||||
const wrap = configs.get(CODE_BLOCK_WRAP_KEY) === 'true';
|
||||
const { walkerContext } = context;
|
||||
walkerContext
|
||||
.openNode(
|
||||
{
|
||||
@@ -30,7 +28,6 @@ export const codeBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher = {
|
||||
flavour: 'affine:code',
|
||||
props: {
|
||||
language: o.node.lang ?? 'Plain Text',
|
||||
wrap,
|
||||
text: {
|
||||
'$blocksuite:internal:text$': true,
|
||||
delta: [
|
||||
|
||||
@@ -2,7 +2,6 @@ import { CodeBlockSchema } from '@blocksuite/affine-model';
|
||||
import {
|
||||
BlockNotionHtmlAdapterExtension,
|
||||
type BlockNotionHtmlAdapterMatcher,
|
||||
CODE_BLOCK_WRAP_KEY,
|
||||
HastUtils,
|
||||
} from '@blocksuite/affine-shared/adapters';
|
||||
import { nanoid } from '@blocksuite/store';
|
||||
@@ -21,8 +20,7 @@ export const codeBlockNotionHtmlAdapterMatcher: BlockNotionHtmlAdapterMatcher =
|
||||
if (!code) {
|
||||
return;
|
||||
}
|
||||
const { walkerContext, deltaConverter, configs } = context;
|
||||
const wrap = configs.get(CODE_BLOCK_WRAP_KEY) === 'true';
|
||||
const { walkerContext, deltaConverter } = context;
|
||||
const codeText =
|
||||
code.children.length === 1 && code.children[0].type === 'text'
|
||||
? code.children[0]
|
||||
@@ -35,7 +33,6 @@ export const codeBlockNotionHtmlAdapterMatcher: BlockNotionHtmlAdapterMatcher =
|
||||
flavour: CodeBlockSchema.model.flavour,
|
||||
props: {
|
||||
language: 'Plain Text',
|
||||
wrap,
|
||||
text: {
|
||||
'$blocksuite:internal:text$': true,
|
||||
delta: deltaConverter.astToDelta(codeText, {
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import { deleteTextCommand } from '@blocksuite/affine-components/rich-text';
|
||||
import {
|
||||
HtmlAdapter,
|
||||
pasteMiddleware,
|
||||
PlainTextAdapter,
|
||||
} from '@blocksuite/affine-shared/adapters';
|
||||
import {
|
||||
getBlockIndexCommand,
|
||||
getBlockSelectionsCommand,
|
||||
getTextSelectionCommand,
|
||||
} from '@blocksuite/affine-shared/commands';
|
||||
import {
|
||||
type BlockComponent,
|
||||
Clipboard,
|
||||
@@ -42,17 +36,17 @@ export class CodeClipboardController {
|
||||
const e = ctx.get('clipboardState').raw;
|
||||
e.preventDefault();
|
||||
|
||||
this._std.store.captureSync();
|
||||
this._std.doc.captureSync();
|
||||
this._std.command
|
||||
.chain()
|
||||
.try(cmd => [
|
||||
cmd.pipe(getTextSelectionCommand).pipe((ctx, next) => {
|
||||
cmd.getTextSelection().inline<'currentSelectionPath'>((ctx, next) => {
|
||||
const textSelection = ctx.currentTextSelection;
|
||||
if (!textSelection) return;
|
||||
const end = textSelection.to ?? textSelection.from;
|
||||
next({ currentSelectionPath: end.blockId });
|
||||
}),
|
||||
cmd.pipe(getBlockSelectionsCommand).pipe((ctx, next) => {
|
||||
cmd.getBlockSelections().inline<'currentSelectionPath'>((ctx, next) => {
|
||||
const currentBlockSelections = ctx.currentBlockSelections;
|
||||
if (!currentBlockSelections) return;
|
||||
const blockSelection = currentBlockSelections.at(-1);
|
||||
@@ -60,16 +54,16 @@ export class CodeClipboardController {
|
||||
next({ currentSelectionPath: blockSelection.blockId });
|
||||
}),
|
||||
])
|
||||
.pipe(getBlockIndexCommand)
|
||||
.try(cmd => [cmd.pipe(getTextSelectionCommand).pipe(deleteTextCommand)])
|
||||
.pipe((ctx, next) => {
|
||||
.getBlockIndex()
|
||||
.try(cmd => [cmd.getTextSelection().deleteText()])
|
||||
.inline((ctx, next) => {
|
||||
if (!ctx.parentBlock) {
|
||||
return;
|
||||
}
|
||||
this._clipboard
|
||||
.paste(
|
||||
e,
|
||||
this._std.store,
|
||||
this._std.doc,
|
||||
ctx.parentBlock.model.id,
|
||||
ctx.blockIndex ? ctx.blockIndex + 1 : 1
|
||||
)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ConfigExtensionFactory } from '@blocksuite/block-std';
|
||||
import type { BundledLanguageInfo, ThemeInput } from 'shiki';
|
||||
|
||||
export interface CodeBlockConfig {
|
||||
@@ -14,6 +13,3 @@ export interface CodeBlockConfig {
|
||||
*/
|
||||
showLineNumbers?: boolean;
|
||||
}
|
||||
|
||||
export const CodeBlockConfigExtension =
|
||||
ConfigExtensionFactory<CodeBlockConfig>('affine:code');
|
||||
|
||||
@@ -6,13 +6,11 @@ import { type Signal, signal } from '@preact/signals-core';
|
||||
import {
|
||||
bundledLanguagesInfo,
|
||||
createHighlighterCore,
|
||||
createOnigurumaEngine,
|
||||
type HighlighterCore,
|
||||
type MaybeGetter,
|
||||
} from 'shiki';
|
||||
import getWasm from 'shiki/wasm';
|
||||
|
||||
import { CodeBlockConfigExtension } from './code-block-config.js';
|
||||
import {
|
||||
CODE_BLOCK_DEFAULT_DARK_THEME,
|
||||
CODE_BLOCK_DEFAULT_LIGHT_THEME,
|
||||
@@ -28,10 +26,7 @@ export class CodeBlockService extends BlockService {
|
||||
highlighter$: Signal<HighlighterCore | null> = signal(null);
|
||||
|
||||
get langs() {
|
||||
return (
|
||||
this.std.getOptional(CodeBlockConfigExtension.identifier)?.langs ??
|
||||
bundledLanguagesInfo
|
||||
);
|
||||
return this.std.getConfig('affine:code')?.langs ?? bundledLanguagesInfo;
|
||||
}
|
||||
|
||||
get themeKey() {
|
||||
@@ -47,12 +42,10 @@ export class CodeBlockService extends BlockService {
|
||||
this.bindHotKey(textKeymap(this.std));
|
||||
|
||||
createHighlighterCore({
|
||||
engine: createOnigurumaEngine(() => getWasm),
|
||||
loadWasm: getWasm,
|
||||
})
|
||||
.then(async highlighter => {
|
||||
const config = this.std.getOptional(
|
||||
CodeBlockConfigExtension.identifier
|
||||
);
|
||||
const config = this.std.getConfig('affine:code');
|
||||
const darkTheme = config?.theme?.dark ?? CODE_BLOCK_DEFAULT_DARK_THEME;
|
||||
const lightTheme =
|
||||
config?.theme?.light ?? CODE_BLOCK_DEFAULT_LIGHT_THEME;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
BlockViewExtension,
|
||||
type ExtensionType,
|
||||
FlavourExtension,
|
||||
WidgetViewExtension,
|
||||
WidgetViewMapExtension,
|
||||
} from '@blocksuite/block-std';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
import { literal, unsafeStatic } from 'lit/static-html.js';
|
||||
|
||||
import { CodeBlockAdapterExtensions } from './adapters/extension.js';
|
||||
@@ -14,17 +14,13 @@ import {
|
||||
import { CodeBlockService } from './code-block-service.js';
|
||||
import { AFFINE_CODE_TOOLBAR_WIDGET } from './code-toolbar/index.js';
|
||||
|
||||
export const codeToolbarWidget = WidgetViewExtension(
|
||||
'affine:code',
|
||||
AFFINE_CODE_TOOLBAR_WIDGET,
|
||||
literal`${unsafeStatic(AFFINE_CODE_TOOLBAR_WIDGET)}`
|
||||
);
|
||||
|
||||
export const CodeBlockSpec: ExtensionType[] = [
|
||||
FlavourExtension('affine:code'),
|
||||
CodeBlockService,
|
||||
BlockViewExtension('affine:code', literal`affine-code`),
|
||||
codeToolbarWidget,
|
||||
WidgetViewMapExtension('affine:code', {
|
||||
codeToolbar: literal`${unsafeStatic(AFFINE_CODE_TOOLBAR_WIDGET)}`,
|
||||
}),
|
||||
CodeBlockInlineManagerExtension,
|
||||
CodeBlockUnitSpecExtension,
|
||||
CodeBlockAdapterExtensions,
|
||||
|
||||
@@ -11,12 +11,8 @@ import {
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { getViewportElement } from '@blocksuite/affine-shared/utils';
|
||||
import type { BlockComponent } from '@blocksuite/block-std';
|
||||
import {
|
||||
BlockSelection,
|
||||
getInlineRangeProvider,
|
||||
TextSelection,
|
||||
} from '@blocksuite/block-std';
|
||||
import { IS_MAC, IS_MOBILE } from '@blocksuite/global/env';
|
||||
import { getInlineRangeProvider } from '@blocksuite/block-std';
|
||||
import { IS_MAC } from '@blocksuite/global/env';
|
||||
import { noop } from '@blocksuite/global/utils';
|
||||
import {
|
||||
INLINE_ROOT_ATTR,
|
||||
@@ -32,7 +28,6 @@ import { classMap } from 'lit/directives/class-map.js';
|
||||
import type { ThemedToken } from 'shiki';
|
||||
|
||||
import { CodeClipboardController } from './clipboard/index.js';
|
||||
import { CodeBlockConfigExtension } from './code-block-config.js';
|
||||
import { CodeBlockInlineManagerExtension } from './code-block-inline.js';
|
||||
import type { CodeBlockService } from './code-block-service.js';
|
||||
import { codeBlockStyles } from './styles.js';
|
||||
@@ -183,7 +178,7 @@ export class CodeBlockComponent extends CaptionedBlockComponent<
|
||||
this.bindHotKey({
|
||||
Backspace: ctx => {
|
||||
const state = ctx.get('keyboardState');
|
||||
const textSelection = selectionManager.find(TextSelection);
|
||||
const textSelection = selectionManager.find('text');
|
||||
if (!textSelection) {
|
||||
state.raw.preventDefault();
|
||||
return;
|
||||
@@ -194,7 +189,7 @@ export class CodeBlockComponent extends CaptionedBlockComponent<
|
||||
if (from.index === 0 && from.length === 0) {
|
||||
state.raw.preventDefault();
|
||||
selectionManager.setGroup('note', [
|
||||
selectionManager.create(BlockSelection, { blockId: this.blockId }),
|
||||
selectionManager.create('block', { blockId: this.blockId }),
|
||||
]);
|
||||
return true;
|
||||
}
|
||||
@@ -384,14 +379,12 @@ export class CodeBlockComponent extends CaptionedBlockComponent<
|
||||
|
||||
override renderBlock(): TemplateResult<1> {
|
||||
const showLineNumbers =
|
||||
this.std.getOptional(CodeBlockConfigExtension.identifier)
|
||||
?.showLineNumbers ?? true;
|
||||
this.std.getConfig('affine:code')?.showLineNumbers ?? true;
|
||||
|
||||
return html`
|
||||
<div
|
||||
class=${classMap({
|
||||
'affine-code-block-container': true,
|
||||
mobile: IS_MOBILE,
|
||||
wrap: this.model.wrap,
|
||||
})}
|
||||
>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { MoreVerticalIcon } from '@blocksuite/affine-components/icons';
|
||||
import { createLitPortal } from '@blocksuite/affine-components/portal';
|
||||
import type {
|
||||
EditorIconButton,
|
||||
@@ -5,7 +6,6 @@ import type {
|
||||
} from '@blocksuite/affine-components/toolbar';
|
||||
import { renderGroups } from '@blocksuite/affine-components/toolbar';
|
||||
import { noop, WithDisposable } from '@blocksuite/global/utils';
|
||||
import { MoreVerticalIcon } from '@blocksuite/icons/lit';
|
||||
import { flip, offset } from '@floating-ui/dom';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
@@ -17,19 +17,15 @@ export class AffineCodeToolbar extends WithDisposable(LitElement) {
|
||||
static override styles = css`
|
||||
:host {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.code-toolbar-container {
|
||||
width: auto;
|
||||
height: 24px;
|
||||
gap: 4px;
|
||||
padding: 4px;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.code-toolbar-button {
|
||||
@@ -132,7 +128,7 @@ export class AffineCodeToolbar extends WithDisposable(LitElement) {
|
||||
?disabled=${this.context.doc.readonly}
|
||||
@click=${() => this._toggleMoreMenu()}
|
||||
>
|
||||
${MoreVerticalIcon()}
|
||||
${MoreVerticalIcon}
|
||||
</editor-icon-button>
|
||||
</editor-toolbar>
|
||||
`;
|
||||
|
||||
@@ -17,10 +17,6 @@ export class LanguageListButton extends WithDisposable(
|
||||
SignalWatcher(LitElement)
|
||||
) {
|
||||
static override styles = css`
|
||||
:host {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.lang-button {
|
||||
background-color: var(--affine-background-primary-color);
|
||||
box-shadow: var(--affine-shadow-1);
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
} from '@blocksuite/affine-components/icons';
|
||||
import type { MenuItemGroup } from '@blocksuite/affine-components/toolbar';
|
||||
import { isInsidePageEditor } from '@blocksuite/affine-shared/utils';
|
||||
import { BlockSelection } from '@blocksuite/block-std';
|
||||
import { noop, sleep } from '@blocksuite/global/utils';
|
||||
import { html } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
@@ -135,7 +134,7 @@ export const clipboardGroup: MenuItemGroup<CodeBlockToolbarContext> = {
|
||||
host.updateComplete
|
||||
.then(() => {
|
||||
host.selection.setGroup('note', [
|
||||
host.selection.create(BlockSelection, {
|
||||
host.selection.create('block', {
|
||||
blockId: codeId,
|
||||
}),
|
||||
]);
|
||||
|
||||
@@ -9,12 +9,8 @@ import {
|
||||
} from '@blocksuite/affine-components/toolbar';
|
||||
import type { CodeBlockModel } from '@blocksuite/affine-model';
|
||||
import { PAGE_HEADER_HEIGHT } from '@blocksuite/affine-shared/consts';
|
||||
import {
|
||||
BlockSelection,
|
||||
TextSelection,
|
||||
WidgetComponent,
|
||||
} from '@blocksuite/block-std';
|
||||
import { limitShift, shift, size } from '@floating-ui/dom';
|
||||
import { WidgetComponent } from '@blocksuite/block-std';
|
||||
import { limitShift, shift } from '@floating-ui/dom';
|
||||
import { html } from 'lit';
|
||||
|
||||
import type { CodeBlockComponent } from '../code-block.js';
|
||||
@@ -38,7 +34,7 @@ export class AffineCodeToolbarWidget extends WidgetComponent<
|
||||
const codeBlock = this.block;
|
||||
const selection = this.host.selection;
|
||||
|
||||
const textSelection = selection.find(TextSelection);
|
||||
const textSelection = selection.find('text');
|
||||
if (
|
||||
!!textSelection &&
|
||||
(!!textSelection.to || !!textSelection.from.length)
|
||||
@@ -46,7 +42,7 @@ export class AffineCodeToolbarWidget extends WidgetComponent<
|
||||
return null;
|
||||
}
|
||||
|
||||
const blockSelections = selection.filter(BlockSelection);
|
||||
const blockSelections = selection.filter('block');
|
||||
if (
|
||||
blockSelections.length > 1 ||
|
||||
(blockSelections.length === 1 &&
|
||||
@@ -82,13 +78,8 @@ export class AffineCodeToolbarWidget extends WidgetComponent<
|
||||
},
|
||||
computePosition: {
|
||||
referenceElement: codeBlock,
|
||||
placement: 'top',
|
||||
placement: 'right-start',
|
||||
middleware: [
|
||||
size({
|
||||
apply({ rects, elements }) {
|
||||
elements.floating.style.width = `${rects.reference.width}px`;
|
||||
},
|
||||
}),
|
||||
shift({
|
||||
crossAxis: true,
|
||||
padding: {
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import type * as CommandsType from '@blocksuite/affine-shared/commands';
|
||||
|
||||
import { CodeBlockComponent } from './code-block';
|
||||
import type { CodeBlockConfig } from './code-block-config';
|
||||
import {
|
||||
AFFINE_CODE_TOOLBAR_WIDGET,
|
||||
AffineCodeToolbarWidget,
|
||||
@@ -15,7 +18,15 @@ export function effects() {
|
||||
customElements.define('affine-code', CodeBlockComponent);
|
||||
}
|
||||
|
||||
declare type _GLOBAL_ = typeof CommandsType;
|
||||
|
||||
declare global {
|
||||
namespace BlockSuite {
|
||||
interface BlockConfigs {
|
||||
'affine:code': CodeBlockConfig;
|
||||
}
|
||||
}
|
||||
|
||||
interface HTMLElementTagNameMap {
|
||||
'language-list-button': LanguageListButton;
|
||||
'affine-code-toolbar': AffineCodeToolbar;
|
||||
|
||||
@@ -9,16 +9,12 @@ export const codeBlockStyles = css`
|
||||
font-size: var(--affine-font-xs);
|
||||
line-height: var(--affine-line-height);
|
||||
position: relative;
|
||||
padding: 28px 24px;
|
||||
padding: 12px;
|
||||
background: var(--affine-background-code-block);
|
||||
border-radius: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.affine-code-block-container.mobile {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.affine-code-block-container .inline-editor {
|
||||
font-family: var(--affine-font-code-family);
|
||||
font-variant-ligatures: none;
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
||||
"rootDir": "./src/",
|
||||
"outDir": "./dist/",
|
||||
"noEmit": false
|
||||
},
|
||||
"include": ["./src"],
|
||||
"references": [
|
||||
{ "path": "../components" },
|
||||
{ "path": "../model" },
|
||||
{ "path": "../shared" },
|
||||
{ "path": "../../framework/block-std" },
|
||||
{ "path": "../../framework/global" },
|
||||
{ "path": "../../framework/inline" },
|
||||
{ "path": "../../framework/store" }
|
||||
{
|
||||
"path": "../../framework"
|
||||
},
|
||||
{
|
||||
"path": "../model"
|
||||
},
|
||||
{
|
||||
"path": "../components"
|
||||
},
|
||||
{
|
||||
"path": "../shared"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -20,13 +20,12 @@
|
||||
"@blocksuite/block-std": "workspace:*",
|
||||
"@blocksuite/data-view": "workspace:*",
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/icons": "^2.2.3",
|
||||
"@blocksuite/inline": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.12",
|
||||
"@toeverything/theme": "^1.1.3",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
@@ -42,5 +41,5 @@
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.20.0"
|
||||
"version": "0.19.0"
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ todoMeta.addProperty({
|
||||
metaConfig: propertyPresets.textPropertyConfig,
|
||||
get: block => block.doc.meta?.title ?? '',
|
||||
updated: (block, callback) => {
|
||||
return block.doc.workspace.slots.docListUpdated.on(() => {
|
||||
return block.doc.collection.meta.docMetaUpdated.on(() => {
|
||||
callback();
|
||||
});
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { EditorHost } from '@blocksuite/block-std';
|
||||
import { DataSourceBase, type PropertyMetaConfig } from '@blocksuite/data-view';
|
||||
import { propertyPresets } from '@blocksuite/data-view/property-presets';
|
||||
import { assertExists, Slot } from '@blocksuite/global/utils';
|
||||
import type { Block, Store } from '@blocksuite/store';
|
||||
import type { Block, Doc } from '@blocksuite/store';
|
||||
|
||||
import type { BlockMeta } from './block-meta/base.js';
|
||||
import { blockMetaMap } from './block-meta/index.js';
|
||||
@@ -59,7 +59,7 @@ export class BlockQueryDataSource extends DataSourceBase {
|
||||
}
|
||||
|
||||
get workspace() {
|
||||
return this.host.doc.workspace;
|
||||
return this.host.doc.collection;
|
||||
}
|
||||
|
||||
constructor(
|
||||
@@ -73,16 +73,16 @@ export class BlockQueryDataSource extends DataSourceBase {
|
||||
this.columnMetaMap.set(property.metaConfig.type, property.metaConfig);
|
||||
}
|
||||
for (const collection of this.workspace.docs.values()) {
|
||||
for (const block of Object.values(collection.getStore().blocks.peek())) {
|
||||
for (const block of Object.values(collection.getDoc().blocks.peek())) {
|
||||
if (this.meta.selector(block)) {
|
||||
this.blockMap.set(block.id, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.workspace.docs.forEach(doc => {
|
||||
this.listenToDoc(doc.getStore());
|
||||
this.listenToDoc(doc.getDoc());
|
||||
});
|
||||
this.workspace.slots.docCreated.on(id => {
|
||||
this.workspace.slots.docAdded.on(id => {
|
||||
const doc = this.workspace.getDoc(id);
|
||||
if (doc) {
|
||||
this.listenToDoc(doc);
|
||||
@@ -140,7 +140,7 @@ export class BlockQueryDataSource extends DataSourceBase {
|
||||
return this.block.columns.find(v => v.id === id);
|
||||
}
|
||||
|
||||
listenToDoc(doc: Store) {
|
||||
listenToDoc(doc: Doc) {
|
||||
this.docDisposeMap.set(
|
||||
doc.id,
|
||||
doc.slots.blockUpdated.on(v => {
|
||||
@@ -167,7 +167,7 @@ export class BlockQueryDataSource extends DataSourceBase {
|
||||
type ?? propertyPresets.multiSelectPropertyConfig.type
|
||||
].create(this.newColumnName());
|
||||
|
||||
const id = doc.workspace.idGenerator();
|
||||
const id = doc.generateBlockId();
|
||||
if (this.block.columns.some(v => v.id === id)) {
|
||||
return id;
|
||||
}
|
||||
@@ -211,7 +211,7 @@ export class BlockQueryDataSource extends DataSourceBase {
|
||||
}
|
||||
}
|
||||
|
||||
propertyDuplicate(_columnId: string): string | undefined {
|
||||
propertyDuplicate(_columnId: string): string {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import {
|
||||
BlockRenderer,
|
||||
DatabaseSelection,
|
||||
NoteRenderer,
|
||||
} from '@blocksuite/affine-block-database';
|
||||
import { BlockRenderer, NoteRenderer } from '@blocksuite/affine-block-database';
|
||||
import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption';
|
||||
import {
|
||||
menu,
|
||||
popMenu,
|
||||
popupTargetFromElement,
|
||||
} from '@blocksuite/affine-components/context-menu';
|
||||
import { CopyIcon, DeleteIcon } from '@blocksuite/affine-components/icons';
|
||||
import {
|
||||
CopyIcon,
|
||||
DeleteIcon,
|
||||
MoreHorizontalIcon,
|
||||
} from '@blocksuite/affine-components/icons';
|
||||
import { PeekViewProvider } from '@blocksuite/affine-components/peek';
|
||||
import { toast } from '@blocksuite/affine-components/toast';
|
||||
import { NOTE_SELECTOR } from '@blocksuite/affine-shared/consts';
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
import {
|
||||
createRecordDetail,
|
||||
createUniComponentFromWebComponent,
|
||||
DatabaseSelection,
|
||||
type DataSource,
|
||||
DataView,
|
||||
dataViewCommonStyle,
|
||||
@@ -38,7 +39,6 @@ import {
|
||||
uniMap,
|
||||
} from '@blocksuite/data-view';
|
||||
import { widgetPresets } from '@blocksuite/data-view/widget-presets';
|
||||
import { MoreHorizontalIcon } from '@blocksuite/icons/lit';
|
||||
import { Slice } from '@blocksuite/store';
|
||||
import { computed, signal } from '@preact/signals-core';
|
||||
import { css, nothing, unsafeCSS } from 'lit';
|
||||
@@ -241,7 +241,7 @@ export class DataViewBlockComponent extends CaptionedBlockComponent<DataViewBloc
|
||||
return nothing;
|
||||
}
|
||||
return html` <div class="database-ops" @click="${this._clickDatabaseOps}">
|
||||
${MoreHorizontalIcon()}
|
||||
${MoreHorizontalIcon}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,7 @@ import {
|
||||
type InsertToPosition,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import type { DataViewDataType } from '@blocksuite/data-view';
|
||||
import {
|
||||
BlockModel,
|
||||
BlockSchemaExtension,
|
||||
defineBlockSchema,
|
||||
} from '@blocksuite/store';
|
||||
import { BlockModel, defineBlockSchema } from '@blocksuite/store';
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
@@ -37,7 +33,7 @@ export class DataViewBlockModel extends BlockModel<Props> {
|
||||
}
|
||||
|
||||
duplicateView(id: string): string {
|
||||
const newId = this.doc.workspace.idGenerator();
|
||||
const newId = this.doc.generateBlockId();
|
||||
this.doc.transact(() => {
|
||||
const index = this.views.findIndex(v => v.id === id);
|
||||
const view = this.views[index];
|
||||
@@ -97,6 +93,3 @@ export const DataViewBlockSchema = defineBlockSchema({
|
||||
return new DataViewBlockModel();
|
||||
},
|
||||
});
|
||||
|
||||
export const DataViewBlockSchemaExtension =
|
||||
BlockSchemaExtension(DataViewBlockSchema);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user