Compare commits

..

2 Commits

Author SHA1 Message Date
DarkSky
2e26c9fe69 feat: client size improve 2026-02-23 01:19:15 +08:00
DarkSky
d385514fca feat: init native mermaid & typst integrate 2026-02-22 20:41:17 +08:00
250 changed files with 4960 additions and 4965 deletions

View File

@@ -3,6 +3,6 @@ contact_links:
- name: Something else?
url: https://github.com/toeverything/AFFiNE/discussions
about: Feel free to ask and answer questions over in GitHub Discussions
- name: AFFiNE Community Support (Discord)
url: https://affine.pro/redirect/discord
- name: AFFiNE Community Support
url: https://community.affine.pro
about: AFFiNE Community - a place to ask, learn and engage with others

View File

@@ -1,20 +0,0 @@
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: []

View File

@@ -7,6 +7,7 @@ inputs:
ios-app-version:
description: 'iOS App Store Version (Optional, use App version if empty)'
required: false
type: string
runs:
using: 'composite'
steps:

View File

@@ -3,4 +3,4 @@ name: affine
description: AFFiNE cloud chart
type: application
version: 0.0.0
appVersion: "0.26.3"
appVersion: "0.26.1"

View File

@@ -3,7 +3,7 @@ name: doc
description: AFFiNE doc server
type: application
version: 0.0.0
appVersion: "0.26.3"
appVersion: "0.26.1"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -3,7 +3,7 @@ name: front
description: AFFiNE front server
type: application
version: 0.0.0
appVersion: "0.26.3"
appVersion: "0.26.1"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -96,20 +96,12 @@ spec:
httpGet:
path: /info
port: http
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 }}
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
readinessProbe:
httpGet:
path: /info
port: http
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 }}
initialDelaySeconds: {{ .Values.probe.initialDelaySeconds }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}

View File

@@ -31,21 +31,13 @@ podSecurityContext:
resources:
limits:
cpu: '1'
memory: 4Gi
memory: 2Gi
requests:
cpu: '1'
memory: 2Gi
probe:
initialDelaySeconds: 20
timeoutSeconds: 5
periodSeconds: 10
failureThreshold: 6
successThreshold: 1
liveness:
initialDelaySeconds: 60
failureThreshold: 12
readiness: {}
services:
sync:

View File

