mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-25 10:22:55 +08:00
Compare commits
16 Commits
v0.26.3-be
...
v0.26.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79460072bb | ||
|
|
41b3b0e82e | ||
|
|
9c99293c92 | ||
|
|
6aba4350ac | ||
|
|
046e126054 | ||
|
|
c2c7dde06c | ||
|
|
5fb1c11a96 | ||
|
|
3e39dbb298 | ||
|
|
e617740974 | ||
|
|
744c78abbb | ||
|
|
91c5869053 | ||
|
|
6d805b302c | ||
|
|
fb9f49b948 | ||
|
|
ef6717e59a | ||
|
|
ad988dbd1e | ||
|
|
3d01766f55 |
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -3,6 +3,6 @@ contact_links:
|
|||||||
- name: Something else?
|
- name: Something else?
|
||||||
url: https://github.com/toeverything/AFFiNE/discussions
|
url: https://github.com/toeverything/AFFiNE/discussions
|
||||||
about: Feel free to ask and answer questions over in GitHub Discussions
|
about: Feel free to ask and answer questions over in GitHub Discussions
|
||||||
- name: AFFiNE Community Support
|
- name: AFFiNE Community Support (Discord)
|
||||||
url: https://community.affine.pro
|
url: https://affine.pro/redirect/discord
|
||||||
about: AFFiNE Community - a place to ask, learn and engage with others
|
about: AFFiNE Community - a place to ask, learn and engage with others
|
||||||
|
|||||||
20
.github/actionlint.yaml
vendored
Normal file
20
.github/actionlint.yaml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
self-hosted-runner:
|
||||||
|
# Labels of self-hosted runner in array of strings.
|
||||||
|
labels:
|
||||||
|
- win-signer
|
||||||
|
|
||||||
|
# Configuration variables in array of strings defined in your repository or
|
||||||
|
# organization. `null` means disabling configuration variables check.
|
||||||
|
# Empty array means no configuration variable is allowed.
|
||||||
|
config-variables: null
|
||||||
|
|
||||||
|
# Configuration for file paths. The keys are glob patterns to match to file
|
||||||
|
# paths relative to the repository root. The values are the configurations for
|
||||||
|
# the file paths. Note that the path separator is always '/'.
|
||||||
|
# The following configurations are available.
|
||||||
|
#
|
||||||
|
# "ignore" is an array of regular expression patterns. Matched error messages
|
||||||
|
# are ignored. This is similar to the "-ignore" command line option.
|
||||||
|
paths:
|
||||||
|
# .github/workflows/**/*.yml:
|
||||||
|
# ignore: []
|
||||||
1
.github/actions/setup-version/action.yml
vendored
1
.github/actions/setup-version/action.yml
vendored
@@ -7,7 +7,6 @@ inputs:
|
|||||||
ios-app-version:
|
ios-app-version:
|
||||||
description: 'iOS App Store Version (Optional, use App version if empty)'
|
description: 'iOS App Store Version (Optional, use App version if empty)'
|
||||||
required: false
|
required: false
|
||||||
type: string
|
|
||||||
runs:
|
runs:
|
||||||
using: 'composite'
|
using: 'composite'
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
2
.github/helm/affine/Chart.yaml
vendored
2
.github/helm/affine/Chart.yaml
vendored
@@ -3,4 +3,4 @@ name: affine
|
|||||||
description: AFFiNE cloud chart
|
description: AFFiNE cloud chart
|
||||||
type: application
|
type: application
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
appVersion: "0.26.1"
|
appVersion: "0.26.3"
|
||||||
|
|||||||
2
.github/helm/affine/charts/doc/Chart.yaml
vendored
2
.github/helm/affine/charts/doc/Chart.yaml
vendored
@@ -3,7 +3,7 @@ name: doc
|
|||||||
description: AFFiNE doc server
|
description: AFFiNE doc server
|
||||||
type: application
|
type: application
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
appVersion: "0.26.1"
|
appVersion: "0.26.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
- name: gcloud-sql-proxy
|
- name: gcloud-sql-proxy
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
|
|||||||
2
.github/helm/affine/charts/front/Chart.yaml
vendored
2
.github/helm/affine/charts/front/Chart.yaml
vendored
@@ -3,7 +3,7 @@ name: front
|
|||||||
description: AFFiNE front server
|
description: AFFiNE front server
|
||||||
type: application
|
type: application
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
appVersion: "0.26.1"
|
appVersion: "0.26.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
- name: gcloud-sql-proxy
|
- name: gcloud-sql-proxy
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
|
|||||||
@@ -96,12 +96,20 @@ spec:
|
|||||||
httpGet:
|
httpGet:
|
||||||
path: /info
|
path: /info
|
||||||
port: http
|
port: http
|
||||||
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
|
initialDelaySeconds: {{ default .Values.probe.initialDelaySeconds .Values.probe.liveness.initialDelaySeconds }}
|
||||||
|
timeoutSeconds: {{ default .Values.probe.timeoutSeconds .Values.probe.liveness.timeoutSeconds }}
|
||||||
|
periodSeconds: {{ default .Values.probe.periodSeconds .Values.probe.liveness.periodSeconds }}
|
||||||
|
failureThreshold: {{ default .Values.probe.failureThreshold .Values.probe.liveness.failureThreshold }}
|
||||||
|
successThreshold: {{ default .Values.probe.successThreshold .Values.probe.liveness.successThreshold }}
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /info
|
path: /info
|
||||||
port: http
|
port: http
|
||||||
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
|
initialDelaySeconds: {{ default .Values.probe.initialDelaySeconds .Values.probe.readiness.initialDelaySeconds }}
|
||||||
|
timeoutSeconds: {{ default .Values.probe.timeoutSeconds .Values.probe.readiness.timeoutSeconds }}
|
||||||
|
periodSeconds: {{ default .Values.probe.periodSeconds .Values.probe.readiness.periodSeconds }}
|
||||||
|
failureThreshold: {{ default .Values.probe.failureThreshold .Values.probe.readiness.failureThreshold }}
|
||||||
|
successThreshold: {{ default .Values.probe.successThreshold .Values.probe.readiness.successThreshold }}
|
||||||
resources:
|
resources:
|
||||||
{{- toYaml .Values.resources | nindent 12 }}
|
{{- toYaml .Values.resources | nindent 12 }}
|
||||||
{{- with .Values.nodeSelector }}
|
{{- with .Values.nodeSelector }}
|
||||||
|
|||||||
10
.github/helm/affine/charts/front/values.yaml
vendored
10
.github/helm/affine/charts/front/values.yaml
vendored
@@ -31,13 +31,21 @@ podSecurityContext:
|
|||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
cpu: '1'
|
cpu: '1'
|
||||||
memory: 2Gi
|
memory: 4Gi
|
||||||
requests:
|
requests:
|
||||||
cpu: '1'
|
cpu: '1'
|
||||||
memory: 2Gi
|
memory: 2Gi
|
||||||
|
|
||||||
probe:
|
probe:
|
||||||
initialDelaySeconds: 20
|
initialDelaySeconds: 20
|
||||||
|
timeoutSeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
failureThreshold: 6
|
||||||
|
successThreshold: 1
|
||||||
|
liveness:
|
||||||
|
initialDelaySeconds: 60
|
||||||
|
failureThreshold: 12
|
||||||
|
readiness: {}
|
||||||
|
|
||||||
services:
|
services:
|
||||||
sync:
|
sync:
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ name: graphql
|
|||||||
description: AFFiNE GraphQL server
|
description: AFFiNE GraphQL server
|
||||||
type: application
|
type: application
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
appVersion: "0.26.1"
|
appVersion: "0.26.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
- name: gcloud-sql-proxy
|
- name: gcloud-sql-proxy
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
|
|||||||
100
.github/workflows/build-test.yml
vendored
100
.github/workflows/build-test.yml
vendored
@@ -68,9 +68,26 @@ jobs:
|
|||||||
runs-on: ubuntu-24.04-arm
|
runs-on: ubuntu-24.04-arm
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Go (for actionlint)
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: 'stable'
|
||||||
|
- name: Install actionlint
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
go install github.com/rhysd/actionlint/cmd/actionlint@v1.7.11
|
||||||
|
- name: Run actionlint
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
"$(go env GOPATH)/bin/actionlint"
|
||||||
- name: Run oxlint
|
- name: Run oxlint
|
||||||
# oxlint is fast, so wrong code will fail quickly
|
# oxlint is fast, so wrong code will fail quickly
|
||||||
run: yarn dlx $(node -e "console.log(require('./package.json').scripts['lint:ox'].replace('oxlint', 'oxlint@' + require('./package.json').devDependencies.oxlint))")
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
oxlint_version="$(node -e "console.log(require('./package.json').devDependencies.oxlint)")"
|
||||||
|
yarn dlx "oxlint@${oxlint_version}" --deny-warnings
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
with:
|
with:
|
||||||
@@ -108,20 +125,45 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
yarn affine bs-docs build
|
yarn affine bs-docs build
|
||||||
git checkout packages/frontend/i18n/src/i18n-completenesses.json
|
git checkout packages/frontend/i18n/src/i18n-completenesses.json
|
||||||
git status --porcelain | grep . && {
|
if git status --porcelain | grep -q .; then
|
||||||
echo "Run 'yarn typecheck && yarn affine bs-docs build' and make sure all changes are submitted"
|
echo "Run 'yarn typecheck && yarn affine bs-docs build' and make sure all changes are submitted"
|
||||||
exit 1
|
exit 1
|
||||||
} || {
|
else
|
||||||
echo "All changes are submitted"
|
echo "All changes are submitted"
|
||||||
}
|
fi
|
||||||
|
|
||||||
|
rust-test-filter:
|
||||||
|
name: Rust test filter
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
run-rust: ${{ steps.rust-filter.outputs.rust }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: dorny/paths-filter@v3
|
||||||
|
id: rust-filter
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
rust:
|
||||||
|
- '**/*.rs'
|
||||||
|
- '**/Cargo.toml'
|
||||||
|
- '**/Cargo.lock'
|
||||||
|
- '.cargo/**'
|
||||||
|
- 'rust-toolchain*'
|
||||||
|
- '.github/actions/build-rust/**'
|
||||||
|
|
||||||
lint-rust:
|
lint-rust:
|
||||||
name: Lint Rust
|
name: Lint Rust
|
||||||
|
if: ${{ needs.rust-test-filter.outputs.run-rust == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- rust-test-filter
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: ./.github/actions/build-rust
|
- uses: ./.github/actions/build-rust
|
||||||
with:
|
with:
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
package: 'affine'
|
||||||
no-build: 'true'
|
no-build: 'true'
|
||||||
- name: fmt check
|
- name: fmt check
|
||||||
run: |
|
run: |
|
||||||
@@ -159,12 +201,12 @@ jobs:
|
|||||||
yarn affine i18n build
|
yarn affine i18n build
|
||||||
yarn affine server genconfig
|
yarn affine server genconfig
|
||||||
git checkout packages/frontend/i18n/src/i18n-completenesses.json
|
git checkout packages/frontend/i18n/src/i18n-completenesses.json
|
||||||
git status --porcelain | grep . && {
|
if git status --porcelain | grep -q .; then
|
||||||
echo "Run 'yarn affine init && yarn affine gql build && yarn affine i18n build && yarn affine server genconfig' and make sure all changes are submitted"
|
echo "Run 'yarn affine init && yarn affine gql build && yarn affine i18n build && yarn affine server genconfig' and make sure all changes are submitted"
|
||||||
exit 1
|
exit 1
|
||||||
} || {
|
else
|
||||||
echo "All changes are submitted"
|
echo "All changes are submitted"
|
||||||
}
|
fi
|
||||||
|
|
||||||
check-yarn-binary:
|
check-yarn-binary:
|
||||||
name: Check yarn binary
|
name: Check yarn binary
|
||||||
@@ -173,7 +215,9 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Run check
|
- name: Run check
|
||||||
run: |
|
run: |
|
||||||
yarn set version $(node -e "console.log(require('./package.json').packageManager.split('@')[1])")
|
set -euo pipefail
|
||||||
|
yarn_version="$(node -e "console.log(require('./package.json').packageManager.split('@')[1])")"
|
||||||
|
yarn set version "$yarn_version"
|
||||||
git diff --exit-code
|
git diff --exit-code
|
||||||
|
|
||||||
e2e-blocksuite-test:
|
e2e-blocksuite-test:
|
||||||
@@ -188,6 +232,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
with:
|
with:
|
||||||
|
extra-flags: workspaces focus @affine/monorepo @affine-test/blocksuite @blocksuite/playground @blocksuite/integration-test
|
||||||
playwright-install: true
|
playwright-install: true
|
||||||
playwright-platform: 'chromium'
|
playwright-platform: 'chromium'
|
||||||
electron-install: false
|
electron-install: false
|
||||||
@@ -215,6 +260,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
with:
|
with:
|
||||||
|
extra-flags: workspaces focus @affine/monorepo @affine-test/blocksuite @blocksuite/playground @blocksuite/integration-test
|
||||||
playwright-install: true
|
playwright-install: true
|
||||||
playwright-platform: 'chromium,firefox,webkit'
|
playwright-platform: 'chromium,firefox,webkit'
|
||||||
electron-install: false
|
electron-install: false
|
||||||
@@ -298,6 +344,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
with:
|
with:
|
||||||
|
extra-flags: workspaces focus @affine/monorepo @affine-test/affine-local @affine/web @affine/server
|
||||||
playwright-install: true
|
playwright-install: true
|
||||||
playwright-platform: 'chromium'
|
playwright-platform: 'chromium'
|
||||||
electron-install: false
|
electron-install: false
|
||||||
@@ -329,6 +376,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
with:
|
with:
|
||||||
|
extra-flags: workspaces focus @affine/monorepo @affine-test/affine-mobile @affine/mobile
|
||||||
playwright-install: true
|
playwright-install: true
|
||||||
electron-install: false
|
electron-install: false
|
||||||
full-cache: true
|
full-cache: true
|
||||||
@@ -400,7 +448,7 @@ jobs:
|
|||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
export PLATFORM_ARCH_ABI=$(node -e "console.log(require('@napi-rs/cli').parseTriple('x86_64-unknown-linux-gnu').platformArchABI)")
|
PLATFORM_ARCH_ABI="$(node -e "console.log(require('@napi-rs/cli').parseTriple('x86_64-unknown-linux-gnu').platformArchABI)")"
|
||||||
echo "filename=affine.$PLATFORM_ARCH_ABI.node" >> "$GITHUB_OUTPUT"
|
echo "filename=affine.$PLATFORM_ARCH_ABI.node" >> "$GITHUB_OUTPUT"
|
||||||
- name: Build AFFiNE native
|
- name: Build AFFiNE native
|
||||||
uses: ./.github/actions/build-rust
|
uses: ./.github/actions/build-rust
|
||||||
@@ -439,7 +487,7 @@ jobs:
|
|||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
export PLATFORM_ARCH_ABI=$(node -e "console.log(require('@napi-rs/cli').parseTriple('${{ matrix.spec.target }}').platformArchABI)")
|
PLATFORM_ARCH_ABI="$(node -e "console.log(require('@napi-rs/cli').parseTriple('${{ matrix.spec.target }}').platformArchABI)")"
|
||||||
echo "filename=affine.$PLATFORM_ARCH_ABI.node" >> "$GITHUB_OUTPUT"
|
echo "filename=affine.$PLATFORM_ARCH_ABI.node" >> "$GITHUB_OUTPUT"
|
||||||
- name: Build AFFiNE native
|
- name: Build AFFiNE native
|
||||||
uses: ./.github/actions/build-rust
|
uses: ./.github/actions/build-rust
|
||||||
@@ -488,7 +536,7 @@ jobs:
|
|||||||
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}
|
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
export PLATFORM_ARCH_ABI=$(node -e "console.log(require('@napi-rs/cli').parseTriple('${{ matrix.spec.target }}').platformArchABI)")
|
PLATFORM_ARCH_ABI="$(node -e "console.log(require('@napi-rs/cli').parseTriple('${{ matrix.spec.target }}').platformArchABI)")"
|
||||||
echo "filename=affine.$PLATFORM_ARCH_ABI.node" >> "$GITHUB_OUTPUT"
|
echo "filename=affine.$PLATFORM_ARCH_ABI.node" >> "$GITHUB_OUTPUT"
|
||||||
- name: Build AFFiNE native
|
- name: Build AFFiNE native
|
||||||
uses: ./.github/actions/build-rust
|
uses: ./.github/actions/build-rust
|
||||||
@@ -536,6 +584,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
with:
|
with:
|
||||||
|
extra-flags: workspaces focus @affine/monorepo @affine/electron-renderer @affine/nbstore @toeverything/infra
|
||||||
electron-install: false
|
electron-install: false
|
||||||
full-cache: true
|
full-cache: true
|
||||||
- name: Build Electron renderer
|
- name: Build Electron renderer
|
||||||
@@ -617,6 +666,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
with:
|
with:
|
||||||
|
extra-flags: workspaces focus @affine/monorepo @affine/server
|
||||||
electron-install: false
|
electron-install: false
|
||||||
full-cache: true
|
full-cache: true
|
||||||
|
|
||||||
@@ -697,6 +747,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
with:
|
with:
|
||||||
|
extra-flags: workspaces focus @affine/monorepo @affine/server
|
||||||
electron-install: false
|
electron-install: false
|
||||||
full-cache: true
|
full-cache: true
|
||||||
|
|
||||||
@@ -713,8 +764,6 @@ jobs:
|
|||||||
run: yarn affine @affine/server test:coverage "**/*/*elasticsearch.spec.ts" --forbid-only
|
run: yarn affine @affine/server test:coverage "**/*/*elasticsearch.spec.ts" --forbid-only
|
||||||
env:
|
env:
|
||||||
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
|
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
|
||||||
CI_NODE_INDEX: ${{ matrix.node_index }}
|
|
||||||
CI_NODE_TOTAL: ${{ matrix.total_nodes }}
|
|
||||||
|
|
||||||
- name: Upload server test coverage results
|
- name: Upload server test coverage results
|
||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v5
|
||||||
@@ -761,6 +810,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
with:
|
with:
|
||||||
|
extra-flags: workspaces focus @affine/monorepo @affine/server
|
||||||
electron-install: false
|
electron-install: false
|
||||||
full-cache: true
|
full-cache: true
|
||||||
|
|
||||||
@@ -787,7 +837,10 @@ jobs:
|
|||||||
|
|
||||||
miri:
|
miri:
|
||||||
name: miri code check
|
name: miri code check
|
||||||
|
if: ${{ needs.rust-test-filter.outputs.run-rust == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- rust-test-filter
|
||||||
env:
|
env:
|
||||||
RUST_BACKTRACE: full
|
RUST_BACKTRACE: full
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
@@ -812,7 +865,10 @@ jobs:
|
|||||||
|
|
||||||
loom:
|
loom:
|
||||||
name: loom thread test
|
name: loom thread test
|
||||||
|
if: ${{ needs.rust-test-filter.outputs.run-rust == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- rust-test-filter
|
||||||
env:
|
env:
|
||||||
RUSTFLAGS: --cfg loom
|
RUSTFLAGS: --cfg loom
|
||||||
RUST_BACKTRACE: full
|
RUST_BACKTRACE: full
|
||||||
@@ -835,7 +891,10 @@ jobs:
|
|||||||
|
|
||||||
fuzzing:
|
fuzzing:
|
||||||
name: fuzzing
|
name: fuzzing
|
||||||
|
if: ${{ needs.rust-test-filter.outputs.run-rust == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- rust-test-filter
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
steps:
|
steps:
|
||||||
@@ -871,7 +930,10 @@ jobs:
|
|||||||
|
|
||||||
rust-test:
|
rust-test:
|
||||||
name: Run native tests
|
name: Run native tests
|
||||||
|
if: ${{ needs.rust-test-filter.outputs.run-rust == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- rust-test-filter
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
steps:
|
steps:
|
||||||
@@ -879,6 +941,7 @@ jobs:
|
|||||||
- name: Setup Rust
|
- name: Setup Rust
|
||||||
uses: ./.github/actions/build-rust
|
uses: ./.github/actions/build-rust
|
||||||
with:
|
with:
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
package: 'affine'
|
package: 'affine'
|
||||||
no-build: 'true'
|
no-build: 'true'
|
||||||
|
|
||||||
@@ -971,6 +1034,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
with:
|
with:
|
||||||
|
extra-flags: workspaces focus @affine/monorepo @affine/server
|
||||||
electron-install: false
|
electron-install: false
|
||||||
full-cache: true
|
full-cache: true
|
||||||
|
|
||||||
@@ -1043,6 +1107,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
with:
|
with:
|
||||||
|
extra-flags: workspaces focus @affine/monorepo @affine-test/affine-cloud-copilot @affine/web @affine/server
|
||||||
playwright-install: true
|
playwright-install: true
|
||||||
playwright-platform: 'chromium'
|
playwright-platform: 'chromium'
|
||||||
electron-install: false
|
electron-install: false
|
||||||
@@ -1125,7 +1190,10 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
with:
|
with:
|
||||||
|
extra-flags: workspaces focus @affine/monorepo @affine-test/affine-cloud @affine-test/affine-desktop-cloud @affine/web @affine/server @affine/electron @affine/electron-renderer @affine/nbstore @toeverything/infra
|
||||||
playwright-install: true
|
playwright-install: true
|
||||||
|
playwright-platform: 'chromium'
|
||||||
|
electron-install: ${{ matrix.tests.shard == 'desktop' && 'true' || 'false' }}
|
||||||
hard-link-nm: false
|
hard-link-nm: false
|
||||||
|
|
||||||
- name: Download server-native.node
|
- name: Download server-native.node
|
||||||
@@ -1204,7 +1272,8 @@ jobs:
|
|||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
with:
|
with:
|
||||||
extra-flags: workspaces focus @affine/electron @affine/monorepo @affine-test/affine-desktop @affine/nbstore @toeverything/infra
|
extra-flags: workspaces focus @affine/electron @affine/monorepo @affine-test/affine-desktop @affine/nbstore @toeverything/infra
|
||||||
playwright-install: true
|
playwright-install: ${{ matrix.spec.test && 'true' || 'false' }}
|
||||||
|
playwright-platform: 'chromium'
|
||||||
hard-link-nm: false
|
hard-link-nm: false
|
||||||
enableScripts: false
|
enableScripts: false
|
||||||
|
|
||||||
@@ -1212,7 +1281,7 @@ jobs:
|
|||||||
id: filename
|
id: filename
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
export PLATFORM_ARCH_ABI=$(node -e "console.log(require('@napi-rs/cli').parseTriple('${{ matrix.spec.target }}').platformArchABI)")
|
PLATFORM_ARCH_ABI="$(node -e "console.log(require('@napi-rs/cli').parseTriple('${{ matrix.spec.target }}').platformArchABI)")"
|
||||||
echo "filename=affine.$PLATFORM_ARCH_ABI.node" >> "$GITHUB_OUTPUT"
|
echo "filename=affine.$PLATFORM_ARCH_ABI.node" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Download ${{ steps.filename.outputs.filename }}
|
- name: Download ${{ steps.filename.outputs.filename }}
|
||||||
@@ -1319,6 +1388,7 @@ jobs:
|
|||||||
- server-test
|
- server-test
|
||||||
- server-e2e-test
|
- server-e2e-test
|
||||||
- rust-test
|
- rust-test
|
||||||
|
- rust-test-filter
|
||||||
- copilot-test-filter
|
- copilot-test-filter
|
||||||
- copilot-api-test
|
- copilot-api-test
|
||||||
- copilot-e2e-test
|
- copilot-e2e-test
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
mkdir -p builds
|
mkdir -p builds
|
||||||
mv packages/frontend/apps/electron/out/*/make/zip/linux/${{ inputs.arch }}/*.zip ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.zip
|
mv packages/frontend/apps/electron/out/*/make/zip/linux/${{ inputs.arch }}/*.zip ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.zip
|
||||||
mv packages/frontend/apps/electron/out/*/make/*.AppImage ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.appimage
|
mv packages/frontend/apps/electron/out/*/make/AppImage/${{ inputs.arch }}/*.AppImage ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.appimage
|
||||||
mv packages/frontend/apps/electron/out/*/make/deb/${{ inputs.arch }}/*.deb ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.deb
|
mv packages/frontend/apps/electron/out/*/make/deb/${{ inputs.arch }}/*.deb ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.deb
|
||||||
mv packages/frontend/apps/electron/out/*/make/flatpak/*/*.flatpak ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.flatpak
|
mv packages/frontend/apps/electron/out/*/make/flatpak/*/*.flatpak ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.flatpak
|
||||||
|
|
||||||
|
|||||||
10
.github/workflows/release-mobile.yml
vendored
10
.github/workflows/release-mobile.yml
vendored
@@ -128,9 +128,9 @@ jobs:
|
|||||||
- name: Testflight
|
- name: Testflight
|
||||||
working-directory: packages/frontend/apps/ios/App
|
working-directory: packages/frontend/apps/ios/App
|
||||||
run: |
|
run: |
|
||||||
echo -n "${{ env.BUILD_PROVISION_PROFILE }}" | base64 --decode -o $PP_PATH
|
printf '%s' "$BUILD_PROVISION_PROFILE" | base64 --decode -o "$PP_PATH"
|
||||||
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles"
|
||||||
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
|
cp "$PP_PATH" "$HOME/Library/MobileDevice/Provisioning Profiles"
|
||||||
fastlane beta
|
fastlane beta
|
||||||
env:
|
env:
|
||||||
BUILD_TARGET: distribution
|
BUILD_TARGET: distribution
|
||||||
@@ -160,7 +160,9 @@ jobs:
|
|||||||
- name: Load Google Service file
|
- name: Load Google Service file
|
||||||
env:
|
env:
|
||||||
DATA: ${{ secrets.FIREBASE_ANDROID_GOOGLE_SERVICE_JSON }}
|
DATA: ${{ secrets.FIREBASE_ANDROID_GOOGLE_SERVICE_JSON }}
|
||||||
run: echo $DATA | base64 -di > packages/frontend/apps/android/App/app/google-services.json
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
printf '%s' "$DATA" | base64 -di > packages/frontend/apps/android/App/app/google-services.json
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -91,8 +91,8 @@ Thanks for checking us out, we appreciate your interest and sincerely hope that
|
|||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
| Bug Reports | Feature Requests | Questions/Discussions | AFFiNE Community |
|
| Bug Reports | Feature Requests | Questions/Discussions | AFFiNE Community |
|
||||||
| --------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------- |
|
| --------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------- |
|
||||||
| [Create a bug report](https://github.com/toeverything/AFFiNE/issues/new?assignees=&labels=bug%2Cproduct-review&template=BUG-REPORT.yml&title=TITLE) | [Submit a feature request](https://github.com/toeverything/AFFiNE/issues/new?assignees=&labels=feat%2Cproduct-review&template=FEATURE-REQUEST.yml&title=TITLE) | [Check GitHub Discussion](https://github.com/toeverything/AFFiNE/discussions) | [Visit the AFFiNE Community](https://community.affine.pro) |
|
| [Create a bug report](https://github.com/toeverything/AFFiNE/issues/new?assignees=&labels=bug%2Cproduct-review&template=BUG-REPORT.yml&title=TITLE) | [Submit a feature request](https://github.com/toeverything/AFFiNE/issues/new?assignees=&labels=feat%2Cproduct-review&template=FEATURE-REQUEST.yml&title=TITLE) | [Check GitHub Discussion](https://github.com/toeverything/AFFiNE/discussions) | [Visit the AFFiNE's Discord](https://affine.pro/redirect/discord) |
|
||||||
| Something isn't working as expected | An idea for a new feature, or improvements | Discuss and ask questions | A place to ask, learn and engage with others |
|
| Something isn't working as expected | An idea for a new feature, or improvements | Discuss and ask questions | A place to ask, learn and engage with others |
|
||||||
|
|
||||||
Calling all developers, testers, tech writers and more! Contributions of all types are more than welcome, you can read more in [docs/types-of-contributions.md](docs/types-of-contributions.md). If you are interested in contributing code, read our [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) and feel free to check out our GitHub issues to get stuck in to show us what you’re made of.
|
Calling all developers, testers, tech writers and more! Contributions of all types are more than welcome, you can read more in [docs/types-of-contributions.md](docs/types-of-contributions.md). If you are interested in contributing code, read our [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) and feel free to check out our GitHub issues to get stuck in to show us what you’re made of.
|
||||||
@@ -101,11 +101,9 @@ Calling all developers, testers, tech writers and more! Contributions of all typ
|
|||||||
|
|
||||||
For **bug reports**, **feature requests** and other **suggestions** you can also [create a new issue](https://github.com/toeverything/AFFiNE/issues/new/choose) and choose the most appropriate template for your feedback.
|
For **bug reports**, **feature requests** and other **suggestions** you can also [create a new issue](https://github.com/toeverything/AFFiNE/issues/new/choose) and choose the most appropriate template for your feedback.
|
||||||
|
|
||||||
For **translation** and **language support** you can visit our [i18n General Space](https://community.affine.pro/c/i18n-general).
|
For **translation** and **language support** you can visit our [Discord](https://affine.pro/redirect/discord).
|
||||||
|
|
||||||
Looking for **other ways to contribute** and wondering where to start? Check out the [AFFiNE Ambassador program](https://community.affine.pro/c/start-here/affine-ambassador), we work closely with passionate community members and provide them with a wide range of support and resources.
|
If you have questions, you are welcome to contact us. One of the best places to get more info and learn more is in the [Discord](https://affine.pro/redirect/discord) where you can engage with other like-minded individuals.
|
||||||
|
|
||||||
If you have questions, you are welcome to contact us. One of the best places to get more info and learn more is in the [AFFiNE Community](https://community.affine.pro) where you can engage with other like-minded individuals.
|
|
||||||
|
|
||||||
## Templates
|
## Templates
|
||||||
|
|
||||||
@@ -182,20 +180,16 @@ Begin with Docker to deploy your own feature-rich, unrestricted version of AFFiN
|
|||||||
|
|
||||||
[](https://template.run.claw.cloud/?openapp=system-fastdeploy%3FtemplateName%3Daffine)
|
[](https://template.run.claw.cloud/?openapp=system-fastdeploy%3FtemplateName%3Daffine)
|
||||||
|
|
||||||
## Hiring
|
|
||||||
|
|
||||||
Some amazing companies, including AFFiNE, are looking for developers! Are you interested in joining AFFiNE or its partners? Check out our [Discord channel](https://affine.pro/redirect/discord) for some of the latest jobs available.
|
|
||||||
|
|
||||||
## Feature Request
|
## Feature Request
|
||||||
|
|
||||||
For feature requests, please see [community.affine.pro](https://community.affine.pro/c/feature-requests/).
|
For feature requests, please see [discussions](https://github.com/toeverything/AFFiNE/discussions/categories/ideas).
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
### Codespaces
|
### Codespaces
|
||||||
|
|
||||||
From the GitHub repo main page, click the green "Code" button and select "Create codespace on master". This will open a new Codespace with the (supposedly auto-forked
|
From the GitHub repo main page, click the green "Code" button and select "Create codespace on master". This will open a new Codespace with the (supposedly auto-forked
|
||||||
AFFiNE repo cloned, built, and ready to go.
|
AFFiNE repo cloned, built, and ready to go).
|
||||||
|
|
||||||
### Local
|
### Local
|
||||||
|
|
||||||
|
|||||||
@@ -296,7 +296,7 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1",
|
"version": "0.26.3",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vanilla-extract/vite-plugin": "^5.0.0",
|
"@vanilla-extract/vite-plugin": "^5.0.0",
|
||||||
"msw": "^2.12.4",
|
"msw": "^2.12.4",
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
"@toeverything/theme": "^1.1.23",
|
"@toeverything/theme": "^1.1.23",
|
||||||
"file-type": "^21.0.0",
|
"file-type": "^21.0.0",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
@@ -41,5 +40,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
"@preact/signals-core": "^1.8.0",
|
"@preact/signals-core": "^1.8.0",
|
||||||
"@toeverything/theme": "^1.1.23",
|
"@toeverything/theme": "^1.1.23",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"yjs": "^13.6.27",
|
"yjs": "^13.6.27",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
@@ -45,5 +44,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
"@types/mdast": "^4.0.4",
|
"@types/mdast": "^4.0.4",
|
||||||
"emoji-mart": "^5.6.0",
|
"emoji-mart": "^5.6.0",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
@@ -45,5 +44,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,9 +216,13 @@ export class CalloutBlockComponent extends CaptionedBlockComponent<CalloutBlockM
|
|||||||
override renderBlock() {
|
override renderBlock() {
|
||||||
const icon = this.model.props.icon$.value;
|
const icon = this.model.props.icon$.value;
|
||||||
const backgroundColorName = this.model.props.backgroundColorName$.value;
|
const backgroundColorName = this.model.props.backgroundColorName$.value;
|
||||||
|
const normalizedBackgroundName =
|
||||||
|
backgroundColorName === 'default' || backgroundColorName === ''
|
||||||
|
? 'grey'
|
||||||
|
: backgroundColorName;
|
||||||
const backgroundColor = (
|
const backgroundColor = (
|
||||||
cssVarV2.block.callout.background as Record<string, string>
|
cssVarV2.block.callout.background as Record<string, string>
|
||||||
)[backgroundColorName ?? ''];
|
)[normalizedBackgroundName ?? 'grey'];
|
||||||
|
|
||||||
const iconContent = getIcon(icon);
|
const iconContent = getIcon(icon);
|
||||||
|
|
||||||
|
|||||||
@@ -68,14 +68,14 @@ const backgroundColorAction = {
|
|||||||
${repeat(colors, color => {
|
${repeat(colors, color => {
|
||||||
const isDefault = color === 'default';
|
const isDefault = color === 'default';
|
||||||
const value = isDefault
|
const value = isDefault
|
||||||
? null
|
? cssVarV2.block.callout.background.grey
|
||||||
: `var(--affine-text-highlight-${color})`;
|
: `var(--affine-text-highlight-${color})`;
|
||||||
const displayName = `${color} Background`;
|
const displayName = `${color} Background`;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<editor-menu-action
|
<editor-menu-action
|
||||||
data-testid="background-${color}"
|
data-testid="background-${color}"
|
||||||
@click=${() => updateBackground(color)}
|
@click=${() => updateBackground(isDefault ? 'grey' : color)}
|
||||||
>
|
>
|
||||||
<affine-text-duotone-icon
|
<affine-text-duotone-icon
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
"@toeverything/theme": "^1.1.23",
|
"@toeverything/theme": "^1.1.23",
|
||||||
"@types/mdast": "^4.0.4",
|
"@types/mdast": "^4.0.4",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"shiki": "^3.19.0",
|
"shiki": "^3.19.0",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
@@ -48,5 +47,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,16 @@ export const codeBlockStyles = css`
|
|||||||
|
|
||||||
${scrollbarStyle('.affine-code-block-container rich-text')}
|
${scrollbarStyle('.affine-code-block-container rich-text')}
|
||||||
|
|
||||||
|
/* In Chromium 121+, non-auto scrollbar-width/color override ::-webkit-scrollbar styles. */
|
||||||
|
@supports not selector(::-webkit-scrollbar) {
|
||||||
|
.affine-code-block-container rich-text {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: ${unsafeCSSVarV2('icon/secondary', '#b1b1b1')}
|
||||||
|
transparent;
|
||||||
|
scrollbar-gutter: stable both-edges;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.affine-code-block-container .inline-editor {
|
.affine-code-block-container .inline-editor {
|
||||||
font-family: var(--affine-font-code-family);
|
font-family: var(--affine-font-code-family);
|
||||||
font-variant-ligatures: none;
|
font-variant-ligatures: none;
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
"@toeverything/theme": "^1.1.23",
|
"@toeverything/theme": "^1.1.23",
|
||||||
"@types/mdast": "^4.0.4",
|
"@types/mdast": "^4.0.4",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
@@ -42,5 +41,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
"@types/mdast": "^4.0.4",
|
"@types/mdast": "^4.0.4",
|
||||||
"date-fns": "^4.0.0",
|
"date-fns": "^4.0.0",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"yjs": "^13.6.27",
|
"yjs": "^13.6.27",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
@@ -48,5 +47,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
"@toeverything/theme": "^1.1.23",
|
"@toeverything/theme": "^1.1.23",
|
||||||
"@types/mdast": "^4.0.4",
|
"@types/mdast": "^4.0.4",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
@@ -39,5 +38,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
"@preact/signals-core": "^1.8.0",
|
"@preact/signals-core": "^1.8.0",
|
||||||
"@toeverything/theme": "^1.1.23",
|
"@toeverything/theme": "^1.1.23",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
@@ -43,5 +42,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"lodash-es": "^4.17.23",
|
"lodash-es": "^4.17.23",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"yjs": "^13.6.27",
|
"yjs": "^13.6.27",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
@@ -49,5 +48,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"lodash-es": "^4.17.23",
|
"lodash-es": "^4.17.23",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"yjs": "^13.6.27",
|
"yjs": "^13.6.27",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
@@ -49,5 +48,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
"@toeverything/theme": "^1.1.23",
|
"@toeverything/theme": "^1.1.23",
|
||||||
"@types/mdast": "^4.0.4",
|
"@types/mdast": "^4.0.4",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"yjs": "^13.6.27",
|
"yjs": "^13.6.27",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
@@ -44,5 +43,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
"@toeverything/theme": "^1.1.23",
|
"@toeverything/theme": "^1.1.23",
|
||||||
"file-type": "^21.0.0",
|
"file-type": "^21.0.0",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
@@ -44,5 +43,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
"@types/mdast": "^4.0.4",
|
"@types/mdast": "^4.0.4",
|
||||||
"katex": "^0.16.27",
|
"katex": "^0.16.27",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"remark-math": "^6.0.0",
|
"remark-math": "^6.0.0",
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
@@ -46,5 +45,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
"@toeverything/theme": "^1.1.23",
|
"@toeverything/theme": "^1.1.23",
|
||||||
"@types/mdast": "^4.0.4",
|
"@types/mdast": "^4.0.4",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
@@ -46,5 +45,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@
|
|||||||
"@vanilla-extract/css": "^1.17.0",
|
"@vanilla-extract/css": "^1.17.0",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"lodash-es": "^4.17.23",
|
"lodash-es": "^4.17.23",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
@@ -49,5 +48,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
"@toeverything/theme": "^1.1.23",
|
"@toeverything/theme": "^1.1.23",
|
||||||
"@types/mdast": "^4.0.4",
|
"@types/mdast": "^4.0.4",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
@@ -42,5 +41,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,6 @@
|
|||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"lodash-es": "^4.17.23",
|
"lodash-es": "^4.17.23",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"yjs": "^13.6.27",
|
"yjs": "^13.6.27",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
@@ -67,5 +66,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,5 +45,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,5 +46,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,5 +42,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,5 +82,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,5 +48,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { describe, expect, it, vi } from 'vitest';
|
|||||||
import type { GroupBy } from '../core/common/types.js';
|
import type { GroupBy } from '../core/common/types.js';
|
||||||
import type { DataSource } from '../core/data-source/base.js';
|
import type { DataSource } from '../core/data-source/base.js';
|
||||||
import { DetailSelection } from '../core/detail/selection.js';
|
import { DetailSelection } from '../core/detail/selection.js';
|
||||||
|
import type { FilterGroup } from '../core/filter/types.js';
|
||||||
import { groupByMatchers } from '../core/group-by/define.js';
|
import { groupByMatchers } from '../core/group-by/define.js';
|
||||||
import { t } from '../core/logical/type-presets.js';
|
import { t } from '../core/logical/type-presets.js';
|
||||||
import type { DataViewCellLifeCycle } from '../core/property/index.js';
|
import type { DataViewCellLifeCycle } from '../core/property/index.js';
|
||||||
@@ -17,7 +18,10 @@ import {
|
|||||||
pickKanbanGroupColumn,
|
pickKanbanGroupColumn,
|
||||||
resolveKanbanGroupBy,
|
resolveKanbanGroupBy,
|
||||||
} from '../view-presets/kanban/group-by-utils.js';
|
} from '../view-presets/kanban/group-by-utils.js';
|
||||||
import { materializeKanbanColumns } from '../view-presets/kanban/kanban-view-manager.js';
|
import {
|
||||||
|
KanbanSingleView,
|
||||||
|
materializeKanbanColumns,
|
||||||
|
} from '../view-presets/kanban/kanban-view-manager.js';
|
||||||
import type { KanbanCard } from '../view-presets/kanban/pc/card.js';
|
import type { KanbanCard } from '../view-presets/kanban/pc/card.js';
|
||||||
import { KanbanDragController } from '../view-presets/kanban/pc/controller/drag.js';
|
import { KanbanDragController } from '../view-presets/kanban/pc/controller/drag.js';
|
||||||
import type { KanbanGroup } from '../view-presets/kanban/pc/group.js';
|
import type { KanbanGroup } from '../view-presets/kanban/pc/group.js';
|
||||||
@@ -270,6 +274,73 @@ describe('kanban', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('filtering', () => {
|
||||||
|
const sharedFilter: FilterGroup = {
|
||||||
|
type: 'group',
|
||||||
|
op: 'and',
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
type: 'filter',
|
||||||
|
left: {
|
||||||
|
type: 'ref',
|
||||||
|
name: 'status',
|
||||||
|
},
|
||||||
|
function: 'is',
|
||||||
|
args: [{ type: 'literal', value: 'Done' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const sharedTitleProperty = {
|
||||||
|
id: 'title',
|
||||||
|
cellGetOrCreate: () => ({
|
||||||
|
jsonValue$: {
|
||||||
|
value: 'Task 1',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
it('evaluates filters with hidden columns', () => {
|
||||||
|
const statusProperty = {
|
||||||
|
id: 'status',
|
||||||
|
cellGetOrCreate: () => ({
|
||||||
|
jsonValue$: {
|
||||||
|
value: 'Done',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const view = {
|
||||||
|
filter$: { value: sharedFilter },
|
||||||
|
// Simulate status being hidden in current view.
|
||||||
|
properties$: { value: [sharedTitleProperty] },
|
||||||
|
propertiesRaw$: { value: [sharedTitleProperty, statusProperty] },
|
||||||
|
} as unknown as KanbanSingleView;
|
||||||
|
|
||||||
|
expect(KanbanSingleView.prototype.isShow.call(view, 'row-1')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false when hidden filtered column does not match', () => {
|
||||||
|
const statusProperty = {
|
||||||
|
id: 'status',
|
||||||
|
cellGetOrCreate: () => ({
|
||||||
|
jsonValue$: {
|
||||||
|
value: 'In Progress',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const view = {
|
||||||
|
filter$: { value: sharedFilter },
|
||||||
|
// Simulate status being hidden in current view.
|
||||||
|
properties$: { value: [sharedTitleProperty] },
|
||||||
|
propertiesRaw$: { value: [sharedTitleProperty, statusProperty] },
|
||||||
|
} as unknown as KanbanSingleView;
|
||||||
|
|
||||||
|
expect(KanbanSingleView.prototype.isShow.call(view, 'row-1')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('drag indicator', () => {
|
describe('drag indicator', () => {
|
||||||
it('shows drop preview when insert position exists', () => {
|
it('shows drop preview when insert position exists', () => {
|
||||||
const controller = createDragController();
|
const controller = createDragController();
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
import { describe, expect, test } from 'vitest';
|
import { describe, expect, test } from 'vitest';
|
||||||
|
|
||||||
|
import type { FilterGroup } from '../core/filter/types.js';
|
||||||
import { numberFormats } from '../property-presets/number/utils/formats.js';
|
import { numberFormats } from '../property-presets/number/utils/formats.js';
|
||||||
import {
|
import {
|
||||||
formatNumber,
|
formatNumber,
|
||||||
NumberFormatSchema,
|
NumberFormatSchema,
|
||||||
parseNumber,
|
parseNumber,
|
||||||
} from '../property-presets/number/utils/formatter.js';
|
} from '../property-presets/number/utils/formatter.js';
|
||||||
|
import { DEFAULT_COLUMN_WIDTH } from '../view-presets/table/consts.js';
|
||||||
import { mobileEffects } from '../view-presets/table/mobile/effect.js';
|
import { mobileEffects } from '../view-presets/table/mobile/effect.js';
|
||||||
import type { MobileTableGroup } from '../view-presets/table/mobile/group.js';
|
import type { MobileTableGroup } from '../view-presets/table/mobile/group.js';
|
||||||
import { pcEffects } from '../view-presets/table/pc/effect.js';
|
import { pcEffects } from '../view-presets/table/pc/effect.js';
|
||||||
import type { TableGroup } from '../view-presets/table/pc/group.js';
|
import type { TableGroup } from '../view-presets/table/pc/group.js';
|
||||||
|
import {
|
||||||
|
materializeTableColumns,
|
||||||
|
TableSingleView,
|
||||||
|
} from '../view-presets/table/table-view-manager.js';
|
||||||
|
|
||||||
/** @vitest-environment happy-dom */
|
/** @vitest-environment happy-dom */
|
||||||
|
|
||||||
@@ -41,6 +47,146 @@ describe('TableGroup', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('table column materialization', () => {
|
||||||
|
test('appends missing properties while preserving existing order and state', () => {
|
||||||
|
const columns = [
|
||||||
|
{ id: 'status', width: 240, hide: true },
|
||||||
|
{ id: 'title', width: 320 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const next = materializeTableColumns(columns, ['title', 'status', 'date']);
|
||||||
|
|
||||||
|
expect(next).toEqual([
|
||||||
|
{ id: 'status', width: 240, hide: true },
|
||||||
|
{ id: 'title', width: 320 },
|
||||||
|
{ id: 'date', width: DEFAULT_COLUMN_WIDTH },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('drops stale columns that no longer exist in data source', () => {
|
||||||
|
const columns = [
|
||||||
|
{ id: 'title', width: 320 },
|
||||||
|
{ id: 'removed', width: 200, hide: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
const next = materializeTableColumns(columns, ['title']);
|
||||||
|
|
||||||
|
expect(next).toEqual([{ id: 'title', width: 320 }]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns original reference when columns are already materialized', () => {
|
||||||
|
const columns = [
|
||||||
|
{ id: 'title', width: 320 },
|
||||||
|
{ id: 'status', width: 240, hide: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
const next = materializeTableColumns(columns, ['title', 'status']);
|
||||||
|
|
||||||
|
expect(next).toBe(columns);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('supports type-aware default width when materializing missing columns', () => {
|
||||||
|
const next = materializeTableColumns([], ['title', 'status'], id =>
|
||||||
|
id === 'title' ? 260 : DEFAULT_COLUMN_WIDTH
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(next).toEqual([
|
||||||
|
{ id: 'title', width: 260 },
|
||||||
|
{ id: 'status', width: DEFAULT_COLUMN_WIDTH },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('table filtering', () => {
|
||||||
|
test('evaluates filters with hidden columns', () => {
|
||||||
|
const filter: FilterGroup = {
|
||||||
|
type: 'group',
|
||||||
|
op: 'and',
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
type: 'filter',
|
||||||
|
left: {
|
||||||
|
type: 'ref',
|
||||||
|
name: 'status',
|
||||||
|
},
|
||||||
|
function: 'is',
|
||||||
|
args: [{ type: 'literal', value: 'Done' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const titleProperty = {
|
||||||
|
id: 'title',
|
||||||
|
cellGetOrCreate: () => ({
|
||||||
|
jsonValue$: {
|
||||||
|
value: 'Task 1',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
const statusProperty = {
|
||||||
|
id: 'status',
|
||||||
|
cellGetOrCreate: () => ({
|
||||||
|
jsonValue$: {
|
||||||
|
value: 'Done',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const view = {
|
||||||
|
filter$: { value: filter },
|
||||||
|
// Simulate status being hidden in current view.
|
||||||
|
properties$: { value: [titleProperty] },
|
||||||
|
propertiesRaw$: { value: [titleProperty, statusProperty] },
|
||||||
|
} as unknown as TableSingleView;
|
||||||
|
|
||||||
|
expect(TableSingleView.prototype.isShow.call(view, 'row-1')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns false when hidden filtered column does not match', () => {
|
||||||
|
const filter: FilterGroup = {
|
||||||
|
type: 'group',
|
||||||
|
op: 'and',
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
type: 'filter',
|
||||||
|
left: {
|
||||||
|
type: 'ref',
|
||||||
|
name: 'status',
|
||||||
|
},
|
||||||
|
function: 'is',
|
||||||
|
args: [{ type: 'literal', value: 'Done' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const titleProperty = {
|
||||||
|
id: 'title',
|
||||||
|
cellGetOrCreate: () => ({
|
||||||
|
jsonValue$: {
|
||||||
|
value: 'Task 1',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
const statusProperty = {
|
||||||
|
id: 'status',
|
||||||
|
cellGetOrCreate: () => ({
|
||||||
|
jsonValue$: {
|
||||||
|
value: 'In Progress',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const view = {
|
||||||
|
filter$: { value: filter },
|
||||||
|
// Simulate status being hidden in current view.
|
||||||
|
properties$: { value: [titleProperty] },
|
||||||
|
propertiesRaw$: { value: [titleProperty, statusProperty] },
|
||||||
|
} as unknown as TableSingleView;
|
||||||
|
|
||||||
|
expect(TableSingleView.prototype.isShow.call(view, 'row-1')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('number formatter', () => {
|
describe('number formatter', () => {
|
||||||
test('number format menu should expose all schema formats', () => {
|
test('number format menu should expose all schema formats', () => {
|
||||||
const menuFormats = numberFormats.map(format => format.type);
|
const menuFormats = numberFormats.map(format => format.type);
|
||||||
|
|||||||
@@ -349,7 +349,7 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
|
|||||||
isShow(rowId: string): boolean {
|
isShow(rowId: string): boolean {
|
||||||
if (this.filter$.value?.conditions.length) {
|
if (this.filter$.value?.conditions.length) {
|
||||||
const rowMap = Object.fromEntries(
|
const rowMap = Object.fromEntries(
|
||||||
this.properties$.value.map(column => [
|
this.propertiesRaw$.value.map(column => [
|
||||||
column.id,
|
column.id,
|
||||||
column.cellGetOrCreate(rowId).jsonValue$.value,
|
column.cellGetOrCreate(rowId).jsonValue$.value,
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -54,7 +54,9 @@ export class DatabaseCellContainer extends SignalWatcher(
|
|||||||
const selectionView = this.selectionView;
|
const selectionView = this.selectionView;
|
||||||
if (selectionView) {
|
if (selectionView) {
|
||||||
const selection = selectionView.selection;
|
const selection = selectionView.selection;
|
||||||
if (selection && this.isSelected(selection) && editing) {
|
const shouldEnterEditMode =
|
||||||
|
editing && this.cell?.beforeEnterEditMode() !== false;
|
||||||
|
if (selection && this.isSelected(selection) && shouldEnterEditMode) {
|
||||||
selectionView.selection = TableViewAreaSelection.create({
|
selectionView.selection = TableViewAreaSelection.create({
|
||||||
groupKey: this.groupKey,
|
groupKey: this.groupKey,
|
||||||
focus: {
|
focus: {
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ export class TableViewCellContainer extends SignalWatcher(
|
|||||||
const selectionView = this.selectionController;
|
const selectionView = this.selectionController;
|
||||||
if (selectionView) {
|
if (selectionView) {
|
||||||
const selection = selectionView.selection;
|
const selection = selectionView.selection;
|
||||||
if (selection && this.isSelected(selection) && editing) {
|
const shouldEnterEditMode =
|
||||||
|
editing && this.cell?.beforeEnterEditMode() !== false;
|
||||||
|
if (selection && this.isSelected(selection) && shouldEnterEditMode) {
|
||||||
selectionView.selection = TableViewAreaSelection.create({
|
selectionView.selection = TableViewAreaSelection.create({
|
||||||
groupKey: this.groupKey,
|
groupKey: this.groupKey,
|
||||||
focus: {
|
focus: {
|
||||||
|
|||||||
@@ -26,6 +26,52 @@ import type { ViewManager } from '../../core/view-manager/view-manager.js';
|
|||||||
import { DEFAULT_COLUMN_MIN_WIDTH, DEFAULT_COLUMN_WIDTH } from './consts.js';
|
import { DEFAULT_COLUMN_MIN_WIDTH, DEFAULT_COLUMN_WIDTH } from './consts.js';
|
||||||
import type { TableViewData } from './define.js';
|
import type { TableViewData } from './define.js';
|
||||||
|
|
||||||
|
export const materializeColumnsByPropertyIds = (
|
||||||
|
columns: TableColumnData[],
|
||||||
|
propertyIds: string[],
|
||||||
|
getDefaultWidth: (id: string) => number = () => DEFAULT_COLUMN_WIDTH
|
||||||
|
) => {
|
||||||
|
const needShow = new Set(propertyIds);
|
||||||
|
const orderedColumns: TableColumnData[] = [];
|
||||||
|
|
||||||
|
for (const column of columns) {
|
||||||
|
if (needShow.has(column.id)) {
|
||||||
|
orderedColumns.push(column);
|
||||||
|
needShow.delete(column.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const id of needShow) {
|
||||||
|
orderedColumns.push({ id, width: getDefaultWidth(id), hide: undefined });
|
||||||
|
}
|
||||||
|
|
||||||
|
return orderedColumns;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const materializeTableColumns = (
|
||||||
|
columns: TableColumnData[],
|
||||||
|
propertyIds: string[],
|
||||||
|
getDefaultWidth?: (id: string) => number
|
||||||
|
) => {
|
||||||
|
const nextColumns = materializeColumnsByPropertyIds(
|
||||||
|
columns,
|
||||||
|
propertyIds,
|
||||||
|
getDefaultWidth
|
||||||
|
);
|
||||||
|
const unchanged =
|
||||||
|
columns.length === nextColumns.length &&
|
||||||
|
columns.every((column, index) => {
|
||||||
|
const nextColumn = nextColumns[index];
|
||||||
|
return (
|
||||||
|
nextColumn != null &&
|
||||||
|
column.id === nextColumn.id &&
|
||||||
|
column.hide === nextColumn.hide
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return unchanged ? columns : nextColumns;
|
||||||
|
};
|
||||||
|
|
||||||
export class TableSingleView extends SingleViewBase<TableViewData> {
|
export class TableSingleView extends SingleViewBase<TableViewData> {
|
||||||
propertiesRaw$ = computed(() => {
|
propertiesRaw$ = computed(() => {
|
||||||
const needShow = new Set(this.dataSource.properties$.value);
|
const needShow = new Set(this.dataSource.properties$.value);
|
||||||
@@ -220,14 +266,10 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
|||||||
return this.data$.value?.mode ?? 'table';
|
return this.data$.value?.mode ?? 'table';
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(viewManager: ViewManager, viewId: string) {
|
|
||||||
super(viewManager, viewId);
|
|
||||||
}
|
|
||||||
|
|
||||||
isShow(rowId: string): boolean {
|
isShow(rowId: string): boolean {
|
||||||
if (this.filter$.value?.conditions.length) {
|
if (this.filter$.value?.conditions.length) {
|
||||||
const rowMap = Object.fromEntries(
|
const rowMap = Object.fromEntries(
|
||||||
this.properties$.value.map(column => [
|
this.propertiesRaw$.value.map(column => [
|
||||||
column.id,
|
column.id,
|
||||||
column.cellGetOrCreate(rowId).jsonValue$.value,
|
column.cellGetOrCreate(rowId).jsonValue$.value,
|
||||||
])
|
])
|
||||||
@@ -290,6 +332,33 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private materializeColumns() {
|
||||||
|
const data = this.data$.value;
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextColumns = materializeTableColumns(
|
||||||
|
data.columns,
|
||||||
|
this.dataSource.properties$.value,
|
||||||
|
id => this.propertyGetOrCreate(id).width$.value
|
||||||
|
);
|
||||||
|
if (nextColumns === data.columns) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dataUpdate(() => ({ columns: nextColumns }));
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(viewManager: ViewManager, viewId: string) {
|
||||||
|
super(viewManager, viewId);
|
||||||
|
// Materialize view columns on view activation so newly added properties
|
||||||
|
// can participate in hide/order operations in table.
|
||||||
|
queueMicrotask(() => {
|
||||||
|
this.materializeColumns();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type TableColumnData = TableViewData['columns'][number];
|
type TableColumnData = TableViewData['columns'][number];
|
||||||
|
|||||||
@@ -26,5 +26,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,5 +42,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,5 +35,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
"@preact/signals-core": "^1.8.0",
|
"@preact/signals-core": "^1.8.0",
|
||||||
"@toeverything/theme": "^1.1.23",
|
"@toeverything/theme": "^1.1.23",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
@@ -40,5 +39,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"lodash-es": "^4.17.23",
|
"lodash-es": "^4.17.23",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
@@ -42,5 +41,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
"@toeverything/theme": "^1.1.23",
|
"@toeverything/theme": "^1.1.23",
|
||||||
"@vanilla-extract/css": "^1.17.0",
|
"@vanilla-extract/css": "^1.17.0",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
@@ -41,5 +40,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"lodash-es": "^4.17.23",
|
"lodash-es": "^4.17.23",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"yjs": "^13.6.27",
|
"yjs": "^13.6.27",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
@@ -43,5 +42,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"lodash-es": "^4.17.23",
|
"lodash-es": "^4.17.23",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"yjs": "^13.6.27",
|
"yjs": "^13.6.27",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
@@ -44,5 +43,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
"fractional-indexing": "^3.2.0",
|
"fractional-indexing": "^3.2.0",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"lodash-es": "^4.17.23",
|
"lodash-es": "^4.17.23",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"yjs": "^13.6.27",
|
"yjs": "^13.6.27",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
@@ -48,5 +47,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"lodash-es": "^4.17.23",
|
"lodash-es": "^4.17.23",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"yjs": "^13.6.27",
|
"yjs": "^13.6.27",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
@@ -45,5 +44,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,6 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"lodash-es": "^4.17.23",
|
"lodash-es": "^4.17.23",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"simple-xml-to-json": "^1.2.2",
|
"simple-xml-to-json": "^1.2.2",
|
||||||
"yjs": "^13.6.27",
|
"yjs": "^13.6.27",
|
||||||
@@ -51,5 +50,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"lodash-es": "^4.17.23",
|
"lodash-es": "^4.17.23",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"yjs": "^13.6.27",
|
"yjs": "^13.6.27",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
@@ -45,5 +44,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"lodash-es": "^4.17.23",
|
"lodash-es": "^4.17.23",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"yjs": "^13.6.27",
|
"yjs": "^13.6.27",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
@@ -45,5 +44,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"lodash-es": "^4.17.23",
|
"lodash-es": "^4.17.23",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"yjs": "^13.6.27",
|
"yjs": "^13.6.27",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
@@ -44,5 +43,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"lodash-es": "^4.17.23",
|
"lodash-es": "^4.17.23",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"yjs": "^13.6.27",
|
"yjs": "^13.6.27",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
@@ -44,5 +43,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"lodash-es": "^4.17.23",
|
"lodash-es": "^4.17.23",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"yjs": "^13.6.27",
|
"yjs": "^13.6.27",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
@@ -43,5 +42,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,5 +25,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,5 +42,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,5 +47,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,5 +50,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,5 +44,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,5 +42,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,5 +56,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,5 +43,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,5 +30,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,5 +41,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,6 @@
|
|||||||
"micromark-extension-gfm-table": "^2.1.0",
|
"micromark-extension-gfm-table": "^2.1.0",
|
||||||
"micromark-extension-gfm-task-list-item": "^2.1.0",
|
"micromark-extension-gfm-task-list-item": "^2.1.0",
|
||||||
"micromark-util-combine-extensions": "^2.0.0",
|
"micromark-util-combine-extensions": "^2.0.0",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"pdfmake": "^0.2.20",
|
"pdfmake": "^0.2.20",
|
||||||
"quick-lru": "^7.3.0",
|
"quick-lru": "^7.3.0",
|
||||||
"rehype-parse": "^9.0.0",
|
"rehype-parse": "^9.0.0",
|
||||||
@@ -77,5 +76,5 @@
|
|||||||
"@types/pdfmake": "^0.2.12",
|
"@types/pdfmake": "^0.2.12",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,24 @@ const imageProxyMiddlewareBuilder = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const IMAGE_PROXY_PATH = '/api/worker/image-proxy';
|
||||||
|
|
||||||
|
export const isImageProxyURL = (imageUrl: string) => {
|
||||||
|
try {
|
||||||
|
const url = new URL(imageUrl, globalThis.location.origin);
|
||||||
|
return url.pathname === IMAGE_PROXY_PATH && url.searchParams.has('url');
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const defaultImageProxyMiddlewarBuilder = imageProxyMiddlewareBuilder();
|
const defaultImageProxyMiddlewarBuilder = imageProxyMiddlewareBuilder();
|
||||||
|
|
||||||
export const setImageProxyMiddlewareURL = defaultImageProxyMiddlewarBuilder.set;
|
export const setImageProxyMiddlewareURL = defaultImageProxyMiddlewarBuilder.set;
|
||||||
|
|
||||||
export const defaultImageProxyMiddleware =
|
export const defaultImageProxyMiddleware: TransformerMiddleware = args => {
|
||||||
defaultImageProxyMiddlewarBuilder.get();
|
return defaultImageProxyMiddlewarBuilder.get()(args);
|
||||||
|
};
|
||||||
|
|
||||||
// TODO(@mirone): this should be configured when setup instead of runtime
|
// TODO(@mirone): this should be configured when setup instead of runtime
|
||||||
export class ImageProxyService extends StoreExtension {
|
export class ImageProxyService extends StoreExtension {
|
||||||
@@ -40,7 +52,7 @@ export class ImageProxyService extends StoreExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buildUrl(imageUrl: string) {
|
buildUrl(imageUrl: string) {
|
||||||
if (imageUrl.startsWith(this.imageProxyURL)) {
|
if (imageUrl.startsWith(this.imageProxyURL) || isImageProxyURL(imageUrl)) {
|
||||||
return imageUrl;
|
return imageUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { css, unsafeCSS } from 'lit';
|
import { css, unsafeCSS } from 'lit';
|
||||||
|
|
||||||
|
import { unsafeCSSVarV2 } from '../theme/css-variables';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* You should add a container before the scrollbar style to prevent the style pollution of the whole doc.
|
* You should add a container before the scrollbar style to prevent the style pollution of the whole doc.
|
||||||
*/
|
*/
|
||||||
@@ -28,7 +30,7 @@ export const scrollbarStyle = (container: string) => {
|
|||||||
}
|
}
|
||||||
${unsafeCSS(container)}::-webkit-scrollbar-thumb {
|
${unsafeCSS(container)}::-webkit-scrollbar-thumb {
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
background-color: #b1b1b1;
|
background-color: ${unsafeCSSVarV2('icon/secondary', '#b1b1b1')};
|
||||||
}
|
}
|
||||||
${unsafeCSS(container)}::-webkit-scrollbar-corner {
|
${unsafeCSS(container)}::-webkit-scrollbar-corner {
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"lit": "^3.2.0",
|
"lit": "^3.2.0",
|
||||||
"lodash-es": "^4.17.23",
|
"lodash-es": "^4.17.23",
|
||||||
"minimatch": "^10.1.1",
|
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
@@ -45,5 +44,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,5 +34,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,5 +36,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,5 +40,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,5 +38,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,5 +36,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,5 +34,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,5 +55,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,5 +43,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,9 +220,7 @@ export class ImportDoc extends WithDisposable(LitElement) {
|
|||||||
</header>
|
</header>
|
||||||
<div>
|
<div>
|
||||||
AFFiNE will gradually support more file formats for import.
|
AFFiNE will gradually support more file formats for import.
|
||||||
<a
|
<a href="https://affine.pro/redirect/discord" target="_blank"
|
||||||
href="https://community.affine.pro/c/feature-requests/import-export"
|
|
||||||
target="_blank"
|
|
||||||
>Provide feedback.</a
|
>Provide feedback.</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -37,5 +37,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,5 +37,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,5 +35,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,5 +30,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,5 +36,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,5 +38,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
} from '@blocksuite/affine-shared/services';
|
} from '@blocksuite/affine-shared/services';
|
||||||
import { unsafeCSSVar, unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
import { unsafeCSSVar, unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||||
import { matchModels } from '@blocksuite/affine-shared/utils';
|
import { matchModels } from '@blocksuite/affine-shared/utils';
|
||||||
|
import { IS_MOBILE } from '@blocksuite/global/env';
|
||||||
import {
|
import {
|
||||||
Bound,
|
Bound,
|
||||||
getCommonBound,
|
getCommonBound,
|
||||||
@@ -109,6 +110,17 @@ export class AffineToolbarWidget extends WidgetComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editor-toolbar[data-mobile='true'] {
|
||||||
|
position: fixed;
|
||||||
|
top: auto;
|
||||||
|
left: 50%;
|
||||||
|
bottom: 16px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
max-width: calc(100vw - 32px);
|
||||||
|
overflow-x: auto;
|
||||||
|
touch-action: pan-x;
|
||||||
|
}
|
||||||
|
|
||||||
${unsafeCSS(darkToolbarStyles('editor-toolbar'))}
|
${unsafeCSS(darkToolbarStyles('editor-toolbar'))}
|
||||||
${unsafeCSS(lightToolbarStyles('editor-toolbar'))}
|
${unsafeCSS(lightToolbarStyles('editor-toolbar'))}
|
||||||
`;
|
`;
|
||||||
@@ -268,10 +280,111 @@ export class AffineToolbarWidget extends WidgetComponent {
|
|||||||
const { flags, flavour$, message$, placement$ } = toolbarRegistry;
|
const { flags, flavour$, message$, placement$ } = toolbarRegistry;
|
||||||
const context = new ToolbarContext(std);
|
const context = new ToolbarContext(std);
|
||||||
|
|
||||||
// TODO(@fundon): fix toolbar position shaking when the wheel scrolls
|
const isNativeTextSelection = () => {
|
||||||
// document.body.append(toolbar);
|
const dbSel = std.selection.find(DatabaseSelection);
|
||||||
|
const dbViewSel = dbSel?.viewSelection;
|
||||||
|
if (
|
||||||
|
dbViewSel &&
|
||||||
|
((dbViewSel.selectionType === 'area' && dbViewSel.isEditing) ||
|
||||||
|
(dbViewSel.selectionType === 'cell' && dbViewSel.isEditing))
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tableViewSelection = std.selection.find(TableSelection)?.data;
|
||||||
|
return tableViewSelection?.type === 'area';
|
||||||
|
};
|
||||||
|
|
||||||
|
let updateMobilePosition: (() => void) | null = null;
|
||||||
|
|
||||||
|
if (IS_MOBILE) {
|
||||||
|
toolbar.dataset.mobile = 'true';
|
||||||
this.shadowRoot!.append(toolbar);
|
this.shadowRoot!.append(toolbar);
|
||||||
|
|
||||||
|
// Position toolbar above virtual keyboard using Visual Viewport API
|
||||||
|
updateMobilePosition = () => {
|
||||||
|
const vv = window.visualViewport;
|
||||||
|
if (!vv) return;
|
||||||
|
const keyboardHeight = window.innerHeight - vv.height - vv.offsetTop;
|
||||||
|
toolbar.style.bottom = `${Math.max(16, keyboardHeight + 16)}px`;
|
||||||
|
};
|
||||||
|
if (window.visualViewport) {
|
||||||
|
disposables.addFromEvent(
|
||||||
|
window.visualViewport,
|
||||||
|
'resize',
|
||||||
|
updateMobilePosition
|
||||||
|
);
|
||||||
|
disposables.addFromEvent(
|
||||||
|
window.visualViewport,
|
||||||
|
'scroll',
|
||||||
|
updateMobilePosition
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep mobile selection in sync with toolbar flags. On some mobile browsers,
|
||||||
|
// long-press selection may skip the std selection stream intermittently.
|
||||||
|
const syncMobileTextSelection = () => {
|
||||||
|
if (!context.activated) {
|
||||||
|
flags.toggle(Flag.Text, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isNativeTextSelection()) {
|
||||||
|
flags.toggle(Flag.Text, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selection = window.getSelection();
|
||||||
|
const hasSelection =
|
||||||
|
selection &&
|
||||||
|
selection.rangeCount > 0 &&
|
||||||
|
!selection.isCollapsed &&
|
||||||
|
selection.toString().length > 0;
|
||||||
|
const range = hasSelection ? selection.getRangeAt(0) : null;
|
||||||
|
const inEditor = Boolean(
|
||||||
|
range && host.contains(range.commonAncestorContainer)
|
||||||
|
);
|
||||||
|
|
||||||
|
batch(() => {
|
||||||
|
flags.toggle(Flag.Text, inEditor);
|
||||||
|
|
||||||
|
if (!inEditor || !range) return;
|
||||||
|
|
||||||
|
this.setReferenceElementWithRange(range);
|
||||||
|
|
||||||
|
sideOptions$.value = null;
|
||||||
|
flavour$.value = 'affine:note';
|
||||||
|
placement$.value = toolbarRegistry.getModulePlacement('affine:note');
|
||||||
|
flags.refresh(Flag.Text);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let selectionTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
let touchTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
const scheduleSyncMobileTextSelection = (delay: number) => {
|
||||||
|
if (selectionTimeout) clearTimeout(selectionTimeout);
|
||||||
|
selectionTimeout = setTimeout(syncMobileTextSelection, delay);
|
||||||
|
};
|
||||||
|
const scheduleTouchSync = (delay: number) => {
|
||||||
|
if (touchTimeout) clearTimeout(touchTimeout);
|
||||||
|
touchTimeout = setTimeout(syncMobileTextSelection, delay);
|
||||||
|
};
|
||||||
|
disposables.addFromEvent(document, 'selectionchange', () => {
|
||||||
|
scheduleSyncMobileTextSelection(50);
|
||||||
|
});
|
||||||
|
disposables.addFromEvent(host, 'touchend', () => {
|
||||||
|
scheduleTouchSync(100);
|
||||||
|
});
|
||||||
|
disposables.add(() => {
|
||||||
|
if (selectionTimeout) clearTimeout(selectionTimeout);
|
||||||
|
if (touchTimeout) clearTimeout(touchTimeout);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ensures a stable initial offset before the first viewport event arrives.
|
||||||
|
updateMobilePosition?.();
|
||||||
|
} else {
|
||||||
|
this.shadowRoot!.append(toolbar);
|
||||||
|
}
|
||||||
|
|
||||||
// Formatting
|
// Formatting
|
||||||
// Selects text in note.
|
// Selects text in note.
|
||||||
disposables.add(
|
disposables.add(
|
||||||
@@ -305,30 +418,12 @@ export class AffineToolbarWidget extends WidgetComponent {
|
|||||||
disposables.addFromEvent(document, 'selectionchange', () => {
|
disposables.addFromEvent(document, 'selectionchange', () => {
|
||||||
const range = std.range.value ?? null;
|
const range = std.range.value ?? null;
|
||||||
let activated = context.activated && Boolean(range && !range.collapsed);
|
let activated = context.activated && Boolean(range && !range.collapsed);
|
||||||
let isNative = false;
|
|
||||||
|
|
||||||
if (activated) {
|
if (activated) {
|
||||||
const result = std.selection.find(DatabaseSelection);
|
activated = isNativeTextSelection();
|
||||||
const viewSelection = result?.viewSelection;
|
|
||||||
if (viewSelection) {
|
|
||||||
isNative =
|
|
||||||
(viewSelection.selectionType === 'area' &&
|
|
||||||
viewSelection.isEditing) ||
|
|
||||||
(viewSelection.selectionType === 'cell' && viewSelection.isEditing);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isNative) {
|
|
||||||
const result = std.selection.find(TableSelection);
|
|
||||||
const viewSelection = result?.data;
|
|
||||||
if (viewSelection) {
|
|
||||||
isNative = viewSelection.type === 'area';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
batch(() => {
|
batch(() => {
|
||||||
activated &&= isNative;
|
|
||||||
|
|
||||||
// Focues outside: `doc-title`
|
// Focues outside: `doc-title`
|
||||||
if (
|
if (
|
||||||
flags.check(Flag.Text) &&
|
flags.check(Flag.Text) &&
|
||||||
@@ -662,6 +757,14 @@ export class AffineToolbarWidget extends WidgetComponent {
|
|||||||
|
|
||||||
disposables.add(
|
disposables.add(
|
||||||
effect(() => {
|
effect(() => {
|
||||||
|
if (IS_MOBILE) {
|
||||||
|
const value = flags.value$.value;
|
||||||
|
if (!context.activated) return;
|
||||||
|
if (Flag.None === value || flags.contains(Flag.Hiding, value)) return;
|
||||||
|
updateMobilePosition?.();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!abortController.signal.aborted) {
|
if (!abortController.signal.aborted) {
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,5 +35,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,5 +17,5 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@blocksuite/affine": "workspace:*"
|
"@blocksuite/affine": "workspace:*"
|
||||||
},
|
},
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,5 +64,5 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,5 +47,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
119
blocksuite/framework/std/src/__tests__/keymap.unit.spec.ts
Normal file
119
blocksuite/framework/std/src/__tests__/keymap.unit.spec.ts
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import { describe, expect, test } from 'vitest';
|
||||||
|
|
||||||
|
import { bindKeymap } from '../event/keymap.js';
|
||||||
|
|
||||||
|
const createKeyboardEvent = (options: {
|
||||||
|
key: string;
|
||||||
|
keyCode: number;
|
||||||
|
altKey?: boolean;
|
||||||
|
ctrlKey?: boolean;
|
||||||
|
metaKey?: boolean;
|
||||||
|
shiftKey?: boolean;
|
||||||
|
}): KeyboardEvent => {
|
||||||
|
const event = new KeyboardEvent('keydown', {
|
||||||
|
key: options.key,
|
||||||
|
altKey: options.altKey ?? false,
|
||||||
|
ctrlKey: options.ctrlKey ?? false,
|
||||||
|
metaKey: options.metaKey ?? false,
|
||||||
|
shiftKey: options.shiftKey ?? false,
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(event, 'keyCode', {
|
||||||
|
configurable: true,
|
||||||
|
get: () => options.keyCode,
|
||||||
|
});
|
||||||
|
Object.defineProperty(event, 'which', {
|
||||||
|
configurable: true,
|
||||||
|
get: () => options.keyCode,
|
||||||
|
});
|
||||||
|
|
||||||
|
return event;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createCtx = (event: KeyboardEvent) => {
|
||||||
|
return {
|
||||||
|
get(name: string) {
|
||||||
|
if (name === 'keyboardState') {
|
||||||
|
return { raw: event };
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('bindKeymap', () => {
|
||||||
|
test('falls back to physical key for ctrl shortcuts on non-US layouts', () => {
|
||||||
|
let handled = false;
|
||||||
|
const handler = bindKeymap({
|
||||||
|
'Ctrl-f': () => {
|
||||||
|
handled = true;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const event = createKeyboardEvent({
|
||||||
|
key: 'а',
|
||||||
|
keyCode: 70,
|
||||||
|
ctrlKey: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(handler(createCtx(event))).toBe(true);
|
||||||
|
expect(handled).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not fallback for Alt+locale-character letter input', () => {
|
||||||
|
let handled = false;
|
||||||
|
const handler = bindKeymap({
|
||||||
|
'Alt-s': () => {
|
||||||
|
handled = true;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const event = createKeyboardEvent({
|
||||||
|
key: 'ś',
|
||||||
|
keyCode: 83,
|
||||||
|
altKey: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(handler(createCtx(event))).toBe(false);
|
||||||
|
expect(handled).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('keeps Alt+digit fallback for non-ASCII key outputs', () => {
|
||||||
|
let handled = false;
|
||||||
|
const handler = bindKeymap({
|
||||||
|
'Alt-0': () => {
|
||||||
|
handled = true;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const event = createKeyboardEvent({
|
||||||
|
key: 'º',
|
||||||
|
keyCode: 48,
|
||||||
|
altKey: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(handler(createCtx(event))).toBe(true);
|
||||||
|
expect(handled).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not fallback on non-ASCII input without modifiers', () => {
|
||||||
|
let handled = false;
|
||||||
|
const handler = bindKeymap({
|
||||||
|
'[': () => {
|
||||||
|
handled = true;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const event = createKeyboardEvent({
|
||||||
|
key: 'х',
|
||||||
|
keyCode: 219,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(handler(createCtx(event))).toBe(false);
|
||||||
|
expect(handled).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -90,9 +90,21 @@ export function bindKeymap(
|
|||||||
// Do NOT fallback when the key produces a non-ASCII character (e.g., Cyrillic 'х' on Russian keyboard),
|
// Do NOT fallback when the key produces a non-ASCII character (e.g., Cyrillic 'х' on Russian keyboard),
|
||||||
// because the user intends to type that character, not trigger a shortcut bound to the physical key.
|
// because the user intends to type that character, not trigger a shortcut bound to the physical key.
|
||||||
// See: https://github.com/toeverything/AFFiNE/issues/14059
|
// See: https://github.com/toeverything/AFFiNE/issues/14059
|
||||||
const hasModifier = event.shiftKey || event.altKey || event.metaKey;
|
const hasModifier =
|
||||||
|
event.shiftKey || event.altKey || event.ctrlKey || event.metaKey;
|
||||||
const baseName = base[event.keyCode];
|
const baseName = base[event.keyCode];
|
||||||
if (hasModifier && baseName && baseName !== name) {
|
const isSingleAscii = name.length === 1 && name.charCodeAt(0) <= 0x7e;
|
||||||
|
const isAltInputChar = event.altKey && !event.ctrlKey && !isSingleAscii;
|
||||||
|
// Keep supporting existing Alt+digit shortcuts (e.g. Alt-0/1/2 in edgeless)
|
||||||
|
// while preventing Alt-based locale input characters from triggering letter shortcuts.
|
||||||
|
const isDigitBaseKey =
|
||||||
|
baseName != null && baseName.length === 1 && /[0-9]/.test(baseName);
|
||||||
|
if (
|
||||||
|
hasModifier &&
|
||||||
|
baseName &&
|
||||||
|
baseName !== name &&
|
||||||
|
!(isAltInputChar && !isDigitBaseKey)
|
||||||
|
) {
|
||||||
const fromCode = map[modifiers(baseName, event)];
|
const fromCode = map[modifiers(baseName, event)];
|
||||||
if (fromCode && fromCode(ctx)) {
|
if (fromCode && fromCode(ctx)) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"lodash.ismatch": "^4.4.0",
|
"lodash.ismatch": "^4.4.0",
|
||||||
"lodash.merge": "^4.6.2",
|
"lodash.merge": "^4.6.2",
|
||||||
"minimatch": "^10.1.1",
|
"minimatch": "^10.2.2",
|
||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"y-protocols": "^1.0.6",
|
"y-protocols": "^1.0.6",
|
||||||
@@ -42,5 +42,5 @@
|
|||||||
"!dist/__tests__",
|
"!dist/__tests__",
|
||||||
"shim.d.ts"
|
"shim.d.ts"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,5 +33,5 @@
|
|||||||
"!src/__tests__",
|
"!src/__tests__",
|
||||||
"!dist/__tests__"
|
"!dist/__tests__"
|
||||||
],
|
],
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,5 +46,5 @@
|
|||||||
"vite-plugin-wasm": "^3.5.0",
|
"vite-plugin-wasm": "^3.5.0",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"version": "0.26.1"
|
"version": "0.26.3"
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user