refactor(editor): move mini mindmap to ai module (#9497)

This commit is contained in:
Saul-Mirone 2025-01-03 03:34:56 +00:00
parent cbc84ff672
commit 30a181da38
No known key found for this signature in database
GPG Key ID: 0D941B4A9125B742
19 changed files with 85 additions and 210 deletions

View File

@ -181,11 +181,6 @@ import {
AFFINE_VIEWPORT_OVERLAY_WIDGET, AFFINE_VIEWPORT_OVERLAY_WIDGET,
AffineViewportOverlayWidget, AffineViewportOverlayWidget,
} from './root-block/widgets/viewport-overlay/viewport-overlay.js'; } from './root-block/widgets/viewport-overlay/viewport-overlay.js';
import {
MindmapRootBlock,
MindmapSurfaceBlock,
MiniMindmapPreview,
} from './surface-block/mini-mindmap/index.js';
export function effects() { export function effects() {
registerSpecs(); registerSpecs();
@ -232,8 +227,6 @@ export function effects() {
customElements.define('affine-page-root', PageRootBlockComponent); customElements.define('affine-page-root', PageRootBlockComponent);
customElements.define('affine-preview-root', PreviewRootBlockComponent); customElements.define('affine-preview-root', PreviewRootBlockComponent);
customElements.define('mini-mindmap-preview', MiniMindmapPreview);
customElements.define('mini-mindmap-surface-block', MindmapSurfaceBlock);
customElements.define('affine-edgeless-root', EdgelessRootBlockComponent); customElements.define('affine-edgeless-root', EdgelessRootBlockComponent);
customElements.define('edgeless-copilot-panel', EdgelessCopilotPanel); customElements.define('edgeless-copilot-panel', EdgelessCopilotPanel);
customElements.define( customElements.define(
@ -371,7 +364,6 @@ export function effects() {
); );
customElements.define('edgeless-text-editor', EdgelessTextEditor); customElements.define('edgeless-text-editor', EdgelessTextEditor);
customElements.define('affine-image-toolbar', AffineImageToolbar); customElements.define('affine-image-toolbar', AffineImageToolbar);
customElements.define('mini-mindmap-root-block', MindmapRootBlock);
customElements.define('affine-block-selection', BlockSelection); customElements.define('affine-block-selection', BlockSelection);
customElements.define('edgeless-slide-menu', EdgelessSlideMenu); customElements.define('edgeless-slide-menu', EdgelessSlideMenu);
customElements.define( customElements.define(

View File

@ -33,11 +33,6 @@ export { EditPropsMiddlewareBuilder } from './root-block/edgeless/middlewares/ba
export { EdgelessSnapManager } from './root-block/edgeless/utils/snap-manager.js'; export { EdgelessSnapManager } from './root-block/edgeless/utils/snap-manager.js';
export * from './root-block/index.js'; export * from './root-block/index.js';
export * from './schemas.js'; export * from './schemas.js';
export {
markdownToMindmap,
MindmapSurfaceBlock,
MiniMindmapPreview,
} from './surface-block/mini-mindmap/index.js';
export * from '@blocksuite/affine-block-attachment'; export * from '@blocksuite/affine-block-attachment';
export * from '@blocksuite/affine-block-bookmark'; export * from '@blocksuite/affine-block-bookmark';
export * from '@blocksuite/affine-block-code'; export * from '@blocksuite/affine-block-code';

View File

@ -1,4 +0,0 @@
export { markdownToMindmap, MiniMindmapPreview } from './mindmap-preview.js';
export { MindmapRootBlock } from './mindmap-root-block.js';
export { MindmapService } from './mindmap-service.js';
export { MindmapSurfaceBlock } from './surface-block.js';

View File

@ -1,117 +0,0 @@
import type { EditorHost } from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import {
type EdgelessRootBlockComponent,
type MindmapElementModel,
type MindmapSurfaceBlock,
MiniMindmapPreview,
} from '@blocksuite/blocks';
import { beforeEach, describe, expect, test } from 'vitest';
import { wait } from '../utils/common.js';
import { getDocRootBlock } from '../utils/edgeless.js';
import { setupEditor } from '../utils/setup.js';
describe('mini mindmap preview', () => {
let edgeless!: EdgelessRootBlockComponent;
const createPreview = (host: EditorHost, answer: string) => {
const mindmapPreview = new MiniMindmapPreview();
mindmapPreview.answer = answer;
mindmapPreview.host = host;
mindmapPreview.ctx = {
get() {
return {};
},
set() {},
};
document.body.append(mindmapPreview);
return mindmapPreview;
};
beforeEach(async () => {
const cleanup = await setupEditor('edgeless');
edgeless = getDocRootBlock(doc, editor, 'edgeless');
edgeless.gfx.tool.setTool('default');
return cleanup;
});
test('mini mindmap basic', async () => {
const mindmapAnswer = `
- Mindmap
- Node 1
- Node 1.1
- Node 1.2
- Node 2
- Node 2.1
- Node 2.2
`;
const miniMindMapPreview = createPreview(
window.editor.host!,
mindmapAnswer
);
await wait(50);
const miniMindMapSurface = miniMindMapPreview.renderRoot.querySelector(
'mini-mindmap-surface-block'
) as MindmapSurfaceBlock;
// model-related properties
expect(miniMindMapPreview.mindmapId).toBeDefined();
expect(miniMindMapPreview.portalHost).toBeDefined();
expect(miniMindMapPreview.doc).toBeDefined();
expect(miniMindMapPreview.surface).toBeDefined();
expect(miniMindMapPreview.surface!.elementModels.length).toBe(8);
// renderer
expect(miniMindMapSurface.renderer).toBeDefined();
expect(miniMindMapSurface.renderer?.canvas.isConnected).toBe(true);
expect(miniMindMapSurface.renderer?.canvas.width).toBeGreaterThan(0);
expect(miniMindMapSurface.renderer?.canvas.height).toBeGreaterThan(0);
return () => {
miniMindMapPreview.remove();
};
});
test('mini mindmap should layout automatically', async () => {
const mindmapAnswer = `
- Main node
- Child node
- Second child node
- Third child node
`;
const miniMindMapPreview = createPreview(
window.editor.host!,
mindmapAnswer
);
await wait(50);
const gfx = miniMindMapPreview.portalHost.std.get(GfxControllerIdentifier);
const mindmap = gfx.surface!.elementModels.filter(
model => model.type === 'mindmap'
)[0] as MindmapElementModel;
const [child1, child2, child3] = mindmap.tree.children;
const root = mindmap.tree;
expect(mindmap).not.toBeUndefined();
expect(root.children.length).toBe(3);
expect(root.element.x).toBeLessThan(child1.element.x);
// children should be aligned horizontally
expect(child1.element.x).toBe(child2.element.x);
expect(child2.element.x).toBe(child3.element.x);
// children
expect(child1.element.y + child1.element.h).toBeLessThan(child2.element.y);
expect(child2.element.y + child2.element.h).toBeLessThan(child3.element.y);
});
});

View File

@ -1,16 +0,0 @@
import type { Doc, DocCollection, Job } from '@blocksuite/store';
import type { AffineEditorContainer } from '../index.js';
declare global {
const editor: AffineEditorContainer;
const doc: Doc;
const collection: DocCollection;
const job: Job;
interface Window {
editor: AffineEditorContainer;
doc: Doc;
job: Job;
collection: DocCollection;
}
}

View File

@ -1,5 +1,5 @@
import { effects as blocksEffects } from '@blocksuite/blocks/effects'; import { effects as blocksEffects } from '@blocksuite/blocks/effects';
import type { BlockCollection } from '@blocksuite/store'; import type { BlockCollection, Doc, Job } from '@blocksuite/store';
import { effects } from '../../effects.js'; import { effects } from '../../effects.js';
@ -109,3 +109,16 @@ export function cleanup() {
delete (window as any).doc; delete (window as any).doc;
} }
declare global {
const editor: AffineEditorContainer;
const doc: Doc;
const collection: DocCollection;
const job: Job;
interface Window {
editor: AffineEditorContainer;
doc: Doc;
job: Job;
collection: DocCollection;
}
}

View File

@ -1,8 +1,8 @@
import '@blocksuite/affine-block-surface/effects'; import '@blocksuite/affine-block-surface/effects';
export * from './editors/index.js'; export * from './editors';
export * from './fragments/index.js'; export * from './fragments';
export * from './helpers/index.js'; export * from './helpers';
const env = const env =
typeof globalThis !== 'undefined' typeof globalThis !== 'undefined'

View File

@ -2,16 +2,13 @@ import './action-wrapper';
import type { EditorHost } from '@blocksuite/affine/block-std'; import type { EditorHost } from '@blocksuite/affine/block-std';
import { ShadowlessElement } from '@blocksuite/affine/block-std'; import { ShadowlessElement } from '@blocksuite/affine/block-std';
import { MiniMindmapPreview } from '@blocksuite/affine/blocks'; import { WithDisposable } from '@blocksuite/affine/global/utils';
import { noop, WithDisposable } from '@blocksuite/affine/global/utils';
import { html } from 'lit'; import { html } from 'lit';
import { property } from 'lit/decorators.js'; import { property } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js'; import { styleMap } from 'lit/directives/style-map.js';
import type { ChatAction } from '../chat-context'; import type { ChatAction } from '../chat-context';
noop(MiniMindmapPreview);
export class ActionMindmap extends WithDisposable(ShadowlessElement) { export class ActionMindmap extends WithDisposable(ShadowlessElement) {
@property({ attribute: false }) @property({ attribute: false })
accessor item!: ChatAction; accessor item!: ChatAction;

View File

@ -3,18 +3,12 @@ import type {
AffineAIPanelWidgetConfig, AffineAIPanelWidgetConfig,
MindmapStyle, MindmapStyle,
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
import {
markdownToMindmap,
MiniMindmapPreview,
} from '@blocksuite/affine/blocks';
import { noop } from '@blocksuite/affine/global/utils';
import { html, nothing } from 'lit'; import { html, nothing } from 'lit';
import { markdownToMindmap } from '../mini-mindmap';
import { getAIPanelWidget } from '../utils/ai-widgets'; import { getAIPanelWidget } from '../utils/ai-widgets';
import type { AIContext } from '../utils/context'; import type { AIContext } from '../utils/context';
noop(MiniMindmapPreview);
export const createMindmapRenderer: ( export const createMindmapRenderer: (
host: EditorHost, host: EditorHost,
/** /**

View File

@ -1,11 +1,13 @@
import { Container } from '@blocksuite/global/di'; import {
import { DocCollection, Schema } from '@blocksuite/store'; defaultBlockMarkdownAdapterMatchers,
inlineDeltaToMarkdownAdapterMatchers,
markdownInlineToDeltaMatchers,
} from '@blocksuite/affine/blocks';
import { Container } from '@blocksuite/affine/global/di';
import { DocCollection, Schema } from '@blocksuite/affine/store';
import { describe, expect, test } from 'vitest'; import { describe, expect, test } from 'vitest';
import { defaultBlockMarkdownAdapterMatchers } from '../../_common/adapters/index.js'; import { markdownToMindmap } from '../mindmap-preview.js';
import { inlineDeltaToMarkdownAdapterMatchers } from '../../_common/adapters/markdown/delta-converter/inline-delta.js';
import { markdownInlineToDeltaMatchers } from '../../_common/adapters/markdown/delta-converter/markdown-inline.js';
import { markdownToMindmap } from '../../surface-block/mini-mindmap/mindmap-preview.js';
const container = new Container(); const container = new Container();
[ [

View File

@ -0,0 +1,14 @@
import { MiniMindmapPreview } from './mindmap-preview.js';
import { MindmapRootBlock } from './mindmap-root-block.js';
import { MindmapSurfaceBlock } from './surface-block.js';
export { markdownToMindmap, MiniMindmapPreview } from './mindmap-preview.js';
export { MindmapRootBlock } from './mindmap-root-block.js';
export { MindmapService } from './mindmap-service.js';
export { MindmapSurfaceBlock } from './surface-block.js';
export function registerMiniMindmapBlocks() {
customElements.define('mini-mindmap-root-block', MindmapRootBlock);
customElements.define('mini-mindmap-preview', MiniMindmapPreview);
customElements.define('mini-mindmap-surface-block', MindmapSurfaceBlock);
}

View File

@ -1,18 +1,16 @@
import type { SurfaceBlockModel } from '@blocksuite/affine-block-surface'; import { BlockStdScope, type EditorHost } from '@blocksuite/affine/block-std';
import { import {
MarkdownAdapter,
type MindmapElementModel,
MindmapStyle,
MindmapStyleFour, MindmapStyleFour,
MindmapStyleOne, MindmapStyleOne,
MindmapStyleThree, MindmapStyleThree,
MindmapStyleTwo, MindmapStyleTwo,
} from '@blocksuite/affine-components/icons'; type SurfaceBlockModel,
import { } from '@blocksuite/affine/blocks';
type MindmapElementModel, import type { ServiceProvider } from '@blocksuite/affine/global/di';
MindmapStyle, import { WithDisposable } from '@blocksuite/affine/global/utils';
} from '@blocksuite/affine-model';
import { MarkdownAdapter } from '@blocksuite/affine-shared/adapters';
import { BlockStdScope, type EditorHost } from '@blocksuite/block-std';
import type { ServiceProvider } from '@blocksuite/global/di';
import { WithDisposable } from '@blocksuite/global/utils';
import { import {
type Doc, type Doc,
DocCollection, DocCollection,
@ -20,7 +18,7 @@ import {
IdGeneratorType, IdGeneratorType,
Job, Job,
Schema, Schema,
} from '@blocksuite/store'; } from '@blocksuite/affine/store';
import { css, html, LitElement, nothing } from 'lit'; import { css, html, LitElement, nothing } from 'lit';
import { property, query } from 'lit/decorators.js'; import { property, query } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js'; import { repeat } from 'lit/directives/repeat.js';
@ -133,7 +131,9 @@ export class MiniMindmapPreview extends WithDisposable(LitElement) {
} }
this.doc.transact(() => { this.doc.transact(() => {
this._mindmap!.style = style; if (this._mindmap) {
this._mindmap.style = style;
}
}); });
this.ctx.set({ style }); this.ctx.set({ style });

View File

@ -1,5 +1,5 @@
import type { RootBlockModel } from '@blocksuite/affine-model'; import { BlockComponent } from '@blocksuite/affine/block-std';
import { BlockComponent } from '@blocksuite/block-std'; import type { RootBlockModel } from '@blocksuite/affine/blocks';
import { html } from 'lit'; import { html } from 'lit';
export class MindmapRootBlock extends BlockComponent<RootBlockModel> { export class MindmapRootBlock extends BlockComponent<RootBlockModel> {

View File

@ -1,6 +1,6 @@
import { RootBlockSchema } from '@blocksuite/affine-model'; import { BlockService } from '@blocksuite/affine/block-std';
import { BlockService } from '@blocksuite/block-std'; import { RootBlockSchema } from '@blocksuite/affine/blocks';
import { Slot } from '@blocksuite/global/utils'; import { Slot } from '@blocksuite/affine/global/utils';
export class MindmapService extends BlockService { export class MindmapService extends BlockService {
static override readonly flavour = RootBlockSchema.model.flavour; static override readonly flavour = RootBlockSchema.model.flavour;

View File

@ -1,18 +1,16 @@
import {
MindMapView,
SurfaceBlockSchema,
} from '@blocksuite/affine-block-surface';
import { RootBlockSchema } from '@blocksuite/affine-model';
import {
DocModeService,
ThemeService,
} from '@blocksuite/affine-shared/services';
import { import {
BlockViewExtension, BlockViewExtension,
type ExtensionType, type ExtensionType,
FlavourExtension, FlavourExtension,
} from '@blocksuite/block-std'; } from '@blocksuite/affine/block-std';
import type { BlockSchema } from '@blocksuite/store'; import {
DocModeService,
MindMapView,
RootBlockSchema,
SurfaceBlockSchema,
ThemeService,
} from '@blocksuite/affine/blocks';
import type { BlockSchema } from '@blocksuite/affine/store';
import { literal } from 'lit/static-html.js'; import { literal } from 'lit/static-html.js';
import type { z } from 'zod'; import type { z } from 'zod';

View File

@ -1,15 +1,18 @@
/* oxlint-disable @typescript-eslint/no-non-null-assertion */ /* oxlint-disable @typescript-eslint/no-non-null-assertion */
import type { SurfaceBlockModel } from '@blocksuite/affine-block-surface'; import { BlockComponent } from '@blocksuite/affine/block-std';
import { GfxControllerIdentifier } from '@blocksuite/affine/block-std/gfx';
import type {
Color,
ShapeElementModel,
SurfaceBlockModel,
} from '@blocksuite/affine/blocks';
import { import {
CanvasRenderer, CanvasRenderer,
elementRenderers, elementRenderers,
fitContent, fitContent,
} from '@blocksuite/affine-block-surface'; ThemeProvider,
import type { Color, ShapeElementModel } from '@blocksuite/affine-model'; } from '@blocksuite/affine/blocks';
import { ThemeProvider } from '@blocksuite/affine-shared/services'; import type { Bound } from '@blocksuite/affine/global/utils';
import { BlockComponent } from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import type { Bound } from '@blocksuite/global/utils';
import { html } from 'lit'; import { html } from 'lit';
import { query } from 'lit/decorators.js'; import { query } from 'lit/decorators.js';

View File

@ -1,5 +1,5 @@
import { SurfaceBlockSchema } from '@blocksuite/affine-block-surface'; import { BlockService } from '@blocksuite/affine/block-std';
import { BlockService } from '@blocksuite/block-std'; import { SurfaceBlockSchema } from '@blocksuite/affine/blocks';
export class MindmapSurfaceBlockService extends BlockService { export class MindmapSurfaceBlockService extends BlockService {
static override readonly flavour = SurfaceBlockSchema.model.flavour; static override readonly flavour = SurfaceBlockSchema.model.flavour;

View File

@ -21,6 +21,7 @@ import { ChatPanelMessages } from './ai/chat-panel/chat-panel-messages';
import { AIErrorWrapper } from './ai/messages/error'; import { AIErrorWrapper } from './ai/messages/error';
import { AISlidesRenderer } from './ai/messages/slides-renderer'; import { AISlidesRenderer } from './ai/messages/slides-renderer';
import { AIAnswerWrapper } from './ai/messages/wrapper'; import { AIAnswerWrapper } from './ai/messages/wrapper';
import { registerMiniMindmapBlocks } from './ai/mini-mindmap';
import { ChatBlockInput } from './ai/peek-view/chat-block-input'; import { ChatBlockInput } from './ai/peek-view/chat-block-input';
import { AIChatBlockPeekView } from './ai/peek-view/chat-block-peek-view'; import { AIChatBlockPeekView } from './ai/peek-view/chat-block-peek-view';
import { DateTime } from './ai/peek-view/date-time'; import { DateTime } from './ai/peek-view/date-time';
@ -38,6 +39,7 @@ import { ImagePlaceholder } from './blocks/ai-chat-block/components/image-placeh
import { UserInfo } from './blocks/ai-chat-block/components/user-info'; import { UserInfo } from './blocks/ai-chat-block/components/user-info';
export function registerBlocksuitePresetsCustomComponents() { export function registerBlocksuitePresetsCustomComponents() {
registerMiniMindmapBlocks();
customElements.define('ask-ai-icon', AskAIIcon); customElements.define('ask-ai-icon', AskAIIcon);
customElements.define('ask-ai-button', AskAIButton); customElements.define('ask-ai-button', AskAIButton);
customElements.define('ask-ai-toolbar-button', AskAIToolbarButton); customElements.define('ask-ai-toolbar-button', AskAIToolbarButton);

View File

@ -1,5 +1,7 @@
export default [ import { defineWorkspace } from 'vitest/config';
export default defineWorkspace([
'.', '.',
'./packages/frontend/apps/electron', './packages/frontend/apps/electron',
'./blocksuite/**/*/vitest.config.ts', './blocksuite/**/*/vitest.config.ts',
]; ]);