EYHN
b401012d85
feat(core): user service loading state ( #10922 )
2025-03-17 09:28:25 +00:00
Saul-Mirone
26285f7dcb
feat(editor): unify block props api ( #10888 )
...
Closes: [BS-2707](https://linear.app/affine-design/issue/BS-2707/统一使用props获取和更新block-prop )
2025-03-16 05:48:34 +00:00
L-Sun
3b4453d2b8
chore(editor): update default width of page block ( #10873 )
...
Close [BS-2498](https://linear.app/affine-design/issue/BS-2498/page-block首次切换时默认宽度为800px )
2025-03-14 12:59:17 +00:00
akumatus
daccb2c865
feat(core): add ai file context api ( #10842 )
...
Close [BS-2349](https://linear.app/affine-design/issue/BS-2349 ).
### What Changed?
- Add file context graphql apis
- Pass matched file chunks to LLM
[录屏2025-02-19 23.27.47.mov <span class="graphite__hidden">(uploaded via Graphite)</span> <img class="graphite__hidden" src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/sJGviKxfE3Ap685cl5bj/8e8a98ca-6959-4bb6-9759-b51d97cede49.mov " />](https://app.graphite.dev/media/video/sJGviKxfE3Ap685cl5bj/8e8a98ca-6959-4bb6-9759-b51d97cede49.mov )
2025-03-14 04:29:54 +00:00
yoyoyohamapi
e086fd2a43
refactor(editor): getFirstContentBlock -> getFirstBlock & getLastContentBlock -> getLastBlock ( #10809 )
2025-03-14 02:35:22 +00:00
yoyoyohamapi
d3aae962bc
test(editor): getFirstContentBlock & getLastContentBlock & isNothingSelected command ( #10757 )
...
### 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...
});
});
```
2025-03-14 02:35:21 +00:00
yoyoyohamapi
04efca362e
feat(editor): is nothing selected command ( #10721 )
...
### 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
2025-03-14 02:35:21 +00:00
yoyoyohamapi
aa15b106d9
feat(editor): content block getter command ( #10720 )
...
### 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
2025-03-14 02:35:20 +00:00
Saul-Mirone
5148e67891
feat(editor): improve block meta updated event handler ( #10815 )
2025-03-13 06:34:03 +00:00
donteatfriedrice
d2c62602a4
feat(editor): support embed iframe block ( #10740 )
...
To close:
[BS-2660](https://linear.app/affine-design/issue/BS-2660/slash-menu-支持-iframe-embed )
[BS-2661](https://linear.app/affine-design/issue/BS-2661/iframe-embed-block-model-and-block-component )
[BS-2662](https://linear.app/affine-design/issue/BS-2662/iframe-embed-block-toolbar )
[BS-2768](https://linear.app/affine-design/issue/BS-2768/iframe-embed-block-loading-和-error-态 )
[BS-2670](https://linear.app/affine-design/issue/BS-2670/iframe-embed-block-导出 )
# PR Description
# Add Embed Iframe Block Support
## Overview
This PR introduces a new `EmbedIframeBlock` to enhance content embedding capabilities within our editor. This block allows users to seamlessly embed external content from various providers (Google Drive, Spotify, etc.) directly into their docs.
## New Blocks
### EmbedIframeBlock
The core block that renders embedded iframe content. This block:
* Displays external content within a secure iframe
* Handles loading states with visual feedback
* Provides error handling with edit and retry options
* Supports customization of width, height, and other iframe attributes
### Supporting Components
* **EmbedIframeCreateModal**: Modal interface for creating new iframe embeds
* **EmbedIframeLinkEditPopup**: UI for editing existing embed links
* **EmbedIframeLoadingCard**: Visual feedback during content loading
* **EmbedIframeErrorCard**: Error handling with retry functionality
## New Store Extensions
### EmbedIframeConfigExtension
This extension provides configuration for different embed providers:
```typescript
/**
* The options for the iframe
* @example
* {
* defaultWidth: '100%',
* defaultHeight: '152px',
* style: 'border-radius: 8px;',
* allow: 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture',
* }
* =>
* <iframe
* width="100%"
* height="152px"
* style="border-radius: 8px;"
* allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture"
* ></iframe>
*/
export type IframeOptions = {
defaultWidth?: string;
defaultHeight?: string;
style?: string;
referrerpolicy?: string;
scrolling?: boolean;
allow?: string;
allowFullscreen?: boolean;
};
/**
* Define the config of an embed iframe block provider
*/
export type EmbedIframeConfig = {
/**
* The name of the embed iframe block provider
*/
name: string;
/**
* The function to match the url
*/
match: (url: string) => boolean;
/**
* The function to build the oEmbed URL for fetching embed data
*/
buildOEmbedUrl: (url: string) => string | undefined;
/**
* Use oEmbed URL directly as iframe src without fetching oEmbed data
*/
useOEmbedUrlDirectly: boolean;
/**
* The options for the iframe
*/
options?: IframeOptions;
};
export const EmbedIframeConfigIdentifier =
createIdentifier<EmbedIframeConfig>('EmbedIframeConfig');
export function EmbedIframeConfigExtension(
config: EmbedIframeConfig
): ExtensionType & {
identifier: ServiceIdentifier<EmbedIframeConfig>;
} {
const identifier = EmbedIframeConfigIdentifier(config.name);
return {
setup: di => {
di.addImpl(identifier, () => config);
},
identifier,
};
}
```
**example:**
```typescript
// blocksuite/affine/blocks/block-embed/src/embed-iframe-block/configs/providers/spotify.ts
const SPOTIFY_DEFAULT_WIDTH = '100%';
const SPOTIFY_DEFAULT_HEIGHT = '152px';
// https://developer.spotify.com/documentation/embeds/reference/oembed
const spotifyEndpoint = 'https://open.spotify.com/oembed ';
const spotifyUrlValidationOptions: EmbedIframeUrlValidationOptions = {
protocols: ['https:'],
hostnames: ['open.spotify.com', 'spotify.link'],
};
const spotifyConfig = {
name: 'spotify',
match: (url: string) =>
validateEmbedIframeUrl(url, spotifyUrlValidationOptions),
buildOEmbedUrl: (url: string) => {
const match = validateEmbedIframeUrl(url, spotifyUrlValidationOptions);
if (!match) {
return undefined;
}
const encodedUrl = encodeURIComponent(url);
const oEmbedUrl = `${spotifyEndpoint}?url=${encodedUrl}`;
return oEmbedUrl;
},
useOEmbedUrlDirectly: false,
options: {
defaultWidth: SPOTIFY_DEFAULT_WIDTH,
defaultHeight: SPOTIFY_DEFAULT_HEIGHT,
allow:
'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture',
style: 'border-radius: 12px;',
allowFullscreen: true,
},
};
// add the config extension to store
export const SpotifyEmbedConfig = EmbedIframeConfigExtension(spotifyConfig);
```
**Key features:**
* Provider registration and discovery
* URL pattern matching
* Provider-specific embed options (width, height, features)
### EmbedIframeService
This service provides abilities to handle URL validation, data fetching, and block creation
**Type:**
```typescript
/**
* Service for handling embeddable URLs
*/
export interface EmbedIframeProvider {
/**
* Check if a URL can be embedded
* @param url URL to check
* @returns true if the URL can be embedded, false otherwise
*/
canEmbed: (url: string) => boolean;
/**
* Build a API URL for fetching embed data
* @param url URL to build API URL
* @returns API URL if the URL can be embedded, undefined otherwise
*/
buildOEmbedUrl: (url: string) => string | undefined;
/**
* Get the embed iframe config
* @param url URL to get embed iframe config
* @returns Embed iframe config if the URL can be embedded, undefined otherwise
*/
getConfig: (url: string) => EmbedIframeConfig | undefined;
/**
* Get embed iframe data
* @param url URL to get embed iframe data
* @returns Embed iframe data if the URL can be embedded, undefined otherwise
*/
getEmbedIframeData: (url: string) => Promise<EmbedIframeData | null>;
/**
* Parse an embeddable URL and add an EmbedIframeBlock to doc
* @param url Original url to embed
* @param parentId Parent block ID
* @param index Optional index to insert at
* @returns Created block id if successful, undefined if the URL cannot be embedded
*/
addEmbedIframeBlock: (
props: Partial<EmbedIframeBlockProps>,
parentId: string,
index?: number
) => string | undefined;
}
```
**Implemetation:**
```typescript
export class EmbedIframeService
extends StoreExtension
implements EmbedIframeProvider
{
static override key = 'embed-iframe-service';
private readonly _configs: EmbedIframeConfig[];
constructor(store: Store) {
super(store);
this._configs = Array.from(
store.provider.getAll(EmbedIframeConfigIdentifier).values()
);
}
canEmbed = (url: string): boolean => {
return this._configs.some(config => config.match(url));
};
buildOEmbedUrl = (url: string): string | undefined => {
return this._configs.find(config => config.match(url))?.buildOEmbedUrl(url);
};
getConfig = (url: string): EmbedIframeConfig | undefined => {
return this._configs.find(config => config.match(url));
};
getEmbedIframeData = async (
url: string,
signal?: AbortSignal
): Promise<EmbedIframeData | null> => {
try {
const config = this._configs.find(config => config.match(url));
if (!config) {
return null;
}
const oEmbedUrl = config.buildOEmbedUrl(url);
if (!oEmbedUrl) {
return null;
}
// if the config useOEmbedUrlDirectly is true, return the url directly as iframe_url
if (config.useOEmbedUrlDirectly) {
return {
iframe_url: oEmbedUrl,
};
}
// otherwise, fetch the oEmbed data
const response = await fetch(oEmbedUrl, { signal });
if (!response.ok) {
console.warn(
`Failed to fetch oEmbed data: ${response.status} ${response.statusText}`
);
return null;
}
const data = await response.json();
return data as EmbedIframeData;
} catch (error) {
if (error instanceof Error && error.name !== 'AbortError') {
console.error('Error fetching embed iframe data:', error);
}
return null;
}
};
addEmbedIframeBlock = (
props: Partial<EmbedIframeBlockProps>,
parentId: string,
index?: number
): string | undefined => {
const blockId = this.store.addBlock(
'affine:embed-iframe',
props,
parentId,
index
);
return blockId;
};
}
```
**Usage:**
```typescript
// Usage example
const embedIframeService = this.std.get(EmbedIframeService);
// Check if a URL can be embedded
const canEmbed = embedIframeService.canEmbed(url);
// Get embed data for a URL
const embedData = await embedIframeService.getEmbedIframeData(url);
// Add an embed iframe block to the document
const block = embedIframeService.addEmbedIframeBlock({
url,
iframeUrl: embedData.iframe_url,
title: embedData.title,
description: embedData.description
}, parentId, index);
```
**Key features:**
* URL validation and transformation
* Provider-specific data fetching
* Block creation and management
## Adaptations
### Toolbar Integration
Added toolbar actions for embedded content:
* Copy link
* Edit embed title and description
* Toggle between inline/card views
* Add caption
* And more
### Slash Menu Integration
Added a new slash menu option for embedding content:
* Embed item for inserting embed iframe block
* Conditional rendering based on feature flags
### Adapters
Implemented adapters for various formats:
* **HTML Adapter**: Exports embed original urls as html links
* **Markdown Adapter**: Exports embed original urls as markdown links
* **Plain Text Adapter**: Exports embed original urls as link text
## To Be Continued:
- [ ] **UI Optimization**
- [ ] **Edgeless Mode Support**
- [ ] **Mobile Support**
2025-03-13 04:11:46 +00:00
EYHN
4b5d1de206
feat(core): add blocksuite writer info service ( #10754 )
2025-03-12 05:02:04 +00:00
Mirone
cd63e0ed8b
feat(editor): replace slot with rxjs subject ( #10768 )
2025-03-12 11:29:24 +09:00
Yifeng Wang
06889295e0
Merge pull request #10745 from toeverything/doodl/gfx-turbo-renderer
...
refactor(editor): add gfx turbo renderer package
2025-03-11 12:48:31 +08:00
Saul-Mirone
9cfd1c321e
fix(editor): missing re-subscription for slots on store ( #10750 )
2025-03-11 04:07:06 +00:00
doodlewind
ad36a9de35
refactor(editor): add gfx turbo renderer package ( #10745 )
...
The `ViewportTurboRendererExtension` is now extracted from `@blocksuite/affine-shared` to `@blocksuite/affine-gfx-turbo-renderer` with minimal dependencies, mirroring the gfx text package in #10378 .
2025-03-11 03:21:52 +00:00
zzj3720
4a45cc9ba4
refactor(editor): implement uni-component in AFFiNE ( #10747 )
2025-03-10 14:23:24 +00:00
doodlewind
d0bc1a0271
fix(editor): incorrect text position in turbo renderer ( #10728 )
...
Fixed incorrect text positioning regression across multiple lines (#10624 )
Before:

After:

2025-03-10 06:27:38 +00:00
Saul-Mirone
4dd5f2ffb0
feat(editor): add viewport element service ( #10727 )
2025-03-10 04:26:18 +00:00
doodlewind
334912e85b
perf(editor): lazy DOM update with idle state in gfx viewport ( #10624 )
...
Currently, `GfxViewportElement` hides DOM blocks outside the viewport using `display: none` to optimize performance. However, this approach presents two issues:
1. Even when hidden, all top-level blocks still undergo frequent CSS transform updates during viewport panning and zooming.
2. Hidden blocks cannot access DOM layout information, preventing `TurboRenderer` from updating the complete canvas bitmap.
To address this, this PR introduces a refactoring that divides all top-level edgeless blocks into two states: `idle` and `active`. The improvements are as follows:
1. Blocks outside the viewport are set to the `idle` state, meaning they no longer update their DOM during viewport panning or zooming. Only `active` blocks within the viewport are updated frame by frame.
2. For `idle` blocks, the hiding method switches from `display: none` to `visibility: hidden`, ensuring their layout information remains accessible to `TurboRenderer`.
[Screen Recording 2025-03-07 at 3.23.56 PM.mov <span class="graphite__hidden">(uploaded via Graphite)</span> <img class="graphite__hidden" src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/lEGcysB4lFTEbCwZ8jMv/4bac640b-f5b6-4b0b-904d-5899f96cf375.mov " />](https://app.graphite.dev/media/video/lEGcysB4lFTEbCwZ8jMv/4bac640b-f5b6-4b0b-904d-5899f96cf375.mov )
While this minimizes DOM updates, it introduces a trade-off: `idle` blocks retain an outdated layout state. Since their positions are updated using a lazy update strategy, their layout state remains frozen at the moment they were last moved out of the viewport:

To resolve this, the PR serializes and stores the viewport field of the block at that moment on the `idle` block itself. This allows the correct layout, positioned in the model coordinate system, to be restored from the stored data.
2025-03-08 01:38:02 +00:00
EYHN
4677049b5c
feat(core): add public user service ( #10695 )
2025-03-07 08:00:27 +00:00
L-Sun
98d44d5be5
refactor(editor): move focus block commands to blocksuite/shared ( #10671 )
2025-03-07 03:07:49 +00:00
L-Sun
62d8c0c7cb
refactor(editor): adjust folder structure for slash menu extension ( #10588 )
...
Close [BS-2743](https://linear.app/affine-design/issue/BS-2743/slash-menu插件化:调整文件夹结构 )
This PR move slash menu from `affine-root-block` to `widget-affine-slash-menu`, and make it as a `WidgetViewExtension`
2025-03-06 16:12:06 +00:00
Saul-Mirone
84e2dda3f8
refactor(editor): separate lit and slot in global ( #10666 )
2025-03-06 10:24:59 +00:00
Saul-Mirone
7ae9daa6f6
refactor(editor): use lodash ( #10657 )
2025-03-06 17:11:12 +08:00
fundon
ec9bd1f383
feat(editor): add toolbar registry extension ( #9572 )
...
### What's Changed!
#### Added
Manage various types of toolbars uniformly in one place.
* `affine-toolbar-widget`
* `ToolbarRegistryExtension`
The toolbar currently supports and handles several scenarios:
1. Select blocks: `BlockSelection`
2. Select text: `TextSelection` or `NativeSelection`
3. Hover a link: `affine-link` and `affine-reference`
#### Removed
Remove redundant toolbar implementations.
* `attachment` toolbar
* `bookmark` toolbar
* `embed` toolbar
* `formatting` toolbar
* `affine-link` toolbar
* `affine-reference` toolbar
### How to migrate?
Here is an example that can help us migrate some unrefactored toolbars:
Check out the more detailed types of [`ToolbarModuleConfig`](c178debf2d/blocksuite/affine/shared/src/services/toolbar-service/config.ts ).
1. Add toolbar configuration file to a block type, such as bookmark block: [`config.ts`](c178debf2d/blocksuite/affine/block-bookmark/src/configs/toolbar.ts )
```ts
export const builtinToolbarConfig = {
actions: [
{
id: 'a.preview',
content(ctx) {
const model = ctx.getCurrentModelBy(BlockSelection, BookmarkBlockModel);
if (!model) return null;
const { url } = model;
return html`<affine-link-preview .url=${url}></affine-link-preview>`;
},
},
{
id: 'b.conversions',
actions: [
{
id: 'inline',
label: 'Inline view',
run(ctx) {
},
},
{
id: 'card',
label: 'Card view',
disabled: true,
},
{
id: 'embed',
label: 'Embed view',
disabled(ctx) {
},
run(ctx) {
},
},
],
content(ctx) {
},
} satisfies ToolbarActionGroup<ToolbarAction>,
{
id: 'c.style',
actions: [
{
id: 'horizontal',
label: 'Large horizontal style',
},
{
id: 'list',
label: 'Small horizontal style',
},
],
content(ctx) {
},
} satisfies ToolbarActionGroup<ToolbarAction>,
{
id: 'd.caption',
tooltip: 'Caption',
icon: CaptionIcon(),
run(ctx) {
},
},
{
placement: ActionPlacement.More,
id: 'a.clipboard',
actions: [
{
id: 'copy',
label: 'Copy',
icon: CopyIcon(),
run(ctx) {
},
},
{
id: 'duplicate',
label: 'Duplicate',
icon: DuplicateIcon(),
run(ctx) {
},
},
],
},
{
placement: ActionPlacement.More,
id: 'b.refresh',
label: 'Reload',
icon: ResetIcon(),
run(ctx) {
},
},
{
placement: ActionPlacement.More,
id: 'c.delete',
label: 'Delete',
icon: DeleteIcon(),
variant: 'destructive',
run(ctx) {
},
},
],
} as const satisfies ToolbarModuleConfig;
```
2. Add configuration extension to a block spec: [bookmark's spec](c178debf2d/blocksuite/affine/block-bookmark/src/bookmark-spec.ts )
```ts
const flavour = BookmarkBlockSchema.model.flavour;
export const BookmarkBlockSpec: ExtensionType[] = [
...,
ToolbarModuleExtension({
id: BlockFlavourIdentifier(flavour),
config: builtinToolbarConfig,
}),
].flat();
```
3. If the bock type already has a toolbar configuration built in, we can customize it in the following ways:
Check out the [editor's config](c178debf2d/packages/frontend/core/src/blocksuite/extensions/editor-config/index.ts (L51C4-L54C8) ) file.
```ts
// Defines a toolbar configuration for the bookmark block type
const customBookmarkToolbarConfig = {
actions: [
...
]
} as const satisfies ToolbarModuleConfig;
// Adds it into the editor's config
ToolbarModuleExtension({
id: BlockFlavourIdentifier('custom:affine:bookmark'),
config: customBookmarkToolbarConfig,
}),
```
4. If we want to extend the global:
```ts
// Defines a toolbar configuration
const customWildcardToolbarConfig = {
actions: [
...
]
} as const satisfies ToolbarModuleConfig;
// Adds it into the editor's config
ToolbarModuleExtension({
id: BlockFlavourIdentifier('custom:affine:*'),
config: customWildcardToolbarConfig,
}),
```
Currently, only most toolbars in page mode have been refactored. Next is edgeless mode.
2025-03-06 06:46:03 +00:00
Saul-Mirone
2b30d756e2
refactor(editor): replace debounce and throttle with lodash ( #10639 )
2025-03-06 04:46:52 +00:00
Saul-Mirone
7e39893aac
refactor(editor): remove assert functions ( #10629 )
2025-03-05 10:20:02 +00:00
EYHN
201c3438ba
feat(core): add user list service for blocksuite ( #10627 )
2025-03-05 10:06:14 +00:00
Flrande
bd62634a76
feat(editor): add callout block ( #10563 )
...
- Add `CalloutBlockModel `
- Implement `CalloutBlockComponent `
- Integrate with slash menu (/)
2025-03-05 09:28:51 +00:00
Saul-Mirone
b8ecfbdae6
refactor(editor): remove assertExists ( #10615 )
2025-03-05 00:13:08 +00:00
Saul-Mirone
66d9d576e0
refactor(editor): add gfx entry in bs global package ( #10612 )
2025-03-04 12:46:50 +00:00
Saul-Mirone
5ad3d3c94a
refactor(editor): move figma squircle to shared ( #10610 )
2025-03-04 10:26:58 +00:00
Saul-Mirone
4c3c953a36
chore(editor): merge clamp functions ( #10577 )
...
Closes: [BS-2625](https://linear.app/affine-design/issue/BS-2625/合并clamp函数 )
2025-03-03 10:33:38 +00:00
Saul-Mirone
a587abca85
feat(editor): add block meta service ( #10561 )
2025-03-03 06:13:06 +00:00
Saul-Mirone
4eae3cc66a
feat(editor): add user list services ( #10555 )
2025-03-03 03:15:00 +00:00
doodlewind
bd1b26a230
feat(editor): support zooming placeholder in turbo renderer ( #10504 )
...
[Screen Recording 2025-02-28 at 4.32.20 PM.mov <span class="graphite__hidden">(uploaded via Graphite)</span> <img class="graphite__hidden" src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/lEGcysB4lFTEbCwZ8jMv/6c08b827-8428-42f3-aa7a-a2366756bd16.mov " />](https://app.graphite.dev/media/video/lEGcysB4lFTEbCwZ8jMv/6c08b827-8428-42f3-aa7a-a2366756bd16.mov )
This prevents painting task backlog during zooming by adding a fast placeholder painter, with a `zooming` state in renderer state machine.
2025-03-03 02:07:46 +00:00
Saul-Mirone
fdde818ddd
feat(editor): add block meta feature flag ( #10548 )
2025-03-03 01:45:33 +00:00
Saul-Mirone
f23f29610c
refactor(editor): remove stable feature flags ( #10547 )
2025-03-02 08:41:12 +00:00
Saul-Mirone
6353e72078
feat(editor): add embed option config extension ( #10540 )
...
This PR implements a significant refactoring of the embed block services across multiple components (Figma, GitHub, Loom, and YouTube) in the BlockSuite codebase. Here are the key changes:
1. **Architecture Change**:
- Moves from a dynamic registration pattern to a more declarative configuration approach
- Replaces `EmbedOptionProvider.registerEmbedBlockOptions()` calls with a new `EmbedOptionConfig` factory function
2. **Service Refactoring**:
- For each embed block type (Figma, GitHub, Loom, YouTube):
- Separates configuration from service logic
- Creates new `EmbedBlockOptionConfig` constants using the new `EmbedOptionConfig` factory
- Removes the `mounted()` lifecycle hook from services where it was only used for registration
3. **Core Changes**:
- In `embed-option-service.ts`:
- Introduces new `EmbedOptionConfigIdentifier` for dependency injection
- Adds `EmbedOptionConfig` factory function for creating embed configurations
- Updates `EmbedOptionService` constructor to automatically register configurations
- Modifies DI setup to include `StdIdentifier` dependency
4. **Spec Updates**:
- Updates all block specs to include the new configuration objects
- Maintains existing functionality while using the new pattern
The main benefit of this refactoring is:
- More declarative configuration approach
- Better separation of concerns
- Reduced runtime registration code
- More predictable initialization of embed options
2025-03-01 16:40:18 +00:00
doodlewind
12acf7e4a0
fix(editor): range IndexSizeError on firefox ( #10519 )
...
This is a Firefox compat issue, I've manually confirmed this implementation works in latest Firefox build.
2025-02-28 11:50:56 +00:00
L-Sun
d476d3b1df
fix(editor): android keyboard can not be opened ( #10502 )
...
Close [BS-2674](https://linear.app/affine-design/issue/BS-2674/[android]-%E6%96%87%E6%9C%AC%E7%BC%96%E8%BE%91%E5%8C%BA%E5%9F%9F%E7%82%B9%E5%87%BB%E5%90%8E%E6%97%A0%E6%B3%95%E6%BF%80%E6%B4%BB%E9%94%AE%E7%9B%98 ) [BS-2609](https://linear.app/affine-design/issue/BS-2609/[android]-%E8%BE%93%E5%85%A5%E7%9A%84-toolbar-%E6%B2%A1%E6%9C%89%E4%BA%86 )
2025-02-28 08:23:26 +00:00
doodlewind
f1df774188
fix(editor): invalidate turbo renderer on selection update ( #10499 )
2025-02-28 08:08:28 +00:00
L-Sun
b995b4f965
chore(editor): update page block telemetry ( #10487 )
...
Close [BS-2711](https://linear.app/affine-design/issue/BS-2711/page-block埋点更新 )
2025-02-27 09:58:58 +00:00
Saul-Mirone
4c736bc190
feat(editor): type safe draft model and transformer ( #10486 )
2025-02-27 09:19:49 +00:00
doodlewind
8cec22cc64
fix(editor): handle resize in turbo renderer ( #10465 )
2025-02-27 04:18:37 +00:00
Yifeng Wang
e9484e8e15
refactor(editor): remove non null asserts in turbo renderer ( #10454 )
2025-02-27 10:47:26 +08:00
doodlewind
3252dd7a31
feat(editor): automatically hide canvas optimized blocks ( #10451 )
...
Qualified DOM blocks can now be optimized away automatically.
<img alt="image.png" width="500" src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/lEGcysB4lFTEbCwZ8jMv/102bf813-154a-4816-9eb0-2c9c0ce01fe7.png ">
Since this is under development, verifying state correctness is more important than rendering details.
This won't affect current production version since the passive APIs added to `GfxViewportElement` are disabled by the `enableOptimization = false` config.
2025-02-27 02:10:49 +00:00
Saul-Mirone
fd6d96a38e
refactor(editor): use transformer from store when possible ( #10453 )
2025-02-26 14:15:04 +00:00
doodlewind
0f8c837fbe
refactor(editor): simplify renderer state ( #10441 )
...
The redundant `monitoring` state has been removed. The new `ready` state is used for querying if DOM elements can be safely optimized away.
2025-02-26 08:39:58 +00:00
doodlewind
e38e59d4e5
refactor(editor): request refresh after finding stale bitmap ( #10438 )
...
This ensures a bitmap will be eventually generated after tile got invalidated.
2025-02-26 06:49:09 +00:00