@@ -3,7 +3,7 @@ name: graphql
description: AFFiNE GraphQL server
type: application
version: 0.0.0
appVersion: "0.26.3"
appVersion: "0.26.1"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -68,26 +68,9 @@ jobs:
runs-on: ubuntu-24.04-arm
steps:
- 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
# oxlint is fast, so wrong code will fail quickly
run: |
set -euo pipefail
oxlint_version="$(node -e "console.log(require('./package.json').devDependencies.oxlint)")"
yarn dlx "oxlint@${oxlint_version}" --deny-warnings
run: yarn dlx $(node -e "console.log(require('./package.json').scripts['lint:ox'].replace('oxlint', 'oxlint@' + require('./package.json').devDependencies.oxlint))")
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
@@ -125,45 +108,20 @@ jobs:
run: |
yarn affine bs-docs build
git checkout packages/frontend/i18n/src/i18n-completenesses.json
if git status --porcelain | grep -q .; then
git status --porcelain | grep . && {
echo "Run 'yarn typecheck && yarn affine bs-docs build' and make sure all changes are submitted"
exit 1
else
} || {
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:
name: Lint Rust
if: ${{ needs.rust-test-filter.outputs.run-rust == 'true' }}
runs-on: ubuntu-latest
needs:
- rust-test-filter
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/build-rust
with:
target: x86_64-unknown-linux-gnu
package: 'affine'
no-build: 'true'
- name: fmt check
run: |
@@ -201,12 +159,12 @@ jobs:
yarn affine i18n build
yarn affine server genconfig
git checkout packages/frontend/i18n/src/i18n-completenesses.json
if git status --porcelain | grep -q .; then
git status --porcelain | grep . && {
echo "Run 'yarn affine init && yarn affine gql build && yarn affine i18n build && yarn affine server genconfig' and make sure all changes are submitted"
exit 1
else
} || {
echo "All changes are submitted"
fi
}
check-yarn-binary:
name: Check yarn binary
@@ -215,9 +173,7 @@ jobs:
- uses: actions/checkout@v4
- name: Run check
run: |
set -euo pipefail
yarn_version="$(node -e "console.log(require('./package.json').packageManager.split('@')[1])")"
yarn set version "$yarn_version"
yarn set version $(node -e "console.log(require('./package.json').packageManager.split('@')[1])")
git diff --exit-code
e2e-blocksuite-test:
@@ -232,7 +188,6 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine/monorepo @affine-test/blocksuite @blocksuite/playground @blocksuite/integration-test
playwright-install: true
playwright-platform: 'chromium'
electron-install: false
@@ -260,7 +215,6 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine/monorepo @affine-test/blocksuite @blocksuite/playground @blocksuite/integration-test
playwright-install: true
playwright-platform: 'chromium,firefox,webkit'
electron-install: false
@@ -344,7 +298,6 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine/monorepo @affine-test/affine-local @affine/web @affine/server
playwright-install: true
playwright-platform: 'chromium'
electron-install: false
@@ -376,7 +329,6 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine/monorepo @affine-test/affine-mobile @affine/mobile
playwright-install: true
electron-install: false
full-cache: true
@@ -448,7 +400,7 @@ jobs:
working-directory: ${{ github.workspace }}
shell: bash
run: |
PLATFORM_ARCH_ABI="$(node -e "console.log(require('@napi-rs/cli').parseTriple('x86_64-unknown-linux-gnu').platformArchABI)")"
export 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"
- name: Build AFFiNE native
uses: ./.github/actions/build-rust
@@ -487,7 +439,7 @@ jobs:
working-directory: ${{ github.workspace }}
shell: bash
run: |
PLATFORM_ARCH_ABI="$(node -e "console.log(require('@napi-rs/cli').parseTriple('${{ matrix.spec.target }}').platformArchABI)")"
export 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"
- name: Build AFFiNE native
uses: ./.github/actions/build-rust
@@ -536,7 +488,7 @@ jobs:
working-directory: ${{ env.DEV_DRIVE_WORKSPACE }}
shell: bash
run: |
PLATFORM_ARCH_ABI="$(node -e "console.log(require('@napi-rs/cli').parseTriple('${{ matrix.spec.target }}').platformArchABI)")"
export 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"
- name: Build AFFiNE native
uses: ./.github/actions/build-rust
@@ -584,7 +536,6 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine/monorepo @affine/electron-renderer @affine/nbstore @toeverything/infra
electron-install: false
full-cache: true
- name: Build Electron renderer
@@ -666,7 +617,6 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine/monorepo @affine/server
electron-install: false
full-cache: true
@@ -747,7 +697,6 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine/monorepo @affine/server
electron-install: false
full-cache: true
@@ -764,6 +713,8 @@ jobs:
run: yarn affine @affine/server test:coverage "**/*/*elasticsearch.spec.ts" --forbid-only
env:
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
CI_NODE_INDEX: ${{ matrix.node_index }}
CI_NODE_TOTAL: ${{ matrix.total_nodes }}
- name: Upload server test coverage results
uses: codecov/codecov-action@v5
@@ -810,7 +761,6 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine/monorepo @affine/server
electron-install: false
full-cache: true
@@ -837,10 +787,7 @@ jobs:
miri:
name: miri code check
if: ${{ needs.rust-test-filter.outputs.run-rust == 'true' }}
runs-on: ubuntu-latest
needs:
- rust-test-filter
env:
RUST_BACKTRACE: full
CARGO_TERM_COLOR: always
@@ -865,10 +812,7 @@ jobs:
loom:
name: loom thread test
if: ${{ needs.rust-test-filter.outputs.run-rust == 'true' }}
runs-on: ubuntu-latest
needs:
- rust-test-filter
env:
RUSTFLAGS: --cfg loom
RUST_BACKTRACE: full
@@ -891,10 +835,7 @@ jobs:
fuzzing:
name: fuzzing
if: ${{ needs.rust-test-filter.outputs.run-rust == 'true' }}
runs-on: ubuntu-latest
needs:
- rust-test-filter
env:
CARGO_TERM_COLOR: always
steps:
@@ -930,10 +871,7 @@ jobs:
rust-test:
name: Run native tests
if: ${{ needs.rust-test-filter.outputs.run-rust == 'true' }}
runs-on: ubuntu-latest
needs:
- rust-test-filter
env:
CARGO_TERM_COLOR: always
steps:
@@ -941,7 +879,6 @@ jobs:
- name: Setup Rust
uses: ./.github/actions/build-rust
with:
target: x86_64-unknown-linux-gnu
package: 'affine'
no-build: 'true'
@@ -1034,7 +971,6 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine/monorepo @affine/server
electron-install: false
full-cache: true
@@ -1107,7 +1043,6 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine/monorepo @affine-test/affine-cloud-copilot @affine/web @affine/server
playwright-install: true
playwright-platform: 'chromium'
electron-install: false
@@ -1190,10 +1125,7 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node
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-platform: 'chromium'
electron-install: ${{ matrix.tests.shard == 'desktop' && 'true' || 'false' }}
hard-link-nm: false
- name: Download server-native.node
@@ -1272,8 +1204,7 @@ jobs:
timeout-minutes: 10
with:
extra-flags: workspaces focus @affine/electron @affine/monorepo @affine-test/affine-desktop @affine/nbstore @toeverything/infra
playwright-install: ${{ matrix.spec.test && 'true' || 'false' }}
playwright-platform: 'chromium'
playwright-install: true
hard-link-nm: false
enableScripts: false
@@ -1281,7 +1212,7 @@ jobs:
id: filename
shell: bash
run: |
PLATFORM_ARCH_ABI="$(node -e "console.log(require('@napi-rs/cli').parseTriple('${{ matrix.spec.target }}').platformArchABI)")"
export 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"
- name: Download ${{ steps.filename.outputs.filename }}
@@ -1388,7 +1319,6 @@ jobs:
- server-test
- server-e2e-test
- rust-test
- rust-test-filter
- copilot-test-filter
- copilot-api-test
- copilot-e2e-test

View File

@@ -174,7 +174,7 @@ jobs:
run: |
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/AppImage/${{ inputs.arch }}/*.AppImage ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.appimage
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/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

View File

@@ -128,9 +128,9 @@ jobs:
- name: Testflight
working-directory: packages/frontend/apps/ios/App
run: |
printf '%s' "$BUILD_PROVISION_PROFILE" | base64 --decode -o "$PP_PATH"
mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles"
cp "$PP_PATH" "$HOME/Library/MobileDevice/Provisioning Profiles"
echo -n "${{ env.BUILD_PROVISION_PROFILE }}" | base64 --decode -o $PP_PATH
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
fastlane beta
env:
BUILD_TARGET: distribution
@@ -160,9 +160,7 @@ jobs:
- name: Load Google Service file
env:
DATA: ${{ secrets.FIREBASE_ANDROID_GOOGLE_SERVICE_JSON }}
run: |
set -euo pipefail
printf '%s' "$DATA" | base64 -di > packages/frontend/apps/android/App/app/google-services.json
run: echo $DATA | base64 -di > packages/frontend/apps/android/App/app/google-services.json
- name: Setup Node.js
uses: ./.github/actions/setup-node
timeout-minutes: 10

2030
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -90,10 +90,10 @@ Thanks for checking us out, we appreciate your interest and sincerely hope that
## Contributing
| 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'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 |
| 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) |
| 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 youre made of.
@@ -101,9 +101,11 @@ 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 **translation** and **language support** you can visit our [Discord](https://affine.pro/redirect/discord).
For **translation** and **language support** you can visit our [i18n General Space](https://community.affine.pro/c/i18n-general).
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.
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 [AFFiNE Community](https://community.affine.pro) where you can engage with other like-minded individuals.
## Templates
@@ -180,16 +182,20 @@ Begin with Docker to deploy your own feature-rich, unrestricted version of AFFiN
[![Run on ClawCloud](https://raw.githubusercontent.com/ClawCloud/Run-Template/refs/heads/main/Run-on-ClawCloud.svg)](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
For feature requests, please see [discussions](https://github.com/toeverything/AFFiNE/discussions/categories/ideas).
For feature requests, please see [community.affine.pro](https://community.affine.pro/c/feature-requests/).
## Building
### 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
AFFiNE repo cloned, built, and ready to go).
AFFiNE repo cloned, built, and ready to go.
### Local

View File

@@ -296,7 +296,7 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3",
"version": "0.26.1",
"devDependencies": {
"@vanilla-extract/vite-plugin": "^5.0.0",
"msw": "^2.12.4",

View File

@@ -26,6 +26,7 @@
"@toeverything/theme": "^1.1.23",
"file-type": "^21.0.0",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
@@ -40,5 +41,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -26,6 +26,7 @@
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -44,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -8,10 +8,9 @@ export default defineConfig({
browser: {
enabled: true,
headless: true,
name: 'chromium',
instances: [{ browser: 'chromium' }],
provider: 'playwright',
isolate: false,
providerOptions: {},
},
include: ['src/__tests__/**/*.unit.spec.ts'],
testTimeout: 500,

View File

@@ -30,6 +30,7 @@
"@types/mdast": "^4.0.4",
"emoji-mart": "^5.6.0",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
@@ -44,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -216,13 +216,9 @@ export class CalloutBlockComponent extends CaptionedBlockComponent<CalloutBlockM
override renderBlock() {
const icon = this.model.props.icon$.value;
const backgroundColorName = this.model.props.backgroundColorName$.value;
const normalizedBackgroundName =
backgroundColorName === 'default' || backgroundColorName === ''
? 'grey'
: backgroundColorName;
const backgroundColor = (
cssVarV2.block.callout.background as Record<string, string>
)[normalizedBackgroundName ?? 'grey'];
)[backgroundColorName ?? ''];
const iconContent = getIcon(icon);

View File

@@ -68,14 +68,14 @@ const backgroundColorAction = {
${repeat(colors, color => {
const isDefault = color === 'default';
const value = isDefault
? cssVarV2.block.callout.background.grey
? null
: `var(--affine-text-highlight-${color})`;
const displayName = `${color} Background`;
return html`
<editor-menu-action
data-testid="background-${color}"
@click=${() => updateBackground(isDefault ? 'grey' : color)}
@click=${() => updateBackground(color)}
>
<affine-text-duotone-icon
style=${styleMap({

View File

@@ -31,6 +31,7 @@
"@toeverything/theme": "^1.1.23",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"shiki": "^3.19.0",
"zod": "^3.25.76"
@@ -47,5 +48,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -27,16 +27,6 @@ export const codeBlockStyles = css`
${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 {
font-family: var(--affine-font-code-family);
font-variant-ligatures: none;

View File

@@ -27,6 +27,7 @@
"@toeverything/theme": "^1.1.23",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
@@ -41,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -32,6 +32,7 @@
"@types/mdast": "^4.0.4",
"date-fns": "^4.0.0",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -47,5 +48,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -24,6 +24,7 @@
"@toeverything/theme": "^1.1.23",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
@@ -38,5 +39,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -28,6 +28,7 @@
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
@@ -42,5 +43,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -30,6 +30,7 @@
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -48,5 +49,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -30,6 +30,7 @@
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -48,5 +49,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -28,6 +28,7 @@
"@toeverything/theme": "^1.1.23",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -43,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -28,6 +28,7 @@
"@toeverything/theme": "^1.1.23",
"file-type": "^21.0.0",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
@@ -43,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -30,6 +30,7 @@
"@types/mdast": "^4.0.4",
"katex": "^0.16.27",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"remark-math": "^6.0.0",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
@@ -45,5 +46,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -27,6 +27,7 @@
"@toeverything/theme": "^1.1.23",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
@@ -45,5 +46,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -33,6 +33,7 @@
"@vanilla-extract/css": "^1.17.0",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
@@ -48,5 +49,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -26,6 +26,7 @@
"@toeverything/theme": "^1.1.23",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
@@ -41,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -50,6 +50,7 @@
"html2canvas": "^1.4.1",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -66,5 +67,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -46,5 +46,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -82,5 +82,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -48,5 +48,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -4,7 +4,6 @@ import { describe, expect, it, vi } from 'vitest';
import type { GroupBy } from '../core/common/types.js';
import type { DataSource } from '../core/data-source/base.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 { t } from '../core/logical/type-presets.js';
import type { DataViewCellLifeCycle } from '../core/property/index.js';
@@ -18,10 +17,7 @@ import {
pickKanbanGroupColumn,
resolveKanbanGroupBy,
} from '../view-presets/kanban/group-by-utils.js';
import {
KanbanSingleView,
materializeKanbanColumns,
} from '../view-presets/kanban/kanban-view-manager.js';
import { materializeKanbanColumns } from '../view-presets/kanban/kanban-view-manager.js';
import type { KanbanCard } from '../view-presets/kanban/pc/card.js';
import { KanbanDragController } from '../view-presets/kanban/pc/controller/drag.js';
import type { KanbanGroup } from '../view-presets/kanban/pc/group.js';
@@ -274,73 +270,6 @@ 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', () => {
it('shows drop preview when insert position exists', () => {
const controller = createDragController();

View File

@@ -1,21 +1,15 @@
import { describe, expect, test } from 'vitest';
import type { FilterGroup } from '../core/filter/types.js';
import { numberFormats } from '../property-presets/number/utils/formats.js';
import {
formatNumber,
NumberFormatSchema,
parseNumber,
} 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 type { MobileTableGroup } from '../view-presets/table/mobile/group.js';
import { pcEffects } from '../view-presets/table/pc/effect.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 */
@@ -47,146 +41,6 @@ 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', () => {
test('number format menu should expose all schema formats', () => {
const menuFormats = numberFormats.map(format => format.type);

View File

@@ -349,7 +349,7 @@ export class KanbanSingleView extends SingleViewBase<KanbanViewData> {
isShow(rowId: string): boolean {
if (this.filter$.value?.conditions.length) {
const rowMap = Object.fromEntries(
this.propertiesRaw$.value.map(column => [
this.properties$.value.map(column => [
column.id,
column.cellGetOrCreate(rowId).jsonValue$.value,
])

View File

@@ -54,9 +54,7 @@ export class DatabaseCellContainer extends SignalWatcher(
const selectionView = this.selectionView;
if (selectionView) {
const selection = selectionView.selection;
const shouldEnterEditMode =
editing && this.cell?.beforeEnterEditMode() !== false;
if (selection && this.isSelected(selection) && shouldEnterEditMode) {
if (selection && this.isSelected(selection) && editing) {
selectionView.selection = TableViewAreaSelection.create({
groupKey: this.groupKey,
focus: {

View File

@@ -57,9 +57,7 @@ export class TableViewCellContainer extends SignalWatcher(
const selectionView = this.selectionController;
if (selectionView) {
const selection = selectionView.selection;
const shouldEnterEditMode =
editing && this.cell?.beforeEnterEditMode() !== false;
if (selection && this.isSelected(selection) && shouldEnterEditMode) {
if (selection && this.isSelected(selection) && editing) {
selectionView.selection = TableViewAreaSelection.create({
groupKey: this.groupKey,
focus: {

View File

@@ -26,52 +26,6 @@ import type { ViewManager } from '../../core/view-manager/view-manager.js';
import { DEFAULT_COLUMN_MIN_WIDTH, DEFAULT_COLUMN_WIDTH } from './consts.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> {
propertiesRaw$ = computed(() => {
const needShow = new Set(this.dataSource.properties$.value);
@@ -266,10 +220,14 @@ export class TableSingleView extends SingleViewBase<TableViewData> {
return this.data$.value?.mode ?? 'table';
}
constructor(viewManager: ViewManager, viewId: string) {
super(viewManager, viewId);
}
isShow(rowId: string): boolean {
if (this.filter$.value?.conditions.length) {
const rowMap = Object.fromEntries(
this.propertiesRaw$.value.map(column => [
this.properties$.value.map(column => [
column.id,
column.cellGetOrCreate(rowId).jsonValue$.value,
])
@@ -332,33 +290,6 @@ 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];

View File

@@ -26,5 +26,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -35,5 +35,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -26,6 +26,7 @@
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.23",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
@@ -39,5 +40,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -28,6 +28,7 @@
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
@@ -41,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -27,6 +27,7 @@
"@toeverything/theme": "^1.1.23",
"@vanilla-extract/css": "^1.17.0",
"lit": "^3.2.0",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
@@ -40,5 +41,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -27,6 +27,7 @@
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -42,5 +43,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -28,6 +28,7 @@
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -43,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -29,6 +29,7 @@
"fractional-indexing": "^3.2.0",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -47,5 +48,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -30,6 +30,7 @@
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -44,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -34,6 +34,7 @@
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"simple-xml-to-json": "^1.2.2",
"yjs": "^13.6.27",
@@ -50,5 +51,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -30,6 +30,7 @@
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -44,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -27,6 +27,7 @@
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -44,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -28,6 +28,7 @@
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -43,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -29,6 +29,7 @@
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -43,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -27,6 +27,7 @@
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
@@ -42,5 +43,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -25,5 +25,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -47,5 +47,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -8,10 +8,9 @@ export default defineConfig({
browser: {
enabled: true,
headless: true,
name: 'chromium',
instances: [{ browser: 'chromium' }],
provider: 'playwright',
isolate: false,
providerOptions: {},
},
include: ['src/__tests__/**/*.unit.spec.ts'],
testTimeout: 500,

View File

@@ -50,5 +50,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -56,5 +56,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -43,5 +43,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -30,5 +30,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -41,5 +41,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -39,6 +39,7 @@
"micromark-extension-gfm-table": "^2.1.0",
"micromark-extension-gfm-task-list-item": "^2.1.0",
"micromark-util-combine-extensions": "^2.0.0",
"minimatch": "^10.1.1",
"pdfmake": "^0.2.20",
"quick-lru": "^7.3.0",
"rehype-parse": "^9.0.0",
@@ -76,5 +77,5 @@
"@types/pdfmake": "^0.2.12",
"vitest": "^3.2.4"
},
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -21,24 +21,12 @@ 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();
export const setImageProxyMiddlewareURL = defaultImageProxyMiddlewarBuilder.set;
export const defaultImageProxyMiddleware: TransformerMiddleware = args => {
return defaultImageProxyMiddlewarBuilder.get()(args);
};
export const defaultImageProxyMiddleware =
defaultImageProxyMiddlewarBuilder.get();
// TODO(@mirone): this should be configured when setup instead of runtime
export class ImageProxyService extends StoreExtension {
@@ -52,7 +40,7 @@ export class ImageProxyService extends StoreExtension {
}
buildUrl(imageUrl: string) {
if (imageUrl.startsWith(this.imageProxyURL) || isImageProxyURL(imageUrl)) {
if (imageUrl.startsWith(this.imageProxyURL)) {
return imageUrl;
}

View File

@@ -1,7 +1,5 @@
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.
*/
@@ -30,7 +28,7 @@ export const scrollbarStyle = (container: string) => {
}
${unsafeCSS(container)}::-webkit-scrollbar-thumb {
border-radius: 2px;
background-color: ${unsafeCSSVarV2('icon/secondary', '#b1b1b1')};
background-color: #b1b1b1;
}
${unsafeCSS(container)}::-webkit-scrollbar-corner {
display: none;

View File

@@ -31,6 +31,7 @@
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.23",
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
@@ -44,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -34,5 +34,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -36,5 +36,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -40,5 +40,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -38,5 +38,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -36,5 +36,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -34,5 +34,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -55,5 +55,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -43,5 +43,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -220,7 +220,9 @@ export class ImportDoc extends WithDisposable(LitElement) {
</header>
<div>
AFFiNE will gradually support more file formats for import.
<a href="https://affine.pro/redirect/discord" target="_blank"
<a
href="https://community.affine.pro/c/feature-requests/import-export"
target="_blank"
>Provide feedback.</a
>
</div>

View File

@@ -37,5 +37,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -37,5 +37,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -35,5 +35,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -30,5 +30,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -36,5 +36,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -38,5 +38,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -20,7 +20,6 @@ import {
} from '@blocksuite/affine-shared/services';
import { unsafeCSSVar, unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { matchModels } from '@blocksuite/affine-shared/utils';
import { IS_MOBILE } from '@blocksuite/global/env';
import {
Bound,
getCommonBound,
@@ -110,17 +109,6 @@ 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(lightToolbarStyles('editor-toolbar'))}
`;
@@ -280,110 +268,9 @@ export class AffineToolbarWidget extends WidgetComponent {
const { flags, flavour$, message$, placement$ } = toolbarRegistry;
const context = new ToolbarContext(std);
const isNativeTextSelection = () => {
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);
// 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);
}
// TODO(@fundon): fix toolbar position shaking when the wheel scrolls
// document.body.append(toolbar);
this.shadowRoot!.append(toolbar);
// Formatting
// Selects text in note.
@@ -418,12 +305,30 @@ export class AffineToolbarWidget extends WidgetComponent {
disposables.addFromEvent(document, 'selectionchange', () => {
const range = std.range.value ?? null;
let activated = context.activated && Boolean(range && !range.collapsed);
let isNative = false;
if (activated) {
activated = isNativeTextSelection();
const result = std.selection.find(DatabaseSelection);
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(() => {
activated &&= isNative;
// Focues outside: `doc-title`
if (
flags.check(Flag.Text) &&
@@ -757,14 +662,6 @@ export class AffineToolbarWidget extends WidgetComponent {
disposables.add(
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) {
abortController.abort();
}

View File

@@ -35,5 +35,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -17,5 +17,5 @@
"dependencies": {
"@blocksuite/affine": "workspace:*"
},
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -64,5 +64,5 @@
"devDependencies": {
"vitest": "^3.2.4"
},
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -47,5 +47,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.26.3"
"version": "0.26.1"
}

View File

@@ -1,119 +0,0 @@
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);
});
});

View File

@@ -90,21 +90,9 @@ export function bindKeymap(
// 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.
// See: https://github.com/toeverything/AFFiNE/issues/14059
const hasModifier =
event.shiftKey || event.altKey || event.ctrlKey || event.metaKey;
const hasModifier = event.shiftKey || event.altKey || event.metaKey;
const baseName = base[event.keyCode];
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)
) {
if (hasModifier && baseName && baseName !== name) {
const fromCode = map[modifiers(baseName, event)];
if (fromCode && fromCode(ctx)) {
return true;

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