#### PR Dependency Tree
* **PR #14897** 👈
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 reliability of shape and connector detection by forcing full
DOM renders during waits.
* Fixed race conditions in code-block theme loading and cleanup when
components unmount.
* Refined viewport element discovery to correctly handle
rotated/canvas-layer elements and avoid stale DOM removal.
* **Tests**
* Increased polling timeouts and retries to reduce flakiness.
* Disabled per-file parallelism and ensured test setup performs full
cleanup before starting; extended test timeout.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This PR fixes#14040
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Code blocks can be collapsed and expanded via a toolbar toggle
(visible when the document is editable).
* Collapsed code blocks show a limited preview (~8 lines) with a bottom
fade overlay and reduced padding.
* Toolbar button updates icon and tooltip to reflect collapsed/expanded
state.
* Collapse state is preserved on the block so its current
collapsed/expanded setting is retained.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
### Summary of Changes
Resolves#14715 and #14280.
When a user types into a **Select/Multi-Select** table cell to
create/choose a tag, that character is stashed on the cell container
(setTagDraft) instead of going through valueSetFromString. Opening the
tag picker reads it via consumeTagDraftFromTableCellHost.
### Verification
- Added unit test to check that single-character input doesn't
immediately call valueSetFromString.
https://github.com/user-attachments/assets/432b2693-52f9-4ab4-a694-8440aea007a3
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Tag selection popups now initialize with draft text from keypresses in
tag columns, improving user experience when editing tags.
* **Tests**
* Added comprehensive hotkey tests for single-select and multi-select
tag column behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
### Summary of Changes
Resolves#13340. Change align-items to stretch to full width to avoid
tag/label from overlapping with equation.
### Screenshot Verification
**Before**
<img width="661" height="256" alt="Screenshot 2026-04-19 at 5 58 03 PM"
src="https://github.com/user-attachments/assets/a99d0138-838f-4f91-bd63-cbd07710484c"
/>
**After**
<img width="614" height="275" alt="Screenshot 2026-04-19 at 5 58 16 PM"
src="https://github.com/user-attachments/assets/7e62ab09-f290-4b6e-9cd6-d20b8f990da3"
/>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Style**
* Improved the vertical alignment of LaTeX block content to better
utilize container space.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This PR fixes#14874
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Release Notes
* **Bug Fixes**
* Removed height limitation on Mermaid diagram previews in code blocks,
allowing larger diagrams to render at their full size without being
constrained by a fixed maximum height.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
#### PR Dependency Tree
* **PR #14896** 👈
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 configuration option to manage Google Calendar account linking
access. Administrators can now disable new account connections to
control calendar service integrations. When disabled, the Google
provider is hidden from available options and new linking attempts are
blocked, while existing accounts remain fully functional.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
fix#14894
#### PR Dependency Tree
* **PR #14895** 👈
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**
* Improved database initialization for self-hosted deployments with
automatic creation and repair of embedding tables and indexes, applied
only when related base tables and extensions are present.
* Updated pre-deploy process to run Prisma migrations, perform
embedding-table maintenance, and execute additional data migrations as
part of setup.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Fixes#14519
## Summary
On macOS, the Option key combined with a letter produces locale input
characters (e.g. Polish layout: Option+S → `ś`, Option+L → `ł`). The
AFFiNE command registry registers shortcuts like `Alt+KeyS` (used for
Page ↔ Edgeless mode switch) via `tinykeys`, which matches on
`event.code` (the physical key) — so it fires even when the user was
actually typing a non-ASCII character.
Reported in #14519: Polish users cannot type `ś` inside AFFiNE because
Option+S triggers the mode switch instead.
## Fix
In the command registry handler
([registry.ts](packages/frontend/core/src/commands/registry/registry.ts)),
skip the command when Alt is the only modifier **and** the key produced
a non-ASCII character — the user intends to type the character, not
invoke the shortcut.
Matches the existing handling in blocksuite's `keymap.ts` (added for the
same class of issue in #14059).
## Demo
https://github.com/user-attachments/assets/eb6d2e69-39bf-4236-a886-9e2bde425626
## Verified locally (macOS)
- Switched input source to Polish
- Typed `właśnie` in an AFFiNE doc — all characters including `ś`
(Option+S), `ł` (Option+L) now produce the correct output
- Previously Option+S would toggle edgeless mode
- US layout (Option+S → `ß`) and other locale chars (ą, ń, ę) also now
pass through correctly
- Regular Cmd-based shortcuts (Cmd+K, Cmd+S, etc.) unaffected because
the guard excludes `metaKey`
## Test plan
- [x] On macOS, add Polish input source (System Settings → Keyboard →
Input Sources → +)
- [x] Switch to Polish layout
- [x] In any AFFiNE doc, type Option+S → `ś` appears (not mode switch)
- [x] Confirm other shortcuts (Cmd+K, Cmd+Enter, etc.) still work
- [x] Confirm on US layout that Option+S produces `ß` (OS default)
without firing the mode switch
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Fixed keyboard event handling with Alt key and non-ASCII characters to
prevent unintended command execution.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Fixes#14717
## Summary
When a native `affine:table` block is placed in a note on the edgeless
canvas, dragging the column resize handle (or the column/row drag
handles) causes the canvas to pan instead of triggering the resize/drag,
because the edgeless `DragController` listens at the `pointerdown` level
— earlier than `SelectionController`'s existing `mousedown` handler.
## Fix
Two interception layers added to
`blocksuite/affine/blocks/table/src/selection-controller.ts`, matching
the working pattern in `affine:database`'s `database-header-column.ts`:
1. **DOM-level `pointerdown` `stopPropagation()`** in `dragListener()` —
prevents the edgeless `DragController` from capturing the event before
BlockSuite's event system sees it.
2. **`handleEvent('dragStart', ...)`** in `hostConnected()` — returns
`true` when the target is a resize/drag handle, so the BlockSuite event
dispatcher doesn't route to the edgeless tool controller.
Selectors guarded: `[data-width-adjust-column-id]`,
`[data-drag-column-id]`, `[data-drag-row-id]`.
Mobile and readonly states preserved (matching existing `dragListener()`
guards).
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved drag-and-drop interaction handling for table operations,
including column width adjustment and row/column dragging. Enhanced
event handling to prevent unintended drag actions and ensure proper
behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
### Problem
●In edgeless mode, after clicking and moving in a connector's label
editor, if the label editor has empty content at the end of the editing,
the label editor for that connector can not be triggered again.
●The following video demonstrates this issue:
https://github.com/user-attachments/assets/8d300720-5ed8-4f9c-90fa-fbf059417ff8
### Root Cause
**Direct cause**
●The `labelOffset` property is **stashed** at drag-start, but is **not
properly popped** afterward. As a result, when
`mountConnectorLabelEditor()` is called the second time
(`packages/affine/gfx/connector/src/text/edgeless-connector-label-editor.ts`),
`connector.labelOffset` returns `undefined` instead of the default value
provided by the `@field` decorator.
**Why moving after clicking incorrectly triggers a drag-start**
●The root issue lies in the interaction between click and drag event
handling. Here's the actual flow:
1.`dispatcher.add('click', () => true)` is registered in
`EdgelessConnectorLabelEditor`.
2.On pointer-down, both `ClickController` and `DragController` receive
the event.
3.On pointer-up, `ClickController` fires a **synthetic click**. The
handler from step 1 returns `true`, triggering
`context.get('defaultState').event.stopPropagation()`.
4.This prevents the native pointer-up from bubbling to `DragController`.
However, a subsequent pointer-move still causes `DragController` to
**incorrectly synthesize a drag-start + drag-move**.
**Fundamental root cause**
●The line `context.get('defaultState').event.stopPropagation()` in
`UIEventDispatcher::run()` stops **both** synthetic and native event
bubbling. It should only stop synthetic event propagation.
●The synthetic event bubbling stopping is already properly handled by
the immediate `return` statement on the next line, because the runners
are prepared in strict order (current → parent → grandparent → ... →
global) by `UIEventDispatcher::_getEventScope()` and then **executed
sequentially** in `UIEventDispatcher::run()`.
### Fix
●Since I cannot rule out that other (current or future) event handlers
may rely on this native event bubbling stopping behavior, I chose not to
remove the `context.get('defaultState').event.stopPropagation()` line
completely. Instead, I added a new constant and now skip
`stopPropagation()` **only** for the following synthetic events:
```ts
const syntheticEventNames = new Set(['click', 'doubleClick', 'tripleClick']);
```
These currently represent all known synthetic click events triggered
from pointer-up.
### After
●The video below shows the behavior after this fix.
https://github.com/user-attachments/assets/65b8a3ce-0767-4d80-986b-8bc6081ddd4c
# Closes#14855.
## The bug
When an `affine:embed-synced-doc` is placed on an edgeless canvas and
resized which sets `model.props.scale` to a value ≠ 1 - the
block-selection frame rendered **inside** that embedded editor is drawn
offset from the actual block boundary. The reporter hit this in Safari,
but the root cause is platform-independent.

