Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b15a2a9638 | |||
| f3218ab3bc | |||
| f79324b6a1 | |||
| adcc6b578c | |||
| 7f833f8c15 | |||
| 785951bbfa | |||
| 19e9f970f4 | |||
| c362737441 | |||
| 618042812b | |||
| 0ff7c5f897 | |||
| b8dcb85007 | |||
| 5ac15f12e6 | |||
| efe36161e8 | |||
| 126677d7ad | |||
| 007bbabce4 | |||
| 64cc99354e | |||
| 1516903c77 | |||
| 4f831732e1 | |||
| ef28e36441 | |||
| 7b1dfb7ee8 | |||
| 5fcc402280 |
@@ -17,7 +17,6 @@
|
||||
"@blocksuite/blocks": "workspace:*",
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/inline": "workspace:*",
|
||||
"@blocksuite/presets": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@blocksuite/sync": "workspace:*"
|
||||
},
|
||||
@@ -37,7 +36,6 @@
|
||||
"./inline": "./src/inline/index.ts",
|
||||
"./inline/consts": "./src/inline/consts.ts",
|
||||
"./inline/types": "./src/inline/types.ts",
|
||||
"./presets": "./src/presets/index.ts",
|
||||
"./blocks": "./src/blocks/index.ts",
|
||||
"./blocks/schemas": "./src/blocks/schemas.ts",
|
||||
"./sync": "./src/sync/index.ts"
|
||||
@@ -83,9 +81,6 @@
|
||||
"inline/types": [
|
||||
"dist/inline/types.d.ts"
|
||||
],
|
||||
"presets": [
|
||||
"dist/presets/index.d.ts"
|
||||
],
|
||||
"blocks": [
|
||||
"dist/blocks/index.d.ts"
|
||||
],
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { effects as blocksEffects } from '@blocksuite/blocks/effects';
|
||||
import { effects as presetsEffects } from '@blocksuite/presets/effects';
|
||||
|
||||
export function effects() {
|
||||
blocksEffects();
|
||||
presetsEffects();
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from '@blocksuite/presets';
|
||||
@@ -11,7 +11,6 @@
|
||||
{ "path": "../../blocks" },
|
||||
{ "path": "../../framework/global" },
|
||||
{ "path": "../../framework/inline" },
|
||||
{ "path": "../../presets" },
|
||||
{ "path": "../../framework/store" },
|
||||
{ "path": "../../framework/sync" }
|
||||
]
|
||||
|
||||
@@ -38,8 +38,6 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
||||
|
||||
protected _isResizing = false;
|
||||
|
||||
protected _isSelected = false;
|
||||
|
||||
protected _whenHover: HoverController | null = new HoverController(
|
||||
this,
|
||||
({ abortController }) => {
|
||||
|
||||
@@ -35,7 +35,7 @@ export class AttachmentEdgelessBlockComponent extends toGfxBlockComponent(
|
||||
this.slots.elementResizeEnd.on(() => {
|
||||
this._isResizing = false;
|
||||
this._showOverlay =
|
||||
this._isResizing || this._isDragging || !this._isSelected;
|
||||
this._isResizing || this._isDragging || !this.selected$.peek();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@ import {
|
||||
} from '@blocksuite/affine-components/caption';
|
||||
import type { BookmarkBlockModel } from '@blocksuite/affine-model';
|
||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||
import { html } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { type ClassInfo, classMap } from 'lit/directives/class-map.js';
|
||||
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { refreshBookmarkUrlData } from './utils.js';
|
||||
@@ -14,6 +15,12 @@ import { refreshBookmarkUrlData } from './utils.js';
|
||||
export const BOOKMARK_MIN_WIDTH = 450;
|
||||
|
||||
export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBlockModel> {
|
||||
selectedStyle$: ReadonlySignal<ClassInfo> | null = computed<ClassInfo>(
|
||||
() => ({
|
||||
'selected-style': this.selected$.value,
|
||||
})
|
||||
);
|
||||
|
||||
private _fetchAbortController?: AbortController;
|
||||
|
||||
blockDraggable = true;
|
||||
@@ -69,16 +76,12 @@ export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBloc
|
||||
}
|
||||
|
||||
override renderBlock() {
|
||||
const selected = this.selected$.value;
|
||||
const isInEdgeless =
|
||||
this.std.get(DocModeProvider).getEditorMode() === 'edgeless';
|
||||
|
||||
return html`
|
||||
<div
|
||||
draggable="${this.blockDraggable ? 'true' : 'false'}"
|
||||
class=${classMap({
|
||||
'affine-bookmark-container': true,
|
||||
'selected-style': selected && !isInEdgeless,
|
||||
...this.selectedStyle$?.value,
|
||||
})}
|
||||
style=${this.containerStyleMap}
|
||||
>
|
||||
|
||||
@@ -10,6 +10,8 @@ import { BookmarkBlockComponent } from './bookmark-block.js';
|
||||
export class BookmarkEdgelessBlockComponent extends toGfxBlockComponent(
|
||||
BookmarkBlockComponent
|
||||
) {
|
||||
override selectedStyle$ = null;
|
||||
|
||||
override blockDraggable = false;
|
||||
|
||||
override getRenderingRect() {
|
||||
|
||||
@@ -34,8 +34,6 @@ import {
|
||||
} from './properties/index.js';
|
||||
import {
|
||||
addProperty,
|
||||
applyCellsUpdate,
|
||||
applyPropertyUpdate,
|
||||
copyCellsByProperty,
|
||||
deleteRows,
|
||||
deleteView,
|
||||
@@ -169,7 +167,6 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
||||
columnId: propertyId,
|
||||
value: newValue,
|
||||
});
|
||||
applyCellsUpdate(this._model);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +196,6 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
||||
insertToPosition,
|
||||
property.create(this.newPropertyName())
|
||||
);
|
||||
applyPropertyUpdate(this._model);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -283,7 +279,6 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
||||
propertyDataSet(propertyId: string, data: Record<string, unknown>): void {
|
||||
this._runCapture();
|
||||
this.updateProperty(propertyId, () => ({ data }));
|
||||
applyPropertyUpdate(this._model);
|
||||
}
|
||||
|
||||
propertyDataTypeGet(propertyId: string): TypeInstance | undefined {
|
||||
@@ -337,7 +332,6 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
||||
schema
|
||||
);
|
||||
copyCellsByProperty(this._model, copyId, id);
|
||||
applyPropertyUpdate(this._model);
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -366,7 +360,6 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
||||
propertyNameSet(propertyId: string, name: string): void {
|
||||
this.doc.captureSync();
|
||||
this.updateProperty(propertyId, () => ({ name }));
|
||||
applyPropertyUpdate(this._model);
|
||||
}
|
||||
|
||||
override propertyReadonlyGet(propertyId: string): boolean {
|
||||
@@ -421,7 +414,6 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
||||
}
|
||||
});
|
||||
updateCells(this._model, propertyId, cells);
|
||||
applyPropertyUpdate(this._model);
|
||||
}
|
||||
|
||||
rowAdd(insertPosition: InsertToPosition | number): string {
|
||||
|
||||
@@ -37,24 +37,6 @@ export function addProperty(
|
||||
return id;
|
||||
}
|
||||
|
||||
export function applyCellsUpdate(model: DatabaseBlockModel) {
|
||||
model.doc.updateBlock(model, {
|
||||
cells: model.cells,
|
||||
});
|
||||
}
|
||||
|
||||
export function applyPropertyUpdate(model: DatabaseBlockModel) {
|
||||
model.doc.updateBlock(model, {
|
||||
columns: model.columns,
|
||||
});
|
||||
}
|
||||
|
||||
export function applyViewsUpdate(model: DatabaseBlockModel) {
|
||||
model.doc.updateBlock(model, {
|
||||
views: model.views,
|
||||
});
|
||||
}
|
||||
|
||||
export function copyCellsByProperty(
|
||||
model: DatabaseBlockModel,
|
||||
fromId: Column['id'],
|
||||
@@ -156,7 +138,6 @@ export function moveViewTo(
|
||||
arr => insertPositionToIndex(position, arr)
|
||||
);
|
||||
});
|
||||
applyViewsUpdate(model);
|
||||
}
|
||||
|
||||
export function updateCell(
|
||||
@@ -255,6 +236,5 @@ export const updateView = <ViewData extends ViewBasicDataType>(
|
||||
return { ...v, ...update(v as ViewData) };
|
||||
});
|
||||
});
|
||||
applyViewsUpdate(model);
|
||||
};
|
||||
export const DATABASE_CONVERT_WHITE_LIST = ['affine:list', 'affine:paragraph'];
|
||||
|
||||
@@ -12,10 +12,11 @@ import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import type { BlockService } from '@blocksuite/block-std';
|
||||
import type { GfxCompatibleProps } from '@blocksuite/block-std/gfx';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||
import type { TemplateResult } from 'lit';
|
||||
import { html } from 'lit';
|
||||
import { query } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { type ClassInfo, classMap } from 'lit/directives/class-map.js';
|
||||
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
export class EmbedBlockComponent<
|
||||
@@ -23,6 +24,12 @@ export class EmbedBlockComponent<
|
||||
Service extends BlockService = BlockService,
|
||||
WidgetName extends string = string,
|
||||
> extends CaptionedBlockComponent<Model, Service, WidgetName> {
|
||||
selectedStyle$: ReadonlySignal<ClassInfo> | null = computed<ClassInfo>(
|
||||
() => ({
|
||||
'selected-style': this.selected$.value,
|
||||
})
|
||||
);
|
||||
|
||||
private _fetchAbortController = new AbortController();
|
||||
|
||||
_cardStyle: EmbedCardStyle = 'horizontal';
|
||||
@@ -43,10 +50,6 @@ export class EmbedBlockComponent<
|
||||
protected embedContainerStyle: StyleInfo = {};
|
||||
|
||||
renderEmbed = (content: () => TemplateResult) => {
|
||||
const selected = this.selected$.value;
|
||||
const isInEdgeless =
|
||||
this.std.get(DocModeProvider).getEditorMode() === 'edgeless';
|
||||
|
||||
if (
|
||||
this._cardStyle === 'horizontal' ||
|
||||
this._cardStyle === 'horizontalThin' ||
|
||||
@@ -54,7 +57,7 @@ export class EmbedBlockComponent<
|
||||
) {
|
||||
this.style.display = 'block';
|
||||
|
||||
if (isInEdgeless) {
|
||||
if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
|
||||
this.style.minWidth = `${EMBED_CARD_MIN_WIDTH}px`;
|
||||
}
|
||||
}
|
||||
@@ -64,7 +67,7 @@ export class EmbedBlockComponent<
|
||||
draggable="${this.blockDraggable ? 'true' : 'false'}"
|
||||
class=${classMap({
|
||||
'embed-block-container': true,
|
||||
'selected-style': selected && !isInEdgeless,
|
||||
...this.selectedStyle$?.value,
|
||||
})}
|
||||
style=${styleMap({
|
||||
height: `${this._cardHeight}px`,
|
||||
|
||||
@@ -22,6 +22,8 @@ export function toEdgelessEmbedBlock<
|
||||
B extends typeof EmbedBlockComponent<Model, Service, WidgetName>,
|
||||
>(block: B) {
|
||||
return class extends toGfxBlockComponent(block) {
|
||||
override selectedStyle$ = null;
|
||||
|
||||
_isDragging = false;
|
||||
|
||||
_isResizing = false;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"author": "toeverything",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@blocksuite/affine-block-surface": "workspace:*",
|
||||
"@blocksuite/affine-components": "workspace:*",
|
||||
"@blocksuite/affine-model": "workspace:*",
|
||||
"@blocksuite/affine-shared": "workspace:*",
|
||||
@@ -27,6 +28,7 @@
|
||||
"@types/mdast": "^4.0.4",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"yjs": "^13.6.21",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"exports": {
|
||||
|
||||
@@ -21,14 +21,13 @@ import {
|
||||
type IVec,
|
||||
type SerializedXYWH,
|
||||
} from '@blocksuite/global/utils';
|
||||
import { Text } from '@blocksuite/store';
|
||||
import { type BlockModel, Text } from '@blocksuite/store';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import { areSetsEqual } from './utils/misc.js';
|
||||
import { isFrameBlock } from './utils/query.js';
|
||||
|
||||
const FRAME_PADDING = 40;
|
||||
|
||||
export type NavigatorMode = 'fill' | 'fit';
|
||||
|
||||
export class FrameOverlay extends Overlay {
|
||||
static override overlayName: string = 'frame';
|
||||
|
||||
@@ -461,3 +460,13 @@ export class EdgelessFrameManager extends GfxExtension {
|
||||
this._disposable.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function areSetsEqual<T>(setA: Set<T>, setB: Set<T>) {
|
||||
if (setA.size !== setB.size) return false;
|
||||
for (const a of setA) if (!setB.has(a)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isFrameBlock(element: unknown): element is FrameBlockModel {
|
||||
return !!element && (element as BlockModel).flavour === 'affine:frame';
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './frame-block.js';
|
||||
export * from './frame-manager.js';
|
||||
export * from './frame-spec.js';
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
},
|
||||
"include": ["./src"],
|
||||
"references": [
|
||||
{ "path": "../block-surface" },
|
||||
{ "path": "../components" },
|
||||
{ "path": "../model" },
|
||||
{ "path": "../shared" },
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@vanilla-extract/css": "^1.14.0",
|
||||
"@vanilla-extract/css": "^1.17.0",
|
||||
"lit": "^3.2.0",
|
||||
"yjs": "^13.6.21",
|
||||
"zod": "^3.24.1"
|
||||
|
||||
@@ -2,8 +2,7 @@ import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const tableContainer = style({
|
||||
display: 'block',
|
||||
backgroundColor: 'var(--affine-background-primary-color)',
|
||||
padding: '10px 0 18px',
|
||||
padding: '10px 0 18px 10px',
|
||||
overflowX: 'auto',
|
||||
overflowY: 'visible',
|
||||
selectors: {
|
||||
|
||||
@@ -135,7 +135,7 @@ export class TableBlockComponent extends CaptionedBlockComponent<TableBlockModel
|
||||
contenteditable="false"
|
||||
class=${tableContainer}
|
||||
style=${styleMap({
|
||||
marginLeft: `-${virtualPadding}px`,
|
||||
marginLeft: `-${virtualPadding + 10}px`,
|
||||
marginRight: `-${virtualPadding}px`,
|
||||
position: 'relative',
|
||||
})}
|
||||
|
||||
@@ -4,7 +4,8 @@ import { createVar, style } from '@vanilla-extract/css';
|
||||
export const cellContainerStyle = style({
|
||||
position: 'relative',
|
||||
alignItems: 'center',
|
||||
border: '1px solid var(--affine-border-color)',
|
||||
border: '1px solid',
|
||||
borderColor: cssVarV2.table.border,
|
||||
borderCollapse: 'collapse',
|
||||
isolation: 'auto',
|
||||
textAlign: 'start',
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "@blocksuite/affine-fragment-frame-panel",
|
||||
"description": "Frame panel fragment for BlockSuite.",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test:unit": "nx vite:test --run --passWithNoTests",
|
||||
"test:unit:coverage": "nx vite:test --run --coverage",
|
||||
"test:e2e": "playwright test"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"keywords": [],
|
||||
"author": "toeverything",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@blocksuite/affine-block-frame": "workspace:*",
|
||||
"@blocksuite/affine-block-surface": "workspace:*",
|
||||
"@blocksuite/affine-components": "workspace:*",
|
||||
"@blocksuite/affine-model": "workspace:*",
|
||||
"@blocksuite/affine-shared": "workspace:*",
|
||||
"@blocksuite/block-std": "workspace:*",
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/icons": "^2.2.1",
|
||||
"@blocksuite/inline": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./effects": "./src/effects.ts"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"dist",
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.19.0"
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
import { type EditorHost, ShadowlessElement } from '@blocksuite/block-std';
|
||||
import { generateKeyBetweenV2 } from '@blocksuite/block-std/gfx';
|
||||
import { EdgelessFrameManager } from '@blocksuite/affine-block-frame';
|
||||
import type { FrameBlockModel } from '@blocksuite/affine-model';
|
||||
import {
|
||||
DocModeProvider,
|
||||
EdgelessFrameManager,
|
||||
EdgelessRootService,
|
||||
EditPropsStore,
|
||||
type FrameBlockModel,
|
||||
} from '@blocksuite/blocks';
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { type EditorHost, ShadowlessElement } from '@blocksuite/block-std';
|
||||
import {
|
||||
generateKeyBetweenV2,
|
||||
GfxControllerIdentifier,
|
||||
} from '@blocksuite/block-std/gfx';
|
||||
import {
|
||||
Bound,
|
||||
DisposableGroup,
|
||||
@@ -110,7 +112,7 @@ export class FramePanelBody extends SignalWatcher(
|
||||
}
|
||||
|
||||
this._selected = [];
|
||||
this._edgelessRootService?.selection.set({
|
||||
this._gfx.selection.set({
|
||||
elements: this._selected,
|
||||
editing: false,
|
||||
});
|
||||
@@ -126,6 +128,10 @@ export class FramePanelBody extends SignalWatcher(
|
||||
|
||||
private _lastEdgelessRootId = '';
|
||||
|
||||
private get _gfx() {
|
||||
return this.editorHost.std.get(GfxControllerIdentifier);
|
||||
}
|
||||
|
||||
private readonly _selectFrame = (e: SelectEvent) => {
|
||||
const { selected, id, multiselect } = e.detail;
|
||||
|
||||
@@ -138,7 +144,7 @@ export class FramePanelBody extends SignalWatcher(
|
||||
this._selected = [id];
|
||||
}
|
||||
|
||||
this._edgelessRootService?.selection.set({
|
||||
this._gfx.selection.set({
|
||||
elements: this._selected,
|
||||
editing: false,
|
||||
});
|
||||
@@ -152,10 +158,6 @@ export class FramePanelBody extends SignalWatcher(
|
||||
}));
|
||||
};
|
||||
|
||||
get _edgelessRootService() {
|
||||
return this.editorHost.std.getOptional(EdgelessRootService);
|
||||
}
|
||||
|
||||
get frames() {
|
||||
const frames = this.editorHost.doc
|
||||
.getBlocksByFlavour('affine:frame')
|
||||
@@ -229,8 +231,9 @@ export class FramePanelBody extends SignalWatcher(
|
||||
private _fitToElement(e: FitViewEvent) {
|
||||
const { block } = e.detail;
|
||||
const bound = Bound.deserialize(block.xywh);
|
||||
const docModeProvider = this.editorHost.std.get(DocModeProvider);
|
||||
|
||||
if (!this._edgelessRootService) {
|
||||
if (docModeProvider.getEditorMode() !== 'edgeless') {
|
||||
// When click frame card in page mode
|
||||
// Should switch to edgeless mode and set viewport to the frame
|
||||
const viewport = {
|
||||
@@ -242,11 +245,7 @@ export class FramePanelBody extends SignalWatcher(
|
||||
this.editorHost.std.get(EditPropsStore).setStorage('viewport', viewport);
|
||||
this.editorHost.std.get(DocModeProvider).setEditorMode('edgeless');
|
||||
} else {
|
||||
this._edgelessRootService.viewport.setViewportByBound(
|
||||
bound,
|
||||
this.viewportPadding,
|
||||
true
|
||||
);
|
||||
this._gfx.viewport.setViewportByBound(bound, this.viewportPadding, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,7 +397,7 @@ export class FramePanelBody extends SignalWatcher(
|
||||
this._setDocDisposables(this.editorHost.doc);
|
||||
// after switch to edgeless mode, should update the selection
|
||||
if (this.editorHost.doc.id === this._lastEdgelessRootId) {
|
||||
this._edgelessRootService?.selection.set({
|
||||
this._gfx.selection.set({
|
||||
elements: this._selected,
|
||||
editing: false,
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { RichText } from '@blocksuite/affine-components/rich-text';
|
||||
import type { FrameBlockModel } from '@blocksuite/affine-model';
|
||||
import { ShadowlessElement } from '@blocksuite/block-std';
|
||||
import type { FrameBlockModel, RichText } from '@blocksuite/blocks';
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import { css, html } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FrameBlockModel } from '@blocksuite/affine-model';
|
||||
import { ShadowlessElement } from '@blocksuite/block-std';
|
||||
import type { FrameBlockModel } from '@blocksuite/blocks';
|
||||
import { DisposableGroup, WithDisposable } from '@blocksuite/global/utils';
|
||||
import { css, html, type PropertyValues } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { FrameBlockModel } from '@blocksuite/affine-model';
|
||||
import { on, once } from '@blocksuite/affine-shared/utils';
|
||||
import { ShadowlessElement } from '@blocksuite/block-std';
|
||||
import { type FrameBlockModel, on, once } from '@blocksuite/blocks';
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import { css, html, nothing } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
@@ -0,0 +1,32 @@
|
||||
import {
|
||||
AFFINE_FRAME_PANEL_BODY,
|
||||
FramePanelBody,
|
||||
} from './body/frame-panel-body';
|
||||
import { AFFINE_FRAME_CARD, FrameCard } from './card/frame-card';
|
||||
import {
|
||||
AFFINE_FRAME_CARD_TITLE,
|
||||
FrameCardTitle,
|
||||
} from './card/frame-card-title';
|
||||
import {
|
||||
AFFINE_FRAME_TITLE_EDITOR,
|
||||
FrameCardTitleEditor,
|
||||
} from './card/frame-card-title-editor';
|
||||
import { AFFINE_FRAME_PANEL, FramePanel } from './frame-panel';
|
||||
import {
|
||||
AFFINE_FRAME_PANEL_HEADER,
|
||||
FramePanelHeader,
|
||||
} from './header/frame-panel-header';
|
||||
import {
|
||||
AFFINE_FRAMES_SETTING_MENU,
|
||||
FramesSettingMenu,
|
||||
} from './header/frames-setting-menu';
|
||||
|
||||
export function effects() {
|
||||
customElements.define(AFFINE_FRAME_PANEL, FramePanel);
|
||||
customElements.define(AFFINE_FRAME_TITLE_EDITOR, FrameCardTitleEditor);
|
||||
customElements.define(AFFINE_FRAME_CARD, FrameCard);
|
||||
customElements.define(AFFINE_FRAME_CARD_TITLE, FrameCardTitle);
|
||||
customElements.define(AFFINE_FRAME_PANEL_BODY, FramePanelBody);
|
||||
customElements.define(AFFINE_FRAME_PANEL_HEADER, FramePanelHeader);
|
||||
customElements.define(AFFINE_FRAMES_SETTING_MENU, FramesSettingMenu);
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import type { NavigatorMode } from '@blocksuite/affine-block-frame';
|
||||
import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
createButtonPopper,
|
||||
DocModeProvider,
|
||||
EdgelessLegacySlotIdentifier,
|
||||
EdgelessRootService,
|
||||
EditPropsStore,
|
||||
type NavigatorMode,
|
||||
} from '@blocksuite/blocks';
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { createButtonPopper } from '@blocksuite/affine-shared/utils';
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||
import { DisposableGroup, WithDisposable } from '@blocksuite/global/utils';
|
||||
import { PresentationIcon, SettingsIcon } from '@blocksuite/icons/lit';
|
||||
import { css, html, LitElement, type PropertyValues } from 'lit';
|
||||
@@ -112,13 +112,18 @@ export class FramePanelHeader extends WithDisposable(LitElement) {
|
||||
|
||||
private _edgelessDisposables: DisposableGroup | null = null;
|
||||
|
||||
private get _gfx() {
|
||||
return this.editorHost.std.get(GfxControllerIdentifier);
|
||||
}
|
||||
|
||||
private readonly _enterPresentationMode = () => {
|
||||
if (!this._edgelessRootService) {
|
||||
const docModeProvider = this.editorHost.std.get(DocModeProvider);
|
||||
if (docModeProvider.getEditorMode() !== 'edgeless') {
|
||||
this.editorHost.std.get(DocModeProvider).setEditorMode('edgeless');
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this._edgelessRootService?.gfx.tool.setTool({
|
||||
this._gfx.tool.setTool({
|
||||
type: 'frameNavigator',
|
||||
mode: this._navigatorMode,
|
||||
});
|
||||
@@ -132,8 +137,6 @@ export class FramePanelHeader extends WithDisposable(LitElement) {
|
||||
private _navigatorMode: NavigatorMode = 'fit';
|
||||
|
||||
private readonly _setEdgelessDisposables = () => {
|
||||
if (!this._edgelessRootService) return;
|
||||
|
||||
const slots = this.editorHost.std.get(EdgelessLegacySlotIdentifier);
|
||||
|
||||
this._clearEdgelessDisposables();
|
||||
@@ -145,10 +148,6 @@ export class FramePanelHeader extends WithDisposable(LitElement) {
|
||||
);
|
||||
};
|
||||
|
||||
private get _edgelessRootService() {
|
||||
return this.editorHost.std.getOptional(EdgelessRootService);
|
||||
}
|
||||
|
||||
private _tryLoadNavigatorStateLocalRecord() {
|
||||
this._navigatorMode = this.editorHost.std
|
||||
.get(EditPropsStore)
|
||||
@@ -219,7 +218,8 @@ export class FramePanelHeader extends WithDisposable(LitElement) {
|
||||
|
||||
override updated(_changedProperties: PropertyValues) {
|
||||
if (_changedProperties.has('editorHost')) {
|
||||
if (this._edgelessRootService) {
|
||||
const docModeProvider = this.editorHost.std.get(DocModeProvider);
|
||||
if (docModeProvider.getEditorMode() === 'edgeless') {
|
||||
this._setEdgelessDisposables();
|
||||
} else {
|
||||
this._clearEdgelessDisposables();
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
EdgelessLegacySlotIdentifier,
|
||||
EdgelessRootService,
|
||||
DocModeProvider,
|
||||
EditPropsStore,
|
||||
} from '@blocksuite/blocks';
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import { css, html, LitElement, type PropertyValues } from 'lit';
|
||||
import { property, state } from 'lit/decorators.js';
|
||||
@@ -103,10 +103,6 @@ export class FramesSettingMenu extends WithDisposable(LitElement) {
|
||||
this._editPropsStore.setStorage('presentHideToolbar', this.hideToolbar);
|
||||
};
|
||||
|
||||
private get _edgelessRootService() {
|
||||
return this.editorHost.std.getOptional(EdgelessRootService);
|
||||
}
|
||||
|
||||
private get _editPropsStore() {
|
||||
return this.editorHost.std.get(EditPropsStore);
|
||||
}
|
||||
@@ -176,7 +172,8 @@ export class FramesSettingMenu extends WithDisposable(LitElement) {
|
||||
|
||||
override updated(_changedProperties: PropertyValues) {
|
||||
if (_changedProperties.has('editorHost')) {
|
||||
if (this._edgelessRootService) {
|
||||
const docModeProvider = this.editorHost.std.get(DocModeProvider);
|
||||
if (docModeProvider.getEditorMode() === 'edgeless') {
|
||||
this.disposables.add(
|
||||
this.slots.navigatorSettingUpdated.on(
|
||||
({ blackBackground, hideToolbar }) => {
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './frame-panel';
|
||||
export * from './tool';
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { NavigatorMode } from '@blocksuite/affine-block-frame';
|
||||
import { BaseTool } from '@blocksuite/block-std/gfx';
|
||||
|
||||
import type { NavigatorMode } from '../../../_common/edgeless/frame/consts.js';
|
||||
|
||||
type PresentToolOption = {
|
||||
mode?: NavigatorMode;
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
import { type FrameBlockModel, on, once } from '@blocksuite/blocks';
|
||||
import type { FrameBlockModel } from '@blocksuite/affine-model';
|
||||
import { on, once } from '@blocksuite/affine-shared/utils';
|
||||
|
||||
import type { FramePanelBody } from '../body/frame-panel-body.js';
|
||||
import { FrameCard } from '../card/frame-card.js';
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
||||
},
|
||||
"include": ["./src"],
|
||||
"references": [
|
||||
{ "path": "../block-frame" },
|
||||
{ "path": "../block-surface" },
|
||||
{ "path": "../components" },
|
||||
{ "path": "../model" },
|
||||
{ "path": "../shared" },
|
||||
{ "path": "../../framework/block-std" },
|
||||
{ "path": "../../framework/global" },
|
||||
{ "path": "../../framework/inline" },
|
||||
{ "path": "../../framework/store" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "@blocksuite/affine-fragment-outline",
|
||||
"description": "Outline fragment for BlockSuite.",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test:unit": "nx vite:test --run --passWithNoTests",
|
||||
"test:unit:coverage": "nx vite:test --run --coverage",
|
||||
"test:e2e": "playwright test"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"keywords": [],
|
||||
"author": "toeverything",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@blocksuite/affine-block-note": "workspace:*",
|
||||
"@blocksuite/affine-components": "workspace:*",
|
||||
"@blocksuite/affine-model": "workspace:*",
|
||||
"@blocksuite/affine-shared": "workspace:*",
|
||||
"@blocksuite/block-std": "workspace:*",
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/icons": "^2.2.1",
|
||||
"@blocksuite/inline": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"@vanilla-extract/css": "^1.17.0",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./effects": "./src/effects.ts"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"dist",
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.19.0"
|
||||
}
|
||||
@@ -1,17 +1,10 @@
|
||||
import { effects } from '@blocksuite/affine-block-note/effects';
|
||||
import { changeNoteDisplayMode } from '@blocksuite/affine-block-note';
|
||||
import { NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine-model';
|
||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { matchModels } from '@blocksuite/affine-shared/utils';
|
||||
import { ShadowlessElement, SurfaceSelection } from '@blocksuite/block-std';
|
||||
import {
|
||||
changeNoteDisplayMode,
|
||||
matchModels,
|
||||
NoteBlockModel,
|
||||
NoteDisplayMode,
|
||||
} from '@blocksuite/blocks';
|
||||
import {
|
||||
Bound,
|
||||
noop,
|
||||
SignalWatcher,
|
||||
WithDisposable,
|
||||
} from '@blocksuite/global/utils';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||
import { Bound, SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
import { consume } from '@lit/context';
|
||||
import { effect, signal } from '@preact/signals-core';
|
||||
@@ -21,8 +14,6 @@ import { classMap } from 'lit/directives/class-map.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { when } from 'lit/directives/when.js';
|
||||
|
||||
noop(effects);
|
||||
|
||||
import { type TocContext, tocContext } from '../config';
|
||||
import type {
|
||||
ClickBlockEvent,
|
||||
@@ -78,10 +69,6 @@ export class OutlinePanelBody extends SignalWatcher(
|
||||
return this.editor.doc;
|
||||
}
|
||||
|
||||
private get edgeless() {
|
||||
return this.editor.querySelector('affine-edgeless-root');
|
||||
}
|
||||
|
||||
get viewportPadding(): [number, number, number, number] {
|
||||
const fitPadding = this._context.fitPadding$.value;
|
||||
return fitPadding.length === 4
|
||||
@@ -92,9 +79,9 @@ export class OutlinePanelBody extends SignalWatcher(
|
||||
}
|
||||
|
||||
private _deSelectNoteInEdgelessMode(note: NoteBlockModel) {
|
||||
if (!this.edgeless) return;
|
||||
const gfx = this.editor.std.get(GfxControllerIdentifier);
|
||||
const selection = gfx.selection;
|
||||
|
||||
const { selection } = this.edgeless.service;
|
||||
if (!selection.has(note.id)) return;
|
||||
const selectedIds = selection.selectedIds.filter(id => id !== note.id);
|
||||
selection.set({
|
||||
@@ -115,18 +102,12 @@ export class OutlinePanelBody extends SignalWatcher(
|
||||
}
|
||||
|
||||
private _fitToElement(e: FitViewEvent) {
|
||||
const edgeless = this.edgeless;
|
||||
|
||||
if (!edgeless) return;
|
||||
const gfx = this.editor.std.get(GfxControllerIdentifier);
|
||||
|
||||
const { block } = e.detail;
|
||||
const bound = Bound.deserialize(block.xywh);
|
||||
|
||||
edgeless.service.viewport.setViewportByBound(
|
||||
bound,
|
||||
this.viewportPadding,
|
||||
true
|
||||
);
|
||||
gfx.viewport.setViewportByBound(bound, this.viewportPadding, true);
|
||||
}
|
||||
|
||||
// when display mode change to page only, we should de-select the note if it is selected in edgeless mode
|
||||
@@ -199,6 +180,8 @@ export class OutlinePanelBody extends SignalWatcher(
|
||||
|
||||
private _selectNote(e: SelectEvent) {
|
||||
const { selected, id, multiselect } = e.detail;
|
||||
const gfx = this.editor.std.get(GfxControllerIdentifier);
|
||||
const editorMode = this.editor.std.get(DocModeProvider).getEditorMode();
|
||||
const note = this.doc.getBlock(id)?.model;
|
||||
if (!note || !matchModels(note, [NoteBlockModel])) return;
|
||||
|
||||
@@ -212,8 +195,8 @@ export class OutlinePanelBody extends SignalWatcher(
|
||||
selectedNotes = [note];
|
||||
}
|
||||
|
||||
if (this.edgeless) {
|
||||
this.edgeless?.service.selection.set({
|
||||
if (editorMode === 'edgeless') {
|
||||
gfx.selection.set({
|
||||
elements: selectedNotes.map(({ id }) => id),
|
||||
editing: false,
|
||||
});
|
||||
@@ -224,7 +207,9 @@ export class OutlinePanelBody extends SignalWatcher(
|
||||
|
||||
private _watchSelectedNotes() {
|
||||
return effect(() => {
|
||||
const { std, doc, mode } = this.editor;
|
||||
const { std, doc } = this.editor;
|
||||
const docModeService = this.editor.std.get(DocModeProvider);
|
||||
const mode = docModeService.getEditorMode();
|
||||
if (mode !== 'edgeless') return;
|
||||
|
||||
const currSelectedNotes = std.selection
|
||||
@@ -1,9 +1,6 @@
|
||||
import { type NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine-model';
|
||||
import { createButtonPopper } from '@blocksuite/affine-shared/utils';
|
||||
import { ShadowlessElement } from '@blocksuite/block-std';
|
||||
import {
|
||||
createButtonPopper,
|
||||
type NoteBlockModel,
|
||||
NoteDisplayMode,
|
||||
} from '@blocksuite/blocks';
|
||||
import { SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
|
||||
import { ArrowDownSmallIcon, InvisibleIcon } from '@blocksuite/icons/lit';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
@@ -1,16 +1,16 @@
|
||||
import type {
|
||||
AttachmentBlockModel,
|
||||
BookmarkBlockModel,
|
||||
CodeBlockModel,
|
||||
DatabaseBlockModel,
|
||||
ImageBlockModel,
|
||||
ListBlockModel,
|
||||
ParagraphBlockModel,
|
||||
RootBlockModel,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { DocDisplayMetaProvider } from '@blocksuite/affine-shared/services';
|
||||
import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
|
||||
import { ShadowlessElement } from '@blocksuite/block-std';
|
||||
import {
|
||||
type AttachmentBlockModel,
|
||||
type BookmarkBlockModel,
|
||||
type CodeBlockModel,
|
||||
type DatabaseBlockModel,
|
||||
DocDisplayMetaProvider,
|
||||
type ImageBlockModel,
|
||||
type ListBlockModel,
|
||||
type ParagraphBlockModel,
|
||||
type RootBlockModel,
|
||||
} from '@blocksuite/blocks';
|
||||
import { noop, SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
|
||||
import { LinkedPageIcon } from '@blocksuite/icons/lit';
|
||||
import type { DeltaInsert } from '@blocksuite/inline';
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { ParagraphBlockModel, Signal } from '@blocksuite/blocks';
|
||||
import type { ParagraphBlockModel } from '@blocksuite/affine-model';
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import {
|
||||
AttachmentIcon,
|
||||
BlockIcon,
|
||||
@@ -20,10 +21,9 @@ import {
|
||||
TextIcon,
|
||||
} from '@blocksuite/icons/lit';
|
||||
import { createContext } from '@lit/context';
|
||||
import type { Signal } from '@preact/signals-core';
|
||||
import type { TemplateResult } from 'lit';
|
||||
|
||||
import type { AffineEditorContainer } from '../../editors/editor-container.js';
|
||||
|
||||
const _16px = { width: '16px', height: '16px' };
|
||||
|
||||
const paragraphIconMap: Record<
|
||||
@@ -85,7 +85,7 @@ export const headingKeys = new Set(
|
||||
export const outlineSettingsKey = 'outlinePanelSettings';
|
||||
|
||||
export type TocContext = {
|
||||
editor$: Signal<AffineEditorContainer>;
|
||||
editor$: Signal<EditorHost>;
|
||||
enableSorting$: Signal<boolean>;
|
||||
showIcons$: Signal<boolean>;
|
||||
fitPadding$: Signal<number[]>;
|
||||
@@ -0,0 +1,39 @@
|
||||
import { AFFINE_OUTLINE_NOTICE, OutlineNotice } from './body/outline-notice';
|
||||
import {
|
||||
AFFINE_OUTLINE_PANEL_BODY,
|
||||
OutlinePanelBody,
|
||||
} from './body/outline-panel-body';
|
||||
import { AFFINE_OUTLINE_NOTE_CARD, OutlineNoteCard } from './card/outline-card';
|
||||
import {
|
||||
AFFINE_OUTLINE_BLOCK_PREVIEW,
|
||||
OutlineBlockPreview,
|
||||
} from './card/outline-preview';
|
||||
import {
|
||||
AFFINE_OUTLINE_PANEL_HEADER,
|
||||
OutlinePanelHeader,
|
||||
} from './header/outline-panel-header';
|
||||
import {
|
||||
AFFINE_OUTLINE_NOTE_PREVIEW_SETTING_MENU,
|
||||
OutlineNotePreviewSettingMenu,
|
||||
} from './header/outline-setting-menu';
|
||||
import {
|
||||
AFFINE_MOBILE_OUTLINE_MENU,
|
||||
MobileOutlineMenu,
|
||||
} from './mobile-outline-panel';
|
||||
import { AFFINE_OUTLINE_PANEL, OutlinePanel } from './outline-panel';
|
||||
import { AFFINE_OUTLINE_VIEWER, OutlineViewer } from './outline-viewer';
|
||||
|
||||
export function effects() {
|
||||
customElements.define(
|
||||
AFFINE_OUTLINE_NOTE_PREVIEW_SETTING_MENU,
|
||||
OutlineNotePreviewSettingMenu
|
||||
);
|
||||
customElements.define(AFFINE_OUTLINE_NOTICE, OutlineNotice);
|
||||
customElements.define(AFFINE_OUTLINE_PANEL, OutlinePanel);
|
||||
customElements.define(AFFINE_OUTLINE_PANEL_HEADER, OutlinePanelHeader);
|
||||
customElements.define(AFFINE_OUTLINE_NOTE_CARD, OutlineNoteCard);
|
||||
customElements.define(AFFINE_OUTLINE_VIEWER, OutlineViewer);
|
||||
customElements.define(AFFINE_MOBILE_OUTLINE_MENU, MobileOutlineMenu);
|
||||
customElements.define(AFFINE_OUTLINE_BLOCK_PREVIEW, OutlineBlockPreview);
|
||||
customElements.define(AFFINE_OUTLINE_PANEL_BODY, OutlinePanelBody);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createButtonPopper } from '@blocksuite/affine-shared/utils';
|
||||
import { ShadowlessElement } from '@blocksuite/block-std';
|
||||
import { createButtonPopper } from '@blocksuite/blocks';
|
||||
import { SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
|
||||
import { SettingsIcon, SortIcon } from '@blocksuite/icons/lit';
|
||||
import { consume } from '@lit/context';
|
||||
@@ -1,11 +1,16 @@
|
||||
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||
import { PropTypes, requiredProperties } from '@blocksuite/block-std';
|
||||
import {
|
||||
matchModels,
|
||||
NoteDisplayMode,
|
||||
ParagraphBlockModel,
|
||||
RootBlockModel,
|
||||
} from '@blocksuite/blocks';
|
||||
} from '@blocksuite/affine-model';
|
||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||
import { matchModels } from '@blocksuite/affine-shared/utils';
|
||||
import {
|
||||
type EditorHost,
|
||||
PropTypes,
|
||||
requiredProperties,
|
||||
} from '@blocksuite/block-std';
|
||||
import { SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
import { signal } from '@preact/signals-core';
|
||||
@@ -14,7 +19,6 @@ import { property } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
|
||||
import type { AffineEditorContainer } from '../../editors/editor-container.js';
|
||||
import { getHeadingBlocksFromDoc } from './utils/query.js';
|
||||
import {
|
||||
observeActiveHeadingDuringScroll,
|
||||
@@ -162,8 +166,9 @@ export class MobileOutlineMenu extends SignalWatcher(
|
||||
};
|
||||
|
||||
override render() {
|
||||
if (this.editor.doc.root === null || this.editor.mode === 'edgeless')
|
||||
return nothing;
|
||||
const docModeService = this.editor.std.get(DocModeProvider);
|
||||
const mode = docModeService.getEditorMode();
|
||||
if (this.editor.doc.root === null || mode === 'edgeless') return nothing;
|
||||
|
||||
const headingBlocks = getHeadingBlocksFromDoc(
|
||||
this.editor.doc,
|
||||
@@ -182,7 +187,7 @@ export class MobileOutlineMenu extends SignalWatcher(
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor editor!: AffineEditorContainer;
|
||||
accessor editor!: EditorHost;
|
||||
}
|
||||
|
||||
declare global {
|
||||
@@ -1,4 +1,6 @@
|
||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import {
|
||||
type EditorHost,
|
||||
PropTypes,
|
||||
requiredProperties,
|
||||
ShadowlessElement,
|
||||
@@ -9,7 +11,6 @@ import { effect, signal } from '@preact/signals-core';
|
||||
import { html, type PropertyValues } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
|
||||
import type { AffineEditorContainer } from '../../editors/editor-container.js';
|
||||
import { outlineSettingsKey, type TocContext, tocContext } from './config.js';
|
||||
import * as styles from './outline-panel.css';
|
||||
|
||||
@@ -21,6 +22,12 @@ export const AFFINE_OUTLINE_PANEL = 'affine-outline-panel';
|
||||
export class OutlinePanel extends SignalWatcher(
|
||||
WithDisposable(ShadowlessElement)
|
||||
) {
|
||||
private _getEditorMode(host: EditorHost) {
|
||||
const docModeService = host.std.get(DocModeProvider);
|
||||
const mode = docModeService.getEditorMode();
|
||||
return mode;
|
||||
}
|
||||
|
||||
private _setContext() {
|
||||
this._context = {
|
||||
editor$: signal(this.editor),
|
||||
@@ -39,7 +46,7 @@ export class OutlinePanel extends SignalWatcher(
|
||||
}
|
||||
|
||||
const editor = this._context.editor$.value;
|
||||
if (editor.mode === 'edgeless') {
|
||||
if (this._getEditorMode(editor) === 'edgeless') {
|
||||
this._context.enableSorting$.value = true;
|
||||
} else if (settings) {
|
||||
this._context.enableSorting$.value = settings.enableSorting;
|
||||
@@ -51,7 +58,8 @@ export class OutlinePanel extends SignalWatcher(
|
||||
private _watchSettingsChange() {
|
||||
this.disposables.add(
|
||||
effect(() => {
|
||||
if (this._context.editor$.value.mode === 'edgeless') return;
|
||||
if (this._getEditorMode(this._context.editor$.value) === 'edgeless')
|
||||
return;
|
||||
|
||||
const showPreviewIcon = this._context.showIcons$.value;
|
||||
const enableNotesSorting = this._context.enableSorting$.value;
|
||||
@@ -84,7 +92,7 @@ export class OutlinePanel extends SignalWatcher(
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (!this.editor.host) return;
|
||||
if (!this.editor) return;
|
||||
|
||||
return html`
|
||||
<affine-outline-panel-header></affine-outline-panel-header>
|
||||
@@ -97,7 +105,7 @@ export class OutlinePanel extends SignalWatcher(
|
||||
private accessor _context!: TocContext;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor editor!: AffineEditorContainer;
|
||||
accessor editor!: EditorHost;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor fitPadding!: number[];
|
||||
@@ -1,9 +1,12 @@
|
||||
import { NoteDisplayMode } from '@blocksuite/affine-model';
|
||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { scrollbarStyle } from '@blocksuite/affine-shared/styles';
|
||||
import {
|
||||
type EditorHost,
|
||||
PropTypes,
|
||||
requiredProperties,
|
||||
ShadowlessElement,
|
||||
} from '@blocksuite/block-std';
|
||||
import { NoteDisplayMode, scrollbarStyle } from '@blocksuite/blocks';
|
||||
import { SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
|
||||
import { TocIcon } from '@blocksuite/icons/lit';
|
||||
import { provide } from '@lit/context';
|
||||
@@ -13,7 +16,6 @@ import { property, query, state } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
|
||||
import type { AffineEditorContainer } from '../../editors/editor-container.js';
|
||||
import { type TocContext, tocContext } from './config.js';
|
||||
import { getHeadingBlocksFromDoc } from './utils/query.js';
|
||||
import {
|
||||
@@ -219,8 +221,9 @@ export class OutlineViewer extends SignalWatcher(
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (this.editor.doc.root === null || this.editor.mode === 'edgeless')
|
||||
return nothing;
|
||||
const docModeService = this.editor.std.get(DocModeProvider);
|
||||
const mode = docModeService.getEditorMode();
|
||||
if (this.editor.doc.root === null || mode === 'edgeless') return nothing;
|
||||
|
||||
const headingBlocks = getHeadingBlocksFromDoc(
|
||||
this.editor.doc,
|
||||
@@ -308,7 +311,7 @@ export class OutlineViewer extends SignalWatcher(
|
||||
private accessor _showViewer: boolean = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor editor!: AffineEditorContainer;
|
||||
accessor editor!: EditorHost;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor toggleOutlinePanel: (() => void) | null = null;
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { NoteBlockModel, NoteDisplayMode } from '@blocksuite/blocks';
|
||||
import type { NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine-model';
|
||||
|
||||
export type ReorderEvent = CustomEvent<{
|
||||
currentNumber: number;
|
||||
@@ -1,11 +1,10 @@
|
||||
import {
|
||||
BlocksUtils,
|
||||
matchModels,
|
||||
NoteBlockModel,
|
||||
NoteDisplayMode,
|
||||
ParagraphBlockModel,
|
||||
RootBlockModel,
|
||||
} from '@blocksuite/blocks';
|
||||
} from '@blocksuite/affine-model';
|
||||
import { matchModels } from '@blocksuite/affine-shared/utils';
|
||||
import type { BlockModel, Store } from '@blocksuite/store';
|
||||
|
||||
import { headingKeys } from '../config.js';
|
||||
@@ -35,14 +34,14 @@ export function getNotesFromDoc(
|
||||
}
|
||||
|
||||
export function isRootBlock(block: BlockModel): block is RootBlockModel {
|
||||
return BlocksUtils.matchModels(block, [RootBlockModel]);
|
||||
return matchModels(block, [RootBlockModel]);
|
||||
}
|
||||
|
||||
export function isHeadingBlock(
|
||||
block: BlockModel
|
||||
): block is ParagraphBlockModel {
|
||||
return (
|
||||
BlocksUtils.matchModels(block, [ParagraphBlockModel]) &&
|
||||
matchModels(block, [ParagraphBlockModel]) &&
|
||||
headingKeys.has(block.type$.value)
|
||||
);
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
import { getDocTitleByEditorHost } from '@blocksuite/affine-components/doc-title';
|
||||
import { NoteDisplayMode } from '@blocksuite/affine-model';
|
||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import type { Viewport } from '@blocksuite/affine-shared/types';
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import { NoteDisplayMode } from '@blocksuite/blocks';
|
||||
import { clamp, DisposableGroup } from '@blocksuite/global/utils';
|
||||
|
||||
import type { AffineEditorContainer } from '../../../editors/editor-container.js';
|
||||
import { getDocTitleByEditorHost } from '../../doc-title/index.js';
|
||||
import { getHeadingBlocksFromDoc } from './query.js';
|
||||
|
||||
export function scrollToBlock(editor: AffineEditorContainer, blockId: string) {
|
||||
const { host, mode } = editor;
|
||||
if (mode === 'edgeless' || !host) return;
|
||||
export function scrollToBlock(host: EditorHost, blockId: string) {
|
||||
const docModeService = host.std.get(DocModeProvider);
|
||||
const mode = docModeService.getEditorMode();
|
||||
if (mode === 'edgeless') return;
|
||||
|
||||
if (editor.doc.root?.id === blockId) {
|
||||
if (host.doc.root?.id === blockId) {
|
||||
const docTitle = getDocTitleByEditorHost(host);
|
||||
if (!docTitle) return;
|
||||
|
||||
@@ -50,12 +52,11 @@ export function isBlockBeforeViewportCenter(
|
||||
}
|
||||
|
||||
export const observeActiveHeadingDuringScroll = (
|
||||
getEditor: () => AffineEditorContainer, // workaround for editor changed
|
||||
getEditor: () => EditorHost, // workaround for editor changed
|
||||
update: (activeHeading: string | null) => void
|
||||
) => {
|
||||
const handler = () => {
|
||||
const { host } = getEditor();
|
||||
if (!host) return;
|
||||
const host = getEditor();
|
||||
|
||||
const headings = getHeadingBlocksFromDoc(
|
||||
host.doc,
|
||||
@@ -82,15 +83,14 @@ export const observeActiveHeadingDuringScroll = (
|
||||
let highlightMask: HTMLDivElement | null = null;
|
||||
let highlightTimeoutId: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
function highlightBlock(editor: AffineEditorContainer, blockId: string) {
|
||||
function highlightBlock(host: EditorHost, blockId: string) {
|
||||
const emptyClear = () => {};
|
||||
|
||||
const { host } = editor;
|
||||
if (!host) return emptyClear;
|
||||
if (host.doc.root?.id === blockId) return emptyClear;
|
||||
|
||||
if (editor.doc.root?.id === blockId) return emptyClear;
|
||||
|
||||
const rootComponent = host.querySelector('affine-page-root');
|
||||
const rootComponent = host.querySelector<
|
||||
HTMLElement & { viewport: Viewport }
|
||||
>('affine-page-root');
|
||||
if (!rootComponent) return emptyClear;
|
||||
|
||||
if (!rootComponent.viewport) {
|
||||
@@ -153,11 +153,11 @@ function highlightBlock(editor: AffineEditorContainer, blockId: string) {
|
||||
// this function is useful when the scroll need smooth animation
|
||||
let highlightIntervalId: ReturnType<typeof setInterval> | null = null;
|
||||
export async function scrollToBlockWithHighlight(
|
||||
editor: AffineEditorContainer,
|
||||
host: EditorHost,
|
||||
blockId: string,
|
||||
timeout = 3000
|
||||
) {
|
||||
scrollToBlock(editor, blockId);
|
||||
scrollToBlock(host, blockId);
|
||||
|
||||
let timeCount = 0;
|
||||
|
||||
@@ -174,10 +174,9 @@ export async function scrollToBlockWithHighlight(
|
||||
return;
|
||||
}
|
||||
|
||||
const { host } = editor;
|
||||
const block = host?.view.getBlock(blockId);
|
||||
const block = host.view.getBlock(blockId);
|
||||
|
||||
if (!host || !block || timeCount > timeout) {
|
||||
if (!block || timeCount > timeout) {
|
||||
clearInterval(highlightIntervalId);
|
||||
resolve(() => {});
|
||||
return;
|
||||
@@ -195,7 +194,7 @@ export async function scrollToBlockWithHighlight(
|
||||
clearInterval(highlightIntervalId);
|
||||
|
||||
// highlight block
|
||||
resolve(highlightBlock(editor, blockId));
|
||||
resolve(highlightBlock(host, blockId));
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
||||
},
|
||||
"include": ["./src"],
|
||||
"references": [
|
||||
{ "path": "../block-note" },
|
||||
{ "path": "../components" },
|
||||
{ "path": "../model" },
|
||||
{ "path": "../shared" },
|
||||
{ "path": "../../framework/block-std" },
|
||||
{ "path": "../../framework/global" },
|
||||
{ "path": "../../framework/inline" },
|
||||
{ "path": "../../framework/store" }
|
||||
]
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
import { Pane } from 'tweakpane';
|
||||
|
||||
import { getSentenceRects, segmentSentences } from './text-utils.js';
|
||||
import type { ParagraphLayout, SectionLayout } from './types.js';
|
||||
import type { ParagraphLayout, ViewportLayout } from './types.js';
|
||||
import type { ViewportTurboRendererExtension } from './viewport-renderer.js';
|
||||
|
||||
export function syncCanvasSize(canvas: HTMLCanvasElement, host: HTMLElement) {
|
||||
@@ -21,30 +21,30 @@ export function syncCanvasSize(canvas: HTMLCanvasElement, host: HTMLElement) {
|
||||
canvas.style.pointerEvents = 'none';
|
||||
}
|
||||
|
||||
export function getSectionLayout(
|
||||
export function getViewportLayout(
|
||||
host: HTMLElement,
|
||||
viewport: Viewport
|
||||
): SectionLayout {
|
||||
): ViewportLayout {
|
||||
const paragraphBlocks = host.querySelectorAll(
|
||||
'.affine-paragraph-rich-text-wrapper [data-v-text="true"]'
|
||||
);
|
||||
|
||||
const zoom = viewport.zoom;
|
||||
|
||||
let sectionMinX = Infinity;
|
||||
let sectionMinY = Infinity;
|
||||
let sectionMaxX = -Infinity;
|
||||
let sectionMaxY = -Infinity;
|
||||
let layoutMinX = Infinity;
|
||||
let layoutMinY = Infinity;
|
||||
let layoutMaxX = -Infinity;
|
||||
let layoutMaxY = -Infinity;
|
||||
|
||||
const paragraphs: ParagraphLayout[] = Array.from(paragraphBlocks).map(p => {
|
||||
const sentences = segmentSentences(p.textContent || '');
|
||||
const sentenceLayouts = sentences.map(sentence => {
|
||||
const rects = getSentenceRects(p, sentence);
|
||||
rects.forEach(({ rect }) => {
|
||||
sectionMinX = Math.min(sectionMinX, rect.x);
|
||||
sectionMinY = Math.min(sectionMinY, rect.y);
|
||||
sectionMaxX = Math.max(sectionMaxX, rect.x + rect.w);
|
||||
sectionMaxY = Math.max(sectionMaxY, rect.y + rect.h);
|
||||
layoutMinX = Math.min(layoutMinX, rect.x);
|
||||
layoutMinY = Math.min(layoutMinY, rect.y);
|
||||
layoutMaxX = Math.max(layoutMaxX, rect.x + rect.w);
|
||||
layoutMaxY = Math.max(layoutMaxY, rect.y + rect.h);
|
||||
});
|
||||
return {
|
||||
text: sentence,
|
||||
@@ -72,22 +72,22 @@ export function getSectionLayout(
|
||||
};
|
||||
});
|
||||
|
||||
const sectionModelCoord = viewport.toModelCoordFromClientCoord([
|
||||
sectionMinX,
|
||||
sectionMinY,
|
||||
const layoutModelCoord = viewport.toModelCoordFromClientCoord([
|
||||
layoutMinX,
|
||||
layoutMinY,
|
||||
]);
|
||||
const w = (sectionMaxX - sectionMinX) / zoom / viewport.viewScale;
|
||||
const h = (sectionMaxY - sectionMinY) / zoom / viewport.viewScale;
|
||||
const section: SectionLayout = {
|
||||
const w = (layoutMaxX - layoutMinX) / zoom / viewport.viewScale;
|
||||
const h = (layoutMaxY - layoutMinY) / zoom / viewport.viewScale;
|
||||
const layout: ViewportLayout = {
|
||||
paragraphs,
|
||||
rect: {
|
||||
x: sectionModelCoord[0],
|
||||
y: sectionModelCoord[1],
|
||||
x: layoutModelCoord[0],
|
||||
y: layoutModelCoord[1],
|
||||
w: Math.max(w, 0),
|
||||
h: Math.max(h, 0),
|
||||
},
|
||||
};
|
||||
return section;
|
||||
return layout;
|
||||
}
|
||||
|
||||
export function initTweakpane(
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { type SectionLayout } from './types.js';
|
||||
import { type ViewportLayout } from './types.js';
|
||||
|
||||
type WorkerMessagePaint = {
|
||||
type: 'paintSection';
|
||||
type: 'paintLayout';
|
||||
data: {
|
||||
section: SectionLayout;
|
||||
layout: ViewportLayout;
|
||||
width: number;
|
||||
height: number;
|
||||
dpr: number;
|
||||
@@ -38,20 +38,15 @@ function getBaseline() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/** Section painter in worker */
|
||||
class SectionPainter {
|
||||
/** Layout painter in worker */
|
||||
class LayoutPainter {
|
||||
private readonly canvas: OffscreenCanvas = new OffscreenCanvas(0, 0);
|
||||
private ctx: OffscreenCanvasRenderingContext2D | null = null;
|
||||
private zoom = 1;
|
||||
|
||||
setSize(
|
||||
sectionRectW: number,
|
||||
sectionRectH: number,
|
||||
dpr: number,
|
||||
zoom: number
|
||||
) {
|
||||
const width = sectionRectW * dpr * zoom;
|
||||
const height = sectionRectH * dpr * zoom;
|
||||
setSize(layoutRectW: number, layoutRectH: number, dpr: number, zoom: number) {
|
||||
const width = layoutRectW * dpr * zoom;
|
||||
const height = layoutRectH * dpr * zoom;
|
||||
|
||||
this.canvas.width = width;
|
||||
this.canvas.height = height;
|
||||
@@ -68,11 +63,11 @@ class SectionPainter {
|
||||
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
}
|
||||
|
||||
paint(section: SectionLayout) {
|
||||
paint(layout: ViewportLayout) {
|
||||
const { canvas, ctx } = this;
|
||||
if (!canvas || !ctx) return;
|
||||
if (section.rect.w === 0 || section.rect.h === 0) {
|
||||
console.warn('empty section rect');
|
||||
if (layout.rect.w === 0 || layout.rect.h === 0) {
|
||||
console.warn('empty layout rect');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -83,7 +78,7 @@ class SectionPainter {
|
||||
// Track rendered positions to avoid duplicate rendering across all paragraphs and sentences
|
||||
const renderedPositions = new Set<string>();
|
||||
|
||||
section.paragraphs.forEach(paragraph => {
|
||||
layout.paragraphs.forEach(paragraph => {
|
||||
const fontSize = 15;
|
||||
ctx.font = `300 ${fontSize}px Inter`;
|
||||
const baselineY = getBaseline();
|
||||
@@ -91,8 +86,8 @@ class SectionPainter {
|
||||
paragraph.sentences.forEach(sentence => {
|
||||
ctx.strokeStyle = 'yellow';
|
||||
sentence.rects.forEach(textRect => {
|
||||
const x = textRect.rect.x - section.rect.x;
|
||||
const y = textRect.rect.y - section.rect.y;
|
||||
const x = textRect.rect.x - layout.rect.x;
|
||||
const y = textRect.rect.y - layout.rect.y;
|
||||
|
||||
const posKey = `${x},${y}`;
|
||||
// Only render if we haven't rendered at this position before
|
||||
@@ -112,7 +107,7 @@ class SectionPainter {
|
||||
}
|
||||
}
|
||||
|
||||
const painter = new SectionPainter();
|
||||
const painter = new LayoutPainter();
|
||||
let fontLoaded = false;
|
||||
|
||||
font
|
||||
@@ -131,10 +126,10 @@ self.onmessage = async (e: MessageEvent<WorkerMessage>) => {
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'paintSection': {
|
||||
const { section, width, height, dpr, zoom } = data;
|
||||
case 'paintLayout': {
|
||||
const { layout, width, height, dpr, zoom } = data;
|
||||
painter.setSize(width, height, dpr, zoom);
|
||||
painter.paint(section);
|
||||
painter.paint(layout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export interface ParagraphLayout {
|
||||
zoom: number;
|
||||
}
|
||||
|
||||
export interface SectionLayout {
|
||||
export interface ViewportLayout {
|
||||
paragraphs: ParagraphLayout[];
|
||||
rect: Rect;
|
||||
}
|
||||
|
||||
@@ -6,15 +6,15 @@ import {
|
||||
} from '@blocksuite/block-std';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||
import { type Container, type ServiceIdentifier } from '@blocksuite/global/di';
|
||||
import { nextTick } from '@blocksuite/global/utils';
|
||||
import { debounce, DisposableGroup } from '@blocksuite/global/utils';
|
||||
import { type Pane } from 'tweakpane';
|
||||
|
||||
import {
|
||||
getSectionLayout,
|
||||
getViewportLayout,
|
||||
initTweakpane,
|
||||
syncCanvasSize,
|
||||
} from './dom-utils.js';
|
||||
import { type SectionLayout } from './types.js';
|
||||
import { type ViewportLayout } from './types.js';
|
||||
|
||||
export const ViewportTurboRendererIdentifier = LifeCycleWatcherIdentifier(
|
||||
'ViewportTurboRenderer'
|
||||
@@ -22,10 +22,15 @@ export const ViewportTurboRendererIdentifier = LifeCycleWatcherIdentifier(
|
||||
|
||||
interface Tile {
|
||||
bitmap: ImageBitmap;
|
||||
zoom: number;
|
||||
}
|
||||
|
||||
// With high enough zoom, fallback to DOM rendering
|
||||
const zoomThreshold = 1;
|
||||
|
||||
export class ViewportTurboRendererExtension extends LifeCycleWatcher {
|
||||
state: 'monitoring' | 'paused' = 'paused';
|
||||
disposables = new DisposableGroup();
|
||||
|
||||
static override setup(di: Container) {
|
||||
di.addImpl(ViewportTurboRendererIdentifier, this, [StdIdentifier]);
|
||||
@@ -33,8 +38,7 @@ export class ViewportTurboRendererExtension extends LifeCycleWatcher {
|
||||
|
||||
public readonly canvas: HTMLCanvasElement = document.createElement('canvas');
|
||||
private readonly worker: Worker;
|
||||
private lastZoom: number | null = null;
|
||||
private lastSection: SectionLayout | null = null;
|
||||
private layoutCache: ViewportLayout | null = null;
|
||||
private tile: Tile | null = null;
|
||||
private debugPane: Pane | null = null;
|
||||
|
||||
@@ -56,6 +60,19 @@ export class ViewportTurboRendererExtension extends LifeCycleWatcher {
|
||||
this.refresh().catch(console.error);
|
||||
});
|
||||
|
||||
const debounceOptions = { leading: false, trailing: true };
|
||||
const debouncedLayoutUpdate = debounce(
|
||||
() => this.updateLayoutCache(),
|
||||
500,
|
||||
debounceOptions
|
||||
);
|
||||
this.disposables.add(
|
||||
this.std.store.slots.blockUpdated.on(() => {
|
||||
this.clearTile();
|
||||
debouncedLayoutUpdate();
|
||||
})
|
||||
);
|
||||
|
||||
document.fonts.load('15px Inter').then(() => {
|
||||
// this.state = 'monitoring';
|
||||
this.refresh().catch(console.error);
|
||||
@@ -63,16 +80,14 @@ export class ViewportTurboRendererExtension extends LifeCycleWatcher {
|
||||
}
|
||||
|
||||
override unmounted() {
|
||||
if (this.tile) {
|
||||
this.tile.bitmap.close();
|
||||
this.tile = null;
|
||||
}
|
||||
this.clearTile();
|
||||
if (this.debugPane) {
|
||||
this.debugPane.dispose();
|
||||
this.debugPane = null;
|
||||
}
|
||||
this.worker.terminate();
|
||||
this.canvas.remove();
|
||||
this.disposables.dispose();
|
||||
}
|
||||
|
||||
get viewport() {
|
||||
@@ -82,30 +97,44 @@ export class ViewportTurboRendererExtension extends LifeCycleWatcher {
|
||||
async refresh(force = false) {
|
||||
if (this.state === 'paused' && !force) return;
|
||||
|
||||
await nextTick(); // Improves stability during zooming
|
||||
|
||||
if (this.canUseCache()) {
|
||||
this.drawCachedBitmap(this.lastSection!);
|
||||
if (this.viewport.zoom > zoomThreshold) {
|
||||
this.clearCanvas();
|
||||
} else if (this.canUseBitmapCache()) {
|
||||
this.drawCachedBitmap(this.layoutCache!);
|
||||
} else {
|
||||
const section = getSectionLayout(this.std.host, this.viewport);
|
||||
await this.paintSection(section);
|
||||
this.lastSection = section;
|
||||
this.lastZoom = this.viewport.zoom;
|
||||
this.drawCachedBitmap(section);
|
||||
// Unneeded most of the time, the DOM query is debounced after block update
|
||||
if (!this.layoutCache) {
|
||||
this.updateLayoutCache();
|
||||
}
|
||||
|
||||
await this.paintLayout(this.layoutCache!);
|
||||
this.drawCachedBitmap(this.layoutCache!);
|
||||
}
|
||||
}
|
||||
|
||||
private async paintSection(section: SectionLayout): Promise<void> {
|
||||
private updateLayoutCache() {
|
||||
const layout = getViewportLayout(this.std.host, this.viewport);
|
||||
this.layoutCache = layout;
|
||||
}
|
||||
|
||||
private clearTile() {
|
||||
if (this.tile) {
|
||||
this.tile.bitmap.close();
|
||||
this.tile = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async paintLayout(layout: ViewportLayout): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
if (!this.worker) return;
|
||||
|
||||
const dpr = window.devicePixelRatio;
|
||||
this.worker.postMessage({
|
||||
type: 'paintSection',
|
||||
type: 'paintLayout',
|
||||
data: {
|
||||
section,
|
||||
width: section.rect.w,
|
||||
height: section.rect.h,
|
||||
layout,
|
||||
width: layout.rect.w,
|
||||
height: layout.rect.h,
|
||||
dpr,
|
||||
zoom: this.viewport.zoom,
|
||||
},
|
||||
@@ -113,7 +142,7 @@ export class ViewportTurboRendererExtension extends LifeCycleWatcher {
|
||||
|
||||
this.worker.onmessage = (e: MessageEvent) => {
|
||||
if (e.data.type === 'bitmapPainted') {
|
||||
this.handlePaintedBitmap(e.data.bitmap, section, resolve);
|
||||
this.handlePaintedBitmap(e.data.bitmap, layout, resolve);
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -121,40 +150,49 @@ export class ViewportTurboRendererExtension extends LifeCycleWatcher {
|
||||
|
||||
private handlePaintedBitmap(
|
||||
bitmap: ImageBitmap,
|
||||
section: SectionLayout,
|
||||
layout: ViewportLayout,
|
||||
resolve: () => void
|
||||
) {
|
||||
if (this.tile) {
|
||||
this.tile.bitmap.close();
|
||||
}
|
||||
this.tile = { bitmap };
|
||||
this.drawCachedBitmap(section);
|
||||
this.tile = {
|
||||
bitmap,
|
||||
zoom: this.viewport.zoom,
|
||||
};
|
||||
this.drawCachedBitmap(layout);
|
||||
resolve();
|
||||
}
|
||||
|
||||
private canUseCache(): boolean {
|
||||
private canUseBitmapCache(): boolean {
|
||||
return (
|
||||
!!this.lastSection && !!this.tile && this.viewport.zoom === this.lastZoom
|
||||
!!this.layoutCache && !!this.tile && this.viewport.zoom === this.tile.zoom
|
||||
);
|
||||
}
|
||||
|
||||
private drawCachedBitmap(section: SectionLayout) {
|
||||
private clearCanvas() {
|
||||
const ctx = this.canvas.getContext('2d');
|
||||
if (!ctx) return;
|
||||
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
}
|
||||
|
||||
private drawCachedBitmap(layout: ViewportLayout) {
|
||||
const bitmap = this.tile!.bitmap;
|
||||
const ctx = this.canvas.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
const sectionViewCoord = this.viewport.toViewCoord(
|
||||
section.rect.x,
|
||||
section.rect.y
|
||||
this.clearCanvas();
|
||||
const layoutViewCoord = this.viewport.toViewCoord(
|
||||
layout.rect.x,
|
||||
layout.rect.y
|
||||
);
|
||||
|
||||
ctx.drawImage(
|
||||
bitmap,
|
||||
sectionViewCoord[0] * window.devicePixelRatio,
|
||||
sectionViewCoord[1] * window.devicePixelRatio,
|
||||
section.rect.w * window.devicePixelRatio * this.viewport.zoom,
|
||||
section.rect.h * window.devicePixelRatio * this.viewport.zoom
|
||||
layoutViewCoord[0] * window.devicePixelRatio,
|
||||
layoutViewCoord[1] * window.devicePixelRatio,
|
||||
layout.rect.w * window.devicePixelRatio * this.viewport.zoom,
|
||||
layout.rect.h * window.devicePixelRatio * this.viewport.zoom
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
"@blocksuite/affine-block-surface-ref": "workspace:*",
|
||||
"@blocksuite/affine-block-table": "workspace:*",
|
||||
"@blocksuite/affine-components": "workspace:*",
|
||||
"@blocksuite/affine-fragment-frame-panel": "workspace:*",
|
||||
"@blocksuite/affine-fragment-outline": "workspace:*",
|
||||
"@blocksuite/affine-model": "workspace:*",
|
||||
"@blocksuite/affine-shared": "workspace:*",
|
||||
"@blocksuite/affine-widget-drag-handle": "workspace:*",
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export type NavigatorMode = 'fill' | 'fit';
|
||||
@@ -1,11 +1,12 @@
|
||||
import { ConnectionOverlay } from '@blocksuite/affine-block-surface';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
|
||||
import { EdgelessRootBlockSpec } from '../../root-block/edgeless/edgeless-root-spec.js';
|
||||
import {
|
||||
EdgelessFrameManager,
|
||||
FrameOverlay,
|
||||
} from '../../root-block/edgeless/frame-manager.js';
|
||||
} from '@blocksuite/affine-block-frame';
|
||||
import { ConnectionOverlay } from '@blocksuite/affine-block-surface';
|
||||
import { PresentTool } from '@blocksuite/affine-fragment-frame-panel';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
|
||||
import { EdgelessRootBlockSpec } from '../../root-block/edgeless/edgeless-root-spec.js';
|
||||
import { BrushTool } from '../../root-block/edgeless/gfx-tool/brush-tool.js';
|
||||
import { ConnectorTool } from '../../root-block/edgeless/gfx-tool/connector-tool.js';
|
||||
import { CopilotTool } from '../../root-block/edgeless/gfx-tool/copilot-tool.js';
|
||||
@@ -13,7 +14,6 @@ import { DefaultTool } from '../../root-block/edgeless/gfx-tool/default-tool.js'
|
||||
import { MindMapIndicatorOverlay } from '../../root-block/edgeless/gfx-tool/default-tool-ext/mind-map-ext/indicator-overlay.js';
|
||||
import { EmptyTool } from '../../root-block/edgeless/gfx-tool/empty-tool.js';
|
||||
import { EraserTool } from '../../root-block/edgeless/gfx-tool/eraser-tool.js';
|
||||
import { PresentTool } from '../../root-block/edgeless/gfx-tool/frame-navigator-tool.js';
|
||||
import { FrameTool } from '../../root-block/edgeless/gfx-tool/frame-tool.js';
|
||||
import { LassoTool } from '../../root-block/edgeless/gfx-tool/lasso-tool.js';
|
||||
import { NoteTool } from '../../root-block/edgeless/gfx-tool/note-tool.js';
|
||||
|
||||
@@ -22,6 +22,7 @@ import { effects as componentCaptionEffects } from '@blocksuite/affine-component
|
||||
import { effects as componentColorPickerEffects } from '@blocksuite/affine-components/color-picker';
|
||||
import { effects as componentContextMenuEffects } from '@blocksuite/affine-components/context-menu';
|
||||
import { effects as componentDatePickerEffects } from '@blocksuite/affine-components/date-picker';
|
||||
import { effects as componentDocTitleEffects } from '@blocksuite/affine-components/doc-title';
|
||||
import { effects as componentDropIndicatorEffects } from '@blocksuite/affine-components/drop-indicator';
|
||||
import { effects as componentEmbedCardModalEffects } from '@blocksuite/affine-components/embed-card-modal';
|
||||
import { FilterableListComponent } from '@blocksuite/affine-components/filterable-list';
|
||||
@@ -32,6 +33,8 @@ import { SmoothCorner } from '@blocksuite/affine-components/smooth-corner';
|
||||
import { effects as componentToggleButtonEffects } from '@blocksuite/affine-components/toggle-button';
|
||||
import { ToggleSwitch } from '@blocksuite/affine-components/toggle-switch';
|
||||
import { effects as componentToolbarEffects } from '@blocksuite/affine-components/toolbar';
|
||||
import { effects as fragmentFramePanelEffects } from '@blocksuite/affine-fragment-frame-panel/effects';
|
||||
import { effects as fragmentOutlineEffects } from '@blocksuite/affine-fragment-outline/effects';
|
||||
import { effects as widgetDragHandleEffects } from '@blocksuite/affine-widget-drag-handle/effects';
|
||||
import { effects as widgetEdgelessAutoConnectEffects } from '@blocksuite/affine-widget-edgeless-auto-connect/effects';
|
||||
import { effects as widgetFrameTitleEffects } from '@blocksuite/affine-widget-frame-title/effects';
|
||||
@@ -184,6 +187,8 @@ export function effects() {
|
||||
stdEffects();
|
||||
inlineEffects();
|
||||
|
||||
dataViewEffects();
|
||||
|
||||
blockNoteEffects();
|
||||
blockAttachmentEffects();
|
||||
blockBookmarkEffects();
|
||||
@@ -213,6 +218,7 @@ export function effects() {
|
||||
componentAiItemEffects();
|
||||
componentColorPickerEffects();
|
||||
componentEmbedCardModalEffects();
|
||||
componentDocTitleEffects();
|
||||
|
||||
widgetScrollAnchoringEffects();
|
||||
widgetMobileToolbarEffects();
|
||||
@@ -222,7 +228,9 @@ export function effects() {
|
||||
widgetRemoteSelectionEffects();
|
||||
widgetDragHandleEffects();
|
||||
widgetEdgelessAutoConnectEffects();
|
||||
dataViewEffects();
|
||||
|
||||
fragmentFramePanelEffects();
|
||||
fragmentOutlineEffects();
|
||||
|
||||
customElements.define('affine-page-root', PageRootBlockComponent);
|
||||
customElements.define('affine-preview-root', PreviewRootBlockComponent);
|
||||
|
||||
@@ -7,7 +7,6 @@ import { splitElements } from './root-block/edgeless/utils/clipboard-utils.js';
|
||||
import { isCanvasElement } from './root-block/edgeless/utils/query.js';
|
||||
|
||||
export * from './_common/adapters/index.js';
|
||||
export { type NavigatorMode } from './_common/edgeless/frame/consts.js';
|
||||
export * from './_common/transformers/index.js';
|
||||
export * from './_specs/index.js';
|
||||
export { EdgelessTemplatePanel } from './root-block/edgeless/components/toolbar/template/template-panel.js';
|
||||
@@ -16,10 +15,6 @@ export type {
|
||||
TemplateCategory,
|
||||
TemplateManager,
|
||||
} from './root-block/edgeless/components/toolbar/template/template-type.js';
|
||||
export {
|
||||
EdgelessFrameManager,
|
||||
FrameOverlay,
|
||||
} from './root-block/edgeless/frame-manager.js';
|
||||
export { CopilotTool } from './root-block/edgeless/gfx-tool/copilot-tool.js';
|
||||
export * from './root-block/edgeless/gfx-tool/index.js';
|
||||
export { EditPropsMiddlewareBuilder } from './root-block/edgeless/middlewares/base.js';
|
||||
@@ -58,6 +53,10 @@ export {
|
||||
type MenuOptions,
|
||||
onMenuOpen,
|
||||
} from '@blocksuite/affine-components/context-menu';
|
||||
export {
|
||||
DocTitle,
|
||||
getDocTitleByEditorHost,
|
||||
} from '@blocksuite/affine-components/doc-title';
|
||||
export {
|
||||
HoverController,
|
||||
whenHover,
|
||||
@@ -91,6 +90,8 @@ export {
|
||||
ToolbarMoreMenuConfigExtension,
|
||||
Tooltip,
|
||||
} from '@blocksuite/affine-components/toolbar';
|
||||
export * from '@blocksuite/affine-fragment-frame-panel';
|
||||
export * from '@blocksuite/affine-fragment-outline';
|
||||
export * from '@blocksuite/affine-model';
|
||||
export {
|
||||
AttachmentAdapter,
|
||||
|
||||
@@ -6,6 +6,11 @@ import {
|
||||
SYNCED_MIN_HEIGHT,
|
||||
SYNCED_MIN_WIDTH,
|
||||
} from '@blocksuite/affine-block-embed';
|
||||
import {
|
||||
type EdgelessFrameManager,
|
||||
type FrameOverlay,
|
||||
isFrameBlock,
|
||||
} from '@blocksuite/affine-block-frame';
|
||||
import {
|
||||
CanvasElementType,
|
||||
isNoteBlock,
|
||||
@@ -65,10 +70,6 @@ import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
|
||||
import type {
|
||||
EdgelessFrameManager,
|
||||
FrameOverlay,
|
||||
} from '../../frame-manager.js';
|
||||
import {
|
||||
AI_CHAT_BLOCK_MAX_HEIGHT,
|
||||
AI_CHAT_BLOCK_MAX_WIDTH,
|
||||
@@ -90,7 +91,6 @@ import {
|
||||
isEmbedLoomBlock,
|
||||
isEmbedSyncedDocBlock,
|
||||
isEmbedYoutubeBlock,
|
||||
isFrameBlock,
|
||||
isImageBlock,
|
||||
isMindmapNode,
|
||||
} from '../../utils/query.js';
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import {
|
||||
isFrameBlock,
|
||||
type NavigatorMode,
|
||||
} from '@blocksuite/affine-block-frame';
|
||||
import { toast } from '@blocksuite/affine-components/toast';
|
||||
import type { FrameBlockModel } from '@blocksuite/affine-model';
|
||||
import { EditPropsStore } from '@blocksuite/affine-shared/services';
|
||||
@@ -15,9 +19,7 @@ import { cssVar } from '@toeverything/theme';
|
||||
import { css, html, LitElement, nothing, type PropertyValues } from 'lit';
|
||||
import { property, state } from 'lit/decorators.js';
|
||||
|
||||
import type { NavigatorMode } from '../../../../_common/edgeless/frame/consts.js';
|
||||
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
|
||||
import { isFrameBlock } from '../../utils/query.js';
|
||||
import { launchIntoFullscreen } from '../utils.js';
|
||||
import { EdgelessToolbarToolMixin } from './mixins/tool.mixin.js';
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { EdgelessFrameManager } from '@blocksuite/affine-block-frame';
|
||||
import {
|
||||
EdgelessCRUDIdentifier,
|
||||
EdgelessLegacySlotIdentifier,
|
||||
@@ -36,7 +37,6 @@ import { Bound, getCommonBound } from '@blocksuite/global/utils';
|
||||
import { effect } from '@preact/signals-core';
|
||||
|
||||
import { RootService } from '../root-service.js';
|
||||
import type { EdgelessFrameManager } from './frame-manager.js';
|
||||
import { TemplateJob } from './services/template.js';
|
||||
import {
|
||||
createInsertPlaceMiddleware,
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { insertEdgelessTextCommand } from '@blocksuite/affine-block-edgeless-text';
|
||||
import {
|
||||
type EdgelessFrameManager,
|
||||
type FrameOverlay,
|
||||
isFrameBlock,
|
||||
} from '@blocksuite/affine-block-frame';
|
||||
import {
|
||||
ConnectorUtils,
|
||||
isNoteBlock,
|
||||
@@ -50,14 +55,9 @@ import { effect } from '@preact/signals-core';
|
||||
|
||||
import { isSingleMindMapNode } from '../../../_common/edgeless/mindmap/index.js';
|
||||
import type { EdgelessRootBlockComponent } from '../edgeless-root-block.js';
|
||||
import type { EdgelessFrameManager, FrameOverlay } from '../frame-manager.js';
|
||||
import { prepareCloneData } from '../utils/clone-utils.js';
|
||||
import { calPanDelta } from '../utils/panning-utils.js';
|
||||
import {
|
||||
isCanvasElement,
|
||||
isEdgelessTextBlock,
|
||||
isFrameBlock,
|
||||
} from '../utils/query.js';
|
||||
import { isCanvasElement, isEdgelessTextBlock } from '../utils/query.js';
|
||||
import type { EdgelessSnapManager } from '../utils/snap-manager.js';
|
||||
import {
|
||||
addText,
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import type {
|
||||
EdgelessFrameManager,
|
||||
FrameOverlay,
|
||||
} from '@blocksuite/affine-block-frame';
|
||||
import { OverlayIdentifier } from '@blocksuite/affine-block-surface';
|
||||
import type { FrameBlockModel } from '@blocksuite/affine-model';
|
||||
import {
|
||||
@@ -15,8 +19,6 @@ import { Bound, Vec } from '@blocksuite/global/utils';
|
||||
import { Text } from '@blocksuite/store';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import type { EdgelessFrameManager, FrameOverlay } from '../frame-manager.js';
|
||||
|
||||
export class FrameTool extends BaseTool {
|
||||
static override toolName = 'frame';
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ export { CopilotTool } from './copilot-tool.js';
|
||||
export { DefaultTool } from './default-tool.js';
|
||||
export { EmptyTool } from './empty-tool.js';
|
||||
export { EraserTool } from './eraser-tool.js';
|
||||
export { PresentTool } from './frame-navigator-tool.js';
|
||||
export { FrameTool } from './frame-tool.js';
|
||||
export { LassoTool, type LassoToolOption } from './lasso-tool.js';
|
||||
export { NoteTool, type NoteToolOption } from './note-tool.js';
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import {
|
||||
EdgelessFrameManager,
|
||||
isFrameBlock,
|
||||
} from '@blocksuite/affine-block-frame';
|
||||
import { isNoteBlock } from '@blocksuite/affine-block-surface';
|
||||
import type {
|
||||
EdgelessTextBlockModel,
|
||||
@@ -18,12 +22,10 @@ import { getCommonBoundWithRotation, groupBy } from '@blocksuite/global/utils';
|
||||
import { type BlockSnapshot, BlockSnapshotSchema } from '@blocksuite/store';
|
||||
|
||||
import type { EdgelessRootBlockComponent } from '../edgeless-root-block.js';
|
||||
import { EdgelessFrameManager } from '../frame-manager.js';
|
||||
import { getSortedCloneElements, prepareCloneData } from './clone-utils.js';
|
||||
import {
|
||||
isEdgelessTextBlock,
|
||||
isEmbedSyncedDocBlock,
|
||||
isFrameBlock,
|
||||
isImageBlock,
|
||||
} from './query.js';
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
export function areSetsEqual<T>(setA: Set<T>, setB: Set<T>) {
|
||||
if (setA.size !== setB.size) return false;
|
||||
for (const a of setA) if (!setB.has(a)) return false;
|
||||
return true;
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
type EmbedLoomModel,
|
||||
type EmbedSyncedDocModel,
|
||||
type EmbedYoutubeModel,
|
||||
type FrameBlockModel,
|
||||
type ImageBlockModel,
|
||||
MindmapElementModel,
|
||||
ShapeElementModel,
|
||||
@@ -49,10 +48,6 @@ export function isEdgelessTextBlock(
|
||||
);
|
||||
}
|
||||
|
||||
export function isFrameBlock(element: unknown): element is FrameBlockModel {
|
||||
return !!element && (element as BlockModel).flavour === 'affine:frame';
|
||||
}
|
||||
|
||||
export function isImageBlock(
|
||||
element: BlockModel | GfxModel | null
|
||||
): element is ImageBlockModel {
|
||||
|
||||
@@ -85,10 +85,6 @@ export class AffineAIPanelWidget extends WidgetComponent {
|
||||
|
||||
private _answer: string | null = null;
|
||||
|
||||
private readonly _cancelCallback = () => {
|
||||
this.focus();
|
||||
};
|
||||
|
||||
private readonly _clearDiscardModal = () => {
|
||||
if (this._discardModalAbort) {
|
||||
this._discardModalAbort.abort();
|
||||
@@ -97,25 +93,7 @@ export class AffineAIPanelWidget extends WidgetComponent {
|
||||
};
|
||||
|
||||
private readonly _clickOutside = () => {
|
||||
switch (this.state) {
|
||||
case 'hidden':
|
||||
return;
|
||||
case 'error':
|
||||
case 'finished':
|
||||
if (!this._answer) {
|
||||
this.hide();
|
||||
} else {
|
||||
this.discard();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this.discard();
|
||||
}
|
||||
};
|
||||
|
||||
private readonly _discardCallback = () => {
|
||||
this.hide();
|
||||
this.config?.discardCallback?.();
|
||||
this._discardWithConfirmation();
|
||||
};
|
||||
|
||||
private _discardModalAbort: AbortController | null = null;
|
||||
@@ -171,27 +149,27 @@ export class AffineAIPanelWidget extends WidgetComponent {
|
||||
|
||||
ctx: unknown = null;
|
||||
|
||||
discard = () => {
|
||||
if ((this.state === 'finished' || this.state === 'error') && !this.answer) {
|
||||
this._discardCallback();
|
||||
private readonly _discardWithConfirmation = () => {
|
||||
if (this.state === 'hidden') {
|
||||
return;
|
||||
}
|
||||
if (this.state === 'input') {
|
||||
if (this.state === 'input' || !this.answer) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
this.showDiscardModal()
|
||||
.then(discard => {
|
||||
if (discard) {
|
||||
this._discardCallback();
|
||||
} else {
|
||||
this._cancelCallback();
|
||||
}
|
||||
this.restoreSelection();
|
||||
discard && this.discard();
|
||||
})
|
||||
.catch(console.error);
|
||||
};
|
||||
|
||||
discard = () => {
|
||||
this.hide();
|
||||
this.restoreSelection();
|
||||
this.config?.discardCallback?.();
|
||||
};
|
||||
|
||||
/**
|
||||
* You can evaluate this method multiple times to regenerate the answer.
|
||||
*/
|
||||
@@ -472,7 +450,7 @@ export class AffineAIPanelWidget extends WidgetComponent {
|
||||
'input',
|
||||
() =>
|
||||
html`<ai-panel-input
|
||||
.onBlur=${this.discard}
|
||||
.onBlur=${this._discardWithConfirmation}
|
||||
.onFinish=${this._inputFinish}
|
||||
.onInput=${this.onInput}
|
||||
.networkSearchConfig=${config.networkSearchConfig}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { EdgelessFrameManager } from '@blocksuite/affine-block-frame';
|
||||
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
|
||||
import type {
|
||||
EdgelessColorPickerButton,
|
||||
@@ -36,7 +37,6 @@ import { join } from 'lit/directives/join.js';
|
||||
import { when } from 'lit/directives/when.js';
|
||||
|
||||
import type { EdgelessRootBlockComponent } from '../../edgeless/edgeless-root-block.js';
|
||||
import type { EdgelessFrameManager } from '../../edgeless/frame-manager.js';
|
||||
import { mountFrameTitleEditor } from '../../edgeless/utils/text.js';
|
||||
|
||||
function getMostCommonColor(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isFrameBlock } from '@blocksuite/affine-block-frame';
|
||||
import { isNoteBlock } from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
cloneGroups,
|
||||
@@ -47,7 +48,6 @@ import {
|
||||
isBookmarkBlock,
|
||||
isEdgelessTextBlock,
|
||||
isEmbeddedBlock,
|
||||
isFrameBlock,
|
||||
isImageBlock,
|
||||
} from '../../edgeless/utils/query.js';
|
||||
import { renderAddFrameButton } from './add-frame-button.js';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isFrameBlock } from '@blocksuite/affine-block-frame';
|
||||
import {
|
||||
isNoteBlock,
|
||||
type SurfaceBlockComponent,
|
||||
@@ -18,7 +19,6 @@ import {
|
||||
isEmbeddedLinkBlock,
|
||||
isEmbedLinkedDocBlock,
|
||||
isEmbedSyncedDocBlock,
|
||||
isFrameBlock,
|
||||
isImageBlock,
|
||||
} from '../../../edgeless/utils/query.js';
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isFrameBlock } from '@blocksuite/affine-block-frame';
|
||||
import { getSurfaceBlock, isNoteBlock } from '@blocksuite/affine-block-surface';
|
||||
import type { FrameBlockModel, NoteBlockModel } from '@blocksuite/affine-model';
|
||||
import { NoteDisplayMode } from '@blocksuite/affine-model';
|
||||
@@ -12,7 +13,6 @@ import {
|
||||
mapFrameIds,
|
||||
sortEdgelessElements,
|
||||
} from '../../../edgeless/utils/clone-utils.js';
|
||||
import { isFrameBlock } from '../../../edgeless/utils/query.js';
|
||||
|
||||
function addBlocksToDoc(targetDoc: Store, model: BlockModel, parentId: string) {
|
||||
// Add current block to linked doc
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
{ "path": "../affine/block-surface-ref" },
|
||||
{ "path": "../affine/block-table" },
|
||||
{ "path": "../affine/components" },
|
||||
{ "path": "../affine/fragment-frame-panel" },
|
||||
{ "path": "../affine/fragment-outline" },
|
||||
{ "path": "../affine/model" },
|
||||
{ "path": "../affine/shared" },
|
||||
{ "path": "../affine/widget-drag-handle" },
|
||||
|
||||
@@ -3,7 +3,11 @@ import { css, html } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
|
||||
import { PropTypes, requiredProperties } from '../view/decorators/required.js';
|
||||
import { type EditorHost, ShadowlessElement } from '../view/index.js';
|
||||
import {
|
||||
type BlockComponent,
|
||||
type EditorHost,
|
||||
ShadowlessElement,
|
||||
} from '../view/index.js';
|
||||
import type { GfxBlockElementModel } from './model/gfx-block-model.js';
|
||||
import { Viewport } from './viewport.js';
|
||||
|
||||
@@ -31,6 +35,13 @@ export function requestThrottledConnectedFrame<
|
||||
}) as T;
|
||||
}
|
||||
|
||||
function setDisplay(view: BlockComponent | null, display: 'block' | 'none') {
|
||||
if (!view) return;
|
||||
if (view.style.display !== display) {
|
||||
view.style.display = display;
|
||||
}
|
||||
}
|
||||
|
||||
@requiredProperties({
|
||||
viewport: PropTypes.instanceOf(Viewport),
|
||||
})
|
||||
@@ -53,10 +64,7 @@ export class GfxViewportElement extends WithDisposable(ShadowlessElement) {
|
||||
|
||||
modelsInViewport.forEach(model => {
|
||||
const view = host.std.view.getBlock(model.id);
|
||||
|
||||
if (view) {
|
||||
view.style.display = '';
|
||||
}
|
||||
setDisplay(view, 'block');
|
||||
|
||||
if (this._lastVisibleModels?.has(model)) {
|
||||
this._lastVisibleModels!.delete(model);
|
||||
@@ -65,10 +73,7 @@ export class GfxViewportElement extends WithDisposable(ShadowlessElement) {
|
||||
|
||||
this._lastVisibleModels?.forEach(model => {
|
||||
const view = host.std.view.getBlock(model.id);
|
||||
|
||||
if (view) {
|
||||
view.style.display = 'none';
|
||||
}
|
||||
setDisplay(view, 'none');
|
||||
});
|
||||
|
||||
this._lastVisibleModels = modelsInViewport;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// because AbortSignal.throwIfAborted is not available in abortcontroller-polyfill
|
||||
export function throwIfAborted(abort?: AbortSignal) {
|
||||
if (abort?.aborted) {
|
||||
throw new Error(abort.reason);
|
||||
throw abort.reason;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# `@blocksuite/integration-test`
|
||||
|
||||
Integration test for BlockSuite.
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@blocksuite/presets",
|
||||
"description": "Prebuilt BlockSuite editors and opt-in additional UI components.",
|
||||
"name": "@blocksuite/integration-test",
|
||||
"description": "Integration test for BlockSuite",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |