Compare commits

...

123 Commits

Author SHA1 Message Date
DarkSky
5041578768 feat: bump ios toolchain (#14325)
#### PR Dependency Tree


* **PR #14325** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)
2026-01-27 12:48:06 +08:00
Akshaj Rawat
b8f626513f feat: disable high power consumption without charger (#14281)
Co-authored-by: DarkSky <darksky2048@gmail.com>
2026-01-27 04:46:16 +08:00
DarkSky
3b4b0bad22 feat: improve admin panel styles (#14318) 2026-01-27 04:44:21 +08:00
DarkSky
7d47cc52b6 fix: firefox input (#14315)
fix #14296 
fix #14289

#### PR Dependency Tree


* **PR #14315** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved inline editor stability for selection edge cases and
beforeinput handling, with better recovery and native-input protection.
* Fixed potential crashes when deleting with selections outside the
editor bounds, including Firefox-specific scenarios.

* **Tests**
* Added unit tests covering beforeinput behavior and added Firefox
end-to-end regression tests.

* **Chores**
  * Reduced CI test parallelism to streamline pipeline.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-27 00:54:21 +08:00
DarkSky
27ed15a83e fix: chat session cannot delete (#14312)
fix #14309



#### PR Dependency Tree


* **PR #14312** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added AI chat session deletion with confirmation dialogs and
success/failure notifications.
* Localized AI chat panel labels, loading messages, and session
management text across multiple languages.

* **Documentation**
* Added internationalization support for chat panel titles, history
loading states, and deletion confirmations.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-26 19:47:47 +08:00
renovate[bot]
5498133627 chore: bump up rustc version to v1.93.0 (#14303)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [rustc](https://redirect.github.com/rust-lang/rust) | minor | `1.92.0`
→ `1.93.0` |

---

### Release Notes

<details>
<summary>rust-lang/rust (rustc)</summary>

###
[`v1.93.0`](https://redirect.github.com/rust-lang/rust/blob/HEAD/RELEASES.md#Version-1930-2026-01-22)

[Compare
Source](https://redirect.github.com/rust-lang/rust/compare/1.92.0...1.93.0)

\==========================

<a id="1.93.0-Language"></a>

## Language

- [Stabilize several s390x `vector`-related target features and the
`is_s390x_feature_detected!`
macro](https://redirect.github.com/rust-lang/rust/pull/145656)
- [Stabilize declaration of C-style variadic functions for the `system`
ABI](https://redirect.github.com/rust-lang/rust/pull/145954)
- [Emit error when using some keyword as a `cfg`
predicate](https://redirect.github.com/rust-lang/rust/pull/146978)
- [Stabilize
`asm_cfg`](https://redirect.github.com/rust-lang/rust/pull/147736)
- [During const-evaluation, support copying pointers
byte-by-byte](https://redirect.github.com/rust-lang/rust/pull/148259)
- [LUB coercions now correctly handle function item types, and functions
with differing
safeties](https://redirect.github.com/rust-lang/rust/pull/148602)
- [Allow `const` items that contain mutable references to `static`
(which is *very* unsafe, but not *always*
UB)](https://redirect.github.com/rust-lang/rust/pull/148746)
- [Add warn-by-default `const_item_interior_mutations` lint to warn
against calls which mutate interior mutable `const`
items](https://redirect.github.com/rust-lang/rust/pull/148407)
- [Add warn-by-default `function_casts_as_integer`
lint](https://redirect.github.com/rust-lang/rust/pull/141470)

<a id="1.93.0-Compiler"></a>

## Compiler

- [Stabilize
`-Cjump-tables=bool`](https://redirect.github.com/rust-lang/rust/pull/145974).
The flag was previously called `-Zno-jump-tables`.

<a id="1.93.0-Platform-Support"></a>

## Platform Support

- [Promote `riscv64a23-unknown-linux-gnu` to Tier 2 (without host
tools)](https://redirect.github.com/rust-lang/rust/pull/148435)

Refer to Rust's [platform support page][platform-support-doc]
for more information on Rust's tiered platform support.

[platform-support-doc]:
https://doc.rust-lang.org/rustc/platform-support.html

<a id="1.93.0-Libraries"></a>

## Libraries

- [Stop internally using `specialization` on the `Copy` trait as it is
unsound in the presence of lifetime dependent `Copy` implementations.
This may result in some performance regressions as some standard library
APIs may now call `Clone::clone` instead of performing bitwise
copies](https://redirect.github.com/rust-lang/rust/pull/135634)
- [Allow the global allocator to use thread-local storage and
`std::thread::current()`](https://redirect.github.com/rust-lang/rust/pull/144465)
- [Make `BTree::append` not update existing keys when appending an entry
which already
exists](https://redirect.github.com/rust-lang/rust/pull/145628)
- [Don't require `T: RefUnwindSafe` for `vec::IntoIter<T>:
UnwindSafe`](https://redirect.github.com/rust-lang/rust/pull/145665)

<a id="1.93.0-Stabilized-APIs"></a>

## Stabilized APIs

-
[`<[MaybeUninit<T>]>::assume_init_drop`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.assume_init_drop)
-
[`<[MaybeUninit<T>]>::assume_init_ref`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.assume_init_ref)
-
[`<[MaybeUninit<T>]>::assume_init_mut`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.assume_init_mut)
-
[`<[MaybeUninit<T>]>::write_copy_of_slice`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.write_copy_of_slice)
-
[`<[MaybeUninit<T>]>::write_clone_of_slice`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.write_clone_of_slice)
-
[`String::into_raw_parts`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.into_raw_parts)
-
[`Vec::into_raw_parts`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.into_raw_parts)
-
[`<iN>::unchecked_neg`](https://doc.rust-lang.org/stable/std/primitive.isize.html#method.unchecked_neg)
-
[`<iN>::unchecked_shl`](https://doc.rust-lang.org/stable/std/primitive.isize.html#method.unchecked_shl)
-
[`<iN>::unchecked_shr`](https://doc.rust-lang.org/stable/std/primitive.isize.html#method.unchecked_shr)
-
[`<uN>::unchecked_shl`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.unchecked_shl)
-
[`<uN>::unchecked_shr`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.unchecked_shr)
-
[`<[T]>::as_array`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_array)
-
[`<[T]>::as_mut_array`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_mut_array)
- [`<*const
[T]>::as_array`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_array)
- [`<*mut
[T]>::as_mut_array`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_mut_array)
-
[`VecDeque::pop_front_if`](https://doc.rust-lang.org/stable/std/collections/struct.VecDeque.html#method.pop_front_if)
-
[`VecDeque::pop_back_if`](https://doc.rust-lang.org/stable/std/collections/struct.VecDeque.html#method.pop_back_if)
-
[`Duration::from_nanos_u128`](https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_nanos_u128)
-
[`char::MAX_LEN_UTF8`](https://doc.rust-lang.org/stable/std/primitive.char.html#associatedconstant.MAX_LEN_UTF8)
-
[`char::MAX_LEN_UTF16`](https://doc.rust-lang.org/stable/std/primitive.char.html#associatedconstant.MAX_LEN_UTF16)
-
[`std::fmt::from_fn`](https://doc.rust-lang.org/stable/std/fmt/fn.from_fn.html)
-
[`std::fmt::FromFn`](https://doc.rust-lang.org/stable/std/fmt/struct.FromFn.html)

<a id="1.93.0-Cargo"></a>

## Cargo

- [Enable CARGO\_CFG\_DEBUG\_ASSERTIONS in build scripts based on
profile](https://redirect.github.com/rust-lang/cargo/pull/16160/)
- [In `cargo tree`, support long forms for `--format`
variables](https://redirect.github.com/rust-lang/cargo/pull/16204/)
- [Add `--workspace` to `cargo
clean`](https://redirect.github.com/rust-lang/cargo/pull/16263/)

<a id="1.93.0-Rustdoc"></a>

## Rustdoc

- [Remove
`#![doc(document_private_items)]`](https://redirect.github.com/rust-lang/rust/pull/146495)
- [Include attribute and derive macros in search filters for
"macros"](https://redirect.github.com/rust-lang/rust/pull/148176)
- [Include extern crates in search filters for
`import`](https://redirect.github.com/rust-lang/rust/pull/148301)
- [Validate usage of crate-level doc
attributes](https://redirect.github.com/rust-lang/rust/pull/149197).
This means if any of `html_favicon_url`, `html_logo_url`,
`html_playground_url`, `issue_tracker_base_url`, or `html_no_source`
either has a missing value, an unexpected value, or a value of the wrong
type, rustdoc will emit the deny-by-default lint
`rustdoc::invalid_doc_attributes`.

<a id="1.93.0-Compatibility-Notes"></a>

## Compatibility Notes

- [Introduce `pin_v2` into the builtin attributes
namespace](https://redirect.github.com/rust-lang/rust/pull/139751)
- [Update bundled musl to
1.2.5](https://redirect.github.com/rust-lang/rust/pull/142682)
- [On Emscripten, the unwinding ABI used when compiling with
`panic=unwind` was changed from the JS exception handling ABI to the
wasm exception handling
ABI.](https://redirect.github.com/rust-lang/rust/pull/147224) If linking
C/C++ object files with Rust objects, `-fwasm-exceptions` must be passed
to the linker now. On nightly Rust, it is possible to get the old
behavior with `-Zwasm-emscripten-eh=false -Zbuild-std`, but it will be
removed in a future release.
- The `#[test]` attribute, used to define tests, was previously ignored
in various places where it had no meaning (e.g on trait methods or
types). Putting the `#[test]` attribute in these places is no longer
ignored, and will now result in an error; this may also result in errors
when generating rustdoc. [Error when `test` attribute is applied to
structs](https://redirect.github.com/rust-lang/rust/pull/147841)
- Cargo now sets the `CARGO_CFG_DEBUG_ASSERTIONS` environment variable
in more situations. This will cause crates depending on `static-init`
versions 1.0.1 to 1.0.3 to fail compilation with "failed to resolve: use
of unresolved module or unlinked crate `parking_lot`". See [the linked
issue](https://redirect.github.com/rust-lang/rust/issues/150646#issuecomment-3718964342)
for details.
- [User written types in the `offset_of!` macro are now checked to be
well formed.](https://redirect.github.com/rust-lang/rust/issues/150465/)
- `cargo publish` no longer emits `.crate` files as a final artifact for
user access when the `build.build-dir` config is unset
- [Upgrade the `deref_nullptr` lint from warn-by-default to
deny-by-default](https://redirect.github.com/rust-lang/rust/pull/148122)
- [Add future-incompatibility warning for `...` function parameters
without a pattern outside of `extern`
blocks](https://redirect.github.com/rust-lang/rust/pull/143619)
- [Introduce future-compatibility warning for `repr(C)` enums whose
discriminant values do not fit into a `c_int` or
`c_uint`](https://redirect.github.com/rust-lang/rust/pull/147017)
- [Introduce future-compatibility warning against ignoring `repr(C)`
types as part of
`repr(transparent)`](https://redirect.github.com/rust-lang/rust/pull/147185)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi45Mi4xIiwidXBkYXRlZEluVmVyIjoiNDIuOTIuMSIsInRhcmdldEJyYW5jaCI6ImNhbmFyeSIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-26 17:24:43 +08:00
JustAnDK
ecc98573eb fix: safe cookie parsing (#14292)
# Summary
This PR fixes a server-side cookie parsing edge case where malformed
cookie values throw `URI malformed`, causing socket.io auth to fail and
clients to get stuck in infinite workspace loading/syncing.

# Observed Behavior
- User creates a cloud-backed workspace and invites another user to it.
- Second user accepts the invite, awaits approval, and attempts to load
the workspace, getting stuck in infinite loading state.
- `api/workspaces/<id>/docs/<id>` return 404 for those users, as the
workspace they are trying to access was not synced to the server.
- Server logs show socket.io `CONNECT_ERROR` with `URI malformed`, then
connection closed.

# Confirmed Trigger
An externally-managed `auth_session` cookie containing a raw `%` symbol
causes `decodeURIComponent` to throw. This matches the observed
socket.io `CONNECT_ERROR`, explaining why some users were affected while
the rest were not.

# Root Cause
The `parseCookies` function calls `decodeURIComponent` on every cookie
key/value without guard, so when a malformed percent-encoded value is
encountered, `decodeURIComponent` throws, which bubbles into the
socket.io auth middleware, aborting the connection.

# Fix
Wrap `decodeURIComponent` calls in `try/catch`, on failure falling back
to the raw key/value.

# Testing
- Manually regenerating the bad cookie until no malformed parts are
present resolves the issue.
- With the guard in place, affected users can open shared workspaces
with sync successfully completing.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved cookie parsing robustness so malformed cookie values no
longer cause errors; the system now preserves raw cookie values when
decoding fails.

* **Tests**
* Added test coverage to ensure cookie parsing handles invalid/malformed
cookie values without throwing.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-26 17:24:11 +08:00
renovate[bot]
69907083f7 chore: bump up opentelemetry (#14300)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
|
[@opentelemetry/core](https://redirect.github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-core)
([source](https://redirect.github.com/open-telemetry/opentelemetry-js))
| [`2.2.0` →
`2.5.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2fcore/2.2.0/2.5.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2fcore/2.5.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2fcore/2.2.0/2.5.0?slim=true)
|
|
[@opentelemetry/exporter-prometheus](https://redirect.github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-exporter-prometheus)
([source](https://redirect.github.com/open-telemetry/opentelemetry-js))
| [`^0.208.0` →
`^0.211.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2fexporter-prometheus/0.208.0/0.211.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2fexporter-prometheus/0.211.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2fexporter-prometheus/0.208.0/0.211.0?slim=true)
|
|
[@opentelemetry/exporter-zipkin](https://redirect.github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-exporter-zipkin)
([source](https://redirect.github.com/open-telemetry/opentelemetry-js))
| [`2.2.0` →
`2.5.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2fexporter-zipkin/2.2.0/2.5.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2fexporter-zipkin/2.5.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2fexporter-zipkin/2.2.0/2.5.0?slim=true)
|
|
[@opentelemetry/host-metrics](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/tree/main/packages/host-metrics#readme)
([source](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/tree/HEAD/packages/host-metrics))
| [`0.38.0` →
`0.38.2`](https://renovatebot.com/diffs/npm/@opentelemetry%2fhost-metrics/0.38.0/0.38.2)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2fhost-metrics/0.38.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2fhost-metrics/0.38.0/0.38.2?slim=true)
|
|
[@opentelemetry/instrumentation](https://redirect.github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation)
([source](https://redirect.github.com/open-telemetry/opentelemetry-js))
| [`^0.208.0` →
`^0.211.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation/0.208.0/0.211.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation/0.211.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation/0.208.0/0.211.0?slim=true)
|
|
[@opentelemetry/instrumentation-graphql](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/tree/main/packages/instrumentation-graphql#readme)
([source](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/tree/HEAD/packages/instrumentation-graphql))
| [`^0.56.0` →
`^0.58.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-graphql/0.56.0/0.58.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation-graphql/0.58.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation-graphql/0.56.0/0.58.0?slim=true)
|
|
[@opentelemetry/instrumentation-http](https://redirect.github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-http)
([source](https://redirect.github.com/open-telemetry/opentelemetry-js))
| [`^0.208.0` →
`^0.211.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-http/0.208.0/0.211.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation-http/0.211.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation-http/0.208.0/0.211.0?slim=true)
|
|
[@opentelemetry/instrumentation-ioredis](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/tree/main/packages/instrumentation-ioredis#readme)
([source](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/tree/HEAD/packages/instrumentation-ioredis))
| [`^0.57.0` →
`^0.59.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-ioredis/0.57.0/0.59.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation-ioredis/0.59.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation-ioredis/0.57.0/0.59.0?slim=true)
|
|
[@opentelemetry/instrumentation-nestjs-core](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/tree/main/packages/instrumentation-nestjs-core#readme)
([source](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/tree/HEAD/packages/instrumentation-nestjs-core))
| [`^0.55.0` →
`^0.57.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-nestjs-core/0.55.0/0.57.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation-nestjs-core/0.57.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation-nestjs-core/0.55.0/0.57.0?slim=true)
|
|
[@opentelemetry/instrumentation-socket.io](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/tree/main/packages/instrumentation-socket.io#readme)
([source](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/tree/HEAD/packages/instrumentation-socket.io))
| [`^0.55.0` →
`^0.57.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-socket.io/0.55.1/0.57.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation-socket.io/0.57.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation-socket.io/0.55.1/0.57.0?slim=true)
|
|
[@opentelemetry/resources](https://redirect.github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-resources)
([source](https://redirect.github.com/open-telemetry/opentelemetry-js))
| [`2.2.0` →
`2.5.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2fresources/2.2.0/2.5.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2fresources/2.5.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2fresources/2.2.0/2.5.0?slim=true)
|
|
[@opentelemetry/sdk-metrics](https://redirect.github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics)
([source](https://redirect.github.com/open-telemetry/opentelemetry-js))
| [`2.2.0` →
`2.5.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2fsdk-metrics/2.2.0/2.5.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2fsdk-metrics/2.5.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2fsdk-metrics/2.2.0/2.5.0?slim=true)
|
|
[@opentelemetry/sdk-node](https://redirect.github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-sdk-node)
([source](https://redirect.github.com/open-telemetry/opentelemetry-js))
| [`^0.208.0` →
`^0.211.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2fsdk-node/0.208.0/0.211.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2fsdk-node/0.211.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2fsdk-node/0.208.0/0.211.0?slim=true)
|
|
[@opentelemetry/sdk-trace-node](https://redirect.github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-node)
([source](https://redirect.github.com/open-telemetry/opentelemetry-js))
| [`2.2.0` →
`2.5.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2fsdk-trace-node/2.2.0/2.5.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2fsdk-trace-node/2.5.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2fsdk-trace-node/2.2.0/2.5.0?slim=true)
|
|
[@opentelemetry/semantic-conventions](https://redirect.github.com/open-telemetry/opentelemetry-js/tree/main/semantic-conventions)
([source](https://redirect.github.com/open-telemetry/opentelemetry-js))
| [`1.38.0` →
`1.39.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2fsemantic-conventions/1.38.0/1.39.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2fsemantic-conventions/1.39.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2fsemantic-conventions/1.38.0/1.39.0?slim=true)
|

---

### Release Notes

<details>
<summary>open-telemetry/opentelemetry-js
(@&#8203;opentelemetry/core)</summary>

###
[`v2.5.0`](https://redirect.github.com/open-telemetry/opentelemetry-js/blob/HEAD/CHANGELOG.md#250)

[Compare
Source](https://redirect.github.com/open-telemetry/opentelemetry-js/compare/v2.4.0...v2.5.0)

##### 🐛 Bug Fixes

- refactor(resources): use runtime check for default service name
[#&#8203;6257](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6257)
[@&#8203;overbalance](https://redirect.github.com/overbalance)

##### 🏠 Internal

- chore(context-async-hooks): Deprecate `AsyncHooksContextManager`
[#&#8203;6298](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6298)
[@&#8203;trentm](https://redirect.github.com/trentm)
- chore: fix CODEOWNERS rule ordering
[#&#8203;6297](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6297)
[@&#8203;overbalance](https://redirect.github.com/overbalance)
- fix(github): fix CODEOWNERS browser package paths
[#&#8203;6303](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6303)
[@&#8203;overbalance](https://redirect.github.com/overbalance)
- fix(build): update
[@&#8203;types/node](https://redirect.github.com/types/node) to
18.19.130, remove DOM types from base tsconfig
[#&#8203;6280](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6280)
[@&#8203;overbalance](https://redirect.github.com/overbalance)

###
[`v2.4.0`](https://redirect.github.com/open-telemetry/opentelemetry-js/blob/HEAD/CHANGELOG.md#240)

[Compare
Source](https://redirect.github.com/open-telemetry/opentelemetry-js/compare/v2.3.0...v2.4.0)

##### 🐛 Bug Fixes

- fix(sdk-metrics): improve PeriodicExportingMetricReader() constructor
input validation
[#&#8203;6286](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6286)
[@&#8203;cjihrig](https://redirect.github.com/cjihrig)
- fix(core): Avoid using DOM types for otperformance export
[#&#8203;6278](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6278)
[@&#8203;samchungy](https://redirect.github.com/samchungy)

##### 🏠 Internal

- chore(browser): fix CODEOWNERS paths for browser-related packages
- refactor(sdk-metrics): remove Promise.allSettled() ponyfill
[#&#8203;6277](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6277)
[@&#8203;cjihrig](https://redirect.github.com/cjihrig)

###
[`v2.3.0`](https://redirect.github.com/open-telemetry/opentelemetry-js/blob/HEAD/CHANGELOG.md#230)

[Compare
Source](https://redirect.github.com/open-telemetry/opentelemetry-js/compare/v2.2.0...v2.3.0)

##### 🚀 Features

- feat(sdk-trace-base): implement on ending in span processor
[#&#8203;6024](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6024)
[@&#8203;majanjua-amzn](https://redirect.github.com/majanjua-amzn)
  - note: this feature is experimental and subject to change

##### 🐛 Bug Fixes

- fix(sdk-metrics): remove setImmediate usage in ConsoleMetricExporter
[#&#8203;6199](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6199)
[@&#8203;overbalance](https://redirect.github.com/overbalance)

##### 🏠 Internal

- refactor(bundler-tests): split webpack tests into webpack-4 and
webpack-5
[#&#8203;6098](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6098)
[@&#8203;overbalance](https://redirect.github.com/overbalance)
- refactor(sdk-metrics): remove isNotNullish() utility function
[#&#8203;6151](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6151)
[@&#8203;cjihrig](https://redirect.github.com/cjihrig)
- refactor(sdk-metrics): remove FlatMap() utility function
[#&#8203;6154](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6154)
[@&#8203;cjihrig](https://redirect.github.com/cjihrig)
- refactor(sdk-metrics): simplify AllowList and DenyList processors
[#&#8203;6159](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6159)
[@&#8203;cjihrig](https://redirect.github.com/cjihrig)
- chore: disallow constructor parameter property syntax
[#&#8203;6187](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6187)
[@&#8203;legendecas](https://redirect.github.com/legendecas)
- refactor(sdk-metrics): use test() instead of match() in isValidName()
[#&#8203;6205](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6205)
[@&#8203;cjihrig](https://redirect.github.com/cjihrig)
- refactor(core): remove TimeOriginLegacy Safari <15 fallback
[#&#8203;6235](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6235)
[@&#8203;overbalance](https://redirect.github.com/overbalance)
- chore: remove backcompat workspace
[#&#8203;6238](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6238)
[@&#8203;overbalance](https://redirect.github.com/overbalance)
- refactor(core,resources): consolidate platform-specific code
[#&#8203;6208](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6208)
[@&#8203;overbalance](https://redirect.github.com/overbalance)
- test(api): remove unnecessary conditional
[#&#8203;6241](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6241)
[@&#8203;cjihrig](https://redirect.github.com/cjihrig)
- refactor(api): remove several reverse() calls
[#&#8203;6252](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6252)
[@&#8203;cjihrig](https://redirect.github.com/cjihrig)
- refactor(api): remove unnecessary map() call
[#&#8203;6251](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6251)
[@&#8203;cjihrig](https://redirect.github.com/cjihrig)
- chore: add zed to gitignore
[#&#8203;6258](https://redirect.github.com/open-telemetry/opentelemetry-js/pull/6258)
[@&#8203;overbalance](https://redirect.github.com/overbalance)

</details>

<details>
<summary>open-telemetry/opentelemetry-js-contrib
(@&#8203;opentelemetry/host-metrics)</summary>

###
[`v0.38.2`](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/blob/HEAD/packages/host-metrics/CHANGELOG.md#0382-2026-01-21)

[Compare
Source](c84212cca7...7a5f3c0a09)

##### Bug Fixes

- **deps:** update dependency systeminformation to v5.30.3
([#&#8203;3335](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/issues/3335))
([9af0086](9af0086223))

###
[`v0.38.1`](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/blob/HEAD/packages/host-metrics/CHANGELOG.md#0381-2026-01-14)

[Compare
Source](66935ac724...c84212cca7)

##### Bug Fixes

- **deps:** update dependency systeminformation to v5.27.14 \[security]
([#&#8203;3308](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/issues/3308))
([c2d0bc5](c2d0bc5b19))

</details>

<details>
<summary>open-telemetry/opentelemetry-js-contrib
(@&#8203;opentelemetry/instrumentation-graphql)</summary>

###
[`v0.58.0`](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/blob/HEAD/packages/instrumentation-graphql/CHANGELOG.md#0580-2026-01-21)

[Compare
Source](c84212cca7...7a5f3c0a09)

##### Features

- **deps:** update deps matching '@&#8203;opentelemetry/\*'
([#&#8203;3353](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/issues/3353))
([a56bbdc](a56bbdc34a))

###
[`v0.57.0`](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/blob/HEAD/packages/instrumentation-graphql/CHANGELOG.md#0570-2026-01-14)

[Compare
Source](94e5b7da45...c84212cca7)

##### Features

- **deps:** update deps matching '@&#8203;opentelemetry/\*'
([#&#8203;3332](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/issues/3332))
([925a150](925a1501ce))
- **deps:** update deps matching '@&#8203;opentelemetry/\*'
([#&#8203;3340](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/issues/3340))
([2954943](29549434e7))

</details>

<details>
<summary>open-telemetry/opentelemetry-js-contrib
(@&#8203;opentelemetry/instrumentation-ioredis)</summary>

###
[`v0.59.0`](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/blob/HEAD/packages/instrumentation-ioredis/CHANGELOG.md#0590-2026-01-21)

[Compare
Source](c84212cca7...7a5f3c0a09)

##### Features

- **deps:** update deps matching '@&#8203;opentelemetry/\*'
([#&#8203;3353](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/issues/3353))
([a56bbdc](a56bbdc34a))

##### Dependencies

- The following workspace dependencies were updated
  - devDependencies
-
[@&#8203;opentelemetry/contrib-test-utils](https://redirect.github.com/opentelemetry/contrib-test-utils)
bumped from ^0.57.0 to ^0.58.0

###
[`v0.58.0`](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/blob/HEAD/packages/instrumentation-ioredis/CHANGELOG.md#0580-2026-01-14)

[Compare
Source](66935ac724...c84212cca7)

##### Features

- **deps:** update deps matching '@&#8203;opentelemetry/\*'
([#&#8203;3332](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/issues/3332))
([925a150](925a1501ce))
- **deps:** update deps matching '@&#8203;opentelemetry/\*'
([#&#8203;3340](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/issues/3340))
([2954943](29549434e7))

##### Dependencies

- The following workspace dependencies were updated
  - devDependencies
-
[@&#8203;opentelemetry/contrib-test-utils](https://redirect.github.com/opentelemetry/contrib-test-utils)
bumped from ^0.56.0 to ^0.57.0

</details>

<details>
<summary>open-telemetry/opentelemetry-js-contrib
(@&#8203;opentelemetry/instrumentation-nestjs-core)</summary>

###
[`v0.57.0`](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/blob/HEAD/packages/instrumentation-nestjs-core/CHANGELOG.md#0570-2026-01-21)

[Compare
Source](c84212cca7...7a5f3c0a09)

##### Features

- **deps:** update deps matching '@&#8203;opentelemetry/\*'
([#&#8203;3353](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/issues/3353))
([a56bbdc](a56bbdc34a))

###
[`v0.56.0`](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/blob/HEAD/packages/instrumentation-nestjs-core/CHANGELOG.md#0560-2026-01-14)

[Compare
Source](94e5b7da45...c84212cca7)

##### Features

- **deps:** update deps matching '@&#8203;opentelemetry/\*'
([#&#8203;3332](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/issues/3332))
([925a150](925a1501ce))
- **deps:** update deps matching '@&#8203;opentelemetry/\*'
([#&#8203;3340](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/issues/3340))
([2954943](29549434e7))

</details>

<details>
<summary>open-telemetry/opentelemetry-js-contrib
(@&#8203;opentelemetry/instrumentation-socket.io)</summary>

###
[`v0.57.0`](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/blob/HEAD/packages/instrumentation-socket.io/CHANGELOG.md#0570-2026-01-21)

[Compare
Source](c84212cca7...7a5f3c0a09)

##### Features

- **deps:** update deps matching '@&#8203;opentelemetry/\*'
([#&#8203;3353](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/issues/3353))
([a56bbdc](a56bbdc34a))

##### Dependencies

- The following workspace dependencies were updated
  - devDependencies
-
[@&#8203;opentelemetry/contrib-test-utils](https://redirect.github.com/opentelemetry/contrib-test-utils)
bumped from ^0.57.0 to ^0.58.0

###
[`v0.56.0`](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/blob/HEAD/packages/instrumentation-socket.io/CHANGELOG.md#0560-2026-01-14)

[Compare
Source](66935ac724...c84212cca7)

##### Features

- **deps:** update deps matching '@&#8203;opentelemetry/\*'
([#&#8203;3332](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/issues/3332))
([925a150](925a1501ce))
- **deps:** update deps matching '@&#8203;opentelemetry/\*'
([#&#8203;3340](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/issues/3340))
([2954943](29549434e7))

##### Dependencies

- The following workspace dependencies were updated
  - devDependencies
-
[@&#8203;opentelemetry/contrib-test-utils](https://redirect.github.com/opentelemetry/contrib-test-utils)
bumped from ^0.56.0 to ^0.57.0

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi45Mi4xIiwidXBkYXRlZEluVmVyIjoiNDIuOTIuMSIsInRhcmdldEJyYW5jaCI6ImNhbmFyeSIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-26 13:33:43 +08:00
renovate[bot]
268eb1f7ba chore: bump up Node.js to v22.22.0 (#14299)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [node](https://nodejs.org)
([source](https://redirect.github.com/nodejs/node)) | minor | `22.21.1`
→ `22.22.0` |

---

### Release Notes

<details>
<summary>nodejs/node (node)</summary>

###
[`v22.22.0`](https://redirect.github.com/nodejs/node/releases/tag/v22.22.0):
2026-01-13, Version 22.22.0 &#x27;Jod&#x27; (LTS),
@&#8203;marco-ippolito

[Compare
Source](https://redirect.github.com/nodejs/node/compare/v22.21.1...v22.22.0)

This is a security release.

##### Notable Changes

lib:

- (CVE-2025-59465) add TLSSocket default error handler
- (CVE-2025-55132) disable futimes when permission model is enabled
  lib,permission:
- (CVE-2025-55130) require full read and write to symlink APIs
  src:
- (CVE-2025-59466) rethrow stack overflow exceptions in async\_hooks
  src,lib:
- (CVE-2025-55131) refactor unsafe buffer creation to remove zero-fill
toggle
  tls:
- (CVE-2026-21637) route callback exceptions through error handlers

##### Commits

-
\[[`6badf4e6f4`](https://redirect.github.com/nodejs/node/commit/6badf4e6f4)]
- **deps**: update c-ares to v1.34.6 (Node.js GitHub Bot)
[#&#8203;60997](https://redirect.github.com/nodejs/node/pull/60997)
-
\[[`37509c3ff0`](https://redirect.github.com/nodejs/node/commit/37509c3ff0)]
- **deps**: update undici to 6.23.0 (Matteo Collina)
[nodejs-private/node-private#791](https://redirect.github.com/nodejs-private/node-private/pull/791)
-
\[[`eb8e41f8db`](https://redirect.github.com/nodejs/node/commit/eb8e41f8db)]
- **(CVE-2025-59465)** **lib**: add TLSSocket default error handler
(RafaelGSS)
[nodejs-private/node-private#797](https://redirect.github.com/nodejs-private/node-private/pull/797)
-
\[[`ebbf942a83`](https://redirect.github.com/nodejs/node/commit/ebbf942a83)]
- **(CVE-2025-55132)** **lib**: disable futimes when permission model is
enabled (RafaelGSS)
[nodejs-private/node-private#748](https://redirect.github.com/nodejs-private/node-private/pull/748)
-
\[[`6b4849583a`](https://redirect.github.com/nodejs/node/commit/6b4849583a)]
- **(CVE-2025-55130)** **lib,permission**: require full read and write
to symlink APIs (RafaelGSS)
[nodejs-private/node-private#760](https://redirect.github.com/nodejs-private/node-private/pull/760)
-
\[[`ddadc31f09`](https://redirect.github.com/nodejs/node/commit/ddadc31f09)]
- **(CVE-2025-59466)** **src**: rethrow stack overflow exceptions in
async\_hooks (Matteo Collina)
[nodejs-private/node-private#773](https://redirect.github.com/nodejs-private/node-private/pull/773)
-
\[[`d4d9f3915f`](https://redirect.github.com/nodejs/node/commit/d4d9f3915f)]
- **(CVE-2025-55131)** **src,lib**: refactor unsafe buffer creation to
remove zero-fill toggle (Сковорода Никита Андреевич)
[nodejs-private/node-private#759](https://redirect.github.com/nodejs-private/node-private/pull/759)
-
\[[`25d6799df6`](https://redirect.github.com/nodejs/node/commit/25d6799df6)]
- **(CVE-2026-21637)** **tls**: route callback exceptions through error
handlers (Matteo Collina)
[nodejs-private/node-private#796](https://redirect.github.com/nodejs-private/node-private/pull/796)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi45Mi4xIiwidXBkYXRlZEluVmVyIjoiNDIuOTIuMSIsInRhcmdldEJyYW5jaCI6ImNhbmFyeSIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-26 13:33:25 +08:00
renovate[bot]
50507fc9bf chore: bump up RevenueCat/purchases-ios-spm version to from: "5.55.3" (#14302)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[RevenueCat/purchases-ios-spm](https://redirect.github.com/RevenueCat/purchases-ios-spm)
| minor | `from: "5.0.1"` → `from: "5.55.3"` |

---

### Release Notes

<details>
<summary>RevenueCat/purchases-ios-spm
(RevenueCat/purchases-ios-spm)</summary>

###
[`v5.55.3`](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.55.2...5.55.3)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.55.2...5.55.3)

###
[`v5.55.2`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5552)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.55.1...5.55.2)

#### 5.55.2

###
[`v5.55.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5551)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.55.0...5.55.1)

#### 5.55.1

###
[`v5.55.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5550)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.54.1...5.55.0)

#### 5.55.0

###
[`v5.54.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5541)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.54.0...5.54.1)

#### 5.54.1

###
[`v5.54.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5540)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.53.0...5.54.0)

#### 5.54.0

###
[`v5.53.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5530)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.52.1...5.53.0)

#### 5.53.0

###
[`v5.52.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5521)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.52.0...5.52.1)

#### 5.52.1

###
[`v5.52.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5520)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.51.1...5.52.0)

#### 5.52.0

###
[`v5.51.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5511)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.51.0...5.51.1)

#### 5.51.1

###
[`v5.51.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5510)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.50.1...5.51.0)

#### 5.51.0

###
[`v5.50.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5501)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.50.0...5.50.1)

#### 5.50.1

###
[`v5.50.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5500)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.49.3...5.50.0)

#### 5.50.0

###
[`v5.49.3`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5493)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.49.2...5.49.3)

#### 5.49.3

###
[`v5.49.2`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5492)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.49.1...5.49.2)

#### 5.49.2

###
[`v5.49.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5491)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.49.0...5.49.1)

#### 5.49.1

###
[`v5.49.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5490)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.48.0...5.49.0)

#### 5.49.0

###
[`v5.48.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5480)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.47.1...5.48.0)

#### 5.48.0

###
[`v5.47.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5471)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.47.0...5.47.1)

#### 5.47.1

###
[`v5.47.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5470)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.46.3...5.47.0)

#### 5.47.0

###
[`v5.46.3`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5463)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.46.2...5.46.3)

##### 🔄 Other Changes

- Use cached offerings on network errors
([#&#8203;5707](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/5707))
via Antonio Pallares
([@&#8203;ajpallares](https://redirect.github.com/ajpallares))
- Allow the use of Test Store in release builds using the uiPreview
dangerous setting for the RC Mobile app
([#&#8203;5765](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/5765))
via Rick ([@&#8203;rickvdl](https://redirect.github.com/rickvdl))
- Fix signature verification fallback urls
([#&#8203;5756](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/5756))
via Antonio Pallares
([@&#8203;ajpallares](https://redirect.github.com/ajpallares))

###
[`v5.46.2`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5462)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.46.1...5.46.2)

#### 5.46.2

###
[`v5.46.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5461)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.46.0...5.46.1)

#### 5.46.1

###
[`v5.46.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5460)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.45.1...5.46.0)

#### 5.46.0

###
[`v5.45.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5451)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.45.0...5.45.1)

#### 5.45.1

###
[`v5.45.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5450)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.44.1...5.45.0)

#### 5.45.0

###
[`v5.44.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5441)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.44.0...5.44.1)

#### 5.44.1

###
[`v5.44.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5440)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.43.0...5.44.0)

#### 5.44.0

###
[`v5.43.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5430)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.42.0...5.43.0)

#### 5.43.0

###
[`v5.42.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5420)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.41.0...5.42.0)

#### 5.42.0

###
[`v5.41.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5410)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.40.0...5.41.0)

#### 5.41.0

###
[`v5.40.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5400)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.39.3...5.40.0)

#### 5.40.0

###
[`v5.39.3`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5393)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.39.2...5.39.3)

#### 5.39.3

###
[`v5.39.2`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5392)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.39.1...5.39.2)

#### 5.39.2

###
[`v5.39.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5391)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.39.0...5.39.1)

#### 5.39.1

###
[`v5.39.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5390)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.38.2...5.39.0)

#### 5.39.0

###
[`v5.38.2`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5382)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.38.1...5.38.2)

#### 5.38.2

###
[`v5.38.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5381)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.38.0...5.38.1)

#### 5.38.1

###
[`v5.38.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5380)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.37.0...5.38.0)

#### 5.38.0

###
[`v5.37.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5370)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.36.0...5.37.0)

#### 5.37.0

###
[`v5.36.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5360)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.35.1...5.36.0)

#### 5.36.0

###
[`v5.35.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5351)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.35.0...5.35.1)

#### 5.35.1

###
[`v5.35.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5350)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.34.0...5.35.0)

#### 5.35.0

###
[`v5.34.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5340)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.33.1...5.34.0)

#### 5.34.0

###
[`v5.33.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5331)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.33.0...5.33.1)

#### 5.33.1

###
[`v5.33.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5330)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.32.0...5.33.0)

#### 5.33.0

###
[`v5.32.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5320)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.31.0...5.32.0)

#### 5.32.0

###
[`v5.31.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5310)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.30.0...5.31.0)

#### 5.31.0

###
[`v5.30.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.29.0...5.30.0)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.29.0...5.30.0)

###
[`v5.29.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5290)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.28.1...5.29.0)

#### 5.29.0

###
[`v5.28.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5281)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.28.0...5.28.1)

#### 5.28.1

###
[`v5.28.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5280)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.27.1...5.28.0)

#### 5.28.0

###
[`v5.27.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5271)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.26.0...5.27.1)

#### 5.27.1

###
[`v5.26.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5260)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.25.3...5.26.0)

#### 5.26.0

###
[`v5.25.3`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5253)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.25.2...5.25.3)

#### 5.25.3

###
[`v5.25.2`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5252)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.25.1...5.25.2)

#### 5.25.2

###
[`v5.25.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5251)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.25.0...5.25.1)

#### 5.25.1

###
[`v5.25.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5250)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.24.0...5.25.0)

#### 5.25.0

###
[`v5.24.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5240)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.23.0...5.24.0)

#### 5.24.0

###
[`v5.23.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5230)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.22.2...5.23.0)

#### 5.23.0

###
[`v5.22.2`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5222)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.22.1...5.22.2)

#### 5.22.2

###
[`v5.22.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5221)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.22.0...5.22.1)

#### 5.22.1

###
[`v5.22.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5220)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.21.2...5.22.0)

#### 5.22.0

###
[`v5.21.2`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5212)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.21.1...5.21.2)

#### 5.21.2

###
[`v5.21.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5211)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.21.0...5.21.1)

#### 5.21.1

###
[`v5.21.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5210)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.20.3...5.21.0)

#### 5.21.0

###
[`v5.20.3`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5203)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.20.2...5.20.3)

#### 5.20.3

###
[`v5.20.2`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5202)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.20.1...5.20.2)

#### 5.20.2

###
[`v5.20.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5201)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.20.0...5.20.1)

#### 5.20.1

###
[`v5.20.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5200)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.19.0...5.20.0)

#### 5.20.0

###
[`v5.19.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5190)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.18.0...5.19.0)

#### 5.19.0

###
[`v5.18.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5180)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.17.0...5.18.0)

#### 5.18.0

###
[`v5.17.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5170)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.16.1...5.17.0)

#### 5.17.0

###
[`v5.16.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5161)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.16.0...5.16.1)

#### 5.16.1

###
[`v5.16.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5160)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.15.1...5.16.0)

#### 5.16.0

###
[`v5.15.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5151)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.15.0...5.15.1)

#### 5.15.1

###
[`v5.15.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5150)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.14.6...5.15.0)

#### 5.15.0

###
[`v5.14.6`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5146)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.14.5...5.14.6)

#### 5.14.6

###
[`v5.14.5`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5145)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.14.4...5.14.5)

#### 5.14.5

###
[`v5.14.4`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5144)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.14.3...5.14.4)

#### 5.14.4

###
[`v5.14.3`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5143)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.14.2...5.14.3)

#### 5.14.3

###
[`v5.14.2`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5142)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.14.1...5.14.2)

#### 5.14.2

###
[`v5.14.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5141)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.14.0...5.14.1)

#### 5.14.1

###
[`v5.14.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5140)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.13.0...5.14.0)

#### 5.14.0

###
[`v5.13.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5130)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.12.1...5.13.0)

#### 5.13.0

###
[`v5.12.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5121)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.12.0...5.12.1)

#### 5.12.1

###
[`v5.12.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5120)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.11.0...5.12.0)

#### 5.12.0

###
[`v5.11.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5110)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.10.0...5.11.0)

#### 5.11.0

###
[`v5.10.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#5100)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.9.0...5.10.0)

#### 5.10.0

###
[`v5.9.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#590)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.8.0...5.9.0)

#### 5.9.0

###
[`v5.8.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#580)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.7.1...5.8.0)

#### 5.8.0

###
[`v5.7.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#571)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.7.0...5.7.1)

#### 5.7.1

###
[`v5.7.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#570)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.6.0...5.7.0)

#### 5.7.0

###
[`v5.6.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#560)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.5.0...5.6.0)

#### 5.6.0

###
[`v5.5.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#550)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.4.0...5.5.0)

#### 5.5.0

###
[`v5.4.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#540)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.3.4...5.4.0)

#### 5.4.0

###
[`v5.3.4`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#534)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.3.3...5.3.4)

#### 5.3.4

###
[`v5.3.3`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#533)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.3.2...5.3.3)

##### Bugfixes

- Remove usage of adServicesToken in syncPurchases
([#&#8203;4257](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4257))
via Mark Villacampa
([@&#8203;MarkVillacampa](https://redirect.github.com/MarkVillacampa))
- Fixes a Paywall Template 7 crash when none of the tiers have any
available products.
([#&#8203;4243](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4243))
via JayShortway
([@&#8203;JayShortway](https://redirect.github.com/JayShortway))
- \[SK2] send unsynced attributes when syncing purchases
([#&#8203;4245](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4245))
via Mark Villacampa
([@&#8203;MarkVillacampa](https://redirect.github.com/MarkVillacampa))

##### Other Changes

- Do not embed `RevenueCat.framework` in `RevenueCatUI`
([#&#8203;4256](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4256))
via Cesar de la Vega
([@&#8203;vegaro](https://redirect.github.com/vegaro))
- Add warnings and clarifications to v5 migration docs
([#&#8203;4231](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4231))
via Mark Villacampa
([@&#8203;MarkVillacampa](https://redirect.github.com/MarkVillacampa))
- Fixes SwiftLint violation of rule optional\_data\_string\_conversion
([#&#8203;4252](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4252))
via JayShortway
([@&#8203;JayShortway](https://redirect.github.com/JayShortway))
- Paywall Components Localized Strings
([#&#8203;4237](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4237))
via James Borthwick
([@&#8203;jamesrb1](https://redirect.github.com/jamesrb1))
- Update `fastlane-plugin-revenuecat_internal`
([#&#8203;4244](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4244))
via Cesar de la Vega
([@&#8203;vegaro](https://redirect.github.com/vegaro))
- Add missing `#if PAYWALL_COMPONENTS`
([#&#8203;4241](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4241))
via James Borthwick
([@&#8203;jamesrb1](https://redirect.github.com/jamesrb1))
- Paywalls Components Viewmodels + partial localization support
([#&#8203;4230](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4230))
via James Borthwick
([@&#8203;jamesrb1](https://redirect.github.com/jamesrb1))

###
[`v5.3.2`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#532)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.3.1...5.3.2)

##### Bugfixes

- \[Customer Center] Build `WrongPlatformView` from JSON
([#&#8203;4234](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4234))
via Cesar de la Vega
([@&#8203;vegaro](https://redirect.github.com/vegaro))
- Add `feedbackSurveyCompleted` event to Customer Center events
([#&#8203;4194](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4194))
via Cesar de la Vega
([@&#8203;vegaro](https://redirect.github.com/vegaro))

##### Other Changes

- \[Diagnostics] Add `backend_error_code` property
([#&#8203;4236](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4236))
via Toni Rico ([@&#8203;tonidero](https://redirect.github.com/tonidero))
- Update README.md
([#&#8203;3986](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/3986))
via Khoa ([@&#8203;onmyway133](https://redirect.github.com/onmyway133))

###
[`v5.3.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#531)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.3.0...5.3.1)

##### Bugfixes

- Fix `compatibleTopBarTrailing` in MacOS and api tests
([#&#8203;4226](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4226))
via Cesar de la Vega
([@&#8203;vegaro](https://redirect.github.com/vegaro))
- \[Paywall] Fix restoreStarted not being called on
`presentPaywallIfNeeded` when using `requiredEntitlementIdentifier`
([#&#8203;4223](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4223))
via Josh Holtz
([@&#8203;joshdholtz](https://redirect.github.com/joshdholtz))
- \[CustomerCenter] Move sheet and restore alert creation to
`ManageSubscriptionsView`
([#&#8203;4220](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4220))
via Cesar de la Vega
([@&#8203;vegaro](https://redirect.github.com/vegaro))
- \[EXTERNAL] `Custom Entitlements Computation`: fix support display on
debug screen
([#&#8203;4215](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4215))
by [@&#8203;NachoSoto](https://redirect.github.com/NachoSoto)
([#&#8203;4218](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4218))
via Toni Rico ([@&#8203;tonidero](https://redirect.github.com/tonidero))
- \[Customer Center] Add padding to `No thanks` in promotional offer
screen
([#&#8203;4221](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4221))
via Cesar de la Vega
([@&#8203;vegaro](https://redirect.github.com/vegaro))
- Fix version number in plist files
([#&#8203;4213](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4213))
via Cesar de la Vega
([@&#8203;vegaro](https://redirect.github.com/vegaro))
- fix mac os sandbox check slowness
([#&#8203;3879](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/3879))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))
- \[Customer Center] Fix `FeedbackSurveyView` not opening
([#&#8203;4208](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4208))
via Cesar de la Vega
([@&#8203;vegaro](https://redirect.github.com/vegaro))
- Remove `unneeded_override` disable to fix linter
([#&#8203;4209](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4209))
via Cesar de la Vega
([@&#8203;vegaro](https://redirect.github.com/vegaro))

##### Dependency Updates

- Bump rexml from 3.3.3 to 3.3.6 in
/Tests/InstallationTests/CocoapodsInstallation
([#&#8203;4210](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4210))
via dependabot\[bot]
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])
- Bump rexml from 3.3.3 to 3.3.6
([#&#8203;4211](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4211))
via dependabot\[bot]
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])

##### Other Changes

- Update readme wording
([#&#8203;3914](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/3914))
via James Borthwick
([@&#8203;jamesrb1](https://redirect.github.com/jamesrb1))
- Set a maximum duration for iOS 15 tests
([#&#8203;4229](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4229))
via Cesar de la Vega
([@&#8203;vegaro](https://redirect.github.com/vegaro))
- Paywall Components Initial Commit
([#&#8203;4224](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4224))
via James Borthwick
([@&#8203;jamesrb1](https://redirect.github.com/jamesrb1))
- \[CustomerCenter] Open App Store when the user wants to update their
app
([#&#8203;4199](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4199))
via JayShortway
([@&#8203;JayShortway](https://redirect.github.com/JayShortway))
- \[Customer Center] Shows a warning when the app is not the latest
version
([#&#8203;4193](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4193))
via JayShortway
([@&#8203;JayShortway](https://redirect.github.com/JayShortway))
- Fix integration tests simulator version
([#&#8203;4219](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4219))
via Cesar de la Vega
([@&#8203;vegaro](https://redirect.github.com/vegaro))
- Pin swift-docc-plugin to 1.3.0
([#&#8203;4216](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4216))
via James Borthwick
([@&#8203;jamesrb1](https://redirect.github.com/jamesrb1))

###
[`v5.3.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#530)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.2.3...5.3.0)

##### New Features

- Price rounding logic
([#&#8203;4132](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4132))
via James Borthwick
([@&#8203;jamesrb1](https://redirect.github.com/jamesrb1))

##### Bugfixes

- \[Customer Center] Migrate to List style
([#&#8203;4190](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4190))
via Cody Kerns
([@&#8203;codykerns](https://redirect.github.com/codykerns))
- \[Paywalls] Improve locale consistency
([#&#8203;4158](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4158))
via Josh Holtz
([@&#8203;joshdholtz](https://redirect.github.com/joshdholtz))
- Set Paywalls Tester deployment target to iOS 15
([#&#8203;4196](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4196))
via James Borthwick
([@&#8203;jamesrb1](https://redirect.github.com/jamesrb1))
- \[Customer Center] Hide Contact Support button if URL can't be created
([#&#8203;4192](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4192))
via Cesar de la Vega
([@&#8203;vegaro](https://redirect.github.com/vegaro))
- Fix the setting for SKIP\_INSTALL in Xcode project
([#&#8203;4195](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4195))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))
- \[Customer Center] Improving customer center buttons
([#&#8203;4165](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4165))
via Cody Kerns
([@&#8203;codykerns](https://redirect.github.com/codykerns))
- Revert workaround for iOS 18 beta 5 SwiftUI crash
([#&#8203;4173](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4173))
via Mark Villacampa
([@&#8203;MarkVillacampa](https://redirect.github.com/MarkVillacampa))
- \[Paywalls] Make iOS version calculation lazy
([#&#8203;4163](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4163))
via Mark Villacampa
([@&#8203;MarkVillacampa](https://redirect.github.com/MarkVillacampa))
- Observe `PurchaseHandler` when owned externally
([#&#8203;4097](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4097))
via James Borthwick
([@&#8203;jamesrb1](https://redirect.github.com/jamesrb1))

##### Dependency Updates

- Bump fastlane-plugin-revenuecat\_internal from `d5f0742` to `4c4b8ce`
([#&#8203;4167](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4167))
via dependabot\[bot]
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])
- Bump rexml from 3.2.8 to 3.3.3 in
/Tests/InstallationTests/CocoapodsInstallation
([#&#8203;4176](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4176))
via dependabot\[bot]
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])
- Bump rexml from 3.2.9 to 3.3.3
([#&#8203;4175](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4175))
via dependabot\[bot]
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])

##### Other Changes

- \[Customer Center] Clean up colors in WrongPlatformView and
NoSubscriptionsView
([#&#8203;4204](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4204))
via Cesar de la Vega
([@&#8203;vegaro](https://redirect.github.com/vegaro))
- Fix failing `all-tests` and retry more flaky tests
([#&#8203;4188](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4188))
via Josh Holtz
([@&#8203;joshdholtz](https://redirect.github.com/joshdholtz))
- Compatibility content unavailable improvements
([#&#8203;4197](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4197))
via James Borthwick
([@&#8203;jamesrb1](https://redirect.github.com/jamesrb1))
- Create lane to enable customer center
([#&#8203;4191](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4191))
via Cesar de la Vega
([@&#8203;vegaro](https://redirect.github.com/vegaro))
- XCFramework artifacts in CircleCI
([#&#8203;4189](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4189))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))
- \[Customer Center] CustomerCenterViewModel checks whether the app is
the latest version
([#&#8203;4169](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4169))
via JayShortway
([@&#8203;JayShortway](https://redirect.github.com/JayShortway))
- export RevenueCatUI xcframework
([#&#8203;4172](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4172))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))
- Corrects references from ManageSubscriptionsButtonStyle to
ButtonsStyle.
([#&#8203;4186](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4186))
via JayShortway
([@&#8203;JayShortway](https://redirect.github.com/JayShortway))
- Speed up carthage installation tests
([#&#8203;4184](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4184))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))
- Customer center improvements
([#&#8203;4166](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4166))
via James Borthwick
([@&#8203;jamesrb1](https://redirect.github.com/jamesrb1))
- replace `color(from colorInformation:)` global with extension
([#&#8203;4183](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4183))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))
- Generating new test snapshots for `main` - ios-13
([#&#8203;4181](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4181))
via RevenueCat Git Bot
([@&#8203;RCGitBot](https://redirect.github.com/RCGitBot))
- Generating new test snapshots for `main` - ios-16
([#&#8203;4182](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4182))
via RevenueCat Git Bot
([@&#8203;RCGitBot](https://redirect.github.com/RCGitBot))
- Generating new test snapshots for `main` - ios-14
([#&#8203;4180](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4180))
via RevenueCat Git Bot
([@&#8203;RCGitBot](https://redirect.github.com/RCGitBot))
- Generating new test snapshots for `main` - ios-15
([#&#8203;4179](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4179))
via RevenueCat Git Bot
([@&#8203;RCGitBot](https://redirect.github.com/RCGitBot))
- Fix tests in main
([#&#8203;4174](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4174))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))
- Enable customer center tests
([#&#8203;4171](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4171))
via James Borthwick
([@&#8203;jamesrb1](https://redirect.github.com/jamesrb1))
- \[Customer Center] Initial implementation
([#&#8203;3967](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/3967))
via Cesar de la Vega
([@&#8203;vegaro](https://redirect.github.com/vegaro))

###
[`v5.2.3`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#523)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.2.2...5.2.3)

##### Bugfixes

- Fix Paywalls crash on iOS 18 beta
([#&#8203;4154](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4154))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))

##### Dependency Updates

- Bump danger from 9.4.3 to 9.5.0
([#&#8203;4143](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4143))
via dependabot\[bot]
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])
- Bump nokogiri from 1.16.6 to 1.16.7
([#&#8203;4129](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4129))
via dependabot\[bot]
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])
- Bump fastlane from 2.221.1 to 2.222.0
([#&#8203;4130](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4130))
via dependabot\[bot]
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])

##### Other Changes

- Update deployment targets for tests
([#&#8203;4145](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4145))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))
- Deploy purchaserTester: clean up dry-run parameter
([#&#8203;4140](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4140))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))
- Clean up API Testers
([#&#8203;4141](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4141))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))
- More project structure cleanup
([#&#8203;4131](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4131))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))
- temporarily disables purchasetester deploy
([#&#8203;4133](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4133))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))
- Fix trigger all tests branch
([#&#8203;4135](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4135))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))
- Clean up XCWorkspace and testing apps
([#&#8203;4111](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4111))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))
- tests trigger: add target-branch parameter to trigger from the right
branch
([#&#8203;4121](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4121))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))
- Re-added the RevenueCatUI tests job on every commit
([#&#8203;4113](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4113))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))

###
[`v5.2.2`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#522-customercenteralpha3)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.2.1...5.2.2)

- Fix for disabled promo offer button
([#&#8203;4142](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4142))

###
[`v5.2.1`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#521-customercenteralpha1)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.2.0...5.2.1)

- Initial Customer Center Alpha Release

###
[`v5.2.0`](https://redirect.github.com/RevenueCat/purchases-ios-spm/blob/HEAD/CHANGELOG.md#520)

[Compare
Source](https://redirect.github.com/RevenueCat/purchases-ios-spm/compare/5.1.0...5.2.0)

##### New Features

- Added new paywall template to support multiple tiered subscriptions
([#&#8203;4022](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4022))
via Josh Holtz
([@&#8203;joshdholtz](https://redirect.github.com/joshdholtz))

##### Bugfixes

- Fix certain completion blocks not being dispatched on the main thread
([#&#8203;4058](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4058))
via Mark Villacampa
([@&#8203;MarkVillacampa](https://redirect.github.com/MarkVillacampa))
- Only checks staged files for leftover API keys.
([#&#8203;4073](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4073))
via JayShortway
([@&#8203;JayShortway](https://redirect.github.com/JayShortway))

##### Other Changes

- \[Ci] Fix trigger to run all tests from github actions
([#&#8203;4075](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4075))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))
- added new workflow to trigger all tests
([#&#8203;4051](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4051))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))
- Reduce CI jobs
([#&#8203;4025](https://redirect.github.com/RevenueCat/purchases-ios-spm/issues/4025))
via Andy Boedo ([@&#8203;aboedo](https://redirect.github.com/aboedo))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi45Mi4xIiwidXBkYXRlZEluVmVyIjoiNDIuOTIuMSIsInRhcmdldEJyYW5jaCI6ImNhbmFyeSIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-26 05:44:47 +08:00
DarkSky
09cc2dceda feat: cleanup chat panel (#14259)
#### PR Dependency Tree


* **PR #14258**
  * **PR #14259** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Split AI initialization into separate editor, app, and shared
registries; removed legacy chat-panel and replaced it with a
component-based editor chat, updating wiring and public exports.
* Propagated server/subscription/model services into chat/playground
components and improved session lifecycle and UI composition.

* **Tests**
* Added tests for AI effect registration and chat session resolution;
extended DOM/test utilities and assertions.

* **Chores**
  * Added happy-dom for runtime and test environments.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-26 04:02:07 +08:00
Shyim
02449026b9 fix: add expose port 3010 in Dockerfile (#14293)
Expose is requierd for automatic port finding with load balancers like
Traefik without having to configure the port explict.

> error="service \"affine-affine\" error: port is missing"
container=affine-affine-a76ca4362da101be5a53279db7aac67595a9df0783b0026efc3e5431009cbd66

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
  * Updated deployment configuration for container port exposure.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-25 17:35:15 +08:00
Peng Xiao
056f2c1161 fix: should allow affine.pro to be embedded (#14285) 2026-01-20 23:33:28 +08:00
likljn
94431df236 fix(core): make member popover responsive (#14277)
## Problem
In Kanban view, clicking the `Empty` value for the Member property can
cause the member popover layout to overflow/break due to a fixed
container width.

### Before ScreenShot
<img width="410" height="119" alt="image"
src="https://github.com/user-attachments/assets/e0d28a37-2ea4-4a65-afca-e4ef10af37dd"
/>

## Repro
1. Open a Database in Kanban view
2. Ensure the Member property has some cards with `Empty`
3. Click the `Empty` member value
4. Observe the popover layout overflow/broken UI

## Solution
Make the popover container responsive by using `width: 100%` with
`maxWidth: 415px` to preserve the original intended size while
preventing overflow in narrow containers.

### After ScreenShot
<img width="410" height="146" alt="image (1)"
src="https://github.com/user-attachments/assets/d97e6b8b-eabc-499a-9f04-0422505c67bf"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Style**
* Improved member selector popover styling: unified padding, made width
responsive with a max width limit, and added box-sizing for more
consistent layout and spacing across screen sizes.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-18 09:37:08 +00:00
DarkSky
f373e08583 feat: refactor doc write in native (#14272) 2026-01-18 16:31:12 +08:00
Gabriele
753b11deeb fix: resolve navbar overlay issue on sign-in page (#14274)
This pr fixes #14273.

I have implemented two minor CSS adjustments to resolve the navbar
interaction issue on the sign-in page:

- Removed position: relative and z-index: 1 from signInPageContainer.
- Set z-index: 1 on the SignInPanel div (prevent SignInBackgroundArts
from overlapping the SignInPanel)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Style**
  * Adjusted z-index layering for the sign-in page component.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-17 17:32:42 +00:00
Heera Rana
17f2ebc4de feat: render document header image from document metadata (#14255)
### What
Adds support for rendering an optional image above the document title
using document metadata.

### Why
Provides a visual identifier for documents and improves readability for
users who rely on visual cues.

### How
- Reads `headerImage` from document metadata (if present)
- Renders the image above the editor when present
- Fully optional and non-breaking
- No BlockSuite or data model changes

### Related
fix #14240


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Documents can now display header images in the page editor. When a
header image is available, it appears above the editor content,
enhancing visual presentation and providing better context for your
documents.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-16 14:18:59 +00:00
realies
0da91e406e feat(server): add document write tools for mcp (#14245)
## Summary

This PR adds write capabilities to AFFiNE's MCP (Model Context Protocol)
integration, enabling external tools (Claude, GPT, etc.) to create and
modify documents programmatically.

**New MCP Tools:**
- `create_document` - Create new documents from markdown content
- `update_document` - Update document content using structural diffing
for minimal changes (preserves document history and enables real-time
collaboration)

**Implementation:**
- `markdown_to_ydoc.rs` - Converts markdown to AFFiNE-compatible y-octo
binary format
- `markdown_utils.rs` - Shared markdown parsing utilities (used by both
ydoc-to-md and md-to-ydoc)
- `update_ydoc.rs` - Structural diffing implementation for updating
existing documents
- `DocWriter` service - TypeScript service for document operations
- Exposes `markdownToDocBinary` and `updateDocBinary` via napi bindings

**Supported Markdown Elements:**
- Headings (H1-H6)
- Paragraphs
- Bullet lists and numbered lists
- Code blocks (with language detection)
- Blockquotes
- Horizontal dividers
- Todo items (checkboxes)

**y-octo Changes:**
This PR reverts the y-octo sync (ca2462f, a5b60cf) which introduced a
concurrency bug causing hangs when creating documents with many nested
block structures. It also ports the improved `get_node_index` binary
search fix from upstream that prevents divide-by-zero panics when
decoding documents.

## Test Results 

### Unit Tests (47/47 passing)

| Test Suite | Tests | Status |
|------------|-------|--------|
| markdown_to_ydoc | 16/16 |  Pass |
| markdown_utils | 11/11 |  Pass |
| update_ydoc | 13/13 |  Pass |
| delta_markdown | 2/2 |  Pass |
| affine (doc parser) | 5/5 |  Pass |

### End-to-End MCP Testing 

Tested against local AFFiNE server with real MCP client requests:

| Tool | Result | Notes |
|------|--------|-------|
| `tools/list` |  Pass | Returns all 5 tools with correct schemas |
| `create_document` |  Pass | Successfully created test documents |
| `update_document` |  Pass | Successfully updated documents with
structural diffing |
| `read_document` |  Pass | Existing tool, works correctly |
| `keyword_search` |  Pass | Existing tool, works correctly |

**E2E Test Details:**
- Started local AFFiNE server with PostgreSQL, Redis, and Manticore
- Created test user and workspace via seed/GraphQL
- Verified MCP endpoint at `/api/workspaces/:workspaceId/mcp`
- Tested JSON-RPC calls with proper SSE streaming
- Confirmed documents are stored and indexed correctly (verified via
server logs)

## Test Plan
- [x] All Rust unit tests pass (47 tests)
- [x] Native bindings build successfully (release mode)
- [x] Document creation via MCP works end-to-end
- [x] Document update via MCP works end-to-end
- [x] CodeRabbit feedback addressed
- [ ] Integration testing with Claude/GPT MCP clients

Closes #14161

---

**Requested by:** @realies  
**Key guidance from:** @darkskygit (use y-octo instead of yjs for memory
efficiency)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Create documents from Markdown: generate new documents directly from
Markdown content with automatic title extraction
* Update documents with Markdown: modify existing documents using
Markdown as the source with automatic diff calculation for efficient
updates
* Copilot integration: new tools for document creation and updates
through Copilot's interface

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-16 20:57:24 +08:00
DarkSky
2c5559ed0b fix: missing session fields 2026-01-16 17:26:05 +08:00
DarkSky
924d58603f chore: improve event flow (#14266) 2026-01-16 16:07:27 +08:00
DarkSky
d4581b839a chore: add tools 2026-01-16 10:53:13 +08:00
DarkSky
8d14607c2b feat: improve indexer perf 2026-01-15 19:27:41 +08:00
DarkSky
00a458543f feat: cleanup chat panel (#14258)
#### PR Dependency Tree


* **PR #14258** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **Removed Features**
* Web search functionality has been removed from AI chat and related AI
features. Users will no longer see network search options or toggles in
chat preferences and panels.
  * AI chat requests no longer support external web search capabilities.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


#### PR Dependency Tree


* **PR #14258** 👈
  * **PR #14259**

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)
2026-01-15 19:20:55 +08:00
DarkSky
ac7a95e708 feat: misc optimizations (#14257)
fix #13798
2026-01-15 03:47:22 +08:00
DarkSky
76e1721d70 fix: journal conflict handle 2026-01-15 00:55:35 +08:00
DarkSky
fc59dff9e2 chore: enable blur background for mac 2026-01-15 00:35:11 +08:00
DarkSky
27a58e764c chore: bump version & deps 2026-01-15 00:33:51 +08:00
DarkSky
13907f7234 fix(core): event flow handle (#14256) 2026-01-15 00:04:32 +08:00
DarkSky
7c24b2521a feat: reduce backend (#14251)
#### PR Dependency Tree


* **PR #14251** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Current user profile now exposes access tokens, revealed tokens, and
detailed calendar accounts/subscriptions.
* Workspace now exposes permissions, calendars, calendar events, and a
workspace-scoped blob upload part URL.
  * New document-update mutation for applying doc updates.

* **API Changes**
  * validateAppConfig is now a query (mutation deprecated).
* Several legacy top-level calendar/blob endpoints deprecated in favor
of user/workspace fields.

* **Refactor**
* Calendar, blob-upload and access-token surfaces reorganized to use
user/workspace-centric fields.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-14 00:01:07 +08:00
github-actions[bot]
7c440686ad chore(i18n): sync translations (#14148)
New Crowdin translations by [Crowdin GH
Action](https://github.com/crowdin/github-action)

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: DarkSky <darksky2048@gmail.com>
2026-01-13 22:09:42 +08:00
DarkSky
b331a08744 feat: native update merge (#14250)
#### PR Dependency Tree


* **PR #14250** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Backend Optimization**
  * Faster document retrieval via a native binary fetch path.
* Native-accelerated merging of document updates for improved
performance and consistency.
* **Indexing & Reliability**
* Indexing now only proceeds on valid parse results, with clearer
warnings and richer metadata on failures.
* More consistent sync behavior and enhanced diagnostic logging for
indexing operations.
* **Tests**
  * Expanded tests to cover native binary retrieval error handling.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-13 22:03:55 +08:00
DarkSky
279b7bb64f feat(core): integrate google calendar sync (#14248)
fix #14170 
fix #13893 
fix #13673 
fix #13543 
fix #13308 
fix #7607




#### PR Dependency Tree


* **PR #14247**
  * **PR #14248** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Integrations panel in Account Settings to link/unlink calendar
providers.
  * Collapsible settings wrapper for improved layout.

* **Improvements**
* Calendar system reworked: per-account calendar groups, simplified
toggles with explicit Save, richer event display (multi-dot date
indicators), improved event time/title handling across journal views.

* **Localization**
* Added calendar keys: save-error, no-journal, no-calendar; removed
legacy duplicate-error keys.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-13 02:38:16 +08:00
DarkSky
89f0430242 fix: race conditions (y-crdt/y-octo#51) 2026-01-13 01:10:07 +08:00
DarkSky
0bd8160ed4 feat: init cloud calendar support (#14247)
#### PR Dependency Tree


* **PR #14247** 👈
  * **PR #14248**

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Google Calendar integration (disabled by default): link/unlink
accounts, OAuth flow, webhooks, real-time push, background sync,
workspace calendars with customizable items and date-range event
viewing.
* **GraphQL / Client**
* New queries & mutations for accounts, subscriptions, events,
providers, and workspace calendar management.
* **Localization**
* Added localized error message for calendar provider request failures.
* **Tests**
* Backend tests covering sync, webhook renewal, and error/error-recovery
scenarios.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-12 23:17:43 +08:00
DarkSky
a5b60cf679 fix: index calc & detached node handle (y-crdt/y-octo#50) 2026-01-11 18:44:55 +08:00
DarkSky
ca2462f987 feat(native): sync yocto codes (#14243)
#### PR Dependency Tree


* **PR #14243** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Batch management API for coordinated document mutations and change
tracking.
* New document accessors (IDs, state snapshots, change/delete set
queries) and subscriber count.

* **Chores**
  * Upgraded Rust edition across packages to 2024.
  * Repository-wide formatting, stylistic cleanups and test adjustments.

* **Breaking Changes**
* Removed the Node native bindings package and its JS/TS declarations
and tests (no longer published/available).

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-11 06:08:33 +08:00
Adit Syed Afnan
d515d295ce feat(editor): enhance string comparison handling in eval.ts (#14233)
Refactors the compareString function to safely handle null and undefined
inputs and improves overall string comparison logic. This prevents
incorrect sort behavior and ensures consistent ordering when comparing
mixed or missing values, particularly in table view sorting scenarios.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved string comparison used for sorting: empty values are
consistently placed last, numeric parts sort numerically before
non-numeric parts, and mixed-type and case variations are handled more
predictably for stable, consistent ordering across data views.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-09 02:58:00 +00:00
DarkSky
e4dc82ee35 chore: bump deps (#14227)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Updated backend service dependencies to the latest stable versions for
improved performance and security.
* Upgraded UI component library dependencies to the latest minor
releases.

* **Improvements**
* Enhanced web search functionality for better search results on
standard AI models.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-07 13:15:17 +08:00
renovate[bot]
aa6f26b1a5 chore: bump up opentelemetry (#14208)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
|
[@opentelemetry/instrumentation-ioredis](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/tree/main/packages/instrumentation-ioredis#readme)
([source](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/tree/HEAD/packages/instrumentation-ioredis))
| [`^0.56.0` →
`^0.57.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-ioredis/0.56.0/0.57.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation-ioredis/0.57.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation-ioredis/0.56.0/0.57.0?slim=true)
|
|
[@opentelemetry/instrumentation-socket.io](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/tree/main/packages/instrumentation-socket.io#readme)
([source](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/tree/HEAD/packages/instrumentation-socket.io))
| [`0.55.0` →
`0.55.1`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-socket.io/0.55.0/0.55.1)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation-socket.io/0.55.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation-socket.io/0.55.0/0.55.1?slim=true)
|

---

### Release Notes

<details>
<summary>open-telemetry/opentelemetry-js-contrib
(@&#8203;opentelemetry/instrumentation-ioredis)</summary>

###
[`v0.57.0`](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/blob/HEAD/packages/instrumentation-ioredis/CHANGELOG.md#0570-2025-12-17)

[Compare
Source](94e5b7da45...66935ac724)

##### Features

- **instrumentations-ioredis:** support `net.*` and database semconv
migration
([#&#8203;3266](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/issues/3266))
([9f92c8b](9f92c8b5b1))

##### Dependencies

- The following workspace dependencies were updated
  - devDependencies
-
[@&#8203;opentelemetry/contrib-test-utils](https://redirect.github.com/opentelemetry/contrib-test-utils)
bumped from ^0.55.0 to ^0.56.0

</details>

<details>
<summary>open-telemetry/opentelemetry-js-contrib
(@&#8203;opentelemetry/instrumentation-socket.io)</summary>

###
[`v0.55.1`](https://redirect.github.com/open-telemetry/opentelemetry-js-contrib/blob/HEAD/packages/instrumentation-socket.io/CHANGELOG.md#0551-2025-12-17)

[Compare
Source](94e5b7da45...66935ac724)

##### Dependencies

- The following workspace dependencies were updated
  - devDependencies
-
[@&#8203;opentelemetry/contrib-test-utils](https://redirect.github.com/opentelemetry/contrib-test-utils)
bumped from ^0.55.0 to ^0.56.0

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi42OS4xIiwidXBkYXRlZEluVmVyIjoiNDIuNjkuMSIsInRhcmdldEJyYW5jaCI6ImNhbmFyeSIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-07 11:45:15 +08:00
likljn
c1d43b9b18 fix(editor): keep slash menu alive on text input when no_result (#14141)
**Problem**
Slash menu can be prematurely aborted when the query is still in
`no_result`
due to async query updates after deletion.

**Solution**
Keep the slash menu alive on text input while in `no_result`,
preventing aborts based on a stale query state.

**Repro**
1. Type `/eeee`
2. Delete to `/`
3. Type `h`
4. Slash menu should recover and show results


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Enhanced slash-menu keyboard interaction: users can now continue
typing to refine queries when no results are displayed, instead of the
menu closing unexpectedly. Keyboard navigation and other controls remain
responsive.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
2026-01-07 02:13:13 +00:00
DarkSky
b8e597fa1d fix: hide search local label if need 2026-01-07 10:42:51 +08:00
Cats Juice
cf98afb32e chore: bump theme@1.1.23 (#14222)
close #13952

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Upgraded the shared theme library from v1.1.16 to v1.1.23 across the
project (core components, UI widgets, content blocks, and frontend
apps), delivering the latest styling and design refinements
platform-wide.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: L-Sun <zover.v@gmail.com>
2026-01-06 20:48:44 +08:00
Yiding Jia
a11e9fe8ca feat(server): add LISTEN_ADDR env var for allowing server to listen on ipv6 (#14211)
The old code hardcoded 0.0.0.0 which means the server only listened for
ipv4 connections, making it not work on ipv6-only networks.

This change adds a LISTEN_ADDR env var which allows the server to bind
to ipv6 as well.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Server listen address is now configurable via the LISTEN_ADDR
environment variable (default: 0.0.0.0), enabling IPv4/IPv6 or
interface-specific binding.
* Configuration schemas and admin UI now expose the listen address
option so deployments can view and override it.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-05 09:31:47 +00:00
DarkSky
f42246aba1 fix: allow method for cors 2026-01-05 13:14:56 +08:00
Whitewater
f5394b7450 fix: refine handling for non-standard keyboards to avoid incorrect keyCode fallback (#14206)
Fix https://github.com/toeverything/AFFiNE/issues/14059

With the help of Claude Opus 4.5

Improve handling of keyCode fallback for non-standard keyboards by only
applying it when modifier keys are pressed. This change prevents
incorrect fallback behavior for non-ASCII characters, ensuring users can
type intended characters without triggering shortcuts.

After


https://github.com/user-attachments/assets/00ab4fb2-4bc2-4ca7-a284-9782686d298c

Event dump for Cyrillic x

```json

{
 "key": "х",
 "keyCode": 219,
 "which": 219,
 "code": "BracketLeft",
 "location": 0,
 "altKey": false,
 "ctrlKey": false,
 "metaKey": false,
 "shiftKey": false,
 "repeat": false
}
```

blocksuite commit
4c0d39890f (diff-68c46455e0eece88312235df85f8ce27ae254efccde6fb987f2505180730bd8c)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Refined keyboard input handling to properly support non-ASCII
characters (e.g., Cyrillic, Greek) by ensuring user-typed characters are
preserved instead of inadvertently triggering keyboard shortcuts. The
fix maintains keyboard shortcut functionality while improving
compatibility with international keyboards and input methods.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-04 09:18:03 +00:00
DarkSky
e7d0f31546 fix: blob redirect 2026-01-04 00:23:51 +08:00
DarkSky
fe5d6c0c0f feat(editor): support frontmatter & colored text parsing (#14205)
fix #13847
2026-01-03 22:43:11 +08:00
Yiding Jia
510933becf chore(server): bump ioredis to 5.8.2 for ipv6 support (#14204)
Bump ioredis to 5.8.2 for ipv6 support. 

Prior to 5.8.2 ioredis required passing `family: 0` or `family: 6` when
constructing a client in order to connect to redis over ipv6. This was
fixed in 5.8.2.

fix #14197
2026-01-03 01:06:30 +00:00
DarkSky
3633c75c6f feat: cleanup tables (#14203)
#### PR Dependency Tree


* **PR #14203** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Removed deprecated database tables, enums and schema fields (cleanup
of legacy subscription, invoice, runtime settings and session expiry
data). This includes irreversible data removal for those legacy
elements.
* **Tests**
* Updated tests and test data to align with the cleaned-up schema and
removed fields.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-03 03:50:14 +08:00
DarkSky
41addfe311 fix: blob sync 2026-01-03 01:40:13 +08:00
DarkSky
9a7f8e7d4d feat: workspace level share settings (#14201)
fix #13698
2026-01-03 01:13:27 +08:00
DarkSky
60de882a30 feat: shared link list (#14200)
#### PR Dependency Tree


* **PR #14200** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **New Features**
* Added a "Shared Links" panel to workspace management, enabling admins
to view all published documents within a workspace
* Added publication date tracking for published documents, now displayed
alongside shared links

* **Chores**
  * Removed deprecated `publicPages` field; use `publicDocs` instead

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-02 21:07:41 +08:00
zetaloop
9f96633b33 fix: normalize shortcut display (#14196)
Normalize shortcut tokens and remove stray whitespace.
Uncomment group/ungroup shortcuts now that the feature is implemented.
Fix Windows redo shortcut display.

<img width="142" height="230" alt="image"
src="https://github.com/user-attachments/assets/989e061e-1ca2-489c-ab8e-6baad853d438"
/><img width="142" height="37" alt="image"
src="https://github.com/user-attachments/assets/671ed9b2-ccad-44ad-8889-7810bb01143c"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Standardized keyboard shortcut representations across the application
for improved consistency and clarity in shortcut displays.
* Corrected spacing inconsistencies in shortcut entries to ensure
uniform formatting.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
2026-01-01 13:07:35 +00:00
DarkSky
1e8095c224 fix: ci 2026-01-01 18:20:18 +08:00
DarkSky
0b0ae5ea0a feat: add queue management for admin panel 2026-01-01 06:13:50 +08:00
DarkSky
f745f7b669 feat: pre-aggregation workspace stats 2026-01-01 05:01:52 +08:00
DarkSky
97507e7043 fix: query type cast 2026-01-01 02:41:03 +08:00
DarkSky
91e6f3c45c feat: improve workspace index 2026-01-01 01:39:40 +08:00
DarkSky
c7b74384a4 fix: types 2025-12-31 23:55:51 +08:00
DarkSky
20c4951847 feat: improve workspace list perf 2025-12-31 22:59:52 +08:00
DarkSky
bc03fab649 chore: cleanup logs 2025-12-31 12:31:58 +08:00
DarkSky
99332228da feat: native sync state (#14190)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added indexed clock management capabilities for documents, enabling
get, set, and clear operations across Android, iOS, Electron, and web
platforms.

* **Refactor**
* Improved storage architecture to dynamically select platform-specific
implementations (SQLite for Electron, IndexedDB for others).

* **Bug Fixes**
* Enhanced document operations to properly maintain and clean up indexer
synchronization state during document lifecycle changes.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-31 04:09:32 +08:00
DarkSky
95ef04f3e0 fix: cloud workspace search prefer & highlights 2025-12-30 12:46:43 +08:00
DarkSky
e2adab7805 fix: table compatibility (#14185) 2025-12-30 12:01:27 +08:00
DarkSky
30fb953344 fix: build 2025-12-30 05:41:55 +08:00
DarkSky
ff2e96d847 feat: database indexing support (#14181) 2025-12-30 05:23:09 +08:00
DarkSky
95a5e941e7 feat: improve admin panel (#14180) 2025-12-30 05:22:54 +08:00
DarkSky
d6b380aee5 feat: improve pdf rendering (#14171)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Bitmap caching for PDF pages to speed up rendering and reduce repeated
work.
* Automatic prefetching of adjacent pages and expanded viewport overscan
for smoother scrolling.

* **Performance**
* LRU-style in-memory cache with eviction to manage memory and improve
responsiveness.
* Reusable-bitmap lookup and error-tolerant fallbacks for more reliable,
faster page display.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-29 22:01:07 +08:00
Yii
1b9d065778 chore(yocto): should auto gc after applying updates (#12199)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Improved document update handling to optimize storage automatically
when garbage collection is enabled.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
2025-12-29 21:48:23 +08:00
DarkSky
e12fe9c12b fix: message handle (#14178)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Robustly sanitize session titles, messages, attachments, and embedded
data to remove invalid/null characters and prevent corrupt persistence.
* Improve chat title generation to skip or recover from invalid input
and log contextual errors without crashing.
* Add more detailed storage and workspace logs and reduce repetitive
checks to aid troubleshooting and stability.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-29 21:47:53 +08:00
DarkSky
1bfd29df99 docs: update docs 2025-12-29 19:08:49 +08:00
renovate[bot]
a38e94f314 chore: bump up Node.js to v22.21.1 (#14175)
> **Note:** This PR body was truncated due to platform limits.

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [node](https://nodejs.org)
([source](https://redirect.github.com/nodejs/node)) | minor | `22.16.0`
-> `22.21.1` |

---

### Release Notes

<details>
<summary>nodejs/node (node)</summary>

###
[`v22.21.1`](https://redirect.github.com/nodejs/node/releases/tag/v22.21.1):
2025-10-28, Version 22.21.1 &#x27;Jod&#x27; (LTS), @&#8203;aduh95

[Compare
Source](https://redirect.github.com/nodejs/node/compare/v22.21.0...v22.21.1)

##### Commits

-
\[[`af33e8e668`](https://redirect.github.com/nodejs/node/commit/af33e8e668)]
- **benchmark**: remove unused variable from util/priority-queue (Bruno
Rodrigues)
[#&#8203;59872](https://redirect.github.com/nodejs/node/pull/59872)
-
\[[`6764ce8756`](https://redirect.github.com/nodejs/node/commit/6764ce8756)]
- **benchmark**: update count to n in permission startup (Bruno
Rodrigues)
[#&#8203;59872](https://redirect.github.com/nodejs/node/pull/59872)
-
\[[`4e8d99f0dc`](https://redirect.github.com/nodejs/node/commit/4e8d99f0dc)]
- **benchmark**: update num to n in dgram offset-length (Bruno
Rodrigues)
[#&#8203;59872](https://redirect.github.com/nodejs/node/pull/59872)
-
\[[`af0a8ba7f8`](https://redirect.github.com/nodejs/node/commit/af0a8ba7f8)]
- **benchmark**: adjust dgram offset-length len values (Bruno Rodrigues)
[#&#8203;59708](https://redirect.github.com/nodejs/node/pull/59708)
-
\[[`78efd1be4a`](https://redirect.github.com/nodejs/node/commit/78efd1be4a)]
- **benchmark**: update num to n in dgram offset-length (Bruno
Rodrigues)
[#&#8203;59708](https://redirect.github.com/nodejs/node/pull/59708)
-
\[[`df72dc96e9`](https://redirect.github.com/nodejs/node/commit/df72dc96e9)]
- **console,util**: improve array inspection performance (Ruben
Bridgewater)
[#&#8203;60037](https://redirect.github.com/nodejs/node/pull/60037)
-
\[[`ef67d09f50`](https://redirect.github.com/nodejs/node/commit/ef67d09f50)]
- **http**: improve writeEarlyHints by avoiding for-of loop (Haram
Jeong)
[#&#8203;59958](https://redirect.github.com/nodejs/node/pull/59958)
-
\[[`23468fd76b`](https://redirect.github.com/nodejs/node/commit/23468fd76b)]
- **http2**: fix allowHttp1+Upgrade, broken by shouldUpgradeCallback
(Tim Perry)
[#&#8203;59924](https://redirect.github.com/nodejs/node/pull/59924)
-
\[[`56abc4ac76`](https://redirect.github.com/nodejs/node/commit/56abc4ac76)]
- **lib**: optimize priority queue (Gürgün Dayıoğlu)
[#&#8203;60039](https://redirect.github.com/nodejs/node/pull/60039)
-
\[[`ea5cfd98c5`](https://redirect.github.com/nodejs/node/commit/ea5cfd98c5)]
- **lib**: implement passive listener behavior per spec (BCD1me)
[#&#8203;59995](https://redirect.github.com/nodejs/node/pull/59995)
-
\[[`c2dd6eed2f`](https://redirect.github.com/nodejs/node/commit/c2dd6eed2f)]
- **process**: fix wrong asyncContext under unhandled-rejections=strict
(Shima Ryuhei)
[#&#8203;60103](https://redirect.github.com/nodejs/node/pull/60103)
-
\[[`81a3055710`](https://redirect.github.com/nodejs/node/commit/81a3055710)]
- **process**: fix default `env` for `process.execve` (Richard Lau)
[#&#8203;60029](https://redirect.github.com/nodejs/node/pull/60029)
-
\[[`fe492c7ace`](https://redirect.github.com/nodejs/node/commit/fe492c7ace)]
- **process**: fix hrtime fast call signatures (Renegade334)
[#&#8203;59600](https://redirect.github.com/nodejs/node/pull/59600)
-
\[[`76b4cab8fc`](https://redirect.github.com/nodejs/node/commit/76b4cab8fc)]
- **src**: bring permissions macros in line with general C/C++ standards
(Anna Henningsen)
[#&#8203;60053](https://redirect.github.com/nodejs/node/pull/60053)
-
\[[`21970970c7`](https://redirect.github.com/nodejs/node/commit/21970970c7)]
- **src**: remove `AnalyzeTemporaryDtors` option from .clang-tidy
(iknoom)
[#&#8203;60008](https://redirect.github.com/nodejs/node/pull/60008)
-
\[[`609c063e81`](https://redirect.github.com/nodejs/node/commit/609c063e81)]
- **src**: remove unused variables from report (Moonki Choi)
[#&#8203;60047](https://redirect.github.com/nodejs/node/pull/60047)
-
\[[`987841a773`](https://redirect.github.com/nodejs/node/commit/987841a773)]
- **src**: avoid unnecessary string allocations in SPrintF impl (Anna
Henningsen)
[#&#8203;60052](https://redirect.github.com/nodejs/node/pull/60052)
-
\[[`6e386c0632`](https://redirect.github.com/nodejs/node/commit/6e386c0632)]
- **src**: make ToLower/ToUpper input args more flexible (Anna
Henningsen)
[#&#8203;60052](https://redirect.github.com/nodejs/node/pull/60052)
-
\[[`c3be1226c7`](https://redirect.github.com/nodejs/node/commit/c3be1226c7)]
- **src**: allow `std::string_view` arguments to `SPrintF()` and friends
(Anna Henningsen)
[#&#8203;60058](https://redirect.github.com/nodejs/node/pull/60058)
-
\[[`764d35647d`](https://redirect.github.com/nodejs/node/commit/764d35647d)]
- **src**: remove unnecessary `std::string` error messages (Anna
Henningsen)
[#&#8203;60057](https://redirect.github.com/nodejs/node/pull/60057)
-
\[[`1289ef89ec`](https://redirect.github.com/nodejs/node/commit/1289ef89ec)]
- **src**: remove unnecessary shadowed functions on Utf8Value &
BufferValue (Anna Henningsen)
[#&#8203;60056](https://redirect.github.com/nodejs/node/pull/60056)
-
\[[`d1fb8a538d`](https://redirect.github.com/nodejs/node/commit/d1fb8a538d)]
- **src**: avoid unnecessary string -> `char*` -> string round trips
(Anna Henningsen)
[#&#8203;60055](https://redirect.github.com/nodejs/node/pull/60055)
-
\[[`54b439fb5a`](https://redirect.github.com/nodejs/node/commit/54b439fb5a)]
- **src**: fill `options_args`, `options_env` after vectors are
finalized (iknoom)
[#&#8203;59945](https://redirect.github.com/nodejs/node/pull/59945)
-
\[[`c7c597e2ca`](https://redirect.github.com/nodejs/node/commit/c7c597e2ca)]
- **src**: use RAII for uv\_process\_options\_t (iknoom)
[#&#8203;59945](https://redirect.github.com/nodejs/node/pull/59945)
-
\[[`b928ea9716`](https://redirect.github.com/nodejs/node/commit/b928ea9716)]
- **test**: ensure that the message event is fired (Luigi Pinca)
[#&#8203;59952](https://redirect.github.com/nodejs/node/pull/59952)
-
\[[`e4b95a5158`](https://redirect.github.com/nodejs/node/commit/e4b95a5158)]
- **test**: replace diagnostics\_channel stackframe in output snapshots
(Chengzhong Wu)
[#&#8203;60024](https://redirect.github.com/nodejs/node/pull/60024)
-
\[[`4206406694`](https://redirect.github.com/nodejs/node/commit/4206406694)]
- **test**: mark test-web-locks skip on IBM i (SRAVANI GUNDEPALLI)
[#&#8203;59996](https://redirect.github.com/nodejs/node/pull/59996)
-
\[[`26394cd5bf`](https://redirect.github.com/nodejs/node/commit/26394cd5bf)]
- **test**: expand tls-check-server-identity coverage (Diango Gavidia)
[#&#8203;60002](https://redirect.github.com/nodejs/node/pull/60002)
-
\[[`b58df47995`](https://redirect.github.com/nodejs/node/commit/b58df47995)]
- **test**: fix typo of test-benchmark-readline.js (Deokjin Kim)
[#&#8203;59993](https://redirect.github.com/nodejs/node/pull/59993)
-
\[[`af3a59dba8`](https://redirect.github.com/nodejs/node/commit/af3a59dba8)]
- **test**: verify tracing channel doesn't swallow unhandledRejection
(Gerhard Stöbich)
[#&#8203;59974](https://redirect.github.com/nodejs/node/pull/59974)
-
\[[`cee362242b`](https://redirect.github.com/nodejs/node/commit/cee362242b)]
- **timers**: fix binding fast call signatures (Renegade334)
[#&#8203;59600](https://redirect.github.com/nodejs/node/pull/59600)
-
\[[`40fea57fdd`](https://redirect.github.com/nodejs/node/commit/40fea57fdd)]
- **tools**: add message on auto-fixing js lint issues in gh workflow
(Dario Piotrowicz)
[#&#8203;59128](https://redirect.github.com/nodejs/node/pull/59128)
-
\[[`aac90d351b`](https://redirect.github.com/nodejs/node/commit/aac90d351b)]
- **tools**: verify signatures when updating nghttp\* (Antoine du Hamel)
[#&#8203;60113](https://redirect.github.com/nodejs/node/pull/60113)
-
\[[`9fae03c7d9`](https://redirect.github.com/nodejs/node/commit/9fae03c7d9)]
- **tools**: use dependabot cooldown and move tools/doc (Rafael Gonzaga)
[#&#8203;59978](https://redirect.github.com/nodejs/node/pull/59978)
-
\[[`81548abdf6`](https://redirect.github.com/nodejs/node/commit/81548abdf6)]
- **wasi**: fix WasiFunction fast call signature (Renegade334)
[#&#8203;59600](https://redirect.github.com/nodejs/node/pull/59600)

###
[`v22.21.0`](https://redirect.github.com/nodejs/node/releases/tag/v22.21.0):
2025-10-20, Version 22.21.0 &#x27;Jod&#x27; (LTS), @&#8203;aduh95

[Compare
Source](https://redirect.github.com/nodejs/node/compare/v22.20.0...v22.21.0)

##### Notable Changes

-
\[[`1486fedea1`](https://redirect.github.com/nodejs/node/commit/1486fedea1)]
- **(SEMVER-MINOR)** **cli**: add `--use-env-proxy` (Joyee Cheung)
[#&#8203;59151](https://redirect.github.com/nodejs/node/pull/59151)
-
\[[`bedaaa11fc`](https://redirect.github.com/nodejs/node/commit/bedaaa11fc)]
- **(SEMVER-MINOR)** **http**: support http proxy for fetch under
`NODE_USE_ENV_PROXY` (Joyee Cheung)
[#&#8203;57165](https://redirect.github.com/nodejs/node/pull/57165)
-
\[[`af8b5fa29d`](https://redirect.github.com/nodejs/node/commit/af8b5fa29d)]
- **(SEMVER-MINOR)** **http**: add `shouldUpgradeCallback` to let
servers control HTTP upgrades (Tim Perry)
[#&#8203;59824](https://redirect.github.com/nodejs/node/pull/59824)
-
\[[`42102594b1`](https://redirect.github.com/nodejs/node/commit/42102594b1)]
- **(SEMVER-MINOR)** **http,https**: add built-in proxy support in
`http`/`https.request` and `Agent` (Joyee Cheung)
[#&#8203;58980](https://redirect.github.com/nodejs/node/pull/58980)
-
\[[`686ac49b82`](https://redirect.github.com/nodejs/node/commit/686ac49b82)]
- **(SEMVER-MINOR)** **src**: add percentage support to
`--max-old-space-size` (Asaf Federman)
[#&#8203;59082](https://redirect.github.com/nodejs/node/pull/59082)

##### Commits

-
\[[`a71dd592e3`](https://redirect.github.com/nodejs/node/commit/a71dd592e3)]
- **benchmark**: calibrate config dgram multi-buffer (Bruno Rodrigues)
[#&#8203;59696](https://redirect.github.com/nodejs/node/pull/59696)
-
\[[`16c4b466f4`](https://redirect.github.com/nodejs/node/commit/16c4b466f4)]
- **benchmark**: calibrate config cluster/echo.js (Nam Yooseong)
[#&#8203;59836](https://redirect.github.com/nodejs/node/pull/59836)
-
\[[`53cb9f3b6c`](https://redirect.github.com/nodejs/node/commit/53cb9f3b6c)]
- **build**: add the missing macro definitions for OpenHarmony (hqzing)
[#&#8203;59804](https://redirect.github.com/nodejs/node/pull/59804)
-
\[[`ec5290fe01`](https://redirect.github.com/nodejs/node/commit/ec5290fe01)]
- **build**: do not include custom ESLint rules testing in tarball
(Antoine du Hamel)
[#&#8203;59809](https://redirect.github.com/nodejs/node/pull/59809)
-
\[[`1486fedea1`](https://redirect.github.com/nodejs/node/commit/1486fedea1)]
- **(SEMVER-MINOR)** **cli**: add --use-env-proxy (Joyee Cheung)
[#&#8203;59151](https://redirect.github.com/nodejs/node/pull/59151)
-
\[[`1f93913446`](https://redirect.github.com/nodejs/node/commit/1f93913446)]
- **crypto**: use `return await` when returning Promises from async
functions (Renegade334)
[#&#8203;59841](https://redirect.github.com/nodejs/node/pull/59841)
-
\[[`f488b2ff73`](https://redirect.github.com/nodejs/node/commit/f488b2ff73)]
- **crypto**: use async functions for non-stub Promise-returning
functions (Renegade334)
[#&#8203;59841](https://redirect.github.com/nodejs/node/pull/59841)
-
\[[`aed9fd5ac4`](https://redirect.github.com/nodejs/node/commit/aed9fd5ac4)]
- **crypto**: avoid calls to `promise.catch()` (Renegade334)
[#&#8203;59841](https://redirect.github.com/nodejs/node/pull/59841)
-
\[[`37c2d186f0`](https://redirect.github.com/nodejs/node/commit/37c2d186f0)]
- **deps**: update amaro to 1.1.4 (pmarchini)
[#&#8203;60044](https://redirect.github.com/nodejs/node/pull/60044)
-
\[[`28aea13419`](https://redirect.github.com/nodejs/node/commit/28aea13419)]
- **deps**: update archs files for openssl-3.5.4 (Node.js GitHub Bot)
[#&#8203;60101](https://redirect.github.com/nodejs/node/pull/60101)
-
\[[`ddbc1aa0bb`](https://redirect.github.com/nodejs/node/commit/ddbc1aa0bb)]
- **deps**: upgrade openssl sources to openssl-3.5.4 (Node.js GitHub
Bot) [#&#8203;60101](https://redirect.github.com/nodejs/node/pull/60101)
-
\[[`badbba2da9`](https://redirect.github.com/nodejs/node/commit/badbba2da9)]
- **deps**: update googletest to
[`50b8600`](https://redirect.github.com/nodejs/node/commit/50b8600)
(Node.js GitHub Bot)
[#&#8203;59955](https://redirect.github.com/nodejs/node/pull/59955)
-
\[[`48aaf98a08`](https://redirect.github.com/nodejs/node/commit/48aaf98a08)]
- **deps**: update archs files for openssl-3.5.3 (Node.js GitHub Bot)
[#&#8203;59901](https://redirect.github.com/nodejs/node/pull/59901)
-
\[[`e02a562ea6`](https://redirect.github.com/nodejs/node/commit/e02a562ea6)]
- **deps**: upgrade openssl sources to openssl-3.5.3 (Node.js GitHub
Bot) [#&#8203;59901](https://redirect.github.com/nodejs/node/pull/59901)
-
\[[`7e0e86cb92`](https://redirect.github.com/nodejs/node/commit/7e0e86cb92)]
- **deps**: upgrade npm to 10.9.4 (npm team)
[#&#8203;60074](https://redirect.github.com/nodejs/node/pull/60074)
-
\[[`91dda5facf`](https://redirect.github.com/nodejs/node/commit/91dda5facf)]
- **deps**: update undici to 6.22.0 (Matteo Collina)
[#&#8203;60112](https://redirect.github.com/nodejs/node/pull/60112)
-
\[[`3a3220a2f0`](https://redirect.github.com/nodejs/node/commit/3a3220a2f0)]
- **dgram**: restore buffer optimization in fixBufferList (Yoo)
[#&#8203;59934](https://redirect.github.com/nodejs/node/pull/59934)
-
\[[`09bdcce6b8`](https://redirect.github.com/nodejs/node/commit/09bdcce6b8)]
- **diagnostics\_channel**: fix race condition with diagnostics\_channel
and GC (Ugaitz Urien)
[#&#8203;59910](https://redirect.github.com/nodejs/node/pull/59910)
-
\[[`b3eeb3bd13`](https://redirect.github.com/nodejs/node/commit/b3eeb3bd13)]
- **doc**: provide alternative to `url.parse()` using WHATWG URL
(Steven)
[#&#8203;59736](https://redirect.github.com/nodejs/node/pull/59736)
-
\[[`1ddaab1904`](https://redirect.github.com/nodejs/node/commit/1ddaab1904)]
- **doc**: mention reverse proxy and include simple example (Steven)
[#&#8203;59736](https://redirect.github.com/nodejs/node/pull/59736)
-
\[[`3b3b71e99c`](https://redirect.github.com/nodejs/node/commit/3b3b71e99c)]
- **doc**: mark `.env` files support as stable (Santeri Hiltunen)
[#&#8203;59925](https://redirect.github.com/nodejs/node/pull/59925)
-
\[[`d37f67d1bd`](https://redirect.github.com/nodejs/node/commit/d37f67d1bd)]
- **doc**: remove optional title prefixes (Aviv Keller)
[#&#8203;60087](https://redirect.github.com/nodejs/node/pull/60087)
-
\[[`ca2dff63f9`](https://redirect.github.com/nodejs/node/commit/ca2dff63f9)]
- **doc**: fix typo on child\_process.md (Angelo Gazzola)
[#&#8203;60114](https://redirect.github.com/nodejs/node/pull/60114)
-
\[[`3fca564a05`](https://redirect.github.com/nodejs/node/commit/3fca564a05)]
- **doc**: add automated migration info to deprecations (Augustin
Mauroy)
[#&#8203;60022](https://redirect.github.com/nodejs/node/pull/60022)
-
\[[`4bc366fc16`](https://redirect.github.com/nodejs/node/commit/4bc366fc16)]
- **doc**: use "WebAssembly" instead of "Web Assembly" (Tobias Nießen)
[#&#8203;59954](https://redirect.github.com/nodejs/node/pull/59954)
-
\[[`4808dbdd9a`](https://redirect.github.com/nodejs/node/commit/4808dbdd9a)]
- **doc**: fix typo in section on microtask order (Tobias Nießen)
[#&#8203;59932](https://redirect.github.com/nodejs/node/pull/59932)
-
\[[`d6e303d645`](https://redirect.github.com/nodejs/node/commit/d6e303d645)]
- **doc**: update V8 fast API guidance (René)
[#&#8203;58999](https://redirect.github.com/nodejs/node/pull/58999)
-
\[[`0a3a3f729e`](https://redirect.github.com/nodejs/node/commit/0a3a3f729e)]
- **doc**: add security escalation policy (Ulises Gascón)
[#&#8203;59806](https://redirect.github.com/nodejs/node/pull/59806)
-
\[[`8fd669c70d`](https://redirect.github.com/nodejs/node/commit/8fd669c70d)]
- **doc**: type improvement of file `http.md` (yusheng chen)
[#&#8203;58189](https://redirect.github.com/nodejs/node/pull/58189)
-
\[[`9833dc6060`](https://redirect.github.com/nodejs/node/commit/9833dc6060)]
- **doc**: rephrase dynamic import() description (Nam Yooseong)
[#&#8203;59224](https://redirect.github.com/nodejs/node/pull/59224)
-
\[[`2870a73681`](https://redirect.github.com/nodejs/node/commit/2870a73681)]
- **doc,crypto**: update subtle.generateKey and subtle.importKey (Filip
Skokan)
[#&#8203;59851](https://redirect.github.com/nodejs/node/pull/59851)
-
\[[`85818db93c`](https://redirect.github.com/nodejs/node/commit/85818db93c)]
- **fs,win**: do not add a second trailing slash in readdir (Gerhard
Stöbich)
[#&#8203;59847](https://redirect.github.com/nodejs/node/pull/59847)
-
\[[`bedaaa11fc`](https://redirect.github.com/nodejs/node/commit/bedaaa11fc)]
- **(SEMVER-MINOR)** **http**: support http proxy for fetch under
NODE\_USE\_ENV\_PROXY (Joyee Cheung)
[#&#8203;57165](https://redirect.github.com/nodejs/node/pull/57165)
-
\[[`af8b5fa29d`](https://redirect.github.com/nodejs/node/commit/af8b5fa29d)]
- **(SEMVER-MINOR)** **http**: add shouldUpgradeCallback to let servers
control HTTP upgrades (Tim Perry)
[#&#8203;59824](https://redirect.github.com/nodejs/node/pull/59824)
-
\[[`758271ae66`](https://redirect.github.com/nodejs/node/commit/758271ae66)]
- **http**: optimize checkIsHttpToken for short strings (방진혁)
[#&#8203;59832](https://redirect.github.com/nodejs/node/pull/59832)
-
\[[`42102594b1`](https://redirect.github.com/nodejs/node/commit/42102594b1)]
- **(SEMVER-MINOR)** **http,https**: add built-in proxy support in
http/https.request and Agent (Joyee Cheung)
[#&#8203;58980](https://redirect.github.com/nodejs/node/pull/58980)
-
\[[`a33ed9bf96`](https://redirect.github.com/nodejs/node/commit/a33ed9bf96)]
- **inspector**: ensure adequate memory allocation for
`Binary::toBase64` (René)
[#&#8203;59870](https://redirect.github.com/nodejs/node/pull/59870)
-
\[[`34c686be2b`](https://redirect.github.com/nodejs/node/commit/34c686be2b)]
- **lib**: update inspect output format for subclasses (Miguel Marcondes
Filho)
[#&#8203;59687](https://redirect.github.com/nodejs/node/pull/59687)
-
\[[`12e553529c`](https://redirect.github.com/nodejs/node/commit/12e553529c)]
- **lib**: add source map support for assert messages (Chengzhong Wu)
[#&#8203;59751](https://redirect.github.com/nodejs/node/pull/59751)
-
\[[`d2a70571f8`](https://redirect.github.com/nodejs/node/commit/d2a70571f8)]
- **lib,src**: refactor assert to load error source from memory
(Chengzhong Wu)
[#&#8203;59751](https://redirect.github.com/nodejs/node/pull/59751)
-
\[[`20a9e86b5d`](https://redirect.github.com/nodejs/node/commit/20a9e86b5d)]
- **meta**: move Michael to emeritus (Michael Dawson)
[#&#8203;60070](https://redirect.github.com/nodejs/node/pull/60070)
-
\[[`c591cca15c`](https://redirect.github.com/nodejs/node/commit/c591cca15c)]
- **meta**: bump github/codeql-action from 3.30.0 to 3.30.5
(dependabot\[bot])
[#&#8203;60089](https://redirect.github.com/nodejs/node/pull/60089)
-
\[[`090ba141b1`](https://redirect.github.com/nodejs/node/commit/090ba141b1)]
- **meta**: bump codecov/codecov-action from 5.5.0 to 5.5.1
(dependabot\[bot])
[#&#8203;60091](https://redirect.github.com/nodejs/node/pull/60091)
-
\[[`a0ba6884a5`](https://redirect.github.com/nodejs/node/commit/a0ba6884a5)]
- **meta**: bump actions/stale from 9.1.0 to 10.0.0 (dependabot\[bot])
[#&#8203;60092](https://redirect.github.com/nodejs/node/pull/60092)
-
\[[`0feca0c541`](https://redirect.github.com/nodejs/node/commit/0feca0c541)]
- **meta**: bump actions/setup-node from 4.4.0 to 5.0.0
(dependabot\[bot])
[#&#8203;60093](https://redirect.github.com/nodejs/node/pull/60093)
-
\[[`7cd2b42d18`](https://redirect.github.com/nodejs/node/commit/7cd2b42d18)]
- **meta**: bump step-security/harden-runner from 2.12.2 to 2.13.1
(dependabot\[bot])
[#&#8203;60094](https://redirect.github.com/nodejs/node/pull/60094)
-
\[[`1f3b9d66ac`](https://redirect.github.com/nodejs/node/commit/1f3b9d66ac)]
- **meta**: bump actions/cache from 4.2.4 to 4.3.0 (dependabot\[bot])
[#&#8203;60095](https://redirect.github.com/nodejs/node/pull/60095)
-
\[[`0fedbb3de7`](https://redirect.github.com/nodejs/node/commit/0fedbb3de7)]
- **meta**: bump ossf/scorecard-action from 2.4.2 to 2.4.3
(dependabot\[bot])
[#&#8203;60096](https://redirect.github.com/nodejs/node/pull/60096)
-
\[[`04590b8267`](https://redirect.github.com/nodejs/node/commit/04590b8267)]
- **meta**: bump actions/setup-python from 5.6.0 to 6.0.0
(dependabot\[bot])
[#&#8203;60090](https://redirect.github.com/nodejs/node/pull/60090)
-
\[[`2bf0a9318f`](https://redirect.github.com/nodejs/node/commit/2bf0a9318f)]
- **meta**: add .npmrc with ignore-scripts=true (Joyee Cheung)
[#&#8203;59914](https://redirect.github.com/nodejs/node/pull/59914)
-
\[[`e10dc7b81c`](https://redirect.github.com/nodejs/node/commit/e10dc7b81c)]
- **module**: allow overriding linked requests for a ModuleWrap
(Chengzhong Wu)
[#&#8203;59527](https://redirect.github.com/nodejs/node/pull/59527)
-
\[[`2237142369`](https://redirect.github.com/nodejs/node/commit/2237142369)]
- **module**: link module with a module request record (Chengzhong Wu)
[#&#8203;58886](https://redirect.github.com/nodejs/node/pull/58886)
-
\[[`6d24b88fbc`](https://redirect.github.com/nodejs/node/commit/6d24b88fbc)]
- **node-api**: added SharedArrayBuffer api (Mert Can Altin)
[#&#8203;59071](https://redirect.github.com/nodejs/node/pull/59071)
-
\[[`4cc84c96f4`](https://redirect.github.com/nodejs/node/commit/4cc84c96f4)]
- **node-api**: make napi\_delete\_reference use node\_api\_basic\_env
(Jeetu Suthar)
[#&#8203;59684](https://redirect.github.com/nodejs/node/pull/59684)
-
\[[`e790eb6b50`](https://redirect.github.com/nodejs/node/commit/e790eb6b50)]
- **repl**: fix cpu overhead pasting big strings to the REPL (Ruben
Bridgewater)
[#&#8203;59857](https://redirect.github.com/nodejs/node/pull/59857)
-
\[[`99ea08dc43`](https://redirect.github.com/nodejs/node/commit/99ea08dc43)]
- **repl**: add isValidParentheses check before wrap input (Xuguang Mei)
[#&#8203;59607](https://redirect.github.com/nodejs/node/pull/59607)
-
\[[`e4a4f63019`](https://redirect.github.com/nodejs/node/commit/e4a4f63019)]
- **sqlite**: fix crash session extension callbacks with workers (Bart
Louwers)
[#&#8203;59848](https://redirect.github.com/nodejs/node/pull/59848)
-
\[[`42c5544b97`](https://redirect.github.com/nodejs/node/commit/42c5544b97)]
- **src**: assert memory calc for max-old-space-size-percentage (Asaf
Federman)
[#&#8203;59460](https://redirect.github.com/nodejs/node/pull/59460)
-
\[[`686ac49b82`](https://redirect.github.com/nodejs/node/commit/686ac49b82)]
- **(SEMVER-MINOR)** **src**: add percentage support to
--max-old-space-size (Asaf Federman)
[#&#8203;59082](https://redirect.github.com/nodejs/node/pull/59082)
-
\[[`84701ff668`](https://redirect.github.com/nodejs/node/commit/84701ff668)]
- **src**: clear all linked module caches once instantiated (Chengzhong
Wu) [#&#8203;59117](https://redirect.github.com/nodejs/node/pull/59117)
-
\[[`8e182e561f`](https://redirect.github.com/nodejs/node/commit/8e182e561f)]
- **src**: remove unnecessary `Environment::GetCurrent()` calls (Moonki
Choi)
[#&#8203;59814](https://redirect.github.com/nodejs/node/pull/59814)
-
\[[`c9cde35c4d`](https://redirect.github.com/nodejs/node/commit/c9cde35c4d)]
- **src**: simplify is\_callable by making it a concept (Tobias Nießen)
[#&#8203;58169](https://redirect.github.com/nodejs/node/pull/58169)
-
\[[`892b425ee1`](https://redirect.github.com/nodejs/node/commit/892b425ee1)]
- **src**: rename private fields to follow naming convention (Moonki
Choi)
[#&#8203;59923](https://redirect.github.com/nodejs/node/pull/59923)
-
\[[`36b68db7f5`](https://redirect.github.com/nodejs/node/commit/36b68db7f5)]
- **src**: reduce the nearest parent package JSON cache size (Michael
Smith)
[#&#8203;59888](https://redirect.github.com/nodejs/node/pull/59888)
-
\[[`26b40bad02`](https://redirect.github.com/nodejs/node/commit/26b40bad02)]
- **src**: replace FIXED\_ONE\_BYTE\_STRING with Environment-cached
strings (Moonki Choi)
[#&#8203;59891](https://redirect.github.com/nodejs/node/pull/59891)
-
\[[`34dcb7dc32`](https://redirect.github.com/nodejs/node/commit/34dcb7dc32)]
- **src**: create strings in `FIXED_ONE_BYTE_STRING` as internalized
(Anna Henningsen)
[#&#8203;59826](https://redirect.github.com/nodejs/node/pull/59826)
-
\[[`4d748add05`](https://redirect.github.com/nodejs/node/commit/4d748add05)]
- **src**: remove `std::array` overload of `FIXED_ONE_BYTE_STRING` (Anna
Henningsen)
[#&#8203;59826](https://redirect.github.com/nodejs/node/pull/59826)
-
\[[`bb6fd7c2d1`](https://redirect.github.com/nodejs/node/commit/bb6fd7c2d1)]
- **src**: ensure `v8::Eternal` is empty before setting it (Anna
Henningsen)
[#&#8203;59825](https://redirect.github.com/nodejs/node/pull/59825)
-
\[[`7a91282bf9`](https://redirect.github.com/nodejs/node/commit/7a91282bf9)]
- **src**: use simdjson::pad (0hm☘️)
[#&#8203;59391](https://redirect.github.com/nodejs/node/pull/59391)
-
\[[`ba00875f01`](https://redirect.github.com/nodejs/node/commit/ba00875f01)]
- **stream**: use new AsyncResource instead of bind (Matteo Collina)
[#&#8203;59867](https://redirect.github.com/nodejs/node/pull/59867)
-
\[[`ebec3ef68b`](https://redirect.github.com/nodejs/node/commit/ebec3ef68b)]
- **(SEMVER-MINOR)** **test**: move http proxy tests to
test/client-proxy (Joyee Cheung)
[#&#8203;58980](https://redirect.github.com/nodejs/node/pull/58980)
-
\[[`7067d79fb3`](https://redirect.github.com/nodejs/node/commit/7067d79fb3)]
- **test**: mark sea tests flaky on macOS x64 (Richard Lau)
[#&#8203;60068](https://redirect.github.com/nodejs/node/pull/60068)
-
\[[`ca1942c9d5`](https://redirect.github.com/nodejs/node/commit/ca1942c9d5)]
- **test**: testcase demonstrating issue 59541 (Eric Rannaud)
[#&#8203;59801](https://redirect.github.com/nodejs/node/pull/59801)
-
\[[`660d57355e`](https://redirect.github.com/nodejs/node/commit/660d57355e)]
- **test,doc**: skip --max-old-space-size-percentage on 32-bit platforms
(Asaf Federman)
[#&#8203;60144](https://redirect.github.com/nodejs/node/pull/60144)
-
\[[`19a7b1ef26`](https://redirect.github.com/nodejs/node/commit/19a7b1ef26)]
- **tls**: load bundled and extra certificates off-thread (Joyee Cheung)
[#&#8203;59856](https://redirect.github.com/nodejs/node/pull/59856)
-
\[[`095e7a81fc`](https://redirect.github.com/nodejs/node/commit/095e7a81fc)]
- **tls**: only do off-thread certificate loading on loading tls (Joyee
Cheung)
[#&#8203;59856](https://redirect.github.com/nodejs/node/pull/59856)
-
\[[`c42c1204c7`](https://redirect.github.com/nodejs/node/commit/c42c1204c7)]
- **tools**: fix `tools/make-v8.sh` for clang (Richard Lau)
[#&#8203;59893](https://redirect.github.com/nodejs/node/pull/59893)
-
\[[`b632a1d98d`](https://redirect.github.com/nodejs/node/commit/b632a1d98d)]
- **tools**: skip test-internet workflow for draft PRs (Michaël Zasso)
[#&#8203;59817](https://redirect.github.com/nodejs/node/pull/59817)
-
\[[`6021c3ac76`](https://redirect.github.com/nodejs/node/commit/6021c3ac76)]
- **tools**: copyedit `build-tarball.yml` (Antoine du Hamel)
[#&#8203;59808](https://redirect.github.com/nodejs/node/pull/59808)
-
\[[`ef005d0c9b`](https://redirect.github.com/nodejs/node/commit/ef005d0c9b)]
- **typings**: update 'types' binding (René)
[#&#8203;59692](https://redirect.github.com/nodejs/node/pull/59692)
-
\[[`28ef564ecd`](https://redirect.github.com/nodejs/node/commit/28ef564ecd)]
- **typings**: remove unused imports (Nam Yooseong)
[#&#8203;59880](https://redirect.github.com/nodejs/node/pull/59880)
-
\[[`f88752ddb6`](https://redirect.github.com/nodejs/node/commit/f88752ddb6)]
- **url**: replaced slice with at (Mikhail)
[#&#8203;59181](https://redirect.github.com/nodejs/node/pull/59181)
-
\[[`24c224960c`](https://redirect.github.com/nodejs/node/commit/24c224960c)]
- **url**: add type checking to urlToHttpOptions() (simon-id)
[#&#8203;59753](https://redirect.github.com/nodejs/node/pull/59753)
-
\[[`f2fbcc576d`](https://redirect.github.com/nodejs/node/commit/f2fbcc576d)]
- **util**: fix debuglog.enabled not being present with callback logger
(Ruben Bridgewater)
[#&#8203;59858](https://redirect.github.com/nodejs/node/pull/59858)
-
\[[`6277058e43`](https://redirect.github.com/nodejs/node/commit/6277058e43)]
- **vm**: sync-ify SourceTextModule linkage (Chengzhong Wu)
[#&#8203;59000](https://redirect.github.com/nodejs/node/pull/59000)
-
\[[`5bf21a4309`](https://redirect.github.com/nodejs/node/commit/5bf21a4309)]
- **vm**: explain how to share promises between contexts w/
afterEvaluate (Eric Rannaud)
[#&#8203;59801](https://redirect.github.com/nodejs/node/pull/59801)
-
\[[`312b33a083`](https://redirect.github.com/nodejs/node/commit/312b33a083)]
- **vm**: "afterEvaluate", evaluate() return a promise from the outer
context (Eric Rannaud)
[#&#8203;59801](https://redirect.github.com/nodejs/node/pull/59801)
-
\[[`1eadab863c`](https://redirect.github.com/nodejs/node/commit/1eadab863c)]
- **win,tools**: add description to signature (Martin Costello)
[#&#8203;59877](https://redirect.github.com/nodejs/node/pull/59877)
-
\[[`816e1befb1`](https://redirect.github.com/nodejs/node/commit/816e1befb1)]
- **zlib**: reduce code duplication (jhofstee)
[#&#8203;57810](https://redirect.github.com/nodejs/node/pull/57810)

###
[`v22.20.0`](https://redirect.github.com/nodejs/node/releases/tag/v22.20.0):
2025-09-24, Version 22.20.0 &#x27;Jod&#x27; (LTS), @&#8203;richardlau

[Compare
Source](https://redirect.github.com/nodejs/node/compare/v22.19.0...v22.20.0)

##### Notable Changes

##### OpenSSL updated to 3.5.2

For official Node.js builds, or builds using the default build
configuration, Node.js now bundles OpenSSL 3.5.2. This update allows
Node.js 22.x to be supported through to the planned End-of-Life date of
2027-04-30 as the previously bundled OpenSSL 3.0.x goes out of support
in September 2026.

This change does not affect third-party builds of Node.js that link to
an external OpenSSL (or OpenSSL-compatible) library.

##### Other notable changes

-
\[[`5b83e1e0a2`](https://redirect.github.com/nodejs/node/commit/5b83e1e0a2)]
- **crypto**: update root certificates to NSS 3.114 (Node.js GitHub Bot)
[#&#8203;59571](https://redirect.github.com/nodejs/node/pull/59571)
-
\[[`34b25fd97b`](https://redirect.github.com/nodejs/node/commit/34b25fd97b)]
- **doc**: stabilize --disable-sigusr1 (Rafael Gonzaga)
[#&#8203;59707](https://redirect.github.com/nodejs/node/pull/59707)
-
\[[`bf41218ed9`](https://redirect.github.com/nodejs/node/commit/bf41218ed9)]
- **doc**: mark `path.matchesGlob` as stable (Aviv Keller)
[#&#8203;59572](https://redirect.github.com/nodejs/node/pull/59572)
-
\[[`1dbad2058f`](https://redirect.github.com/nodejs/node/commit/1dbad2058f)]
- **(SEMVER-MINOR)** **http**: add Agent.agentKeepAliveTimeoutBuffer
option (Haram Jeong)
[#&#8203;59315](https://redirect.github.com/nodejs/node/pull/59315)
-
\[[`062e837d5f`](https://redirect.github.com/nodejs/node/commit/062e837d5f)]
- **(SEMVER-MINOR)** **http2**: add support for raw header arrays in
h2Stream.respond() (Tim Perry)
[#&#8203;59455](https://redirect.github.com/nodejs/node/pull/59455)
-
\[[`b8066611c3`](https://redirect.github.com/nodejs/node/commit/b8066611c3)]
- **inspector**: add http2 tracking support (Darshan Sen)
[#&#8203;59611](https://redirect.github.com/nodejs/node/pull/59611)
-
\[[`9b7dd40da8`](https://redirect.github.com/nodejs/node/commit/9b7dd40da8)]
- **(SEMVER-MINOR)** **sea**: implement execArgvExtension (Joyee Cheung)
[#&#8203;59560](https://redirect.github.com/nodejs/node/pull/59560)
-
\[[`48bfbd3dca`](https://redirect.github.com/nodejs/node/commit/48bfbd3dca)]
- **(SEMVER-MINOR)** **sea**: support execArgv in sea config (Joyee
Cheung)
[#&#8203;59314](https://redirect.github.com/nodejs/node/pull/59314)
-
\[[`cf06e74076`](https://redirect.github.com/nodejs/node/commit/cf06e74076)]
- **(SEMVER-MINOR)** **stream**: add brotli support to CompressionStream
and DecompressionStream (Matthew Aitken)
[#&#8203;59464](https://redirect.github.com/nodejs/node/pull/59464)
-
\[[`62bb80c17e`](https://redirect.github.com/nodejs/node/commit/62bb80c17e)]
- **(SEMVER-MINOR)** **test\_runner**: support object property mocking
(Idan Goshen)
[#&#8203;58438](https://redirect.github.com/nodejs/node/pull/58438)
-
\[[`9e2aa23be9`](https://redirect.github.com/nodejs/node/commit/9e2aa23be9)]
- **(SEMVER-MINOR)** **worker**: add cpu profile APIs for worker
(theanarkh)
[#&#8203;59428](https://redirect.github.com/nodejs/node/pull/59428)

##### Commits

-
\[[`b7b78fd565`](https://redirect.github.com/nodejs/node/commit/b7b78fd565)]
- **assert**: cap input size in myersDiff to avoid Int32Array overflow
(Haram Jeong)
[#&#8203;59578](https://redirect.github.com/nodejs/node/pull/59578)
-
\[[`9da50a6c53`](https://redirect.github.com/nodejs/node/commit/9da50a6c53)]
- **benchmark**: sqlite prevent create both tables on prepare selects
(Bruno Rodrigues)
[#&#8203;59709](https://redirect.github.com/nodejs/node/pull/59709)
-
\[[`4c1538770e`](https://redirect.github.com/nodejs/node/commit/4c1538770e)]
- **benchmark**: calibrate config array-vs-concat (Rafael Gonzaga)
[#&#8203;59587](https://redirect.github.com/nodejs/node/pull/59587)
-
\[[`fc3f82d683`](https://redirect.github.com/nodejs/node/commit/fc3f82d683)]
- **benchmark**: calibrate config v8/serialize.js (Rafael Gonzaga)
[#&#8203;59586](https://redirect.github.com/nodejs/node/pull/59586)
-
\[[`e95c9b2950`](https://redirect.github.com/nodejs/node/commit/e95c9b2950)]
- **benchmark**: reduce readfile-permission-enabled config (Rafael
Gonzaga)
[#&#8203;59589](https://redirect.github.com/nodejs/node/pull/59589)
-
\[[`e4fea38b31`](https://redirect.github.com/nodejs/node/commit/e4fea38b31)]
- **benchmark**: calibrate length of util.diff (Rafael Gonzaga)
[#&#8203;59588](https://redirect.github.com/nodejs/node/pull/59588)
-
\[[`c5d68c4a0f`](https://redirect.github.com/nodejs/node/commit/c5d68c4a0f)]
- **benchmark, test**: replace CRLF variable with string literal (Lee
Jiho)
[#&#8203;59466](https://redirect.github.com/nodejs/node/pull/59466)
-
\[[`129a1d673b`](https://redirect.github.com/nodejs/node/commit/129a1d673b)]
- **build**: fix getting OpenSSL version on Windows (Michaël Zasso)
[#&#8203;59609](https://redirect.github.com/nodejs/node/pull/59609)
-
\[[`9f53db7162`](https://redirect.github.com/nodejs/node/commit/9f53db7162)]
- **build**: fix 'implicit-function-declaration' on OpenHarmony platform
(hqzing)
[#&#8203;59547](https://redirect.github.com/nodejs/node/pull/59547)
-
\[[`3839593e07`](https://redirect.github.com/nodejs/node/commit/3839593e07)]
- **build**: use `windows-2025` runner (Michaël Zasso)
[#&#8203;59673](https://redirect.github.com/nodejs/node/pull/59673)
-
\[[`e430464669`](https://redirect.github.com/nodejs/node/commit/e430464669)]
- **build**: compile bundled uvwasi conditionally (Carlo Cabrera)
[#&#8203;59622](https://redirect.github.com/nodejs/node/pull/59622)
-
\[[`e2c9cab0cd`](https://redirect.github.com/nodejs/node/commit/e2c9cab0cd)]
- **build**: do not set `-mminimal-toc` with `clang` (Richard Lau)
[#&#8203;59484](https://redirect.github.com/nodejs/node/pull/59484)
-
\[[`208bc810a1`](https://redirect.github.com/nodejs/node/commit/208bc810a1)]
- **child\_process**: remove unsafe array iteration (hotpineapple)
[#&#8203;59347](https://redirect.github.com/nodejs/node/pull/59347)
-
\[[`d74799d90c`](https://redirect.github.com/nodejs/node/commit/d74799d90c)]
- **crypto**: load system CA certificates off thread (Joyee Cheung)
[#&#8203;59550](https://redirect.github.com/nodejs/node/pull/59550)
-
\[[`5b83e1e0a2`](https://redirect.github.com/nodejs/node/commit/5b83e1e0a2)]
- **crypto**: update root certificates to NSS 3.114 (Node.js GitHub Bot)
[#&#8203;59571](https://redirect.github.com/nodejs/node/pull/59571)
-
\[[`d289b1d1af`](https://redirect.github.com/nodejs/node/commit/d289b1d1af)]
- **deps**: V8: cherry-pick
[`e3df60f`](https://redirect.github.com/nodejs/node/commit/e3df60f3f5ab)
(Chengzhong Wu)
[#&#8203;58691](https://redirect.github.com/nodejs/node/pull/58691)
-
\[[`cf5d91e2a6`](https://redirect.github.com/nodejs/node/commit/cf5d91e2a6)]
- **deps**: update uvwasi to 0.0.23 (Node.js GitHub Bot)
[#&#8203;59791](https://redirect.github.com/nodejs/node/pull/59791)
-
\[[`1cf24a0445`](https://redirect.github.com/nodejs/node/commit/1cf24a0445)]
- **deps**: update histogram to 0.11.9 (Node.js GitHub Bot)
[#&#8203;59689](https://redirect.github.com/nodejs/node/pull/59689)
-
\[[`8638bd3f2e`](https://redirect.github.com/nodejs/node/commit/8638bd3f2e)]
- **deps**: update googletest to
[`eb2d85e`](https://redirect.github.com/nodejs/node/commit/eb2d85e)
(Node.js GitHub Bot)
[#&#8203;59335](https://redirect.github.com/nodejs/node/pull/59335)
-
\[[`3ff4eb5b37`](https://redirect.github.com/nodejs/node/commit/3ff4eb5b37)]
- **deps**: update amaro to 1.1.2 (Node.js GitHub Bot)
[#&#8203;59616](https://redirect.github.com/nodejs/node/pull/59616)
-
\[[`4d268ac034`](https://redirect.github.com/nodejs/node/commit/4d268ac034)]
- **deps**: V8: cherry-pick
[`7b91e3e`](https://redirect.github.com/nodejs/node/commit/7b91e3e2cbaf)
(Milad Fa)
[#&#8203;59485](https://redirect.github.com/nodejs/node/pull/59485)
-
\[[`83410eb0e3`](https://redirect.github.com/nodejs/node/commit/83410eb0e3)]
- **deps**: V8: cherry-pick
[`59d52e3`](https://redirect.github.com/nodejs/node/commit/59d52e311bb1)
(Milad Fa)
[#&#8203;59485](https://redirect.github.com/nodejs/node/pull/59485)
-
\[[`5780af02cb`](https://redirect.github.com/nodejs/node/commit/5780af02cb)]
- **deps**: update amaro to 1.1.1 (Node.js GitHub Bot)
[#&#8203;59141](https://redirect.github.com/nodejs/node/pull/59141)
-
\[[`2986eca821`](https://redirect.github.com/nodejs/node/commit/2986eca821)]
- **deps**: V8: cherry-pick
[`6b1b9bc`](https://redirect.github.com/nodejs/node/commit/6b1b9bca2a8)
(zhoumingtao)
[#&#8203;59283](https://redirect.github.com/nodejs/node/pull/59283)
-
\[[`98e399b3ea`](https://redirect.github.com/nodejs/node/commit/98e399b3ea)]
- **deps**: update archs files for openssl-3.5.2 (Node.js GitHub Bot)
[#&#8203;59371](https://redirect.github.com/nodejs/node/pull/59371)
-
\[[`2b983a7520`](https://redirect.github.com/nodejs/node/commit/2b983a7520)]
- **deps**: upgrade openssl sources to openssl-3.5.2 (Node.js GitHub
Bot) [#&#8203;59371](https://redirect.github.com/nodejs/node/pull/59371)
-
\[[`7ffbb42454`](https://redirect.github.com/nodejs/node/commit/7ffbb42454)]
- **deps**: update archs files for openssl-3.5.1 (Node.js GitHub Bot)
[#&#8203;59234](https://redirect.github.com/nodejs/node/pull/59234)
-
\[[`bd48a60a75`](https://redirect.github.com/nodejs/node/commit/bd48a60a75)]
- **deps**: upgrade openssl sources to openssl-3.5.1 (Node.js GitHub
Bot) [#&#8203;59234](https://redirect.github.com/nodejs/node/pull/59234)
-
\[[`24762a10ca`](https://redirect.github.com/nodejs/node/commit/24762a10ca)]
- **deps**: fix OpenSSL security level at 1 (Richard Lau)
[#&#8203;59859](https://redirect.github.com/nodejs/node/pull/59859)
-
\[[`1233e92d10`](https://redirect.github.com/nodejs/node/commit/1233e92d10)]
- **diagnostics\_channel**: revoke DEP0163 (René)
[#&#8203;59758](https://redirect.github.com/nodejs/node/pull/59758)
-
\[[`34b25fd97b`](https://redirect.github.com/nodejs/node/commit/34b25fd97b)]
- **doc**: stabilize --disable-sigusr1 (Rafael Gonzaga)
[#&#8203;59707](https://redirect.github.com/nodejs/node/pull/59707)
-
\[[`d7adf8be64`](https://redirect.github.com/nodejs/node/commit/d7adf8be64)]
- **doc**: update "Type stripping in dependencies" section (Josh Kelley)
[#&#8203;59652](https://redirect.github.com/nodejs/node/pull/59652)
-
\[[`a1d7e4fdbf`](https://redirect.github.com/nodejs/node/commit/a1d7e4fdbf)]
- **doc**: add Miles Guicent as triager (Miles Guicent)
[#&#8203;59562](https://redirect.github.com/nodejs/node/pull/59562)
-
\[[`bf41218ed9`](https://redirect.github.com/nodejs/node/commit/bf41218ed9)]
- **doc**: mark `path.matchesGlob` as stable (Aviv Keller)
[#&#8203;59572](https://redirect.github.com/nodejs/node/pull/59572)
-
\[[`afaa1ccb74`](https://redirect.github.com/nodejs/node/commit/afaa1ccb74)]
- **doc**: improve documentation for raw headers in HTTP/2 APIs (Tim
Perry)
[#&#8203;59633](https://redirect.github.com/nodejs/node/pull/59633)
-
\[[`b95ff56102`](https://redirect.github.com/nodejs/node/commit/b95ff56102)]
- **doc**: update install\_tools.bat free disk space (Stefan Stojanovic)
[#&#8203;59579](https://redirect.github.com/nodejs/node/pull/59579)
-
\[[`6ff939b8d3`](https://redirect.github.com/nodejs/node/commit/6ff939b8d3)]
- **doc**: fix filehandle.read typo (Ruy Adorno)
[#&#8203;59635](https://redirect.github.com/nodejs/node/pull/59635)
-
\[[`963bfa9d6f`](https://redirect.github.com/nodejs/node/commit/963bfa9d6f)]
- **doc**: fix missing link to the Error documentation in the `http`
page (Alexander Makarenko)
[#&#8203;59080](https://redirect.github.com/nodejs/node/pull/59080)
-
\[[`0e10a8ea27`](https://redirect.github.com/nodejs/node/commit/0e10a8ea27)]
- **doc**: improve `sqlite.backup()` progress/fulfillment documentation
(René)
[#&#8203;59598](https://redirect.github.com/nodejs/node/pull/59598)
-
\[[`18ceefbabd`](https://redirect.github.com/nodejs/node/commit/18ceefbabd)]
- **doc**: clarify experimental platform vulnerability policy (Matteo
Collina)
[#&#8203;59591](https://redirect.github.com/nodejs/node/pull/59591)
-
\[[`66cdd00368`](https://redirect.github.com/nodejs/node/commit/66cdd00368)]
- **doc**: link to `TypedArray.from()` in signature (Aviv Keller)
[#&#8203;59226](https://redirect.github.com/nodejs/node/pull/59226)
-
\[[`9f058ce7c0`](https://redirect.github.com/nodejs/node/commit/9f058ce7c0)]
- **doc**: fix typos in `environment_variables.md` (PhistucK)
[#&#8203;59536](https://redirect.github.com/nodejs/node/pull/59536)
-
\[[`3cfec820e9`](https://redirect.github.com/nodejs/node/commit/3cfec820e9)]
- **doc**: add security incident reponse plan (Rafael Gonzaga)
[#&#8203;59470](https://redirect.github.com/nodejs/node/pull/59470)
-
\[[`46aa3434e6`](https://redirect.github.com/nodejs/node/commit/46aa3434e6)]
- **doc**: clarify maxRSS unit in `process.resourceUsage()` (Alex Yang)
[#&#8203;59511](https://redirect.github.com/nodejs/node/pull/59511)
-
\[[`adf98f600a`](https://redirect.github.com/nodejs/node/commit/adf98f600a)]
- **doc**: add missing Zstd strategy constants (RANDRIAMANANTENA
Narindra Tiana Annaick)
[#&#8203;59312](https://redirect.github.com/nodejs/node/pull/59312)
-
\[[`f335989942`](https://redirect.github.com/nodejs/node/commit/f335989942)]
- **doc**: fix the version tls.DEFAULT\_CIPHERS was added (Allon
Murienik)
[#&#8203;59247](https://redirect.github.com/nodejs/node/pull/59247)
-
\[[`7fa14fcf54`](https://redirect.github.com/nodejs/node/commit/7fa14fcf54)]
- **doc**: clarify glob's exclude option behavior (hotpineapple)
[#&#8203;59245](https://redirect.github.com/nodejs/node/pull/59245)
-
\[[`85b8d255c9`](https://redirect.github.com/nodejs/node/commit/85b8d255c9)]
- **doc**: add RafaelGSS as performance strategic lead (Rafael Gonzaga)
[#&#8203;59445](https://redirect.github.com/nodejs/node/pull/59445)
-
\[[`16b1f7a602`](https://redirect.github.com/nodejs/node/commit/16b1f7a602)]
- **doc**: add new environment variables doc page (Dario Piotrowicz)
[#&#8203;59052](https://redirect.github.com/nodejs/node/pull/59052)
-
\[[`b4a43ed83a`](https://redirect.github.com/nodejs/node/commit/b4a43ed83a)]
- **doc**: add missing environment variables to manpage (amir lavasani)
[#&#8203;58963](https://redirect.github.com/nodejs/node/pull/58963)
-
\[[`c923cfe898`](https://redirect.github.com/nodejs/node/commit/c923cfe898)]
- **doc**: fix links in test.md (Vas Sudanagunta)
[#&#8203;58876](https://redirect.github.com/nodejs/node/pull/58876)
-
\[[`a93a8b5eda`](https://redirect.github.com/nodejs/node/commit/a93a8b5eda)]
- **doc**: mark type stripping as release candidate (Marco Ippolito)
[#&#8203;57705](https://redirect.github.com/nodejs/node/pull/57705)
-
\[[`d302cb3bb2`](https://redirect.github.com/nodejs/node/commit/d302cb3bb2)]
- **esm**: add experimental support for addon modules (Chengzhong Wu)
[#&#8203;55844](https://redirect.github.com/nodejs/node/pull/55844)
-
\[[`d55c3e7f0b`](https://redirect.github.com/nodejs/node/commit/d55c3e7f0b)]
- **esm**: link modules synchronously when no async loader hooks are
used (Joyee Cheung)
[#&#8203;59519](https://redirect.github.com/nodejs/node/pull/59519)
-
\[[`9e1fbb620f`](https://redirect.github.com/nodejs/node/commit/9e1fbb620f)]
- **esm**: show race error message for inner module job race (Joyee
Cheung)
[#&#8203;59519](https://redirect.github.com/nodejs/node/pull/59519)
-
\[[`8c4dcd5199`](https://redirect.github.com/nodejs/node/commit/8c4dcd5199)]
- **esm**: sync-ify module translation (Joyee Cheung)
[#&#8203;59453](https://redirect.github.com/nodejs/node/pull/59453)
-
\[[`71038932d3`](https://redirect.github.com/nodejs/node/commit/71038932d3)]
- **fs**: fix wrong order of file names in cpSync error message
(Nicholas Paun)
[#&#8203;59775](https://redirect.github.com/nodejs/node/pull/59775)
-
\[[`5692dec451`](https://redirect.github.com/nodejs/node/commit/5692dec451)]
- **fs**: fix dereference: false on cpSync (Nicholas Paun)
[#&#8203;59681](https://redirect.github.com/nodejs/node/pull/59681)
-
\[[`dafd561d37`](https://redirect.github.com/nodejs/node/commit/dafd561d37)]
- **fs**: fix return value of fs APIs (theanarkh)
[#&#8203;58996](https://redirect.github.com/nodejs/node/pull/58996)
-
\[[`da6e8cb75b`](https://redirect.github.com/nodejs/node/commit/da6e8cb75b)]
- **http**: unbreak keepAliveTimeoutBuffer (Robert Nagy)
[#&#8203;59784](https://redirect.github.com/nodejs/node/pull/59784)
-
\[[`673a48f0a2`](https://redirect.github.com/nodejs/node/commit/673a48f0a2)]
- **http**: use cached '1.1' http version string (Robert Nagy)
[#&#8203;59717](https://redirect.github.com/nodejs/node/pull/59717)
-
\[[`1dbad2058f`](https://redirect.github.com/nodejs/node/commit/1dbad2058f)]
- **(SEMVER-MINOR)** **http**: add Agent.agentKeepAliveTimeoutBuffer
option (Haram Jeong)
[#&#8203;59315](https://redirect.github.com/nodejs/node/pull/59315)
-
\[[`062e837d5f`](https://redirect.github.com/nodejs/node/commit/062e837d5f)]
- **(SEMVER-MINOR)** **http2**: add support for raw header arrays in
h2Stream.respond() (Tim Perry)
[#&#8203;59455](https://redirect.github.com/nodejs/node/pull/59455)
-
\[[`4d4fb51b89`](https://redirect.github.com/nodejs/node/commit/4d4fb51b89)]
- **http2**: report sent headers object in client stream dcs (Darshan
Sen) [#&#8203;59419](https://redirect.github.com/nodejs/node/pull/59419)
-
\[[`b8066611c3`](https://redirect.github.com/nodejs/node/commit/b8066611c3)]
- **inspector**: add http2 tracking support (Darshan Sen)
[#&#8203;59611](https://redirect.github.com/nodejs/node/pull/59611)
-
\[[`9b2c013032`](https://redirect.github.com/nodejs/node/commit/9b2c013032)]
- **inspector**: prevent propagation of promise hooks to noPromise hooks
(Shima Ryuhei)
[#&#8203;58841](https://redirect.github.com/nodejs/node/pull/58841)
-
\[[`a2329895e7`](https://redirect.github.com/nodejs/node/commit/a2329895e7)]
- **lib**: fix DOMException subclass support (Chengzhong Wu)
[#&#8203;59680](https://redirect.github.com/nodejs/node/pull/59680)
-
\[[`edb9248bdd`](https://redirect.github.com/nodejs/node/commit/edb9248bdd)]
- **lib**: make domexception a native error (Chengzhong Wu)
[#&#8203;58691](https://redirect.github.com/nodejs/node/pull/58691)
-
\[[`ccf29cda19`](https://redirect.github.com/nodejs/node/commit/ccf29cda19)]
- ***Revert*** "**lib**: optimize writable stream buffer clearing" (Yoo)
[#&#8203;59743](https://redirect.github.com/nodejs/node/pull/59743)
-
\[[`f291eda277`](https://redirect.github.com/nodejs/node/commit/f291eda277)]
- **lib**: fix isReadable and isWritable return type value (Gabriel
Quaresma)
[#&#8203;59089](https://redirect.github.com/nodejs/node/pull/59089)
-
\[[`10ae8684ea`](https://redirect.github.com/nodejs/node/commit/10ae8684ea)]
- **lib**: revert to using default derived class constructors (René)
[#&#8203;59650](https://redirect.github.com/nodejs/node/pull/59650)
-
\[[`5d3b80d62d`](https://redirect.github.com/nodejs/node/commit/5d3b80d62d)]
- **lib**: do not modify prototype deprecated asyncResource (encore)
(Szymon Łągiewka)
[#&#8203;59518](https://redirect.github.com/nodejs/node/pull/59518)
-
\[[`3c4541f878`](https://redirect.github.com/nodejs/node/commit/3c4541f878)]
- **lib**: simplify IPv6 checks in isLoopback() (Krishnadas)
[#&#8203;59375](https://redirect.github.com/nodejs/node/pull/59375)
-
\[[`0b631bbffa`](https://redirect.github.com/nodejs/node/commit/0b631bbffa)]
- **lib**: handle windows reserved device names on UNC (Rafael Gonzaga)
[#&#8203;59286](https://redirect.github.com/nodejs/node/pull/59286)
-
\[[`297f62ba1f`](https://redirect.github.com/nodejs/node/commit/297f62ba1f)]
- **meta**: bump `codecov/codecov-action` (dependabot\[bot])
[#&#8203;59726](https://redirect.github.com/nodejs/node/pull/59726)
-
\[[`3dcd8446b6`](https://redirect.github.com/nodejs/node/commit/3dcd8446b6)]
- **meta**: bump actions/download-artifact from 4.3.0 to 5.0.0
(dependabot\[bot])
[#&#8203;59729](https://redirect.github.com/nodejs/node/pull/59729)
-
\[[`d0d357f683`](https://redirect.github.com/nodejs/node/commit/d0d357f683)]
- **meta**: bump github/codeql-action from 3.29.2 to 3.30.0
(dependabot\[bot])
[#&#8203;59728](https://redirect.github.com/nodejs/node/pull/59728)
-
\[[`2a0e264949`](https://redirect.github.com/nodejs/node/commit/2a0e264949)]
- **meta**: bump actions/cache from 4.2.3 to 4.2.4 (dependabot\[bot])
[#&#8203;59727](https://redirect.github.com/nodejs/node/pull/59727)
-
\[[`0a013d1da1`](https://redirect.github.com/nodejs/node/commit/0a013d1da1)]
- **meta**: bump actions/checkout from 4.2.2 to 5.0.0 (dependabot\[bot])
[#&#8203;59725](https://redirect.github.com/nodejs/node/pull/59725)
-
\[[`c690b53d24`](https://redirect.github.com/nodejs/node/commit/c690b53d24)]
- **meta**: update devcontainer to the latest schema (Aviv Keller)
[#&#8203;54347](https://redirect.github.com/nodejs/node/pull/54347)
-
\[[`61171c7756`](https://redirect.github.com/nodejs/node/commit/61171c7756)]
- **module**: correctly detect top-level await in ambiguous contexts
(Shima Ryuhei)
[#&#8203;58646](https://redirect.github.com/nodejs/node/pull/58646)
-
\[[`75bf3f4a87`](https://redirect.github.com/nodejs/node/commit/75bf3f4a87)]
- **node-api**: link to other programming language bindings (Chengzhong
Wu) [#&#8203;59516](https://redirect.github.com/nodejs/node/pull/59516)
-
\[[`9a05107558`](https://redirect.github.com/nodejs/node/commit/9a05107558)]
- **node-api**: clarify enum value ABI stability (Chengzhong Wu)
[#&#8203;59085](https://redirect.github.com/nodejs/node/pull/59085)
-
\[[`658c31d60c`](https://redirect.github.com/nodejs/node/commit/658c31d60c)]
- **path**: refactor path joining logic for clarity and performance (Lee
Jiho)
[#&#8203;59781](https://redirect.github.com/nodejs/node/pull/59781)
-
\[[`9cc89f55f7`](https://redirect.github.com/nodejs/node/commit/9cc89f55f7)]
- **path,win**: fix bug in resolve and normalize (Hüseyin Açacak)
[#&#8203;55623](https://redirect.github.com/nodejs/node/pull/55623)
-
\[[`24e825f8f5`](https://redirect.github.com/nodejs/node/commit/24e825f8f5)]
- **sea**: implement sea.getAssetKeys() (Joyee Cheung)
[#&#8203;59661](https://redirect.github.com/nodejs/node/pull/59661)
-
\[[`c66af21e55`](https://redirect.github.com/nodejs/node/commit/c66af21e55)]
- **sea**: allow using inspector command line flags with SEA (Joyee
Cheung)
[#&#8203;59568](https://redirect.github.com/nodejs/node/pull/59568)
-
\[[`9b7dd40da8`](https://redirect.github.com/nodejs/node/commit/9b7dd40da8)]
- **(SEMVER-MINOR)** **sea**: implement execArgvExtension (Joyee Cheung)
[#&#8203;59560](https://redirect.github.com/nodejs/node/pull/59560)
-
\[[`48bfbd3dca`](https://redirect.github.com/nodejs/node/commit/48bfbd3dca)]
- **(SEMVER-MINOR)** **sea**: support execArgv in sea config (Joyee
Cheung)
[#&#8203;59314](https://redirect.github.com/nodejs/node/pull/59314)
-
\[[`5559456fe4`](https://redirect.github.com/nodejs/node/commit/5559456fe4)]
- **sqlite**: add sqlite-type symbol for DatabaseSync (Alex Yang)
[#&#8203;59405](https://redirect.github.com/nodejs/node/pull/59405)
-
\[[`3478130da3`](https://redirect.github.com/nodejs/node/commit/3478130da3)]
- **sqlite**: handle ?NNN parameters as positional (Edy Silva)
[#&#8203;59350](https://redirect.github.com/nodejs/node/pull/59350)
-
\[[`312bc4e5d1`](https://redirect.github.com/nodejs/node/commit/312bc4e5d1)]
- **sqlite**: avoid useless call to FromMaybe() (Tobias Nießen)
[#&#8203;59490](https://redirect.github.com/nodejs/node/pull/59490)
-
\[[`937e9bb1c6`](https://redirect.github.com/nodejs/node/commit/937e9bb1c6)]
- **src**: track BaseObjects with an efficient list (Chengzhong Wu)
[#&#8203;55104](https://redirect.github.com/nodejs/node/pull/55104)
-
\[[`be2a5e170d`](https://redirect.github.com/nodejs/node/commit/be2a5e170d)]
- **src**: track async resources via pointers to stack-allocated handles
(Anna Henningsen)
[#&#8203;59704](https://redirect.github.com/nodejs/node/pull/59704)
-
\[[`f232bf2c11`](https://redirect.github.com/nodejs/node/commit/f232bf2c11)]
- **src**: fix build on NetBSD (Thomas Klausner)
[#&#8203;59718](https://redirect.github.com/nodejs/node/pull/59718)
-
\[[`e9a685bc3d`](https://redirect.github.com/nodejs/node/commit/e9a685bc3d)]
- **src**: fix race on process exit and off thread CA loading
(Chengzhong Wu)
[#&#8203;59632](https://redirect.github.com/nodejs/node/pull/59632)
-
\[[`24428fc8fb`](https://redirect.github.com/nodejs/node/commit/24428fc8fb)]
- **src**: add name for more threads (theanarkh)
[#&#8203;59601](https://redirect.github.com/nodejs/node/pull/59601)
-
\[[`fd7559f8c3`](https://redirect.github.com/nodejs/node/commit/fd7559f8c3)]
- **src**: remove JSONParser (Joyee Cheung)
[#&#8203;59619](https://redirect.github.com/nodejs/node/pull/59619)
-
\[[`8c296bac99`](https://redirect.github.com/nodejs/node/commit/8c296bac99)]
- **src**: enforce assumptions in FIXED\_ONE\_BYTE\_STRING (Tobias
Nießen)
[#&#8203;58155](https://redirect.github.com/nodejs/node/pull/58155)
-
\[[`1b4885a3f2`](https://redirect.github.com/nodejs/node/commit/1b4885a3f2)]
- **src**: use simdjson to parse --snapshot-config (Joyee Cheung)
[#&#8203;59473](https://redirect.github.com/nodejs/node/pull/59473)
-
\[[`9f798de6b0`](https://redirect.github.com/nodejs/node/commit/9f798de6b0)]
- **src**: fix order of CHECK\_NOT\_NULL/dereference (Tobias Nießen)
[#&#8203;59487](https://redirect.github.com/nodejs/node/pull/59487)
-
\[[`f764be27dc`](https://redirect.github.com/nodejs/node/commit/f764be27dc)]
- **src**: move shared\_ptr objects in KeyObjectData (Tobias Nießen)
[#&#8203;59472](https://redirect.github.com/nodejs/node/pull/59472)
-
\[[`d30287fe12`](https://redirect.github.com/nodejs/node/commit/d30287fe12)]
- **src**: iterate metadata version entries with std::array (Chengzhong
Wu) [#&#8203;57866](https://redirect.github.com/nodejs/node/pull/57866)
-
\[[`b2bf620c7b`](https://redirect.github.com/nodejs/node/commit/b2bf620c7b)]
- **src**: internalize `v8::ConvertableToTraceFormat` in traces
(Chengzhong Wu)
[#&#8203;57866](https://redirect.github.com/nodejs/node/pull/57866)
-
\[[`b3c507c8ef`](https://redirect.github.com/nodejs/node/commit/b3c507c8ef)]
- **src**: remove duplicate assignment of `O_EXCL` in node\_constants.cc
(Daniel Osvaldo R)
[#&#8203;59049](https://redirect.github.com/nodejs/node/pull/59049)
-
\[[`20aec239d4`](https://redirect.github.com/nodejs/node/commit/20aec239d4)]
- **src**: add Intel CET properties to large\_pages.S (tjuhaszrh)
[#&#8203;59363](https://redirect.github.com/nodejs/node/pull/59363)
-
\[[`8e0f9cd061`](https://redirect.github.com/nodejs/node/commit/8e0f9cd061)]
- **src**: remove unused DSAKeyExportJob (Filip Skokan)
[#&#8203;59291](https://redirect.github.com/nodejs/node/pull/59291)
-
\[[`0c2b6df94f`](https://redirect.github.com/nodejs/node/commit/0c2b6df94f)]
- **src,sqlite**: refactor value conversion (Edy Silva)
[#&#8203;59659](https://redirect.github.com/nodejs/node/pull/59659)
-
\[[`b95cfdf0e5`](https://redirect.github.com/nodejs/node/commit/b95cfdf0e5)]
- **stream**: replace manual function validation with validateFunction
(방진혁)
[#&#8203;59529](https://redirect.github.com/nodejs/node/pull/59529)
-
\[[`cf06e74076`](https://redirect.github.com/nodejs/node/commit/cf06e74076)]
- **(SEMVER-MINOR)** **stream**: add brotli support to CompressionStream
and DecompressionStream (Matthew Aitken)
[#&#8203;59464](https://redirect.github.com/nodejs/node/pull/59464)
-
\[[`903ebd373a`](https://redirect.github.com/nodejs/node/commit/903ebd373a)]
- **test**: skip more sea tests on Linux ppc64le (Richard Lau)
[#&#8203;59755](https://redirect.github.com/nodejs/node/pull/59755)
-
\[[`e961060bb6`](https://redirect.github.com/nodejs/node/commit/e961060bb6)]
- **test**: fix internet/test-dns (Michaël Zasso)
[#&#8203;59660](https://redirect.github.com/nodejs/node/pull/59660)
- \[[`c2b22f50a8`](https://redirect.git

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi41OS4wIiwidXBkYXRlZEluVmVyIjoiNDIuNTkuMCIsInRhcmdldEJyYW5jaCI6ImNhbmFyeSIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-29 18:55:11 +08:00
DarkSky
6951f1002f feat: improve event handle (#14177) 2025-12-29 18:54:59 +08:00
DarkSky
20a80015c0 feat: integrate native indexer for mobile (#14174)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **New Features**
* Added full-text search functionality to mobile apps (Android and iOS),
enabling document indexing and search capabilities.
* Enhanced blob upload support with new GraphQL mutations for creating,
completing, and managing file uploads.

* **Improvements**
* iOS and Android now use SQLite storage backend for improved indexing
performance, aligning with desktop experience.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-28 21:34:39 +08:00
DarkSky
504460438f fix: mobile mult-select tag delete (#14172)
fix #14167

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## New Features
* Added Backspace key support to delete the last selected tag when the
input field is empty
* Added delete icon buttons next to each tag for quick removal
* Features available on both mobile and desktop tag pickers

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-28 17:16:20 +08:00
DarkSky
582340b0b7 fix: lint 2025-12-28 15:32:25 +08:00
renovate[bot]
11d9a41433 chore: bump up apple/swift-collections version to from: "1.3.0" (#13688)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[apple/swift-collections](https://redirect.github.com/apple/swift-collections)
| minor | `from: "1.2.1"` -> `from: "1.3.0"` |

---

### Release Notes

<details>
<summary>apple/swift-collections (apple/swift-collections)</summary>

###
[`v1.3.0`](https://redirect.github.com/apple/swift-collections/releases/tag/1.3.0):
Swift Collections 1.3.0

[Compare
Source](https://redirect.github.com/apple/swift-collections/compare/1.2.1...1.3.0)

This feature release supports Swift toolchain versions 6.0, 6.1 and 6.2,
and it includes the following improvements:

##### `BasicContainers` module

This new module collects ownership-aware, low-level variants of existing
data structures in the core standard library. In this release, this
module consists of two array variants, `UniqueArray` and `RigidArray`.

These new types are provided as less flexible, noncopyable alternatives
to the classic `Array` type. The standard `Array` implements value
semantics with the copy-on-write optimization; this inherently requires
elements to be copyable, and it is itself copyable.

`struct UniqueArray<Element>` is a noncopyable array variant that takes
away `Array`'s copy-on-write behavior, enabling support for noncopyable
elements. This type's noncopyability means mutations can always assume
that the array is uniquely owned, with no shared copies (hence the
name!). This means that array mutations such as mutating an element at
an index can behave much more predictably, with no unexpected
performance spikes due to having to copy shared storage.

`struct RigidArray<Element>` goes even further, by also disabling
dynamic resizing. Rigid arrays have a fixed capacity: they are
initialized with room for a particular number of elements, and they
never implicitly grow (nor shrink) their storage. When a rigid array's
count reaches its capacity, it becomes unable to add any new items --
inserting into a full array is considered a programming error. This
makes this a quite inflexible (or *rigid*) type indeed, as avoiding
storage overflow requires careful, up front planning on the resource
needs of the task at hand. In exchange, rigid arrays can have extremely
predictable performance characteristics.

`UniqueArray` is a great default choice when a task just needs an array
type that is able store noncopyable elements. `RigidArray` is best
reserved for use cases that require absolute, pedantic control over
memory use or latency -- such as control software running in
environments with extremely limited memory, or when a certain task must
always be completed in some given amount of time.

The `Unique` and `Rigid` prefixes applied here establish a general
naming convention for low-level variants of the classic copy-on-write
data structure implementations. Future releases are expected to flesh
out our zoo of container types by adding `Unique` and `Rigid` variants
of the existing `Set`, `Dictionary`, `Deque`, `Heap` and other
constructs, with type names such as as `RigidDictionary` and
`UniqueDeque`.

##### `TrailingElementsModule` module

This new module ships a new `TrailingArray` construct, a preview of a
new low-level, ownership-aware variant of `ManagedBuffer`. This is
primarily intended as a interoperability helper for C constructs that
consist of a fixed-size header directly followed by variable-size
storage buffer.

##### `ContainersPreview` module

This module is intended to contain previews of an upcoming
ownership-aware container model. In this initial release, this module
consists of just one construct: `struct Box<T>`.

`Box` is a wrapper type that forms a noncopyable, heap allocated box
around an arbitrary value.

#### What's Changed

- Merge release/1.1 to main by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;204](https://redirect.github.com/apple/swift-collections/pull/204)
- Merge relase/1.1 to main, without taking any changes by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;206](https://redirect.github.com/apple/swift-collections/pull/206)
- \[Heap] Add methods to replace minimum/maximum (redux) by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;208](https://redirect.github.com/apple/swift-collections/pull/208)
- Persistent collections updates (part 10) by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;207](https://redirect.github.com/apple/swift-collections/pull/207)
- Update CMakeLists.txt by
[@&#8203;compnerd](https://redirect.github.com/compnerd) in
[#&#8203;215](https://redirect.github.com/apple/swift-collections/pull/215)
- Merge latest changes from release/1.1 to main by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;220](https://redirect.github.com/apple/swift-collections/pull/220)
- Merge branch release/1.1 to main by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;231](https://redirect.github.com/apple/swift-collections/pull/231)
- \[SortedCollections] Disable tests with
[@&#8203;testable](https://redirect.github.com/testable) imports in
release builds by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;232](https://redirect.github.com/apple/swift-collections/pull/232)
- \[Hashtable] Minor Documentation Fix (Typo) by
[@&#8203;nickkohrn](https://redirect.github.com/nickkohrn) in
[#&#8203;241](https://redirect.github.com/apple/swift-collections/pull/241)
- Merge branch `release/1.1` to `main` by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;248](https://redirect.github.com/apple/swift-collections/pull/248)
- Update README.md by
[@&#8203;glessard](https://redirect.github.com/glessard) in
[#&#8203;251](https://redirect.github.com/apple/swift-collections/pull/251)
- \[OrderedDictionary] Explicitly mention in documentation that
keys/values are ordered by
[@&#8203;warpling](https://redirect.github.com/warpling) in
[#&#8203;254](https://redirect.github.com/apple/swift-collections/pull/254)
- build: support ARM64 spelling by
[@&#8203;compnerd](https://redirect.github.com/compnerd) in
[#&#8203;282](https://redirect.github.com/apple/swift-collections/pull/282)
- Merge release/1.1 to main by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;284](https://redirect.github.com/apple/swift-collections/pull/284)
- Update release checklist by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;323](https://redirect.github.com/apple/swift-collections/pull/323)
- build: update the build rules for adjusted tree layout by
[@&#8203;compnerd](https://redirect.github.com/compnerd) in
[#&#8203;331](https://redirect.github.com/apple/swift-collections/pull/331)
- build: support using swift-collections in larger projects by
[@&#8203;compnerd](https://redirect.github.com/compnerd) in
[#&#8203;330](https://redirect.github.com/apple/swift-collections/pull/330)
- Merge release/1.1 to main by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;332](https://redirect.github.com/apple/swift-collections/pull/332)
- build: support building in Debug mode on Windows by
[@&#8203;compnerd](https://redirect.github.com/compnerd) in
[#&#8203;333](https://redirect.github.com/apple/swift-collections/pull/333)
- Bugfix Incorrect Assert in BTree.removeFirst/removeLast by
[@&#8203;LeoNavel](https://redirect.github.com/LeoNavel) in
[#&#8203;349](https://redirect.github.com/apple/swift-collections/pull/349)
- Fix typos by [@&#8203;rex4539](https://redirect.github.com/rex4539) in
[#&#8203;356](https://redirect.github.com/apple/swift-collections/pull/356)
- Merge branch `release/1.1` to `main` by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;358](https://redirect.github.com/apple/swift-collections/pull/358)
- Merge.1.1→main by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;361](https://redirect.github.com/apple/swift-collections/pull/361)
- Add post-merge CI support by
[@&#8203;shahmishal](https://redirect.github.com/shahmishal) in
[#&#8203;367](https://redirect.github.com/apple/swift-collections/pull/367)
- Update CODEOWNERS by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;375](https://redirect.github.com/apple/swift-collections/pull/375)
- Merge release/1.1 to main by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;386](https://redirect.github.com/apple/swift-collections/pull/386)
- Merge release/1.1 to main by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;410](https://redirect.github.com/apple/swift-collections/pull/410)
- \[BTree]\[NFC] Rephrase some comments by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;427](https://redirect.github.com/apple/swift-collections/pull/427)
- \[CI] Pull Request testing support via GitHub Actions by
[@&#8203;shahmishal](https://redirect.github.com/shahmishal) in
[#&#8203;426](https://redirect.github.com/apple/swift-collections/pull/426)
- \[OrderedDictionary Documentation] fix a typo by
[@&#8203;Gyuni](https://redirect.github.com/Gyuni) in
[#&#8203;445](https://redirect.github.com/apple/swift-collections/pull/445)
- Install swiftmodules with full module triple by
[@&#8203;etcwilde](https://redirect.github.com/etcwilde) in
[#&#8203;470](https://redirect.github.com/apple/swift-collections/pull/470)
- \[OrderedSet] Add `OrderedSet.appending(contentsOf:)` by
[@&#8203;pm-dev](https://redirect.github.com/pm-dev) in
[#&#8203;452](https://redirect.github.com/apple/swift-collections/pull/452)
- ManagedBuffer.capacity is unavailable on OpenBSD. by
[@&#8203;3405691582](https://redirect.github.com/3405691582) in
[#&#8203;456](https://redirect.github.com/apple/swift-collections/pull/456)
- Align Heap.\_UnsafeHandle min/maxValue tie-breaking with Swift.min/max
by [@&#8203;DakshinD](https://redirect.github.com/DakshinD) in
[#&#8203;455](https://redirect.github.com/apple/swift-collections/pull/455)
- Add Heap.removeAll(where:) by
[@&#8203;DakshinD](https://redirect.github.com/DakshinD) in
[#&#8203;454](https://redirect.github.com/apple/swift-collections/pull/454)
- Merge release/1.2 to main by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;450](https://redirect.github.com/apple/swift-collections/pull/450)
- Disable `SortedCollections` module by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;479](https://redirect.github.com/apple/swift-collections/pull/479)
- Enable MemberImportVisibility and fix issues uncovered by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;480](https://redirect.github.com/apple/swift-collections/pull/480)
- fix comment for OrderedSet.appending(contentsOf:) by
[@&#8203;ozumin](https://redirect.github.com/ozumin) in
[#&#8203;478](https://redirect.github.com/apple/swift-collections/pull/478)
- Bump requirements of nested benchmarking package by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;481](https://redirect.github.com/apple/swift-collections/pull/481)
- Fix CMake build by
[@&#8203;etcwilde](https://redirect.github.com/etcwilde) in
[#&#8203;482](https://redirect.github.com/apple/swift-collections/pull/482)
- Merge changes on `release/1.2` to `main` branch by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;487](https://redirect.github.com/apple/swift-collections/pull/487)
- Enable macOS testing on GitHub Actions by
[@&#8203;shahmishal](https://redirect.github.com/shahmishal) in
[#&#8203;483](https://redirect.github.com/apple/swift-collections/pull/483)
- Fix API documentation links in README.md by
[@&#8203;azarovalex](https://redirect.github.com/azarovalex) in
[#&#8203;490](https://redirect.github.com/apple/swift-collections/pull/490)
- Skip Xcode 16.0 and 16.1 in PR workflow by
[@&#8203;natecook1000](https://redirect.github.com/natecook1000) in
[#&#8203;493](https://redirect.github.com/apple/swift-collections/pull/493)
- Fix OrderedSet example usage by
[@&#8203;azarovalex](https://redirect.github.com/azarovalex) in
[#&#8203;491](https://redirect.github.com/apple/swift-collections/pull/491)
- Add support for embedded Swift mode by
[@&#8203;parkera](https://redirect.github.com/parkera) in
[#&#8203;494](https://redirect.github.com/apple/swift-collections/pull/494)
- Include DequeModule in the Foundation toolchain build by
[@&#8203;cthielen](https://redirect.github.com/cthielen) in
[#&#8203;495](https://redirect.github.com/apple/swift-collections/pull/495)
- Fix CMake build for `release/1.2` by
[@&#8203;cthielen](https://redirect.github.com/cthielen) in
[#&#8203;498](https://redirect.github.com/apple/swift-collections/pull/498)
- fix minor typo in init docs for Deque.swift by
[@&#8203;t089](https://redirect.github.com/t089) in
[#&#8203;503](https://redirect.github.com/apple/swift-collections/pull/503)
- \[SortedSet] Fix subtreeCount inconsistency after remove at index by
[@&#8203;brianchang928](https://redirect.github.com/brianchang928) in
[#&#8203;502](https://redirect.github.com/apple/swift-collections/pull/502)
- Add the missing COLLECTIONS\_SINGLE\_MODULE when import
InternalCollectionsUtils by
[@&#8203;faimin](https://redirect.github.com/faimin) in
[#&#8203;501](https://redirect.github.com/apple/swift-collections/pull/501)
- \[SortedCollections] Fix incorrect offset calculation in
BTree.findAnyIndex by
[@&#8203;brianchang928](https://redirect.github.com/brianchang928) in
[#&#8203;506](https://redirect.github.com/apple/swift-collections/pull/506)
- \[SortedCollections] Fix B-tree root reduction during element removal
causing data loss by
[@&#8203;brianchang928](https://redirect.github.com/brianchang928) in
[#&#8203;507](https://redirect.github.com/apple/swift-collections/pull/507)
- Add checks for Wasm compatibility to `pull_request.yml` by
[@&#8203;MaxDesiatov](https://redirect.github.com/MaxDesiatov) in
[#&#8203;509](https://redirect.github.com/apple/swift-collections/pull/509)
- First round of noncopyable constructs: `Box`, `RigidArray`,
`DynamicArray` by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;508](https://redirect.github.com/apple/swift-collections/pull/508)
- \[actions] exclude Xcode 26 beta 6 by
[@&#8203;glessard](https://redirect.github.com/glessard) in
[#&#8203;514](https://redirect.github.com/apple/swift-collections/pull/514)
- Add "trailing elements" module with facilities for tail-allocated
storage by [@&#8203;DougGregor](https://redirect.github.com/DougGregor)
in
[#&#8203;513](https://redirect.github.com/apple/swift-collections/pull/513)
- \[Xcode] Add trailing elements to xcodeproj by
[@&#8203;Azoy](https://redirect.github.com/Azoy) in
[#&#8203;515](https://redirect.github.com/apple/swift-collections/pull/515)
- Containers: Naming updates, minor tweaks by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;516](https://redirect.github.com/apple/swift-collections/pull/516)
- Add BasicContainer rename to xcodeproj by
[@&#8203;Azoy](https://redirect.github.com/Azoy) in
[#&#8203;517](https://redirect.github.com/apple/swift-collections/pull/517)
- Prepare for tagging 1.3.0 by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;523](https://redirect.github.com/apple/swift-collections/pull/523)
- \[Docs] Fix landing page of collections documentation by
[@&#8203;Azoy](https://redirect.github.com/Azoy) in
[#&#8203;520](https://redirect.github.com/apple/swift-collections/pull/520)
- build: Install libraries in an `arch` sub-folder by
[@&#8203;Steelskin](https://redirect.github.com/Steelskin) in
[#&#8203;505](https://redirect.github.com/apple/swift-collections/pull/505)
- More release preparations for 1.3.0 by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;524](https://redirect.github.com/apple/swift-collections/pull/524)
- One last round of documentation updates by
[@&#8203;lorentey](https://redirect.github.com/lorentey) in
[#&#8203;525](https://redirect.github.com/apple/swift-collections/pull/525)

#### New Contributors

- [@&#8203;nickkohrn](https://redirect.github.com/nickkohrn) made their
first contribution in
[#&#8203;241](https://redirect.github.com/apple/swift-collections/pull/241)
- [@&#8203;warpling](https://redirect.github.com/warpling) made their
first contribution in
[#&#8203;254](https://redirect.github.com/apple/swift-collections/pull/254)
- [@&#8203;LeoNavel](https://redirect.github.com/LeoNavel) made their
first contribution in
[#&#8203;349](https://redirect.github.com/apple/swift-collections/pull/349)
- [@&#8203;rex4539](https://redirect.github.com/rex4539) made their
first contribution in
[#&#8203;356](https://redirect.github.com/apple/swift-collections/pull/356)
- [@&#8203;Gyuni](https://redirect.github.com/Gyuni) made their first
contribution in
[#&#8203;445](https://redirect.github.com/apple/swift-collections/pull/445)
- [@&#8203;pm-dev](https://redirect.github.com/pm-dev) made their first
contribution in
[#&#8203;452](https://redirect.github.com/apple/swift-collections/pull/452)
- [@&#8203;DakshinD](https://redirect.github.com/DakshinD) made their
first contribution in
[#&#8203;455](https://redirect.github.com/apple/swift-collections/pull/455)
- [@&#8203;ozumin](https://redirect.github.com/ozumin) made their first
contribution in
[#&#8203;478](https://redirect.github.com/apple/swift-collections/pull/478)
- [@&#8203;azarovalex](https://redirect.github.com/azarovalex) made
their first contribution in
[#&#8203;490](https://redirect.github.com/apple/swift-collections/pull/490)
- [@&#8203;natecook1000](https://redirect.github.com/natecook1000) made
their first contribution in
[#&#8203;493](https://redirect.github.com/apple/swift-collections/pull/493)
- [@&#8203;parkera](https://redirect.github.com/parkera) made their
first contribution in
[#&#8203;494](https://redirect.github.com/apple/swift-collections/pull/494)
- [@&#8203;t089](https://redirect.github.com/t089) made their first
contribution in
[#&#8203;503](https://redirect.github.com/apple/swift-collections/pull/503)
- [@&#8203;brianchang928](https://redirect.github.com/brianchang928)
made their first contribution in
[#&#8203;502](https://redirect.github.com/apple/swift-collections/pull/502)
- [@&#8203;faimin](https://redirect.github.com/faimin) made their first
contribution in
[#&#8203;501](https://redirect.github.com/apple/swift-collections/pull/501)
- [@&#8203;MaxDesiatov](https://redirect.github.com/MaxDesiatov) made
their first contribution in
[#&#8203;509](https://redirect.github.com/apple/swift-collections/pull/509)
- [@&#8203;DougGregor](https://redirect.github.com/DougGregor) made
their first contribution in
[#&#8203;513](https://redirect.github.com/apple/swift-collections/pull/513)

**Full Changelog**:
<https://github.com/apple/swift-collections/compare/1.2.1...1.3.0>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [x] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xMzEuOSIsInVwZGF0ZWRJblZlciI6IjQyLjU5LjAiLCJ0YXJnZXRCcmFuY2giOiJjYW5hcnkiLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-28 15:18:35 +08:00
renovate[bot]
f49f42ce76 chore: bump up Lakr233/ListViewKit version to from: "1.1.8" (#14078)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [Lakr233/ListViewKit](https://redirect.github.com/Lakr233/ListViewKit)
| patch | `from: "1.1.6"` -> `from: "1.1.8"` |

---

### Release Notes

<details>
<summary>Lakr233/ListViewKit (Lakr233/ListViewKit)</summary>

###
[`v1.1.8`](https://redirect.github.com/Lakr233/ListViewKit/compare/1.1.7...1.1.8)

[Compare
Source](https://redirect.github.com/Lakr233/ListViewKit/compare/1.1.7...1.1.8)

###
[`v1.1.7`](https://redirect.github.com/Lakr233/ListViewKit/compare/1.1.6...1.1.7)

[Compare
Source](https://redirect.github.com/Lakr233/ListViewKit/compare/1.1.6...1.1.7)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi4zMi4yIiwidXBkYXRlZEluVmVyIjoiNDIuMzIuMiIsInRhcmdldEJyYW5jaCI6ImNhbmFyeSIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
2025-12-28 15:17:50 +08:00
renovate[bot]
f78dc44690 chore: bump up Lakr233/MarkdownView version to from: "3.4.7" (#14090)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[Lakr233/MarkdownView](https://redirect.github.com/Lakr233/MarkdownView)
| patch | `from: "3.4.2"` -> `from: "3.4.7"` |

---

### Release Notes

<details>
<summary>Lakr233/MarkdownView (Lakr233/MarkdownView)</summary>

###
[`v3.4.7`](https://redirect.github.com/Lakr233/MarkdownView/compare/3.4.6...3.4.7)

[Compare
Source](https://redirect.github.com/Lakr233/MarkdownView/compare/3.4.6...3.4.7)

###
[`v3.4.6`](https://redirect.github.com/Lakr233/MarkdownView/compare/3.4.5...3.4.6)

[Compare
Source](https://redirect.github.com/Lakr233/MarkdownView/compare/3.4.5...3.4.6)

###
[`v3.4.5`](https://redirect.github.com/Lakr233/MarkdownView/compare/3.4.4...3.4.5)

[Compare
Source](https://redirect.github.com/Lakr233/MarkdownView/compare/3.4.4...3.4.5)

###
[`v3.4.4`](https://redirect.github.com/Lakr233/MarkdownView/compare/3.4.3...3.4.4)

[Compare
Source](https://redirect.github.com/Lakr233/MarkdownView/compare/3.4.3...3.4.4)

###
[`v3.4.3`](https://redirect.github.com/Lakr233/MarkdownView/compare/3.4.2...3.4.3)

[Compare
Source](https://redirect.github.com/Lakr233/MarkdownView/compare/3.4.2...3.4.3)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [x] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi40Mi4yIiwidXBkYXRlZEluVmVyIjoiNDIuNTkuMCIsInRhcmdldEJyYW5jaCI6ImNhbmFyeSIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-28 15:16:48 +08:00
DarkSky
a38e7e58e0 docs: update template 2025-12-28 07:39:14 +08:00
DarkSky
4f1d57ade5 feat: integrate typst preview & fix mermaid style (#14168)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Typst code block preview with interactive rendering controls (zoom,
pan, reset) and user-friendly error messages

* **Style**
  * Centered Mermaid diagram rendering for improved layout

* **Tests**
  * Added end-to-end preview validation tests for Typst and Mermaid

* **Chores**
* Added WebAssembly type declarations and updated frontend packages;
removed a build debug configuration entry

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-28 04:55:22 +08:00
DarkSky
1b532d5c6c fix: inline doc toolbar tooltip (#14169)
fix #14001

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Style**
* Updated tooltip text from "Edit" to "Edit Description" in link and
toolbar configurations to provide clearer guidance on the edit action's
purpose across the application.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-28 02:37:02 +08:00
DarkSky
6514614df8 feat: bump electron (#14158) 2025-12-27 23:54:11 +08:00
DarkSky
702dbf7be4 fix: client indexing & outdated scheme (#14160)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Optimized storage handling with platform-specific
implementations—SQLite for Electron and IndexedDB for other environments
for improved performance.

* **Bug Fixes**
* Enhanced recording file access and retrieval functionality for better
reliability.
  * Strengthened local file protocol handling and security restrictions.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-27 17:56:42 +08:00
DarkSky
78949044ec feat: improve idb perf (#14159)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Performance**
* Optimized database operations through improved batch processing to
accelerate data retrieval, updates, and deletion operations for better
efficiency.

* **Reliability**
* Enhanced transaction durability handling to strengthen data
consistency and ensure more reliable persistence of database changes and
updates.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-27 08:22:37 +08:00
DarkSky
4eed92cebf feat: improve electron sandbox (#14156) 2025-12-27 03:23:28 +08:00
DarkSky
3fe8923fc3 fix: flatpak bundle (#14155) 2025-12-26 23:37:53 +08:00
DarkSky
ca386283c5 feat: bump electron (#14151) 2025-12-26 09:41:16 +08:00
DarkSky
2e38898937 feat: refresh index if version changed (#14150) 2025-12-26 01:08:05 +08:00
DarkSky
e8693a3a25 feat: introduce fuzzy search for native indexer (#14109) 2025-12-25 04:40:23 +08:00
github-actions[bot]
b6dc68eddf chore(i18n): sync translations (#14054)
New Crowdin translations by [Crowdin GH
Action](https://github.com/crowdin/github-action)

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: DarkSky <darksky2048@gmail.com>
2025-12-24 03:12:32 +08:00
keke
08a30edb2d chore: correct the wrong file path in building doc (#14145)
When I read the
[building-desktop-client-app.md](https://github.com/toeverything/AFFiNE/blob/canary/docs/building-desktop-client-app.md)
to build Artifacts locally, I find there have some legacy path due to
some project structure updates.

So this is a litte fix to correct the unmatched path in the doc.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Updated desktop client app building documentation to reflect changes
in the project structure and configuration setup.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-24 02:45:39 +08:00
Daniel Dybing
6c9ab603eb feat(i18n): updated Norwegian translations to 20% (#14133)
Updated translations for Norwegian Bokmål to 20%. 

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Localization**
* Enhanced Norwegian Bokmål language support with expanded translations
covering profile settings, email verification, journal, tags, copy
actions, edgeless mode, and additional interface elements.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
2025-12-24 01:42:23 +08:00
DarkSky
4b721dffe0 feat: set admin name when self hosted init (#14146)
fix #14134
2025-12-23 23:38:34 +08:00
DarkSky
a1f1c61a9f fix: ci 2025-12-23 23:26:19 +08:00
DarkSky
76524084d1 feat: multipart blob sync support (#14138)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Flexible blob uploads: GRAPHQL, presigned, and multipart flows with
per‑part URLs, abort/complete operations, presigned proxy endpoints, and
nightly cleanup of expired pending uploads.

* **API / Schema**
* GraphQL additions: new types, mutations, enum and error to manage
upload lifecycle (create, complete, abort, get part URL).

* **Database**
* New blob status enum and columns (status, upload_id); listing now
defaults to completed blobs.

* **Localization**
  * Added user-facing message: "Blob is invalid."

* **Tests**
* Expanded unit and end‑to‑end coverage for upload flows, proxy
behavior, multipart and provider integrations.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-23 22:09:21 +08:00
DarkSky
a9937e18b6 fix: cleanup expired records (#14140) 2025-12-23 22:08:57 +08:00
Daniel Dybing
7539135c4d feat(i18n): Updated Norwegian translations to 12% (#14125)
Updated Norwegian bokmål translations to 12% completeness.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Localization**
* Expanded Norwegian language support with numerous new translations for
UI elements, including workspace settings, keyboard shortcuts,
authentication messages, and cloud features.
* Improved translation coverage for Norwegian, bringing the completeness
metric from 9 to 12 with additional localized strings across the
application.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-21 20:02:44 +00:00
Daniel Dybing
8f59509e73 feat(editor): add delete key support for table view (#14119)
This PR allows the user to use the `Delete` key to delete the content of
one or more cells in a Table View. Previously, this was only possible to
do with the `Backspace` key. Both keys can now be used, which is often
the norm in other tools - such as Notion and Excel.

In short, the logic for the `Backspace` key has been moved to a separate
function which is called by keyevents from both the `Backspace` and
`Delete` keys.

Affected files: 
-
blocksuite/affine/data-view/src/view-presets/table/pc-virtual/controller/hotkeys.ts
-
blocksuite/affine/data-view/src/view-presets/table/pc/controller/hotkeys.ts

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Optimized table hotkey handling logic to consolidate delete and
backspace operations for improved code maintainability.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-22 03:17:05 +08:00
DarkSky
321965a424 feat: impl text delta support (#14132) 2025-12-22 03:16:16 +08:00
renovate[bot]
efbdee5508 chore: bump up storybook version to v10.1.10 [SECURITY] (#14131)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [storybook](https://storybook.js.org)
([source](https://redirect.github.com/storybookjs/storybook/tree/HEAD/code/core))
| [`10.1.5` ->
`10.1.10`](https://renovatebot.com/diffs/npm/storybook/10.1.5/10.1.10) |
![age](https://developer.mend.io/api/mc/badges/age/npm/storybook/10.1.10?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/storybook/10.1.5/10.1.10?slim=true)
|

### GitHub Vulnerability Alerts

####
[CVE-2025-68429](https://redirect.github.com/storybookjs/storybook/security/advisories/GHSA-8452-54wp-rmv6)

On December 11th, the Storybook team received a responsible disclosure
alerting them to a potential vulnerability in certain built and
published Storybooks.

The vulnerability is a bug in how Storybook handles environment
variables defined in a `.env` file, which could, in specific
circumstances, lead to those variables being unexpectedly bundled into
the artifacts created by the `storybook build` command. When a built
Storybook is published to the web, the bundle’s source is viewable, thus
potentially exposing those variables to anyone with access. If those
variables contained secrets, they should be considered compromised.

## Who is impacted?

For a project to be vulnerable to this issue, it must:

- Build the Storybook (i.e. run `storybook build` directly or
indirectly) in a directory that contains a `.env` file (including
variants like `.env.local`)
- The `.env` file contains sensitive secrets
- Use Storybook version `7.0.0` or above
- Publish the built Storybook to the web

Storybooks built without a `.env` file at build time are not affected,
including common CI-based builds where secrets are provided via platform
environment variables rather than `.env` files.

Users' Storybook runtime environments (i.e. `storybook dev`) are not
affected. Deployed applications that share a repo with a project's
Storybook are not affected.

Storybook 6 and below are not affected.

## Recommended actions

First, Storybook recommends that everyone audit for any sensitive
secrets provided via `.env` files and rotate those keys.

Second, Storybook has released patched versions of all affected major
Storybook versions that no longer have this vulnerability. Projects
should upgrade their Storybook—on both local machines and CI
environments—to one of these versions **before publishing again**.

- `10.1.10+`
- `9.1.17+`
- `8.6.15+`
- `7.6.21+`

Finally, some projects may have been relying on the undocumented
behavior at the heart of this issue and will need to change how they
reference environment variables after this update. If a project can no
longer read necessary environmental variable values, it can either
prefix the variables with `STORYBOOK_` or use the [`env` property in
Storybook’s
configuration](https://storybook.js.org/docs/configure/environment-variables#using-storybook-configuration)
to manually specify values. In either case, **do not** include sensitive
secrets as they *will* be included in the built bundle.

## Further information

Details of the vulnerability can be found on the [Storybook
announcement](https://storybook.js.org/blog/security-advisory).

---

### Release Notes

<details>
<summary>storybookjs/storybook (storybook)</summary>

###
[`v10.1.10`](https://redirect.github.com/storybookjs/storybook/blob/HEAD/CHANGELOG.md#10110)

[Compare
Source](https://redirect.github.com/storybookjs/storybook/compare/v10.1.9...v10.1.10)

- Core: Fix `.env`-file parsing -
[#&#8203;33383](https://redirect.github.com/storybookjs/storybook/pull/33383),
thanks [@&#8203;JReinhold](https://redirect.github.com/JReinhold)!
- Next.js: Handle v14 compatibility for draftMode import -
[#&#8203;33341](https://redirect.github.com/storybookjs/storybook/pull/33341),
thanks [@&#8203;tanujbhaud](https://redirect.github.com/tanujbhaud)!

###
[`v10.1.9`](https://redirect.github.com/storybookjs/storybook/blob/HEAD/CHANGELOG.md#1019)

[Compare
Source](https://redirect.github.com/storybookjs/storybook/compare/v10.1.8...v10.1.9)

- Telemetry: Remove instance of check for sub-error handling -
[#&#8203;33356](https://redirect.github.com/storybookjs/storybook/pull/33356),
thanks
[@&#8203;valentinpalkovic](https://redirect.github.com/valentinpalkovic)!

###
[`v10.1.8`](https://redirect.github.com/storybookjs/storybook/compare/v10.1.7...7cd0cbca4ee2f2c082c9876de2fb2feba6c12bbf)

[Compare
Source](https://redirect.github.com/storybookjs/storybook/compare/v10.1.7...v10.1.8)

###
[`v10.1.7`](https://redirect.github.com/storybookjs/storybook/blob/HEAD/CHANGELOG.md#1017)

[Compare
Source](https://redirect.github.com/storybookjs/storybook/compare/v10.1.6...v10.1.7)

- Automigrate: Fix missing await -
[#&#8203;33333](https://redirect.github.com/storybookjs/storybook/pull/33333),
thanks
[@&#8203;valentinpalkovic](https://redirect.github.com/valentinpalkovic)!
- CLI: Remove REACT\_PROJECT projectType -
[#&#8203;33334](https://redirect.github.com/storybookjs/storybook/pull/33334),
thanks
[@&#8203;valentinpalkovic](https://redirect.github.com/valentinpalkovic)!
- Core: Exclude open from pre-bundling to make local xdg-open reachable
-
[#&#8203;33325](https://redirect.github.com/storybookjs/storybook/pull/33325),
thanks [@&#8203;Sidnioulz](https://redirect.github.com/Sidnioulz)!
- Nextjs-Vite: Install `vite` during migration if not installed yet -
[#&#8203;33316](https://redirect.github.com/storybookjs/storybook/pull/33316),
thanks [@&#8203;ghengeveld](https://redirect.github.com/ghengeveld)!
- Telemetry: Fix race condition in telemetry cache causing malformed
JSON -
[#&#8203;33323](https://redirect.github.com/storybookjs/storybook/pull/33323),
thanks
[@&#8203;valentinpalkovic](https://redirect.github.com/valentinpalkovic)!

###
[`v10.1.6`](https://redirect.github.com/storybookjs/storybook/blob/HEAD/CHANGELOG.md#1016)

[Compare
Source](https://redirect.github.com/storybookjs/storybook/compare/v10.1.5...v10.1.6)

- Manager: Do not display non-existing shortcuts in the settings page -
[#&#8203;32711](https://redirect.github.com/storybookjs/storybook/pull/32711),
thanks [@&#8203;DKER2](https://redirect.github.com/DKER2)!
- Preview: Enforce inert body if manager is focus-trapped -
[#&#8203;33186](https://redirect.github.com/storybookjs/storybook/pull/33186),
thanks [@&#8203;Sidnioulz](https://redirect.github.com/Sidnioulz)!
- Telemetry: Await pending operations in getLastEvents to prevent race
conditions -
[#&#8203;33285](https://redirect.github.com/storybookjs/storybook/pull/33285),
thanks
[@&#8203;valentinpalkovic](https://redirect.github.com/valentinpalkovic)!
- UI: Fix keyboard navigation bug for "reset" option in `Select` -
[#&#8203;33268](https://redirect.github.com/storybookjs/storybook/pull/33268),
thanks [@&#8203;Sidnioulz](https://redirect.github.com/Sidnioulz)!

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "" (UTC), Automerge - At any time (no
schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi41OS4wIiwidXBkYXRlZEluVmVyIjoiNDIuNTkuMCIsInRhcmdldEJyYW5jaCI6ImNhbmFyeSIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-21 09:21:20 +00:00
Daniel Dybing
28a1ac4772 feat(core): focus on text body when opening journal (#14122)
Related to issue https://github.com/toeverything/AFFiNE/issues/14094

This PR makes it so that focus is put on the input body when loading a
journal. A check is made when loading the document whether it is a
normal document or a journal document. If it is a journal document, the
last noteblock in the document is focused on. This does not change how
the title is focused on normal documents.

This makes it more effortless to use the journal, as you don't have to
click on the body of the journal after opening/creating it.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Improved editor focus for journal documents: when opening or switching
to a journal the cursor now auto-positions to the end of the last note
entry (or the input area) after a short, smooth delay for faster typing
and reliable focus behavior.

* **Bug Fixes**
* Added safeguards and error handling to make automatic focus more
robust across load and editor states.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-20 06:45:40 +00:00
Daniel Dybing
caeec23ec6 feat(i18n): added support for Norwegian (Bokmål) (#14121)
Added support for Norwegian (Bokmål). 

Translation completeness is currently at 9%. 

<img width="1908" height="909" alt="Screenshot from 2025-12-18 13-57-15"
src="https://github.com/user-attachments/assets/4a6def20-92d5-4415-9976-301e23887187"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Norwegian Bokmål (nb-NO) language is now available with localized
interface and UI translations.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-19 17:24:17 +08:00
Daniel Dybing
a1767ebedb fix(core): fixed keyboard shortcut help for Windows and Linux (#14088)
This PR is related to issue
https://github.com/toeverything/AFFiNE/issues/13290

Keyboard shortcut for copying a private link works as expected, but the
overview of shortcuts shows the Mac shortcut for Windows, web and Linux
users. This fix shows the correct (Ctrl+Shift+C) shortcut to the
aforementioned users.

I have not tested this on a Mac (neither in browser nor in the app), but
ideally this should not have an impact for Mac users as the logic for
showing the correct shortcut is already implemented.

Affected files: 
- packages/frontend/core/src/components/hooks/affine/use-shortcuts.ts


Old: 
<img width="1402" height="946" alt="old_shortcut"
src="https://github.com/user-attachments/assets/5c8f2133-2b4d-49c7-8054-851c7de8f3cd"
/>

New:
<img width="650" height="379" alt="Keyboard shortcut fix"
src="https://github.com/user-attachments/assets/a29e2f7a-53d7-4743-a9b1-aa30e7622dd1"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Corrected the keyboard shortcut for copying private links on Windows
from Command+Shift+C to Ctrl+Shift+C.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-17 19:58:01 +08:00
hi
b052c92421 fix: fix typo in link shortcut key binding (#14117)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Corrected keyboard shortcut mapping for link function, ensuring it
properly recognizes Ctrl+K command.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-17 10:15:25 +00:00
Daniel Dybing
66407f2b2f feat(core): adapt date fields in database for notion import (#14111)
This is related to issue/feature request
https://github.com/toeverything/AFFiNE/issues/13962.

This PR extends the Notion import functionality to properly handle date
fields from databases. Previously, these were imported as text (see
photo below), which served little purpose. These Notion date fields are
now parsed as actual dates, and imported to AFFiNE as epoch time (which
is what the date field in AFFiNe expects). Because of this, even date
fields with time (e.g. 09:00 AM) are also handled correctly - although
they are only shown as dates, since AFFiNE's `Date` field does not
support time.

Tested with several Notion imports both with and without time, and they
all seem to work correctly.


Affected files: 
- blocksuite/affine/blocks/database/src/adapters/notion-html.ts

Old: 
<img width="802" height="305" alt="image"
src="https://github.com/user-attachments/assets/44019dba-cffb-4a30-a5ea-69cd9f86e0a1"
/>

New: 
<img width="804" height="271" alt="image"
src="https://github.com/user-attachments/assets/3f52f328-7ee3-4754-9726-10dcfa0f8462"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Enhanced Notion imports with automatic date column detection. When
importing Notion databases, date fields are now automatically
recognized, properly configured as date columns, and formatted
correctly. This improvement ensures accurate data preservation,
eliminates manual type corrections, and provides a streamlined import
experience for all users working with date-rich Notion databases.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-16 02:55:34 +00:00
Daniel Dybing
f5076a37ae feat(core): find todo list based on 'checkbox' search query (#13982)
This feature enhances the /slash command by allowing users to search for
'checkbox' and have the to-do list item show up as a result. Users come
from different systems and environments, and some may use the name
'checkbox' but be confused as they cannot find it in the search menu.

This is achieved by adding a `searchAlias` property on the to-do list
item block that contains the string `checkbox`.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added search-alias support for slash menu items so entries can be
found by alternative terms.
* To-do List entry now includes "checkbox" as an additional searchable
alias to improve discoverability.
* Slash menu search results updated to reflect alias-driven matches
(additional item appears when searching).

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
2025-12-15 15:32:00 +08:00
DarkSky
4717886c9e fix: title icon display (#14101)
fix #14073
2025-12-14 01:00:01 +08:00
DarkSky
844b9d9592 feat(server): impl native reader for server (#14100) 2025-12-14 00:28:43 +08:00
Xun Sun
a0eeed0cdb feat: implement export as PDF (#14057)
I used [pdfmake](https://www.npmjs.com/package/pdfmake) to implement an
"export as PDF" feature, and I am happy to share with you!

This should fix #13577, fix #8846, and fix #13959.

A showcase:

[Getting
Started.pdf](https://github.com/user-attachments/files/24013057/Getting.Started.pdf)

Although it might miss rendering some properties currently, it can
evolve in the long run and provide a more native experience for the
users.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
- Experimental "Export to PDF" option added to the export menu (behind a
feature flag)
- PDF export supports headings, paragraphs, lists, code blocks, tables,
images, callouts, linked documents and embedded content

* **Chores**
  - Added PDF rendering library and consolidated PDF utilities
  - Feature flag introduced to control rollout

* **Tests**
  - Comprehensive unit tests added for PDF content rendering logic

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: DarkSky <darksky2048@gmail.com>
2025-12-13 18:05:25 +08:00
Fangdun Tsai
246e09e0cd fix: roll back electron version to v35 (#14089)
In electron v36, all workers do not work. 
The webpack configuration is too complicated, so go back first.

If start a new project with [forge](https://www.electronforge.io/) and
latest electron, the worker works well.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Downgraded the Electron development/runtime used for building and
testing the desktop app from v36 to v35; this is a
development-environment change with no functional or API changes
affecting end users.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-12 02:46:58 +00:00
renovate[bot]
7f96c97b67 chore: bump up rustc version to v1.92.0 (#13624)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [rustc](https://redirect.github.com/rust-lang/rust) | minor | `1.91.0`
-> `1.92.0` |

---

### Release Notes

<details>
<summary>rust-lang/rust (rustc)</summary>

###
[`v1.92.0`](https://redirect.github.com/rust-lang/rust/blob/HEAD/RELEASES.md#Version-1920-2025-12-11)

[Compare
Source](https://redirect.github.com/rust-lang/rust/compare/1.91.1...1.92.0)

\==========================

<a id="1.92.0-Language"></a>

## Language

- [Document `MaybeUninit` representation and
validity](https://redirect.github.com/rust-lang/rust/pull/140463)
- [Allow `&raw [mut | const]` for union field in safe
code](https://redirect.github.com/rust-lang/rust/pull/141469)
- [Prefer item bounds of associated types over where-bounds for
auto-traits and
`Sized`](https://redirect.github.com/rust-lang/rust/pull/144064)
- [Do not materialize `X` in `[X; 0]` when `X` is unsizing a
const](https://redirect.github.com/rust-lang/rust/pull/145277)
- [Support combining `#[track_caller]` and `#[no_mangle]` (requires
every declaration specifying `#[track_caller]` as
well)](https://redirect.github.com/rust-lang/rust/pull/145724)
- [Make never type lints `never_type_fallback_flowing_into_unsafe` and
`dependency_on_unit_never_type_fallback`
deny-by-default](https://redirect.github.com/rust-lang/rust/pull/146167)
- [Allow specifying multiple bounds for same associated item, except in
trait objects](https://redirect.github.com/rust-lang/rust/pull/146593)
- [Slightly strengthen higher-ranked region handling in
coherence](https://redirect.github.com/rust-lang/rust/pull/146725)
- [The `unused_must_use` lint no longer warns on `Result<(),
Uninhabited>` (for instance, `Result<(), !>`), or
`ControlFlow<Uninhabited,
()>`](https://redirect.github.com/rust-lang/rust/pull/147382). This
avoids having to check for an error that can never happen.

<a id="1.92.0-Compiler"></a>

## Compiler

- [Make `mips64el-unknown-linux-muslabi64` link
dynamically](https://redirect.github.com/rust-lang/rust/pull/146858)
- [Remove current code for embedding command-line args in
PDB](https://redirect.github.com/rust-lang/rust/pull/147022)
Command-line information is typically not needed by debugging tools, and
the removed code
was causing problems for incremental builds even on targets that don't
use PDB debuginfo.

<a id="1.92.0-Libraries"></a>

## Libraries

- [Specialize `Iterator::eq{_by}` for `TrustedLen`
iterators](https://redirect.github.com/rust-lang/rust/pull/137122)
- [Simplify `Extend` for
tuples](https://redirect.github.com/rust-lang/rust/pull/138799)
- [Added details to `Debug` for
`EncodeWide`](https://redirect.github.com/rust-lang/rust/pull/140153).
-
[`iter::Repeat::last`](https://redirect.github.com/rust-lang/rust/pull/147258)
and [`count`](https://redirect.github.com/rust-lang/rust/pull/146410)
will now panic, rather than looping infinitely.

<a id="1.92.0-Stabilized-APIs"></a>

## Stabilized APIs

-
[`NonZero<u{N}>::div_ceil`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.div_ceil)
-
[`Location::file_as_c_str`](https://doc.rust-lang.org/stable/std/panic/struct.Location.html#method.file_as_c_str)
-
[`RwLockWriteGuard::downgrade`](https://doc.rust-lang.org/stable/std/sync/struct.RwLockWriteGuard.html#method.downgrade)
-
[`Box::new_zeroed`](https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.new_zeroed)
-
[`Box::new_zeroed_slice`](https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.new_zeroed_slice)
-
[`Rc::new_zeroed`](https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.new_zeroed)
-
[`Rc::new_zeroed_slice`](https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.new_zeroed_slice)
-
[`Arc::new_zeroed`](https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.new_zeroed)
-
[`Arc::new_zeroed_slice`](https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.new_zeroed_slice)
-
[`btree_map::Entry::insert_entry`](https://doc.rust-lang.org/stable/std/collections/btree_map/enum.Entry.html#method.insert_entry)
-
[`btree_map::VacantEntry::insert_entry`](https://doc.rust-lang.org/stable/std/collections/btree_map/struct.VacantEntry.html#method.insert_entry)
- [`impl Extend<proc_macro::Group> for
proc_macro::TokenStream`](https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CGroup%3E-for-TokenStream)
- [`impl Extend<proc_macro::Literal> for
proc_macro::TokenStream`](https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CLiteral%3E-for-TokenStream)
- [`impl Extend<proc_macro::Punct> for
proc_macro::TokenStream`](https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CPunct%3E-for-TokenStream)
- [`impl Extend<proc_macro::Ident> for
proc_macro::TokenStream`](https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CIdent%3E-for-TokenStream)

These previously stable APIs are now stable in const contexts:

-
[`<[_]>::rotate_left`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.rotate_left)
-
[`<[_]>::rotate_right`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.rotate_right)

<a id="1.92.0-Cargo"></a>

## Cargo

- [Added a new
chapter](https://redirect.github.com/rust-lang/cargo/issues/16119) to
the Cargo book, ["Optimizing Build
Performance"](https://doc.rust-lang.org/stable/cargo/guide/build-performance.html).

<a id="1.92.0-Rustdoc"></a>

## Rustdoc

- [If a trait item appears in rustdoc search, hide the corresponding
impl items](https://redirect.github.com/rust-lang/rust/pull/145898).
Previously a search for "last" would show both `Iterator::last` as well
as impl methods like `std::vec::IntoIter::last`. Now these impl methods
will be hidden, freeing up space for inherent methods like
`BTreeSet::last`.
- [Relax rules for identifiers in
search](https://redirect.github.com/rust-lang/rust/pull/147860).
Previously you could only search for identifiers that were valid in rust
code, now searches only need to be valid as part of an identifier. For
example, you can now perform a search that starts with a digit.

<a id="1.92.0-Compatibility-Notes"></a>

## Compatibility Notes

- [Fix backtraces with `-C panic=abort` on Linux by generating unwind
tables by
default](https://redirect.github.com/rust-lang/rust/pull/143613). Build
with `-C force-unwind-tables=no` to keep omitting unwind tables.

* As part of the larger effort refactoring compiler built-in attributes
and their diagnostics, [the future-compatibility lint
`invalid_macro_export_arguments` is upgraded to deny-by-default and will
be reported in dependencies
too.](https://redirect.github.com/rust-lang/rust/pull/143857)
* [Update the minimum external LLVM to
20](https://redirect.github.com/rust-lang/rust/pull/145071)
* [Prevent downstream `impl DerefMut for
Pin<LocalType>`](https://redirect.github.com/rust-lang/rust/pull/145608)
* [Don't apply temporary lifetime extension rules to the arguments of
non-extended `pin!` and formatting
macros](https://redirect.github.com/rust-lang/rust/pull/145838)

###
[`v1.91.1`](https://redirect.github.com/rust-lang/rust/blob/HEAD/RELEASES.md#Version-1911-2025-11-10)

[Compare
Source](https://redirect.github.com/rust-lang/rust/compare/1.91.0...1.91.1)

\===========================

<a id="1.91.1"></a>

- [Enable file locking support in
illumos](https://redirect.github.com/rust-lang/rust/pull/148322). This
fixes Cargo not locking the build directory on illumos.
- [Fix `wasm_import_module` attribute
cross-crate](https://redirect.github.com/rust-lang/rust/pull/148363).
This fixes linker errors on WASM targets.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45Ny4xMCIsInVwZGF0ZWRJblZlciI6IjQyLjQyLjIiLCJ0YXJnZXRCcmFuY2giOiJjYW5hcnkiLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-12 02:26:29 +00:00
Richard Lora
f832b28dac feat(editor): add date grouping configurations (#12679)
https://github.com/user-attachments/assets/d5578060-2c8c-47a5-ba65-ef2e9430518b

This PR adds the ability to group-by date with configuration which an
example is shown in the image below:


![image](https://github.com/user-attachments/assets/8762342a-999e-444e-afa2-5cfbf7e24907)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Date-based grouping modes (relative, day, week Sun/Mon, month, year),
a date group renderer, and quick lookup for group-by configs by name.

* **Improvements**
* Enhanced group settings: date sub‑modes, week‑start, per‑group
visibility, Hide All/Show All, date sort order, improved drag/drop and
reorder.
* Consistent popup placement/middleware, nested popup positioning,
per‑item close-on-select, and enforced minimum menu heights.
* UI: empty groups now display "No <property>"; views defensively handle
null/hidden groups.

* **Tests**
  * Added unit tests for date-key sorting and comparison.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Norkz <richardlora557@gmail.com>
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
2025-12-11 22:32:21 +00:00
DarkSky
b258fc3775 feat: update dmg compress algorithm 2025-12-11 21:11:35 +08:00
Mord0reK
396cda2fff feat(i18n): add Polish language support (#14080)
It's my first time making a pull request to any repo. If there are any
issues, let me know.

## Summary
Adds Polish language support. Translation is 98% complete (10,447/10,646
words).

## Changes
- Added `pl` to Language type
- Added Polish to SUPPORTED_LANGUAGES with lazy loading

The `pl.json` file already exists in the repo with good translation
coverage. Some AI-related strings are not yet translated but will fall
back to English.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added Polish language support to the application, including localized
language name, native language name, and flag emoji.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-10 12:12:47 +00:00
DarkSky
cb0ff04efa feat: bump more deps (#14079) 2025-12-10 16:02:28 +08:00
DarkSky
40f3337d45 feat: bump deps (#14076)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Updated core dependencies, developer tooling and Rust toolchain to
newer stable versions across the repo
* Upgraded Storybook to v10 and improved ESM path resolution for
storybook tooling
* Broadened native binding platform/architecture support and
strengthened native module version validation, loading and WASI handling

* **New Features**
* Exposed an additional native text export for consumers (enhanced
JS/native surface)

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-10 03:52:14 +08:00
DarkSky
215541d331 feat: improve indexing perf with native indexer (#14066)
fix #12132, #14006, #13496, #12375, #12132 

The previous idb indexer generated a large number of scattered writes
when flushing to disk, which caused CPU and disk write spikes. If the
document volume is extremely large, the accumulation of write
transactions will cause memory usage to continuously increase.

This PR introduces batch writes to mitigate write performance on the web
side, and adds a native indexer on the Electron side to greatly improve
performance.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Full-text search (FTS) added across storage layers and native plugins:
indexing, search, document retrieval, match ranges, and index flushing.
* New SQLite-backed indexer storage, streaming search/aggregate APIs,
and in-memory index with node-building and highlighting.

* **Performance**
* Indexing rewritten for batched, concurrent writes and parallel
metadata updates.
* Search scoring enhanced to consider multiple term positions and
aggregated term data.

* **Other**
  * Configurable refresh interval and indexer version bump.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-09 22:04:50 +08:00
Carson Yang
90d0ca847a docs(README): Update README with new Sealos features (#14067)
Updated the README to reflect changes in Sealos description, features,
and deployment links.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Added Sealos-related badges and links to the Self-Host section in the
README.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-09 14:37:00 +08:00
DarkSky
255b4571c0 chore: bump x86 mac ci 2025-12-09 11:29:44 +08:00
DarkSky
2efb41fc1a chore: change releaser 2025-12-09 10:39:31 +08:00
DarkSky
027f741ed6 chore: bump deps (#14065)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Updated dependency versions across the monorepo (notably zod →
^3.25.76 and vitest-related packages → ^3.2.4), plus minor package bumps
to align tooling and libraries. These are manifest/test-tooling updates
only; no public API, behavior, or end-user features were changed.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-08 21:47:25 +08:00
DarkSky
bc115baf35 chore: update docs 2025-12-08 11:39:22 +08:00
1009 changed files with 54893 additions and 18601 deletions

View File

@@ -6,7 +6,6 @@ yarn install
# Build Server Dependencies
yarn affine @affine/server-native build
yarn affine @affine/reader build
# Create database
yarn affine @affine/server prisma migrate reset -f

View File

@@ -397,7 +397,7 @@
},
"urlPrefix": {
"type": "string",
"description": "The presigned url prefix for the cloudflare r2 storage provider.\nsee https://developers.cloudflare.com/waf/custom-rules/use-cases/configure-token-authentication/ to configure it.\nExample value: \"https://storage.example.com\"\nExample rule: is_timed_hmac_valid_v0(\"your_secret\", http.request.uri, 10800, http.request.timestamp.sec, 6)"
"description": "The custom domain URL prefix for the cloudflare r2 storage provider.\nWhen `enabled=true` and `urlPrefix` + `signKey` are provided, the server will:\n- Redirect GET requests to this custom domain with an HMAC token.\n- Return upload URLs under `/api/storage/*` for uploads.\nPresigned/upload proxy TTL is 1 hour.\nsee https://developers.cloudflare.com/waf/custom-rules/use-cases/configure-token-authentication/ to configure it.\nExample value: \"https://storage.example.com\"\nExample rule: is_timed_hmac_valid_v0(\"your_secret\", http.request.uri, 10800, http.request.timestamp.sec, 6)"
},
"signKey": {
"type": "string",
@@ -518,7 +518,7 @@
},
"urlPrefix": {
"type": "string",
"description": "The presigned url prefix for the cloudflare r2 storage provider.\nsee https://developers.cloudflare.com/waf/custom-rules/use-cases/configure-token-authentication/ to configure it.\nExample value: \"https://storage.example.com\"\nExample rule: is_timed_hmac_valid_v0(\"your_secret\", http.request.uri, 10800, http.request.timestamp.sec, 6)"
"description": "The custom domain URL prefix for the cloudflare r2 storage provider.\nWhen `enabled=true` and `urlPrefix` + `signKey` are provided, the server will:\n- Redirect GET requests to this custom domain with an HMAC token.\n- Return upload URLs under `/api/storage/*` for uploads.\nPresigned/upload proxy TTL is 1 hour.\nsee https://developers.cloudflare.com/waf/custom-rules/use-cases/configure-token-authentication/ to configure it.\nExample value: \"https://storage.example.com\"\nExample rule: is_timed_hmac_valid_v0(\"your_secret\", http.request.uri, 10800, http.request.timestamp.sec, 6)"
},
"signKey": {
"type": "string",
@@ -595,6 +595,11 @@
"description": "Multiple hosts the server will accept requests from.\n@default []",
"default": []
},
"listenAddr": {
"type": "string",
"description": "The address to listen on (e.g., 0.0.0.0 for IPv4, :: for IPv6).\n@default \"0.0.0.0\"\n@environment `LISTEN_ADDR`",
"default": "0.0.0.0"
},
"port": {
"type": "number",
"description": "Which port the server will listen on.\n@default 3010\n@environment `AFFINE_SERVER_PORT`",
@@ -611,11 +616,6 @@
"type": "object",
"description": "Configuration for flags module",
"properties": {
"earlyAccessControl": {
"type": "boolean",
"description": "Only allow users with early access features to access the app\n@default false",
"default": false
},
"allowGuestDemoWorkspace": {
"type": "boolean",
"description": "Whether allow guest users to create demo workspaces.\n@default true",
@@ -634,6 +634,45 @@
}
}
},
"telemetry": {
"type": "object",
"description": "Configuration for telemetry module",
"properties": {
"allowedOrigin": {
"type": "array",
"description": "Allowed origins for telemetry collection.\n@default [\"localhost\",\"127.0.0.1\"]",
"default": [
"localhost",
"127.0.0.1"
]
},
"ga4.measurementId": {
"type": "string",
"description": "GA4 Measurement ID for Measurement Protocol.\n@default \"\"\n@environment `GA4_MEASUREMENT_ID`",
"default": ""
},
"ga4.apiSecret": {
"type": "string",
"description": "GA4 API secret for Measurement Protocol.\n@default \"\"\n@environment `GA4_API_SECRET`",
"default": ""
},
"dedupe.ttlHours": {
"type": "number",
"description": "Telemetry dedupe TTL in hours.\n@default 24",
"default": 24
},
"dedupe.maxEntries": {
"type": "number",
"description": "Telemetry dedupe max entries.\n@default 100000",
"default": 100000
},
"batch.maxEvents": {
"type": "number",
"description": "Max events per telemetry batch.\n@default 25",
"default": 25
}
}
},
"client": {
"type": "object",
"description": "Configuration for client module",
@@ -650,6 +689,40 @@
}
}
},
"calendar": {
"type": "object",
"description": "Configuration for calendar module",
"properties": {
"google": {
"type": "object",
"description": "Google Calendar integration config\n@default {\"enabled\":false,\"clientId\":\"\",\"clientSecret\":\"\",\"externalWebhookUrl\":\"\",\"webhookVerificationToken\":\"\"}\n@link https://developers.google.com/calendar/api/guides/push",
"properties": {
"enabled": {
"type": "boolean"
},
"clientId": {
"type": "string"
},
"clientSecret": {
"type": "string"
},
"externalWebhookUrl": {
"type": "string"
},
"webhookVerificationToken": {
"type": "string"
}
},
"default": {
"enabled": false,
"clientId": "",
"clientSecret": "",
"externalWebhookUrl": "",
"webhookVerificationToken": ""
}
}
}
},
"captcha": {
"type": "object",
"description": "Configuration for captcha module",
@@ -928,7 +1001,7 @@
},
"urlPrefix": {
"type": "string",
"description": "The presigned url prefix for the cloudflare r2 storage provider.\nsee https://developers.cloudflare.com/waf/custom-rules/use-cases/configure-token-authentication/ to configure it.\nExample value: \"https://storage.example.com\"\nExample rule: is_timed_hmac_valid_v0(\"your_secret\", http.request.uri, 10800, http.request.timestamp.sec, 6)"
"description": "The custom domain URL prefix for the cloudflare r2 storage provider.\nWhen `enabled=true` and `urlPrefix` + `signKey` are provided, the server will:\n- Redirect GET requests to this custom domain with an HMAC token.\n- Return upload URLs under `/api/storage/*` for uploads.\nPresigned/upload proxy TTL is 1 hour.\nsee https://developers.cloudflare.com/waf/custom-rules/use-cases/configure-token-authentication/ to configure it.\nExample value: \"https://storage.example.com\"\nExample rule: is_timed_hmac_valid_v0(\"your_secret\", http.request.uri, 10800, http.request.timestamp.sec, 6)"
},
"signKey": {
"type": "string",

View File

@@ -1,6 +1,8 @@
# Editor configuration, see http://editorconfig.org
root = true
[*.rs]
max_line_length = 120
[*]
charset = utf-8
indent_style = space

View File

@@ -74,3 +74,11 @@ body:
description: |
Links? References? Anything that will give us more context about the issue you are encountering!
Tip: You can attach images here
- type: checkboxes
attributes:
label: Is your content generated by AI?
description: >
(Required) Please confirm that the content you submit was not generated by AI or only minimally edited by AI.
If an administrator believes the post contains a large amount of AI-generated content, they may directly close the question.
options:
- label: I confirm that the content I submitted was **not** generated by AI / **merely contained minimal** AI edits.

View File

@@ -35,3 +35,11 @@ body:
See the AFFiNE [Contributing Guide](https://github.com/toeverything/affine/blob/canary/CONTRIBUTING.md) to get started.
options:
- label: Yes I'd like to help by submitting a PR!
- type: checkboxes
attributes:
label: Is your content generated by AI?
description: >
(Required) Please confirm that the content you submit was not generated by AI or only minimally edited by AI.
If an administrator believes the post contains a large amount of AI-generated content, they may directly close the question.
options:
- label: I confirm that the content I submitted was **not** generated by AI / **merely contained minimal** AI edits.

View File

@@ -75,7 +75,11 @@ runs:
shell: bash
if: ${{ runner.os != 'Windows' && inputs.no-build != 'true' }}
run: |
yarn workspace ${{ inputs.package }} build --target ${{ inputs.target }} --use-napi-cross
if [[ "${{ inputs.target }}" == "x86_64-unknown-linux-gnu" ]]; then
yarn workspace ${{ inputs.package }} build --target ${{ inputs.target }}
else
yarn workspace ${{ inputs.package }} build --target ${{ inputs.target }} --use-napi-cross
fi
env:
DEBUG: 'napi:*'

View File

@@ -4,11 +4,6 @@ description: 'Prepare Server Test Environment'
runs:
using: 'composite'
steps:
- name: Bundle @affine/reader
shell: bash
run: |
yarn affine @affine/reader build
- name: Initialize database
shell: bash
run: |

View File

@@ -13,4 +13,6 @@ RUN apt-get update && \
# Enable jemalloc by preloading the library
ENV LD_PRELOAD=libjemalloc.so.2
EXPOSE 3010
CMD ["node", "./dist/main.js"]

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,7 @@ name: renderer
description: AFFiNE renderer server
type: application
version: 0.0.0
appVersion: "0.25.7"
appVersion: "0.26.0"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -3,7 +3,7 @@ name: sync
description: AFFiNE Sync Server
type: application
version: 0.0.0
appVersion: "0.25.7"
appVersion: "0.26.0"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -46,6 +46,7 @@ jobs:
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
GA4_MEASUREMENT_ID: ${{ secrets.GA4_MEASUREMENT_ID }}
- name: Upload web artifact
uses: actions/upload-artifact@v4
with:
@@ -79,6 +80,7 @@ jobs:
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
GA4_MEASUREMENT_ID: ${{ secrets.GA4_MEASUREMENT_ID }}
- name: Upload admin artifact
uses: actions/upload-artifact@v4
with:
@@ -112,6 +114,7 @@ jobs:
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
GA4_MEASUREMENT_ID: ${{ secrets.GA4_MEASUREMENT_ID }}
- name: Upload mobile artifact
uses: actions/upload-artifact@v4
with:
@@ -187,8 +190,6 @@ jobs:
path: ./packages/backend/native
- name: List server-native files
run: ls -alh ./packages/backend/native
- name: Build @affine/reader
run: yarn workspace @affine/reader build
- name: Build Server
run: yarn workspace @affine/server build
- name: Upload server dist

View File

@@ -152,11 +152,6 @@ jobs:
name: server-native.node
path: ./packages/backend/native
- name: Bundle @affine/reader
shell: bash
run: |
yarn workspace @affine/reader build
- name: Run Check
run: |
yarn affine init
@@ -187,7 +182,7 @@ jobs:
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
shard: [1, 2]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
@@ -218,7 +213,7 @@ jobs:
strategy:
fail-fast: false
matrix:
shard: [1, 2]
shard: [1]
browser: ['chromium', 'firefox', 'webkit']
steps:
- uses: actions/checkout@v4
@@ -256,7 +251,7 @@ jobs:
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
shard: [1, 2, 3, 4, 5]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
@@ -287,7 +282,7 @@ jobs:
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4, 5]
shard: [1, 2]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
@@ -318,7 +313,7 @@ jobs:
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4, 5]
shard: [1, 2, 3]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
@@ -512,8 +507,8 @@ jobs:
strategy:
fail-fast: false
matrix:
node_index: [0, 1, 2, 3, 4, 5, 6, 7]
total_nodes: [8]
node_index: [0, 1, 2, 3]
total_nodes: [4]
env:
NODE_ENV: test
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
@@ -803,49 +798,6 @@ jobs:
name: fuzz-artifact
path: packages/common/y-octo/utils/fuzz/artifacts/**/*
y-octo-binding-test:
name: y-octo binding test on ${{ matrix.settings.target }}
runs-on: ${{ matrix.settings.os }}
strategy:
fail-fast: false
matrix:
settings:
- { target: 'x86_64-unknown-linux-gnu', os: 'ubuntu-latest' }
- { target: 'aarch64-unknown-linux-gnu', os: 'ubuntu-24.04-arm' }
- { target: 'x86_64-apple-darwin', os: 'macos-13' }
- { target: 'aarch64-apple-darwin', os: 'macos-latest' }
- { target: 'x86_64-pc-windows-msvc', os: 'windows-latest' }
- { target: 'aarch64-pc-windows-msvc', os: 'windows-11-arm' }
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine-tools/cli @affine/monorepo @y-octo/node
electron-install: false
- name: Install rustup (Windows 11 ARM)
if: matrix.settings.os == 'windows-11-arm'
shell: pwsh
run: |
Invoke-WebRequest -Uri "https://static.rust-lang.org/rustup/dist/aarch64-pc-windows-msvc/rustup-init.exe" -OutFile rustup-init.exe
.\rustup-init.exe --default-toolchain none -y
"$env:USERPROFILE\.cargo\bin" | Out-File -Append -Encoding ascii $env:GITHUB_PATH
"CARGO_HOME=$env:USERPROFILE\.cargo" | Out-File -Append -Encoding ascii $env:GITHUB_ENV
- name: Install Rust (Windows 11 ARM)
if: matrix.settings.os == 'windows-11-arm'
shell: pwsh
run: |
rustup install stable
rustup target add ${{ matrix.settings.target }}
cargo --version
- name: Build Rust
uses: ./.github/actions/build-rust
with:
target: ${{ matrix.settings.target }}
package: '@y-octo/node'
- name: Run tests
run: yarn affine @y-octo/node test
rust-test:
name: Run native tests
runs-on: ubuntu-latest
@@ -972,8 +924,8 @@ jobs:
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
shardTotal: [10]
shardIndex: [1, 2, 3, 4, 5]
shardTotal: [5]
needs:
- build-server-native
services:
@@ -1079,21 +1031,6 @@ jobs:
- name: 'Cloud E2E Test 5/10'
shard: 5
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=5/10
- name: 'Cloud E2E Test 6/10'
shard: 6
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=6/10
- name: 'Cloud E2E Test 7/10'
shard: 7
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=7/10
- name: 'Cloud E2E Test 8/10'
shard: 8
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=8/10
- name: 'Cloud E2E Test 9/10'
shard: 9
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=9/10
- name: 'Cloud E2E Test 10/10'
shard: 10
script: yarn affine @affine-test/affine-cloud e2e --forbid-only --shard=10/10
- name: 'Cloud Desktop E2E Test'
shard: desktop
script: |
@@ -1356,7 +1293,7 @@ jobs:
run: |
sudo add-apt-repository universe
sudo apt install -y libfuse2 elfutils flatpak flatpak-builder
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak update
# some flatpak deps need git protocol.file.allow
git config --global protocol.file.allow always
@@ -1392,7 +1329,6 @@ jobs:
- miri
- loom
- fuzzing
- y-octo-binding-test
- server-test
- server-e2e-test
- rust-test

View File

@@ -0,0 +1,227 @@
name: Release Desktop Platform
on:
workflow_call:
inputs:
build_type:
required: true
type: string
app_version:
required: true
type: string
git_short_hash:
required: true
type: string
runner:
required: true
type: string
platform:
required: true
type: string
arch:
required: true
type: string
target:
required: true
type: string
apple_codesign:
required: false
default: false
type: boolean
install_linux_deps:
required: false
default: false
type: boolean
enable_scripts:
required: false
default: false
type: boolean
outputs:
files_to_be_signed:
description: Files to be signed (Windows only)
value: ${{ jobs.build.outputs.files_to_be_signed }}
permissions:
actions: write
contents: write
security-events: write
id-token: write
attestations: write
jobs:
build:
runs-on: ${{ inputs.runner }}
outputs:
files_to_be_signed: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED }}
env:
BUILD_TYPE: ${{ inputs.build_type }}
RELEASE_VERSION: ${{ inputs.app_version }}
DEBUG: 'affine:*,napi:*'
APP_NAME: affine
MACOSX_DEPLOYMENT_TARGET: '12.0'
SKIP_GENERATE_ASSETS: 1
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_RELEASE: ${{ inputs.app_version }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
GA4_MEASUREMENT_ID: ${{ secrets.GA4_MEASUREMENT_ID }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
uses: ./.github/actions/setup-version
with:
app-version: ${{ inputs.app_version }}
- name: Setup Node.js
timeout-minutes: 10
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine/electron @affine/monorepo @affine/nbstore @toeverything/infra
hard-link-nm: false
nmHoistingLimits: workspaces
enableScripts: ${{ inputs.enable_scripts }}
- name: Build AFFiNE native
uses: ./.github/actions/build-rust
with:
target: ${{ inputs.target }}
package: '@affine/native'
- uses: actions/download-artifact@v4
with:
name: desktop-web
path: packages/frontend/apps/electron/resources/web-static
- name: Build Desktop Layers
run: yarn affine @affine/electron build
- name: Signing By Apple Developer ID
if: ${{ inputs.platform == 'darwin' && inputs.apple_codesign }}
uses: apple-actions/import-codesign-certs@v5
with:
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
- name: Install additional dependencies on Linux
if: ${{ inputs.platform == 'linux' && inputs.install_linux_deps }}
run: |
df -h
sudo add-apt-repository universe
sudo apt install -y libfuse2 elfutils flatpak flatpak-builder
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak update
# some flatpak deps need git protocol.file.allow
git config --global protocol.file.allow always
# clean up apt cache to save disk space
sudo -E apt-get -y purge azure-cli* zulu* hhvm* llvm* firefox* google* dotnet* aspnetcore* powershell* adoptopenjdk* mysql* php* mongodb* moby* snap* || true
sudo -E apt-get -qq autoremove --purge
sudo rm -rf /usr/share/dotnet /opt/ghc /opt/hostedtoolcache/CodeQL /usr/local/lib/android
sudo apt-get clean
rm -rf ~/.cache/yarn ~/.npm
df -h
- name: Remove nbstore node_modules (darwin/linux)
if: ${{ inputs.platform != 'win32' }}
shell: bash
# node_modules of nbstore is not needed for building, and it will make the build process out of memory
run: |
cargo clean
rm -rf packages/frontend/apps/electron/node_modules/@affine/nbstore/node_modules/@blocksuite
rm -rf packages/frontend/apps/electron/node_modules/@affine/native/node_modules
- name: Remove nbstore node_modules (windows)
if: ${{ inputs.platform == 'win32' }}
shell: bash
run: |
rm -rf packages/frontend/apps/electron/node_modules/@affine/nbstore/node_modules/@blocksuite/affine/node_modules
rm -rf packages/frontend/apps/electron/node_modules/@affine/native/node_modules
- name: make
if: ${{ inputs.platform != 'win32' }}
run: yarn affine @affine/electron make --platform=${{ inputs.platform }} --arch=${{ inputs.arch }}
env:
SKIP_WEB_BUILD: 1
HOIST_NODE_MODULES: 1
NODE_OPTIONS: --max-old-space-size=14384
- name: package
if: ${{ inputs.platform == 'win32' }}
run: |
yarn affine @affine/electron package --platform=${{ inputs.platform }} --arch=${{ inputs.arch }}
env:
SKIP_WEB_BUILD: 1
HOIST_NODE_MODULES: 1
NODE_OPTIONS: --max-old-space-size=14384
- name: signing DMG
if: ${{ inputs.platform == 'darwin' && inputs.apple_codesign }}
run: |
codesign --force --sign "Developer ID Application: TOEVERYTHING PTE. LTD." packages/frontend/apps/electron/out/${{ env.BUILD_TYPE }}/make/AFFiNE.dmg
- name: Save artifacts (mac)
if: ${{ inputs.platform == 'darwin' }}
run: |
mkdir -p builds
mv packages/frontend/apps/electron/out/*/make/*.dmg ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ inputs.arch }}.dmg
mv packages/frontend/apps/electron/out/*/make/zip/darwin/${{ inputs.arch }}/*.zip ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ inputs.arch }}.zip
- name: Save artifacts (linux)
if: ${{ inputs.platform == 'linux' }}
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 ./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
- uses: actions/attest-build-provenance@v2
if: ${{ inputs.platform == 'darwin' }}
with:
subject-path: |
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ inputs.arch }}.zip
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ inputs.arch }}.dmg
- uses: actions/attest-build-provenance@v2
if: ${{ inputs.platform == 'linux' }}
with:
subject-path: |
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.zip
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.appimage
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.deb
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ inputs.arch }}.flatpak
- name: Upload Artifact
if: ${{ inputs.platform == 'darwin' || inputs.platform == 'linux' }}
uses: actions/upload-artifact@v4
with:
name: affine-${{ inputs.platform }}-${{ inputs.arch }}-builds
path: builds
- name: get all files to be signed
id: get_files_to_be_signed
if: ${{ inputs.platform == 'win32' }}
shell: pwsh
run: |
Set-Variable -Name FILES_TO_BE_SIGNED -Value ((Get-ChildItem -Path packages/frontend/apps/electron/out -Recurse -File | Where-Object { $_.Extension -in @(".exe", ".node", ".dll", ".msi") } | ForEach-Object { '"' + $_.FullName.Replace((Get-Location).Path + '\packages\frontend\apps\electron\out\', '') + '"' }) -join ' ')
"FILES_TO_BE_SIGNED=$FILES_TO_BE_SIGNED" >> $env:GITHUB_OUTPUT
echo $FILES_TO_BE_SIGNED
- name: Zip artifacts for faster upload
if: ${{ inputs.platform == 'win32' }}
shell: pwsh
run: Compress-Archive -CompressionLevel Fastest -Path packages/frontend/apps/electron/out/* -DestinationPath archive.zip
- name: Save packaged artifacts for signing
if: ${{ inputs.platform == 'win32' }}
uses: actions/upload-artifact@v4
with:
name: packaged-${{ inputs.platform }}-${{ inputs.arch }}
path: |
archive.zip
!**/*.map

View File

@@ -12,6 +12,21 @@ on:
git-short-hash:
required: true
type: string
desktop_macos:
description: 'Desktop - macOS'
required: false
default: true
type: boolean
desktop_windows:
description: 'Desktop - Windows'
required: false
default: true
type: boolean
desktop_linux:
description: 'Desktop - Linux'
required: false
default: true
type: boolean
permissions:
actions: write
@@ -29,6 +44,7 @@ env:
jobs:
before-make:
if: ${{ inputs.desktop_macos || inputs.desktop_windows || inputs.desktop_linux }}
runs-on: ubuntu-latest
environment: ${{ inputs.build-type }}
steps:
@@ -51,6 +67,7 @@ jobs:
SENTRY_RELEASE: ${{ inputs.app-version }}
RELEASE_VERSION: ${{ inputs.app-version }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
GA4_MEASUREMENT_ID: ${{ secrets.GA4_MEASUREMENT_ID }}
- name: Upload web artifact
uses: actions/upload-artifact@v4
@@ -58,7 +75,8 @@ jobs:
name: desktop-web
path: packages/frontend/apps/electron/resources/web-static
make-distribution:
make-distribution-macos:
if: ${{ inputs.desktop_macos }}
strategy:
fail-fast: false
matrix:
@@ -71,223 +89,90 @@ jobs:
platform: darwin
arch: arm64
target: aarch64-apple-darwin
- runner: ubuntu-latest
platform: linux
arch: x64
target: x86_64-unknown-linux-gnu
runs-on: ${{ matrix.spec.runner }}
needs: before-make
environment: ${{ inputs.build-type }}
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
SKIP_GENERATE_ASSETS: 1
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_RELEASE: ${{ inputs.app-version }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
uses: ./.github/actions/setup-version
with:
app-version: ${{ inputs.app-version }}
- name: Setup Node.js
timeout-minutes: 10
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine/electron @affine/monorepo @affine/nbstore @toeverything/infra
hard-link-nm: false
nmHoistingLimits: workspaces
enableScripts: false
- name: Build AFFiNE native
uses: ./.github/actions/build-rust
with:
target: ${{ matrix.spec.target }}
package: '@affine/native'
- uses: actions/download-artifact@v4
with:
name: desktop-web
path: packages/frontend/apps/electron/resources/web-static
uses: ./.github/workflows/release-desktop-platform.yml
secrets: inherit
with:
build_type: ${{ inputs.build-type }}
app_version: ${{ inputs.app-version }}
git_short_hash: ${{ inputs.git-short-hash }}
runner: ${{ matrix.spec.runner }}
platform: ${{ matrix.spec.platform }}
arch: ${{ matrix.spec.arch }}
target: ${{ matrix.spec.target }}
apple_codesign: true
- name: Build Desktop Layers
run: yarn affine @affine/electron build
- name: Signing By Apple Developer ID
if: ${{ matrix.spec.platform == 'darwin' }}
uses: apple-actions/import-codesign-certs@v5
with:
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
- name: Install additional dependencies on Linux
if: ${{ matrix.spec.platform == 'linux' }}
run: |
sudo add-apt-repository universe
sudo apt install -y libfuse2 elfutils flatpak flatpak-builder
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak update
# some flatpak deps need git protocol.file.allow
git config --global protocol.file.allow always
- name: Remove nbstore node_modules
shell: bash
# node_modules of nbstore is not needed for building, and it will make the build process out of memory
run: |
rm -rf packages/frontend/apps/electron/node_modules/@affine/nbstore/node_modules/@blocksuite
rm -rf packages/frontend/apps/electron/node_modules/@affine/native/node_modules
- name: make
run: yarn affine @affine/electron make --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
env:
SKIP_WEB_BUILD: 1
HOIST_NODE_MODULES: 1
NODE_OPTIONS: --max-old-space-size=14384
- name: signing DMG
if: ${{ matrix.spec.platform == 'darwin' }}
run: |
codesign --force --sign "Developer ID Application: TOEVERYTHING PTE. LTD." packages/frontend/apps/electron/out/${{ env.BUILD_TYPE }}/make/AFFiNE.dmg
- name: Save artifacts (mac)
if: ${{ matrix.spec.platform == 'darwin' }}
run: |
mkdir -p builds
mv packages/frontend/apps/electron/out/*/make/*.dmg ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.dmg
mv packages/frontend/apps/electron/out/*/make/zip/darwin/${{ matrix.spec.arch }}/*.zip ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.zip
- name: Save artifacts (linux)
if: ${{ matrix.spec.platform == 'linux' }}
run: |
mkdir -p builds
mv packages/frontend/apps/electron/out/*/make/zip/linux/${{ matrix.spec.arch }}/*.zip ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.zip
mv packages/frontend/apps/electron/out/*/make/*.AppImage ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.appimage
mv packages/frontend/apps/electron/out/*/make/deb/${{ matrix.spec.arch }}/*.deb ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.deb
# mv packages/frontend/apps/electron/out/*/make/flatpak/*/*.flatpak ./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-${{ matrix.spec.arch }}.flatpak
- uses: actions/attest-build-provenance@v2
if: ${{ matrix.spec.platform == 'darwin' }}
with:
subject-path: |
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.zip
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.dmg
- uses: actions/attest-build-provenance@v2
if: ${{ matrix.spec.platform == 'linux' }}
with:
subject-path: |
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-x64.zip
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-x64.appimage
./builds/affine-${{ env.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-x64.deb
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: affine-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}-builds
path: builds
package-distribution-windows:
environment: ${{ inputs.build-type }}
make-distribution-linux:
if: ${{ inputs.desktop_linux }}
strategy:
fail-fast: false
matrix:
spec:
- runner: windows-latest
platform: win32
- runner: ubuntu-latest
platform: linux
arch: x64
target: x86_64-pc-windows-msvc
- runner: windows-latest
platform: win32
arch: arm64
target: aarch64-pc-windows-msvc
runs-on: ${{ matrix.spec.runner }}
target: x86_64-unknown-linux-gnu
needs: before-make
outputs:
FILES_TO_BE_SIGNED_x64: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED_x64 }}
FILES_TO_BE_SIGNED_arm64: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED_arm64 }}
env:
SKIP_GENERATE_ASSETS: 1
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_RELEASE: ${{ inputs.app-version }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
uses: ./.github/actions/setup-version
with:
app-version: ${{ inputs.app-version }}
- name: Setup Node.js
timeout-minutes: 10
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine/electron @affine/monorepo @affine/nbstore @toeverything/infra
hard-link-nm: false
nmHoistingLimits: workspaces
- name: Build AFFiNE native
uses: ./.github/actions/build-rust
with:
target: ${{ matrix.spec.target }}
package: '@affine/native'
- uses: actions/download-artifact@v4
with:
name: desktop-web
path: packages/frontend/apps/electron/resources/web-static
uses: ./.github/workflows/release-desktop-platform.yml
secrets: inherit
with:
build_type: ${{ inputs.build-type }}
app_version: ${{ inputs.app-version }}
git_short_hash: ${{ inputs.git-short-hash }}
runner: ${{ matrix.spec.runner }}
platform: ${{ matrix.spec.platform }}
arch: ${{ matrix.spec.arch }}
target: ${{ matrix.spec.target }}
install_linux_deps: true
- name: Build Desktop Layers
run: yarn affine @affine/electron build
package-distribution-windows-x64:
if: ${{ inputs.desktop_windows }}
needs: before-make
uses: ./.github/workflows/release-desktop-platform.yml
secrets: inherit
with:
build_type: ${{ inputs.build-type }}
app_version: ${{ inputs.app-version }}
git_short_hash: ${{ inputs.git-short-hash }}
runner: windows-latest
platform: win32
arch: x64
target: x86_64-pc-windows-msvc
enable_scripts: true
- name: Remove nbstore node_modules
shell: bash
# node_modules of nbstore is not needed for building, and it will make the build process out of memory
run: |
rm -rf packages/frontend/apps/electron/node_modules/@affine/nbstore/node_modules/@blocksuite/affine/node_modules
rm -rf packages/frontend/apps/electron/node_modules/@affine/native/node_modules
- name: package
run: |
yarn affine @affine/electron package --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
env:
SKIP_WEB_BUILD: 1
HOIST_NODE_MODULES: 1
NODE_OPTIONS: --max-old-space-size=14384
- name: get all files to be signed
id: get_files_to_be_signed
run: |
Set-Variable -Name FILES_TO_BE_SIGNED -Value ((Get-ChildItem -Path packages/frontend/apps/electron/out -Recurse -File | Where-Object { $_.Extension -in @(".exe", ".node", ".dll", ".msi") } | ForEach-Object { '"' + $_.FullName.Replace((Get-Location).Path + '\packages\frontend\apps\electron\out\', '') + '"' }) -join ' ')
"FILES_TO_BE_SIGNED_${{ matrix.spec.arch }}=$FILES_TO_BE_SIGNED" >> $env:GITHUB_OUTPUT
echo $FILES_TO_BE_SIGNED
- name: Zip artifacts for faster upload
run: Compress-Archive -CompressionLevel Fastest -Path packages/frontend/apps/electron/out/* -DestinationPath archive.zip
- name: Save packaged artifacts for signing
uses: actions/upload-artifact@v4
with:
name: packaged-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}
path: |
archive.zip
!**/*.map
package-distribution-windows-arm64:
if: ${{ inputs.desktop_windows }}
needs: before-make
uses: ./.github/workflows/release-desktop-platform.yml
secrets: inherit
with:
build_type: ${{ inputs.build-type }}
app_version: ${{ inputs.app-version }}
git_short_hash: ${{ inputs.git-short-hash }}
runner: windows-latest
platform: win32
arch: arm64
target: aarch64-pc-windows-msvc
enable_scripts: true
sign-packaged-artifacts-windows_x64:
needs: package-distribution-windows
if: ${{ inputs.desktop_windows }}
needs: package-distribution-windows-x64
uses: ./.github/workflows/windows-signer.yml
with:
files: ${{ needs.package-distribution-windows.outputs.FILES_TO_BE_SIGNED_x64 }}
files: ${{ needs.package-distribution-windows-x64.outputs.files_to_be_signed }}
artifact-name: packaged-win32-x64
sign-packaged-artifacts-windows_arm64:
needs: package-distribution-windows
if: ${{ inputs.desktop_windows }}
needs: package-distribution-windows-arm64
uses: ./.github/workflows/windows-signer.yml
with:
files: ${{ needs.package-distribution-windows.outputs.FILES_TO_BE_SIGNED_arm64 }}
files: ${{ needs.package-distribution-windows-arm64.outputs.files_to_be_signed }}
artifact-name: packaged-win32-arm64
make-windows-installer:
if: ${{ inputs.desktop_windows }}
needs:
- sign-packaged-artifacts-windows_x64
- sign-packaged-artifacts-windows_arm64
@@ -349,6 +234,7 @@ jobs:
path: archive.zip
sign-installer-artifacts-windows-x64:
if: ${{ inputs.desktop_windows }}
needs: make-windows-installer
uses: ./.github/workflows/windows-signer.yml
with:
@@ -356,6 +242,7 @@ jobs:
artifact-name: installer-win32-x64
sign-installer-artifacts-windows-arm64:
if: ${{ inputs.desktop_windows }}
needs: make-windows-installer
uses: ./.github/workflows/windows-signer.yml
with:
@@ -363,6 +250,7 @@ jobs:
artifact-name: installer-win32-arm64
finalize-installer-windows:
if: ${{ inputs.desktop_windows }}
needs:
[
sign-installer-artifacts-windows-x64,
@@ -410,17 +298,18 @@ jobs:
path: builds
release:
needs: [before-make, make-distribution, finalize-installer-windows]
if: ${{ inputs.desktop_macos && inputs.desktop_linux && inputs.desktop_windows }}
needs:
[
before-make,
make-distribution-macos,
make-distribution-linux,
finalize-installer-windows,
]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: desktop-web
path: web-static
- name: Zip web-static
run: zip -r web-static.zip web-static
- name: Download Artifacts (macos-x64)
uses: actions/download-artifact@v4
with:

View File

@@ -40,6 +40,7 @@ jobs:
env:
PUBLIC_PATH: '/'
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
GA4_MEASUREMENT_ID: ${{ secrets.GA4_MEASUREMENT_ID }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
@@ -69,6 +70,7 @@ jobs:
env:
PUBLIC_PATH: '/'
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
GA4_MEASUREMENT_ID: ${{ secrets.GA4_MEASUREMENT_ID }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
@@ -110,7 +112,7 @@ jobs:
enableScripts: false
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: 16.4
xcode-version: 26.2
- name: Install Swiftformat
run: brew install swiftformat
- name: Cap sync

View File

@@ -11,8 +11,18 @@ on:
required: true
type: boolean
default: false
desktop:
description: 'Release Desktop?'
desktop_macos:
description: 'Desktop - macOS'
required: true
type: boolean
default: false
desktop_windows:
description: 'Desktop - Windows'
required: true
type: boolean
default: false
desktop_linux:
description: 'Desktop - Linux'
required: true
type: boolean
default: false
@@ -50,6 +60,68 @@ jobs:
id: prepare
uses: ./.github/actions/prepare-release
canary-gate:
name: Canary Gate
runs-on: ubuntu-latest
needs:
- prepare
outputs:
SHOULD_RELEASE: ${{ steps.decide.outputs.SHOULD_RELEASE }}
LAST_CANARY_TAG: ${{ steps.decide.outputs.LAST_CANARY_TAG }}
LAST_CANARY_SHA: ${{ steps.decide.outputs.LAST_CANARY_SHA }}
steps:
- name: Decide whether to release
id: decide
uses: actions/github-script@v7
with:
script: |
const buildType = '${{ needs.prepare.outputs.BUILD_TYPE }}'
if (buildType !== 'canary') {
core.setOutput('SHOULD_RELEASE', 'true')
return
}
const owner = context.repo.owner
const repo = context.repo.repo
const currentSha = context.sha
const canaryTagRe = /^v\d+\.\d+\.\d+-canary\.[0-9a-f]+$/i
let page = 1
const perPage = 100
let lastCanary = null
while (!lastCanary && page <= 10) {
const { data } = await github.rest.repos.listTags({
owner,
repo,
per_page: perPage,
page,
})
for (const tag of data) {
if (canaryTagRe.test(tag.name)) {
lastCanary = tag
break
}
}
if (data.length < perPage) break
page++
}
if (!lastCanary) {
core.warning('No canary tags found; proceeding with canary release.')
core.setOutput('SHOULD_RELEASE', 'true')
return
}
core.setOutput('LAST_CANARY_TAG', lastCanary.name)
core.setOutput('LAST_CANARY_SHA', lastCanary.commit.sha)
const shouldRelease = lastCanary.commit.sha !== currentSha
core.info(`Latest canary tag ${lastCanary.name} -> ${lastCanary.commit.sha}; current ${currentSha}; should_release=${shouldRelease}`)
core.setOutput('SHOULD_RELEASE', shouldRelease ? 'true' : 'false')
cloud:
name: Release Cloud
if: ${{ inputs.web || github.event_name != 'workflow_dispatch' }}
@@ -64,9 +136,11 @@ jobs:
image:
name: Release Docker Image
if: ${{ needs.canary-gate.outputs.SHOULD_RELEASE == 'true' }}
runs-on: ubuntu-latest
needs:
- prepare
- canary-gate
- cloud
steps:
- uses: trstringer/manual-approval@v1
@@ -74,7 +148,7 @@ jobs:
name: Wait for approval
with:
secret: ${{ secrets.GITHUB_TOKEN }}
approvers: forehalo,fengmk2,darkskygit
approvers: darkskygit,pengx17,L-Sun,EYHN
minimum-approvals: 1
fail-on-denial: true
issue-title: Please confirm to release docker image
@@ -102,15 +176,25 @@ jobs:
desktop:
name: Release Desktop
if: ${{ inputs.desktop || github.event_name != 'workflow_dispatch' }}
if: >-
${{
(github.event_name != 'workflow_dispatch' && needs.canary-gate.outputs.SHOULD_RELEASE == 'true') ||
inputs.desktop_macos ||
inputs.desktop_windows ||
inputs.desktop_linux
}}
needs:
- prepare
- canary-gate
uses: ./.github/workflows/release-desktop.yml
secrets: inherit
with:
build-type: ${{ needs.prepare.outputs.BUILD_TYPE }}
app-version: ${{ needs.prepare.outputs.APP_VERSION }}
git-short-hash: ${{ needs.prepare.outputs.GIT_SHORT_HASH }}
desktop_macos: ${{ github.event_name != 'workflow_dispatch' || inputs.desktop_macos }}
desktop_windows: ${{ github.event_name != 'workflow_dispatch' || inputs.desktop_windows }}
desktop_linux: ${{ github.event_name != 'workflow_dispatch' || inputs.desktop_linux }}
mobile:
name: Release Mobile

2
.gitignore vendored
View File

@@ -47,6 +47,8 @@ testem.log
.pnpm-debug.log
/typings
tsconfig.tsbuildinfo
rfc*.md
todo.md
# System Files
.DS_Store

2
.nvmrc
View File

@@ -1 +1 @@
22.16.0
22.22.0

View File

@@ -1,4 +1,8 @@
exclude = ["node_modules/**/*.toml", "target/**/*.toml"]
exclude = [
"node_modules/**/*.toml",
"target/**/*.toml",
"packages/frontend/apps/ios/App/Packages/AffineGraphQL/**/*.toml",
]
# https://taplo.tamasfe.dev/configuration/formatter-options.html
[formatting]

File diff suppressed because one or more lines are too long

View File

@@ -12,4 +12,4 @@ npmPublishAccess: public
npmRegistryServer: "https://registry.npmjs.org"
yarnPath: .yarn/releases/yarn-4.9.1.cjs
yarnPath: .yarn/releases/yarn-4.12.0.cjs

1994
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,6 @@ members = [
"./packages/backend/native",
"./packages/common/native",
"./packages/common/y-octo/core",
"./packages/common/y-octo/node",
"./packages/common/y-octo/utils",
"./packages/frontend/mobile-native",
"./packages/frontend/native",
@@ -47,10 +46,11 @@ resolver = "3"
libc = "0.2"
log = "0.4"
loom = { version = "0.7", features = ["checkpoint"] }
memory-indexer = "0.3.0"
mimalloc = "0.1"
mp4parse = "0.17"
nanoid = "0.4"
napi = { version = "3.0.0-beta.3", features = [
napi = { version = "3.7.0", features = [
"async",
"chrono_date",
"error_anyhow",
@@ -58,7 +58,7 @@ resolver = "3"
"serde",
] }
napi-build = { version = "2" }
napi-derive = { version = "3.0.0-beta.3" }
napi-derive = { version = "3.4" }
nom = "8"
notify = { version = "8", features = ["serde"] }
objc2 = "0.6"
@@ -71,6 +71,7 @@ resolver = "3"
phf = { version = "0.11", features = ["macros"] }
proptest = "1.3"
proptest-derive = "0.5"
pulldown-cmark = "0.13"
rand = "0.9"
rand_chacha = "0.9"
rand_distr = "0.5"

View File

@@ -2,7 +2,7 @@ Copyright (c) 2022-present TOEVERYTHING PTE. LTD. and its affiliates.
Portions of this software are licensed as follows:
- All content that resides under the "packages/backend/server" directory of this repository, if that directory exists, is licensed under the license defined in "packages/backend/server/LICENSE".
- All content that resides under the "packages/backend" and "packages/common/native" directory of this repository, if that directory exists, is licensed under the license defined in "packages/backend/server/LICENSE".
- All third party components incorporated into the AFFiNE Software are licensed under the original license provided by the owner of the applicable component.
- Content outside of the above mentioned directories or restrictions above is available under the "MIT" license as defined in "LICENSE-MIT".

View File

@@ -193,6 +193,8 @@ We would like to express our gratitude to all the individuals who have already c
Begin with Docker to deploy your own feature-rich, unrestricted version of AFFiNE. Our team is diligently updating to the latest version. For more information on how to self-host AFFiNE, please refer to our [documentation](https://docs.affine.pro/self-host-affine).
[![Run on Sealos](https://sealos.io/Deploy-on-Sealos.svg)](https://sealos.io/products/app-store/affine)
[![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

View File

@@ -6,15 +6,14 @@ We recommend users to always use the latest major version. Security updates will
| Version | Supported |
| --------------- | ------------------ |
| 0.24.x (stable) | :white_check_mark: |
| < 0.24.x | :x: |
| 0.25.x (stable) | :white_check_mark: |
| < 0.25.x | :x: |
## Reporting a Vulnerability
We welcome you to provide us with bug reports via and email at [security@toeverything.info](mailto:security@toeverything.info) or submit directly on [GitHub](https://github.com/toeverything/AFFiNE/security), **we encourage you to submit the relevant information directly via GitHub**. We expect your report to contain at least the following for us to evaluate and reproduce:
1. Using platform and version, for example:
- macos arm64 0.12.0-canary-202402220729-0868ac6
- app.affine.pro 0.12.0-canary-202402220729-0868ac6

View File

@@ -79,7 +79,7 @@
"@blocksuite/std": "workspace:*",
"@blocksuite/store": "workspace:*",
"@blocksuite/sync": "workspace:*",
"rxjs": "^7.8.1"
"rxjs": "^7.8.2"
},
"exports": {
".": "./src/index.ts",
@@ -296,10 +296,10 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7",
"version": "0.26.0",
"devDependencies": {
"@vanilla-extract/vite-plugin": "^5.0.0",
"msw": "^2.8.4",
"vitest": "3.1.3"
"msw": "^2.12.4",
"vitest": "^3.2.4"
}
}

View File

@@ -2214,7 +2214,7 @@ describe('html to snapshot', () => {
test('iframe', async () => {
const html = template(
`<iframe width="560" height="315" src="https://www.youtube.com/embed/QDsd0nyzwz0?start=&amp;end=" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>`
`<iframe width="560" height="315" src="https://www.youtube.com/embed/QDsd0nyzwz0?start=&amp;end=" title="YouTube video player" frameborder="0" allow="fullscreen; autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin"></iframe>`
);
const blockSnapshot: BlockSnapshot = {

View File

@@ -1,3 +1,4 @@
import { MarkdownTransformer } from '@blocksuite/affine/widgets/linked-doc';
import {
DefaultTheme,
NoteDisplayMode,
@@ -16,12 +17,15 @@ import type {
SliceSnapshot,
TransformerMiddleware,
} from '@blocksuite/store';
import { AssetsManager, MemoryBlobCRUD } from '@blocksuite/store';
import { AssetsManager, MemoryBlobCRUD, Schema } from '@blocksuite/store';
import { TestWorkspace } from '@blocksuite/store/test';
import { describe, expect, test } from 'vitest';
import { AffineSchemas } from '../../schemas.js';
import { createJob } from '../utils/create-job.js';
import { getProvider } from '../utils/get-provider.js';
import { nanoidReplacement } from '../utils/nanoid-replacement.js';
import { testStoreExtensions } from '../utils/store.js';
const provider = getProvider();
@@ -90,6 +94,39 @@ describe('snapshot to markdown', () => {
expect(target.file).toBe(markdown);
});
test('imports frontmatter metadata into doc meta', async () => {
const schema = new Schema().register(AffineSchemas);
const collection = new TestWorkspace();
collection.storeExtensions = testStoreExtensions;
collection.meta.initialize();
const markdown = `---
title: Web developer
created: 2018-04-12T09:51:00
updated: 2018-04-12T10:00:00
tags: [a, b]
favorite: true
---
Hello world
`;
const docId = await MarkdownTransformer.importMarkdownToDoc({
collection,
schema,
markdown,
fileName: 'fallback-title',
extensions: testStoreExtensions,
});
expect(docId).toBeTruthy();
const meta = collection.meta.getDocMeta(docId!);
expect(meta?.title).toBe('Web developer');
expect(meta?.createDate).toBe(Date.parse('2018-04-12T09:51:00'));
expect(meta?.updatedDate).toBe(Date.parse('2018-04-12T10:00:00'));
expect(meta?.favorite).toBe(true);
expect(meta?.tags).toEqual(['a', 'b']);
});
test('paragraph', async () => {
const blockSnapshot: BlockSnapshot = {
type: 'block',
@@ -2996,6 +3033,50 @@ describe('markdown to snapshot', () => {
});
});
test('html inline color span imports to nearest supported text color', async () => {
const markdown = `<span style="color: #00afde;">Hello</span>`;
const blockSnapshot: BlockSnapshot = {
type: 'block',
id: 'matchesReplaceMap[0]',
flavour: 'affine:note',
props: {
xywh: '[0,0,800,95]',
background: DefaultTheme.noteBackgrounColor,
index: 'a0',
hidden: false,
displayMode: NoteDisplayMode.DocAndEdgeless,
},
children: [
{
type: 'block',
id: 'matchesReplaceMap[1]',
flavour: 'affine:paragraph',
props: {
type: 'text',
text: {
'$blocksuite:internal:text$': true,
delta: [
{
insert: 'Hello',
attributes: {
color: 'var(--affine-v2-text-highlight-fg-blue)',
},
},
],
},
},
children: [],
},
],
};
const mdAdapter = new MarkdownAdapter(createJob(), provider);
const rawBlockSnapshot = await mdAdapter.toBlockSnapshot({
file: markdown,
});
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
});
test('paragraph', async () => {
const markdown = `aaa

File diff suppressed because it is too large Load Diff

View File

@@ -23,12 +23,12 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"file-type": "^21.0.0",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
"zod": "^3.23.8"
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
"exports": {
".": "./src/index.ts",
@@ -41,5 +41,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -24,15 +24,15 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
"yjs": "^13.6.23",
"zod": "^3.23.8"
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
},
"devDependencies": {
"vitest": "3.1.3"
"vitest": "^3.2.4"
},
"exports": {
".": "./src/index.ts",
@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -26,13 +26,13 @@
"@floating-ui/dom": "^1.6.10",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"@types/mdast": "^4.0.4",
"emoji-mart": "^5.6.0",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
"zod": "^3.23.8"
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
"exports": {
".": "./src/index.ts",
@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -6,7 +6,7 @@ import {
import { DefaultInlineManagerExtension } from '@blocksuite/affine-inline-preset';
import {
type CalloutBlockModel,
ParagraphBlockModel,
type ParagraphBlockModel,
} from '@blocksuite/affine-model';
import { focusTextModel } from '@blocksuite/affine-rich-text';
import { EDGELESS_TOP_CONTENTEDITABLE_SELECTOR } from '@blocksuite/affine-shared/consts';

View File

@@ -28,13 +28,13 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
"shiki": "^3.0.0",
"zod": "^3.23.8"
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"shiki": "^3.19.0",
"zod": "^3.25.76"
},
"exports": {
".": "./src/index.ts",
@@ -48,5 +48,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -1,6 +1,4 @@
export const CODE_BLOCK_DEFAULT_DARK_THEME = import(
'shiki/themes/dark-plus.mjs'
);
export const CODE_BLOCK_DEFAULT_LIGHT_THEME = import(
'shiki/themes/light-plus.mjs'
);
export const CODE_BLOCK_DEFAULT_DARK_THEME =
import('shiki/themes/dark-plus.mjs');
export const CODE_BLOCK_DEFAULT_LIGHT_THEME =
import('shiki/themes/light-plus.mjs');

View File

@@ -24,12 +24,12 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
"zod": "^3.23.8"
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
"exports": {
".": "./src/index.ts",
@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -28,14 +28,14 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"@types/mdast": "^4.0.4",
"date-fns": "^4.0.0",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
"yjs": "^13.6.21",
"zod": "^3.23.8"
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
},
"exports": {
".": "./src/index.ts",
@@ -48,5 +48,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -15,6 +15,7 @@ const ColumnClassMap: Record<string, string> = {
typesCheckbox: 'checkbox',
typesText: 'rich-text',
typesTitle: 'title',
typesDate: 'date',
};
const NotionDatabaseToken = '.collection-content';
@@ -165,7 +166,36 @@ export const databaseBlockNotionHtmlAdapterMatcher: BlockNotionHtmlAdapterMatche
if (!column) {
return;
}
if (HastUtils.querySelector(child, '.selected-value')) {
// Check for <time> element to find date field from Notion.
if (HastUtils.querySelector(child, 'time')) {
const timeElement = HastUtils.querySelector(child, 'time');
let rawColumnData =
HastUtils.getTextContent(timeElement).trim();
if (rawColumnData.startsWith('@')) {
rawColumnData = rawColumnData.slice(1);
}
const columnDate = new Date(rawColumnData);
const timestamp = columnDate.getTime();
if (!Number.isNaN(timestamp)) {
column.data = {};
if (column.type !== 'date') {
column.type = 'date';
}
row[column.id] = {
columnId: column.id,
value: timestamp,
};
} else {
row[column.id] = {
columnId: column.id,
value: HastUtils.getTextContent(child),
};
}
} else if (HastUtils.querySelector(child, '.selected-value')) {
if (!('options' in column.data)) {
column.data.options = [];
}

View File

@@ -21,12 +21,12 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
"zod": "^3.23.8"
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
"exports": {
".": "./src/index.ts",
@@ -39,5 +39,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -26,11 +26,11 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
"zod": "^3.23.8"
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
"exports": {
".": "./src/index.ts",
@@ -43,5 +43,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -26,17 +26,17 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.21",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
"yjs": "^13.6.21",
"zod": "^3.23.8"
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
},
"devDependencies": {
"vitest": "3.1.3"
"vitest": "^3.2.4"
},
"exports": {
".": "./src/index.ts",
@@ -49,5 +49,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -26,17 +26,17 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"@types/lodash-es": "^4.17.12",
"lit": "^3.2.0",
"lodash-es": "^4.17.21",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
"yjs": "^13.6.21",
"zod": "^3.23.8"
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
},
"devDependencies": {
"vitest": "3.1.3"
"vitest": "^3.2.4"
},
"exports": {
".": "./src/index.ts",
@@ -49,5 +49,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -82,7 +82,8 @@ export class EmbedFigmaBlockComponent extends EmbedBlockComponent<EmbedFigmaMode
<div class="affine-embed-figma-iframe-container">
<iframe
src=${`https://www.figma.com/embed?embed_host=blocksuite&url=${url}`}
allowfullscreen
sandbox="allow-same-origin allow-scripts allow-presentation"
allow="fullscreen"
loading="lazy"
credentialless
></iframe>

View File

@@ -0,0 +1,79 @@
import { EmbedIframeConfigExtension } from '@blocksuite/affine-shared/services';
import {
type EmbedIframeUrlValidationOptions,
validateEmbedIframeUrl,
} from '../../utils';
const BILIBILI_DEFAULT_WIDTH_IN_SURFACE = 800;
const BILIBILI_DEFAULT_HEIGHT_IN_SURFACE = 450;
const BILIBILI_DEFAULT_HEIGHT_IN_NOTE = 450;
const BILIBILI_DEFAULT_WIDTH_PERCENT = 100;
const bilibiliValidationOptions: EmbedIframeUrlValidationOptions = {
protocols: ['https:'],
hostnames: ['player.bilibili.com', 'www.bilibili.com', 'bilibili.com'],
};
const biliPlayerValidationOptions: EmbedIframeUrlValidationOptions = {
protocols: ['https:'],
hostnames: ['player.bilibili.com'],
};
const AV_REGEX = /av([0-9]+)/i;
const BV_REGEX = /(BV[0-9A-Za-z]{10})/;
const extractAvid = (url: string) => {
const match = url.match(AV_REGEX);
return match ? match[1] : undefined;
};
const extractBvid = (url: string) => {
const match = url.match(BV_REGEX);
return match ? match[1] : undefined;
};
const buildBiliPlayerEmbedUrl = (url: string) => {
// If the user pasted the embed URL directly, keep it
if (validateEmbedIframeUrl(url, biliPlayerValidationOptions)) {
return url;
}
const avid = extractAvid(url);
if (avid) {
const params = new URLSearchParams({
aid: avid,
autoplay: '0',
});
return `https://player.bilibili.com/player.html?${params.toString()}`;
}
const bvid = extractBvid(url);
if (bvid) {
const params = new URLSearchParams({
bvid,
autoplay: '0',
});
return `https://player.bilibili.com/player.html?${params.toString()}`;
}
return undefined;
};
const bilibiliConfig = {
name: 'bilibili',
match: (url: string) =>
validateEmbedIframeUrl(url, bilibiliValidationOptions) &&
(!!extractAvid(url) || !!extractBvid(url)),
buildOEmbedUrl: buildBiliPlayerEmbedUrl,
useOEmbedUrlDirectly: true,
options: {
widthInSurface: BILIBILI_DEFAULT_WIDTH_IN_SURFACE,
heightInSurface: BILIBILI_DEFAULT_HEIGHT_IN_SURFACE,
heightInNote: BILIBILI_DEFAULT_HEIGHT_IN_NOTE,
widthPercent: BILIBILI_DEFAULT_WIDTH_PERCENT,
allow: 'clipboard-write; encrypted-media; picture-in-picture',
sandbox: 'allow-same-origin allow-scripts',
style: 'border: none; border-radius: 8px;',
allowFullscreen: true,
},
};
export const BilibiliEmbedConfig = EmbedIframeConfigExtension(bilibiliConfig);

View File

@@ -10,7 +10,6 @@ const GENERIC_DEFAULT_HEIGHT_IN_NOTE = 400;
* These are based on the centralized cloud constants and known AFFiNE domains
*/
const AFFINE_DOMAINS = [
'affine.pro', // Main AFFiNE domain
'app.affine.pro', // Stable cloud domain
'insider.affine.pro', // Beta/internal cloud domain
'affine.fail', // Canary cloud domain
@@ -67,8 +66,9 @@ const genericConfig = {
heightInNote: GENERIC_DEFAULT_HEIGHT_IN_NOTE,
allowFullscreen: true,
style: 'border: none; border-radius: 8px;',
allow: 'clipboard-read; clipboard-write; picture-in-picture;',
allow: '',
referrerpolicy: 'no-referrer-when-downgrade',
sandbox: 'allow-scripts',
},
};

View File

@@ -1,3 +1,4 @@
import { BilibiliEmbedConfig } from './bilibili';
import { ExcalidrawEmbedConfig } from './excalidraw';
import { GenericEmbedConfig } from './generic';
import { GoogleDocsEmbedConfig } from './google-docs';
@@ -11,5 +12,6 @@ export const EmbedIframeConfigExtensions = [
MiroEmbedConfig,
ExcalidrawEmbedConfig,
GoogleDocsEmbedConfig,
BilibiliEmbedConfig,
GenericEmbedConfig,
];

View File

@@ -23,7 +23,7 @@ import {
type ReadonlySignal,
signal,
} from '@preact/signals-core';
import { html } from 'lit';
import { html, nothing } from 'lit';
import { query } from 'lit/decorators.js';
import { type ClassInfo, classMap } from 'lit/directives/class-map.js';
import { ifDefined } from 'lit/directives/if-defined.js';
@@ -45,6 +45,10 @@ import { safeGetIframeSrc } from './utils.js';
export type EmbedIframeStatus = 'idle' | 'loading' | 'success' | 'error';
const TRUSTED_SANDBOX =
'allow-same-origin allow-scripts allow-forms allow-presentation';
const UNTRUSTED_SANDBOX = 'allow-scripts';
export class EmbedIframeBlockComponent extends CaptionedBlockComponent<EmbedIframeBlockModel> {
selectedStyle$: ReadonlySignal<ClassInfo> | null = computed<ClassInfo>(
() => ({
@@ -89,6 +93,7 @@ export class EmbedIframeBlockComponent extends CaptionedBlockComponent<EmbedIfra
});
protected iframeOptions: IframeOptions | undefined = undefined;
private currentConfigName: string | undefined;
get embedIframeService() {
return this.std.get(EmbedIframeService);
@@ -279,6 +284,10 @@ export class EmbedIframeBlockComponent extends CaptionedBlockComponent<EmbedIfra
const config = this.embedIframeService?.getConfig(url);
if (config) {
this.iframeOptions = config.options;
this.currentConfigName = config.name;
} else {
this.iframeOptions = undefined;
this.currentConfigName = undefined;
}
};
@@ -328,26 +337,46 @@ export class EmbedIframeBlockComponent extends CaptionedBlockComponent<EmbedIfra
referrerpolicy,
scrolling,
allowFullscreen,
sandbox,
} = this.iframeOptions ?? {};
const width = `${widthPercent}%`;
// if the block is in the surface, use 100% as the height
// otherwise, use the heightInNote
const height = this.inSurface ? '100%' : heightInNote;
return html`
<iframe
const sandboxValue =
sandbox ??
(this.currentConfigName === 'generic'
? UNTRUSTED_SANDBOX
: TRUSTED_SANDBOX);
const sourceHost = this._getSourceHost();
return html`<iframe
width=${width ?? DEFAULT_IFRAME_WIDTH}
height=${height ?? DEFAULT_IFRAME_HEIGHT}
?allowfullscreen=${allowFullscreen}
loading="lazy"
frameborder="0"
credentialless
sandbox=${sandboxValue}
src=${ifDefined(iframeUrl)}
allow=${ifDefined(allow)}
referrerpolicy=${ifDefined(referrerpolicy)}
scrolling=${ifDefined(scrolling)}
style=${ifDefined(style)}
></iframe>
`;
${sourceHost
? html`<div class="affine-embed-iframe-source">${sourceHost}</div>`
: nothing}`;
};
private readonly _getSourceHost = () => {
const url = this.model.props.url ?? this.model.props.iframeUrl;
if (!url) return null;
try {
const parsed = new URL(url);
return parsed.hostname;
} catch {
return null;
}
};
private readonly _renderContent = () => {

View File

@@ -23,6 +23,19 @@ export const embedIframeBlockStyles = css`
height: 100%;
display: none;
}
.affine-embed-iframe-source {
position: absolute;
left: 8px;
bottom: 8px;
padding: 2px 6px;
background: rgba(0, 0, 0, 0.7);
color: #fff;
border-radius: 4px;
font-size: 12px;
line-height: 16px;
pointer-events: none;
}
.affine-embed-iframe-block-overlay.show {
display: block;
}

View File

@@ -124,7 +124,8 @@ export class EmbedLoomBlockComponent extends EmbedBlockComponent<
<iframe
src=${`https://www.loom.com/embed/${videoId}?hide_title=true`}
frameborder="0"
allow="fullscreen; accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allow="fullscreen; autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share"
sandbox="allow-scripts allow-same-origin allow-presentation"
loading="lazy"
credentialless
></iframe>

View File

@@ -148,8 +148,8 @@ export class EmbedYoutubeBlockComponent extends EmbedBlockComponent<
type="text/html"
src=${`https://www.youtube.com/embed/${videoId}`}
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
allow="fullscreen; autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share"
sandbox="allow-scripts allow-same-origin allow-presentation"
loading="lazy"
credentialless
></iframe>

View File

@@ -25,13 +25,13 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
"yjs": "^13.6.21",
"zod": "^3.23.8"
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
},
"exports": {
".": "./src/index.ts",
@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -25,12 +25,12 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"file-type": "^21.0.0",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
"zod": "^3.23.8"
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
"exports": {
".": "./src/index.ts",
@@ -44,5 +44,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -25,15 +25,15 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"@types/katex": "^0.16.7",
"@types/mdast": "^4.0.4",
"katex": "^0.16.11",
"katex": "^0.16.27",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"minimatch": "^10.1.1",
"remark-math": "^6.0.0",
"rxjs": "^7.8.1",
"zod": "^3.23.8"
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
"exports": {
".": "./src/index.ts",
@@ -46,5 +46,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -24,15 +24,15 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
"zod": "^3.23.8"
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
"devDependencies": {
"vitest": "3.1.3"
"vitest": "^3.2.4"
},
"exports": {
".": "./src/index.ts",
@@ -46,5 +46,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -1,4 +1,5 @@
import type { ListBlockModel } from '@blocksuite/affine-model';
import { getNumberPrefix } from '@blocksuite/affine-shared/utils';
import {
BulletedList01Icon,
BulletedList02Icon,
@@ -11,8 +12,6 @@ import {
} from '@blocksuite/icons/lit';
import { html } from 'lit';
import { getNumberPrefix } from './get-number-prefix.js';
const getListDeep = (model: ListBlockModel): number => {
let deep = 0;
let parent = model.store.getParent(model);

View File

@@ -27,15 +27,15 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"@types/lodash-es": "^4.17.12",
"@types/mdast": "^4.0.4",
"@vanilla-extract/css": "^1.17.0",
"lit": "^3.2.0",
"lodash-es": "^4.17.21",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
"zod": "^3.23.8"
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
"exports": {
".": "./src/index.ts",
@@ -49,5 +49,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -82,12 +82,13 @@ function createConversionItem(
config: TextConversionConfig,
group?: SlashMenuItem['group']
): SlashMenuActionItem {
const { name, description, icon, flavour, type } = config;
const { name, description, icon, flavour, type, searchAlias = [] } = config;
return {
name,
group,
description,
icon,
searchAlias,
tooltip: tooltips[name],
when: ({ model }) => model.store.schema.flavourSchemaMap.has(flavour),
action: ({ std }) => {

View File

@@ -23,12 +23,12 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"@types/mdast": "^4.0.4",
"lit": "^3.2.0",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
"zod": "^3.23.8"
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
"exports": {
".": "./src/index.ts",
@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -44,16 +44,16 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"@types/lodash-es": "^4.17.12",
"dompurify": "^3.3.0",
"html2canvas": "^1.4.1",
"lit": "^3.2.0",
"lodash-es": "^4.17.21",
"minimatch": "^10.0.1",
"rxjs": "^7.8.1",
"yjs": "^13.6.21",
"zod": "^3.23.8"
"minimatch": "^10.1.1",
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
},
"exports": {
".": "./src/index.ts",
@@ -67,5 +67,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -25,14 +25,14 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"@types/lodash-es": "^4.17.12",
"fractional-indexing": "^3.2.0",
"lit": "^3.2.0",
"lodash-es": "^4.17.21",
"nanoid": "^5.0.7",
"rxjs": "^7.8.1",
"zod": "^3.23.8"
"nanoid": "^5.1.6",
"rxjs": "^7.8.2",
"zod": "^3.25.76"
},
"exports": {
".": "./src/index.ts",
@@ -45,5 +45,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -20,20 +20,20 @@
"@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"@types/lodash-es": "^4.17.12",
"fractional-indexing": "^3.2.0",
"html2canvas": "^1.4.1",
"lit": "^3.2.0",
"lodash-es": "^4.17.21",
"nanoid": "^5.0.7",
"nanoid": "^5.1.6",
"pdf-lib": "^1.17.1",
"rxjs": "^7.8.1",
"yjs": "^13.6.21",
"zod": "^3.23.8"
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
},
"devDependencies": {
"vitest": "3.1.3"
"vitest": "^3.2.4"
},
"exports": {
".": "./src/index.ts",
@@ -46,5 +46,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -8,9 +8,8 @@ import type { RoughCanvas } from '../../index.js';
import type { CanvasRenderer } from '../canvas-renderer.js';
export type ElementRenderer<
T extends
| GfxPrimitiveElementModel
| GfxLocalElementModel = GfxPrimitiveElementModel,
T extends GfxPrimitiveElementModel | GfxLocalElementModel =
GfxPrimitiveElementModel,
> = (
model: T,
ctx: CanvasRenderingContext2D,

View File

@@ -10,7 +10,7 @@
"author": "toeverything",
"license": "MIT",
"dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "^1.4.0",
"@atlaskit/pragmatic-drag-and-drop": "^1.7.7",
"@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-ext-loader": "workspace:*",
"@blocksuite/affine-inline-preset": "workspace:*",
@@ -27,9 +27,9 @@
"@floating-ui/dom": "^1.6.13",
"@preact/signals-core": "^1.8.0",
"lit": "^3.2.0",
"rxjs": "^7.8.1",
"yjs": "^13.6.21",
"zod": "^3.24.1"
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
},
"exports": {
".": "./src/index.ts",
@@ -42,5 +42,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -418,6 +418,7 @@ export class TableCell extends SignalWatcher(
name: 'Paste',
prefix: PasteIcon(),
select: () => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
navigator.clipboard.readText().then(text => {
this.selectionController.doPaste(text, selected);
});

View File

@@ -21,22 +21,22 @@
"@lit/context": "^1.1.2",
"@lottiefiles/dotlottie-wc": "^0.5.0",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"@types/hast": "^3.0.4",
"@types/katex": "^0.16.7",
"@types/lodash-es": "^4.17.12",
"@types/mdast": "^4.0.4",
"collapse-white-space": "^2.1.0",
"date-fns": "^4.0.0",
"katex": "^0.16.11",
"katex": "^0.16.27",
"lit": "^3.2.0",
"lit-html": "^3.2.1",
"lodash-es": "^4.17.21",
"remark-math": "^6.0.0",
"rxjs": "^7.8.1",
"shiki": "^3.0.0",
"yjs": "^13.6.21",
"zod": "^3.23.8"
"rxjs": "^7.8.2",
"shiki": "^3.19.0",
"yjs": "^13.6.27",
"zod": "^3.25.76"
},
"exports": {
".": "./src/index.ts",
@@ -82,5 +82,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -23,6 +23,7 @@ export type MenuButtonData = {
select: (ele: HTMLElement) => void | false;
onHover?: (hover: boolean) => void;
testId?: string;
closeOnSelect?: boolean;
};
export class MenuButton extends MenuFocusable {
@@ -85,7 +86,9 @@ export class MenuButton extends MenuFocusable {
onClick() {
if (this.data.select(this) !== false) {
this.menu.options.onComplete?.();
this.menu.close();
if (this.data.closeOnSelect !== false) {
this.menu.close();
}
}
}
@@ -150,7 +153,9 @@ export class MobileMenuButton extends MenuFocusable {
onClick() {
if (this.data.select(this) !== false) {
this.menu.options.onComplete?.();
this.menu.close();
if (this.data.closeOnSelect !== false) {
this.menu.close();
}
}
}
@@ -200,6 +205,7 @@ export const menuButtonItems = {
select: (ele: HTMLElement) => void | false;
onHover?: (hover: boolean) => void;
class?: MenuClass;
closeOnSelect?: boolean;
hide?: () => boolean;
testId?: string;
}) =>
@@ -219,6 +225,7 @@ export const menuButtonItems = {
},
onHover: config.onHover,
select: config.select,
closeOnSelect: config.closeOnSelect,
class: {
'selected-item': config.isSelected ?? false,
...config.class,

View File

@@ -15,6 +15,7 @@ import {
computePosition,
type Middleware,
offset,
type Placement,
type ReferenceElement,
shift,
} from '@floating-ui/dom';
@@ -37,7 +38,9 @@ export class MenuComponent
display: flex;
flex-direction: column;
user-select: none;
min-width: 180px;
min-width: 320px;
max-width: 320px;
max-height: 700px;
box-shadow: ${unsafeCSSVar('overlayPanelShadow')};
border-radius: 4px;
background-color: ${unsafeCSSVarV2('layer/background/overlayPanel')};
@@ -439,6 +442,7 @@ export const createPopup = (
onClose?: () => void;
middleware?: Array<Middleware | null | undefined | false>;
container?: HTMLElement;
placement?: Placement;
}
) => {
const close = () => {
@@ -448,6 +452,7 @@ export const createPopup = (
const modal = createModal(target.root);
autoUpdate(target.targetRect, content, () => {
computePosition(target.targetRect, content, {
placement: options?.placement,
middleware: options?.middleware ?? [shift({ crossAxis: true })],
})
.then(({ x, y }) => {
@@ -520,6 +525,7 @@ export const popMenu = (
options: MenuOptions;
middleware?: Array<Middleware | null | undefined | false>;
container?: HTMLElement;
placement?: Placement;
}
): MenuHandler => {
if (IS_MOBILE) {
@@ -551,6 +557,7 @@ export const popMenu = (
offset(4),
],
container: props.container,
placement: props.placement,
});
return {
close: closePopup,
@@ -563,12 +570,14 @@ export const popMenu = (
export const popFilterableSimpleMenu = (
target: PopupTarget,
options: MenuConfig[],
onClose?: () => void
onClose?: () => void,
placement: Placement = 'bottom-start'
) => {
popMenu(target, {
options: {
items: options,
onClose,
},
placement,
});
};

View File

@@ -4,12 +4,15 @@ import {
autoPlacement,
autoUpdate,
computePosition,
type Middleware,
offset,
shift,
} from '@floating-ui/dom';
import { html, nothing, type TemplateResult } from 'lit';
import { css, html, nothing, type TemplateResult } from 'lit';
import { property } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { MenuButton } from './button.js';
import { MenuFocusable } from './focusable.js';
import { Menu, type MenuOptions } from './menu.js';
import { popMenu, popupTargetFromElement } from './menu-renderer.js';
@@ -20,29 +23,55 @@ export type MenuSubMenuData = {
options: MenuOptions;
select?: () => void;
class?: string;
openOnHover?: boolean;
middleware?: Middleware[];
autoHeight?: boolean;
closeOnSelect?: boolean;
};
export const subMenuOffset = offset({
mainAxis: 16,
crossAxis: -8.5,
crossAxis: 0,
});
export const subMenuPlacements = autoPlacement({
allowedPlacements: ['right-start', 'left-start', 'right-end', 'left-end'],
allowedPlacements: ['bottom-end'],
});
export const subMenuMiddleware = [subMenuOffset, subMenuPlacements];
export const dropdownSubMenuMiddleware = [
autoPlacement({ allowedPlacements: ['bottom-end'] }),
offset({ mainAxis: 8, crossAxis: 0 }),
shift({ crossAxis: true }),
];
export class MenuSubMenu extends MenuFocusable {
static override styles = [
MenuButton.styles,
css`
.affine-menu-button svg:last-child {
transition: transform 150ms cubic-bezier(0.42, 0, 1, 1);
}
affine-menu-sub-menu.active .affine-menu-button svg:last-child {
transform: rotate(90deg);
}
`,
];
createTime = 0;
override connectedCallback() {
super.connectedCallback();
this.createTime = Date.now();
this.disposables.addFromEvent(this, 'mouseenter', this.onMouseEnter);
if (this.data.openOnHover !== false) {
this.disposables.addFromEvent(this, 'mouseenter', this.onMouseEnter);
}
this.disposables.addFromEvent(this, 'click', e => {
e.preventDefault();
e.stopPropagation();
if (this.data.select) {
this.data.select();
this.menu.close();
if (this.data.closeOnSelect !== false) {
this.menu.close();
}
} else {
this.openSubMenu();
}
@@ -60,11 +89,38 @@ export class MenuSubMenu extends MenuFocusable {
}
openSubMenu() {
if (this.data.openOnHover === false) {
const { menu } = popMenu(popupTargetFromElement(this), {
options: {
...this.data.options,
onComplete: () => {
if (this.data.closeOnSelect !== false) {
this.menu.close();
}
},
onClose: () => {
menu.menuElement.remove();
this.data.options.onClose?.();
},
},
middleware: this.data.middleware,
});
if (this.data.autoHeight) {
menu.menuElement.style.minHeight = 'fit-content';
menu.menuElement.style.maxHeight = 'fit-content';
}
menu.menuElement.style.minWidth = '200px';
this.menu.openSubMenu(menu);
return;
}
const focus = this.menu.currentFocused$.value;
const menu = new Menu({
...this.data.options,
onComplete: () => {
this.menu.close();
if (this.data.closeOnSelect !== false) {
this.menu.close();
}
},
onClose: () => {
menu.menuElement.remove();
@@ -74,9 +130,14 @@ export class MenuSubMenu extends MenuFocusable {
},
});
this.menu.menuElement.parentElement?.append(menu.menuElement);
if (this.data.autoHeight) {
menu.menuElement.style.minHeight = 'fit-content';
menu.menuElement.style.maxHeight = 'fit-content';
}
menu.menuElement.style.minWidth = '200px';
const unsub = autoUpdate(this, menu.menuElement, () => {
computePosition(this, menu.menuElement, {
middleware: subMenuMiddleware,
middleware: this.data.middleware ?? subMenuMiddleware,
})
.then(({ x, y }) => {
menu.menuElement.style.left = `${x}px`;
@@ -125,14 +186,22 @@ export class MobileSubMenu extends MenuFocusable {
options: {
...this.data.options,
onComplete: () => {
this.menu.close();
if (this.data.closeOnSelect !== false) {
this.menu.close();
}
},
onClose: () => {
menu.menuElement.remove();
this.data.options.onClose?.();
},
},
middleware: this.data.middleware,
});
if (this.data.autoHeight) {
menu.menuElement.style.minHeight = 'fit-content';
menu.menuElement.style.maxHeight = 'fit-content';
}
menu.menuElement.style.minWidth = '200px';
this.menu.openSubMenu(menu);
}
@@ -175,6 +244,10 @@ export const subMenuItems = {
options: MenuOptions;
disableArrow?: boolean;
hide?: () => boolean;
openOnHover?: boolean;
middleware?: Middleware[];
autoHeight?: boolean;
closeOnSelect?: boolean;
}) =>
menu => {
if (config.hide?.() || !menu.search(config.name)) {
@@ -190,6 +263,10 @@ export const subMenuItems = {
${config.disableArrow ? nothing : ArrowRightSmallIcon()} `,
class: config.class,
options: config.options,
openOnHover: config.openOnHover,
middleware: config.middleware,
autoHeight: config.autoHeight,
closeOnSelect: config.closeOnSelect,
};
return renderSubMenu(data, menu);
},

View File

@@ -21,16 +21,18 @@
"@floating-ui/dom": "^1.6.13",
"@lit/context": "^1.1.2",
"@preact/signals-core": "^1.8.0",
"@toeverything/theme": "^1.1.16",
"@toeverything/theme": "^1.1.23",
"@types/lodash-es": "^4.17.12",
"clsx": "^2.1.1",
"date-fns": "^4.0.0",
"lit": "^3.2.0",
"lodash-es": "^4.17.21",
"rxjs": "^7.8.1",
"vitest": "^3.2.3",
"yjs": "^13.6.21",
"zod": "^3.23.8"
"rxjs": "^7.8.2",
"yjs": "^13.6.27",
"zod": "^3.25.76"
},
"devDependencies": {
"vitest": "^3.2.4"
},
"exports": {
".": "./src/index.ts",
@@ -46,5 +48,5 @@
"!src/__tests__",
"!dist/__tests__"
],
"version": "0.25.7"
"version": "0.26.0"
}

View File

@@ -0,0 +1,35 @@
import { describe, expect, it } from 'vitest';
import { compareDateKeys } from '../core/group-by/compare-date-keys.js';
describe('compareDateKeys', () => {
it('sorts relative keys ascending', () => {
const cmp = compareDateKeys('date-relative', true);
const keys = ['today', 'last7', 'yesterday', 'last30'];
const sorted = [...keys].sort(cmp);
expect(sorted).toEqual(['last30', 'last7', 'yesterday', 'today']);
});
it('sorts relative keys descending', () => {
const cmp = compareDateKeys('date-relative', false);
const keys = ['today', 'last7', 'yesterday', 'last30'];
const sorted = [...keys].sort(cmp);
expect(sorted).toEqual(['today', 'yesterday', 'last7', 'last30']);
});
it('sorts numeric keys correctly', () => {
const asc = compareDateKeys('date-day', true);
const desc = compareDateKeys('date-day', false);
const keys = ['3', '1', '2'];
expect([...keys].sort(asc)).toEqual(['1', '2', '3']);
expect([...keys].sort(desc)).toEqual(['3', '2', '1']);
});
it('handles mixed relative and numeric keys', () => {
const cmp = compareDateKeys('date-relative', true);
const keys = ['today', '1', 'yesterday', '2'];
const sorted = [...keys].sort(cmp);
expect(sorted[0]).toBe('1');
expect(sorted[sorted.length - 1]).toBe('today');
});
});

View File

@@ -6,6 +6,7 @@ import {
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
import { InvisibleIcon, ViewIcon } from '@blocksuite/icons/lit';
import { ShadowlessElement } from '@blocksuite/std';
import type { Middleware } from '@floating-ui/dom';
import { computed } from '@preact/signals-core';
import { cssVarV2 } from '@toeverything/theme/v2';
import { css, html, unsafeCSS } from 'lit';
@@ -235,13 +236,16 @@ export const popPropertiesSetting = (
view: SingleView;
onClose?: () => void;
onBack?: () => void;
}
},
middleware?: Array<Middleware | null | undefined | false>
) => {
popMenu(target, {
const handler = popMenu(target, {
middleware,
options: {
title: {
text: 'Properties',
onBack: props.onBack,
onClose: props.onClose,
postfix: () => {
const items = props.view.propertiesRaw$.value;
const isAllShowed = items.every(property => !property.hide$.value);
@@ -270,8 +274,10 @@ export const popPropertiesSetting = (
],
}),
],
onClose: props.onClose,
},
});
handler.menu.menuElement.style.minHeight = '550px';
// const view = new DataViewPropertiesSettingView();
// view.view = props.view;

View File

@@ -2,6 +2,7 @@ export type GroupBy = {
type: 'groupBy';
columnId: string;
name: string;
hideEmpty?: boolean;
sort?: {
desc: boolean;
};

View File

@@ -484,6 +484,18 @@ const popMobileTagSelect = (target: PopupTarget, ops: TagSelectOptions) => {
const onInput = (e: InputEvent) => {
tagManager.text$.value = (e.target as HTMLInputElement).value;
};
const onKeydown = (e: KeyboardEvent) => {
e.stopPropagation();
const inputValue = (e.target as HTMLInputElement).value.trim();
if (e.key === 'Backspace' && inputValue === '') {
const values = tagManager.value$.value;
const lastId = values[values.length - 1];
if (lastId) {
e.preventDefault();
tagManager.deleteTag(lastId);
}
}
};
return popMenu(target, {
options: {
onClose: () => {
@@ -511,11 +523,21 @@ const popMobileTagSelect = (target: PopupTarget, ops: TagSelectOptions) => {
});
return html` <div class="${tagContainerStyle}" style=${style}>
<div class="${tagTextStyle}">${option.value}</div>
<div
class="${tagDeleteIconStyle}"
@click="${(e: MouseEvent) => {
e.stopPropagation();
tagManager.deleteTag(id);
}}"
>
${CloseIcon()}
</div>
</div>`;
})}
<input
.value="${tagManager.text$.value}"
@input="${onInput}"
@keydown="${onKeydown}"
placeholder="Type here..."
type="text"
style="outline: none;border: none;flex:1;min-width: 10px"

View File

@@ -24,7 +24,7 @@ export const popCreateFilter = (
middleware?: Middleware[];
}
) => {
popMenu(target, {
const subHandler = popMenu(target, {
middleware: ops?.middleware,
options: {
onClose: props.onClose,
@@ -64,4 +64,5 @@ export const popCreateFilter = (
],
},
});
subHandler.menu.menuElement.style.minHeight = '550px';
};

View File

@@ -15,6 +15,7 @@ export const allLiteralConfig: LiteralItemsConfig[] = [
() => {
return html` <date-picker
.padding="${8}"
.size="${20}"
.value="${value.value}"
.onChange="${(date: Date) => {
onChange(date.getTime());

View File

@@ -0,0 +1,62 @@
export const RELATIVE_ASC = [
'last30',
'last7',
'yesterday',
'today',
'tomorrow',
'next7',
'next30',
] as const;
export const RELATIVE_DESC = [...RELATIVE_ASC].reverse();
/**
* Sorts relative date keys in chronological order
*/
export function sortRelativeKeys(a: string, b: string, asc: boolean): number {
const order: readonly string[] = asc ? RELATIVE_ASC : RELATIVE_DESC;
const idxA = order.indexOf(a);
const idxB = order.indexOf(b);
if (idxA !== -1 && idxB !== -1) return idxA - idxB;
if (idxA !== -1) return asc ? 1 : -1;
if (idxB !== -1) return asc ? -1 : 1;
return 0; // Both not found
}
/**
* Sorts numeric date keys (timestamps)
*/
export function sortNumericKeys(a: string, b: string, asc: boolean): number {
const na = Number(a);
const nb = Number(b);
if (Number.isFinite(na) && Number.isFinite(nb)) {
return asc ? na - nb : nb - na;
}
return 0; // Not both numeric
}
export function compareDateKeys(mode: string | undefined, asc: boolean) {
return (a: string, b: string) => {
if (mode === 'date-relative') {
// Try relative key sorting first
const relativeResult = sortRelativeKeys(a, b, asc);
if (relativeResult !== 0) return relativeResult;
// Try numeric sorting second
const numericResult = sortNumericKeys(a, b, asc);
if (numericResult !== 0) return numericResult;
// Fallback to lexicographic order for mixed cases
return asc ? a.localeCompare(b) : b.localeCompare(a);
}
// Standard numeric/lexicographic comparison for other date modes
return (
sortNumericKeys(a, b, asc) ||
(asc ? a.localeCompare(b) : b.localeCompare(a))
);
};
}

View File

@@ -18,6 +18,7 @@ export const defaultGroupBy = (
type: 'groupBy',
columnId: propertyId,
name: name,
hideEmpty: true,
}
: undefined;
};

View File

@@ -1,9 +1,22 @@
import hash from '@emotion/hash';
import {
addDays,
differenceInCalendarDays,
format as fmt,
isToday,
isTomorrow,
isYesterday,
startOfDay,
startOfMonth,
startOfWeek,
startOfYear,
} from 'date-fns';
import type { TypeInstance } from '../logical/type.js';
import { t } from '../logical/type-presets.js';
import { createUniComponentFromWebComponent } from '../utils/uni-component/uni-component.js';
import { BooleanGroupView } from './renderer/boolean-group.js';
import { DateGroupView } from './renderer/date-group.js';
import { NumberGroupView } from './renderer/number-group.js';
import { SelectGroupView } from './renderer/select-group.js';
import { StringGroupView } from './renderer/string-group.js';
@@ -15,171 +28,239 @@ export const createGroupByConfig = <
GroupValue = unknown,
>(
config: GroupByConfig<Data, MatchType, GroupValue>
): GroupByConfig => {
return config as never as GroupByConfig;
};
): GroupByConfig => config as never;
export const ungroups = {
key: 'Ungroups',
value: null,
};
export const groupByMatchers = [
const WEEK_OPTS_MON = { weekStartsOn: 1 } as const;
const WEEK_OPTS_SUN = { weekStartsOn: 0 } as const;
const rangeLabel = (a: Date, b: Date) =>
`${fmt(a, 'MMM d yyyy')} ${fmt(b, 'MMM d yyyy')}`;
function buildDateCfg(
name: string,
grouper: (ms: number | null) => { key: string; value: number | null }[],
groupName: (v: number | null) => string
): GroupByConfig {
return createGroupByConfig({
name,
matchType: t.date.instance(),
groupName: (_t, v) => groupName(v),
defaultKeys: _t => [ungroups],
valuesGroup: (v: number | null, _t) => grouper(v),
addToGroup: (grp: number | null, _old: number | null) => grp,
view: createUniComponentFromWebComponent(DateGroupView),
});
}
const dateRelativeCfg = buildDateCfg(
'date-relative',
v => {
if (v == null) return [ungroups];
const d = startOfDay(new Date(v));
const today = startOfDay(new Date());
const daysDiff = differenceInCalendarDays(d, today);
// Handle specific days
if (isToday(d)) return [{ key: 'today', value: +d }];
if (isTomorrow(d)) return [{ key: 'tomorrow', value: +d }];
if (isYesterday(d)) return [{ key: 'yesterday', value: +d }];
// Handle future dates
if (daysDiff > 0) {
if (daysDiff <= 7) return [{ key: 'next7', value: +d }];
if (daysDiff <= 30) return [{ key: 'next30', value: +d }];
// Group by month for future dates beyond 30 days
const m = startOfMonth(d);
return [{ key: `${+m}`, value: +m }];
}
// Handle past dates
const daysAgo = -daysDiff;
if (daysAgo <= 7) return [{ key: 'last7', value: +d }];
if (daysAgo <= 30) return [{ key: 'last30', value: +d }];
// Group by month for past dates beyond 30 days
const m = startOfMonth(d);
return [{ key: `${+m}`, value: +m }];
},
v => {
if (v == null) return '';
const d = startOfDay(new Date(v));
const today = startOfDay(new Date());
const daysDiff = differenceInCalendarDays(d, today);
// Handle specific days
if (isToday(d)) return 'Today';
if (isTomorrow(d)) return 'Tomorrow';
if (isYesterday(d)) return 'Yesterday';
// Handle future dates
if (daysDiff > 0) {
if (daysDiff <= 7) return 'Next 7 days';
if (daysDiff <= 30) return 'Next 30 days';
// Show month/year for future dates beyond 30 days
return fmt(new Date(v), 'MMM yyyy');
}
// Handle past dates
const daysAgo = -daysDiff;
if (daysAgo <= 7) return 'Last 7 days';
if (daysAgo <= 30) return 'Last 30 days';
// Show month/year for past dates beyond 30 days
return fmt(new Date(v), 'MMM yyyy');
}
);
const dateDayCfg = buildDateCfg(
'date-day',
v => {
if (v == null) return [ungroups];
const d = startOfDay(new Date(v));
return [{ key: `${+d}`, value: +d }];
},
v => (v ? fmt(new Date(v), 'MMM d yyyy') : '')
);
const dateWeekSunCfg = buildDateCfg(
'date-week-sun',
v => {
if (v == null) return [ungroups];
const w = startOfWeek(new Date(v), WEEK_OPTS_SUN);
return [{ key: `${+w}`, value: +w }];
},
v => (v ? rangeLabel(new Date(v), addDays(new Date(v), 6)) : '')
);
const dateWeekMonCfg = buildDateCfg(
'date-week-mon',
v => {
if (v == null) return [ungroups];
const w = startOfWeek(new Date(v), WEEK_OPTS_MON);
return [{ key: `${+w}`, value: +w }];
},
v => (v ? rangeLabel(new Date(v), addDays(new Date(v), 6)) : '')
);
const dateMonthCfg = buildDateCfg(
'date-month',
v => {
if (v == null) return [ungroups];
const m = startOfMonth(new Date(v));
return [{ key: `${+m}`, value: +m }];
},
v => (v ? fmt(new Date(v), 'MMM yyyy') : '')
);
const dateYearCfg = buildDateCfg(
'date-year',
v => {
if (v == null) return [ungroups];
const y = startOfYear(new Date(v));
return [{ key: `${+y}`, value: +y }];
},
v => (v ? fmt(new Date(v), 'yyyy') : '')
);
export const groupByMatchers: GroupByConfig[] = [
createGroupByConfig({
name: 'select',
matchType: t.tag.instance(),
groupName: (type, value: string | null) => {
if (t.tag.is(type) && type.data) {
if (t.tag.is(type) && type.data)
return type.data.find(v => v.id === value)?.value ?? '';
}
return '';
},
defaultKeys: type => {
if (t.tag.is(type) && type.data) {
return [
ungroups,
...type.data.map(v => ({
key: v.id,
value: v.id,
})),
];
}
return [ungroups];
},
valuesGroup: (value, _type) => {
if (value == null) {
return [ungroups];
}
return [
{
key: `${value}`,
value: value.toString(),
},
];
},
addToGroup: v => v,
defaultKeys: type =>
t.tag.is(type) && type.data
? [ungroups, ...type.data.map(v => ({ key: v.id, value: v.id }))]
: [ungroups],
valuesGroup: (value, _t) =>
value == null ? [ungroups] : [{ key: `${value}`, value }],
addToGroup: (v: string | null, _old: string | null) => v,
view: createUniComponentFromWebComponent(SelectGroupView),
}),
createGroupByConfig({
name: 'multi-select',
matchType: t.array.instance(t.tag.instance()),
groupName: (type, value: string | null) => {
if (t.array.is(type) && t.tag.is(type.element) && type.element.data) {
if (t.array.is(type) && t.tag.is(type.element) && type.element.data)
return type.element.data.find(v => v.id === value)?.value ?? '';
}
return '';
},
defaultKeys: type => {
if (t.array.is(type) && t.tag.is(type.element) && type.element.data) {
return [
ungroups,
...type.element.data.map(v => ({
key: v.id,
value: v.id,
})),
];
}
defaultKeys: type =>
t.array.is(type) && t.tag.is(type.element) && type.element.data
? [
ungroups,
...type.element.data.map(v => ({ key: v.id, value: v.id })),
]
: [ungroups],
valuesGroup: (value, _t) => {
if (value == null) return [ungroups];
if (Array.isArray(value) && value.length)
return value.map(id => ({ key: `${id}`, value: id }));
return [ungroups];
},
valuesGroup: (value, _type) => {
if (value == null) {
return [ungroups];
}
if (Array.isArray(value) && value.length) {
return value.map(id => ({
key: `${id}`,
value: id,
}));
}
return [ungroups];
},
addToGroup: (value, old) => {
if (value == null) {
return old;
}
addToGroup: (
value: string | null,
old: string[] | null
): string[] | null => {
if (value == null) return old;
return Array.isArray(old) ? [...old, value] : [value];
},
removeFromGroup: (value, old) => {
if (Array.isArray(old)) {
return old.filter(v => v !== value);
}
return old;
},
removeFromGroup: (value, old) =>
Array.isArray(old) ? old.filter(v => v !== value) : old,
view: createUniComponentFromWebComponent(SelectGroupView),
}),
createGroupByConfig({
name: 'text',
matchType: t.string.instance(),
groupName: (_type, value: string | null) => {
return `${value ?? ''}`;
},
defaultKeys: _type => {
return [ungroups];
},
valuesGroup: (value, _type) => {
if (typeof value !== 'string' || !value) {
return [ungroups];
}
return [
{
key: hash(value),
value,
},
];
},
addToGroup: v => v,
groupName: (_t, v) => `${v ?? ''}`,
defaultKeys: _t => [ungroups],
valuesGroup: (v, _t) =>
typeof v !== 'string' || !v ? [ungroups] : [{ key: hash(v), value: v }],
addToGroup: (v: string | null, _old: string | null) => v,
view: createUniComponentFromWebComponent(StringGroupView),
}),
createGroupByConfig({
name: 'number',
matchType: t.number.instance(),
groupName: (_type, value: number | null) => {
return `${value ?? ''}`;
},
defaultKeys: _type => {
return [ungroups];
},
valuesGroup: (value: number | null, _type) => {
if (typeof value !== 'number') {
return [ungroups];
}
return [
{
key: `g:${Math.floor(value / 10)}`,
value: Math.floor(value / 10),
},
];
},
addToGroup: value => (typeof value === 'number' ? value * 10 : null),
groupName: (_t, v) => `${v ?? ''}`,
defaultKeys: _t => [ungroups],
valuesGroup: (v, _t) =>
typeof v !== 'number'
? [ungroups]
: [{ key: `g:${Math.floor(v / 10)}`, value: Math.floor(v / 10) }],
addToGroup: (v: number | null, _old: number | null) =>
typeof v === 'number' ? v * 10 : null,
view: createUniComponentFromWebComponent(NumberGroupView),
}),
createGroupByConfig({
name: 'boolean',
matchType: t.boolean.instance(),
groupName: (_type, value: boolean | null) => {
return `${value?.toString() ?? ''}`;
},
defaultKeys: _type => {
return [
{ key: 'true', value: true },
{ key: 'false', value: false },
];
},
valuesGroup: (value, _type) => {
if (typeof value !== 'boolean') {
return [
{
key: 'false',
value: false,
},
];
}
return [
{
key: value.toString(),
value: value,
},
];
},
addToGroup: v => v,
groupName: (_t, v) => `${v?.toString() ?? ''}`,
defaultKeys: _t => [
ungroups,
{ key: 'true', value: true },
{ key: 'false', value: false },
],
valuesGroup: (v, _t) =>
typeof v !== 'boolean' ? [ungroups] : [{ key: v.toString(), value: v }],
addToGroup: (v: boolean | null, _old: boolean | null) => v,
view: createUniComponentFromWebComponent(BooleanGroupView),
}),
dateRelativeCfg,
dateDayCfg,
dateWeekSunCfg,
dateWeekMonCfg,
dateMonthCfg,
dateYearCfg,
];

View File

@@ -9,6 +9,18 @@ export const createGroupByMatcher = (list: GroupByConfig[]) => {
return new Matcher_(list, v => v.matchType);
};
export const findGroupByConfigByName = (
dataSource: DataSource,
name: string
): GroupByConfig | undefined => {
const svc = getGroupByService(dataSource);
const all: GroupByConfig[] = [
...svc.allExternalGroupByConfig(),
...groupByMatchers,
];
return all.find(c => c.name === name);
};
export class GroupByService {
constructor(private readonly dataSource: DataSource) {}

View File

@@ -16,6 +16,14 @@ export class BooleanGroupView extends BaseGroup<boolean, NonNullable<unknown>> {
`;
protected override render(): unknown {
// Handle null/undefined values
if (this.value == null) {
const displayName = `No ${this.group.property.name$.value ?? 'value'}`;
return html` <div class="data-view-group-title-boolean-view">
${displayName}
</div>`;
}
return html` <div class="data-view-group-title-boolean-view">
${this.value
? CheckBoxCheckSolidIcon({ style: `color:#1E96EB` })

View File

@@ -0,0 +1,54 @@
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
import { ShadowlessElement } from '@blocksuite/std';
import { css, html } from 'lit';
import { property } from 'lit/decorators.js';
import type { Group } from '../trait.js';
export class DateGroupView extends SignalWatcher(
WithDisposable(ShadowlessElement)
) {
static override styles = css`
.dv-date-group {
border-radius: 8px;
padding: 4px 8px;
width: max-content;
cursor: default;
display: flex;
align-items: center;
gap: 6px;
}
.dv-date-group:hover {
background-color: var(--affine-hover-color);
}
.counter {
flex-shrink: 0;
min-width: 22px;
height: 22px;
border-radius: 4px;
background: var(--affine-background-secondary-color);
color: var(--affine-text-secondary-color);
font-size: var(--data-view-cell-text-size);
display: flex;
align-items: center;
justify-content: center;
}
`;
@property({ attribute: false })
accessor group!: Group;
protected override render() {
const name = this.group.name$.value;
// Use contextual name based on the property when value is null
const displayName =
name ||
(this.group.value === null
? `No ${this.group.property.name$.value}`
: 'Ungroups');
return html`<div class="dv-date-group">
<span>${displayName}</span>
</div>`;
}
}
customElements.define('data-view-date-group-view', DateGroupView);

View File

@@ -45,7 +45,8 @@ export class NumberGroupView extends BaseGroup<number, NonNullable<unknown>> {
protected override render(): unknown {
if (this.value == null) {
return html` <div>Ungroups</div>`;
const displayName = `No ${this.group.property.name$.value}`;
return html` <div>${displayName}</div>`;
}
if (this.value >= 10) {
return html` <div

View File

@@ -84,10 +84,11 @@ export class SelectGroupView extends BaseGroup<
protected override render(): unknown {
const tag = this.tag;
if (!tag) {
const displayName = `No ${this.group.property.name$.value}`;
return html` <div
style="font-size: 14px;color: var(--affine-text-primary-color);line-height: 22px;"
>
Ungroups
${displayName}
</div>`;
}
const style = styleMap({

View File

@@ -41,7 +41,8 @@ export class StringGroupView extends BaseGroup<string, NonNullable<unknown>> {
protected override render(): unknown {
if (!this.value) {
return html` <div>Ungroups</div>`;
const displayName = `No ${this.group.property.name$.value}`;
return html` <div>${displayName}</div>`;
}
return html` <div
@click="${this._click}"

View File

@@ -1,4 +1,5 @@
import {
dropdownSubMenuMiddleware,
menu,
type MenuConfig,
type MenuOptions,
@@ -6,9 +7,12 @@ import {
type PopupTarget,
} from '@blocksuite/affine-components/context-menu';
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
import { DeleteIcon } from '@blocksuite/icons/lit';
import { DeleteIcon, InvisibleIcon, ViewIcon } from '@blocksuite/icons/lit';
import { ShadowlessElement } from '@blocksuite/std';
import type { Middleware } from '@floating-ui/dom';
import { autoPlacement, offset, shift } from '@floating-ui/dom';
import { computed } from '@preact/signals-core';
import { cssVarV2 } from '@toeverything/theme/v2';
import { css, html, unsafeCSS } from 'lit';
import { property, query } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
@@ -28,6 +32,24 @@ import { getGroupByService } from './matcher.js';
import type { GroupTrait } from './trait.js';
import type { GroupRenderProps } from './types.js';
const dateModeLabel = (key?: string) => {
switch (key) {
case 'date-relative':
return 'Relative';
case 'date-day':
return 'Day';
case 'date-week-mon':
case 'date-week-sun':
return 'Week';
case 'date-month':
return 'Month';
case 'date-year':
return 'Year';
default:
return '';
}
};
export class GroupSetting extends SignalWatcher(
WithDisposable(ShadowlessElement)
) {
@@ -39,13 +61,44 @@ export class GroupSetting extends SignalWatcher(
${unsafeCSS(dataViewCssVariable())};
}
.group-sort-setting {
display: flex;
flex-direction: column;
gap: 4px;
z-index: 1;
max-height: 200px;
overflow: hidden auto;
margin-right: 0;
margin-bottom: 0;
}
/* WebKit-based browser scrollbar styling */
.group-sort-setting::-webkit-scrollbar {
width: 8px;
}
.group-sort-setting::-webkit-scrollbar-thumb {
background-color: #b0b0b0; /* Grey slider */
border-radius: 4px;
}
.group-sort-setting::-webkit-scrollbar-track {
background: transparent;
}
.group-sort-setting {
scrollbar-width: thin;
scrollbar-color: #b0b0b0 transparent;
}
.group-hidden {
opacity: 0.5;
}
.group-item {
display: flex;
padding: 4px 12px;
position: relative;
cursor: grab;
}
.group-item-drag-bar {
width: 4px;
height: 12px;
@@ -57,18 +110,49 @@ export class GroupSetting extends SignalWatcher(
bottom: 0;
margin: auto;
}
.group-item:hover .group-item-drag-bar {
background-color: #c0bfc1;
}
.group-item-op-icon {
display: flex;
align-items: center;
border-radius: 4px;
}
.group-item-op-icon:hover {
background-color: var(--affine-hover-color);
}
.group-item-op-icon svg {
fill: var(--affine-icon-color);
color: var(--affine-icon-color);
width: 20px;
height: 20px;
}
.group-item-name {
font-size: 14px;
line-height: 22px;
flex: 1;
}
.properties-group-op {
padding: 4px 8px;
font-size: 12px;
line-height: 20px;
font-weight: 500;
border-radius: 4px;
cursor: pointer;
color: ${unsafeCSS(cssVarV2.button.primary)};
}
.properties-group-op:hover {
background-color: var(--affine-hover-color);
}
`;
@property({ attribute: false })
accessor groupTrait!: GroupTrait;
groups$ = computed(() => {
return this.groupTrait.groupsDataList$.value;
});
groups$ = computed(() => this.groupTrait.groupsDataListAll$.value);
sortContext = createSortContext({
activators: defaultActivators,
@@ -78,99 +162,101 @@ export class GroupSetting extends SignalWatcher(
const activeId = evt.active.id;
const groups = this.groups$.value;
if (over && over.id !== activeId && groups) {
const activeIndex = groups.findIndex(data => data?.key === activeId);
const overIndex = groups.findIndex(data => data?.key === over.id);
const aIndex = groups.findIndex(g => g?.key === activeId);
const oIndex = groups.findIndex(g => g?.key === over.id);
this.groupTrait.moveGroupTo(
activeId,
activeIndex > overIndex
? {
before: true,
id: over.id,
}
: {
before: false,
id: over.id,
}
aIndex > oIndex
? { before: true, id: over.id }
: { before: false, id: over.id }
);
}
},
modifiers: [
({ transform }) => {
return {
...transform,
x: 0,
};
},
],
items: computed(() => {
return (
this.groupTrait.groupsDataList$.value?.map(
v => v?.key ?? 'default key'
) ?? []
);
}),
modifiers: [({ transform }) => ({ ...transform, x: 0 })],
items: computed(
() =>
this.groupTrait.groupsDataListAll$.value?.map(v => v?.key ?? '') ?? []
),
strategy: verticalListSortingStrategy,
});
override connectedCallback() {
super.connectedCallback();
this._disposables.addFromEvent(this, 'pointerdown', e => {
e.stopPropagation();
});
this._disposables.addFromEvent(this, 'pointerdown', e =>
e.stopPropagation()
);
}
protected override render(): unknown {
const groups = this.groupTrait.groupsDataList$.value;
if (!groups) {
return;
}
protected override render() {
const groups = this.groupTrait.groupsDataListAll$.value;
if (!groups) return;
const map = this.groupTrait.groupDataMap$.value;
const isAllShowed = map
? Object.keys(map).every(k => !this.groupTrait.isGroupHidden(k))
: true;
const clickChangeAll = () => {
if (!map) return;
Object.keys(map).forEach(key => {
this.groupTrait.setGroupHide(key, isAllShowed);
});
};
return html`
<div style="padding: 7px 0;">
<div
style="padding:7px 0;display:flex;justify-content:space-between;align-items:center;"
>
<div
style="padding: 0 4px; font-size: 12px;color: var(--affine-text-secondary-color);line-height: 20px;"
style="padding:0 4px;font-size:12px;color:var(--affine-text-secondary-color);line-height:20px;"
>
Groups
</div>
<div></div>
<div class="properties-group-op" @click="${clickChangeAll}">
${isAllShowed ? 'Hide All' : 'Show All'}
</div>
</div>
<div
style="display:flex;flex-direction: column;gap: 4px;"
class="group-sort-setting"
>
<div class="group-sort-setting">
${repeat(
groups,
group => group?.key ?? 'default key',
group => {
const type = group.property.dataType$.value;
g => g?.key ?? 'k',
g => {
if (!g) return;
const type = g.property.dataType$.value;
if (!type) return;
const props: GroupRenderProps = {
group,
readonly: true,
};
return html` <div
${sortable(group.key)}
${dragHandler(group.key)}
class="dv-hover dv-round-4 group-item"
>
<div class="group-item-drag-bar"></div>
const props: GroupRenderProps = { group: g, readonly: true };
const icon = g.hide$.value ? InvisibleIcon() : ViewIcon();
return html`
<div
style="padding: 0 4px;position:relative;pointer-events: none;max-width: 330px"
${sortable(g.key)}
${dragHandler(g.key)}
class="dv-hover dv-round-4 group-item ${g.hide$.value
? 'group-hidden'
: ''}"
>
${renderUniLit(group.view, props)}
<div class="group-item-drag-bar"></div>
<div
style="position:absolute;left: 0;top: 0;right: 0;bottom: 0;"
></div>
class="group-item-name"
style="padding:0 4px;position:relative;pointer-events:none;max-width:330px;"
>
${renderUniLit(g.view, props)}
<div
style="position:absolute;left:0;top:0;right:0;bottom:0;"
></div>
</div>
<div
class="group-item-op-icon"
@click="${() => g.hideSet(!g.hide$.value)}"
>
${icon}
</div>
</div>
</div>`;
`;
}
)}
</div>
`;
}
@query('.group-sort-setting')
accessor groupContainer!: HTMLElement;
@query('.group-sort-setting') accessor groupContainer!: HTMLElement;
}
export const selectGroupByProperty = (
@@ -184,10 +270,7 @@ export const selectGroupByProperty = (
const view = group.view;
return {
onClose: ops?.onClose,
title: {
text: 'Group by',
onBack: ops?.onBack,
},
title: { text: 'Group by', onBack: ops?.onBack, onClose: ops?.onClose },
items: [
menu.group({
items: view.propertiesRaw$.value
@@ -219,7 +302,7 @@ export const selectGroupByProperty = (
menu.action({
prefix: DeleteIcon(),
hide: () =>
view instanceof KanbanSingleView || group.property$.value == null,
view instanceof KanbanSingleView || !group.property$.value,
class: { 'delete-item': true },
name: 'Remove Grouping',
select: () => {
@@ -232,77 +315,305 @@ export const selectGroupByProperty = (
],
};
};
export const popSelectGroupByProperty = (
target: PopupTarget,
group: GroupTrait,
ops?: {
onSelect?: () => void;
onClose?: () => void;
onBack?: () => void;
}
ops?: { onSelect?: () => void; onClose?: () => void; onBack?: () => void },
middleware?: Array<Middleware | null | undefined | false>
) => {
popMenu(target, {
const handler = popMenu(target, {
options: selectGroupByProperty(group, ops),
middleware,
});
handler.menu.menuElement.style.minHeight = '550px';
};
export const popGroupSetting = (
target: PopupTarget,
group: GroupTrait,
onBack: () => void
onBack: () => void,
onClose?: () => void,
middleware?: Array<Middleware | null | undefined | false>
) => {
const view = group.view;
const groupProperty = group.property$.value;
if (groupProperty == null) {
return;
}
const type = groupProperty.type$.value;
if (!type) {
return;
}
const icon = groupProperty.icon;
const gProp = group.property$.value;
if (!gProp) return;
const type = gProp.type$.value;
if (!type) return;
const icon = gProp.icon;
const menuHandler = popMenu(target, {
options: {
title: {
text: 'Group',
onBack: onBack,
onBack,
onClose,
},
items: [
menu.group({
items: [
menu.subMenu({
menu.action({
name: 'Group By',
postfix: html`
<div
style="display:flex;align-items:center;gap: 4px;font-size: 12px;line-height: 20px;color: var(--affine-text-secondary-color);margin-right: 4px;margin-left: 8px;"
style="display:flex;align-items:center;gap:4px;font-size:14px;line-height:20px;color:var(--affine-text-secondary-color);margin-left:8px;"
class="dv-icon-16"
>
${renderUniLit(icon, {})} ${groupProperty.name$.value}
${renderUniLit(icon, {})} ${gProp.name$.value}
</div>
`,
label: () => html`
<div style="color: var(--affine-text-secondary-color);">
Group By
</div>
`,
options: selectGroupByProperty(group, {
onSelect: () => {
menuHandler.close();
popGroupSetting(target, group, onBack);
select: () => {
const subHandler = popMenu(target, {
options: selectGroupByProperty(group, {
onSelect: () => {
menuHandler.close();
popGroupSetting(
target,
group,
onBack,
onClose,
middleware
);
},
onBack: () => {
menuHandler.close();
popGroupSetting(
target,
group,
onBack,
onClose,
middleware
);
},
onClose,
}),
middleware: [
autoPlacement({
allowedPlacements: ['bottom-start', 'top-start'],
}),
offset({ mainAxis: 15, crossAxis: -162 }),
shift({ crossAxis: true }),
],
});
subHandler.menu.menuElement.style.minHeight = '550px';
},
}),
],
}),
...(type === 'date'
? [
menu.group({
items: [
menu.dynamic(() => [
menu.subMenu({
name: 'Date by',
openOnHover: false,
middleware: dropdownSubMenuMiddleware,
autoHeight: true,
postfix: html`
<div
style="display:flex;align-items:center;gap:4px;font-size:14px;line-height:20px;color:var(--affine-text-secondary-color);margin-left:30px;"
>
${dateModeLabel(group.groupInfo$.value?.config.name)}
</div>
`,
options: {
items: [
menu.dynamic(() =>
(
[
['Relative', 'date-relative'],
['Day', 'date-day'],
[
'Week',
group.groupInfo$.value?.config.name ===
'date-week-mon'
? 'date-week-mon'
: 'date-week-sun',
],
['Month', 'date-month'],
['Year', 'date-year'],
] as [string, string][]
).map(
([label, key]): MenuConfig =>
menu.action({
name: label,
label: () => {
const isSelected =
group.groupInfo$.value?.config.name ===
key;
return html`<span
style="font-size:14px;color:${isSelected
? 'var(--affine-text-emphasis-color)'
: 'var(--affine-text-secondary-color)'}"
>${label}</span
>`;
},
isSelected:
group.groupInfo$.value?.config.name === key,
select: () => {
group.changeGroupMode(key);
return false;
},
})
)
),
],
},
}),
]),
],
}),
...(group.groupInfo$.value?.config.name?.startsWith('date-week')
? [
menu.group({
items: [
menu.dynamic(() => [
menu.subMenu({
name: 'Start week on',
postfix: html`
<div
style="display:flex;align-items:center;gap:4px;font-size:14px;line-height:20px;color:var(--affine-text-secondary-color);margin-left:8px;"
>
${group.groupInfo$.value?.config.name ===
'date-week-mon'
? 'Monday'
: 'Sunday'}
</div>
`,
options: {
items: [
menu.dynamic(() =>
(
[
['Monday', 'date-week-mon'],
['Sunday', 'date-week-sun'],
] as [string, string][]
).map(([label, key]) =>
menu.action({
name: label,
label: () => {
const isSelected =
group.groupInfo$.value?.config
.name === key;
return html`<span
style="font-size:14px;color:${isSelected
? 'var(--affine-text-emphasis-color)'
: 'var(--affine-text-secondary-color)'}"
>${label}</span
>`;
},
isSelected:
group.groupInfo$.value?.config.name ===
key,
select: () => {
group.changeGroupMode(key);
return false;
},
})
)
),
],
},
}),
]),
],
}),
]
: []),
menu.group({
items: [
menu.dynamic(() => [
menu.subMenu({
name: 'Sort',
openOnHover: false,
middleware: dropdownSubMenuMiddleware,
autoHeight: true,
postfix: html`
<div
style="display:flex;align-items:center;gap:4px;font-size:14px;line-height:20px;color:var(--affine-text-secondary-color);margin-left:8px;"
>
${group.sortAsc$.value
? 'Oldest first'
: 'Newest first'}
</div>
`,
options: {
items: [
menu.dynamic(() => [
menu.action({
name: 'Oldest first',
label: () => {
const isSelected = group.sortAsc$.value;
return html`<span
style="font-size:14px;color:${isSelected
? 'var(--affine-text-emphasis-color)'
: 'var(--affine-text-secondary-color)'}"
>Oldest first</span
>`;
},
isSelected: group.sortAsc$.value,
select: () => {
group.setDateSortOrder(true);
return false;
},
}),
menu.action({
name: 'Newest first',
label: () => {
const isSelected = !group.sortAsc$.value;
return html`<span
style="font-size:14px;color:${isSelected
? 'var(--affine-text-emphasis-color)'
: 'var(--affine-text-secondary-color)'}"
>Newest first</span
>`;
},
isSelected: !group.sortAsc$.value,
select: () => {
group.setDateSortOrder(false);
return false;
},
}),
]),
],
},
}),
]),
],
}),
]
: []),
menu.group({
items: [
menu.dynamic(() => [
menu.action({
name: 'Hide empty groups',
isSelected: group.hideEmpty$.value,
select: () => {
group.setHideEmpty(!group.hideEmpty$.value);
return false;
},
}),
}),
]),
],
}),
menu.group({
items: [
menu =>
html` <data-view-group-setting
@mouseenter="${() => menu.closeSubMenu()}"
.groupTrait="${group}"
.columnId="${groupProperty.id}"
></data-view-group-setting>`,
menu => html`
<data-view-group-setting
@mouseenter=${() => menu.closeSubMenu()}
.groupTrait=${group}
.columnId=${gProp.id}
></data-view-group-setting>
`,
],
}),
menu.group({
items: [
menu.action({
@@ -312,11 +623,14 @@ export const popGroupSetting = (
hide: () => !(view instanceof TableSingleView),
select: () => {
group.changeGroup(undefined);
return false;
},
}),
],
}),
],
},
middleware,
});
menuHandler.menu.menuElement.style.minHeight = '550px';
};

View File

@@ -2,7 +2,12 @@ import {
insertPositionToIndex,
type InsertToPosition,
} from '@blocksuite/affine-shared/utils';
import { computed, type ReadonlySignal } from '@preact/signals-core';
import {
computed,
effect,
type ReadonlySignal,
signal,
} from '@preact/signals-core';
import type { GroupBy, GroupProperty } from '../common/types.js';
import type { TypeInstance } from '../logical/type.js';
@@ -11,8 +16,10 @@ import { computedLock } from '../utils/lock.js';
import type { Property } from '../view-manager/property.js';
import type { Row } from '../view-manager/row.js';
import type { SingleView } from '../view-manager/single-view.js';
import { compareDateKeys } from './compare-date-keys.js';
import { defaultGroupBy } from './default.js';
import { getGroupByService } from './matcher.js';
import { findGroupByConfigByName, getGroupByService } from './matcher.js';
// Test
import type { GroupByConfig } from './types.js';
export type GroupInfo<
@@ -42,138 +49,71 @@ export class Group<
get property() {
return this.groupInfo.property;
}
name$ = computed(() => {
const type = this.property.dataType$.value;
if (!type) {
return '';
}
return this.groupInfo.config.groupName(type, this.value);
return type ? this.groupInfo.config.groupName(type, this.value) : '';
});
private get config() {
return this.groupInfo.config;
}
get tType() {
return this.groupInfo.tType;
}
get view() {
return this.config.view;
}
hide$ = computed(() => {
const groupHide =
this.manager.groupPropertiesMap$.value[this.key]?.hide ?? false;
const emptyHidden = this.manager.hideEmpty$.value && this.rows.length === 0;
return groupHide || emptyHidden;
});
hideSet(hide: boolean) {
this.manager.setGroupHide(this.key, hide);
}
}
function hasGroupProperties(
data: unknown
): data is { groupProperties?: GroupProperty[] } {
if (typeof data !== 'object' || data === null) {
return false;
}
if (!('groupProperties' in data)) {
return false;
}
const value = (data as { groupProperties?: unknown }).groupProperties;
return value === undefined || Array.isArray(value);
}
export class GroupTrait {
groupInfo$ = computed<GroupInfo | undefined>(() => {
const groupBy = this.groupBy$.value;
if (!groupBy) {
return;
}
const property = this.view.propertyGetOrCreate(groupBy.columnId);
if (!property) {
return;
}
const tType = property.dataType$.value;
if (!tType) {
return;
}
const groupByService = getGroupByService(this.view.manager.dataSource);
const result = groupByService?.matcher.match(tType);
if (!result) {
return;
}
return {
config: result,
property,
tType: tType,
};
hideEmpty$ = signal<boolean>(true);
sortAsc$ = signal<boolean>(true);
groupProperties$ = computed(() => {
const data = this.view.data$.value;
return hasGroupProperties(data) ? (data.groupProperties ?? []) : [];
});
staticInfo$ = computed(() => {
const groupInfo = this.groupInfo$.value;
if (!groupInfo) {
return;
}
const staticMap = Object.fromEntries(
groupInfo.config
.defaultKeys(groupInfo.tType)
.map(({ key, value }) => [key, new Group(key, value, groupInfo, this)])
);
return {
staticMap,
groupInfo,
};
});
groupDataMap$ = computed(() => {
const staticInfo = this.staticInfo$.value;
if (!staticInfo) {
return;
}
const { staticMap, groupInfo } = staticInfo;
const groupMap: Record<string, Group> = {};
Object.entries(staticMap).forEach(([key, group]) => {
groupMap[key] = new Group(key, group.value, groupInfo, this);
groupPropertiesMap$ = computed(() => {
const map: Record<string, GroupProperty> = {};
this.groupProperties$.value.forEach(g => {
map[g.key] = g;
});
this.view.rows$.value.forEach(row => {
const value = this.view.cellGetOrCreate(row.rowId, groupInfo.property.id)
.jsonValue$.value;
const keys = groupInfo.config.valuesGroup(value, groupInfo.tType);
keys.forEach(({ key, value }) => {
if (!groupMap[key]) {
groupMap[key] = new Group(key, value, groupInfo, this);
}
groupMap[key].rows.push(row);
});
});
return groupMap;
});
groupsDataList$ = computedLock(
computed(() => {
const groupMap = this.groupDataMap$.value;
if (!groupMap) {
return;
}
const sortedGroup = this.ops.sortGroup(Object.keys(groupMap));
sortedGroup.forEach(key => {
if (!groupMap[key]) return;
groupMap[key].rows = this.ops.sortRow(key, groupMap[key].rows);
});
return sortedGroup
.map(key => groupMap[key])
.filter((v): v is Group => v != null);
}),
this.view.isLocked$
);
updateData = (data: NonNullable<unknown>) => {
const property = this.property$.value;
if (!property) {
return;
}
this.view.propertyGetOrCreate(property.id).dataUpdate(() => data);
};
get addGroup() {
return this.property$.value?.meta$.value?.config.addGroup;
}
property$ = computed(() => {
const groupInfo = this.groupInfo$.value;
if (!groupInfo) {
return;
}
return groupInfo.property;
return map;
});
/**
* Synchronize sortAsc$ with the GroupBy sort descriptor
*/
constructor(
private readonly groupBy$: ReadonlySignal<GroupBy | undefined>,
public view: SingleView,
private readonly ops: {
groupBySet: (groupBy: GroupBy | undefined) => void;
sortGroup: (keys: string[]) => string[];
groupBySet: (g: GroupBy | undefined) => void;
sortGroup: (keys: string[], asc?: boolean) => string[];
sortRow: (groupKey: string, rows: Row[]) => Row[];
changeGroupSort: (keys: string[]) => void;
changeRowSort: (
@@ -181,11 +121,188 @@ export class GroupTrait {
groupKey: string,
keys: string[]
) => void;
changeGroupHide?: (key: string, hide: boolean) => void;
}
) {}
) {
effect(() => {
const desc = this.groupBy$.value?.sort?.desc;
if (desc != null && this.sortAsc$.value === desc) {
this.sortAsc$.value = !desc;
}
});
// Sync hideEmpty state with GroupBy data
effect(() => {
const hide = this.groupBy$.value?.hideEmpty;
if (hide != null && this.hideEmpty$.value !== hide) {
this.hideEmpty$.value = hide;
}
});
}
groupInfo$ = computed<GroupInfo | undefined>(() => {
const groupBy = this.groupBy$.value;
if (!groupBy) return;
const property = this.view.propertyGetOrCreate(groupBy.columnId);
if (!property) return;
const tType = property.dataType$.value;
if (!tType) return;
const svc = getGroupByService(this.view.manager.dataSource);
const res =
groupBy.name != null
? (findGroupByConfigByName(
this.view.manager.dataSource,
groupBy.name
) ?? svc?.matcher.match(tType))
: svc?.matcher.match(tType);
if (!res) return;
return { config: res, property, tType };
});
staticInfo$ = computed(() => {
const info = this.groupInfo$.value;
if (!info) return;
const staticMap = Object.fromEntries(
info.config
.defaultKeys(info.tType)
.map(({ key, value }) => [key, new Group(key, value, info, this)])
);
return { staticMap, groupInfo: info };
});
groupDataMap$ = computed(() => {
const si = this.staticInfo$.value;
if (!si) return;
const { staticMap, groupInfo } = si;
// Create fresh Group instances with empty rows arrays
const map: Record<string, Group> = {};
Object.entries(staticMap).forEach(([key, group]) => {
map[key] = new Group(key, group.value, groupInfo, this);
});
// Assign rows to their respective groups
this.view.rows$.value.forEach(row => {
const cell = this.view.cellGetOrCreate(row.rowId, groupInfo.property.id);
const jv = cell.jsonValue$.value;
const keys = groupInfo.config.valuesGroup(jv, groupInfo.tType);
keys.forEach(({ key, value }) => {
if (!map[key]) map[key] = new Group(key, value, groupInfo, this);
map[key].rows.push(row);
});
});
return map;
});
groupsDataList$ = computedLock(
computed(() => {
const map = this.groupDataMap$.value;
if (!map) return;
const gi = this.groupInfo$.value;
let ordered: string[];
if (gi?.config.matchType.name === 'Date') {
ordered = Object.keys(map).sort(
compareDateKeys(gi.config.name, this.sortAsc$.value)
);
} else {
ordered = this.ops.sortGroup(Object.keys(map), this.sortAsc$.value);
}
return ordered
.map(k => map[k])
.filter(
(g): g is Group =>
!!g &&
!this.isGroupHidden(g.key) &&
(!this.hideEmpty$.value || g.rows.length > 0)
);
}),
this.view.isLocked$
);
/**
* Computed list of groups including hidden ones, used by settings UI.
*/
groupsDataListAll$ = computedLock(
computed(() => {
const map = this.groupDataMap$.value;
const info = this.groupInfo$.value;
if (!map || !info) return;
let orderedKeys: string[];
if (info.config.matchType.name === 'Date') {
orderedKeys = Object.keys(map).sort(
compareDateKeys(info.config.name, this.sortAsc$.value)
);
} else {
orderedKeys = this.ops.sortGroup(Object.keys(map), this.sortAsc$.value);
}
const visible: Group[] = [];
const hidden: Group[] = [];
orderedKeys
.map(key => map[key])
.filter((g): g is Group => g != null)
.forEach(g => {
if (g.hide$.value) {
hidden.push(g);
} else {
visible.push(g);
}
});
return [...visible, ...hidden];
}),
this.view.isLocked$
);
/** Whether all groups are currently hidden */
allHidden$ = computed(() => {
const map = this.groupDataMap$.value;
if (!map) return false;
return Object.keys(map).every(key => this.isGroupHidden(key));
});
/**
* Toggle hiding of empty groups.
*/
setHideEmpty(value: boolean) {
this.hideEmpty$.value = value;
const gb = this.groupBy$.value;
if (gb) {
this.ops.groupBySet({ ...gb, hideEmpty: value });
}
}
isGroupHidden(key: string): boolean {
return this.groupPropertiesMap$.value[key]?.hide ?? false;
}
setGroupHide(key: string, hide: boolean) {
this.ops.changeGroupHide?.(key, hide);
}
/**
* Set sort order for date groupings and update GroupBy sort descriptor.
*/
setDateSortOrder(asc: boolean) {
this.sortAsc$.value = asc;
const gb = this.groupBy$.value;
if (gb) {
this.ops.groupBySet({
...gb,
sort: { desc: !asc },
hideEmpty: gb.hideEmpty,
});
}
}
addToGroup(rowId: string, key: string) {
this.view.lockRows(false);
const groupMap = this.groupDataMap$.value;
const groupInfo = this.groupInfo$.value;
if (!groupMap || !groupInfo) {
@@ -205,18 +322,34 @@ export class GroupTrait {
.cellGetOrCreate(rowId, groupInfo.property.id)
.valueSet(newValue);
}
}
const map = this.groupDataMap$.value;
const info = this.groupInfo$.value;
if (!map || !info) return;
changeCardSort(groupKey: string, cardIds: string[]) {
const groups = this.groupsDataList$.value;
if (!groups) {
return;
}
this.ops.changeRowSort(
groups.map(v => v.key),
groupKey,
cardIds
const addFn = info.config.addToGroup;
if (addFn === false) return;
const group = map[key];
if (!group) return;
const current = group.value;
// Handle both null and non-null values to ensure proper group assignment
const newVal = addFn(
current,
this.view.cellGetOrCreate(rowId, info.property.id).jsonValue$.value
);
this.view.cellGetOrCreate(rowId, info.property.id).valueSet(newVal);
}
changeGroupMode(modeName: string) {
const propId = this.property$.value?.id;
if (!propId) return;
this.ops.groupBySet({
type: 'groupBy',
columnId: propId,
name: modeName,
sort: { desc: !this.sortAsc$.value },
hideEmpty: this.hideEmpty$.value,
});
}
changeGroup(columnId: string | undefined) {
@@ -225,31 +358,38 @@ export class GroupTrait {
return;
}
const column = this.view.propertyGetOrCreate(columnId);
const propertyMeta = this.view.manager.dataSource.propertyMetaGet(
const meta = this.view.manager.dataSource.propertyMetaGet(
column.type$.value
);
if (propertyMeta) {
this.ops.groupBySet(
defaultGroupBy(
this.view.manager.dataSource,
propertyMeta,
column.id,
column.data$.value
)
if (meta) {
const gb = defaultGroupBy(
this.view.manager.dataSource,
meta,
column.id,
column.data$.value
);
if (gb) {
gb.sort = { desc: !this.sortAsc$.value };
gb.hideEmpty = this.hideEmpty$.value;
}
this.ops.groupBySet(gb);
}
}
changeGroupSort(keys: string[]) {
this.ops.changeGroupSort(keys);
property$ = computed(() => this.groupInfo$.value?.property);
get addGroup() {
return this.property$.value?.meta$.value?.config.addGroup;
}
defaultGroupProperty(key: string): GroupProperty {
return {
key,
hide: false,
manuallyCardSort: [],
};
updateData = (data: NonNullable<unknown>) => {
const prop = this.property$.value;
if (!prop) return;
this.view.propertyGetOrCreate(prop.id).dataUpdate(() => data);
};
changeGroupSort(keys: string[]) {
this.ops.changeGroupSort(keys);
}
moveCardTo(
@@ -258,7 +398,6 @@ export class GroupTrait {
toGroupKey: string,
position: InsertToPosition
) {
this.view.lockRows(false);
const groupMap = this.groupDataMap$.value;
if (!groupMap) {
return;
@@ -291,16 +430,16 @@ export class GroupTrait {
.map(row => row.rowId) ?? [];
const index = insertPositionToIndex(position, rows, row => row);
rows.splice(index, 0, rowId);
this.changeCardSort(toGroupKey, rows);
const groupKeys = Object.keys(groupMap);
this.ops.changeRowSort(groupKeys, toGroupKey, rows);
}
moveGroupTo(groupKey: string, position: InsertToPosition) {
this.view.lockRows(false);
const groups = this.groupsDataList$.value;
if (!groups) {
return;
}
const keys = groups.map(v => v.key);
const keys = groups.map(v => v!.key);
keys.splice(
keys.findIndex(key => key === groupKey),
1
@@ -311,7 +450,6 @@ export class GroupTrait {
}
removeFromGroup(rowId: string, key: string) {
this.view.lockRows(false);
const groupMap = this.groupDataMap$.value;
if (!groupMap) {
return;
@@ -330,7 +468,6 @@ export class GroupTrait {
}
updateValue(rows: string[], value: unknown) {
this.view.lockRows(false);
const propertyId = this.property$.value?.id;
if (!propertyId) {
return;

View File

@@ -21,8 +21,7 @@ type FnValueType<
export class FnTypeInstance<
Args extends readonly TypeInstance[] = readonly TypeInstance[],
Return extends TypeInstance = TypeInstance,
> implements TypeInstance
{
> implements TypeInstance {
_validate = fnSchema;
readonly _valueType = undefined as never as FnValueType<Args, Return>;
@@ -55,7 +54,6 @@ export class FnTypeInstance<
unify(ctx: TypeVarContext, template: FnTypeInstance, unify: Unify): boolean {
const newCtx = { ...ctx };
// eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let i = 0; i < template.args.length; i++) {
const arg = template.args[i];
const realArg = this.args[i];
@@ -79,9 +77,9 @@ export class FnTypeInstance<
const fnSchema = Zod.function();
export class ArrayTypeInstance<Element extends TypeInstance = TypeInstance>
implements TypeInstance
{
export class ArrayTypeInstance<
Element extends TypeInstance = TypeInstance,
> implements TypeInstance {
readonly _validate;
readonly _valueType = undefined as never as ValueTypeOf<Element>[];

View File

@@ -14,8 +14,7 @@ export class DTInstance<
Name extends string = string,
Data = unknown,
ValueSchema extends Zod.ZodType = Zod.ZodType,
> implements TypeInstance
{
> implements TypeInstance {
readonly _valueType = undefined as never as Zod.TypeOf<ValueSchema>;
constructor(
@@ -47,8 +46,7 @@ export class DataType<
Name extends string = string,
DataSchema extends Zod.ZodType = Zod.ZodType,
ValueSchema extends Zod.ZodType = Zod.ZodType,
> implements TypeDefinition
{
> implements TypeDefinition {
constructor(
private readonly name: Name,
_dataSchema: DataSchema,

View File

@@ -17,9 +17,9 @@ export class TypeVarDefinitionInstance<
) {}
}
export class TypeVarReferenceInstance<Name extends string = string>
implements TypeInstance
{
export class TypeVarReferenceInstance<
Name extends string = string,
> implements TypeInstance {
readonly _validate = unknownSchema;
readonly _valueType = undefined as unknown;

View File

@@ -8,10 +8,10 @@ import type { Cell } from '../view-manager/cell.js';
import type { CellRenderProps, DataViewCellLifeCycle } from './manager.js';
export abstract class BaseCellRenderer<
RawValue = unknown,
JsonValue = unknown,
Data extends Record<string, unknown> = Record<string, unknown>,
>
RawValue = unknown,
JsonValue = unknown,
Data extends Record<string, unknown> = Record<string, unknown>,
>
extends SignalWatcher(WithDisposable(ShadowlessElement))
implements DataViewCellLifeCycle, CellRenderProps<Data, RawValue, JsonValue>
{

View File

@@ -3,6 +3,7 @@ import {
popMenu,
type PopupTarget,
} from '@blocksuite/affine-components/context-menu';
import type { Middleware } from '@floating-ui/dom';
import { renderUniLit } from '../utils/index.js';
import type { SortUtils } from './utils.js';
@@ -13,9 +14,13 @@ export const popCreateSort = (
sortUtils: SortUtils;
onClose?: () => void;
onBack?: () => void;
},
ops?: {
middleware?: Middleware[];
}
) => {
popMenu(target, {
const subHandler = popMenu(target, {
middleware: ops?.middleware,
options: {
onClose: props.onClose,
title: {
@@ -50,4 +55,5 @@ export const popCreateSort = (
],
},
});
subHandler.menu.menuElement.style.minHeight = '550px';
};

View File

@@ -48,32 +48,41 @@ const compareList = <T>(
return 0;
};
const compareString = (a: unknown, b: unknown): CompareType => {
if (typeof a != 'string' || a === '') {
return Compare.GT;
const strA = String(a ?? '');
const strB = String(b ?? '');
if (strA === '' && strB !== '') {
return Compare.GT; // Empty strings come last
}
if (typeof b != 'string' || b === '') {
return Compare.LT;
if (strA !== '' && strB === '') {
return Compare.LT; // Empty strings come last
}
const listA = a.split('.');
const listB = b.split('.');
if (strA === '' && strB === '') {
return 0; // Both empty, equal
}
const listA = strA.split('.');
const listB = strB.split('.');
return compareList(listA, listB, (a, b) => {
const lowA = a.toLowerCase();
const lowB = b.toLowerCase();
const lowA = String(a).toLowerCase(); // Ensure 'a' and 'b' from split are strings too
const lowB = String(b).toLowerCase();
const numberA = Number.parseInt(lowA);
const numberB = Number.parseInt(lowB);
const aIsNaN = Number.isNaN(numberA);
const bIsNaN = Number.isNaN(numberB);
if (aIsNaN && !bIsNaN) {
return 1;
return 1; // Non-numeric part comes after numeric part
}
if (!aIsNaN && bIsNaN) {
return -1;
return -1; // Numeric part comes before non-numeric part
}
if (!aIsNaN && !bIsNaN && numberA !== numberB) {
return numberA - numberB;
return numberA - numberB; // Numeric comparison for numeric parts
}
return lowA.localeCompare(lowB);
return lowA.localeCompare(lowB); // Lexicographical comparison for string parts
});
};
const compareNumber = (a: unknown, b: unknown) => {

View File

@@ -29,8 +29,7 @@ export class CellBase<
RawValue = unknown,
JsonValue = unknown,
Data extends Record<string, unknown> = Record<string, unknown>,
> implements Cell<RawValue, JsonValue, Data>
{
> implements Cell<RawValue, JsonValue, Data> {
get dataSource() {
return this.view.manager.dataSource;
}

View File

@@ -68,8 +68,7 @@ export abstract class PropertyBase<
RawValue = unknown,
JsonValue = unknown,
Data extends Record<string, unknown> = Record<string, unknown>,
> implements Property<RawValue, JsonValue, Data>
{
> implements Property<RawValue, JsonValue, Data> {
meta$ = computed(() => {
return this.dataSource.propertyMetaGet(this.type$.value);
});

View File

@@ -20,6 +20,7 @@ export type MainProperties = {
};
export interface SingleView {
data$: any;
readonly id: string;
readonly type: string;
readonly manager: ViewManager;
@@ -78,8 +79,7 @@ export interface SingleView {
export abstract class SingleViewBase<
ViewData extends DataViewDataType = DataViewDataType,
> implements SingleView
{
> implements SingleView {
private readonly searchString = signal('');
private readonly traitMap = new Map<symbol, unknown>();

View File

@@ -6,8 +6,8 @@ import type { DataViewUILogicBase } from '../view/data-view-base.js';
import type { DataViewWidgetProps } from './types.js';
export class WidgetBase<
ViewLogic extends DataViewUILogicBase = DataViewUILogicBase,
>
ViewLogic extends DataViewUILogicBase = DataViewUILogicBase,
>
extends SignalWatcher(WithDisposable(ShadowlessElement))
implements DataViewWidgetProps<ViewLogic>
{

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