mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-18 06:47:02 +08:00
refactor(editor): optimize ai code structure (#10381)
Let me analyze this diff and provide a clear description of the changes.
This PR introduces several significant changes focused on AI integration and code organization in the AFFiNE codebase:
1. **Enhanced SpecBuilder Functionality** (`blocksuite/affine/shared/src/utils/spec/spec-builder.ts`):
- Added method chaining by returning `this` from `extend`, `omit`, and `replace` methods
- Added new utility methods:
- `hasAll(target: ExtensionType[])`: Checks if all specified extensions exist
- `hasOneOf(target: ExtensionType[])`: Checks if at least one specified extension exists
2. **AI Extensions Modularization**:
- Split the large AI-related code into separate modular files under `packages/frontend/core/src/blocksuite/ai/extensions/`:
- `ai-code.ts`: Code block AI integration
- `ai-edgeless-root.ts`: Edgeless mode AI features
- `ai-image.ts`: Image block AI capabilities
- `ai-page-root.ts`: Page root AI integration
- `ai-paragraph.ts`: Paragraph block AI features
- `enable-ai.ts`: Central AI extension enablement logic
3. **Widget Improvements**:
- Enhanced `AffineAIPanelWidget` and `EdgelessCopilotWidget` with proper widget extensions
- Moved widget-specific extensions into their respective files
- Added proper type definitions and component registrations
4. **Code Organization**:
- Simplified exports in `index.ts`
- Better separation of concerns between different AI-related components
- More modular approach to AI feature integration
5. **AI Integration Architecture**:
- Introduced a new `enableAIExtension` function that handles:
- Replacing standard blocks with AI-enhanced versions
- Conditional enabling of AI features based on the current spec configuration
- Extension of AI chat capabilities
The changes primarily focus on improving code organization, maintainability, and the architecture of AI feature integration in the AFFiNE editor. The modularization will make it easier to maintain and extend AI capabilities across different block types and editor modes.
This commit is contained in:
@@ -6,19 +6,19 @@ import {
|
||||
PageViewportServiceExtension,
|
||||
ThemeService,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { dragHandleWidget } from '@blocksuite/affine-widget-drag-handle';
|
||||
import { docRemoteSelectionWidget } from '@blocksuite/affine-widget-remote-selection';
|
||||
import { scrollAnchoringWidget } from '@blocksuite/affine-widget-scroll-anchoring';
|
||||
import { FlavourExtension } from '@blocksuite/block-std';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
|
||||
import { RootBlockAdapterExtensions } from '../adapters/extension';
|
||||
import {
|
||||
docRemoteSelectionWidget,
|
||||
dragHandleWidget,
|
||||
embedCardToolbarWidget,
|
||||
formatBarWidget,
|
||||
innerModalWidget,
|
||||
linkedDocWidget,
|
||||
modalWidget,
|
||||
scrollAnchoringWidget,
|
||||
slashMenuWidget,
|
||||
viewportOverlayWidget,
|
||||
} from './widgets';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { AFFINE_DRAG_HANDLE_WIDGET } from '@blocksuite/affine-widget-drag-handle';
|
||||
import { AFFINE_DOC_REMOTE_SELECTION_WIDGET } from '@blocksuite/affine-widget-remote-selection';
|
||||
import { AFFINE_SCROLL_ANCHORING_WIDGET } from '@blocksuite/affine-widget-scroll-anchoring';
|
||||
import { WidgetViewExtension } from '@blocksuite/block-std';
|
||||
import { literal, unsafeStatic } from 'lit/static-html.js';
|
||||
@@ -32,11 +30,6 @@ export const linkedDocWidget = WidgetViewExtension(
|
||||
AFFINE_LINKED_DOC_WIDGET,
|
||||
literal`${unsafeStatic(AFFINE_LINKED_DOC_WIDGET)}`
|
||||
);
|
||||
export const dragHandleWidget = WidgetViewExtension(
|
||||
'affine:page',
|
||||
AFFINE_DRAG_HANDLE_WIDGET,
|
||||
literal`${unsafeStatic(AFFINE_DRAG_HANDLE_WIDGET)}`
|
||||
);
|
||||
export const embedCardToolbarWidget = WidgetViewExtension(
|
||||
'affine:page',
|
||||
AFFINE_EMBED_CARD_TOOLBAR_WIDGET,
|
||||
@@ -47,11 +40,6 @@ export const formatBarWidget = WidgetViewExtension(
|
||||
AFFINE_FORMAT_BAR_WIDGET,
|
||||
literal`${unsafeStatic(AFFINE_FORMAT_BAR_WIDGET)}`
|
||||
);
|
||||
export const docRemoteSelectionWidget = WidgetViewExtension(
|
||||
'affine:page',
|
||||
AFFINE_DOC_REMOTE_SELECTION_WIDGET,
|
||||
literal`${unsafeStatic(AFFINE_DOC_REMOTE_SELECTION_WIDGET)}`
|
||||
);
|
||||
export const viewportOverlayWidget = WidgetViewExtension(
|
||||
'affine:page',
|
||||
AFFINE_VIEWPORT_OVERLAY_WIDGET,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { AFFINE_EDGELESS_AUTO_CONNECT_WIDGET } from '@blocksuite/affine-widget-edgeless-auto-connect';
|
||||
import { AFFINE_FRAME_TITLE_WIDGET } from '@blocksuite/affine-widget-frame-title';
|
||||
import { AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET } from '@blocksuite/affine-widget-remote-selection';
|
||||
import { autoConnectWidget } from '@blocksuite/affine-widget-edgeless-auto-connect';
|
||||
import { frameTitleWidget } from '@blocksuite/affine-widget-frame-title';
|
||||
import { edgelessRemoteSelectionWidget } from '@blocksuite/affine-widget-remote-selection';
|
||||
import {
|
||||
BlockServiceWatcher,
|
||||
BlockViewExtension,
|
||||
@@ -20,31 +20,16 @@ import { EDGELESS_SELECTED_RECT_WIDGET } from './components/rects/edgeless-selec
|
||||
import { EDGELESS_TOOLBAR_WIDGET } from './components/toolbar/edgeless-toolbar.js';
|
||||
import { EdgelessRootService } from './edgeless-root-service.js';
|
||||
|
||||
export const edgelessRemoteSelectionWidget = WidgetViewExtension(
|
||||
'affine:page',
|
||||
AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET,
|
||||
literal`${unsafeStatic(AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET)}`
|
||||
);
|
||||
export const edgelessZoomToolbarWidget = WidgetViewExtension(
|
||||
'affine:page',
|
||||
AFFINE_EDGELESS_ZOOM_TOOLBAR_WIDGET,
|
||||
literal`${unsafeStatic(AFFINE_EDGELESS_ZOOM_TOOLBAR_WIDGET)}`
|
||||
);
|
||||
export const frameTitleWidget = WidgetViewExtension(
|
||||
'affine:page',
|
||||
AFFINE_FRAME_TITLE_WIDGET,
|
||||
literal`${unsafeStatic(AFFINE_FRAME_TITLE_WIDGET)}`
|
||||
);
|
||||
export const elementToolbarWidget = WidgetViewExtension(
|
||||
'affine:page',
|
||||
EDGELESS_ELEMENT_TOOLBAR_WIDGET,
|
||||
literal`${unsafeStatic(EDGELESS_ELEMENT_TOOLBAR_WIDGET)}`
|
||||
);
|
||||
export const autoConnectWidget = WidgetViewExtension(
|
||||
'affine:page',
|
||||
AFFINE_EDGELESS_AUTO_CONNECT_WIDGET,
|
||||
literal`${unsafeStatic(AFFINE_EDGELESS_AUTO_CONNECT_WIDGET)}`
|
||||
);
|
||||
export const edgelessDraggingAreaWidget = WidgetViewExtension(
|
||||
'affine:page',
|
||||
EDGELESS_DRAGGING_AREA_WIDGET,
|
||||
|
||||
@@ -13,10 +13,20 @@ export class SpecBuilder {
|
||||
|
||||
extend(extensions: ExtensionType[]) {
|
||||
this._value = [...this._value, ...extensions];
|
||||
return this;
|
||||
}
|
||||
|
||||
omit(target: ExtensionType) {
|
||||
this._value = this._value.filter(extension => extension !== target);
|
||||
return this;
|
||||
}
|
||||
|
||||
hasAll(target: ExtensionType[]) {
|
||||
return target.every(t => this._value.includes(t));
|
||||
}
|
||||
|
||||
hasOneOf(target: ExtensionType[]) {
|
||||
return target.some(t => this._value.includes(t));
|
||||
}
|
||||
|
||||
replace(target: ExtensionType[], newExtension: ExtensionType[]) {
|
||||
@@ -24,5 +34,6 @@ export class SpecBuilder {
|
||||
...this._value.filter(extension => !target.includes(extension)),
|
||||
...newExtension,
|
||||
];
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
import { WidgetViewExtension } from '@blocksuite/block-std';
|
||||
import { literal, unsafeStatic } from 'lit/static-html.js';
|
||||
|
||||
import { AFFINE_DRAG_HANDLE_WIDGET } from './consts';
|
||||
|
||||
export * from './consts';
|
||||
export * from './drag-handle';
|
||||
export * from './utils';
|
||||
export type { DragBlockPayload } from './watchers/drag-event-watcher';
|
||||
|
||||
export const dragHandleWidget = WidgetViewExtension(
|
||||
'affine:page',
|
||||
AFFINE_DRAG_HANDLE_WIDGET,
|
||||
literal`${unsafeStatic(AFFINE_DRAG_HANDLE_WIDGET)}`
|
||||
);
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
} from '@blocksuite/affine-model';
|
||||
import { FeatureFlagService } from '@blocksuite/affine-shared/services';
|
||||
import { matchModels, stopPropagation } from '@blocksuite/affine-shared/utils';
|
||||
import { WidgetComponent } from '@blocksuite/block-std';
|
||||
import { WidgetComponent, WidgetViewExtension } from '@blocksuite/block-std';
|
||||
import {
|
||||
type GfxController,
|
||||
GfxControllerIdentifier,
|
||||
@@ -28,6 +28,7 @@ import { css, html, nothing, type TemplateResult } from 'lit';
|
||||
import { state } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { literal, unsafeStatic } from 'lit/static-html.js';
|
||||
|
||||
const PAGE_VISIBLE_INDEX_LABEL_WIDTH = 44;
|
||||
const PAGE_VISIBLE_INDEX_LABEL_HEIGHT = 24;
|
||||
@@ -613,6 +614,12 @@ export class EdgelessAutoConnectWidget extends WidgetComponent<RootBlockModel> {
|
||||
private accessor _show = false;
|
||||
}
|
||||
|
||||
export const autoConnectWidget = WidgetViewExtension(
|
||||
'affine:page',
|
||||
AFFINE_EDGELESS_AUTO_CONNECT_WIDGET,
|
||||
literal`${unsafeStatic(AFFINE_EDGELESS_AUTO_CONNECT_WIDGET)}`
|
||||
);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'affine-edgeless-auto-connect-widget': EdgelessAutoConnectWidget;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { FrameBlockModel, type RootBlockModel } from '@blocksuite/affine-model';
|
||||
import { WidgetComponent } from '@blocksuite/block-std';
|
||||
import { WidgetComponent, WidgetViewExtension } from '@blocksuite/block-std';
|
||||
import { html } from 'lit';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { literal, unsafeStatic } from 'lit/static-html.js';
|
||||
|
||||
import type { AffineFrameTitle } from './frame-title.js';
|
||||
|
||||
@@ -36,3 +37,9 @@ export class AffineFrameTitleWidget extends WidgetComponent<RootBlockModel> {
|
||||
}
|
||||
|
||||
export * from './styles.js';
|
||||
|
||||
export const frameTitleWidget = WidgetViewExtension(
|
||||
'affine:page',
|
||||
AFFINE_FRAME_TITLE_WIDGET,
|
||||
literal`${unsafeStatic(AFFINE_FRAME_TITLE_WIDGET)}`
|
||||
);
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
import type * as CommandsType from '@blocksuite/affine-shared/commands';
|
||||
import { WidgetViewExtension } from '@blocksuite/block-std';
|
||||
import { literal, unsafeStatic } from 'lit/static-html.js';
|
||||
|
||||
declare type _GLOBAL_ = typeof CommandsType;
|
||||
import { AFFINE_DOC_REMOTE_SELECTION_WIDGET } from './doc';
|
||||
import { AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET } from './edgeless';
|
||||
|
||||
export * from './doc';
|
||||
export * from './edgeless';
|
||||
|
||||
export const docRemoteSelectionWidget = WidgetViewExtension(
|
||||
'affine:page',
|
||||
AFFINE_DOC_REMOTE_SELECTION_WIDGET,
|
||||
literal`${unsafeStatic(AFFINE_DOC_REMOTE_SELECTION_WIDGET)}`
|
||||
);
|
||||
|
||||
export const edgelessRemoteSelectionWidget = WidgetViewExtension(
|
||||
'affine:page',
|
||||
AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET,
|
||||
literal`${unsafeStatic(AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET)}`
|
||||
);
|
||||
|
||||
@@ -1 +1,12 @@
|
||||
import { WidgetViewExtension } from '@blocksuite/block-std';
|
||||
import { literal, unsafeStatic } from 'lit/static-html.js';
|
||||
|
||||
import { AFFINE_SCROLL_ANCHORING_WIDGET } from './scroll-anchoring.js';
|
||||
|
||||
export * from './scroll-anchoring.js';
|
||||
|
||||
export const scrollAnchoringWidget = WidgetViewExtension(
|
||||
'affine:page',
|
||||
AFFINE_SCROLL_ANCHORING_WIDGET,
|
||||
literal`${unsafeStatic(AFFINE_SCROLL_ANCHORING_WIDGET)}`
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user