## Summary
Closes#13954
The "Turn into" menu was missing the Callout option. Users had no way to
convert an existing paragraph/list/code block into a Callout directly
from the slash menu or context toolbar.
## Root Cause
`textConversionConfigs` in `rich-text/src/conversion.ts` had no entry
for `affine:callout`, so the option never appeared in the menu.
Additionally, Callout is a **hub block** — its text lives in a child
paragraph, not in its own `text` prop. This means the generic
`transformModel` path would silently fail, requiring a dedicated
conversion handler.
## Changes
- `blocksuite/affine/rich-text/src/conversion.ts` — add callout entry to
`textConversionConfigs`
- `blocksuite/affine/blocks/note/src/commands/block-type.ts` — add
`transformToCallout` command that:
1. Creates a new `affine:callout` block at the original position
2. Moves the original text into a child `affine:paragraph` inside the
callout
3. Deletes the original block
## Before / After
| Before | After |
|--------|-------|
| "Turn into" menu had no Callout option | Callout appears in "Turn
into" menu |
| Selecting any block → Turn into showed: Heading, Text, Quote, Divider…
| Now also shows: **Callout** |
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Convert selected paragraphs, lists, and code blocks into callouts
while preserving text and nested content.
* Added callout recognition and icon display in rich-text conversion.
* Conversion results now distinguish between selected blocks and text.
* **Bug Fixes**
* Prevented invalid, nested, or incompatible callout conversions.
* The conversion menu now hides Callout when unavailable.
* Preserved original blocks when conversion fails.
* Improved undo and redo behavior for callout conversions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DarkSky <darksky2048@gmail.com>
fix#15599
#### PR Dependency Tree
* **PR #15623** 👈
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**
* Corrected the Control+Y redo shortcut so it only activates on Windows
and Linux, preventing unintended behavior on other platforms.
* **Tests**
* Updated cross-platform keyboard tests to explicitly verify undo and
redo behavior in a Linux browser environment.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
I was reading the journal date suggestions and noticed that
`suggestJournalDate` bounds the parsed day with a hardcoded 31 before
building the date string:
```ts
let day = numbers ? parseInt(numbers) : dayjs().date();
const invalidDay = day < 1 || day > 31;
...
dayjs(`${year}-${month}-${day}`)
```
31 is the length of the longest month, so the guard lets through every
day that is valid in December but not in the month the user actually
typed. dayjs does not reject the overflow — it rolls it into the
following month. On `canary` that gives:
- `feb 30` → `2026-03-02`
- `apr 31` → `2026-05-01`
- on the 31st of a month, a plain `feb` (the case that is meant to keep
today's day) → `2026-03-03`
So a query naming February can land in March, and the suggestion quietly
points at a month the user did not ask for. The existing `dec 33` test
covers the intent — an out-of-range day falls back to today's day — but
December has 31 days, so it never reaches this path.
This bounds the day by the length of the matched month instead, and
clamps the fallback the same way, so asking for `feb` on the 31st of a
month stays inside February (`2026-02-28`) rather than jumping to
`2026-03-03`. Nothing outside `suggestJournalDate` changes.
The two tests added to `suggest-date.spec.ts` fail on the current code:
```
× a day past the end of the month falls back inside that month
expected { dateString: '2026-03-02' } to deeply equal { dateString: '2026-02-16' }
× today's day is clamped to the end of a shorter month
expected { dateString: '2026-03-03' } to deeply equal { dateString: '2026-02-28' }
Tests 2 failed | 16 passed (18)
```
and pass with the fix, along with the rest of the journal module (`23
passed`). `oxlint --deny-warnings` and `oxfmt --check` are clean on both
files. I left the `yarn lint` / `yarn typecheck` box unchecked: `yarn
typecheck` reports 684 errors in my environment, all in
`packages/backend/server` (the Prisma client isn't generated here) and
`packages/frontend/templates`, none in `packages/frontend/core` or in
the two files touched.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Improved journal date suggestions for months with fewer than 31 days.
- Invalid dates, such as February 30 or 31, now resolve to valid dates
within the selected month.
- Suggested dates are capped at the final day of the selected month when
today’s day number exceeds that month’s length.
- February suggestions now correctly account for both leap years and
non-leap years.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DarkSky <darksky2048@gmail.com>
Fixes#15588.
## Root cause
`preprocessLatex` escapes any `$` that is followed by a digit, assuming
it is a price:
```ts
// Escape dollar signs that are likely currency indicators
preprocessedContent = preprocessedContent.replace(/\$(?=\d)/g, '\\$');
```
The step above it shields existing LaTeX from that escape — but only
`$$…$$`, `\[…\]` and `\(…\)`. **Single-dollar inline math was never
protected.**
So for `$4\vee 6=12$`:
1. the protection pass does not match it,
2. the currency pass sees `$4` and rewrites it to `\$4`,
3. the expression never parses as math, and the closing `$` is left
orphaned, which throws off the parse of everything after it.
That last part is why a single such expression breaks the rest of the
document, as the report describes. It also explains the reporter's `{ }`
workaround: `${4}` is `$` followed by `{`, not a digit, so the escape
never fires.
## The fix
Protect single-dollar inline math too, using the usual rule for telling
math from prices:
- the opening `$` is not followed by whitespace,
- the closing `$` is not preceded by whitespace,
- the closing `$` is not followed by a digit.
```ts
/(\$\$[\s\S]*?\$\$|\\\[[\s\S]*?\\\]|\\\(.*?\\\)|\$(?!\s)[^\n$]*?(?<!\s)\$(?!\d))/g
```
Currency is unaffected, which is the part worth checking:
- `costs $5 and $10 today` — the only closing candidate is the `$`
before `10`, and it is preceded by a space, so no match; both escape as
before.
- `$100$200` — the closing candidate is followed by `2`, so no match;
both escape as before.
- `it costs $5.00 total` — no closing `$` at all.
`$$…$$` stays first in the alternation, so display math is still matched
as display math.
## Verification
New
`blocksuite/affine/all/src/__tests__/adapters/latex-preprocessor.unit.spec.ts`,
11 cases: the digit-first expressions from the issue, the currency cases
above, and guards for display math, a `$` followed by whitespace,
`\(…\)` rewriting, and code spans.
Reverting **only** the regex (keeping the export so the module still
loads) fails exactly the four digit-first tests and leaves the other
seven green:
```
× leaves binary operators untouched
× leaves relation untouched
× leaves single digit untouched
× keeps every expression in a sentence mixing math and prose
Tests 4 failed | 7 passed (11)
```
That the currency and existing-behaviour cases pass **without** the fix
is the point — they are regression guards, not props for it.
With the fix:
```
Test Files 14 passed (14)
Tests 234 passed (234)
```
(the whole `blocksuite/affine/all` suite, which includes the 193
existing adapter tests).
## Notes
- `preprocessLatex` is now exported so the delimiter rule can be tested
directly. It was previously module-private with only the extension
exported; `adapters/markdown/index.ts` already does `export * from
'./preprocessor.js'`, so nothing else changed.
- I kept the change to the one regex and did not reformat the rest of
the file — there is no Prettier config in the repo, and running Prettier
with its defaults rewrites quotes and trailing commas across the whole
file.
- The lookbehind `(?<!\s)` is ES2018; the file already targets modern
runtimes.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Improved Markdown LaTeX preprocessing for inline expressions preceded
by even-length backslash runs.
- Continued to distinguish escaped dollar signs from valid math
delimiters, while preserving currency values, display math, `\( \)`
expressions, whitespace, mixed content, and code spans.
- **Tests**
- Added comprehensive coverage for inline and display math, delimiter
escaping, currency values, whitespace, conversions, mixed content,
digit-leading expressions, and code-span protection.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Split out of #15596 on review feedback — this is an independent,
pre-existing bug in the currency pass, so it does not belong in that PR.
The currency pass escapes any `$` followed by a digit without looking at
what precedes it:
```ts
preprocessedContent = preprocessedContent.replace(/\$(?=\d)/g, '\\$');
```
When the dollar is **already escaped**, that turns an odd backslash run
even:
```
input: \$4 (an escaped dollar — the author wants a literal "$4")
output: \\$4 (a literal backslash, then an unescaped `$`)
```
`MarkdownPreprocessorManager.process` runs this before `_markdownToAst`,
so remark-math sees that bare `$` and can take it as an active delimiter
— the opposite of what escaping it was for.
### The fix
Escape only a dollar whose preceding backslash run is **even**, keeping
the run itself:
```ts
preprocessedContent = preprocessedContent.replace(
/(?<!\\)((?:\\\\)*)\$(?=\d)/g,
'$1\\$'
);
```
`(?<!\\)` anchors the run to its start and `((?:\\\\)*)` takes
backslashes in pairs, so an odd run never matches. Behaviour that was
already correct is untouched:
| input | before | after |
|---|---|---|
| `costs $5 today` | `costs \$5 today` | unchanged |
| `costs \\$4 today` (even run — literal `\` then a real `$`) | `costs
\\\$4 today` | unchanged |
| `costs \$4 today` (odd run — already escaped) | `costs \\$4 today` |
**`costs \$4 today`** |
### Relationship to #15596
Both touch this file, so whichever lands second will need a trivial
rebase:
- Both add `export` to `preprocessLatex` (the same one-word change).
- They edit **different** statements — #15596 changes the *protection*
regex above, this one changes the *currency* regex below.
- Test files are deliberately named differently
(`latex-preprocessor.unit.spec.ts` there,
`latex-currency-escape.unit.spec.ts` here) so they do not collide. Happy
to fold them into one file once both are in, if you'd prefer that.
I verified this bug is not introduced by #15596: `\$4` preprocesses to
`\\$4` identically with and without that branch.
## Checklist
- [ ] I have signed the [AFFiNE Contributor License
Agreement](https://cla-assistant.io/toeverything/AFFiNE) — required
before merge; the `license/cla` check must be green ([how it
works](https://github.com/toeverything/AFFiNE/blob/canary/docs/BUILDING.md#sign-the-cla-first))
- [x] The PR targets the `canary` branch and its title follows
[Conventional Commits](https://www.conventionalcommits.org/)
- [x] Tests are added or updated where it makes sense
- [x] `yarn lint` and `yarn typecheck` pass locally
- [x] If the PR code includes AI-generated edits, I have carefully
reviewed it to ensure that the scope of the PR is consistent with what
is claimed in the title and description, and that there are no redundant
or invalid implementations.
- [x] I agree that maintainers may close the PR without discussion if
they consider the code quality to be too low.
### Notes on the checklist
- **CLA:** not signed yet — the repository owner is signing it; the
`license/cla` check will go green once that happens.
- **Tests:** 7 cases in `latex-currency-escape.unit.spec.ts`. Reverting
only the regex fails exactly the three already-escaped cases and leaves
the four guards (plain price, bare amount, two prices, even run) green —
they are regression guards, not props. Full `blocksuite/affine/all`
suite: 230 passing.
- **Lint/format:** `oxlint --deny-warnings` and `oxfmt --check` are both
clean on the two changed files.
- **Typecheck:** clean for these files. For transparency, a full `yarn
typecheck` on my machine also reports pre-existing `@prisma/client`
errors under `packages/backend/server`; those come from my installing
with `--mode=skip-build` (so `prisma generate` never ran) and are
unrelated to this change.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Improved LaTeX handling for currency values by escaping dollar signs
only when required.
- Preserved already-escaped dollar signs and backslash sequences
correctly.
- **Tests**
- Added coverage for plain currency amounts, already-escaped dollar
signs, and even backslash runs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- Search language IDs, display labels, and aliases case-insensitively,
preserving prefix matching and the existing result order.
- Ignore pending grammar-load results when the selected language has
changed, preventing highlighting from returning after switching to Plain
Text.
Related to #15492: display-label matching will also make “Plain Text”
searchable by its full name, while the highlighting fix prevents pending
language loads from restoring highlighting after switching to Plain
Text.
## Validation
Code-block E2E suite (7 passed), scoped TypeScript checks, lint, and
formatting checks passed. Both focused regression tests fail on
unmodified `canary` and pass with the fixes.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Code block language search now matches display names, IDs, aliases,
and labels without regard to letter case.
- Search results prioritize matches at the beginning of language names
and aliases over label-only matches.
- **Bug Fixes**
- Prevented outdated syntax highlighting from appearing after a code
block language is changed or cleared while loading.
- Prevented duplicate language-loading requests during concurrent
highlighting updates.
<!-- 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>
#### PR Dependency Tree
* **PR #15621** 👈
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 payment and customer portal reliability when Stripe customer
records already exist.
- Existing legacy customer records are now recognized and associated
with the correct account context.
- Prevented customer records from being used across incompatible account
contexts.
- Preserved the existing error behavior when no customer record is
available.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
**Problem:** When a language was selected in a code block's language
dropdown, there was no way to reset it back to "Plain Text" (no syntax
highlighting). The dropdown only contained shiki languages, and the null
value representing Plain Text had no corresponding selectable option.
**Solution:** Added a "Plain Text" entry as the first item in the
language selector dropdown. This entry:
- Maps to language: null when selected
- Shows a checkmark when Plain Text is active
- Persists in localStorage alongside recently used languages
- Is searchable via aliases ("plain", "text", "none")
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added Plain Text as a selectable code block language.
* Plain Text is available in newly generated and previously saved
language lists.
* Code blocks correctly show Plain Text when no language is selected.
* Recognizes common names including “plain,” “text,” “plaintext,” “txt,”
and “none.”
* **Tests**
* Added coverage for switching between Rust and Plain Text.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
#### PR Dependency Tree
* **PR #15615** 👈
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**
- Checkout success links are now safely normalized before being sent to
the payment provider.
- Relative callback paths are converted into valid absolute URLs.
- Empty or missing success callbacks now return customers to the default
application page.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
#### PR Dependency Tree
* **PR #15614** 👈
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**
- OAuth availability now reflects whether active OAuth providers are
configured.
- Payment functionality now automatically follows the payment enablement
setting, including configuration changes.
- Subscription plan settings now validate required Pro and Team pricing
before displaying plans.
- **Bug Fixes**
- Added a retry option when plan pricing cannot be loaded or validated.
- Improved invitation notification handling to ensure notifications
appear reliably after inviting a workspace member.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
The changes below are prepared locally and have not been committed or
pushed.
- Save original URLs, user titles, selected text, images and PDFs
through a versioned, file-backed Share Inbox. Stable document/attempt
IDs, serialized imports and committed receipts make retries preserve
user edits and recover native completion.
- Keep rich previews transient. Share previews request transcript
excerpts, clip optional display fields and bound response/image
downloads. Saving does not wait for preview requests or persist provider
metadata, transcript snapshots or dedicated preview blobs.
- Restore the minimal App Group workspace-mode snapshot and
`official`/`deferred` route. Official routes remain fixed for the
pending share; deferred self-hosted previews use the selected server
without a provider fallback.
- Let bookmark and YouTube details read the current workspace's Link
Preview API by URL. Full transcripts, chapters and timestamps load on
expansion without document writes. Reuse editor styles and the existing
portal for canvas details; isolate endpoint/base/transcript caches and
keep transcript results out of persistent cache.
## Validation
- Focused frontend suites: 128 tests passed; the 43 preview/details
cases also passed after the final portal change.
- `ShareInboxSafetyTests`: 57 tests passed on iOS 26.5 simulator.
- `yarn typecheck` and full `yarn lint` passed.
- iOS web bundle, Capacitor asset copy, and signed simulator App build
with embedded ShareExtension passed.
- Browser component checks: desktop, narrow viewport, light/dark themes,
full transcript scrolling, canvas portal positioning and Escape
dismissal.
- Simulator Safari share: rich preview, cancellation, native-to-app
handoff, workspace selection and successful save. The pending manifest
contained original content and routing only, and was removed after
import.
Physical-device and network-capture matrices remain manual. The
simulator YouTube iframe displayed player error 153; this is separate
from the successful preview/import checks and video playback is not
claimed as verified.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* iOS sharing now supports PDF files alongside images, with previews and
attachment validation.
* Share imports offer improved workspace selection, offline handling,
retry recovery, and clearer error messages.
* Link previews can display expandable metadata and transcript details
for bookmarks and YouTube embeds.
* Share inbox entries now identify unsupported versions and provide
safer attachment resolution.
* **Bug Fixes**
* Improved cancellation, cleanup, and stale-request handling during
sharing and importing.
* Enhanced link-preview validation and protection against oversized or
invalid responses.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DarkSky <darksky2048@gmail.com>
## Description
While reading `DocSyncPeer.addPriority` I noticed the fix from #15338
(commit `cd6593c6`) was applied to only one of the three near-identical
copies of that method in `nbstore`. The two others still carry the
pre-#15338 code:
- `DocFrontend.addPriority` —
`packages/common/nbstore/src/frontend/doc.ts`
- `IndexerSyncStatus.addPriority` —
`packages/common/nbstore/src/sync/indexer/index.ts`
Both write the incoming priority over whatever was stored:
```ts
const oldPriority = this.prioritySettings.get(id) ?? 0;
this.prioritySettings.set(id, priority); // overwrites
this.jobDocQueue.setPriority(id, oldPriority + priority); // adds
```
The two lines already disagree with each other, and the release callback
returned by the method *subtracts* `priority` from the current value, so
it only makes sense if the forward path adds. The result is that
overlapping priority requests for the same doc don't stack, and
releasing one of them drops the doc to zero instead of back to the level
the remaining holder asked for.
That overlap is reachable from the UI. `WorkspaceEngine.doc` is a
`DocFrontend` (`entities/engine.ts` → `client.docFrontend`), and two
call sites add +10 to the same `pageId`: `detail-page-wrapper.tsx` and
`peek-view/view/utils.ts`. Open a doc, peek the same doc, close the peek
view, and the still-open doc is left at priority 0 — behind every other
queued doc. The indexer has the same pair: the navigation panel node and
the doc-summary store both add +10 to the same `docId`.
The fix is the same shape as #15338 — compute `newPriority` /
`restoredPriority` once and use it for both the map and the queue.
### Test
`doc priority requests accumulate` in
`packages/common/nbstore/src/__tests__/frontend.spec.ts`. Two holders
take +10 on `high` and one of them releases; `low` sits at +5; both load
jobs are queued before `start()` so the queue priority alone decides the
load order, which the test reads by spying on `storage.getDoc`.
On `canary` it fails:
```
FAIL packages/common/nbstore/src/__tests__/frontend.spec.ts > doc priority requests accumulate
AssertionError: expected [ 'low', 'high' ] to deeply equal [ 'high', 'low' ]
```
With the fix, the whole package is green (`yarn vitest run
packages/common/nbstore`: 16 files, 86 tests).
## Checklist
- [x] I have signed the [AFFiNE Contributor License
Agreement](https://cla-assistant.io/toeverything/AFFiNE)
- [x] The PR targets the `canary` branch and its title follows
[Conventional Commits](https://www.conventionalcommits.org/)
- [x] Tests are added or updated where it makes sense
- [x] `yarn lint` and `yarn typecheck` pass locally
- [x] If the PR code includes AI-generated edits, I have carefully
reviewed it to ensure that the scope of the PR is consistent with what
is claimed in the title and description, and that there are no redundant
or invalid implementations.
- [x] I agree that maintainers may close the PR without discussion if
they consider the code quality to be too low.
AI tools used
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved priority handling for document loading and indexing so
multiple priority requests accumulate correctly.
* Ensured queued work consistently reflects remaining priority requests
after individual requests are released.
* High-priority documents and indexing tasks are now processed ahead of
lower-priority work as expected.
* **Tests**
* Added coverage validating accumulated priority behavior for document
loading and index crawling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
#### PR Dependency Tree
* **PR #15578** 👈
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 macOS application icon capture for media sources.
- Captured icons are resized and provided as PNG images for consistent
display.
- **Bug Fixes**
- Updated native component compatibility checks to recognize the latest
supported native package version.
- Improved reliability when retrieving application icons, including
graceful handling when icon data is unavailable.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
fix#15574
#### PR Dependency Tree
* **PR #15575** 👈
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 source-aware blob handling for current documents and page
history.
* Added document lifecycle support for trashing, restoring, and
permanently deleting documents.
* Copilot attachments now upload directly to session attachment
endpoints.
## Bug Fixes
* Document actions now complete before confirmations, notifications, and
navigation proceed.
* Improved cancellation, upload fallback, size-limit handling, and cloud
synchronization reliability.
* Invite-link expiration now remains reliable for long durations.
* Improved synchronization for document permissions and reserved
documents.
## Changes
* Blob management now displays attachment icons instead of image
previews.
* Cloud synchronization now uses a unified batch protocol.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Closes#13579.
On readonly shared pages, selecting text under a heading persisted as
collapsed caused reactive selection handling to restore the persisted
collapsed value over the viewer-local expanded state. This closed the
heading again and prevented selecting or copying its revealed content.
This change:
- separates persisted collapsed-state synchronization from selection
cleanup;
- makes the readonly-local collapsed state reactive for both BlockSuite
effects and Lit rendering;
- preserves selection and copying while a readonly heading is locally
expanded;
- still clears hidden text selection when the heading is collapsed
again;
- adds a Playwright regression covering collapse → readonly → expand →
drag-select → copy → re-collapse.
## Checklist
- [x] I have signed the [AFFiNE Contributor License
Agreement](https://cla-assistant.io/toeverything/AFFiNE) — required
before merge; the `license/cla` check must be green ([how it
works](https://github.com/toeverything/AFFiNE/blob/canary/docs/BUILDING.md#sign-the-cla-first))
- [x] The PR targets the `canary` branch and its title follows
[Conventional Commits](https://www.conventionalcommits.org/)
- [x] Tests are added or updated where it makes sense
- [x] `yarn lint` and `yarn typecheck` pass locally
## Testing
- Focused Chromium regression: 1 passed
- Adjacent readonly paragraph scenarios: 3 passed
- Targeted `oxfmt --check`: passed
- Targeted `oxlint --deny-warnings`: passed
- `git diff --check`: passed
`yarn typecheck` could not be confirmed in this checkout because
required generated workspace declaration outputs are missing.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved collapsed heading behavior in read-only mode.
* Preserved local heading expansion and text selection when copying
content and collapsing the heading.
* Ensured local expansion state resets correctly when switching out of
and back into read-only mode.
* **Tests**
* Added coverage for read-only heading expansion, keyboard copying,
clipboard access, mode switching, and text selection.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
`PriorityQueue.bubbleDown` compares `swap` against `null`, but `swap` is
initialized to `-1` (`priority-queue.ts:19`), and the loop's own exit
check uses `swap === -1` (`:43`). So `swap === null` is always false and
`swap !== null` always true: the right child is only ever compared with
the left child, never with the element being sunk. A node can sink below
a child larger than itself, breaking the heap invariant.
Randomized check on the current code: 394 of 20,000 random heaps dequeue
out of order. Smallest reproducer is 7 elements — `[1, 2, 3, 7, 6, 5,
4]` dequeues as `[1, 2, 3, 5, 4, 6, 7]`.
The queue backs `AStarRunner` (`a-star.ts:4`, its only consumer), which
routes orthogonal connectors via `ConnectorPathGenerator.updatePath` —
edgeless connector re-routing, mindmap rendering, and auto-complete. The
A* heuristic is admissible, so a path is still produced; the effect is
that nodes are expanded in the wrong order, so the shortest /
fewest-bends guarantee is lost rather than the connector breaking.
### Why this needed a config change too
The package's `vitest.config.ts` was dropped in the #10702 package reorg
and never recreated at `blocksuite/affine/blocks/surface/`, so `vitest`
collects none of its six test files:
```
$ yarn vitest run blocksuite/affine/blocks/surface/src/__tests__/priority-queue.unit.spec.ts
No test files found, exiting with code 1
```
They have not run since 2025-03-08, which is why this survived.
Restoring the config re-enables all six files (40 tests, all passing) so
the regression test added here actually guards the fix.
Verified by reverting the one-line fix with the config in place:
```
FAIL should not sink a node past children that are all larger than it
expected [1,2,3,5,4,6,7] to deeply equal [1,2,3,4,5,6,7]
FAIL should dequeue in ascending order regardless of insertion order
Tests 2 failed | 38 passed (40)
```
## Checklist
- [x] I have signed the [AFFiNE Contributor License
Agreement](https://cla-assistant.io/toeverything/AFFiNE) — required
before merge; the `license/cla` check must be green ([how it
works](https://github.com/toeverything/AFFiNE/blob/canary/docs/BUILDING.md#sign-the-cla-first))
- [x] The PR targets the `canary` branch and its title follows
[Conventional Commits](https://www.conventionalcommits.org/)
- [x] Tests are added or updated where it makes sense
- [x] `yarn lint` and `yarn typecheck` pass locally
- [x] If the PR code includes AI-generated edits, I have carefully
reviewed it to ensure that the scope of the PR is consistent with what
is claimed in the title and description, and that there are no redundant
or invalid implementations.
- [x] I agree that maintainers may close the PR without discussion if
they consider the code quality to be too low.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Corrected priority queue ordering behavior to ensure items are
dequeued in ascending order across insertion sequences.
- **Tests**
- Added coverage for priority queue behavior when comparing nodes with
two larger child values.
- Added automated test configuration and coverage reporting for the
surface components.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
`.docker/selfhost/compose.yml` never sets `DEPLOYMENT_TYPE`, and the
Node and Rust layers disagree about what that means.
The Node side defaults to self-hosted outside development
(`packages/backend/server/src/env.ts:97-100`). The native runtime reads
the same variable straight from the process environment and falls back
to the opposite value
(`packages/backend/native/src/runtime/config.rs:474`):
```rust
fn deployment_from_env() -> Deployment {
if env::var("DEPLOYMENT_TYPE").as_deref() == Ok("selfhosted") {
Deployment::SelfHosted
} else {
Deployment::Cloud
}
}
```
The only process.env.DEPLOYMENT_TYPE assignments in
packages/backend/server/src are in tests and
.github/deployment/node/Dockerfile sets no default.
As a result:
* `custom_endpoint_mode` resolves to `unavailable` rather than
`disabled`/`enabled`, so `allowCustomEndpoint` in `config.json` is
silently ignored (`llm/byok/policy.rs:54`).
* The `SelfHosted && !byok_enabled` guard never fires, and
managed-provider routing is offered on an instance with no managed
access (`llm/route/policy.rs:76`, `:109`).
* Self-hosted instances get the cloud new account delay
(`rolling_quota/workspace_invite_policy.rs:194`).
The BYOK policy matrix test already pins the intended self-hosted
behavior that a default Compose deployment never reaches
(`llm/byok/policy.rs:210-212`):
```text
(Deployment::Cloud, false, false, "unavailable", false),
(Deployment::Cloud, true, true, "unavailable", false),
(Deployment::SelfHosted, false, true, "disabled", false),
(Deployment::SelfHosted, true, false, "enabled", true),
```
## Alternative considered
Changing `deployment_from_env()` to default to `SelfHosted` would repair
existing deployments without requiring users to edit their Compose
configuration, and may ultimately be the better fix. Happy to switch if
preferred.
## Checklist
- [x] I have signed the [AFFiNE Contributor License
Agreement](https://cla-assistant.io/toeverything/AFFiNE) — required
before merge; the `license/cla` check must be green ([how it
works](https://github.com/toeverything/AFFiNE/blob/canary/docs/BUILDING.md#sign-the-cla-first))
- [x] The PR targets the `canary` branch and its title follows
[Conventional Commits](https://www.conventionalcommits.org/)
- [ ] Tests are added or updated where it makes sense
- [x] `yarn lint` and `yarn typecheck` pass locally
- [ ] If the PR code includes AI-generated edits, I have carefully
reviewed it to ensure that the scope of the PR is consistent with what
is claimed in the title and description, and that there are no redundant
or invalid implementations.
- [x] I agree that maintainers may close the PR without discussion if
they consider the code quality to be too low.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Self-hosted deployments now identify their deployment type for both
the server and migration services.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Self-hosted AFFiNE's `/api/worker/link-preview` and
`/api/worker/image-proxy` endpoints forward the requesting browser's
`Accept-Encoding` header to whatever URL they fetch (`cloneHeader()`),
but the underlying native `safeFetch` never decompresses the response
before parsing it. Sites that gzip/brotli-compress their HTML (GitHub
does, for any non-trivially-sized repo page) end up handing AFFiNE
compressed bytes, which the HTMLRewriter meta-tag scraper silently reads
as nothing. The result: link preview cards render with no image, title,
or description.
**Fix:** drop `Accept-Encoding` from the headers forwarded in
`cloneHeader()`, so upstream requests are implicitly identity-encoded
and the response body stays uncompressed for the parsing that follows.
**Before**
<img width="641" height="335" alt="image"
src="https://github.com/user-attachments/assets/8ec8a925-6a1f-422a-9cb0-5e1687a5f75e"
/>
**After**
<img width="659" height="323" alt="image"
src="https://github.com/user-attachments/assets/c35002b5-43c1-4f8b-a4c2-20028f1e453a"
/>
## Checklist
- [x] I have signed the [AFFiNE Contributor License
Agreement](https://cla-assistant.io/toeverything/AFFiNE) — required
before merge; the `license/cla` check must be green ([how it
works](https://github.com/toeverything/AFFiNE/blob/canary/docs/BUILDING.md#sign-the-cla-first))
- [x] The PR targets the `canary` branch and its title follows
[Conventional Commits](https://www.conventionalcommits.org/)
- [x] Tests are added or updated where it makes sense
- [x] `yarn lint` and `yarn typecheck` pass locally
- [x] If the PR code includes AI-generated edits, I have carefully
reviewed it to ensure that the scope of the PR is consistent with what
is claimed in the title and description, and that there are no redundant
or invalid implementations.
- [x] I agree that maintainers may close the PR without discussion if
they consider the code quality to be too low.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Prevented `Accept-Encoding` headers from being forwarded when
processing worker link-preview and image-proxy requests.
* Improved request handling to avoid passing client compression
preferences to external fetch targets.
* **Tests**
* Added end-to-end coverage confirming that `Accept-Encoding` is
excluded from forwarded requests.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<img width="284" height="132" alt="image"
src="https://github.com/user-attachments/assets/a11f1919-ac91-4f87-b03d-4fab6e8cba7e"
/>
## Description
Add a select-all action next to the Trash page title after document
multi-selection starts.
- Select every unique document ID from the explorer groups, including
items outside the rendered viewport.
- Change the action to Clear selection when every Trash document is
selected.
- Reuse the existing Trash bulk-operation permissions without
introducing artificial grouping.
## Testing
- Trash page Playwright E2E: 2 passed
- Targeted TypeScript project build
- lint-staged
- lint:ox
- oxfmt --check
- git diff --check
## Checklist
- [x] I have signed the AFFiNE Contributor License Agreement
- [x] The PR targets the canary branch and its title follows
Conventional Commits
- [x] Tests are added or updated where it makes sense
- [ ] Full yarn lint and yarn typecheck pass locally
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added a select-all and clear-selection control to the Trash view when
multiple documents can be managed.
- Select-all actions now update the document selection toolbar.
- **Bug Fixes**
- Improved Trash view controls for users with administrator or owner
permissions.
- **Tests**
- Added end-to-end coverage for selecting, selecting all, and clearing
multiple trashed documents.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
#### PR Dependency Tree
* **PR #15542** 👈
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**
- Invitation links now require sign-in to view or accept.
- Added guidance when an invitation belongs to a different account, with
options to switch accounts or return to AFFiNE.
- Sign-in preserves the invitation link when switching accounts.
- **Bug Fixes**
- Invitation errors now display accurate messages instead of generic
errors.
- Improved handling of expired, invalid, and missing invitations.
- Invitation status checks now work correctly for authenticated
invitees.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
This change restricts email invitation metadata lookup to the
authenticated user that the invitation was issued to.
## Impact
The public invite-info resolver returns workspace, owner, invitee, and
status details for a valid invitation identifier. For email invitations,
that information should only be returned to the intended recipient after
authentication.
## Fix
- Keep existing link-invitation behavior unchanged.
- For email invitations, require an authenticated user whose id matches
the invitation recipient before returning invitation details.
- Return the existing invalid-invitation error for mismatched or
unauthenticated access.
## Validation
- `git diff --check`
- Full test/lint suite was not run locally because dependencies are not
installed in this checkout.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Tightened invitation access checks so non-link invites are only
readable by the intended recipient.
* Invalid or missing user context now returns an error earlier,
preventing access to invite details when the invitation doesn’t match.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Signed-off-by: failsafesecurity <190101117+failsafesecurity@users.noreply.github.com>
Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
#### PR Dependency Tree
* **PR #15538** 👈
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 rich link previews to mobile and iOS sharing, including images,
metadata, transcripts, and selected text.
* Share imports can now create structured content blocks, embeds,
bookmarks, and transcript callouts.
* Added workspace-aware preview handling for cloud, self-hosted, and
signed-out modes.
* **Accessibility**
* Improved collapse/expand controls with semantic buttons and ARIA
relationships.
* **Bug Fixes**
* Enhanced URL and error sanitization in server logs.
* Improved link-preview CORS support, validation, and request handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added iOS sharing for URLs, text, web pages, and images.
* Shared content can be reviewed, titled, and saved to an AFFiNE
workspace.
* Choose destinations including workspaces, tags, and collections.
* Added previews, attachment handling, import status, retry support, and
success/error feedback.
* Pending shares are processed when AFFiNE opens or returns to the
foreground.
* **Bug Fixes**
* Improved workspace profile handling across different workspace types.
* Onboarding completion is now recorded immediately after successful
sign-in verification.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
<!--
Thank you for contributing to AFFiNE!
The PR title must follow Conventional Commits (enforced by CI):
type(scope): description e.g. fix(editor): keep selection after paste
Types: feat fix docs style refactor perf test build ci chore revert
-->
## Description
<!-- What does this PR do? Link related issues, e.g. "Closes #1234".
Screenshots or recordings are welcome for UI changes. -->
## Checklist
- [ ] I have signed the [AFFiNE Contributor License
Agreement](https://cla-assistant.io/toeverything/AFFiNE) — required
before merge; the `license/cla` check must be green ([how it
works](https://github.com/toeverything/AFFiNE/blob/canary/docs/BUILDING.md#sign-the-cla-first))
- [ ] The PR targets the `canary` branch and its title follows
[Conventional Commits](https://www.conventionalcommits.org/)
- [ ] Tests are added or updated where it makes sense
- [ ] `yarn lint` and `yarn typecheck` pass locally
#### PR Dependency Tree
* **PR #15533** 👈
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 indexer feature synchronization when the indexer is enabled
or configuration changes.
* Enabled indexer-related capabilities without waiting for native search
readiness checks.
* Improved consistency across search, aggregate, document, and
application startup flows.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!--
Thank you for contributing to AFFiNE!
The PR title must follow Conventional Commits (enforced by CI):
type(scope): description e.g. fix(editor): keep selection after paste
Types: feat fix docs style refactor perf test build ci chore revert
-->
## Description
<!-- What does this PR do? Link related issues, e.g. "Closes #1234".
Screenshots or recordings are welcome for UI changes. -->
## Checklist
- [ ] I have signed the [AFFiNE Contributor License
Agreement](https://cla-assistant.io/toeverything/AFFiNE) — required
before merge; the `license/cla` check must be green ([how it
works](https://github.com/toeverything/AFFiNE/blob/canary/docs/BUILDING.md#sign-the-cla-first))
- [ ] The PR targets the `canary` branch and its title follows
[Conventional Commits](https://www.conventionalcommits.org/)
- [ ] Tests are added or updated where it makes sense
- [ ] `yarn lint` and `yarn typecheck` pass locally
#### PR Dependency Tree
* **PR #15532** 👈
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 search reconciliation and publication status handling when
workspace reconciliation fails.
* Prevented failed workspace reconciliation from incorrectly blocking
generation completion.
* Preserved active generation state so reconciliation can retry and
complete pending publications.
* Improved managed provider profile migration, including legacy
configurations, unavailable models, conflicting assignments, and missing
defaults.
* **Tests**
* Expanded coverage for workspace recovery and managed provider profile
migration scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description
Self-hosted GitHub OAuth login fails at token exchange because the
outbound request to `https://github.com/login/oauth/access_token` has no
`User-Agent` header. GitHub then returns 403 ("Request forbidden by
administrative rules"), which is surfaced as
`INVALID_OAUTH_CALLBACK_CODE`.
OAuth `safeFetch` only forwarded `authorization`, `content-type`, and
`accept`, so even a User-Agent on the request would be stripped. This
change:
- allows `user-agent` in OAuth `fetchOptions()`
- always sends `User-Agent: AFFiNE-Server` from `fetchJson()` (covers
token exchange and `api.github.com` user/email fetches)
Fixes#15521
## Checklist
- [x] The PR targets the `canary` branch and its title follows
Conventional Commits
- [x] Tests are added or updated where it makes sense
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved OAuth request compatibility by including a standard
`User-Agent` header.
* Ensured the header is permitted consistently during OAuth token
exchanges.
* **Tests**
* Added coverage to verify case-insensitive handling of the `User-Agent`
header in GitHub OAuth requests.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
fix#15523fix#15526
#### PR Dependency Tree
* **PR #15528** 👈
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 search generation cleanup, provider error reporting, and
reconciliation reliability.
- Retired search resources are cleaned up safely, including after
credential changes.
- Prompt size checks now ignore tool parameters and provide clearer
errors.
- Reserved documents are protected from accidental cleanup, and
malformed identifiers are rejected.
- **Configuration**
- Managed Copilot profiles require explicit, non-duplicated model
assignments.
- Improved managed provider profile migration.
- **Performance & Reliability**
- Reduced unnecessary search-history cleanup and adjusted
consistency-check intervals.
- Failed reconciliation jobs stop after one attempt and are removed
automatically.
- **Data Updates**
- Updated legacy AI session prompt names to current labels.
- Improved cloud load-balancer health-check configuration.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
fix#15514
#### PR Dependency Tree
* **PR #15516** 👈
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 color handling when importing HTML content.
* Added support for hex, RGB/RGBA, percentage, alpha, transparent,
named, and HSL color values.
* Correctly maps supported colors to the app’s color themes.
* Prevents invalid, translucent, or unsuitable colors from being
applied.
* Preserves style values containing additional colons during import.
* Improved imported text formatting for supported and unsupported
colors.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- Route iOS nbstore worker token reads through the main-thread
MessagePort and skip Capacitor Auth on `/socket.io` polling so self-host
WebSocket/XHR sync no longer hangs on Connect timeout.
- Harden workspace `flavour:id` routing, DocSyncPeer abort/status
handling, and `resetSync` so local selfhost edits push and Mac browsers
can receive them.
- Soften root-doc readiness waits and session-exchange throttling to
keep mobile selfhost sign-in/sync stable under retries.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Summary by CodeRabbit
* **New Features**
* Improved workspace switching across local and remote environments,
preserving workspace type and server context.
* Added support for page navigation with query parameters.
* Added iOS local-network permission messaging for self-hosted
workspaces.
* **Bug Fixes**
* Improved document synchronization, reset handling, prioritized
document refreshes, and retry behavior.
* Prevented authentication headers and refresh attempts for socket
connection requests.
* Improved workspace reopening and routing when multiple workspace types
share an ID.
* Fixed handling of unlimited data query limits.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DarkSky <darksky2048@gmail.com>
#### PR Dependency Tree
* **PR #15512** 👈
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**
* Search now supports generation-based indexing with embedded and remote
providers.
* Added automatic search reconciliation and improved handling of
document, workspace, and permission changes.
* Added clearer search status errors for unavailable, syncing, unready,
or failed indexes.
* Added Manticore Search end-to-end support and provider-specific search
behavior.
* **Improvements**
* Search and aggregate pagination now report returned results and
continuation status more accurately.
* Improved permission filtering to prevent inaccessible documents from
appearing in results.
* Admin provider selection now consistently enables indexing.
* **Documentation**
* Clarified search pagination, aggregation counts, provider
configuration, and end-to-end setup.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
fix#15505fix#15502fix#15496fix#15491
#### PR Dependency Tree
* **PR #15510** 👈
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 configurable delays for invitations, invite links, and document
publishing by newly created accounts.
- Added workspace action checks that explain blocked actions and retry
timing.
- BYOK setup now verifies model capabilities and saves only validated
options.
- **Bug Fixes**
- Improved BYOK probing for chat, structured responses, tool calls,
embeddings, reranking, and image generation.
- Preserved probe request order and strengthened response validation.
- Authentication configuration changes now reload correctly.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Fix Android editor IME corruption around composition, autocorrect
replay, delete, Enter, and old WebView delete behavior.
## What changed
- Add Android WebView InputConnection wrapper for editor IME handling.
- Route Android delete events through BlockSuite editor input.
- Guard against keyboard autocorrect/composition replay after delete or
space.
- Stabilize delete fallback on older Android/WebView versions.
- Gate IME diagnostic logs behind debug builds.
- Add Android IME fix notes and regression coverage.
## Validation
- Manual Android testing passed.
- `git diff --check upstream/canary...HEAD`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved Android text editing for Backspace, Delete, Enter, composing
text, and keyboard events.
* Prevented input methods from targeting the wrong editor area.
* Improved caret-based text handling, focus synchronization, and
composing-session cleanup.
* **Platform Improvements**
* Added a dedicated Android input bridge for smoother IME interactions
and fallback keyboard behavior.
* Improved editor actions, input recovery, and trusted-page validation
for Android communication.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DarkSky <darksky2048@gmail.com>
## Summary
- Reworks the iOS onboarding, native sign-in, and paywall flow so
first-install and cold-start entry stay native, adaptive, and aligned
with the current login/subscription gates.
- Removes onboarding swipe paging, refines onboarding/paywall layout
behavior, and keeps AI/paywall entry behavior consistent for logged-in
and subscribed users.
- Adds the new mobile all-docs empty states with localized copy and
dialog entry points, and closes the remaining review follow-ups by
removing the onboarding plan artifact and dropping the iOS AI
subscription bypass.
## Test plan
- Built the iOS app for simulator with `xcodebuild -workspace
App.xcworkspace -scheme App -configuration Debug -sdk iphonesimulator
-destination 'generic/platform=iOS Simulator' ARCHS=arm64
ONLY_ACTIVE_ARCH=YES CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO
build` during the native onboarding/sign-in flow work.
- Let the repo pre-commit hooks run on the latest follow-up commit
(`prettier` + `eslint --fix`).
- Checked diagnostics for the edited TS/TSX files after the review
follow-up changes.
- Manually iterated on onboarding/native sign-in/paywall UI states in
simulator during implementation.
---------
Co-authored-by: DarkSky <darksky2048@gmail.com>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Redesigned mobile Settings with localized sections, promotional
content, support actions, subscription access, and improved account
controls.
- Added sign-in prompts for signed-out users and clearer device-session
handling.
- Added a guided account-deletion flow with email confirmation and
completion messaging.
- Added App Store, download, team invitation, rating, and external
support links.
- **Bug Fixes**
- Improved interactive row behavior, keyboard accessibility, navigation,
and subscription availability.
- Prevented account deletion for unresolved team owners.
- **Style**
- Refreshed mobile settings, subscription, profile, and promotional card
layouts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: DarkSky <darksky2048@gmail.com>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Retry failed Copilot transcription tasks in real time.
* Retried tasks resume processing and report updated status.
* Managed Copilot provider models can be omitted to use provider
defaults.
* **Bug Fixes**
* Improved handling of incomplete BYOK profiles, including safe
replacement of legacy records.
* Duplicate profile creation now returns a clear validation error.
* Improved transcript processing reliability by preventing duplicate or
stale dispatches.
* **Migration**
* Consolidated legacy managed-provider settings while preserving
existing profiles and defaults.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->