### TL;DR
Added "Save as doc" option to Edgeless editor actions.
### What changed?
- Renamed `SAVE_CHAT_TO_BLOCK_ACTION` to `SAVE_AS_BLOCK` and updated its title from "Save chat to block" to "Save as block"
- Renamed `CREATE_AS_DOC` to `SAVE_AS_DOC` and updated its title from "Create as a doc" to "Save as doc"
- Added `SAVE_AS_DOC` to the `EdgelessEditorActions` array, making this option available in the Edgeless editor
### TL;DR
Refactor the insert below functionality to work with page mode and edgeless mode
* Page Mode
- Insert content below the current selection
- If nothing selected, insert content below the last block
* EdgeLess Mode
- If no note block is currently selected, create the content as a new note block.
- Otherwise, insert content into the selected note
Close BS-2760
### What changed?
- Created separate insert handlers for page and edgeless modes with context-aware behavior
- Added support for inserting content when nothing is selected by targeting the last content block
- Added special handling for edgeless mode to support inserting below selected note blocks
- Removed the "Replace selection" action and consolidated insert functionality
- Optimized the clickable area of the action button
Close [BS-2791](https://linear.app/affine-design/issue/BS-2791).
### What Changed?
- Add status filed to `CopilotContextDoc` to querying document embedding processing progress.
- Change `ChipState` from `success` to `finished` to better align with backend server status.
- Add `pollContextDocsAndFiles` API for embedding status polling.
### About Polling
- Set the minimum interval to 1 second and the maximum interval to 30 seconds
- Use exponential backoff and increase the interval by 50% after each poll
- Make sure the interval does not exceed the maximum
### TL;DR
Created dedicated worker entry points to avoid dynamic imports.
### What changed?
- Painters are provided during worker initialization
- Removed `ParagraphPaintConfigExtension` and the associated configuration system
- Created dedicated worker entry points in both the integration test and frontend packages
- Modified `ViewportLayoutPainter` to accept painters in its constructor
- Updated the `TurboRendererConfig` interface to require a `painterWorkerEntry` function
### Why make this change?
Webpack support. Extension objects in main thread are not available to be passed into workers. Dynamic painter path import is hard to support in webpack environment. With the [webpack-ignore](https://webpack.js.org/api/module-methods/#webpackignore) rule, there are still build errors in webpack.
### TL;DR
Added unit tests for block and selection commands, along with a new test helper system for creating test documents.
### What changed?
- Added unit tests for several commands:
- `getFirstContentBlockCommand`
- `getLastContentBlockCommand`
- `isNothingSelectedCommand`
- Created a new test helpers make it easier to create structured test documents with a html-like syntax:
```typescript
import { describe, expect, it } from 'vitest';
import { affine } from '../__tests__/utils/affine-template';
describe('My Test', () => {
it('should correctly handle document structure', () => {
const doc = affine`
<affine-page>
<affine-note>
<affine-paragraph>Test content</affine-paragraph>
</affine-note>
</affine-page>
`;
// Get blocks
const pages = doc.getBlocksByFlavour('affine:page');
const notes = doc.getBlocksByFlavour('affine:note');
const paragraphs = doc.getBlocksByFlavour('affine:paragraph');
expect(pages.length).toBe(1);
expect(notes.length).toBe(1);
expect(paragraphs.length).toBe(1);
// Perform more tests here...
});
});
```
### TL;DR
Added a new command to check if nothing is currently selected in the editor.
### What changed?
- Created new `isNothingSelectedCommand` to determine if there are no active selections
### TL;DR
Added new commands to retrieve the first and last content blocks in a document.
### What changed?
- Created `getFirstContentBlockCommand` to find the first content block in a document
- Created `getLastContentBlockCommand` to find the last content block in a document
- Added `getFirstNoteBlock` utility function to find the first note block in a document
This PR refactored the turbo renderer architecture to support multiple block layout types.
- New base class `BlockLayoutPainter` and `BlockLayoutProvider` are introduced for writing extendable per-block layout querying and painting logic.
- Paragraph-specific lines are all moved into dedicated classes (`ParagraphLayoutProvider` and `ParagraphLayoutPainter`) under the `/variants/paragraph` dir.
- The `renderer-utils.ts` doesn't contain paragraph-specific logic now.
- The `text-utils.ts` is also now scoped for paragraph only.
- Worker messages are now strongly typed.
Upcoming PR should further implement the block registration system using extension API. The `variants` dir could still exist, since there will be similar rendering logic that can be reused among block types (i.e., between paragraph block and list block).