mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-04 19:15:33 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b1d7011047 |
@@ -18,19 +18,11 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 6379:6379
|
- 6379:6379
|
||||||
|
|
||||||
# https://mailpit.axllent.org/docs/install/docker/
|
mailhog:
|
||||||
mailpit:
|
image: mailhog/mailhog:latest
|
||||||
image: axllent/mailpit:latest
|
|
||||||
ports:
|
ports:
|
||||||
- 1025:1025
|
- 1025:1025
|
||||||
- 8025:8025
|
- 8025:8025
|
||||||
environment:
|
|
||||||
MP_MAX_MESSAGES: 5000
|
|
||||||
MP_DATABASE: /data/mailpit.db
|
|
||||||
MP_SMTP_AUTH_ACCEPT_ANY: 1
|
|
||||||
MP_SMTP_AUTH_ALLOW_INSECURE: 1
|
|
||||||
volumes:
|
|
||||||
- mailpit_data:/data
|
|
||||||
|
|
||||||
# https://manual.manticoresearch.com/Starting_the_server/Docker
|
# https://manual.manticoresearch.com/Starting_the_server/Docker
|
||||||
manticoresearch:
|
manticoresearch:
|
||||||
@@ -95,5 +87,4 @@ networks:
|
|||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
manticoresearch_data:
|
manticoresearch_data:
|
||||||
mailpit_data:
|
|
||||||
elasticsearch_data:
|
elasticsearch_data:
|
||||||
|
|||||||
@@ -219,41 +219,6 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Whether ignore email server's TSL certification verification. Enable it for self-signed certificates.\n@default false\n@environment `MAILER_IGNORE_TLS`",
|
"description": "Whether ignore email server's TSL certification verification. Enable it for self-signed certificates.\n@default false\n@environment `MAILER_IGNORE_TLS`",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
|
||||||
"fallbackDomains": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "The emails from these domains are always sent using the fallback SMTP server.\n@default []",
|
|
||||||
"default": []
|
|
||||||
},
|
|
||||||
"fallbackSMTP.host": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Host of the email server (e.g. smtp.gmail.com)\n@default \"\"",
|
|
||||||
"default": ""
|
|
||||||
},
|
|
||||||
"fallbackSMTP.port": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Port of the email server (they commonly are 25, 465 or 587)\n@default 465",
|
|
||||||
"default": 465
|
|
||||||
},
|
|
||||||
"fallbackSMTP.username": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Username used to authenticate the email server\n@default \"\"",
|
|
||||||
"default": ""
|
|
||||||
},
|
|
||||||
"fallbackSMTP.password": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Password used to authenticate the email server\n@default \"\"",
|
|
||||||
"default": ""
|
|
||||||
},
|
|
||||||
"fallbackSMTP.sender": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Sender of all the emails (e.g. \"AFFiNE Team <noreply@affine.pro>\")\n@default \"\"",
|
|
||||||
"default": ""
|
|
||||||
},
|
|
||||||
"fallbackSMTP.ignoreTLS": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Whether ignore email server's TSL certification verification. Enable it for self-signed certificates.\n@default false",
|
|
||||||
"default": false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -664,34 +629,14 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"enabled": {
|
"enabled": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Whether to enable the copilot plugin. <br> Document: <a href=\"https://docs.affine.pro/self-host-affine/administer/ai\" target=\"_blank\">https://docs.affine.pro/self-host-affine/administer/ai</a>\n@default false",
|
"description": "Whether to enable the copilot plugin.\n@default false",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
"scenarios": {
|
|
||||||
"type": "object",
|
|
||||||
"description": "Use custom models in scenarios and override default settings.\n@default {\"override_enabled\":false,\"scenarios\":{\"audio_transcribing\":\"gemini-2.5-flash\",\"chat\":\"claude-sonnet-4@20250514\",\"embedding\":\"gemini-embedding-001\",\"image\":\"gpt-image-1\",\"rerank\":\"gpt-4.1\",\"coding\":\"claude-sonnet-4@20250514\",\"complex_text_generation\":\"gpt-4o-2024-08-06\",\"quick_decision_making\":\"gpt-5-mini\",\"quick_text_generation\":\"gemini-2.5-flash\",\"polish_and_summarize\":\"gemini-2.5-flash\"}}",
|
|
||||||
"default": {
|
|
||||||
"override_enabled": false,
|
|
||||||
"scenarios": {
|
|
||||||
"audio_transcribing": "gemini-2.5-flash",
|
|
||||||
"chat": "claude-sonnet-4@20250514",
|
|
||||||
"embedding": "gemini-embedding-001",
|
|
||||||
"image": "gpt-image-1",
|
|
||||||
"rerank": "gpt-4.1",
|
|
||||||
"coding": "claude-sonnet-4@20250514",
|
|
||||||
"complex_text_generation": "gpt-4o-2024-08-06",
|
|
||||||
"quick_decision_making": "gpt-5-mini",
|
|
||||||
"quick_text_generation": "gemini-2.5-flash",
|
|
||||||
"polish_and_summarize": "gemini-2.5-flash"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"providers.openai": {
|
"providers.openai": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The config for the openai provider.\n@default {\"apiKey\":\"\",\"baseURL\":\"https://api.openai.com/v1\"}\n@link https://github.com/openai/openai-node",
|
"description": "The config for the openai provider.\n@default {\"apiKey\":\"\"}\n@link https://github.com/openai/openai-node",
|
||||||
"default": {
|
"default": {
|
||||||
"apiKey": "",
|
"apiKey": ""
|
||||||
"baseURL": "https://api.openai.com/v1"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"providers.fal": {
|
"providers.fal": {
|
||||||
@@ -703,10 +648,9 @@
|
|||||||
},
|
},
|
||||||
"providers.gemini": {
|
"providers.gemini": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The config for the gemini provider.\n@default {\"apiKey\":\"\",\"baseURL\":\"https://generativelanguage.googleapis.com/v1beta\"}",
|
"description": "The config for the gemini provider.\n@default {\"apiKey\":\"\"}",
|
||||||
"default": {
|
"default": {
|
||||||
"apiKey": "",
|
"apiKey": ""
|
||||||
"baseURL": "https://generativelanguage.googleapis.com/v1beta"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"providers.geminiVertex": {
|
"providers.geminiVertex": {
|
||||||
@@ -753,10 +697,9 @@
|
|||||||
},
|
},
|
||||||
"providers.anthropic": {
|
"providers.anthropic": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The config for the anthropic provider.\n@default {\"apiKey\":\"\",\"baseURL\":\"https://api.anthropic.com/v1\"}",
|
"description": "The config for the anthropic provider.\n@default {\"apiKey\":\"\"}",
|
||||||
"default": {
|
"default": {
|
||||||
"apiKey": "",
|
"apiKey": ""
|
||||||
"baseURL": "https://api.anthropic.com/v1"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"providers.anthropicVertex": {
|
"providers.anthropicVertex": {
|
||||||
|
|||||||
@@ -29,25 +29,25 @@ const isInternal = buildType === 'internal';
|
|||||||
|
|
||||||
const replicaConfig = {
|
const replicaConfig = {
|
||||||
stable: {
|
stable: {
|
||||||
web: 2,
|
web: 3,
|
||||||
graphql: Number(process.env.PRODUCTION_GRAPHQL_REPLICA) || 2,
|
graphql: Number(process.env.PRODUCTION_GRAPHQL_REPLICA) || 3,
|
||||||
sync: Number(process.env.PRODUCTION_SYNC_REPLICA) || 2,
|
sync: Number(process.env.PRODUCTION_SYNC_REPLICA) || 3,
|
||||||
renderer: Number(process.env.PRODUCTION_RENDERER_REPLICA) || 2,
|
renderer: Number(process.env.PRODUCTION_RENDERER_REPLICA) || 3,
|
||||||
doc: Number(process.env.PRODUCTION_DOC_REPLICA) || 2,
|
doc: Number(process.env.PRODUCTION_DOC_REPLICA) || 3,
|
||||||
},
|
},
|
||||||
beta: {
|
beta: {
|
||||||
web: 1,
|
web: 2,
|
||||||
graphql: Number(process.env.BETA_GRAPHQL_REPLICA) || 1,
|
graphql: Number(process.env.BETA_GRAPHQL_REPLICA) || 2,
|
||||||
sync: Number(process.env.BETA_SYNC_REPLICA) || 1,
|
sync: Number(process.env.BETA_SYNC_REPLICA) || 2,
|
||||||
renderer: Number(process.env.BETA_RENDERER_REPLICA) || 1,
|
renderer: Number(process.env.BETA_RENDERER_REPLICA) || 2,
|
||||||
doc: Number(process.env.BETA_DOC_REPLICA) || 1,
|
doc: Number(process.env.BETA_DOC_REPLICA) || 2,
|
||||||
},
|
},
|
||||||
canary: {
|
canary: {
|
||||||
web: 1,
|
web: 2,
|
||||||
graphql: 1,
|
graphql: 2,
|
||||||
sync: 1,
|
sync: 2,
|
||||||
renderer: 1,
|
renderer: 2,
|
||||||
doc: 1,
|
doc: 2,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,15 +4,9 @@ inputs:
|
|||||||
app-version:
|
app-version:
|
||||||
description: 'App Version'
|
description: 'App Version'
|
||||||
required: true
|
required: true
|
||||||
ios-app-version:
|
|
||||||
description: 'iOS App Store Version (Optional, use App version if empty)'
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
runs:
|
runs:
|
||||||
using: 'composite'
|
using: 'composite'
|
||||||
steps:
|
steps:
|
||||||
- name: 'Write Version'
|
- name: 'Write Version'
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
|
||||||
IOS_APP_VERSION: ${{ inputs.ios-app-version }}
|
|
||||||
run: ./scripts/set-version.sh ${{ inputs.app-version }}
|
run: ./scripts/set-version.sh ${{ inputs.app-version }}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
replicaCount: 2
|
replicaCount: 3
|
||||||
enabled: false
|
enabled: false
|
||||||
database:
|
database:
|
||||||
connectionName: ""
|
connectionName: ""
|
||||||
@@ -33,11 +33,8 @@ service:
|
|||||||
|
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
memory: "1Gi"
|
memory: "4Gi"
|
||||||
cpu: "1"
|
cpu: "2"
|
||||||
requests:
|
|
||||||
memory: "512Mi"
|
|
||||||
cpu: "100m"
|
|
||||||
|
|
||||||
volumes: []
|
volumes: []
|
||||||
volumeMounts: []
|
volumeMounts: []
|
||||||
|
|||||||
@@ -465,7 +465,7 @@ jobs:
|
|||||||
name: ${{ env.RELEASE_VERSION }}
|
name: ${{ env.RELEASE_VERSION }}
|
||||||
draft: ${{ inputs.build-type == 'stable' }}
|
draft: ${{ inputs.build-type == 'stable' }}
|
||||||
prerelease: ${{ inputs.build-type != 'stable' }}
|
prerelease: ${{ inputs.build-type != 'stable' }}
|
||||||
tag_name: v${{ env.RELEASE_VERSION}}
|
tag_name: ${{ env.RELEASE_VERSION}}
|
||||||
files: |
|
files: |
|
||||||
./release/*
|
./release/*
|
||||||
./release/.env.example
|
./release/.env.example
|
||||||
|
|||||||
@@ -12,9 +12,6 @@ on:
|
|||||||
build-type:
|
build-type:
|
||||||
type: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
ios-app-version:
|
|
||||||
type: string
|
|
||||||
required: false
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BUILD_TYPE: ${{ inputs.build-type }}
|
BUILD_TYPE: ${{ inputs.build-type }}
|
||||||
@@ -81,7 +78,7 @@ jobs:
|
|||||||
path: packages/frontend/apps/android/dist
|
path: packages/frontend/apps/android/dist
|
||||||
|
|
||||||
ios:
|
ios:
|
||||||
runs-on: 'macos-15'
|
runs-on: ${{ github.ref_name == 'canary' && 'macos-latest' || 'blaze/macos-14' }}
|
||||||
needs:
|
needs:
|
||||||
- build-ios-web
|
- build-ios-web
|
||||||
steps:
|
steps:
|
||||||
@@ -90,7 +87,6 @@ jobs:
|
|||||||
uses: ./.github/actions/setup-version
|
uses: ./.github/actions/setup-version
|
||||||
with:
|
with:
|
||||||
app-version: ${{ inputs.app-version }}
|
app-version: ${{ inputs.app-version }}
|
||||||
ios-app-version: ${{ inputs.ios-app-version }}
|
|
||||||
- name: 'Update Code Sign Identity'
|
- name: 'Update Code Sign Identity'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: ./packages/frontend/apps/ios/update_code_sign_identity.sh
|
run: ./packages/frontend/apps/ios/update_code_sign_identity.sh
|
||||||
@@ -110,7 +106,7 @@ jobs:
|
|||||||
enableScripts: false
|
enableScripts: false
|
||||||
- uses: maxim-lobanov/setup-xcode@v1
|
- uses: maxim-lobanov/setup-xcode@v1
|
||||||
with:
|
with:
|
||||||
xcode-version: 16.4
|
xcode-version: 16.2
|
||||||
- name: Install Swiftformat
|
- name: Install Swiftformat
|
||||||
run: brew install swiftformat
|
run: brew install swiftformat
|
||||||
- name: Cap sync
|
- name: Cap sync
|
||||||
|
|||||||
@@ -21,10 +21,6 @@ on:
|
|||||||
required: true
|
required: true
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
ios-app-version:
|
|
||||||
description: 'iOS App Store Version (Optional, use tag version if empty)'
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -34,7 +30,6 @@ permissions:
|
|||||||
packages: write
|
packages: write
|
||||||
security-events: write
|
security-events: write
|
||||||
attestations: write
|
attestations: write
|
||||||
issues: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
prepare:
|
prepare:
|
||||||
@@ -74,8 +69,7 @@ jobs:
|
|||||||
name: Wait for approval
|
name: Wait for approval
|
||||||
with:
|
with:
|
||||||
secret: ${{ secrets.GITHUB_TOKEN }}
|
secret: ${{ secrets.GITHUB_TOKEN }}
|
||||||
approvers: forehalo,fengmk2,darkskygit
|
approvers: forehalo,fengmk2
|
||||||
minimum-approvals: 1
|
|
||||||
fail-on-denial: true
|
fail-on-denial: true
|
||||||
issue-title: Please confirm to release docker image
|
issue-title: Please confirm to release docker image
|
||||||
issue-body: |
|
issue-body: |
|
||||||
@@ -84,7 +78,7 @@ jobs:
|
|||||||
Tag: ghcr.io/toeverything/affine:${{ needs.prepare.outputs.BUILD_TYPE }}
|
Tag: ghcr.io/toeverything/affine:${{ needs.prepare.outputs.BUILD_TYPE }}
|
||||||
|
|
||||||
> comment with "approve", "approved", "lgtm", "yes" to approve
|
> comment with "approve", "approved", "lgtm", "yes" to approve
|
||||||
> comment with "deny", "denied", "no" to deny
|
> comment with "deny", "deny", "no" to deny
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
@@ -123,4 +117,3 @@ jobs:
|
|||||||
build-type: ${{ needs.prepare.outputs.BUILD_TYPE }}
|
build-type: ${{ needs.prepare.outputs.BUILD_TYPE }}
|
||||||
app-version: ${{ needs.prepare.outputs.APP_VERSION }}
|
app-version: ${{ needs.prepare.outputs.APP_VERSION }}
|
||||||
git-short-hash: ${{ needs.prepare.outputs.GIT_SHORT_HASH }}
|
git-short-hash: ${{ needs.prepare.outputs.GIT_SHORT_HASH }}
|
||||||
ios-app-version: ${{ inputs.ios-app-version }}
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ jobs:
|
|||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
cd ${{ env.ARCHIVE_DIR }}/out
|
cd ${{ env.ARCHIVE_DIR }}/out
|
||||||
signtool sign /tr http://timestamp.globalsign.com/tsa/r6advanced1 /td sha256 /fd sha256 /a ${{ inputs.files }}
|
signtool sign /tr http://timestamp.sectigo.com /td sha256 /fd sha256 /a ${{ inputs.files }}
|
||||||
- name: zip file
|
- name: zip file
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
**/node_modules
|
**/node_modules
|
||||||
.yarn
|
.yarn
|
||||||
.github/helm
|
.github/helm
|
||||||
.git
|
|
||||||
.vscode
|
.vscode
|
||||||
.yarnrc.yml
|
.yarnrc.yml
|
||||||
.docker
|
.docker
|
||||||
|
|||||||
Generated
+12
-12
@@ -93,7 +93,7 @@ dependencies = [
|
|||||||
"symphonia",
|
"symphonia",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"uuid",
|
"uuid",
|
||||||
"windows 0.61.3",
|
"windows 0.61.1",
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1691,7 +1691,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
"windows 0.61.3",
|
"windows 0.61.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2284,7 +2284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
|
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.48.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4732,9 +4732,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tree-sitter"
|
name = "tree-sitter"
|
||||||
version = "0.25.8"
|
version = "0.25.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d7b8994f367f16e6fa14b5aebbcb350de5d7cbea82dc5b00ae997dd71680dd2"
|
checksum = "ac5fff5c47490dfdf473b5228039bfacad9d765d9b6939d26bf7cc064c1c7822"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"regex",
|
"regex",
|
||||||
@@ -4842,9 +4842,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tree-sitter-scala"
|
name = "tree-sitter-scala"
|
||||||
version = "0.24.0"
|
version = "0.23.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7516aeb3d1f40ede8e3045b163e86993b3434514dd06c34c0b75e782d9a0b251"
|
checksum = "efde5e68b4736e9eac17bfa296c6f104a26bffab363b365eb898c40a63c15d2f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"tree-sitter-language",
|
"tree-sitter-language",
|
||||||
@@ -5334,7 +5334,7 @@ version = "0.1.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5365,9 +5365,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.61.3"
|
version = "0.61.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
|
checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-collections",
|
"windows-collections",
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.61.2",
|
||||||
@@ -5477,9 +5477,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-link"
|
name = "windows-link"
|
||||||
version = "0.1.3"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-numerics"
|
name = "windows-numerics"
|
||||||
|
|||||||
+1
-1
@@ -93,7 +93,7 @@ tree-sitter-javascript = { version = "0.23" }
|
|||||||
tree-sitter-kotlin-ng = { version = "1.1" }
|
tree-sitter-kotlin-ng = { version = "1.1" }
|
||||||
tree-sitter-python = { version = "0.23" }
|
tree-sitter-python = { version = "0.23" }
|
||||||
tree-sitter-rust = { version = "0.24" }
|
tree-sitter-rust = { version = "0.24" }
|
||||||
tree-sitter-scala = { version = "0.24" }
|
tree-sitter-scala = { version = "0.23" }
|
||||||
tree-sitter-typescript = { version = "0.23" }
|
tree-sitter-typescript = { version = "0.23" }
|
||||||
uniffi = "0.29"
|
uniffi = "0.29"
|
||||||
url = { version = "2.5" }
|
url = { version = "2.5" }
|
||||||
|
|||||||
@@ -266,7 +266,6 @@
|
|||||||
"./components/toggle-button": "./src/components/toggle-button.ts",
|
"./components/toggle-button": "./src/components/toggle-button.ts",
|
||||||
"./components/toggle-switch": "./src/components/toggle-switch.ts",
|
"./components/toggle-switch": "./src/components/toggle-switch.ts",
|
||||||
"./components/toolbar": "./src/components/toolbar.ts",
|
"./components/toolbar": "./src/components/toolbar.ts",
|
||||||
"./components/tooltip": "./src/components/tooltip.ts",
|
|
||||||
"./components/view-dropdown-menu": "./src/components/view-dropdown-menu.ts",
|
"./components/view-dropdown-menu": "./src/components/view-dropdown-menu.ts",
|
||||||
"./components/tooltip-content-with-shortcut": "./src/components/tooltip-content-with-shortcut.ts",
|
"./components/tooltip-content-with-shortcut": "./src/components/tooltip-content-with-shortcut.ts",
|
||||||
"./components/resource": "./src/components/resource.ts",
|
"./components/resource": "./src/components/resource.ts",
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export * from '@blocksuite/affine-components/tooltip';
|
|
||||||
@@ -39,13 +39,6 @@ export class CodeBlockHighlighter extends LifeCycleWatcher {
|
|||||||
private readonly _loadTheme = async (
|
private readonly _loadTheme = async (
|
||||||
highlighter: HighlighterCore
|
highlighter: HighlighterCore
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
// It is possible that by the time the highlighter is ready all instances
|
|
||||||
// have already been unmounted. In that case there is no need to load
|
|
||||||
// themes or update state.
|
|
||||||
if (CodeBlockHighlighter._refCount === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = this.std.getOptional(CodeBlockConfigExtension.identifier);
|
const config = this.std.getOptional(CodeBlockConfigExtension.identifier);
|
||||||
const darkTheme = config?.theme?.dark ?? CODE_BLOCK_DEFAULT_DARK_THEME;
|
const darkTheme = config?.theme?.dark ?? CODE_BLOCK_DEFAULT_DARK_THEME;
|
||||||
const lightTheme = config?.theme?.light ?? CODE_BLOCK_DEFAULT_LIGHT_THEME;
|
const lightTheme = config?.theme?.light ?? CODE_BLOCK_DEFAULT_LIGHT_THEME;
|
||||||
@@ -85,27 +78,14 @@ export class CodeBlockHighlighter extends LifeCycleWatcher {
|
|||||||
override unmounted(): void {
|
override unmounted(): void {
|
||||||
CodeBlockHighlighter._refCount--;
|
CodeBlockHighlighter._refCount--;
|
||||||
|
|
||||||
// Dispose the shared highlighter **after** any in-flight creation finishes.
|
// Only dispose the shared highlighter when no instances are using it
|
||||||
if (CodeBlockHighlighter._refCount !== 0) {
|
if (
|
||||||
return;
|
CodeBlockHighlighter._refCount === 0 &&
|
||||||
}
|
CodeBlockHighlighter._sharedHighlighter
|
||||||
|
) {
|
||||||
const doDispose = (highlighter: HighlighterCore | null) => {
|
CodeBlockHighlighter._sharedHighlighter.dispose();
|
||||||
if (highlighter) {
|
|
||||||
highlighter.dispose();
|
|
||||||
}
|
|
||||||
CodeBlockHighlighter._sharedHighlighter = null;
|
CodeBlockHighlighter._sharedHighlighter = null;
|
||||||
CodeBlockHighlighter._highlighterPromise = null;
|
CodeBlockHighlighter._highlighterPromise = null;
|
||||||
};
|
|
||||||
|
|
||||||
if (CodeBlockHighlighter._sharedHighlighter) {
|
|
||||||
// Highlighter already created – dispose immediately.
|
|
||||||
doDispose(CodeBlockHighlighter._sharedHighlighter);
|
|
||||||
} else if (CodeBlockHighlighter._highlighterPromise) {
|
|
||||||
// Highlighter still being created – wait for it, then dispose.
|
|
||||||
CodeBlockHighlighter._highlighterPromise
|
|
||||||
.then(doDispose)
|
|
||||||
.catch(console.error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,10 +164,8 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
|||||||
readonly$: ReadonlySignal<boolean> = computed(() => {
|
readonly$: ReadonlySignal<boolean> = computed(() => {
|
||||||
return (
|
return (
|
||||||
this._model.store.readonly ||
|
this._model.store.readonly ||
|
||||||
(IS_MOBILE &&
|
// TODO(@L-Sun): use block level readonly
|
||||||
!this._model.store.provider
|
IS_MOBILE
|
||||||
.get(FeatureFlagService)
|
|
||||||
.getFlag('enable_mobile_database_editing'))
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
BlockElementCommentManager,
|
BlockElementCommentManager,
|
||||||
CommentProviderIdentifier,
|
CommentProviderIdentifier,
|
||||||
DocModeProvider,
|
DocModeProvider,
|
||||||
FeatureFlagService,
|
|
||||||
NotificationProvider,
|
NotificationProvider,
|
||||||
type TelemetryEventMap,
|
type TelemetryEventMap,
|
||||||
TelemetryProvider,
|
TelemetryProvider,
|
||||||
@@ -35,7 +34,6 @@ import {
|
|||||||
uniMap,
|
uniMap,
|
||||||
} from '@blocksuite/data-view';
|
} from '@blocksuite/data-view';
|
||||||
import { widgetPresets } from '@blocksuite/data-view/widget-presets';
|
import { widgetPresets } from '@blocksuite/data-view/widget-presets';
|
||||||
import { IS_MOBILE } from '@blocksuite/global/env';
|
|
||||||
import { Rect } from '@blocksuite/global/gfx';
|
import { Rect } from '@blocksuite/global/gfx';
|
||||||
import {
|
import {
|
||||||
CommentIcon,
|
CommentIcon,
|
||||||
@@ -50,7 +48,6 @@ import { autoUpdate } from '@floating-ui/dom';
|
|||||||
import { computed, signal } from '@preact/signals-core';
|
import { computed, signal } from '@preact/signals-core';
|
||||||
import { html, nothing } from 'lit';
|
import { html, nothing } from 'lit';
|
||||||
import { repeat } from 'lit/directives/repeat.js';
|
import { repeat } from 'lit/directives/repeat.js';
|
||||||
import { styleMap } from 'lit/directives/style-map.js';
|
|
||||||
|
|
||||||
import { popSideDetail } from './components/layout.js';
|
import { popSideDetail } from './components/layout.js';
|
||||||
import { DatabaseConfigExtension } from './config.js';
|
import { DatabaseConfigExtension } from './config.js';
|
||||||
@@ -352,7 +349,6 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
|
|||||||
this.setAttribute(RANGE_SYNC_EXCLUDE_ATTR, 'true');
|
this.setAttribute(RANGE_SYNC_EXCLUDE_ATTR, 'true');
|
||||||
this.classList.add(databaseBlockStyles);
|
this.classList.add(databaseBlockStyles);
|
||||||
this.listenFullWidthChange();
|
this.listenFullWidthChange();
|
||||||
this.handleMobileEditing();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
listenFullWidthChange() {
|
listenFullWidthChange() {
|
||||||
@@ -368,41 +364,6 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMobileEditing() {
|
|
||||||
if (!IS_MOBILE) return;
|
|
||||||
|
|
||||||
let notifyClosed = true;
|
|
||||||
const handler = () => {
|
|
||||||
if (
|
|
||||||
!this.std
|
|
||||||
.get(FeatureFlagService)
|
|
||||||
.getFlag('enable_mobile_database_editing')
|
|
||||||
) {
|
|
||||||
const notification = this.std.getOptional(NotificationProvider);
|
|
||||||
if (notification && notifyClosed) {
|
|
||||||
notifyClosed = false;
|
|
||||||
notification.notify({
|
|
||||||
title: html`<div
|
|
||||||
style=${styleMap({
|
|
||||||
whiteSpace: 'wrap',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
Mobile database editing is not supported yet. You can open it in
|
|
||||||
experimental features, or edit it in desktop mode.
|
|
||||||
</div>`,
|
|
||||||
accent: 'warning',
|
|
||||||
onClose: () => {
|
|
||||||
notifyClosed = true;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.disposables.addFromEvent(this, 'click', handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly dataViewRootLogic = lazy(
|
private readonly dataViewRootLogic = lazy(
|
||||||
() =>
|
() =>
|
||||||
new DataViewRootUILogic({
|
new DataViewRootUILogic({
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { ImageBlockModel } from '@blocksuite/affine-model';
|
import { ImageBlockModel } from '@blocksuite/affine-model';
|
||||||
import {
|
import {
|
||||||
ActionPlacement,
|
ActionPlacement,
|
||||||
blockCommentToolbarButton,
|
|
||||||
type ToolbarModuleConfig,
|
type ToolbarModuleConfig,
|
||||||
ToolbarModuleExtension,
|
ToolbarModuleExtension,
|
||||||
} from '@blocksuite/affine-shared/services';
|
} from '@blocksuite/affine-shared/services';
|
||||||
@@ -50,10 +49,6 @@ const builtinToolbarConfig = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'c.comment',
|
|
||||||
...blockCommentToolbarButton,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
placement: ActionPlacement.More,
|
placement: ActionPlacement.More,
|
||||||
id: 'a.clipboard',
|
id: 'a.clipboard',
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import {
|
|||||||
getPrevContentBlock,
|
getPrevContentBlock,
|
||||||
matchModels,
|
matchModels,
|
||||||
} from '@blocksuite/affine-shared/utils';
|
} from '@blocksuite/affine-shared/utils';
|
||||||
import { IS_ANDROID, IS_MOBILE } from '@blocksuite/global/env';
|
|
||||||
import { BlockSelection, type EditorHost } from '@blocksuite/std';
|
import { BlockSelection, type EditorHost } from '@blocksuite/std';
|
||||||
import type { BlockModel, Text } from '@blocksuite/store';
|
import type { BlockModel, Text } from '@blocksuite/store';
|
||||||
|
|
||||||
@@ -79,28 +78,6 @@ export function mergeWithPrev(editorHost: EditorHost, model: BlockModel) {
|
|||||||
index: lengthBeforeJoin,
|
index: lengthBeforeJoin,
|
||||||
length: 0,
|
length: 0,
|
||||||
}).catch(console.error);
|
}).catch(console.error);
|
||||||
|
|
||||||
// due to some IME like Microsoft Swift IME on Android will reset range after join text,
|
|
||||||
// for example:
|
|
||||||
//
|
|
||||||
// $ZERO_WIDTH_FOR_EMPTY_LINE <--- p1
|
|
||||||
// |aaa <--- p2
|
|
||||||
//
|
|
||||||
// after pressing backspace, during beforeinput event, the native range is (p1, 1) -> (p2, 0)
|
|
||||||
// and after browser and IME handle the event, the native range is (p1, 1) -> (p1, 1)
|
|
||||||
//
|
|
||||||
// a|aa <--- p1
|
|
||||||
//
|
|
||||||
// so we need to set range again after join text.
|
|
||||||
if (IS_ANDROID) {
|
|
||||||
setTimeout(() => {
|
|
||||||
asyncSetInlineRange(editorHost.std, prevBlock, {
|
|
||||||
index: lengthBeforeJoin,
|
|
||||||
length: 0,
|
|
||||||
}).catch(console.error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,17 +91,10 @@ export function mergeWithPrev(editorHost: EditorHost, model: BlockModel) {
|
|||||||
...EMBED_BLOCK_MODEL_LIST,
|
...EMBED_BLOCK_MODEL_LIST,
|
||||||
])
|
])
|
||||||
) {
|
) {
|
||||||
// due to create a block selection will clear text selection, which lead
|
const selection = editorHost.selection.create(BlockSelection, {
|
||||||
// the virtual keyboard to be auto closed on mobile. This behavior breaks
|
blockId: prevBlock.id,
|
||||||
// the user experience.
|
});
|
||||||
if (!IS_MOBILE) {
|
editorHost.selection.setGroup('note', [selection]);
|
||||||
const selection = editorHost.selection.create(BlockSelection, {
|
|
||||||
blockId: prevBlock.id,
|
|
||||||
});
|
|
||||||
editorHost.selection.setGroup('note', [selection]);
|
|
||||||
} else {
|
|
||||||
doc.deleteBlock(prevBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (model.text?.length === 0) {
|
if (model.text?.length === 0) {
|
||||||
doc.deleteBlock(model, {
|
doc.deleteBlock(model, {
|
||||||
|
|||||||
@@ -634,9 +634,9 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager {
|
|||||||
|
|
||||||
const movedElements = new Set([
|
const movedElements = new Set([
|
||||||
...selectedElements,
|
...selectedElements,
|
||||||
...selectedElements.flatMap(el =>
|
...selectedElements
|
||||||
isGfxGroupCompatibleModel(el) ? el.descendantElements : []
|
.map(el => (isGfxGroupCompatibleModel(el) ? el.descendantElements : []))
|
||||||
),
|
.flat(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
movedElements.forEach(element => {
|
movedElements.forEach(element => {
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ export * from './clipboard/command';
|
|||||||
export * from './edgeless-root-block.js';
|
export * from './edgeless-root-block.js';
|
||||||
export { EdgelessRootService } from './edgeless-root-service.js';
|
export { EdgelessRootService } from './edgeless-root-service.js';
|
||||||
export * from './utils/clipboard-utils.js';
|
export * from './utils/clipboard-utils.js';
|
||||||
export { getElementProps, sortEdgelessElements } from './utils/clone-utils.js';
|
export { sortEdgelessElements } from './utils/clone-utils.js';
|
||||||
export { isCanvasElement } from './utils/query.js';
|
export { isCanvasElement } from './utils/query.js';
|
||||||
export { EDGELESS_BLOCK_CHILD_PADDING } from '@blocksuite/affine-shared/consts';
|
export { EDGELESS_BLOCK_CHILD_PADDING } from '@blocksuite/affine-shared/consts';
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
} from '@blocksuite/affine-shared/commands';
|
} from '@blocksuite/affine-shared/commands';
|
||||||
import {
|
import {
|
||||||
ActionPlacement,
|
ActionPlacement,
|
||||||
blockCommentToolbarButton,
|
|
||||||
type ToolbarModuleConfig,
|
type ToolbarModuleConfig,
|
||||||
} from '@blocksuite/affine-shared/services';
|
} from '@blocksuite/affine-shared/services';
|
||||||
import { CaptionIcon, CopyIcon, DeleteIcon } from '@blocksuite/icons/lit';
|
import { CaptionIcon, CopyIcon, DeleteIcon } from '@blocksuite/icons/lit';
|
||||||
@@ -62,10 +61,6 @@ export const surfaceRefToolbarModuleConfig: ToolbarModuleConfig = {
|
|||||||
surfaceRefBlock.captionElement.show();
|
surfaceRefBlock.captionElement.show();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'e.comment',
|
|
||||||
...blockCommentToolbarButton,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'a.clipboard',
|
id: 'a.clipboard',
|
||||||
placement: ActionPlacement.More,
|
placement: ActionPlacement.More,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
getBoundWithRotation,
|
getBoundWithRotation,
|
||||||
intersects,
|
intersects,
|
||||||
} from '@blocksuite/global/gfx';
|
} from '@blocksuite/global/gfx';
|
||||||
import { type BlockStdScope, SurfaceSelection } from '@blocksuite/std';
|
import type { BlockStdScope } from '@blocksuite/std';
|
||||||
import type {
|
import type {
|
||||||
GfxCompatibleInterface,
|
GfxCompatibleInterface,
|
||||||
GridManager,
|
GridManager,
|
||||||
@@ -298,10 +298,7 @@ export class DomRenderer {
|
|||||||
viewportBounds,
|
viewportBounds,
|
||||||
zoom
|
zoom
|
||||||
);
|
);
|
||||||
const zIndexStyle = {
|
Object.assign(domElement.style, geometricStyles);
|
||||||
'z-index': this.layerManager.getZIndex(elementModel),
|
|
||||||
};
|
|
||||||
Object.assign(domElement.style, geometricStyles, zIndexStyle);
|
|
||||||
Object.assign(domElement.style, PLACEHOLDER_RESET_STYLES);
|
Object.assign(domElement.style, PLACEHOLDER_RESET_STYLES);
|
||||||
|
|
||||||
// Clear classes specific to shapes, if applicable
|
// Clear classes specific to shapes, if applicable
|
||||||
@@ -338,10 +335,7 @@ export class DomRenderer {
|
|||||||
zoom
|
zoom
|
||||||
);
|
);
|
||||||
const opacityStyle = getOpacity(elementModel);
|
const opacityStyle = getOpacity(elementModel);
|
||||||
const zIndexStyle = {
|
Object.assign(domElement.style, geometricStyles, opacityStyle);
|
||||||
'z-index': this.layerManager.getZIndex(elementModel),
|
|
||||||
};
|
|
||||||
Object.assign(domElement.style, geometricStyles, opacityStyle, zIndexStyle);
|
|
||||||
|
|
||||||
this._renderElement(elementModel, domElement);
|
this._renderElement(elementModel, domElement);
|
||||||
}
|
}
|
||||||
@@ -390,36 +384,6 @@ export class DomRenderer {
|
|||||||
this.refresh();
|
this.refresh();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Workaround for the group rendering reactive update when selection changed
|
|
||||||
let lastSet = new Set<string>();
|
|
||||||
this._disposables.add(
|
|
||||||
this.std.selection.filter$(SurfaceSelection).subscribe(selections => {
|
|
||||||
const groupRelatedSelection = new Set(
|
|
||||||
selections.flatMap(s =>
|
|
||||||
s.elements.flatMap(e => {
|
|
||||||
const element = surfaceModel.getElementById(e);
|
|
||||||
if (
|
|
||||||
element &&
|
|
||||||
(element.type === 'group' || element.groups.length !== 0)
|
|
||||||
) {
|
|
||||||
return [element.id, ...element.groups.map(g => g.id)];
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (lastSet.symmetricDifference(groupRelatedSelection).size !== 0) {
|
|
||||||
lastSet.union(groupRelatedSelection).forEach(g => {
|
|
||||||
this._markElementDirty(g, UpdateType.ELEMENT_UPDATED);
|
|
||||||
});
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
lastSet = groupRelatedSelection;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addOverlay = (overlay: Overlay) => {
|
addOverlay = (overlay: Overlay) => {
|
||||||
|
|||||||
@@ -73,8 +73,7 @@
|
|||||||
"./edgeless-line-styles-panel": "./src/edgeless-line-styles-panel/index.ts",
|
"./edgeless-line-styles-panel": "./src/edgeless-line-styles-panel/index.ts",
|
||||||
"./edgeless-shape-color-picker": "./src/edgeless-shape-color-picker/index.ts",
|
"./edgeless-shape-color-picker": "./src/edgeless-shape-color-picker/index.ts",
|
||||||
"./open-doc-dropdown-menu": "./src/open-doc-dropdown-menu/index.ts",
|
"./open-doc-dropdown-menu": "./src/open-doc-dropdown-menu/index.ts",
|
||||||
"./slider": "./src/slider/index.ts",
|
"./slider": "./src/slider/index.ts"
|
||||||
"./tooltip": "./src/tooltip/index.ts"
|
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"src",
|
"src",
|
||||||
|
|||||||
@@ -85,8 +85,6 @@ export class MenuSubMenu extends MenuFocusable {
|
|||||||
.catch(err => console.error(err));
|
.catch(err => console.error(err));
|
||||||
});
|
});
|
||||||
this.menu.openSubMenu(menu);
|
this.menu.openSubMenu(menu);
|
||||||
// in case that the menu is not closed, but the component is removed,
|
|
||||||
this.disposables.add(unsub);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override render(): unknown {
|
protected override render(): unknown {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ export const LoadingIcon = ({
|
|||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
fill="none"
|
||||||
style="fill: none;"
|
|
||||||
>
|
>
|
||||||
<style>
|
<style>
|
||||||
.spinner {
|
.spinner {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { effects as tooltipEffects } from '../tooltip/effect.js';
|
|
||||||
import { EditorIconButton } from './icon-button.js';
|
import { EditorIconButton } from './icon-button.js';
|
||||||
import {
|
import {
|
||||||
EditorMenuAction,
|
EditorMenuAction,
|
||||||
@@ -7,6 +6,7 @@ import {
|
|||||||
} from './menu-button.js';
|
} from './menu-button.js';
|
||||||
import { EditorToolbarSeparator } from './separator.js';
|
import { EditorToolbarSeparator } from './separator.js';
|
||||||
import { EditorToolbar } from './toolbar.js';
|
import { EditorToolbar } from './toolbar.js';
|
||||||
|
import { Tooltip } from './tooltip.js';
|
||||||
|
|
||||||
export { EditorChevronDown } from './chevron-down.js';
|
export { EditorChevronDown } from './chevron-down.js';
|
||||||
export { ToolbarMoreMenuConfigExtension } from './config.js';
|
export { ToolbarMoreMenuConfigExtension } from './config.js';
|
||||||
@@ -20,6 +20,7 @@ export { MenuContext } from './menu-context.js';
|
|||||||
export { EditorToolbarSeparator } from './separator.js';
|
export { EditorToolbarSeparator } from './separator.js';
|
||||||
export { darkToolbarStyles, lightToolbarStyles } from './styles.js';
|
export { darkToolbarStyles, lightToolbarStyles } from './styles.js';
|
||||||
export { EditorToolbar } from './toolbar.js';
|
export { EditorToolbar } from './toolbar.js';
|
||||||
|
export { Tooltip } from './tooltip.js';
|
||||||
export type {
|
export type {
|
||||||
AdvancedMenuItem,
|
AdvancedMenuItem,
|
||||||
FatMenuItems,
|
FatMenuItems,
|
||||||
@@ -37,12 +38,11 @@ export {
|
|||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
|
|
||||||
export function effects() {
|
export function effects() {
|
||||||
tooltipEffects();
|
|
||||||
|
|
||||||
customElements.define('editor-toolbar-separator', EditorToolbarSeparator);
|
customElements.define('editor-toolbar-separator', EditorToolbarSeparator);
|
||||||
customElements.define('editor-toolbar', EditorToolbar);
|
customElements.define('editor-toolbar', EditorToolbar);
|
||||||
customElements.define('editor-icon-button', EditorIconButton);
|
customElements.define('editor-icon-button', EditorIconButton);
|
||||||
customElements.define('editor-menu-button', EditorMenuButton);
|
customElements.define('editor-menu-button', EditorMenuButton);
|
||||||
customElements.define('editor-menu-content', EditorMenuContent);
|
customElements.define('editor-menu-content', EditorMenuContent);
|
||||||
customElements.define('editor-menu-action', EditorMenuAction);
|
customElements.define('editor-menu-action', EditorMenuAction);
|
||||||
|
customElements.define('affine-tooltip', Tooltip);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import { Tooltip } from './tooltip.js';
|
|
||||||
|
|
||||||
export function effects() {
|
|
||||||
if (!customElements.get('affine-tooltip')) {
|
|
||||||
customElements.define('affine-tooltip', Tooltip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export { effects } from './effect.js';
|
|
||||||
export { Tooltip } from './tooltip.js';
|
|
||||||
@@ -65,7 +65,7 @@ export abstract class DataViewUILogicBase<
|
|||||||
return handler(context);
|
return handler(context);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setSelection(selection?: Selection) {
|
setSelection(selection?: Selection): void {
|
||||||
this.root.setSelection(selection);
|
this.root.setSelection(selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,9 +73,7 @@ export class MobileKanbanCell extends SignalWatcher(
|
|||||||
if (this.view.readonly$.value) {
|
if (this.view.readonly$.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const setSelection = this.kanbanViewLogic.setSelection.bind(
|
const setSelection = this.kanbanViewLogic.setSelection;
|
||||||
this.kanbanViewLogic
|
|
||||||
);
|
|
||||||
const viewId = this.kanbanViewLogic.view.id;
|
const viewId = this.kanbanViewLogic.view.id;
|
||||||
if (setSelection && viewId) {
|
if (setSelection && viewId) {
|
||||||
if (editing && this.cell?.beforeEnterEditMode() === false) {
|
if (editing && this.cell?.beforeEnterEditMode() === false) {
|
||||||
@@ -103,12 +101,12 @@ export class MobileKanbanCell extends SignalWatcher(
|
|||||||
this.disposables.add(
|
this.disposables.add(
|
||||||
effect(() => {
|
effect(() => {
|
||||||
const isEditing = this.isSelectionEditing$.value;
|
const isEditing = this.isSelectionEditing$.value;
|
||||||
if (isEditing && !this.isEditing$.peek()) {
|
if (isEditing) {
|
||||||
this.isEditing$.value = true;
|
this.isEditing$.value = true;
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
this._cell.value?.afterEnterEditingMode();
|
this._cell.value?.afterEnterEditingMode();
|
||||||
});
|
});
|
||||||
} else if (!isEditing && this.isEditing$.peek()) {
|
} else {
|
||||||
this._cell.value?.beforeExitEditingMode();
|
this._cell.value?.beforeExitEditingMode();
|
||||||
this.isEditing$.value = false;
|
this.isEditing$.value = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,9 +86,6 @@ export class MobileKanbanViewUILogic extends DataViewUILogicBase<
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderAddGroup = () => {
|
renderAddGroup = () => {
|
||||||
if (this.readonly) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const addGroup = this.groupManager.addGroup;
|
const addGroup = this.groupManager.addGroup;
|
||||||
if (!addGroup) {
|
if (!addGroup) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -68,9 +68,7 @@ export class MobileTableCell extends SignalWatcher(
|
|||||||
if (this.view.readonly$.value) {
|
if (this.view.readonly$.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const setSelection = this.tableViewLogic.setSelection.bind(
|
const setSelection = this.tableViewLogic.setSelection;
|
||||||
this.tableViewLogic
|
|
||||||
);
|
|
||||||
const viewId = this.tableViewLogic.view.id;
|
const viewId = this.tableViewLogic.view.id;
|
||||||
if (setSelection && viewId) {
|
if (setSelection && viewId) {
|
||||||
if (editing && this.cell?.beforeEnterEditMode() === false) {
|
if (editing && this.cell?.beforeEnterEditMode() === false) {
|
||||||
@@ -105,13 +103,13 @@ export class MobileTableCell extends SignalWatcher(
|
|||||||
this.disposables.add(
|
this.disposables.add(
|
||||||
effect(() => {
|
effect(() => {
|
||||||
const isEditing = this.isSelectionEditing$.value;
|
const isEditing = this.isSelectionEditing$.value;
|
||||||
if (isEditing && !this.isEditing$.peek()) {
|
if (isEditing) {
|
||||||
this.isEditing$.value = true;
|
this.isEditing$.value = true;
|
||||||
const cell = this._cell.value;
|
const cell = this._cell.value;
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
cell?.afterEnterEditingMode();
|
cell?.afterEnterEditingMode();
|
||||||
});
|
});
|
||||||
} else if (!isEditing && this.isEditing$.peek()) {
|
} else {
|
||||||
this._cell.value?.beforeExitEditingMode();
|
this._cell.value?.beforeExitEditingMode();
|
||||||
this.isEditing$.value = false;
|
this.isEditing$.value = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ export const mobileTableViewWrapper = css({
|
|||||||
position: 'relative',
|
position: 'relative',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
paddingBottom: '4px',
|
paddingBottom: '4px',
|
||||||
|
/**
|
||||||
|
* Disable horizontal scrolling to prevent crashes on iOS Safari
|
||||||
|
* See https://github.com/toeverything/AFFiNE/pull/12203
|
||||||
|
* and https://github.com/toeverything/blocksuite/pull/8784
|
||||||
|
*/
|
||||||
|
overflowX: 'hidden',
|
||||||
overflowY: 'hidden',
|
overflowY: 'hidden',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -88,9 +88,6 @@ export class FilterBar extends SignalWatcher(ShadowlessElement) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly addFilter = (e: MouseEvent) => {
|
private readonly addFilter = (e: MouseEvent) => {
|
||||||
if (this.dataViewLogic.root.config.dataSource.readonly$.peek()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const element = popupTargetFromElement(e.target as HTMLElement);
|
const element = popupTargetFromElement(e.target as HTMLElement);
|
||||||
popCreateFilter(element, {
|
popCreateFilter(element, {
|
||||||
vars: this.vars,
|
vars: this.vars,
|
||||||
|
|||||||
@@ -68,5 +68,5 @@ export function getHeadingBlocksFromDoc(
|
|||||||
ignoreEmpty = false
|
ignoreEmpty = false
|
||||||
) {
|
) {
|
||||||
const notes = getNotesFromStore(store, modes);
|
const notes = getNotesFromStore(store, modes);
|
||||||
return notes.flatMap(note => getHeadingBlocksFromNote(note, ignoreEmpty));
|
return notes.map(note => getHeadingBlocksFromNote(note, ignoreEmpty)).flat();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export * from './adapter';
|
export * from './adapter';
|
||||||
export * from './brush-tool';
|
export * from './brush-tool';
|
||||||
|
export * from './element-renderer';
|
||||||
export * from './eraser-tool';
|
export * from './eraser-tool';
|
||||||
export * from './highlighter-tool';
|
export * from './highlighter-tool';
|
||||||
export * from './renderer';
|
|
||||||
export * from './toolbar/configs';
|
export * from './toolbar/configs';
|
||||||
export * from './toolbar/senior-tool';
|
export * from './toolbar/senior-tool';
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
import {
|
|
||||||
DomElementRendererExtension,
|
|
||||||
type DomRenderer,
|
|
||||||
} from '@blocksuite/affine-block-surface';
|
|
||||||
import type { BrushElementModel } from '@blocksuite/affine-model';
|
|
||||||
import { DefaultTheme } from '@blocksuite/affine-model';
|
|
||||||
|
|
||||||
export const BrushDomRendererExtension = DomElementRendererExtension(
|
|
||||||
'brush',
|
|
||||||
(
|
|
||||||
model: BrushElementModel,
|
|
||||||
domElement: HTMLElement,
|
|
||||||
renderer: DomRenderer
|
|
||||||
) => {
|
|
||||||
const { zoom } = renderer.viewport;
|
|
||||||
const [, , w, h] = model.deserializedXYWH;
|
|
||||||
|
|
||||||
// Early return if invalid dimensions
|
|
||||||
if (w <= 0 || h <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Early return if no commands
|
|
||||||
if (!model.commands) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear previous content
|
|
||||||
domElement.innerHTML = '';
|
|
||||||
|
|
||||||
// Get color value
|
|
||||||
const color = renderer.getColorValue(model.color, DefaultTheme.black, true);
|
|
||||||
|
|
||||||
// Create SVG element
|
|
||||||
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
||||||
svg.style.position = 'absolute';
|
|
||||||
svg.style.left = '0';
|
|
||||||
svg.style.top = '0';
|
|
||||||
svg.style.width = `${w * zoom}px`;
|
|
||||||
svg.style.height = `${h * zoom}px`;
|
|
||||||
svg.style.overflow = 'visible';
|
|
||||||
svg.style.pointerEvents = 'none';
|
|
||||||
svg.setAttribute('viewBox', `0 0 ${w} ${h}`);
|
|
||||||
|
|
||||||
// Apply rotation transform
|
|
||||||
if (model.rotate !== 0) {
|
|
||||||
svg.style.transform = `rotate(${model.rotate}deg)`;
|
|
||||||
svg.style.transformOrigin = 'center';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create path element for the brush stroke
|
|
||||||
const pathElement = document.createElementNS(
|
|
||||||
'http://www.w3.org/2000/svg',
|
|
||||||
'path'
|
|
||||||
);
|
|
||||||
pathElement.setAttribute('d', model.commands);
|
|
||||||
pathElement.setAttribute('fill', color);
|
|
||||||
pathElement.setAttribute('stroke', 'none');
|
|
||||||
|
|
||||||
svg.append(pathElement);
|
|
||||||
domElement.replaceChildren(svg);
|
|
||||||
|
|
||||||
// Set element size and position
|
|
||||||
domElement.style.width = `${w * zoom}px`;
|
|
||||||
domElement.style.height = `${h * zoom}px`;
|
|
||||||
domElement.style.overflow = 'visible';
|
|
||||||
domElement.style.pointerEvents = 'none';
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
import {
|
|
||||||
DomElementRendererExtension,
|
|
||||||
type DomRenderer,
|
|
||||||
} from '@blocksuite/affine-block-surface';
|
|
||||||
import type { HighlighterElementModel } from '@blocksuite/affine-model';
|
|
||||||
import { DefaultTheme } from '@blocksuite/affine-model';
|
|
||||||
|
|
||||||
export const HighlighterDomRendererExtension = DomElementRendererExtension(
|
|
||||||
'highlighter',
|
|
||||||
(
|
|
||||||
model: HighlighterElementModel,
|
|
||||||
domElement: HTMLElement,
|
|
||||||
renderer: DomRenderer
|
|
||||||
) => {
|
|
||||||
const { zoom } = renderer.viewport;
|
|
||||||
const [, , w, h] = model.deserializedXYWH;
|
|
||||||
|
|
||||||
// Early return if invalid dimensions
|
|
||||||
if (w <= 0 || h <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Early return if no commands
|
|
||||||
if (!model.commands) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear previous content
|
|
||||||
domElement.innerHTML = '';
|
|
||||||
|
|
||||||
// Get color value
|
|
||||||
const color = renderer.getColorValue(
|
|
||||||
model.color,
|
|
||||||
DefaultTheme.hightlighterColor,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create SVG element
|
|
||||||
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
||||||
svg.style.position = 'absolute';
|
|
||||||
svg.style.left = '0';
|
|
||||||
svg.style.top = '0';
|
|
||||||
svg.style.width = `${w * zoom}px`;
|
|
||||||
svg.style.height = `${h * zoom}px`;
|
|
||||||
svg.style.overflow = 'visible';
|
|
||||||
svg.style.pointerEvents = 'none';
|
|
||||||
svg.setAttribute('viewBox', `0 0 ${w} ${h}`);
|
|
||||||
|
|
||||||
// Apply rotation transform
|
|
||||||
if (model.rotate !== 0) {
|
|
||||||
svg.style.transform = `rotate(${model.rotate}deg)`;
|
|
||||||
svg.style.transformOrigin = 'center';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create path element for the highlighter stroke
|
|
||||||
const pathElement = document.createElementNS(
|
|
||||||
'http://www.w3.org/2000/svg',
|
|
||||||
'path'
|
|
||||||
);
|
|
||||||
pathElement.setAttribute('d', model.commands);
|
|
||||||
pathElement.setAttribute('fill', color);
|
|
||||||
pathElement.setAttribute('stroke', 'none');
|
|
||||||
|
|
||||||
svg.append(pathElement);
|
|
||||||
domElement.replaceChildren(svg);
|
|
||||||
|
|
||||||
// Set element size and position
|
|
||||||
domElement.style.width = `${w * zoom}px`;
|
|
||||||
domElement.style.height = `${h * zoom}px`;
|
|
||||||
domElement.style.overflow = 'visible';
|
|
||||||
domElement.style.pointerEvents = 'none';
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export { BrushDomRendererExtension } from './brush';
|
|
||||||
export { HighlighterDomRendererExtension } from './highlighter';
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export { BrushElementRendererExtension } from './brush';
|
|
||||||
export { HighlighterElementRendererExtension } from './highlighter';
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export * from './dom';
|
|
||||||
export * from './element';
|
|
||||||
@@ -5,14 +5,9 @@ import {
|
|||||||
|
|
||||||
import { BrushTool } from './brush-tool';
|
import { BrushTool } from './brush-tool';
|
||||||
import { effects } from './effects';
|
import { effects } from './effects';
|
||||||
|
import { BrushElementRendererExtension } from './element-renderer';
|
||||||
import { EraserTool } from './eraser-tool';
|
import { EraserTool } from './eraser-tool';
|
||||||
import { HighlighterTool } from './highlighter-tool';
|
import { HighlighterTool } from './highlighter-tool';
|
||||||
import {
|
|
||||||
BrushDomRendererExtension,
|
|
||||||
BrushElementRendererExtension,
|
|
||||||
HighlighterDomRendererExtension,
|
|
||||||
HighlighterElementRendererExtension,
|
|
||||||
} from './renderer';
|
|
||||||
import {
|
import {
|
||||||
brushToolbarExtension,
|
brushToolbarExtension,
|
||||||
highlighterToolbarExtension,
|
highlighterToolbarExtension,
|
||||||
@@ -35,9 +30,6 @@ export class BrushViewExtension extends ViewExtensionProvider {
|
|||||||
context.register(HighlighterTool);
|
context.register(HighlighterTool);
|
||||||
|
|
||||||
context.register(BrushElementRendererExtension);
|
context.register(BrushElementRendererExtension);
|
||||||
context.register(BrushDomRendererExtension);
|
|
||||||
context.register(HighlighterElementRendererExtension);
|
|
||||||
context.register(HighlighterDomRendererExtension);
|
|
||||||
|
|
||||||
context.register(brushToolbarExtension);
|
context.register(brushToolbarExtension);
|
||||||
context.register(highlighterToolbarExtension);
|
context.register(highlighterToolbarExtension);
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { DomElementRendererExtension } from '@blocksuite/affine-block-surface';
|
||||||
|
|
||||||
|
import { connectorDomRenderer } from './connector-dom/index.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension to register the DOM-based renderer for 'connector' elements.
|
||||||
|
*/
|
||||||
|
export const ConnectorDomRendererExtension = DomElementRendererExtension(
|
||||||
|
'connector',
|
||||||
|
connectorDomRenderer
|
||||||
|
);
|
||||||
+10
-25
@@ -1,18 +1,14 @@
|
|||||||
import {
|
import type { DomRenderer } from '@blocksuite/affine-block-surface';
|
||||||
DomElementRendererExtension,
|
|
||||||
type DomRenderer,
|
|
||||||
} from '@blocksuite/affine-block-surface';
|
|
||||||
import {
|
import {
|
||||||
type ConnectorElementModel,
|
type ConnectorElementModel,
|
||||||
ConnectorMode,
|
ConnectorMode,
|
||||||
DefaultTheme,
|
DefaultTheme,
|
||||||
type LocalConnectorElementModel,
|
|
||||||
type PointStyle,
|
type PointStyle,
|
||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
import { PointLocation, SVGPathBuilder } from '@blocksuite/global/gfx';
|
import { PointLocation, SVGPathBuilder } from '@blocksuite/global/gfx';
|
||||||
|
|
||||||
import { isConnectorWithLabel } from '../connector-manager';
|
import { isConnectorWithLabel } from '../../connector-manager.js';
|
||||||
import { DEFAULT_ARROW_SIZE } from './utils';
|
import { DEFAULT_ARROW_SIZE } from '../utils.js';
|
||||||
|
|
||||||
interface PathBounds {
|
interface PathBounds {
|
||||||
minX: number;
|
minX: number;
|
||||||
@@ -225,8 +221,8 @@ function renderConnectorLabel(
|
|||||||
* @param element - The HTMLElement to apply the connector's styles to.
|
* @param element - The HTMLElement to apply the connector's styles to.
|
||||||
* @param renderer - The main DOMRenderer instance, providing access to viewport and color utilities.
|
* @param renderer - The main DOMRenderer instance, providing access to viewport and color utilities.
|
||||||
*/
|
*/
|
||||||
export const connectorBaseDomRenderer = (
|
export const connectorDomRenderer = (
|
||||||
model: ConnectorElementModel | LocalConnectorElementModel,
|
model: ConnectorElementModel,
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
renderer: DomRenderer
|
renderer: DomRenderer
|
||||||
): void => {
|
): void => {
|
||||||
@@ -362,21 +358,10 @@ export const connectorBaseDomRenderer = (
|
|||||||
element.style.height = `${model.h * zoom}px`;
|
element.style.height = `${model.h * zoom}px`;
|
||||||
element.style.overflow = 'visible';
|
element.style.overflow = 'visible';
|
||||||
element.style.pointerEvents = 'none';
|
element.style.pointerEvents = 'none';
|
||||||
};
|
|
||||||
|
|
||||||
export const connectorDomRenderer = (
|
// Set z-index for layering
|
||||||
model: ConnectorElementModel,
|
element.style.zIndex = renderer.layerManager.getZIndex(model).toString();
|
||||||
element: HTMLElement,
|
|
||||||
renderer: DomRenderer
|
|
||||||
): void => {
|
|
||||||
connectorBaseDomRenderer(model, element, renderer);
|
|
||||||
renderConnectorLabel(model, element, renderer, renderer.viewport.zoom);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
// Render label if present
|
||||||
* Extension to register the DOM-based renderer for 'connector' elements.
|
renderConnectorLabel(model, element, renderer, zoom);
|
||||||
*/
|
};
|
||||||
export const ConnectorDomRendererExtension = DomElementRendererExtension(
|
|
||||||
'connector',
|
|
||||||
connectorDomRenderer
|
|
||||||
);
|
|
||||||
+2
-2
@@ -25,7 +25,7 @@ import {
|
|||||||
} from '@blocksuite/global/gfx';
|
} from '@blocksuite/global/gfx';
|
||||||
import { deltaInsertsToChunks } from '@blocksuite/std/inline';
|
import { deltaInsertsToChunks } from '@blocksuite/std/inline';
|
||||||
|
|
||||||
import { isConnectorWithLabel } from '../connector-manager';
|
import { isConnectorWithLabel } from '../connector-manager.js';
|
||||||
import {
|
import {
|
||||||
DEFAULT_ARROW_SIZE,
|
DEFAULT_ARROW_SIZE,
|
||||||
getArrowOptions,
|
getArrowOptions,
|
||||||
@@ -33,7 +33,7 @@ import {
|
|||||||
renderCircle,
|
renderCircle,
|
||||||
renderDiamond,
|
renderDiamond,
|
||||||
renderTriangle,
|
renderTriangle,
|
||||||
} from './utils';
|
} from './utils.js';
|
||||||
|
|
||||||
export const connector: ElementRenderer<
|
export const connector: ElementRenderer<
|
||||||
ConnectorElementModel | LocalConnectorElementModel
|
ConnectorElementModel | LocalConnectorElementModel
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
export * from './adapter';
|
export * from './adapter';
|
||||||
export * from './connector-manager';
|
export * from './connector-manager';
|
||||||
export * from './connector-tool';
|
export * from './connector-tool';
|
||||||
|
export * from './element-renderer';
|
||||||
|
export { ConnectorDomRendererExtension } from './element-renderer/connector-dom';
|
||||||
export * from './element-transform';
|
export * from './element-transform';
|
||||||
export * from './renderer';
|
|
||||||
export * from './text';
|
export * from './text';
|
||||||
export * from './toolbar/config';
|
export * from './toolbar/config';
|
||||||
export * from './toolbar/quick-tool';
|
export * from './toolbar/quick-tool';
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
export * from './dom-renderer';
|
|
||||||
export * from './element-renderer';
|
|
||||||
@@ -6,11 +6,9 @@ import {
|
|||||||
import { ConnectionOverlay } from './connector-manager';
|
import { ConnectionOverlay } from './connector-manager';
|
||||||
import { ConnectorTool } from './connector-tool';
|
import { ConnectorTool } from './connector-tool';
|
||||||
import { effects } from './effects';
|
import { effects } from './effects';
|
||||||
|
import { ConnectorElementRendererExtension } from './element-renderer';
|
||||||
|
import { ConnectorDomRendererExtension } from './element-renderer/connector-dom';
|
||||||
import { ConnectorFilter } from './element-transform';
|
import { ConnectorFilter } from './element-transform';
|
||||||
import {
|
|
||||||
ConnectorDomRendererExtension,
|
|
||||||
ConnectorElementRendererExtension,
|
|
||||||
} from './renderer';
|
|
||||||
import { connectorToolbarExtension } from './toolbar/config';
|
import { connectorToolbarExtension } from './toolbar/config';
|
||||||
import { connectorQuickTool } from './toolbar/quick-tool';
|
import { connectorQuickTool } from './toolbar/quick-tool';
|
||||||
import { ConnectorElementView, ConnectorInteraction } from './view/view';
|
import { ConnectorElementView, ConnectorInteraction } from './view/view';
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import {
|
|||||||
import type { GroupElementModel } from '@blocksuite/affine-model';
|
import type { GroupElementModel } from '@blocksuite/affine-model';
|
||||||
import { Bound } from '@blocksuite/global/gfx';
|
import { Bound } from '@blocksuite/global/gfx';
|
||||||
|
|
||||||
import { titleRenderParams } from './utils';
|
import { titleRenderParams } from './utils.js';
|
||||||
|
|
||||||
export const group: ElementRenderer<GroupElementModel> = (
|
export const group: ElementRenderer<GroupElementModel> = (
|
||||||
model,
|
model,
|
||||||
+1
-1
@@ -13,7 +13,7 @@ import {
|
|||||||
GROUP_TITLE_FONT_SIZE,
|
GROUP_TITLE_FONT_SIZE,
|
||||||
GROUP_TITLE_OFFSET,
|
GROUP_TITLE_OFFSET,
|
||||||
GROUP_TITLE_PADDING,
|
GROUP_TITLE_PADDING,
|
||||||
} from './consts';
|
} from './consts.js';
|
||||||
|
|
||||||
export function titleRenderParams(group: GroupElementModel, zoom: number) {
|
export function titleRenderParams(group: GroupElementModel, zoom: number) {
|
||||||
let text = group.title.toString().trim();
|
let text = group.title.toString().trim();
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
export * from './adapter';
|
export * from './adapter';
|
||||||
export * from './command';
|
export * from './command';
|
||||||
|
export * from './element-renderer';
|
||||||
export * from './element-view';
|
export * from './element-view';
|
||||||
export * from './renderer';
|
|
||||||
export * from './text/text';
|
export * from './text/text';
|
||||||
export * from './toolbar/config';
|
export * from './toolbar/config';
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
import { DomElementRendererExtension } from '@blocksuite/affine-block-surface';
|
|
||||||
import { FontWeight, type GroupElementModel } from '@blocksuite/affine-model';
|
|
||||||
|
|
||||||
import {
|
|
||||||
GROUP_TITLE_FONT,
|
|
||||||
GROUP_TITLE_FONT_SIZE,
|
|
||||||
GROUP_TITLE_PADDING,
|
|
||||||
} from './consts';
|
|
||||||
import { titleRenderParams } from './utils';
|
|
||||||
|
|
||||||
export const GroupDomRendererExtension = DomElementRendererExtension(
|
|
||||||
'group',
|
|
||||||
(model: GroupElementModel, domElement, renderer) => {
|
|
||||||
const { zoom } = renderer.viewport;
|
|
||||||
const [, , w, h] = model.deserializedXYWH;
|
|
||||||
|
|
||||||
const renderParams = titleRenderParams(model, zoom);
|
|
||||||
model.externalXYWH = renderParams.titleBound.serialize();
|
|
||||||
|
|
||||||
domElement.innerHTML = '';
|
|
||||||
domElement.style.outlineColor = '';
|
|
||||||
domElement.style.outlineWidth = '';
|
|
||||||
domElement.style.outlineStyle = '';
|
|
||||||
|
|
||||||
const elements = renderer.provider.selectedElements?.() || [];
|
|
||||||
|
|
||||||
const renderTitle = () => {
|
|
||||||
const { text } = renderParams;
|
|
||||||
const titleElement = document.createElement('div');
|
|
||||||
titleElement.style.transform = `translate(0, -100%)`;
|
|
||||||
titleElement.style.fontFamily = GROUP_TITLE_FONT;
|
|
||||||
titleElement.style.fontWeight = `${FontWeight.Regular}`;
|
|
||||||
titleElement.style.fontStyle = 'normal';
|
|
||||||
titleElement.style.fontSize = `${GROUP_TITLE_FONT_SIZE}px`;
|
|
||||||
titleElement.style.color = renderer.getPropertyValue('--affine-blue');
|
|
||||||
titleElement.style.textAlign = 'left';
|
|
||||||
titleElement.style.padding = `${GROUP_TITLE_PADDING[0]}px ${GROUP_TITLE_PADDING[1]}px`;
|
|
||||||
titleElement.textContent = text;
|
|
||||||
domElement.replaceChildren(titleElement);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (elements.includes(model.id)) {
|
|
||||||
if (model.showTitle) {
|
|
||||||
renderTitle();
|
|
||||||
} else {
|
|
||||||
domElement.style.outlineColor =
|
|
||||||
renderer.getPropertyValue('--affine-blue');
|
|
||||||
domElement.style.outlineWidth = '2px';
|
|
||||||
domElement.style.outlineStyle = 'solid';
|
|
||||||
}
|
|
||||||
} else if (model.childElements.some(child => elements.includes(child.id))) {
|
|
||||||
domElement.style.outlineColor = '#8FD1FF';
|
|
||||||
domElement.style.outlineWidth = '2px';
|
|
||||||
domElement.style.outlineStyle = 'solid';
|
|
||||||
}
|
|
||||||
|
|
||||||
domElement.style.width = `${w * zoom}px`;
|
|
||||||
domElement.style.height = `${h * zoom}px`;
|
|
||||||
domElement.style.overflow = 'visible';
|
|
||||||
domElement.style.pointerEvents = 'none';
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export * from './dom-renderer';
|
|
||||||
export * from './element-renderer';
|
|
||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
GROUP_TITLE_FONT_SIZE,
|
GROUP_TITLE_FONT_SIZE,
|
||||||
GROUP_TITLE_OFFSET,
|
GROUP_TITLE_OFFSET,
|
||||||
GROUP_TITLE_PADDING,
|
GROUP_TITLE_PADDING,
|
||||||
} from '../renderer/consts';
|
} from '../element-renderer/consts';
|
||||||
|
|
||||||
export function mountGroupTitleEditor(
|
export function mountGroupTitleEditor(
|
||||||
group: GroupElementModel,
|
group: GroupElementModel,
|
||||||
|
|||||||
@@ -4,12 +4,9 @@ import {
|
|||||||
} from '@blocksuite/affine-ext-loader';
|
} from '@blocksuite/affine-ext-loader';
|
||||||
|
|
||||||
import { effects } from './effects';
|
import { effects } from './effects';
|
||||||
|
import { GroupElementRendererExtension } from './element-renderer';
|
||||||
import { GroupElementView, GroupInteraction } from './element-view';
|
import { GroupElementView, GroupInteraction } from './element-view';
|
||||||
import { GroupInteractionExtension } from './interaction-ext';
|
import { GroupInteractionExtension } from './interaction-ext';
|
||||||
import {
|
|
||||||
GroupDomRendererExtension,
|
|
||||||
GroupElementRendererExtension,
|
|
||||||
} from './renderer';
|
|
||||||
import { groupToolbarExtension } from './toolbar/config';
|
import { groupToolbarExtension } from './toolbar/config';
|
||||||
|
|
||||||
export class GroupViewExtension extends ViewExtensionProvider {
|
export class GroupViewExtension extends ViewExtensionProvider {
|
||||||
@@ -23,7 +20,6 @@ export class GroupViewExtension extends ViewExtensionProvider {
|
|||||||
override setup(context: ViewExtensionContext) {
|
override setup(context: ViewExtensionContext) {
|
||||||
super.setup(context);
|
super.setup(context);
|
||||||
context.register(GroupElementRendererExtension);
|
context.register(GroupElementRendererExtension);
|
||||||
context.register(GroupDomRendererExtension);
|
|
||||||
context.register(GroupElementView);
|
context.register(GroupElementView);
|
||||||
if (this.isEdgeless(context.scope)) {
|
if (this.isEdgeless(context.scope)) {
|
||||||
context.register(groupToolbarExtension);
|
context.register(groupToolbarExtension);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export * from './adapter';
|
export * from './adapter';
|
||||||
|
export * from './element-renderer';
|
||||||
export * from './indicator-overlay';
|
export * from './indicator-overlay';
|
||||||
export * from './interactivity';
|
export * from './interactivity';
|
||||||
export * from './renderer';
|
|
||||||
export * from './toolbar/config';
|
export * from './toolbar/config';
|
||||||
export * from './toolbar/senior-tool';
|
export * from './toolbar/senior-tool';
|
||||||
export * from './utils';
|
export * from './utils';
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
import { DomElementRendererExtension } from '@blocksuite/affine-block-surface';
|
|
||||||
import {
|
|
||||||
connectorBaseDomRenderer,
|
|
||||||
ConnectorPathGenerator,
|
|
||||||
} from '@blocksuite/affine-gfx-connector';
|
|
||||||
import type {
|
|
||||||
MindmapElementModel,
|
|
||||||
MindmapNode,
|
|
||||||
} from '@blocksuite/affine-model';
|
|
||||||
import type { GfxModel } from '@blocksuite/std/gfx';
|
|
||||||
|
|
||||||
export const MindmapDomRendererExtension = DomElementRendererExtension(
|
|
||||||
'mindmap',
|
|
||||||
(model: MindmapElementModel, domElement, renderer) => {
|
|
||||||
const bound = model.elementBound;
|
|
||||||
|
|
||||||
const { zoom } = renderer.viewport;
|
|
||||||
// Set element size and position
|
|
||||||
domElement.style.width = `${bound.w * zoom}px`;
|
|
||||||
domElement.style.height = `${bound.h * zoom}px`;
|
|
||||||
domElement.style.overflow = 'visible';
|
|
||||||
domElement.style.pointerEvents = 'none';
|
|
||||||
|
|
||||||
const newChildren: HTMLDivElement[] = [];
|
|
||||||
|
|
||||||
const traverse = (node: MindmapNode) => {
|
|
||||||
const connectors = model.getConnectors(node);
|
|
||||||
if (!connectors) return;
|
|
||||||
|
|
||||||
connectors.reverse().forEach(result => {
|
|
||||||
const { connector, outdated } = result;
|
|
||||||
const elementGetter = (id: string) =>
|
|
||||||
model.surface.getElementById(id) ??
|
|
||||||
(model.surface.store.getModelById(id) as GfxModel);
|
|
||||||
|
|
||||||
if (outdated) {
|
|
||||||
ConnectorPathGenerator.updatePath(connector, null, elementGetter);
|
|
||||||
}
|
|
||||||
|
|
||||||
const connectorContainer = document.createElement('div');
|
|
||||||
connectorContainer.style.position = 'absolute';
|
|
||||||
connectorContainer.style.transformOrigin = 'top left';
|
|
||||||
const geometricStyles = {
|
|
||||||
left: `${(connector.x - bound.x) * zoom}px`,
|
|
||||||
top: `${(connector.y - bound.y) * zoom}px`,
|
|
||||||
};
|
|
||||||
const opacityStyle = { opacity: node.element.opacity };
|
|
||||||
Object.assign(connectorContainer.style, geometricStyles, opacityStyle);
|
|
||||||
|
|
||||||
connectorBaseDomRenderer(connector, connectorContainer, renderer);
|
|
||||||
newChildren.push(connectorContainer);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (node.detail.collapsed) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
node.children.forEach(traverse);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
model.tree && traverse(model.tree);
|
|
||||||
|
|
||||||
domElement.replaceChildren(...newChildren);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export * from './dom-renderer';
|
|
||||||
export * from './element-renderer';
|
|
||||||
@@ -4,12 +4,9 @@ import {
|
|||||||
} from '@blocksuite/affine-ext-loader';
|
} from '@blocksuite/affine-ext-loader';
|
||||||
|
|
||||||
import { effects } from './effects';
|
import { effects } from './effects';
|
||||||
|
import { MindmapElementRendererExtension } from './element-renderer';
|
||||||
import { MindMapIndicatorOverlay } from './indicator-overlay';
|
import { MindMapIndicatorOverlay } from './indicator-overlay';
|
||||||
import { MindMapDragExtension } from './interactivity';
|
import { MindMapDragExtension } from './interactivity';
|
||||||
import {
|
|
||||||
MindmapDomRendererExtension,
|
|
||||||
MindmapElementRendererExtension,
|
|
||||||
} from './renderer';
|
|
||||||
import {
|
import {
|
||||||
mindmapToolbarExtension,
|
mindmapToolbarExtension,
|
||||||
shapeMindmapToolbarExtension,
|
shapeMindmapToolbarExtension,
|
||||||
@@ -28,7 +25,6 @@ export class MindmapViewExtension extends ViewExtensionProvider {
|
|||||||
override setup(context: ViewExtensionContext) {
|
override setup(context: ViewExtensionContext) {
|
||||||
super.setup(context);
|
super.setup(context);
|
||||||
context.register(MindmapElementRendererExtension);
|
context.register(MindmapElementRendererExtension);
|
||||||
context.register(MindmapDomRendererExtension);
|
|
||||||
context.register(mindMapSeniorTool);
|
context.register(mindMapSeniorTool);
|
||||||
context.register(mindmapToolbarExtension);
|
context.register(mindmapToolbarExtension);
|
||||||
context.register(shapeMindmapToolbarExtension);
|
context.register(shapeMindmapToolbarExtension);
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import {
|
import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
|
||||||
DefaultTool,
|
|
||||||
EdgelessLegacySlotIdentifier,
|
|
||||||
} from '@blocksuite/affine-block-surface';
|
|
||||||
import { on } from '@blocksuite/affine-shared/utils';
|
import { on } from '@blocksuite/affine-shared/utils';
|
||||||
import type { PointerEventState } from '@blocksuite/std';
|
import type { PointerEventState } from '@blocksuite/std';
|
||||||
import { BaseTool, MouseButton, type ToolOptions } from '@blocksuite/std/gfx';
|
import { BaseTool, MouseButton, type ToolOptions } from '@blocksuite/std/gfx';
|
||||||
@@ -67,15 +64,12 @@ export class PanTool extends BaseTool<PanToolOption> {
|
|||||||
const { toolType, options: originalToolOptions } = currentTool;
|
const { toolType, options: originalToolOptions } = currentTool;
|
||||||
const selectionToRestore = this.gfx.selection.surfaceSelections;
|
const selectionToRestore = this.gfx.selection.surfaceSelections;
|
||||||
if (!toolType) return;
|
if (!toolType) return;
|
||||||
// restore to DefaultTool if previous tool is CopilotTool
|
|
||||||
if (toolType.toolName === 'copilot') {
|
|
||||||
this.controller.setTool(DefaultTool);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let finalOptions: ToolOptions<BaseTool<any>> | undefined =
|
let finalOptions: ToolOptions<BaseTool<any>> | undefined =
|
||||||
originalToolOptions;
|
originalToolOptions;
|
||||||
if (toolType.toolName === 'frameNavigator') {
|
const PRESENT_TOOL_NAME = 'frameNavigator';
|
||||||
|
|
||||||
|
if (toolType.toolName === PRESENT_TOOL_NAME) {
|
||||||
// When restoring PresentTool (frameNavigator) after a temporary pan (e.g., via middle mouse button),
|
// When restoring PresentTool (frameNavigator) after a temporary pan (e.g., via middle mouse button),
|
||||||
// set 'restoredAfterPan' to true. This allows PresentTool to avoid an unwanted viewport reset
|
// set 'restoredAfterPan' to true. This allows PresentTool to avoid an unwanted viewport reset
|
||||||
// and maintain the panned position.
|
// and maintain the panned position.
|
||||||
@@ -99,17 +93,15 @@ export class PanTool extends BaseTool<PanToolOption> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
this.controller.setTool(PanTool, {
|
||||||
this.controller.setTool(PanTool, {
|
panning: true,
|
||||||
panning: true,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const dispose = on(document, 'pointerup', evt => {
|
const dispose = on(document, 'pointerup', evt => {
|
||||||
if (evt.button === MouseButton.MIDDLE) {
|
if (evt.button === MouseButton.MIDDLE) {
|
||||||
restoreToPrevious();
|
restoreToPrevious();
|
||||||
|
dispose();
|
||||||
}
|
}
|
||||||
dispose();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
export * from './highlighter';
|
||||||
export * from './shape';
|
export * from './shape';
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { DomRenderer } from '@blocksuite/affine-block-surface';
|
import type { DomRenderer } from '@blocksuite/affine-block-surface';
|
||||||
import { isRTL } from '@blocksuite/affine-gfx-text';
|
|
||||||
import type { ShapeElementModel } from '@blocksuite/affine-model';
|
import type { ShapeElementModel } from '@blocksuite/affine-model';
|
||||||
import { DefaultTheme } from '@blocksuite/affine-model';
|
import { DefaultTheme } from '@blocksuite/affine-model';
|
||||||
import { SVGShapeBuilder } from '@blocksuite/global/gfx';
|
import { SVGShapeBuilder } from '@blocksuite/global/gfx';
|
||||||
@@ -100,8 +99,6 @@ export const shapeDomRenderer = (
|
|||||||
const unscaledWidth = model.w;
|
const unscaledWidth = model.w;
|
||||||
const unscaledHeight = model.h;
|
const unscaledHeight = model.h;
|
||||||
|
|
||||||
const newChildren: Element[] = [];
|
|
||||||
|
|
||||||
const fillColor = renderer.getColorValue(
|
const fillColor = renderer.getColorValue(
|
||||||
model.fillColor,
|
model.fillColor,
|
||||||
DefaultTheme.shapeFillColor,
|
DefaultTheme.shapeFillColor,
|
||||||
@@ -173,7 +170,8 @@ export const shapeDomRenderer = (
|
|||||||
}
|
}
|
||||||
svg.append(polygon);
|
svg.append(polygon);
|
||||||
|
|
||||||
newChildren.push(svg);
|
// Replace existing children to avoid memory leaks
|
||||||
|
element.replaceChildren(svg);
|
||||||
} else {
|
} else {
|
||||||
// Standard rendering for other shapes (e.g., rect, ellipse)
|
// Standard rendering for other shapes (e.g., rect, ellipse)
|
||||||
// innerHTML was already cleared by applyShapeSpecificStyles if necessary
|
// innerHTML was already cleared by applyShapeSpecificStyles if necessary
|
||||||
@@ -181,43 +179,10 @@ export const shapeDomRenderer = (
|
|||||||
applyBorderStyles(model, element, strokeColor, zoom); // Uses standard CSS border
|
applyBorderStyles(model, element, strokeColor, zoom); // Uses standard CSS border
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.textDisplay && model.text) {
|
|
||||||
const str = model.text.toString();
|
|
||||||
const textElement = document.createElement('div');
|
|
||||||
if (isRTL(str)) {
|
|
||||||
textElement.dir = 'rtl';
|
|
||||||
}
|
|
||||||
textElement.style.position = 'absolute';
|
|
||||||
textElement.style.inset = '0';
|
|
||||||
textElement.style.display = 'flex';
|
|
||||||
textElement.style.flexDirection = 'column';
|
|
||||||
textElement.style.justifyContent =
|
|
||||||
model.textVerticalAlign === 'center'
|
|
||||||
? 'center'
|
|
||||||
: model.textVerticalAlign === 'top'
|
|
||||||
? 'flex-start'
|
|
||||||
: 'flex-end';
|
|
||||||
textElement.style.whiteSpace = 'pre-wrap';
|
|
||||||
textElement.style.wordBreak = 'break-word';
|
|
||||||
textElement.style.textAlign = model.textAlign;
|
|
||||||
textElement.style.alignmentBaseline = 'alphabetic';
|
|
||||||
textElement.style.fontFamily = model.fontFamily;
|
|
||||||
textElement.style.fontSize = `${model.fontSize * zoom}px`;
|
|
||||||
textElement.style.fontWeight = model.fontWeight;
|
|
||||||
textElement.style.color = renderer.getColorValue(
|
|
||||||
model.color,
|
|
||||||
DefaultTheme.shapeTextColor,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
textElement.textContent = str;
|
|
||||||
newChildren.push(textElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace existing children to avoid memory leaks
|
|
||||||
element.replaceChildren(...newChildren);
|
|
||||||
|
|
||||||
applyTransformStyles(model, element);
|
applyTransformStyles(model, element);
|
||||||
|
|
||||||
|
element.style.zIndex = renderer.layerManager.getZIndex(model).toString();
|
||||||
|
|
||||||
manageClassNames(model, element);
|
manageClassNames(model, element);
|
||||||
applyShadowStyles(model, element, renderer);
|
applyShadowStyles(model, element, renderer);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import {
|
|||||||
} from '@blocksuite/affine-ext-loader';
|
} from '@blocksuite/affine-ext-loader';
|
||||||
|
|
||||||
import { effects } from './effects';
|
import { effects } from './effects';
|
||||||
import { ShapeElementRendererExtension } from './element-renderer';
|
import {
|
||||||
|
HighlighterElementRendererExtension,
|
||||||
|
ShapeElementRendererExtension,
|
||||||
|
} from './element-renderer';
|
||||||
import { ShapeDomRendererExtension } from './element-renderer/shape-dom';
|
import { ShapeDomRendererExtension } from './element-renderer/shape-dom';
|
||||||
import { ShapeElementView, ShapeViewInteraction } from './element-view';
|
import { ShapeElementView, ShapeViewInteraction } from './element-view';
|
||||||
import { ShapeTool } from './shape-tool';
|
import { ShapeTool } from './shape-tool';
|
||||||
@@ -21,6 +24,7 @@ export class ShapeViewExtension extends ViewExtensionProvider {
|
|||||||
override setup(context: ViewExtensionContext) {
|
override setup(context: ViewExtensionContext) {
|
||||||
super.setup(context);
|
super.setup(context);
|
||||||
if (this.isEdgeless(context.scope)) {
|
if (this.isEdgeless(context.scope)) {
|
||||||
|
context.register(HighlighterElementRendererExtension);
|
||||||
context.register(ShapeElementRendererExtension);
|
context.register(ShapeElementRendererExtension);
|
||||||
context.register(ShapeDomRendererExtension);
|
context.register(ShapeDomRendererExtension);
|
||||||
context.register(ShapeElementView);
|
context.register(ShapeElementView);
|
||||||
|
|||||||
@@ -116,7 +116,6 @@ export class EdgelessTemplateButton extends EdgelessToolbarToolMixin(
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
private _cleanup: (() => void) | null = null;
|
private _cleanup: (() => void) | null = null;
|
||||||
private _autoUpdateCleanup: (() => void) | null = null;
|
|
||||||
|
|
||||||
private _prevTool: ToolOptionWithType | null = null;
|
private _prevTool: ToolOptionWithType | null = null;
|
||||||
|
|
||||||
@@ -129,11 +128,6 @@ export class EdgelessTemplateButton extends EdgelessToolbarToolMixin(
|
|||||||
return [TemplateCard1[theme], TemplateCard2[theme], TemplateCard3[theme]];
|
return [TemplateCard1[theme], TemplateCard2[theme], TemplateCard3[theme]];
|
||||||
}
|
}
|
||||||
|
|
||||||
override connectedCallback() {
|
|
||||||
super.connectedCallback();
|
|
||||||
this.disposables.add(() => this._autoUpdateCleanup?.());
|
|
||||||
}
|
|
||||||
|
|
||||||
private _closePanel() {
|
private _closePanel() {
|
||||||
if (this._openedPanel) {
|
if (this._openedPanel) {
|
||||||
this._openedPanel.remove();
|
this._openedPanel.remove();
|
||||||
@@ -181,8 +175,8 @@ export class EdgelessTemplateButton extends EdgelessToolbarToolMixin(
|
|||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
const arrowEl = panel.renderRoot.querySelector('.arrow') as HTMLElement;
|
const arrowEl = panel.renderRoot.querySelector('.arrow') as HTMLElement;
|
||||||
this._autoUpdateCleanup?.();
|
|
||||||
this._autoUpdateCleanup = autoUpdate(this, panel, () => {
|
autoUpdate(this, panel, () => {
|
||||||
computePosition(this, panel, {
|
computePosition(this, panel, {
|
||||||
placement: 'top',
|
placement: 'top',
|
||||||
middleware: [offset(20), arrow({ element: arrowEl }), shift()],
|
middleware: [offset(20), arrow({ element: arrowEl }), shift()],
|
||||||
|
|||||||
@@ -103,52 +103,54 @@ export class InlineCommentManager extends LifeCycleWatcher {
|
|||||||
id: CommentId,
|
id: CommentId,
|
||||||
selections: BaseSelection[]
|
selections: BaseSelection[]
|
||||||
) => {
|
) => {
|
||||||
const needCommentTexts = selections.flatMap(selection => {
|
const needCommentTexts = selections
|
||||||
if (!selection.is(TextSelection)) return [];
|
.map(selection => {
|
||||||
const [_, { selectedBlocks }] = this.std.command
|
if (!selection.is(TextSelection)) return [];
|
||||||
.chain()
|
const [_, { selectedBlocks }] = this.std.command
|
||||||
.pipe(getSelectedBlocksCommand, {
|
.chain()
|
||||||
textSelection: selection,
|
.pipe(getSelectedBlocksCommand, {
|
||||||
})
|
textSelection: selection,
|
||||||
.run();
|
})
|
||||||
|
.run();
|
||||||
|
|
||||||
if (!selectedBlocks) return [];
|
if (!selectedBlocks) return [];
|
||||||
|
|
||||||
type MakeRequired<T, K extends keyof T> = T & {
|
type MakeRequired<T, K extends keyof T> = T & {
|
||||||
[key in K]: NonNullable<T[key]>;
|
[key in K]: NonNullable<T[key]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
return selectedBlocks
|
return selectedBlocks
|
||||||
.map(
|
.map(
|
||||||
({ model }) =>
|
({ model }) =>
|
||||||
[model, getInlineEditorByModel(this.std, model)] as const
|
[model, getInlineEditorByModel(this.std, model)] as const
|
||||||
)
|
)
|
||||||
.filter(
|
.filter(
|
||||||
(
|
(
|
||||||
pair
|
pair
|
||||||
): pair is [MakeRequired<BlockModel, 'text'>, AffineInlineEditor] =>
|
): pair is [MakeRequired<BlockModel, 'text'>, AffineInlineEditor] =>
|
||||||
!!pair[0].text && !!pair[1]
|
!!pair[0].text && !!pair[1]
|
||||||
)
|
)
|
||||||
.map(([model, inlineEditor]) => {
|
.map(([model, inlineEditor]) => {
|
||||||
let from: TextRangePoint;
|
let from: TextRangePoint;
|
||||||
let to: TextRangePoint | null;
|
let to: TextRangePoint | null;
|
||||||
if (model.id === selection.from.blockId) {
|
if (model.id === selection.from.blockId) {
|
||||||
from = selection.from;
|
from = selection.from;
|
||||||
to = null;
|
to = null;
|
||||||
} else if (model.id === selection.to?.blockId) {
|
} else if (model.id === selection.to?.blockId) {
|
||||||
from = selection.to;
|
from = selection.to;
|
||||||
to = null;
|
to = null;
|
||||||
} else {
|
} else {
|
||||||
from = {
|
from = {
|
||||||
blockId: model.id,
|
blockId: model.id,
|
||||||
index: 0,
|
index: 0,
|
||||||
length: model.text.yText.length,
|
length: model.text.yText.length,
|
||||||
};
|
};
|
||||||
to = null;
|
to = null;
|
||||||
}
|
}
|
||||||
return [new TextSelection({ from, to }), inlineEditor] as const;
|
return [new TextSelection({ from, to }), inlineEditor] as const;
|
||||||
});
|
});
|
||||||
});
|
})
|
||||||
|
.flat();
|
||||||
|
|
||||||
if (needCommentTexts.length === 0) return;
|
if (needCommentTexts.length === 0) return;
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,8 @@ import { isEqual } from 'lodash-es';
|
|||||||
})
|
})
|
||||||
export class InlineComment extends WithDisposable(ShadowlessElement) {
|
export class InlineComment extends WithDisposable(ShadowlessElement) {
|
||||||
static override styles = css`
|
static override styles = css`
|
||||||
inline-comment {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline-comment.unresolved {
|
inline-comment.unresolved {
|
||||||
|
display: inline-block;
|
||||||
background-color: ${unsafeCSSVarV2('block/comment/highlightDefault')};
|
background-color: ${unsafeCSSVarV2('block/comment/highlightDefault')};
|
||||||
border-bottom: 2px solid
|
border-bottom: 2px solid
|
||||||
${unsafeCSSVarV2('block/comment/highlightUnderline')};
|
${unsafeCSSVarV2('block/comment/highlightUnderline')};
|
||||||
|
|||||||
@@ -150,9 +150,6 @@ export class AffineReference extends WithDisposable(ShadowlessElement) {
|
|||||||
|
|
||||||
readonly open = (event?: Partial<DocLinkClickedEvent>) => {
|
readonly open = (event?: Partial<DocLinkClickedEvent>) => {
|
||||||
if (!this.config.interactable) return;
|
if (!this.config.interactable) return;
|
||||||
if (event?.event?.button === 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.std.getOptional(RefNodeSlotsProvider)?.docLinkClicked.next({
|
this.std.getOptional(RefNodeSlotsProvider)?.docLinkClicked.next({
|
||||||
...this.referenceInfo,
|
...this.referenceInfo,
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ export class HighlighterElementModel extends GfxPrimitiveElementModel<Highlighte
|
|||||||
instance['_local'].delete('commands');
|
instance['_local'].delete('commands');
|
||||||
})
|
})
|
||||||
@derive((lineWidth: number, instance: Instance) => {
|
@derive((lineWidth: number, instance: Instance) => {
|
||||||
const oldBound = Bound.fromXYWH(instance.deserializedXYWH);
|
const oldBound = instance.elementBound;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
lineWidth === instance.lineWidth ||
|
lineWidth === instance.lineWidth ||
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export const blockCommentToolbarButton: Omit<ToolbarAction, 'id'> = {
|
|||||||
|
|
||||||
// may be hover on a block or element, in this case
|
// may be hover on a block or element, in this case
|
||||||
// the selection is empty, so we need to get the current model
|
// the selection is empty, so we need to get the current model
|
||||||
if (model) {
|
if (model && selections.length === 0) {
|
||||||
if (model instanceof BlockModel) {
|
if (model instanceof BlockModel) {
|
||||||
commentProvider.addComment([
|
commentProvider.addComment([
|
||||||
new BlockSelection({
|
new BlockSelection({
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ export interface BlockSuiteFlags {
|
|||||||
enable_shape_shadow_blur: boolean;
|
enable_shape_shadow_blur: boolean;
|
||||||
enable_mobile_keyboard_toolbar: boolean;
|
enable_mobile_keyboard_toolbar: boolean;
|
||||||
enable_mobile_linked_doc_menu: boolean;
|
enable_mobile_linked_doc_menu: boolean;
|
||||||
enable_mobile_database_editing: boolean;
|
|
||||||
enable_block_meta: boolean;
|
enable_block_meta: boolean;
|
||||||
enable_callout: boolean;
|
enable_callout: boolean;
|
||||||
enable_edgeless_scribbled_style: boolean;
|
enable_edgeless_scribbled_style: boolean;
|
||||||
@@ -42,7 +41,6 @@ export class FeatureFlagService extends StoreExtension {
|
|||||||
enable_mobile_keyboard_toolbar: false,
|
enable_mobile_keyboard_toolbar: false,
|
||||||
enable_mobile_linked_doc_menu: false,
|
enable_mobile_linked_doc_menu: false,
|
||||||
enable_block_meta: true,
|
enable_block_meta: true,
|
||||||
enable_mobile_database_editing: false,
|
|
||||||
enable_callout: false,
|
enable_callout: false,
|
||||||
enable_edgeless_scribbled_style: false,
|
enable_edgeless_scribbled_style: false,
|
||||||
enable_table_virtual_scroll: false,
|
enable_table_virtual_scroll: false,
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import type { Signal } from '@preact/signals-core';
|
|||||||
import type { AffineUserInfo } from './types';
|
import type { AffineUserInfo } from './types';
|
||||||
|
|
||||||
export interface UserService {
|
export interface UserService {
|
||||||
currentUserInfo$: Signal<AffineUserInfo | null>;
|
|
||||||
userInfo$(id: string): Signal<AffineUserInfo | null>;
|
userInfo$(id: string): Signal<AffineUserInfo | null>;
|
||||||
isLoading$(id: string): Signal<boolean>;
|
isLoading$(id: string): Signal<boolean>;
|
||||||
error$(id: string): Signal<string | null>; // user friendly error string
|
error$(id: string): Signal<string | null>; // user friendly error string
|
||||||
|
|||||||
@@ -4,14 +4,6 @@ import type { ReadonlySignal } from '@preact/signals-core';
|
|||||||
export interface VirtualKeyboardProvider {
|
export interface VirtualKeyboardProvider {
|
||||||
readonly visible$: ReadonlySignal<boolean>;
|
readonly visible$: ReadonlySignal<boolean>;
|
||||||
readonly height$: ReadonlySignal<number>;
|
readonly height$: ReadonlySignal<number>;
|
||||||
/**
|
|
||||||
* The static height of the keyboard, it should record the last non-zero height of virtual keyboard
|
|
||||||
*/
|
|
||||||
readonly staticHeight$: ReadonlySignal<number>;
|
|
||||||
/**
|
|
||||||
* The safe area of the app tab, it will be used when the keyboard is open or closed
|
|
||||||
*/
|
|
||||||
readonly appTabSafeArea$: ReadonlySignal<string>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VirtualKeyboardProviderWithAction
|
export interface VirtualKeyboardProviderWithAction
|
||||||
|
|||||||
@@ -11,12 +11,14 @@ export function getSelectedRect(selected: GfxModel[]): DOMRect {
|
|||||||
return new DOMRect();
|
return new DOMRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
const lockedElementsByFrame = selected.flatMap(selectable => {
|
const lockedElementsByFrame = selected
|
||||||
if (selectable instanceof FrameBlockModel && selectable.isLocked()) {
|
.map(selectable => {
|
||||||
return selectable.descendantElements;
|
if (selectable instanceof FrameBlockModel && selectable.isLocked()) {
|
||||||
}
|
return selectable.descendantElements;
|
||||||
return [];
|
}
|
||||||
});
|
return [];
|
||||||
|
})
|
||||||
|
.flat();
|
||||||
|
|
||||||
selected = [...new Set([...selected, ...lockedElementsByFrame])];
|
selected = [...new Set([...selected, ...lockedElementsByFrame])];
|
||||||
|
|
||||||
|
|||||||
@@ -114,7 +114,6 @@ export class PreviewHelper {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let width: number = 500;
|
let width: number = 500;
|
||||||
// oxlint-disable-next-line no-unassigned-vars
|
|
||||||
let height;
|
let height;
|
||||||
|
|
||||||
const noteBlock = this.widget.host.querySelector('affine-note');
|
const noteBlock = this.widget.host.querySelector('affine-note');
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
"@blocksuite/affine-block-paragraph": "workspace:*",
|
"@blocksuite/affine-block-paragraph": "workspace:*",
|
||||||
"@blocksuite/affine-block-surface": "workspace:*",
|
"@blocksuite/affine-block-surface": "workspace:*",
|
||||||
"@blocksuite/affine-block-surface-ref": "workspace:*",
|
"@blocksuite/affine-block-surface-ref": "workspace:*",
|
||||||
"@blocksuite/affine-block-table": "workspace:*",
|
|
||||||
"@blocksuite/affine-components": "workspace:*",
|
"@blocksuite/affine-components": "workspace:*",
|
||||||
"@blocksuite/affine-ext-loader": "workspace:*",
|
"@blocksuite/affine-ext-loader": "workspace:*",
|
||||||
"@blocksuite/affine-fragment-doc-title": "workspace:*",
|
"@blocksuite/affine-fragment-doc-title": "workspace:*",
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import {
|
|||||||
} from '@blocksuite/affine-block-paragraph';
|
} from '@blocksuite/affine-block-paragraph';
|
||||||
import { DefaultTool, getSurfaceBlock } from '@blocksuite/affine-block-surface';
|
import { DefaultTool, getSurfaceBlock } from '@blocksuite/affine-block-surface';
|
||||||
import { insertSurfaceRefBlockCommand } from '@blocksuite/affine-block-surface-ref';
|
import { insertSurfaceRefBlockCommand } from '@blocksuite/affine-block-surface-ref';
|
||||||
import { insertTableBlockCommand } from '@blocksuite/affine-block-table';
|
|
||||||
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
|
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
|
||||||
import { toast } from '@blocksuite/affine-components/toast';
|
import { toast } from '@blocksuite/affine-components/toast';
|
||||||
import { insertInlineLatex } from '@blocksuite/affine-inline-latex';
|
import { insertInlineLatex } from '@blocksuite/affine-inline-latex';
|
||||||
@@ -41,20 +40,14 @@ import {
|
|||||||
deleteSelectedModelsCommand,
|
deleteSelectedModelsCommand,
|
||||||
draftSelectedModelsCommand,
|
draftSelectedModelsCommand,
|
||||||
duplicateSelectedModelsCommand,
|
duplicateSelectedModelsCommand,
|
||||||
focusBlockEnd,
|
|
||||||
getBlockSelectionsCommand,
|
getBlockSelectionsCommand,
|
||||||
getSelectedModelsCommand,
|
getSelectedModelsCommand,
|
||||||
getTextSelectionCommand,
|
getTextSelectionCommand,
|
||||||
} from '@blocksuite/affine-shared/commands';
|
} from '@blocksuite/affine-shared/commands';
|
||||||
import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts';
|
import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts';
|
||||||
import {
|
|
||||||
FeatureFlagService,
|
|
||||||
TelemetryProvider,
|
|
||||||
} from '@blocksuite/affine-shared/services';
|
|
||||||
import type { AffineTextStyleAttributes } from '@blocksuite/affine-shared/types';
|
import type { AffineTextStyleAttributes } from '@blocksuite/affine-shared/types';
|
||||||
import {
|
import {
|
||||||
createDefaultDoc,
|
createDefaultDoc,
|
||||||
isInsideBlockByFlavour,
|
|
||||||
openSingleFileWith,
|
openSingleFileWith,
|
||||||
type Signal,
|
type Signal,
|
||||||
} from '@blocksuite/affine-shared/utils';
|
} from '@blocksuite/affine-shared/utils';
|
||||||
@@ -94,7 +87,6 @@ import {
|
|||||||
RedoIcon,
|
RedoIcon,
|
||||||
RightTabIcon,
|
RightTabIcon,
|
||||||
StrikeThroughIcon,
|
StrikeThroughIcon,
|
||||||
TableIcon,
|
|
||||||
TeXIcon,
|
TeXIcon,
|
||||||
TextIcon,
|
TextIcon,
|
||||||
TodayIcon,
|
TodayIcon,
|
||||||
@@ -168,6 +160,10 @@ export type KeyboardSubToolbarConfig = {
|
|||||||
export type KeyboardToolbarContext = {
|
export type KeyboardToolbarContext = {
|
||||||
std: BlockStdScope;
|
std: BlockStdScope;
|
||||||
rootComponent: BlockComponent;
|
rootComponent: BlockComponent;
|
||||||
|
/**
|
||||||
|
* Close tool bar, and blur the focus if blur is true, default is false
|
||||||
|
*/
|
||||||
|
closeToolbar: (blur?: boolean) => void;
|
||||||
/**
|
/**
|
||||||
* Close current tool panel and show virtual keyboard
|
* Close current tool panel and show virtual keyboard
|
||||||
*/
|
*/
|
||||||
@@ -262,62 +258,6 @@ const textToolActionItems: KeyboardToolbarActionItem[] = [
|
|||||||
.run();
|
.run();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'Table',
|
|
||||||
icon: TableIcon(),
|
|
||||||
showWhen: ({ std, rootComponent: { model } }) =>
|
|
||||||
std.store.schema.flavourSchemaMap.has('affine:table') &&
|
|
||||||
!isInsideBlockByFlavour(std.store, model, 'affine:edgeless-text'),
|
|
||||||
action: ({ std }) => {
|
|
||||||
std.command
|
|
||||||
.chain()
|
|
||||||
.pipe(getSelectedModelsCommand)
|
|
||||||
.pipe(insertTableBlockCommand, {
|
|
||||||
place: 'after',
|
|
||||||
removeEmptyLine: true,
|
|
||||||
})
|
|
||||||
.pipe(({ insertedTableBlockId }) => {
|
|
||||||
if (insertedTableBlockId) {
|
|
||||||
const telemetry = std.getOptional(TelemetryProvider);
|
|
||||||
telemetry?.track('BlockCreated', {
|
|
||||||
blockType: 'affine:table',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Callout',
|
|
||||||
icon: FontIcon(),
|
|
||||||
showWhen: ({ std, rootComponent: { model } }) => {
|
|
||||||
return (
|
|
||||||
std.get(FeatureFlagService).getFlag('enable_callout') &&
|
|
||||||
!isInsideBlockByFlavour(model.store, model, 'affine:edgeless-text')
|
|
||||||
);
|
|
||||||
},
|
|
||||||
action: ({ rootComponent: { model }, std }) => {
|
|
||||||
const { store } = model;
|
|
||||||
const parent = store.getParent(model);
|
|
||||||
if (!parent) return;
|
|
||||||
|
|
||||||
const index = parent.children.indexOf(model);
|
|
||||||
if (index === -1) return;
|
|
||||||
const calloutId = store.addBlock('affine:callout', {}, parent, index + 1);
|
|
||||||
if (!calloutId) return;
|
|
||||||
const paragraphId = store.addBlock('affine:paragraph', {}, calloutId);
|
|
||||||
if (!paragraphId) return;
|
|
||||||
std.host.updateComplete
|
|
||||||
.then(() => {
|
|
||||||
const paragraph = std.view.getBlock(paragraphId);
|
|
||||||
if (!paragraph) return;
|
|
||||||
std.command.exec(focusBlockEnd, {
|
|
||||||
focusBlock: paragraph,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(console.error);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const listToolActionItems: KeyboardToolbarActionItem[] = [
|
const listToolActionItems: KeyboardToolbarActionItem[] = [
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
requiredProperties,
|
requiredProperties,
|
||||||
ShadowlessElement,
|
ShadowlessElement,
|
||||||
} from '@blocksuite/std';
|
} from '@blocksuite/std';
|
||||||
import { html, nothing } from 'lit';
|
import { html, nothing, type PropertyValues } from 'lit';
|
||||||
import { property } from 'lit/decorators.js';
|
import { property } from 'lit/decorators.js';
|
||||||
import { repeat } from 'lit/directives/repeat.js';
|
import { repeat } from 'lit/directives/repeat.js';
|
||||||
|
|
||||||
@@ -71,13 +71,22 @@ export class AffineKeyboardToolPanel extends SignalWatcher(
|
|||||||
.map(group => (typeof group === 'function' ? group(this.context) : group))
|
.map(group => (typeof group === 'function' ? group(this.context) : group))
|
||||||
.filter((group): group is KeyboardToolPanelGroup => group !== null);
|
.filter((group): group is KeyboardToolPanelGroup => group !== null);
|
||||||
|
|
||||||
return html`<div class="affine-keyboard-tool-panel-container">
|
return repeat(
|
||||||
${repeat(
|
groups,
|
||||||
groups,
|
group => group.name,
|
||||||
group => group.name,
|
group => this._renderGroup(group)
|
||||||
group => this._renderGroup(group)
|
);
|
||||||
)}
|
}
|
||||||
</div>`;
|
|
||||||
|
protected override willUpdate(changedProperties: PropertyValues<this>) {
|
||||||
|
if (changedProperties.has('height')) {
|
||||||
|
this.style.height = `${this.height}px`;
|
||||||
|
if (this.height === 0) {
|
||||||
|
this.style.padding = '0';
|
||||||
|
} else {
|
||||||
|
this.style.padding = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
@@ -85,4 +94,7 @@ export class AffineKeyboardToolPanel extends SignalWatcher(
|
|||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor context!: KeyboardToolbarContext;
|
accessor context!: KeyboardToolbarContext;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor height = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
requiredProperties,
|
requiredProperties,
|
||||||
ShadowlessElement,
|
ShadowlessElement,
|
||||||
} from '@blocksuite/std';
|
} from '@blocksuite/std';
|
||||||
import { effect, type Signal, signal } from '@preact/signals-core';
|
import { effect, type Signal, signal, untracked } from '@preact/signals-core';
|
||||||
import { html } from 'lit';
|
import { html } from 'lit';
|
||||||
import { property } from 'lit/decorators.js';
|
import { property } from 'lit/decorators.js';
|
||||||
import { repeat } from 'lit/directives/repeat.js';
|
import { repeat } from 'lit/directives/repeat.js';
|
||||||
@@ -22,6 +22,7 @@ import type {
|
|||||||
KeyboardToolbarItem,
|
KeyboardToolbarItem,
|
||||||
KeyboardToolPanelConfig,
|
KeyboardToolPanelConfig,
|
||||||
} from './config';
|
} from './config';
|
||||||
|
import { PositionController } from './position-controller';
|
||||||
import { keyboardToolbarStyles } from './styles';
|
import { keyboardToolbarStyles } from './styles';
|
||||||
import {
|
import {
|
||||||
isKeyboardSubToolBarConfig,
|
isKeyboardSubToolBarConfig,
|
||||||
@@ -40,7 +41,10 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
|||||||
) {
|
) {
|
||||||
static override styles = keyboardToolbarStyles;
|
static override styles = keyboardToolbarStyles;
|
||||||
|
|
||||||
private readonly _expanded$ = signal(false);
|
/** This field records the panel static height same as the virtual keyboard height */
|
||||||
|
panelHeight$ = signal(0);
|
||||||
|
|
||||||
|
positionController = new PositionController(this);
|
||||||
|
|
||||||
get std() {
|
get std() {
|
||||||
return this.rootComponent.std;
|
return this.rootComponent.std;
|
||||||
@@ -50,31 +54,9 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
|||||||
return this._currentPanelIndex$.value !== -1;
|
return this._currentPanelIndex$.value !== -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private get panelHeight() {
|
|
||||||
return this._expanded$.value
|
|
||||||
? `${
|
|
||||||
this.keyboard.staticHeight$.value !== 0
|
|
||||||
? this.keyboard.staticHeight$.value
|
|
||||||
: 330
|
|
||||||
}px`
|
|
||||||
: this.keyboard.appTabSafeArea$.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prevent flickering during keyboard opening
|
|
||||||
*/
|
|
||||||
private _resetPanelIndexTimeoutId: ReturnType<typeof setTimeout> | null =
|
|
||||||
null;
|
|
||||||
private readonly _closeToolPanel = () => {
|
private readonly _closeToolPanel = () => {
|
||||||
|
this._currentPanelIndex$.value = -1;
|
||||||
if (!this.keyboard.visible$.peek()) this.keyboard.show();
|
if (!this.keyboard.visible$.peek()) this.keyboard.show();
|
||||||
|
|
||||||
if (this._resetPanelIndexTimeoutId) {
|
|
||||||
clearTimeout(this._resetPanelIndexTimeoutId);
|
|
||||||
this._resetPanelIndexTimeoutId = null;
|
|
||||||
}
|
|
||||||
this._resetPanelIndexTimeoutId = setTimeout(() => {
|
|
||||||
this._currentPanelIndex$.value = -1;
|
|
||||||
}, 100);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly _currentPanelIndex$ = signal(-1);
|
private readonly _currentPanelIndex$ = signal(-1);
|
||||||
@@ -101,10 +83,6 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
|||||||
if (this._currentPanelIndex$.value === index) {
|
if (this._currentPanelIndex$.value === index) {
|
||||||
this._closeToolPanel();
|
this._closeToolPanel();
|
||||||
} else {
|
} else {
|
||||||
if (this._resetPanelIndexTimeoutId) {
|
|
||||||
clearTimeout(this._resetPanelIndexTimeoutId);
|
|
||||||
this._resetPanelIndexTimeoutId = null;
|
|
||||||
}
|
|
||||||
this._currentPanelIndex$.value = index;
|
this._currentPanelIndex$.value = index;
|
||||||
this.keyboard.hide();
|
this.keyboard.hide();
|
||||||
this._scrollCurrentBlockIntoView();
|
this._scrollCurrentBlockIntoView();
|
||||||
@@ -145,6 +123,9 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
|||||||
return {
|
return {
|
||||||
std: this.std,
|
std: this.std,
|
||||||
rootComponent: this.rootComponent,
|
rootComponent: this.rootComponent,
|
||||||
|
closeToolbar: (blur = false) => {
|
||||||
|
this.close(blur);
|
||||||
|
},
|
||||||
closeToolPanel: () => {
|
closeToolPanel: () => {
|
||||||
this._closeToolPanel();
|
this._closeToolPanel();
|
||||||
},
|
},
|
||||||
@@ -221,7 +202,7 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _renderItems() {
|
private _renderItems() {
|
||||||
if (!this.std.event.active$.value)
|
if (document.activeElement !== this.rootComponent)
|
||||||
return html`<div class="item-container"></div>`;
|
return html`<div class="item-container"></div>`;
|
||||||
|
|
||||||
const goPrevToolbarAction = when(
|
const goPrevToolbarAction = when(
|
||||||
@@ -245,15 +226,7 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
|||||||
<icon-button
|
<icon-button
|
||||||
size="36px"
|
size="36px"
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
if (this.keyboard.staticHeight$.value === 0) {
|
this.close(true);
|
||||||
this._closeToolPanel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.keyboard.visible$.peek()) {
|
|
||||||
this.keyboard.hide();
|
|
||||||
} else {
|
|
||||||
this.keyboard.show();
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
${KeyboardIcon()}
|
${KeyboardIcon()}
|
||||||
@@ -264,23 +237,6 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
|||||||
override connectedCallback() {
|
override connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
||||||
// There are two cases that `_expanded$` will be true:
|
|
||||||
// 1. when virtual keyboard is opened, the panel need to be expanded and overlapped by the keyboard,
|
|
||||||
// so that the toolbar will be on the top of the keyboard.
|
|
||||||
// 2. the panel is opened, whether the keyboard is closed or not exists (e.g. a physical keyboard connected)
|
|
||||||
//
|
|
||||||
// There is one case that `_expanded$` will be false:
|
|
||||||
// 1. the panel is closed, and the keyboard is closed, the toolbar will be rendered at the bottom of the viewport
|
|
||||||
this._disposables.add(
|
|
||||||
effect(() => {
|
|
||||||
if (this.keyboard.visible$.value || this.panelOpened) {
|
|
||||||
this._expanded$.value = true;
|
|
||||||
} else {
|
|
||||||
this._expanded$.value = false;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// prevent editor blur when click item in toolbar
|
// prevent editor blur when click item in toolbar
|
||||||
this.disposables.addFromEvent(this, 'pointerdown', e => {
|
this.disposables.addFromEvent(this, 'pointerdown', e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -304,17 +260,15 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
|||||||
if (this.keyboard.visible$.value) {
|
if (this.keyboard.visible$.value) {
|
||||||
this._closeToolPanel();
|
this._closeToolPanel();
|
||||||
}
|
}
|
||||||
|
// when keyboard is closed and the panel is not opened, we need to close the toolbar,
|
||||||
|
// this usually happens when user close keyboard from system side
|
||||||
|
else if (this.hasUpdated && untracked(() => !this.panelOpened)) {
|
||||||
|
this.close(true);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this._watchAutoShow();
|
this._watchAutoShow();
|
||||||
|
|
||||||
this.disposables.add(() => {
|
|
||||||
if (this._resetPanelIndexTimeoutId) {
|
|
||||||
clearTimeout(this._resetPanelIndexTimeoutId);
|
|
||||||
this._resetPanelIndexTimeoutId = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _watchAutoShow() {
|
private _watchAutoShow() {
|
||||||
@@ -377,10 +331,7 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
|||||||
<affine-keyboard-tool-panel
|
<affine-keyboard-tool-panel
|
||||||
.config=${this._currentPanelConfig}
|
.config=${this._currentPanelConfig}
|
||||||
.context=${this._context}
|
.context=${this._context}
|
||||||
style=${styleMap({
|
.height=${this.panelHeight$.value}
|
||||||
height: this.panelHeight,
|
|
||||||
paddingBottom: this.keyboard.appTabSafeArea$.value,
|
|
||||||
})}
|
|
||||||
></affine-keyboard-tool-panel>
|
></affine-keyboard-tool-panel>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -388,6 +339,9 @@ export class AffineKeyboardToolbar extends SignalWatcher(
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor keyboard!: VirtualKeyboardProviderWithAction;
|
accessor keyboard!: VirtualKeyboardProviderWithAction;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
accessor close: (blur: boolean) => void = () => {};
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor config!: KeyboardToolbarConfig;
|
accessor config!: KeyboardToolbarConfig;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { type VirtualKeyboardProvider } from '@blocksuite/affine-shared/services';
|
||||||
|
import { DisposableGroup } from '@blocksuite/global/disposable';
|
||||||
|
import type { BlockStdScope, ShadowlessElement } from '@blocksuite/std';
|
||||||
|
import { effect, type Signal } from '@preact/signals-core';
|
||||||
|
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This controller is used to control the keyboard toolbar position
|
||||||
|
*/
|
||||||
|
export class PositionController implements ReactiveController {
|
||||||
|
private readonly _disposables = new DisposableGroup();
|
||||||
|
|
||||||
|
host: ReactiveControllerHost &
|
||||||
|
ShadowlessElement & {
|
||||||
|
std: BlockStdScope;
|
||||||
|
panelHeight$: Signal<number>;
|
||||||
|
keyboard: VirtualKeyboardProvider;
|
||||||
|
panelOpened: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(host: PositionController['host']) {
|
||||||
|
(this.host = host).addController(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
hostConnected() {
|
||||||
|
const { keyboard } = this.host;
|
||||||
|
|
||||||
|
this._disposables.add(
|
||||||
|
effect(() => {
|
||||||
|
if (keyboard.visible$.value) {
|
||||||
|
this.host.panelHeight$.value = keyboard.height$.value;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this.host.style.bottom = '0px';
|
||||||
|
}
|
||||||
|
|
||||||
|
hostDisconnected() {
|
||||||
|
this._disposables.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@ export const keyboardToolbarStyles = css`
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
display: block;
|
display: block;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
bottom: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.keyboard-toolbar {
|
.keyboard-toolbar {
|
||||||
@@ -61,18 +60,14 @@ export const keyboardToolbarStyles = css`
|
|||||||
|
|
||||||
export const keyboardToolPanelStyles = css`
|
export const keyboardToolPanelStyles = css`
|
||||||
affine-keyboard-tool-panel {
|
affine-keyboard-tool-panel {
|
||||||
display: block;
|
|
||||||
overflow-y: auto;
|
|
||||||
box-sizing: border-box;
|
|
||||||
background-color: ${unsafeCSSVarV2('layer/background/primary')};
|
|
||||||
}
|
|
||||||
|
|
||||||
.affine-keyboard-tool-panel-container {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 16px 4px 8px 8px;
|
padding: 16px 4px 8px 8px;
|
||||||
|
overflow-y: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: ${unsafeCSSVarV2('layer/background/primary')};
|
||||||
}
|
}
|
||||||
|
|
||||||
${scrollbarStyle('affine-keyboard-tool-panel')}
|
${scrollbarStyle('affine-keyboard-tool-panel')}
|
||||||
|
|||||||
@@ -20,6 +20,18 @@ import {
|
|||||||
export const AFFINE_KEYBOARD_TOOLBAR_WIDGET = 'affine-keyboard-toolbar-widget';
|
export const AFFINE_KEYBOARD_TOOLBAR_WIDGET = 'affine-keyboard-toolbar-widget';
|
||||||
|
|
||||||
export class AffineKeyboardToolbarWidget extends WidgetComponent<RootBlockModel> {
|
export class AffineKeyboardToolbarWidget extends WidgetComponent<RootBlockModel> {
|
||||||
|
private readonly _close = (blur: boolean) => {
|
||||||
|
if (blur) {
|
||||||
|
if (document.activeElement === this._docTitle?.inlineEditorContainer) {
|
||||||
|
this._docTitle?.inlineEditor?.setInlineRange(null);
|
||||||
|
this._docTitle?.inlineEditor?.eventSource?.blur();
|
||||||
|
} else if (document.activeElement === this.block?.rootComponent) {
|
||||||
|
this.std.selection.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._show$.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
private readonly _show$ = signal(false);
|
private readonly _show$ = signal(false);
|
||||||
|
|
||||||
private _initialInputMode: string = '';
|
private _initialInputMode: string = '';
|
||||||
@@ -61,26 +73,29 @@ export class AffineKeyboardToolbarWidget extends WidgetComponent<RootBlockModel>
|
|||||||
override connectedCallback(): void {
|
override connectedCallback(): void {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
||||||
this.disposables.add(
|
|
||||||
effect(() => {
|
|
||||||
this._show$.value = this.std.event.active$.value;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const rootComponent = this.block?.rootComponent;
|
const rootComponent = this.block?.rootComponent;
|
||||||
if (rootComponent && this.keyboard.fallback) {
|
if (rootComponent) {
|
||||||
this._initialInputMode = rootComponent.inputMode;
|
this.disposables.addFromEvent(rootComponent, 'focus', () => {
|
||||||
this.disposables.add(() => {
|
this._show$.value = true;
|
||||||
rootComponent.inputMode = this._initialInputMode;
|
|
||||||
});
|
});
|
||||||
this.disposables.add(
|
this.disposables.addFromEvent(rootComponent, 'blur', () => {
|
||||||
effect(() => {
|
this._show$.value = false;
|
||||||
// recover input mode when keyboard toolbar is hidden
|
});
|
||||||
if (!this._show$.value) {
|
|
||||||
rootComponent.inputMode = this._initialInputMode;
|
if (this.keyboard.fallback) {
|
||||||
}
|
this._initialInputMode = rootComponent.inputMode;
|
||||||
})
|
this.disposables.add(() => {
|
||||||
);
|
rootComponent.inputMode = this._initialInputMode;
|
||||||
|
});
|
||||||
|
this.disposables.add(
|
||||||
|
effect(() => {
|
||||||
|
// recover input mode when keyboard toolbar is hidden
|
||||||
|
if (!this._show$.value) {
|
||||||
|
rootComponent.inputMode = this._initialInputMode;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._docTitle) {
|
if (this._docTitle) {
|
||||||
@@ -114,6 +129,7 @@ export class AffineKeyboardToolbarWidget extends WidgetComponent<RootBlockModel>
|
|||||||
.keyboard=${this.keyboard}
|
.keyboard=${this.keyboard}
|
||||||
.config=${this.config}
|
.config=${this.config}
|
||||||
.rootComponent=${this.block.rootComponent}
|
.rootComponent=${this.block.rootComponent}
|
||||||
|
.close=${this._close}
|
||||||
></affine-keyboard-toolbar>`}
|
></affine-keyboard-toolbar>`}
|
||||||
></blocksuite-portal>`;
|
></blocksuite-portal>`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
{ "path": "../../blocks/paragraph" },
|
{ "path": "../../blocks/paragraph" },
|
||||||
{ "path": "../../blocks/surface" },
|
{ "path": "../../blocks/surface" },
|
||||||
{ "path": "../../blocks/surface-ref" },
|
{ "path": "../../blocks/surface-ref" },
|
||||||
{ "path": "../../blocks/table" },
|
|
||||||
{ "path": "../../components" },
|
{ "path": "../../components" },
|
||||||
{ "path": "../../ext-loader" },
|
{ "path": "../../ext-loader" },
|
||||||
{ "path": "../../fragments/doc-title" },
|
{ "path": "../../fragments/doc-title" },
|
||||||
|
|||||||
@@ -113,9 +113,11 @@ export class LinkedDocPopover extends SignalWatcher(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private get _flattenActionList() {
|
private get _flattenActionList() {
|
||||||
return this._actionGroup.flatMap(group =>
|
return this._actionGroup
|
||||||
group.items.map(item => ({ ...item, groupName: group.name }))
|
.map(group =>
|
||||||
);
|
group.items.map(item => ({ ...item, groupName: group.name }))
|
||||||
|
)
|
||||||
|
.flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _query() {
|
private get _query() {
|
||||||
@@ -341,18 +343,7 @@ export class LinkedDocPopover extends SignalWatcher(
|
|||||||
override willUpdate() {
|
override willUpdate() {
|
||||||
if (!this.hasUpdated) {
|
if (!this.hasUpdated) {
|
||||||
const updatePosition = throttle(() => {
|
const updatePosition = throttle(() => {
|
||||||
this._position = getPopperPosition(
|
this._position = getPopperPosition(this, this.context.startNativeRange);
|
||||||
{
|
|
||||||
getBoundingClientRect: () => {
|
|
||||||
return {
|
|
||||||
...this.getBoundingClientRect(),
|
|
||||||
// Workaround: the width of the popover is zero when it is not rendered
|
|
||||||
width: 280,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
this.context.startNativeRange
|
|
||||||
);
|
|
||||||
}, 10);
|
}, 10);
|
||||||
|
|
||||||
this.disposables.addFromEvent(window, 'resize', updatePosition);
|
this.disposables.addFromEvent(window, 'resize', updatePosition);
|
||||||
|
|||||||
@@ -65,98 +65,6 @@ export class Unzip {
|
|||||||
this.unzipped = fflate.unzipSync(new Uint8Array(await blob.arrayBuffer()));
|
this.unzipped = fflate.unzipSync(new Uint8Array(await blob.arrayBuffer()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private fixFileNameEncoding(fileName: string): string {
|
|
||||||
try {
|
|
||||||
// check if contains non-ASCII characters
|
|
||||||
if (fileName.split('').some(char => char.charCodeAt(0) > 127)) {
|
|
||||||
// try different encodings
|
|
||||||
const fixedName = this.tryDifferentEncodings(fileName);
|
|
||||||
if (fixedName && fixedName !== fileName) {
|
|
||||||
return fixedName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fileName;
|
|
||||||
} catch {
|
|
||||||
return fileName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// try different encodings
|
|
||||||
private tryDifferentEncodings(fileName: string): string | null {
|
|
||||||
try {
|
|
||||||
// convert string to bytes
|
|
||||||
const bytes = new Uint8Array(fileName.length);
|
|
||||||
for (let i = 0; i < fileName.length; i++) {
|
|
||||||
bytes[i] = fileName.charCodeAt(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// try different encodings
|
|
||||||
// The macOS system zip tool creates archives with UTF-8 encoded filenames.
|
|
||||||
// However, this implementation doesn't strictly adhere to the ZIP specification.
|
|
||||||
// Simply forcing UTF-8 encoding when unzipping should resolve filename corruption issues.
|
|
||||||
const encodings = ['utf-8'];
|
|
||||||
|
|
||||||
for (const encoding of encodings) {
|
|
||||||
try {
|
|
||||||
const decoder = new TextDecoder(encoding);
|
|
||||||
const result = decoder.decode(bytes);
|
|
||||||
|
|
||||||
// check if decoded result is valid
|
|
||||||
if (result && this.isValidDecodedString(result)) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// ignore encoding error, try next encoding
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// ignore conversion error
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if decoded string is valid
|
|
||||||
private isValidDecodedString(str: string): boolean {
|
|
||||||
// check if contains control characters
|
|
||||||
const controlCharCodes = new Set([
|
|
||||||
0x00,
|
|
||||||
0x01,
|
|
||||||
0x02,
|
|
||||||
0x03,
|
|
||||||
0x04,
|
|
||||||
0x05,
|
|
||||||
0x06,
|
|
||||||
0x07,
|
|
||||||
0x08, // \x00-\x08
|
|
||||||
0x0b,
|
|
||||||
0x0c, // \x0B, \x0C
|
|
||||||
0x0e,
|
|
||||||
0x0f,
|
|
||||||
0x10,
|
|
||||||
0x11,
|
|
||||||
0x12,
|
|
||||||
0x13,
|
|
||||||
0x14,
|
|
||||||
0x15,
|
|
||||||
0x16,
|
|
||||||
0x17,
|
|
||||||
0x18,
|
|
||||||
0x19,
|
|
||||||
0x1a,
|
|
||||||
0x1b,
|
|
||||||
0x1c,
|
|
||||||
0x1d,
|
|
||||||
0x1e,
|
|
||||||
0x1f, // \x0E-\x1F
|
|
||||||
0x7f, // \x7F
|
|
||||||
]);
|
|
||||||
|
|
||||||
return !str
|
|
||||||
.split('')
|
|
||||||
.some(char => controlCharCodes.has(char.charCodeAt(0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
*[Symbol.iterator]() {
|
*[Symbol.iterator]() {
|
||||||
const keys = Object.keys(this.unzipped ?? {});
|
const keys = Object.keys(this.unzipped ?? {});
|
||||||
let index = 0;
|
let index = 0;
|
||||||
@@ -173,10 +81,7 @@ export class Unzip {
|
|||||||
const content = new File([this.unzipped![path]], fileName, {
|
const content = new File([this.unzipped![path]], fileName, {
|
||||||
type: mime ?? '',
|
type: mime ?? '',
|
||||||
}) as Blob;
|
}) as Blob;
|
||||||
|
yield { path, content, index };
|
||||||
const fixedPath = this.fixFileNameEncoding(path);
|
|
||||||
|
|
||||||
yield { path: fixedPath, content, index };
|
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,13 +142,15 @@ export class SlashMenu extends WithDisposable(LitElement) {
|
|||||||
// We search first and second layer
|
// We search first and second layer
|
||||||
if (this._filteredItems.length !== 0 && depth >= 1) break;
|
if (this._filteredItems.length !== 0 && depth >= 1) break;
|
||||||
|
|
||||||
queue = queue.flatMap(item => {
|
queue = queue
|
||||||
if (isSubMenuItem(item)) {
|
.map<typeof queue>(item => {
|
||||||
return item.subMenu;
|
if (isSubMenuItem(item)) {
|
||||||
} else {
|
return item.subMenu;
|
||||||
return [];
|
} else {
|
||||||
}
|
return [];
|
||||||
});
|
}
|
||||||
|
})
|
||||||
|
.flat();
|
||||||
|
|
||||||
depth++;
|
depth++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -418,9 +418,9 @@ export class AffineToolbarWidget extends WidgetComponent {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const elementIds = selections.flatMap(s =>
|
const elementIds = selections
|
||||||
s.editing || s.inoperable ? [] : s.elements
|
.map(s => (s.editing || s.inoperable ? [] : s.elements))
|
||||||
);
|
.flat();
|
||||||
const count = elementIds.length;
|
const count = elementIds.length;
|
||||||
const activated = context.activated && Boolean(count);
|
const activated = context.activated && Boolean(count);
|
||||||
|
|
||||||
|
|||||||
@@ -229,7 +229,8 @@ export function renderToolbar(
|
|||||||
? module.config.when(context)
|
? module.config.when(context)
|
||||||
: (module.config.when ?? true)
|
: (module.config.when ?? true)
|
||||||
)
|
)
|
||||||
.flatMap(module => module.config.actions);
|
.map<ToolbarActions>(module => module.config.actions)
|
||||||
|
.flat();
|
||||||
|
|
||||||
const combined = combine(actions, context);
|
const combined = combine(actions, context);
|
||||||
|
|
||||||
|
|||||||
@@ -91,11 +91,15 @@ export class KeyboardControl {
|
|||||||
const disposables = new DisposableGroup();
|
const disposables = new DisposableGroup();
|
||||||
if (IS_ANDROID) {
|
if (IS_ANDROID) {
|
||||||
disposables.add(
|
disposables.add(
|
||||||
this._dispatcher.add('beforeInput', ctx => {
|
this._dispatcher.add(
|
||||||
if (this.composition) return false;
|
'beforeInput',
|
||||||
const binding = androidBindKeymapPatch(keymap);
|
ctx => {
|
||||||
return binding(ctx);
|
if (this.composition) return false;
|
||||||
})
|
const binding = androidBindKeymapPatch(keymap);
|
||||||
|
return binding(ctx);
|
||||||
|
},
|
||||||
|
options
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -226,18 +226,6 @@ export class UIEventDispatcher extends LifeCycleWatcher {
|
|||||||
this._setActive(false);
|
this._setActive(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// When the selection is outside the host, the event dispatcher should be inactive
|
|
||||||
this.disposables.addFromEvent(document, 'selectionchange', () => {
|
|
||||||
const sel = document.getSelection();
|
|
||||||
if (!sel || sel.rangeCount === 0) return;
|
|
||||||
const { anchorNode, focusNode } = sel;
|
|
||||||
if (
|
|
||||||
(anchorNode && !this.host.contains(anchorNode)) ||
|
|
||||||
(focusNode && !this.host.contains(focusNode))
|
|
||||||
) {
|
|
||||||
this._setActive(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _buildEventScopeBySelection(name: EventName) {
|
private _buildEventScopeBySelection(name: EventName) {
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ export function bindKeymap(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// In some IME of Android like, the keypress event dose not contain
|
// In Android, the keypress event dose not contain
|
||||||
// the information about what key is pressed. See
|
// the information about what key is pressed. See
|
||||||
// https://stackoverflow.com/a/68188679
|
// https://stackoverflow.com/a/68188679
|
||||||
// https://stackoverflow.com/a/66724830
|
// https://stackoverflow.com/a/66724830
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export type CanvasLayer = BaseLayer<GfxPrimitiveElementModel> & {
|
|||||||
type: 'canvas';
|
type: 'canvas';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The z-index of the first element in this canvas layer.
|
* The z-index of canvas layer.
|
||||||
*
|
*
|
||||||
* A canvas layer renders all the elements in a single canvas,
|
* A canvas layer renders all the elements in a single canvas,
|
||||||
* this property is used to render the canvas with correct z-index.
|
* this property is used to render the canvas with correct z-index.
|
||||||
@@ -165,7 +165,8 @@ export class LayerManager extends GfxExtension {
|
|||||||
];
|
];
|
||||||
curLayer.zIndex = currentCSSZindex;
|
curLayer.zIndex = currentCSSZindex;
|
||||||
layers.push(curLayer as LayerManager['layers'][number]);
|
layers.push(curLayer as LayerManager['layers'][number]);
|
||||||
currentCSSZindex += curLayer.elements.length;
|
currentCSSZindex +=
|
||||||
|
curLayer.type === 'block' ? curLayer.elements.length : 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const addLayer = (type: 'canvas' | 'block') => {
|
const addLayer = (type: 'canvas' | 'block') => {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { IS_ANDROID } from '@blocksuite/global/env';
|
|
||||||
import type { BaseTextAttributes } from '@blocksuite/store';
|
import type { BaseTextAttributes } from '@blocksuite/store';
|
||||||
|
|
||||||
import type { InlineEditor } from '../inline-editor.js';
|
import type { InlineEditor } from '../inline-editor.js';
|
||||||
@@ -42,10 +41,11 @@ export class EventService<TextAttributes extends BaseTextAttributes> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly _onBeforeInput = async (event: InputEvent) => {
|
private readonly _onBeforeInput = (event: InputEvent) => {
|
||||||
const range = this.editor.rangeService.getNativeRange();
|
const range = this.editor.rangeService.getNativeRange();
|
||||||
if (
|
if (
|
||||||
this.editor.isReadonly ||
|
this.editor.isReadonly ||
|
||||||
|
this._isComposing ||
|
||||||
!range ||
|
!range ||
|
||||||
!this._isRangeCompletelyInRoot(range)
|
!this._isRangeCompletelyInRoot(range)
|
||||||
)
|
)
|
||||||
@@ -54,29 +54,33 @@ export class EventService<TextAttributes extends BaseTextAttributes> {
|
|||||||
let inlineRange = this.editor.toInlineRange(range);
|
let inlineRange = this.editor.toInlineRange(range);
|
||||||
if (!inlineRange) return;
|
if (!inlineRange) return;
|
||||||
|
|
||||||
if (this._isComposing) {
|
|
||||||
if (IS_ANDROID && event.inputType === 'insertCompositionText') {
|
|
||||||
this._compositionInlineRange = inlineRange;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ifHandleTargetRange = true;
|
let ifHandleTargetRange = true;
|
||||||
|
|
||||||
if (
|
if (event.inputType.startsWith('delete')) {
|
||||||
event.inputType.startsWith('delete') &&
|
if (
|
||||||
(isInEmbedGap(range.commonAncestorContainer) ||
|
isInEmbedGap(range.commonAncestorContainer) &&
|
||||||
|
inlineRange.length === 0 &&
|
||||||
|
inlineRange.index > 0
|
||||||
|
) {
|
||||||
|
inlineRange = {
|
||||||
|
index: inlineRange.index - 1,
|
||||||
|
length: 1,
|
||||||
|
};
|
||||||
|
ifHandleTargetRange = false;
|
||||||
|
} else if (
|
||||||
|
isInEmptyLine(range.commonAncestorContainer) &&
|
||||||
|
inlineRange.length === 0 &&
|
||||||
|
inlineRange.index > 0
|
||||||
|
// eslint-disable-next-line sonarjs/no-duplicated-branches
|
||||||
|
) {
|
||||||
|
// do not use target range when deleting across lines
|
||||||
// https://github.com/toeverything/blocksuite/issues/5381
|
// https://github.com/toeverything/blocksuite/issues/5381
|
||||||
isInEmptyLine(range.commonAncestorContainer)) &&
|
inlineRange = {
|
||||||
inlineRange.length === 0 &&
|
index: inlineRange.index - 1,
|
||||||
inlineRange.index > 0
|
length: 1,
|
||||||
) {
|
};
|
||||||
// do not use target range when deleting across lines
|
ifHandleTargetRange = false;
|
||||||
inlineRange = {
|
}
|
||||||
index: inlineRange.index - 1,
|
|
||||||
length: 1,
|
|
||||||
};
|
|
||||||
ifHandleTargetRange = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ifHandleTargetRange) {
|
if (ifHandleTargetRange) {
|
||||||
@@ -93,24 +97,11 @@ export class EventService<TextAttributes extends BaseTextAttributes> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!inlineRange) return;
|
if (!inlineRange) return;
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (IS_ANDROID) {
|
|
||||||
this.editor.rerenderWholeEditor();
|
|
||||||
await this.editor.waitForUpdate();
|
|
||||||
if (
|
|
||||||
event.inputType === 'deleteContentBackward' &&
|
|
||||||
!(inlineRange.index === 0 && inlineRange.length === 0)
|
|
||||||
) {
|
|
||||||
// when press backspace at offset 1, double characters will be removed.
|
|
||||||
// because we mock backspace key event `androidBindKeymapPatch` in blocksuite/framework/std/src/event/keymap.ts
|
|
||||||
// so we need to stop the event propagation to prevent the double characters removal.
|
|
||||||
event.stopPropagation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const ctx: BeforeinputHookCtx<TextAttributes> = {
|
const ctx: BeforeinputHookCtx<TextAttributes> = {
|
||||||
inlineEditor: this.editor,
|
inlineEditor: this.editor,
|
||||||
raw: event,
|
raw: event,
|
||||||
@@ -355,9 +346,11 @@ export class EventService<TextAttributes extends BaseTextAttributes> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.editor.disposables.addFromEvent(eventSource, 'beforeinput', e => {
|
this.editor.disposables.addFromEvent(
|
||||||
this._onBeforeInput(e).catch(console.error);
|
eventSource,
|
||||||
});
|
'beforeinput',
|
||||||
|
this._onBeforeInput
|
||||||
|
);
|
||||||
this.editor.disposables.addFromEvent(
|
this.editor.disposables.addFromEvent(
|
||||||
eventSource,
|
eventSource,
|
||||||
'compositionstart',
|
'compositionstart',
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ function inlineTextStyles(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return styleMap({
|
return styleMap({
|
||||||
'font-weight': props.bold ? 'bold' : 'inherit',
|
'font-weight': props.bold ? 'bold' : 'normal',
|
||||||
'font-style': props.italic ? 'italic' : 'inherit',
|
'font-style': props.italic ? 'italic' : 'normal',
|
||||||
'text-decoration': textDecorations.length > 0 ? textDecorations : 'inherit',
|
'text-decoration': textDecorations.length > 0 ? textDecorations : 'none',
|
||||||
...inlineCodeStyle,
|
...inlineCodeStyle,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user