## Root cause
`affine-embed-edgeless-synced-doc-block` applies `transform:
scale(modelScale)` to its `.affine-embed-synced-doc-container` so the
embedded editor visually fits inside its edgeless xywh
([embed-edgeless-synced-doc-block.ts#L48-L58](https://github.com/toeverything/AFFiNE/blob/canary/blocksuite/affine/blocks/embed-doc/src/embed-synced-doc-block/embed-edgeless-synced-doc-block.ts#L48-L58)).
The inner `Viewport` exposes that outer scale as `viewScale =
boundingClientRect.width / offsetWidth`.
PR #14015 and PR #14074 already taught the surface canvas and
`GfxBlockComponent.getCSSTransform` to compensate by dividing by
`viewScale`. But several selection-related overlays that render inside
the same scaled container were **not** updated in those PRs. They
either:
- read `viewport.toViewCoord(x, y)` - which returns `(x - viewportX) *
zoom * viewScale` and drop the result into CSS `left` / `top` inside the
scaled container, or
- hand-build a `translate(translateX, translateY) scale(zoom)` transform
without `viewScale` compensation.
The outer CSS `scale(viewScale)` then re-applies the scale, leaving the
overlays one factor of `viewScale` away from their blocks. That's
exactly the misalignment in the screenshot - the rect's size looks right
but its position is offset.
## The fix
Mirror the pattern shipped in #14074 everywhere the inner overlays are
placed:
- position: `(model - viewportX) * zoom / viewScale`
- transform scale: `zoom / viewScale`
- translate: `translateX / viewScale, translateY / viewScale`
This keeps the overlays in the same reference frame as
`GfxBlockComponent.getCSSTransform` so they line up with the block
they're framing. When `viewScale === 1` (normal edgeless canvas, outside
any embed) every `/ viewScale` is a no-op and behaviour is unchanged.
## Why this is safe
- When `viewScale === 1` - every existing caller outside
`embed-edgeless-synced-doc` - the math reduces to the original
expression byte-for-byte.
- The fix strictly mirrors the invariant already adopted by
`GfxBlockComponent.getCSSTransform` in #14074. It's the same division by
`viewScale` applied in the same place.
- No public API, type, or DOM structure changed.
## Scope / known limitations
- The `Viewport._cachedBoundingClientRect` cache is only invalidated by
its own `ResizeObserver`
([viewport.ts#L500-L505](https://github.com/toeverything/AFFiNE/blob/canary/blocksuite/framework/std/src/gfx/viewport.ts#L500-L505)).
A CSS-transform change on an ancestor (e.g. the user panning/zooming the
outer edgeless canvas) does not fire it, so in theory `viewScale` can go
stale between outer-viewport updates. In practice this hasn't come up in
repro - the inner viewport's shell is observed and fires whenever layout
shifts. If it turns out to matter I'm happy to add a
`viewport.onResize()` refresh hook off the existing
`GfxViewportInitializer` in a follow-up.
- No integration test added - the existing
`blocksuite/integration-test/edgeless/` suite has no `embed-synced-doc`
harness. Adding one is a larger scope; can follow up if requested.
## Test plan
- [x] `yarn typecheck` - passes
- [x] `yarn lint:ox` - `0 warnings, 0 errors`
- [x] `yarn prettier --write` on the 5 touched files - no changes
- [ ] Manual: on canary, create an edgeless canvas, drop an
embed-synced-doc, resize with `Shift` held so `model.props.scale` ≠ 1,
select any block inside, and verify the blue selection frame sits flush
with the block's boundary (confirm on Safari, Chrome, Firefox).
- [ ] Regression check: on a normal edgeless canvas (no embed), verify
element selection, drag handle, and text/shape inline editors still
render correctly (these code paths hit `viewScale === 1` and should be
unchanged).
## Related PRs
- #14015 - fixed surface canvas at non-1 `viewScale`.
- #14074 - fixed `GfxBlockComponent.getCSSTransform` at non-1
`viewScale`. This PR completes that series by covering the selection
overlays.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Fixed positioning and scaling of inline text editors, selection
rectangles, drag handles, and remote cursors so overlays and editors
remain correctly aligned and sized when the viewport uses an additional
outer scale/transform during zooming and panning.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
This PR fixes `cloudflare-r2` storage configuration so jurisdictional R2
endpoints (for example EU buckets) work correctly.
Closes#14847
## Problem
`cloudflare-r2` currently ignores `config.endpoint` and always uses:
`https://<accountId>.r2.cloudflarestorage.com`
That breaks uploads for jurisdictional buckets that require endpoints
like:
`https://<accountId>.eu.r2.cloudflarestorage.com`
## Changes
- Updated `R2StorageProvider` endpoint resolution:
- use `config.endpoint` when provided
- otherwise fall back to `https://${accountId}.r2.cloudflarestorage.com`
- Kept `forcePathStyle: true` behavior unchanged
- Updated validation to require `accountId` **or** `endpoint`
- Improved storage schema descriptions to mention jurisdiction endpoints
- Added focused unit tests for:
- default account endpoint behavior
- custom jurisdiction endpoint behavior
## Backward Compatibility
- Existing R2 configs that only provide `accountId` continue to work
exactly as before.
- New behavior only applies when a custom `config.endpoint` is
explicitly set.
## Tests
- Added: `packages/backend/server/src/base/storage/__tests__/r2.spec.ts`
- Verifies both default and custom endpoint selection paths.
_Disclaimer: parts of this PR were implemented with AI assistance._
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Cloudflare R2 config adds an optional "jurisdiction" (EU) option and
consistent endpoint derivation for S3-compatible providers.
* **Documentation**
* Storage configuration schemas clarified: S3 endpoint is
optional/derived from region; R2 endpoint removed from schema and
jurisdiction documented.
* **Tests**
* Added tests validating R2 endpoint selection for default,
EU-jurisdiction, undefined-jurisdiction, and missing-account scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
## Summary
- add an Export Y.Doc debug menu item
- encode the active store spaceDoc with Y.encodeStateAsUpdate
- download the update as a binary ydoc-update file
## Test
- yarn workspace @blocksuite/playground build
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added Y.Doc export functionality to the debug menu, enabling users to
download the current space document state as a binary update file
through the "Test Operations" → "Export" menu.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This PR contains the following updates:
| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [uuid](https://redirect.github.com/uuidjs/uuid) | [`^13.0.0` →
`^14.0.0`](https://renovatebot.com/diffs/npm/uuid/13.0.0/14.0.0) |

|

|
---
### uuid: Missing buffer bounds check in v3/v5/v6 when buf is provided
[GHSA-w5hq-g745-h8pq](https://redirect.github.com/advisories/GHSA-w5hq-g745-h8pq)
<details>
<summary>More information</summary>
#### Details
##### Summary
`v3`, `v5`, and `v6` accept external output buffers but do not reject
out-of-range writes (small `buf` or large `offset`).
By contrast, `v4`, `v1`, and `v7` explicitly throw `RangeError` on
invalid bounds.
This inconsistency allows **silent partial writes** into caller-provided
buffers.
##### Affected code
- `src/v35.ts` (`v3`/`v5` path) writes `buf[offset + i]` without bounds
validation.
- `src/v6.ts` writes `buf[offset + i]` without bounds validation.
##### Reproducible PoC
```bash
cd /home/StrawHat/uuid
npm ci
npm run build
node --input-type=module -e "
import {v4,v5,v6} from './dist-node/index.js';
const ns='6ba7b810-9dad-11d1-80b4-00c04fd430c8';
for (const [name,fn] of [
['v4',()=>v4({},new Uint8Array(8),4)],
['v5',()=>v5('x',ns,new Uint8Array(8),4)],
['v6',()=>v6({},new Uint8Array(8),4)],
]) {
try { fn(); console.log(name,'NO_THROW'); }
catch(e){ console.log(name,'THREW',e.name); }
}"
```
Observed:
- `v4 THREW RangeError`
- `v5 NO_THROW`
- `v6 NO_THROW`
Example partial overwrite evidence captured during audit:
```text
same true buf [
170, 170, 170, 170,
75, 224, 100, 63
]
v6 [
187, 187, 187, 187,
31, 19, 185, 64
]
```
##### Security impact
- **Primary**: integrity/robustness issue (silent partial output).
- If an application assumes full UUID writes into preallocated buffers,
this can produce malformed/truncated/partially stale identifiers without
error.
- In systems where caller-controlled offsets/buffer sizes are exposed
indirectly, this may become a security-relevant logic flaw.
##### Suggested fix
Add the same guard used by `v4`/`v1`/`v7`:
```ts
if (offset < 0 || offset + 16 > buf.length) {
throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
}
```
Apply to:
- `src/v35.ts` (covers `v3` and `v5`)
- `src/v6.ts`
#### Severity
- CVSS Score: 6.3 / 10 (Medium)
- Vector String:
`CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N`
#### References
-
[https://github.com/uuidjs/uuid/security/advisories/GHSA-w5hq-g745-h8pq](https://redirect.github.com/uuidjs/uuid/security/advisories/GHSA-w5hq-g745-h8pq)
-
[3d2c5b0342)
-
[https://github.com/uuidjs/uuid/releases/tag/v14.0.0](https://redirect.github.com/uuidjs/uuid/releases/tag/v14.0.0)
-
[https://github.com/advisories/GHSA-w5hq-g745-h8pq](https://redirect.github.com/advisories/GHSA-w5hq-g745-h8pq)
This data is provided by the [GitHub Advisory
Database](https://redirect.github.com/advisories/GHSA-w5hq-g745-h8pq)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>
---
### Release Notes
<details>
<summary>uuidjs/uuid (uuid)</summary>
###
[`v14.0.0`](https://redirect.github.com/uuidjs/uuid/blob/HEAD/CHANGELOG.md#1400-2026-04-19)
[Compare
Source](https://redirect.github.com/uuidjs/uuid/compare/v13.0.0...v14.0.0)
##### Security
- Fixes
[GHSA-w5hq-g745-h8pq](https://redirect.github.com/uuidjs/uuid/security/advisories/GHSA-w5hq-g745-h8pq):
`v3()`, `v5()`, and `v6()` did not validate that writes would remain
within the bounds of a caller-supplied buffer, allowing out-of-bounds
writes when an invalid `offset` was provided. A `RangeError` is now
thrown if `offset < 0` or `offset + 16 > buf.length`.
##### ⚠ BREAKING CHANGES
- `crypto` is now expected to be globally defined (requires
node\@​20+)
([#​935](https://redirect.github.com/uuidjs/uuid/issues/935))
- drop node\@​18 support
([#​934](https://redirect.github.com/uuidjs/uuid/issues/934))
- upgrade minimum supported TypeScript version to 5.4.3, in keeping with
the project's policy of supporting TypeScript versions released within
the last two years
</details>
---
### Configuration
📅 **Schedule**: (UTC)
- Branch creation
- ""
- 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 these
updates 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:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzkuNCIsInVwZGF0ZWRJblZlciI6IjQzLjEzOS40IiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5IiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyJdfQ==-->
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Fixes#14722
## Summary
`popMenu()` in
`blocksuite/affine/components/src/context-menu/menu-renderer.ts` uses
`autoPlacement` + `offset` in its default middleware, but no `shift()` —
so when `autoPlacement` picks a placement that would overflow the
viewport (e.g. database column menu opening near the top of a short
viewport), the menu stays overflowing and top items get clipped above
the viewport.
## Fix
Add `shift({ padding: 8 })` to the default middleware chain.
This matches the behavior of the sibling helper `createPopup()` in the
same file, which already includes `shift()` in its defaults.
## Reproducing (as reported in #14722)
Viewport ~879×461 (Chrome, macOS). Create a database block near the top
of the viewport → click a column name → menu opens upward and the top
items ("Rename", "Filter") get clipped above the viewport.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Enhanced context menu positioning on desktop to provide better
alignment and spacing adjustments near screen boundaries.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Split generated-file validation in the `typecheck` workflow so i18n
outputs and BS docs outputs are checked separately.
This fixes a misleading CI failure message: previously, CI could fail
due to i18n-generated changes like
`packages/frontend/i18n/src/i18n.gen.ts`, but only suggested running
`yarn typecheck && yarn affine bs-docs build`, which does not regenerate
those files.
## Changes
- validate i18n-generated changes immediately after `yarn affine
@affine/i18n build`
- keep ignoring `packages/frontend/i18n/src/i18n-completenesses.json` in
CI as before
- leave `yarn typecheck` as a separate step
- make the BS docs step only check for changes introduced by `yarn
affine bs-docs build`
## Result
CI now gives the correct remediation command depending on which
generated files are out of date:
- i18n issues: `yarn affine @affine/i18n build`
- docs/typecheck issues: `yarn typecheck && yarn affine bs-docs build`
## Testing
- verified the updated workflow YAML parses successfully
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Chores**
* Enhanced build validation for internationalization code generation to
ensure generated changes are properly committed before proceeding. The
validation now occurs immediately after code generation runs, providing
faster feedback during the build process.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
- Allow users to select text and copy it as Markdown via the context
menu
- Add "Copy as Markdown" under Export menu to copy entire document to
clipboard
Fixes#12983
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added "Copy as Markdown" to the toolbar clipboard More menu for
selected content.
* Added "Copy as Markdown" to the page export menu to copy entire pages
as Markdown.
* **Behavior**
* Export flow now returns success/failure so the UI shows a dedicated
success or error notification for clipboard exports.
* **Localization**
* Added strings for "Copy as Markdown" and "Copied as Markdown".
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Whitewater <me@waterwater.moe>
Co-authored-by: lawvs <18554747+lawvs@users.noreply.github.com>
This PR contains the following updates:
| Package | Update | Change |
|---|---|---|
| [node](https://nodejs.org)
([source](https://redirect.github.com/nodejs/node)) | patch | `22.22.1`
→ `22.22.2` |
---
### Release Notes
<details>
<summary>nodejs/node (node)</summary>
###
[`v22.22.2`](https://redirect.github.com/nodejs/node/compare/v22.22.1...v22.22.2)
[Compare
Source](https://redirect.github.com/nodejs/node/compare/v22.22.1...v22.22.2)
</details>
---
### Configuration
📅 **Schedule**: (UTC)
- 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:eyJjcmVhdGVkSW5WZXIiOiI0My4xMjMuOCIsInVwZGF0ZWRJblZlciI6IjQzLjEyMy44IiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5IiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyJdfQ==-->
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This PR contains the following updates:
| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [dompurify](https://redirect.github.com/cure53/DOMPurify) | [`3.3.3` →
`3.4.0`](https://renovatebot.com/diffs/npm/dompurify/3.3.3/3.4.0) |

|

|
### GitHub Vulnerability Alerts
####
[GHSA-39q2-94rc-95cp](https://redirect.github.com/cure53/DOMPurify/security/advisories/GHSA-39q2-94rc-95cp)
## Summary
In `src/purify.ts:1117-1123`, `ADD_TAGS` as a function (via
`EXTRA_ELEMENT_HANDLING.tagCheck`) bypasses `FORBID_TAGS` due to
short-circuit evaluation.
The condition:
```
!(tagCheck(tagName)) && (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName])
```
When `tagCheck(tagName)` returns `true`, the entire condition is `false`
and the element is kept — `FORBID_TAGS[tagName]` is never evaluated.
## Inconsistency
This contradicts the attribute-side pattern at line 1214 where
`FORBID_ATTR` explicitly wins first:
```
if (FORBID_ATTR[lcName]) { continue; }
```
For tags, FORBID should also take precedence over ADD.
## Impact
Applications using both `ADD_TAGS` as a function and `FORBID_TAGS`
simultaneously get unexpected behavior — forbidden tags are allowed
through. Config-dependent but a genuine logic inconsistency.
## Suggested Fix
Check `FORBID_TAGS` before `tagCheck`:
```
if (FORBID_TAGS[tagName]) { /* remove */ }
else if (tagCheck(tagName) || ALLOWED_TAGS[tagName]) { /* keep */ }
```
## Affected Version
v3.3.3 (commit 883ac15)
##### Severity
- CVSS Score: 5.3 / 10 (Medium)
- Vector String:
`CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N`
---
### Release Notes
<details>
<summary>cure53/DOMPurify (dompurify)</summary>
###
[`v3.4.0`](https://redirect.github.com/cure53/DOMPurify/releases/tag/3.4.0):
DOMPurify 3.4.0
[Compare
Source](https://redirect.github.com/cure53/DOMPurify/compare/3.3.3...3.4.0)
**Most relevant changes:**
- Fixed a problem with `FORBID_TAGS` not winning over `ADD_TAGS`, thanks
[@​kodareef5](https://redirect.github.com/kodareef5)
- Fixed several minor problems and typos regarding MathML attributes,
thanks [@​DavidOliver](https://redirect.github.com/DavidOliver)
- Fixed `ADD_ATTR`/`ADD_TAGS` function leaking into subsequent
array-based calls, thanks
[@​1Jesper1](https://redirect.github.com/1Jesper1)
- Fixed a missing `SAFE_FOR_TEMPLATES` scrub in `RETURN_DOM` path,
thanks [@​bencalif](https://redirect.github.com/bencalif)
- Fixed a prototype pollution via `CUSTOM_ELEMENT_HANDLING`, thanks
[@​trace37labs](https://redirect.github.com/trace37labs)
- Fixed an issue with `ADD_TAGS` function form bypassing `FORBID_TAGS`,
thanks [@​eddieran](https://redirect.github.com/eddieran)
- Fixed an issue with `ADD_ATTR` predicates skipping URI validation,
thanks [@​christos-eth](https://redirect.github.com/christos-eth)
- Fixed an issue with `USE_PROFILES` prototype pollution, thanks
[@​christos-eth](https://redirect.github.com/christos-eth)
- Fixed an issue leading to possible mXSS via Re-Contextualization,
thanks
[@​researchatfluidattacks](https://redirect.github.com/researchatfluidattacks)
and others
- Fixed a problem with the type dentition patcher after Node version
bump
- Fixed freezing BS runs by reducing the tested browsers array
- Bumped several dependencies where possible
- Added needed files for OpenSSF scorecard checks
**Published Advisories are here:**
<https://github.com/cure53/DOMPurify/security/advisories?state=published>
</details>
---
### Configuration
📅 **Schedule**: (UTC)
- Branch creation
- ""
- 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:eyJjcmVhdGVkSW5WZXIiOiI0My4xMjAuMiIsInVwZGF0ZWRJblZlciI6IjQzLjEyMC4yIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5IiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyJdfQ==-->
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Fixes#14673
## Summary
When an edgeless text block is locked, `pointer-events: none` on the
inner content div (`edgeless-text-block.ts:308`) blocks all mouse
interaction — including clicking hyperlinks. Locking is intended to
prevent accidental edits, not to block navigation, so links should
remain clickable.
## Fix
Apply a `locked-content` class on the inner div when the block is locked
and not being edited, and add a targeted CSS rule restoring
`pointer-events: auto` on anchor elements within locked content.
## Context
Re-implements the fix from PR #14692 (authored by @moktamd, reverted per
@darkskygit's comment on #14673 because the original contributor had not
signed the CLA). The CLA is signed for this PR.
## Test plan
- [ ] On edgeless canvas, create a text block with a hyperlink (e.g.
`[link](https://affine.pro)`)
- [ ] Lock the block via the shape toolbar
- [ ] Hover the link → cursor shows pointer
- [ ] Click the link → navigation occurs
- [ ] Unlock and confirm editing still works as before
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Links within locked text blocks are now interactive and clickable with
proper visual cursor feedback.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
fix#14735
This PR fixes a remaining desktop case related to #14467.
The previous fix resolved incorrect translation in navigation panels,
but the detail page header tab title was still passing custom document
titles through `i18n.t()`, causing user-defined titles to be
unexpectedly translated.
### Results
https://github.com/user-attachments/assets/4abad3b9-d5d7-442f-b643-6d9ea63fa741
After:
<img width="2100" height="1722" alt="After"
src="https://github.com/user-attachments/assets/0770eae2-e5c5-4816-8d53-e40a4b52800c"
/>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Updated page title retrieval mechanism in workspace detail page
headers. The title is now sourced directly from the document display
metadata service instead of using the previous derivation method.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Chores**
* Updated developer tooling dependencies used for local testing to newer
patch versions for improved stability.
* Bumped backend framework and related packages to newer patch releases
to address fixes and maintain compatibility.
* No functional or public API changes; updates are non-breaking
dependency version bumps.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Porting over iOS fix for self-hosted SSO to Android from #11563.
Fixes#12819
Tested on own instance using Authentik.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Android authentication now supports an optional server parameter in
the callback URL, enabling sign-in against different server instances.
* If the specified server cannot be found, the authentication attempt is
halted and an error is reported.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Summary
Update the edgeless shape text editor to resize mind map node text
bounds while IME composition is in progress.
## Changes
- listen to `compositionupdate` on the inline editor container
- trigger `_updateElementWH()` on `compositionupdate` and
`compositionend`
- keep text box dimensions in sync before composition is committed
## Testing
- Not run locally: `pnpm` is not available in this environment, so
package build/tests could not be executed here.
Fixes#11515
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Editor mounting tolerates missing/null elements and validates input to
avoid errors.
* Text creation/update consistently targets the refreshed element to
prevent mismatches.
* Inline editor listens for IME composition events and schedules
layout/size recalculation (with proper cleanup) so sizing stays in sync.
* **Tests**
* Added an integration test verifying layout/size updates during IME
composition events.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
Co-authored-by: DarkSky <darksky2048@gmail.com>
Bug Resolved
#14795
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Made row and column sorting deterministic when items share the same
order value, reducing unexpected cell shifts.
* Adjusted comparator behavior to preserve tied-order grouping, which
may change displayed column/row sequence in edge cases.
* Improved consistency of table rendering and cell placement across
refreshes and edits.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Fixes#13555https://github.com/user-attachments/assets/12e55c21-080c-4c69-9780-893ccad25b45
## Summary
- make the code block More popup reactive to `wrap` and `lineNumber`
prop updates
- read the latest toggle state at click time so same-menu double toggles
do not reuse stale closures
- add e2e coverage for wrap and line number toggling twice without
closing the More menu
## Bug Reason
- the code block More popup was rendered as a static portal, so it
stayed open without re-rendering after the first toggle
- the `Cancel line number` and `Wrap` menu actions captured render-time
state in their click handlers
- after the first click updated the model, a second click in the same
open menu reused stale state and wrote the same value again, so nothing
changed visually
## Testing
- yarn workspace @affine-test/blocksuite test e2e/code/crud.spec.ts
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Menu toggles now read and update the current wrap and line-number
states reliably.
* **Refactor**
* Replaced inline popup rendering with a dedicated more-menu component
for the code toolbar.
* **Style**
* Prevented text selection on menu action elements for smoother
interaction.
* **Tests**
* Added e2e tests for wrap and line-number toggle flows.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Fixes#14531, where mannual vertical order is broken [Root
Cause](https://github.com/toeverything/AFFiNE/issues/14531#issuecomment-4052422436)
- Restored manual row/card sorting when building grouped kanban data.
- Reapplied `sortRow(...)` to each group before rendering `group.rows`.
- Fixed group/board arrange to reorder from the full group list,
including hidden or empty groups.
- Preserved consistent ordering between the settings panel and persisted
`groupProperties`.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Consolidated internal grouping and sorting logic to ensure consistent
ordering across grouped views; visible behavior unchanged.
* Moving groups or cards now uses a single, consistent ordering approach
to avoid intermittent ordering differences.
* **Tests**
* Added tests to verify manual per-group card order is applied and
preserved when moving cards between groups.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DarkSky <darksky2048@gmail.com>
### PR Description
* clicking a linked calendar account now switches settings to Workspace
Integrations and opens the Calendar settings directly
* calendar OAuth returns now land on Workspace Integrations with the
Calendar settings opened instead of the homepage
* Improves UX by reducing friction when managing calendar integrations
https://www.loom.com/share/49fa5c448ce049659877beb42d7bd81a
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Calendar integration settings can now be opened automatically
(including from OAuth redirects) and workspace settings support a
scroll-to-anchor.
* Integration account rows are now clickable for quick access to
settings.
* **Improvements**
* Enhanced visual feedback with interactive hover and focus states for
integration controls.
* **Tests**
* Added tests covering the OAuth redirect behavior and workspace
settings scroll/open handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DarkSky <darksky2048@gmail.com>
#### PR Dependency Tree
* **PR #14792** 👈
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**
* Admin dashboard returns more accurate sync and storage timelines with
carry‑forwarded minute buckets and corrected current totals.
* **Bug Fixes**
* Active-user flushes are debounced/scheduled to prevent overlapping
writes and reduce stale counts.
* Snapshot writes now retry and will skip gracefully when lock
contention prevents completion, avoiding partial snapshots.
* **Tests**
* New e2e tests cover carry‑forward behavior, no backfill outside
requested windows, and storage history accuracy.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
### Summary
This fixes a few inconsistencies in shared page behavior:
fixes https://github.com/toeverything/AFFiNE/issues/14751
- shared pages now open in the correct published mode when the URL does
not already include ?mode=...
- switching between page and edgeless in shared mode now keeps the URL
query param in sync
- the default Copy Link action now follows the current editor mode
- shared viewers can toggle between page and edgeless mode in readonly
share pages
---
### What Changed
- updated shared page mode resolution to prefer URL mode, with backend
publish mode as fallback
- added query-param syncing for shared page mode changes
- made the default share link copy use:
- page link in page mode
- edgeless link in edgeless mode
- allowed EditorModeSwitch to toggle both ways in shared mode
- extracted shared-mode behavior into small hooks to keep share-page.tsx
cleaner
---
### Demo
https://www.loom.com/share/a287172321fb4fc5b94f7c67a39298a9
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Mode switching between page and edgeless no longer blocked by shared
gating; shared pages initialize and respect the resolved editor mode.
* Shared page URLs stay in sync with editor mode and copy-link actions
include/preserve the selected mode.
* **Tests**
* Added tests for publish-mode resolution, query-string mode handling,
and default share-mode behavior.
* **Bug Fixes**
* Updated shared-page “not found” UI text to match new messaging.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
Fixes#13399
### Issue
When viewing an audio attachment card in horizontal view, the waveform
was being clipped and not fully visible. In vertical view it displayed
correctly.
### Fix
- `audio-waveform` and `progressContainer` flex children were missing
`minWidth: 0` and `flex: 1`, causing container overflow
- `.affine-attachment-container` had a fixed height with `overflow:
hidden`
that cut off the waveform row
### Screenshot Verification
**Before**
<img width="1661" height="935" alt="image"
src="https://github.com/user-attachments/assets/b2f0908b-94fe-4869-bdfb-cc6a757e703d"
/>
**After**
<img width="750" height="182" alt="image"
src="https://github.com/user-attachments/assets/63caac69-f37b-4894-80de-806b691581c8"
/>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
**New Features**
- Introduced audio embed card functionality allowing users to embed and
display audio content directly in documents with standardized dimensions
and improved responsive layout styling for better visual presentation
and integration.
**Improvements**
- Enhanced styling and layout handling for audio player components to
ensure proper display and optimal rendering in various container sizes
and space constraints.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This PR fixes#14755 bug
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Fixed keyboard shortcut handling to properly distinguish between
locale input characters and actual shortcuts when using Alt+Meta key
combinations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->