mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-25 22:38:56 +08:00
chore: merge blocksuite source code (#9213)
This commit is contained in:
@@ -0,0 +1,297 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import type { SurfaceBlockModel } from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
MindmapStyleFour,
|
||||
MindmapStyleOne,
|
||||
MindmapStyleThree,
|
||||
MindmapStyleTwo,
|
||||
} from '@blocksuite/affine-components/icons';
|
||||
import {
|
||||
type MindmapElementModel,
|
||||
MindmapStyle,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { BlockStdScope, type EditorHost } from '@blocksuite/block-std';
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import {
|
||||
type Doc,
|
||||
DocCollection,
|
||||
type DocCollectionOptions,
|
||||
IdGeneratorType,
|
||||
Job,
|
||||
Schema,
|
||||
} from '@blocksuite/store';
|
||||
import { css, html, LitElement, nothing } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { MarkdownAdapter } from '../../_common/adapters/markdown/index.js';
|
||||
import { MiniMindmapSchema, MiniMindmapSpecs } from './spec.js';
|
||||
|
||||
const mindmapStyles = [
|
||||
[MindmapStyle.ONE, MindmapStyleOne],
|
||||
[MindmapStyle.TWO, MindmapStyleTwo],
|
||||
[MindmapStyle.THREE, MindmapStyleThree],
|
||||
[MindmapStyle.FOUR, MindmapStyleFour],
|
||||
];
|
||||
|
||||
type Unpacked<T> = T extends (infer U)[] ? U : T;
|
||||
|
||||
export class MiniMindmapPreview extends WithDisposable(LitElement) {
|
||||
static override styles = css`
|
||||
mini-mindmap-root-block,
|
||||
mini-mindmap-surface-block,
|
||||
editor-host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.select-template-title {
|
||||
align-self: stretch;
|
||||
|
||||
color: var(
|
||||
--light-textColor-textSecondaryColor,
|
||||
var(--textColor-textSecondaryColor, #8e8d91)
|
||||
);
|
||||
|
||||
font-family: Inter;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.template {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.template-item {
|
||||
box-sizing: border-box;
|
||||
border: 2px solid var(--affine-border-color);
|
||||
border-radius: 4px;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.template-item.active,
|
||||
.template-item:hover {
|
||||
border-color: var(--affine-brand-color);
|
||||
}
|
||||
|
||||
.template-item > svg {
|
||||
display: block;
|
||||
}
|
||||
`;
|
||||
|
||||
doc?: Doc;
|
||||
|
||||
mindmapId?: string;
|
||||
|
||||
surface?: SurfaceBlockModel;
|
||||
|
||||
get _mindmap(): MindmapElementModel | null {
|
||||
return (
|
||||
(this.surface?.getElementById(
|
||||
this.mindmapId || ''
|
||||
) as MindmapElementModel) ?? null
|
||||
);
|
||||
}
|
||||
|
||||
private _createTemporaryDoc() {
|
||||
const schema = new Schema();
|
||||
schema.register(MiniMindmapSchema);
|
||||
const options: DocCollectionOptions = {
|
||||
id: 'MINI_MINDMAP_TEMPORARY',
|
||||
schema,
|
||||
idGenerator: IdGeneratorType.NanoID,
|
||||
awarenessSources: [],
|
||||
};
|
||||
|
||||
const collection = new DocCollection(options);
|
||||
collection.meta.initialize();
|
||||
collection.start();
|
||||
|
||||
const doc = collection.createDoc({ id: 'doc:home' }).load();
|
||||
const rootId = doc.addBlock('affine:page', {});
|
||||
const surfaceId = doc.addBlock('affine:surface', {}, rootId);
|
||||
const surface = doc.getBlockById(surfaceId) as SurfaceBlockModel;
|
||||
doc.resetHistory();
|
||||
|
||||
return {
|
||||
doc,
|
||||
surface,
|
||||
};
|
||||
}
|
||||
|
||||
private _switchStyle(style: MindmapStyle) {
|
||||
if (!this._mindmap || !this.doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.doc.transact(() => {
|
||||
this._mindmap!.style = style;
|
||||
});
|
||||
|
||||
this.ctx.set({ style });
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
private _toMindmapNode(answer: string, doc: Doc) {
|
||||
return markdownToMindmap(answer, doc);
|
||||
}
|
||||
|
||||
override connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
|
||||
const tempDoc = this._createTemporaryDoc();
|
||||
const mindmapNode = this._toMindmapNode(this.answer, tempDoc.doc);
|
||||
|
||||
if (!mindmapNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.doc = tempDoc.doc;
|
||||
this.surface = tempDoc.surface;
|
||||
this.mindmapId = this.surface.addElement({
|
||||
type: 'mindmap',
|
||||
children: mindmapNode,
|
||||
style: this.mindmapStyle ?? MindmapStyle.FOUR,
|
||||
});
|
||||
this.surface.getElementById(this.mindmapId) as MindmapElementModel;
|
||||
|
||||
const centerPosition = this._mindmap?.tree.element.xywh;
|
||||
|
||||
this.ctx.set({
|
||||
node: mindmapNode,
|
||||
style: MindmapStyle.FOUR,
|
||||
centerPosition,
|
||||
});
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (!this.doc || !this.surface || !this._mindmap) return nothing;
|
||||
|
||||
const curStyle = this._mindmap.style;
|
||||
|
||||
return html` <div>
|
||||
<div
|
||||
style=${styleMap({
|
||||
height: this.height + 'px',
|
||||
border: '1px solid var(--affine-border-color)',
|
||||
borderRadius: '4px',
|
||||
})}
|
||||
>
|
||||
${new BlockStdScope({
|
||||
doc: this.doc,
|
||||
extensions: MiniMindmapSpecs,
|
||||
}).render()}
|
||||
</div>
|
||||
|
||||
${this.templateShow
|
||||
? html` <div class="select-template-title">Select template</div>
|
||||
<div class="template">
|
||||
${repeat(
|
||||
mindmapStyles,
|
||||
([style]) => style,
|
||||
([style, icon]) => {
|
||||
return html`<div
|
||||
class=${`template-item ${curStyle === style ? 'active' : ''}`}
|
||||
@click=${() => this._switchStyle(style as MindmapStyle)}
|
||||
>
|
||||
${icon}
|
||||
</div>`;
|
||||
}
|
||||
)}
|
||||
</div>`
|
||||
: nothing}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor answer!: string;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor ctx!: {
|
||||
get(): Record<string, unknown>;
|
||||
set(data: Record<string, unknown>): void;
|
||||
};
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor height = 400;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor host!: EditorHost;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor mindmapStyle: MindmapStyle | undefined = undefined;
|
||||
|
||||
@query('editor-host')
|
||||
accessor portalHost!: EditorHost;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor templateShow = true;
|
||||
}
|
||||
|
||||
type Node = {
|
||||
text: string;
|
||||
children: Node[];
|
||||
};
|
||||
|
||||
export const markdownToMindmap = (answer: string, doc: Doc) => {
|
||||
let result: Node | null = null;
|
||||
const job = new Job({ collection: doc.collection });
|
||||
const markdown = new MarkdownAdapter(job);
|
||||
const ast = markdown['_markdownToAst'](answer);
|
||||
const traverse = (
|
||||
markdownNode: Unpacked<(typeof ast)['children']>,
|
||||
firstLevel = false
|
||||
): Node | null => {
|
||||
switch (markdownNode.type) {
|
||||
case 'list':
|
||||
{
|
||||
const listItems = markdownNode.children
|
||||
.map(child => traverse(child))
|
||||
.filter(val => val);
|
||||
|
||||
if (firstLevel) {
|
||||
return listItems[0];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'listItem': {
|
||||
const paragraph = markdownNode.children[0];
|
||||
const list = markdownNode.children[1];
|
||||
const node: Node = {
|
||||
text: '',
|
||||
children: [],
|
||||
};
|
||||
|
||||
if (
|
||||
paragraph?.type === 'paragraph' &&
|
||||
paragraph.children[0]?.type === 'text'
|
||||
) {
|
||||
node.text = paragraph.children[0].value;
|
||||
}
|
||||
|
||||
if (list?.type === 'list') {
|
||||
node.children = list.children
|
||||
.map(child => traverse(child))
|
||||
.filter(val => val) as Node[];
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
if (ast?.children?.[0]?.type === 'list') {
|
||||
result = traverse(ast.children[0], true);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
Reference in New Issue
Block a user