mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-17 06:16:59 +08:00
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.
This commit is contained in:
@@ -1,5 +1,10 @@
|
|||||||
import type { EditorHost } from '@blocksuite/block-std';
|
import type { EditorHost, GfxBlockComponent } from '@blocksuite/block-std';
|
||||||
import { type Viewport } from '@blocksuite/block-std/gfx';
|
import {
|
||||||
|
clientToModelCoord,
|
||||||
|
GfxBlockElementModel,
|
||||||
|
GfxControllerIdentifier,
|
||||||
|
type Viewport,
|
||||||
|
} from '@blocksuite/block-std/gfx';
|
||||||
import { Pane } from 'tweakpane';
|
import { Pane } from 'tweakpane';
|
||||||
|
|
||||||
import { getSentenceRects, segmentSentences } from './text-utils.js';
|
import { getSentenceRects, segmentSentences } from './text-utils.js';
|
||||||
@@ -23,14 +28,64 @@ export function syncCanvasSize(canvas: HTMLCanvasElement, host: HTMLElement) {
|
|||||||
canvas.style.pointerEvents = 'none';
|
canvas.style.pointerEvents = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getParagraphs(host: EditorHost) {
|
||||||
|
const gfx = host.std.get(GfxControllerIdentifier);
|
||||||
|
const models = gfx.gfxElements.filter(e => e instanceof GfxBlockElementModel);
|
||||||
|
const components = models
|
||||||
|
.map(model => gfx.view.get(model.id))
|
||||||
|
.filter(Boolean) as GfxBlockComponent[];
|
||||||
|
|
||||||
|
const paragraphs: ParagraphLayout[] = [];
|
||||||
|
const selector = '.affine-paragraph-rich-text-wrapper [data-v-text="true"]';
|
||||||
|
|
||||||
|
components.forEach(component => {
|
||||||
|
const paragraphNodes = component.querySelectorAll(selector);
|
||||||
|
const viewportRecord = component.gfx.viewport.deserializeRecord(
|
||||||
|
component.dataset.viewportState
|
||||||
|
);
|
||||||
|
if (!viewportRecord) return;
|
||||||
|
const { zoom, viewScale } = viewportRecord;
|
||||||
|
|
||||||
|
paragraphNodes.forEach(paragraphNode => {
|
||||||
|
const paragraph: ParagraphLayout = {
|
||||||
|
sentences: [],
|
||||||
|
};
|
||||||
|
const sentences = segmentSentences(paragraphNode.textContent || '');
|
||||||
|
paragraph.sentences = sentences.map(sentence => {
|
||||||
|
const sentenceRects = getSentenceRects(paragraphNode, sentence);
|
||||||
|
const rects = sentenceRects.map(({ rect }) => {
|
||||||
|
const [modelX, modelY] = clientToModelCoord(viewportRecord, [
|
||||||
|
rect.x,
|
||||||
|
rect.y,
|
||||||
|
]);
|
||||||
|
return {
|
||||||
|
text: sentence,
|
||||||
|
...rect,
|
||||||
|
rect: {
|
||||||
|
x: modelX,
|
||||||
|
y: modelY,
|
||||||
|
w: rect.w / zoom / viewScale,
|
||||||
|
h: rect.h / zoom / viewScale,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
text: sentence,
|
||||||
|
rects,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
paragraphs.push(paragraph);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return paragraphs;
|
||||||
|
}
|
||||||
|
|
||||||
export function getViewportLayout(
|
export function getViewportLayout(
|
||||||
host: EditorHost,
|
host: EditorHost,
|
||||||
viewport: Viewport
|
viewport: Viewport
|
||||||
): ViewportLayout {
|
): ViewportLayout {
|
||||||
const paragraphBlocks = host.querySelectorAll(
|
|
||||||
'.affine-paragraph-rich-text-wrapper [data-v-text="true"]'
|
|
||||||
);
|
|
||||||
|
|
||||||
const zoom = viewport.zoom;
|
const zoom = viewport.zoom;
|
||||||
|
|
||||||
let layoutMinX = Infinity;
|
let layoutMinX = Infinity;
|
||||||
@@ -38,43 +93,19 @@ export function getViewportLayout(
|
|||||||
let layoutMaxX = -Infinity;
|
let layoutMaxX = -Infinity;
|
||||||
let layoutMaxY = -Infinity;
|
let layoutMaxY = -Infinity;
|
||||||
|
|
||||||
const paragraphs: ParagraphLayout[] = Array.from(paragraphBlocks).map(p => {
|
const paragraphs = getParagraphs(host);
|
||||||
const sentences = segmentSentences(p.textContent || '');
|
paragraphs.forEach(paragraph => {
|
||||||
const sentenceLayouts = sentences.map(sentence => {
|
paragraph.sentences.forEach(sentence => {
|
||||||
const rects = getSentenceRects(p, sentence);
|
sentence.rects.forEach(r => {
|
||||||
rects.forEach(({ rect }) => {
|
layoutMinX = Math.min(layoutMinX, r.rect.x);
|
||||||
layoutMinX = Math.min(layoutMinX, rect.x);
|
layoutMinY = Math.min(layoutMinY, r.rect.y);
|
||||||
layoutMinY = Math.min(layoutMinY, rect.y);
|
layoutMaxX = Math.max(layoutMaxX, r.rect.x + r.rect.w);
|
||||||
layoutMaxX = Math.max(layoutMaxX, rect.x + rect.w);
|
layoutMaxY = Math.max(layoutMaxY, r.rect.y + r.rect.h);
|
||||||
layoutMaxY = Math.max(layoutMaxY, rect.y + rect.h);
|
|
||||||
});
|
});
|
||||||
return {
|
|
||||||
text: sentence,
|
|
||||||
rects: rects.map(rect => {
|
|
||||||
const [x, y] = viewport.toModelCoordFromClientCoord([
|
|
||||||
rect.rect.x,
|
|
||||||
rect.rect.y,
|
|
||||||
]);
|
|
||||||
return {
|
|
||||||
...rect,
|
|
||||||
rect: {
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
w: rect.rect.w / zoom / viewport.viewScale,
|
|
||||||
h: rect.rect.h / zoom / viewport.viewScale,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
|
||||||
sentences: sentenceLayouts,
|
|
||||||
zoom,
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const layoutModelCoord = viewport.toModelCoordFromClientCoord([
|
const layoutModelCoord = clientToModelCoord(viewport, [
|
||||||
layoutMinX,
|
layoutMinX,
|
||||||
layoutMinY,
|
layoutMinY,
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ export interface SentenceLayout {
|
|||||||
|
|
||||||
export interface ParagraphLayout {
|
export interface ParagraphLayout {
|
||||||
sentences: SentenceLayout[];
|
sentences: SentenceLayout[];
|
||||||
zoom: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ViewportLayout {
|
export interface ViewportLayout {
|
||||||
|
|||||||
@@ -118,7 +118,6 @@ export class ViewportTurboRendererExtension extends LifeCycleWatcher {
|
|||||||
if (this.viewport.zoom > zoomThreshold) {
|
if (this.viewport.zoom > zoomThreshold) {
|
||||||
this.debugLog('Zoom above threshold, falling back to DOM rendering');
|
this.debugLog('Zoom above threshold, falling back to DOM rendering');
|
||||||
this.setState('pending');
|
this.setState('pending');
|
||||||
this.toggleOptimization(false);
|
|
||||||
this.clearOptimizedBlocks();
|
this.clearOptimizedBlocks();
|
||||||
}
|
}
|
||||||
// -> zooming
|
// -> zooming
|
||||||
@@ -138,7 +137,6 @@ export class ViewportTurboRendererExtension extends LifeCycleWatcher {
|
|||||||
// -> rendering
|
// -> rendering
|
||||||
else {
|
else {
|
||||||
this.setState('rendering');
|
this.setState('rendering');
|
||||||
this.toggleOptimization(false);
|
|
||||||
await this.paintLayout();
|
await this.paintLayout();
|
||||||
this.drawCachedBitmap();
|
this.drawCachedBitmap();
|
||||||
this.updateOptimizedBlocks();
|
this.updateOptimizedBlocks();
|
||||||
@@ -280,30 +278,16 @@ export class ViewportTurboRendererExtension extends LifeCycleWatcher {
|
|||||||
if (!this.viewportElement || !this.layoutCache) return;
|
if (!this.viewportElement || !this.layoutCache) return;
|
||||||
if (!this.canOptimize()) return;
|
if (!this.canOptimize()) return;
|
||||||
|
|
||||||
this.toggleOptimization(true);
|
|
||||||
const blockElements = this.viewportElement.getModelsInViewport();
|
const blockElements = this.viewportElement.getModelsInViewport();
|
||||||
const blockIds = Array.from(blockElements).map(model => model.id);
|
const blockIds = Array.from(blockElements).map(model => model.id);
|
||||||
this.viewportElement.updateOptimizedBlocks(blockIds, true);
|
|
||||||
this.debugLog(`Optimized ${blockIds.length} blocks`);
|
this.debugLog(`Optimized ${blockIds.length} blocks`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private clearOptimizedBlocks() {
|
private clearOptimizedBlocks() {
|
||||||
if (!this.viewportElement) return;
|
|
||||||
this.viewportElement.clearOptimizedBlocks();
|
|
||||||
this.debugLog('Cleared optimized blocks');
|
this.debugLog('Cleared optimized blocks');
|
||||||
}
|
}
|
||||||
|
|
||||||
private toggleOptimization(value: boolean) {
|
|
||||||
if (
|
|
||||||
this.viewportElement &&
|
|
||||||
this.viewportElement.enableOptimization !== value
|
|
||||||
) {
|
|
||||||
this.viewportElement.enableOptimization = value;
|
|
||||||
this.debugLog(`${value ? 'Enabled' : 'Disabled'} optimization`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleResize() {
|
private handleResize() {
|
||||||
this.debugLog('Container resized, syncing canvas size');
|
this.debugLog('Container resized, syncing canvas size');
|
||||||
syncCanvasSize(this.canvas, this.std.host);
|
syncCanvasSize(this.canvas, this.std.host);
|
||||||
|
|||||||
@@ -35,10 +35,21 @@ export function requestThrottledConnectedFrame<
|
|||||||
}) as T;
|
}) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDisplay(view: BlockComponent | null, display: 'block' | 'none') {
|
function setBlockState(view: BlockComponent | null, state: 'active' | 'idle') {
|
||||||
if (!view) return;
|
if (!view) return;
|
||||||
if (view.style.display !== display) {
|
|
||||||
view.style.display = display;
|
if (state === 'active') {
|
||||||
|
view.style.visibility = 'visible';
|
||||||
|
view.style.pointerEvents = 'auto';
|
||||||
|
view.classList.remove('block-idle');
|
||||||
|
view.classList.add('block-active');
|
||||||
|
view.dataset.blockState = 'active';
|
||||||
|
} else {
|
||||||
|
view.style.visibility = 'hidden';
|
||||||
|
view.style.pointerEvents = 'none';
|
||||||
|
view.classList.remove('block-active');
|
||||||
|
view.classList.add('block-idle');
|
||||||
|
view.dataset.blockState = 'idle';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,20 +66,31 @@ export class GfxViewportElement extends WithDisposable(ShadowlessElement) {
|
|||||||
display: block;
|
display: block;
|
||||||
transform: none;
|
transform: none;
|
||||||
}
|
}
|
||||||
`;
|
|
||||||
|
|
||||||
optimizedBlocks = new Set<string>();
|
/* CSS for idle blocks that are hidden but maintain layout */
|
||||||
|
.block-idle {
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
will-change: transform;
|
||||||
|
contain: size layout style;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CSS for active blocks participating in viewport transformations */
|
||||||
|
.block-active {
|
||||||
|
visibility: visible;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
private readonly _hideOutsideBlock = () => {
|
private readonly _hideOutsideBlock = () => {
|
||||||
if (!this.host) return;
|
if (!this.host) return;
|
||||||
|
|
||||||
const { host, optimizedBlocks, enableOptimization } = this;
|
const { host } = this;
|
||||||
const modelsInViewport = this.getModelsInViewport();
|
const modelsInViewport = this.getModelsInViewport();
|
||||||
|
|
||||||
modelsInViewport.forEach(model => {
|
modelsInViewport.forEach(model => {
|
||||||
const view = host.std.view.getBlock(model.id);
|
const view = host.std.view.getBlock(model.id);
|
||||||
const canOptimize = optimizedBlocks.has(model.id) && enableOptimization;
|
setBlockState(view, 'active');
|
||||||
const display = canOptimize ? 'none' : 'block';
|
|
||||||
setDisplay(view, display);
|
|
||||||
|
|
||||||
if (this._lastVisibleModels?.has(model)) {
|
if (this._lastVisibleModels?.has(model)) {
|
||||||
this._lastVisibleModels!.delete(model);
|
this._lastVisibleModels!.delete(model);
|
||||||
@@ -77,7 +99,7 @@ export class GfxViewportElement extends WithDisposable(ShadowlessElement) {
|
|||||||
|
|
||||||
this._lastVisibleModels?.forEach(model => {
|
this._lastVisibleModels?.forEach(model => {
|
||||||
const view = host.std.view.getBlock(model.id);
|
const view = host.std.view.getBlock(model.id);
|
||||||
setDisplay(view, 'none');
|
setBlockState(view, 'idle');
|
||||||
});
|
});
|
||||||
|
|
||||||
this._lastVisibleModels = modelsInViewport;
|
this._lastVisibleModels = modelsInViewport;
|
||||||
@@ -170,28 +192,25 @@ export class GfxViewportElement extends WithDisposable(ShadowlessElement) {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor viewport!: Viewport;
|
accessor viewport!: Viewport;
|
||||||
|
|
||||||
@property({ attribute: false })
|
setBlocksActive(blockIds: string[]): void {
|
||||||
accessor enableOptimization: boolean = false;
|
if (!this.host) return;
|
||||||
|
|
||||||
updateOptimizedBlocks(blockIds: string[], optimized: boolean): void {
|
|
||||||
let changed = false;
|
|
||||||
|
|
||||||
blockIds.forEach(id => {
|
blockIds.forEach(id => {
|
||||||
if (optimized && !this.optimizedBlocks.has(id)) {
|
const view = this.host?.std.view.getBlock(id);
|
||||||
this.optimizedBlocks.add(id);
|
if (view) {
|
||||||
changed = true;
|
setBlockState(view, 'active');
|
||||||
} else if (!optimized && this.optimizedBlocks.has(id)) {
|
|
||||||
this.optimizedBlocks.delete(id);
|
|
||||||
changed = true;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (changed) this._refreshViewport();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clearOptimizedBlocks(): void {
|
setBlocksIdle(blockIds: string[]): void {
|
||||||
if (this.optimizedBlocks.size === 0) return;
|
if (!this.host) return;
|
||||||
this.optimizedBlocks.clear();
|
|
||||||
this._refreshViewport();
|
blockIds.forEach(id => {
|
||||||
|
const view = this.host?.std.view.getBlock(id);
|
||||||
|
if (view) {
|
||||||
|
setBlockState(view, 'idle');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,29 @@ export const ZOOM_INITIAL = 1.0;
|
|||||||
|
|
||||||
export const FIT_TO_SCREEN_PADDING = 100;
|
export const FIT_TO_SCREEN_PADDING = 100;
|
||||||
|
|
||||||
|
export interface ViewportRecord {
|
||||||
|
left: number;
|
||||||
|
top: number;
|
||||||
|
viewportX: number;
|
||||||
|
viewportY: number;
|
||||||
|
zoom: number;
|
||||||
|
viewScale: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clientToModelCoord(
|
||||||
|
viewport: ViewportRecord,
|
||||||
|
clientCoord: [number, number]
|
||||||
|
): IVec {
|
||||||
|
const { left, top, viewportX, viewportY, zoom, viewScale } = viewport;
|
||||||
|
|
||||||
|
const [clientX, clientY] = clientCoord;
|
||||||
|
const viewportInternalX = clientX - left;
|
||||||
|
const viewportInternalY = clientY - top;
|
||||||
|
const modelX = viewportX + viewportInternalX / zoom / viewScale;
|
||||||
|
const modelY = viewportY + viewportInternalY / zoom / viewScale;
|
||||||
|
return [modelX, modelY];
|
||||||
|
}
|
||||||
|
|
||||||
export class Viewport {
|
export class Viewport {
|
||||||
private _cachedBoundingClientRect: DOMRect | null = null;
|
private _cachedBoundingClientRect: DOMRect | null = null;
|
||||||
|
|
||||||
@@ -461,8 +484,7 @@ export class Viewport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toModelCoordFromClientCoord([x, y]: IVec): IVec {
|
toModelCoordFromClientCoord([x, y]: IVec): IVec {
|
||||||
const { left, top } = this;
|
return clientToModelCoord(this, [x, y]);
|
||||||
return this.toModelCoord(x - left, y - top);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toViewBound(bound: Bound) {
|
toViewBound(bound: Bound) {
|
||||||
@@ -484,4 +506,26 @@ export class Viewport {
|
|||||||
const { left, top } = this;
|
const { left, top } = this;
|
||||||
return [x - left, y - top];
|
return [x - left, y - top];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serializeRecord() {
|
||||||
|
return JSON.stringify({
|
||||||
|
left: this.left,
|
||||||
|
top: this.top,
|
||||||
|
viewportX: this.viewportX,
|
||||||
|
viewportY: this.viewportY,
|
||||||
|
zoom: this.zoom,
|
||||||
|
viewScale: this.viewScale,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializeRecord(record?: string) {
|
||||||
|
try {
|
||||||
|
const result = JSON.parse(record || '{}') as ViewportRecord;
|
||||||
|
if (!('zoom' in result)) return null;
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to deserialize viewport record:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ export function isGfxBlockComponent(
|
|||||||
export const GfxElementSymbol = Symbol('GfxElement');
|
export const GfxElementSymbol = Symbol('GfxElement');
|
||||||
|
|
||||||
function updateTransform(element: GfxBlockComponent) {
|
function updateTransform(element: GfxBlockComponent) {
|
||||||
|
if (element.dataset.blockState === 'idle') return;
|
||||||
|
|
||||||
|
const { viewport } = element.gfx;
|
||||||
|
element.dataset.viewportState = viewport.serializeRecord();
|
||||||
element.style.transformOrigin = '0 0';
|
element.style.transformOrigin = '0 0';
|
||||||
element.style.transform = element.getCSSTransform();
|
element.style.transform = element.getCSSTransform();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getImageSelectionsCommand,
|
getImageSelectionsCommand,
|
||||||
getSelectedBlocksCommand,
|
getSelectedBlocksCommand,
|
||||||
getSelectedModelsCommand,
|
getSelectedModelsCommand,
|
||||||
|
getSurfaceBlock,
|
||||||
getTextSelectionCommand,
|
getTextSelectionCommand,
|
||||||
ImageBlockModel,
|
ImageBlockModel,
|
||||||
isCanvasElement,
|
isCanvasElement,
|
||||||
@@ -197,7 +198,7 @@ export const stopPropagation = (e: Event) => {
|
|||||||
|
|
||||||
export function getSurfaceElementFromEditor(editor: EditorHost) {
|
export function getSurfaceElementFromEditor(editor: EditorHost) {
|
||||||
const { doc } = editor;
|
const { doc } = editor;
|
||||||
const surfaceModel = doc.getBlockByFlavour('affine:surface')[0];
|
const surfaceModel = getSurfaceBlock(doc);
|
||||||
if (!surfaceModel) return null;
|
if (!surfaceModel) return null;
|
||||||
|
|
||||||
const surfaceId = surfaceModel.id;
|
const surfaceId = surfaceModel.id;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
FontFamilyMap,
|
FontFamilyMap,
|
||||||
FontStyle,
|
FontStyle,
|
||||||
FontWeightMap,
|
FontWeightMap,
|
||||||
|
getSurfaceBlock,
|
||||||
PointStyle,
|
PointStyle,
|
||||||
StrokeStyle,
|
StrokeStyle,
|
||||||
TextAlign,
|
TextAlign,
|
||||||
@@ -29,7 +30,6 @@ import { menuTrigger, settingWrapper } from '../style.css';
|
|||||||
import { sortedFontWeightEntries, usePalettes } from '../utils';
|
import { sortedFontWeightEntries, usePalettes } from '../utils';
|
||||||
import { Point } from './point';
|
import { Point } from './point';
|
||||||
import { EdgelessSnapshot } from './snapshot';
|
import { EdgelessSnapshot } from './snapshot';
|
||||||
import { getSurfaceBlock } from './utils';
|
|
||||||
|
|
||||||
enum ConnecterStyle {
|
enum ConnecterStyle {
|
||||||
General = 'general',
|
General = 'general',
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ import {
|
|||||||
import { SettingRow } from '@affine/component/setting-components';
|
import { SettingRow } from '@affine/component/setting-components';
|
||||||
import { EditorSettingService } from '@affine/core/modules/editor-setting';
|
import { EditorSettingService } from '@affine/core/modules/editor-setting';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { LayoutType, MindmapStyle } from '@blocksuite/affine/blocks';
|
import {
|
||||||
|
getSurfaceBlock,
|
||||||
|
LayoutType,
|
||||||
|
MindmapStyle,
|
||||||
|
} from '@blocksuite/affine/blocks';
|
||||||
import type { Store } from '@blocksuite/affine/store';
|
import type { Store } from '@blocksuite/affine/store';
|
||||||
import { useFramework, useLiveData } from '@toeverything/infra';
|
import { useFramework, useLiveData } from '@toeverything/infra';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
@@ -15,7 +19,6 @@ import { useCallback, useMemo } from 'react';
|
|||||||
import { DropdownMenu } from '../menu';
|
import { DropdownMenu } from '../menu';
|
||||||
import { menuTrigger, settingWrapper } from '../style.css';
|
import { menuTrigger, settingWrapper } from '../style.css';
|
||||||
import { EdgelessSnapshot } from './snapshot';
|
import { EdgelessSnapshot } from './snapshot';
|
||||||
import { getSurfaceBlock } from './utils';
|
|
||||||
|
|
||||||
const MINDMAP_STYLES = [
|
const MINDMAP_STYLES = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { MenuItem, MenuTrigger, Slider } from '@affine/component';
|
|||||||
import { SettingRow } from '@affine/component/setting-components';
|
import { SettingRow } from '@affine/component/setting-components';
|
||||||
import { EditorSettingService } from '@affine/core/modules/editor-setting';
|
import { EditorSettingService } from '@affine/core/modules/editor-setting';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { DefaultTheme } from '@blocksuite/affine/blocks';
|
import { DefaultTheme, getSurfaceBlock } from '@blocksuite/affine/blocks';
|
||||||
import type { Store } from '@blocksuite/affine/store';
|
import type { Store } from '@blocksuite/affine/store';
|
||||||
import { useFramework, useLiveData } from '@toeverything/infra';
|
import { useFramework, useLiveData } from '@toeverything/infra';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
@@ -13,7 +13,6 @@ import { menuTrigger } from '../style.css';
|
|||||||
import { usePalettes } from '../utils';
|
import { usePalettes } from '../utils';
|
||||||
import { Point } from './point';
|
import { Point } from './point';
|
||||||
import { EdgelessSnapshot } from './snapshot';
|
import { EdgelessSnapshot } from './snapshot';
|
||||||
import { getSurfaceBlock } from './utils';
|
|
||||||
|
|
||||||
export const PenSettings = () => {
|
export const PenSettings = () => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
FontStyle,
|
FontStyle,
|
||||||
FontWeightMap,
|
FontWeightMap,
|
||||||
getShapeName,
|
getShapeName,
|
||||||
|
getSurfaceBlock,
|
||||||
ShapeStyle,
|
ShapeStyle,
|
||||||
ShapeType,
|
ShapeType,
|
||||||
StrokeStyle,
|
StrokeStyle,
|
||||||
@@ -39,7 +40,6 @@ import { sortedFontWeightEntries, usePalettes } from '../utils';
|
|||||||
import type { DocName } from './docs';
|
import type { DocName } from './docs';
|
||||||
import { Point } from './point';
|
import { Point } from './point';
|
||||||
import { EdgelessSnapshot } from './snapshot';
|
import { EdgelessSnapshot } from './snapshot';
|
||||||
import { getSurfaceBlock } from './utils';
|
|
||||||
|
|
||||||
enum ShapeTextFontSize {
|
enum ShapeTextFontSize {
|
||||||
'16px' = '16',
|
'16px' = '16',
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
import type { SurfaceBlockModel } from '@blocksuite/affine/block-std/gfx';
|
|
||||||
import type { FrameBlockModel } from '@blocksuite/affine/blocks';
|
import type { FrameBlockModel } from '@blocksuite/affine/blocks';
|
||||||
import type { Store } from '@blocksuite/affine/store';
|
import type { Store } from '@blocksuite/affine/store';
|
||||||
|
|
||||||
export function getSurfaceBlock(doc: Store) {
|
|
||||||
const blocks = doc.getBlocksByFlavour('affine:surface');
|
|
||||||
return blocks.length !== 0 ? (blocks[0].model as SurfaceBlockModel) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getFrameBlock(doc: Store) {
|
export function getFrameBlock(doc: Store) {
|
||||||
const blocks = doc.getBlocksByFlavour('affine:frame');
|
const blocks = doc.getBlocksByFlavour('affine:frame');
|
||||||
return blocks.length !== 0 ? (blocks[0].model as FrameBlockModel) : null;
|
return blocks.length !== 0 ? (blocks[0].model as FrameBlockModel) : null;
|
||||||
|
|||||||
Reference in New Issue
Block a user