Compare commits

..

2 Commits

Author SHA1 Message Date
EYHN
68b57cdee7 feat(core): move collapsed groups state to masonry 2025-05-15 15:36:42 +09:00
EYHN
afa108d517 feat(core): edit and delete pinned collections in all docs 2025-05-15 13:28:35 +09:00
276 changed files with 1517 additions and 5816 deletions

View File

@@ -1 +0,0 @@
.env

View File

@@ -23,7 +23,6 @@ services:
environment:
- REDIS_SERVER_HOST=redis
- DATABASE_URL=postgresql://${DB_USERNAME}:${DB_PASSWORD}@postgres:5432/${DB_DATABASE:-affine}
- AFFINE_INDEXER_SEARCH_ENDPOINT=http://indexer:9308
restart: unless-stopped
affine_migration:
@@ -39,7 +38,6 @@ services:
environment:
- REDIS_SERVER_HOST=redis
- DATABASE_URL=postgresql://${DB_USERNAME}:${DB_PASSWORD}@postgres:5432/${DB_DATABASE:-affine}
- AFFINE_INDEXER_SEARCH_ENDPOINT=http://indexer:9308
depends_on:
postgres:
condition: service_healthy
@@ -59,7 +57,7 @@ services:
restart: unless-stopped
postgres:
image: pgvector/pgvector:pg16
image: postgres:16
container_name: affine_postgres
volumes:
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data

View File

@@ -582,80 +582,10 @@ jobs:
ports:
- 1025:1025
- 8025:8025
indexer:
manticoresearch:
image: manticoresearch/manticore:9.2.14
ports:
- 9308:9308
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
full-cache: true
- name: Download server-native.node
uses: actions/download-artifact@v4
with:
name: server-native.node
path: ./packages/backend/native
- name: Prepare Server Test Environment
uses: ./.github/actions/server-test-env
- name: Run server tests
run: yarn affine @affine/server test:coverage --forbid-only
env:
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
CI_NODE_INDEX: ${{ matrix.node_index }}
CI_NODE_TOTAL: ${{ matrix.total_nodes }}
- name: Upload server test coverage results
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/backend/server/.coverage/lcov.info
flags: server-test
name: affine
fail_ci_if_error: false
server-test-elasticsearch:
name: Server Test with Elasticsearch
runs-on: ubuntu-latest
needs:
- optimize_ci
- build-server-native
if: needs.optimize_ci.outputs.skip == 'false'
strategy:
fail-fast: false
env:
NODE_ENV: test
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
REDIS_SERVER_HOST: localhost
AFFINE_INDEXER_SEARCH_PROVIDER: elasticsearch
AFFINE_INDEXER_SEARCH_ENDPOINT: http://localhost:9200
services:
postgres:
image: pgvector/pgvector:pg16
env:
POSTGRES_PASSWORD: affine
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
redis:
image: redis
ports:
- 6379:6379
mailer:
image: mailhog/mailhog
ports:
- 1025:1025
- 8025:8025
steps:
# https://github.com/elastic/elastic-github-actions/blob/master/elasticsearch/README.md
- name: Configure sysctl limits for Elasticsearch
@@ -688,8 +618,8 @@ jobs:
- name: Prepare Server Test Environment
uses: ./.github/actions/server-test-env
- name: Run server tests with elasticsearch only
run: yarn affine @affine/server test:coverage "**/*/*elasticsearch.spec.ts" --forbid-only
- name: Run server tests
run: yarn affine @affine/server test:coverage --forbid-only
env:
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
CI_NODE_INDEX: ${{ matrix.node_index }}
@@ -956,10 +886,6 @@ jobs:
ports:
- 1025:1025
- 8025:8025
indexer:
image: manticoresearch/manticore:9.2.14
ports:
- 9308:9308
steps:
- uses: actions/checkout@v4
@@ -1055,10 +981,6 @@ jobs:
image: redis
ports:
- 6379:6379
indexer:
image: manticoresearch/manticore:9.2.14
ports:
- 9308:9308
steps:
- uses: actions/checkout@v4
@@ -1081,7 +1003,6 @@ jobs:
- 'packages/backend/server/src/plugins/copilot/**'
- 'packages/backend/server/tests/copilot.*'
- 'packages/frontend/core/src/blocksuite/ai/**'
- 'packages/frontend/core/src/modules/workspace-indexer-embedding/**'
- 'tests/affine-cloud-copilot/**'
- name: Setup Node.js

View File

@@ -59,10 +59,6 @@ jobs:
ports:
- 1025:1025
- 8025:8025
indexer:
image: manticoresearch/manticore:9.2.14
ports:
- 9308:9308
steps:
- uses: actions/checkout@v4
@@ -134,10 +130,6 @@ jobs:
image: redis
ports:
- 6379:6379
indexer:
image: manticoresearch/manticore:9.2.14
ports:
- 9308:9308
steps:
- uses: actions/checkout@v4

View File

@@ -180,7 +180,6 @@ jobs:
- name: Testflight
if: ${{ env.BUILD_TYPE != 'stable' }}
working-directory: packages/frontend/apps/ios/App
continue-on-error: true
run: |
echo -n "${{ env.BUILD_PROVISION_PROFILE }}" | base64 --decode -o $PP_PATH
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles

2
.nvmrc
View File

@@ -1 +1 @@
22.15.1
22.15.0

View File

@@ -58,14 +58,11 @@
"@blocksuite/affine-shared": "workspace:*",
"@blocksuite/affine-widget-drag-handle": "workspace:*",
"@blocksuite/affine-widget-edgeless-auto-connect": "workspace:*",
"@blocksuite/affine-widget-edgeless-dragging-area": "workspace:*",
"@blocksuite/affine-widget-edgeless-selected-rect": "workspace:*",
"@blocksuite/affine-widget-edgeless-toolbar": "workspace:*",
"@blocksuite/affine-widget-edgeless-zoom-toolbar": "workspace:*",
"@blocksuite/affine-widget-frame-title": "workspace:*",
"@blocksuite/affine-widget-keyboard-toolbar": "workspace:*",
"@blocksuite/affine-widget-linked-doc": "workspace:*",
"@blocksuite/affine-widget-note-slicer": "workspace:*",
"@blocksuite/affine-widget-page-dragging-area": "workspace:*",
"@blocksuite/affine-widget-remote-selection": "workspace:*",
"@blocksuite/affine-widget-scroll-anchoring": "workspace:*",
@@ -181,8 +178,6 @@
"./widgets/drag-handle/view": "./src/widgets/drag-handle/view.ts",
"./widgets/edgeless-auto-connect": "./src/widgets/edgeless-auto-connect/index.ts",
"./widgets/edgeless-auto-connect/view": "./src/widgets/edgeless-auto-connect/view.ts",
"./widgets/edgeless-dragging-area": "./src/widgets/edgeless-dragging-area/index.ts",
"./widgets/edgeless-dragging-area/view": "./src/widgets/edgeless-dragging-area/view.ts",
"./widgets/edgeless-toolbar": "./src/widgets/edgeless-toolbar/index.ts",
"./widgets/edgeless-toolbar/view": "./src/widgets/edgeless-toolbar/view.ts",
"./widgets/frame-title": "./src/widgets/frame-title/index.ts",

View File

@@ -40,14 +40,11 @@ import { InlinePresetViewExtension } from '@blocksuite/affine-inline-preset/view
import { ReferenceViewExtension } from '@blocksuite/affine-inline-reference/view';
import { DragHandleViewExtension } from '@blocksuite/affine-widget-drag-handle/view';
import { EdgelessAutoConnectViewExtension } from '@blocksuite/affine-widget-edgeless-auto-connect/view';
import { EdgelessDraggingAreaViewExtension } from '@blocksuite/affine-widget-edgeless-dragging-area/view';
import { EdgelessSelectedRectViewExtension } from '@blocksuite/affine-widget-edgeless-selected-rect/view';
import { EdgelessToolbarViewExtension } from '@blocksuite/affine-widget-edgeless-toolbar/view';
import { EdgelessZoomToolbarViewExtension } from '@blocksuite/affine-widget-edgeless-zoom-toolbar/view';
import { FrameTitleViewExtension } from '@blocksuite/affine-widget-frame-title/view';
import { KeyboardToolbarViewExtension } from '@blocksuite/affine-widget-keyboard-toolbar/view';
import { LinkedDocViewExtension } from '@blocksuite/affine-widget-linked-doc/view';
import { NoteSlicerViewExtension } from '@blocksuite/affine-widget-note-slicer/view';
import { PageDraggingAreaViewExtension } from '@blocksuite/affine-widget-page-dragging-area/view';
import { RemoteSelectionViewExtension } from '@blocksuite/affine-widget-remote-selection/view';
import { ScrollAnchoringViewExtension } from '@blocksuite/affine-widget-scroll-anchoring/view';
@@ -115,9 +112,6 @@ export function getInternalViewExtensions() {
ViewportOverlayViewExtension,
EdgelessZoomToolbarViewExtension,
PageDraggingAreaViewExtension,
EdgelessSelectedRectViewExtension,
EdgelessDraggingAreaViewExtension,
NoteSlicerViewExtension,
// Fragment
DocTitleViewExtension,

View File

@@ -1 +0,0 @@
export * from '@blocksuite/affine-widget-edgeless-dragging-area';

View File

@@ -1 +0,0 @@
export * from '@blocksuite/affine-widget-edgeless-dragging-area/view';

View File

@@ -1 +0,0 @@
export * from '@blocksuite/affine-widget-edgeless-selected-rect';

View File

@@ -1 +0,0 @@
export * from '@blocksuite/affine-widget-edgeless-selected-rect/view';

View File

@@ -1 +0,0 @@
export * from '@blocksuite/affine-widget-note-slicer';

View File

@@ -1 +0,0 @@
export * from '@blocksuite/affine-widget-note-slicer/view';

View File

@@ -55,14 +55,11 @@
{ "path": "../shared" },
{ "path": "../widgets/drag-handle" },
{ "path": "../widgets/edgeless-auto-connect" },
{ "path": "../widgets/edgeless-dragging-area" },
{ "path": "../widgets/edgeless-selected-rect" },
{ "path": "../widgets/edgeless-toolbar" },
{ "path": "../widgets/edgeless-zoom-toolbar" },
{ "path": "../widgets/frame-title" },
{ "path": "../widgets/keyboard-toolbar" },
{ "path": "../widgets/linked-doc" },
{ "path": "../widgets/note-slicer" },
{ "path": "../widgets/page-dragging-area" },
{ "path": "../widgets/remote-selection" },
{ "path": "../widgets/scroll-anchoring" },

View File

@@ -32,6 +32,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./store": "./src/store.ts",
"./view": "./src/view.ts"
},

View File

@@ -17,9 +17,7 @@ import {
AttachmentBlockStyles,
} from '@blocksuite/affine-model';
import {
DocModeProvider,
FileSizeLimitProvider,
TelemetryProvider,
ThemeProvider,
} from '@blocksuite/affine-shared/services';
import { humanFileSize } from '@blocksuite/affine-shared/utils';
@@ -145,11 +143,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
this.disposables.add(this.resourceController.subscribe());
this.disposables.add(this.resourceController);
this.disposables.add(
this.model.props.sourceId$.subscribe(() => {
this.refreshData();
})
);
this.refreshData();
if (!this.model.props.style && !this.store.readonly) {
this.store.withoutTransact(() => {
@@ -189,22 +183,6 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
@click=${(event: MouseEvent) => {
event.stopPropagation();
onOverFileSize?.();
{
const mode =
this.std.get(DocModeProvider).getEditorMode() ?? 'page';
const segment = mode === 'page' ? 'doc' : 'whiteboard';
this.std
.getOptional(TelemetryProvider)
?.track('AttachmentUpgradedEvent', {
segment,
page: `${segment} editor`,
module: 'attachment',
control: 'upgrade',
category: 'card',
type: this.model.props.name.split('.').pop() ?? '',
});
}
}}
>
${UpgradeIcon()} Upgrade
@@ -220,22 +198,6 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
@click=${(event: MouseEvent) => {
event.stopPropagation();
this.refreshData();
{
const mode =
this.std.get(DocModeProvider).getEditorMode() ?? 'page';
const segment = mode === 'page' ? 'doc' : 'whiteboard';
this.std
.getOptional(TelemetryProvider)
?.track('AttachmentReloadedEvent', {
segment,
page: `${segment} editor`,
module: 'attachment',
control: 'reload',
category: 'card',
type: this.model.props.name.split('.').pop() ?? '',
});
}
}}
>
${ResetIcon()} Reload

View File

@@ -264,12 +264,6 @@ const builtinToolbarConfig = {
run(ctx) {
const block = ctx.getCurrentBlockByType(AttachmentBlockComponent);
block?.reload();
ctx.track('AttachmentReloadedEvent', {
...trackBaseProps,
control: 'reload',
type: block?.model.props.name.split('.').pop() ?? '',
});
},
},
{

View File

@@ -130,7 +130,7 @@ async function buildPropsWith(
std.getOptional(TelemetryProvider)?.track('AttachmentUploadedEvent', {
page: `${mode} editor`,
module: 'attachment',
segment: mode,
segment: 'attachment',
control: 'uploader',
type,
category,

View File

@@ -36,6 +36,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./store": "./src/store.ts",
"./view": "./src/view.ts"
},

View File

@@ -1,3 +1,5 @@
import '@blocksuite/affine-block-embed/effects';
import { insertEmbedCard } from '@blocksuite/affine-block-embed';
import type { EmbedCardStyle } from '@blocksuite/affine-model';
import { EmbedOptionProvider } from '@blocksuite/affine-shared/services';

View File

@@ -35,6 +35,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts",
"./store": "./src/store.ts"
},

View File

@@ -37,6 +37,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./turbo-painter": "./src/turbo/code-painter.worker.ts",
"./view": "./src/view.ts",
"./store": "./src/store.ts"

View File

@@ -388,10 +388,8 @@ export class CodeBlockComponent extends CaptionedBlockComponent<CodeBlockModel>
override renderBlock(): TemplateResult<1> {
const showLineNumbers =
(this.std.getOptional(CodeBlockConfigExtension.identifier)
?.showLineNumbers ??
true) &&
(this.model.props.lineNumber ?? true);
this.std.getOptional(CodeBlockConfigExtension.identifier)
?.showLineNumbers ?? true;
const preview = !!this.model.props.preview;
const previewContext = this.std.getOptional(
@@ -405,7 +403,6 @@ export class CodeBlockComponent extends CaptionedBlockComponent<CodeBlockModel>
'affine-code-block-container': true,
mobile: IS_MOBILE,
wrap: this.model.props.wrap,
'disable-line-numbers': !showLineNumbers,
})}
>
<rich-text
@@ -423,14 +420,16 @@ export class CodeBlockComponent extends CaptionedBlockComponent<CodeBlockModel>
.enableUndoRedo=${false}
.wrapText=${this.model.props.wrap}
.verticalScrollContainerGetter=${() => getViewportElement(this.host)}
.vLineRenderer=${(vLine: VLine) => {
return html`
<span contenteditable="false" class="line-number"
>${vLine.index + 1}</span
>
${vLine.renderVElements()}
`;
}}
.vLineRenderer=${showLineNumbers
? (vLine: VLine) => {
return html`
<span contenteditable="false" class="line-number"
>${vLine.index + 1}</span
>
${vLine.renderVElements()}
`;
}
: undefined}
>
</rich-text>
<div

View File

@@ -1,13 +0,0 @@
import { toGfxBlockComponent } from '@blocksuite/std';
import { CodeBlockComponent } from './code-block.js';
export class CodeEdgelessBlockComponent extends toGfxBlockComponent(
CodeBlockComponent
) {}
declare global {
interface HTMLElementTagNameMap {
'affine-edgeless-code': CodeEdgelessBlockComponent;
}
}

View File

@@ -9,12 +9,10 @@ import {
import type { MenuItemGroup } from '@blocksuite/affine-components/toolbar';
import { isInsidePageEditor } from '@blocksuite/affine-shared/utils';
import { noop, sleep } from '@blocksuite/global/utils';
import { NumberedListIcon } from '@blocksuite/icons/lit';
import { BlockSelection } from '@blocksuite/std';
import { html } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import { CodeBlockConfigExtension } from '../code-block-config.js';
import type { CodeBlockToolbarContext } from './context.js';
import { duplicateCodeBlock } from './utils.js';
@@ -150,40 +148,6 @@ export const clipboardGroup: MenuItemGroup<CodeBlockToolbarContext> = {
};
},
},
{
type: 'line-number',
when: ({ std }) =>
std.getOptional(CodeBlockConfigExtension.identifier)?.showLineNumbers ??
true,
generate: ({ blockComponent, close }) => {
return {
action: () => {},
render: () => {
const lineNumber = blockComponent.model.props.lineNumber ?? true;
const label = lineNumber ? 'Cancel line number' : 'Line number';
return html`
<editor-menu-action
@click=${() => {
blockComponent.store.updateBlock(blockComponent.model, {
lineNumber: !lineNumber,
});
close();
}}
aria-label=${label}
>
${NumberedListIcon()}
<span class="label">${label}</span>
<toggle-switch
style="margin-left: auto;"
.on="${lineNumber}"
></toggle-switch>
</editor-menu-action>
`;
},
};
},
},
{
type: 'duplicate',
label: 'Duplicate',

View File

@@ -1,5 +1,4 @@
import { CodeBlockComponent } from './code-block';
import { CodeEdgelessBlockComponent } from './code-edgeless-block';
import {
AFFINE_CODE_TOOLBAR_WIDGET,
AffineCodeToolbarWidget,
@@ -15,7 +14,6 @@ export function effects() {
customElements.define(AFFINE_CODE_TOOLBAR_WIDGET, AffineCodeToolbarWidget);
customElements.define('affine-code-unit', AffineCodeUnit);
customElements.define('affine-code', CodeBlockComponent);
customElements.define('affine-edgeless-code', CodeEdgelessBlockComponent);
customElements.define('preview-button', PreviewButton);
}

View File

@@ -50,10 +50,6 @@ export const codeBlockStyles = css`
user-select: none;
}
.affine-code-block-container.disable-line-numbers .line-number {
display: none;
}
affine-code .affine-code-block-preview {
padding: 12px;
}

View File

@@ -41,11 +41,7 @@ export class CodeBlockViewExtension extends ViewExtensionProvider {
context.register([
FlavourExtension('affine:code'),
CodeBlockHighlighter,
BlockViewExtension('affine:code', model => {
return model.parent?.flavour === 'affine:surface'
? literal`affine-edgeless-code`
: literal`affine-code`;
}),
BlockViewExtension('affine:code', literal`affine-code`),
SlashMenuConfigExtension('affine:code', codeSlashMenuConfig),
CodeKeymapExtension,
...getCodeClipboardExtensions(),

View File

@@ -33,6 +33,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts",
"./store": "./src/store.ts"
},

View File

@@ -39,6 +39,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./store": "./src/store.ts",
"./view": "./src/view.ts"
},

View File

@@ -29,6 +29,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts",
"./store": "./src/store.ts"
},

View File

@@ -34,6 +34,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts",
"./store": "./src/store.ts"
},

View File

@@ -40,6 +40,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts",
"./store": "./src/store.ts"
},

View File

@@ -52,25 +52,23 @@ export const EmbedSyncedDocInteraction =
scale = newBound.w / realWidth;
}
const newWidth = newBound.w / scale;
newBound.w =
clamp(
newBound.w / scale,
constraint.minWidth,
constraint.maxWidth
) * scale;
clamp(newWidth, constraint.minWidth, constraint.maxWidth) * scale;
newBound.h =
clamp(
newBound.h / scale,
constraint.minHeight,
constraint.maxHeight
) * scale;
clamp(newBound.h, constraint.minHeight, constraint.maxHeight) *
scale;
const newHeight = newBound.h / scale;
if (model.isFolded && newHeight > constraint.minHeight) {
model.props.preFoldHeight = 0;
} else if (!model.isFolded && newHeight <= constraint.minHeight) {
model.props.preFoldHeight = initHeight;
// only adjust height check the fold state
if (originalBound.w === newBound.w) {
let preFoldHeight = 0;
if (newHeight === constraint.minHeight) {
preFoldHeight = initHeight;
}
model.props.preFoldHeight = preFoldHeight;
}
model.props.scale = scale;

View File

@@ -76,8 +76,6 @@ export function calcSyncedDocFullHeight(block: BlockComponent) {
const bottomPadding = 8;
return (
(headerHeight + contentHeight + bottomPadding) /
block.gfx.viewport.zoom /
(block.model.props.scale ?? 1)
(headerHeight + contentHeight + bottomPadding) / block.gfx.viewport.zoom
);
}

View File

@@ -40,6 +40,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts",
"./store": "./src/store.ts"
},

View File

@@ -35,6 +35,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./store": "./src/store.ts",
"./view": "./src/view.ts"
},

View File

@@ -185,33 +185,9 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
if (document.fullscreenElement) {
document.exitFullscreen().catch(console.error);
}
// Reset the flag when fully exiting presentation mode
this.edgeless.std
.get(EditPropsStore)
.setStorage('presentNoFrameToastShown', false);
}
private _moveToCurrentFrame(forceMove = false) {
const currentToolOption = this.gfx.tool.currentToolOption$.value;
const toolOptions = currentToolOption?.options;
// If PresentTool is being activated after a temporary pan (indicated by restoredAfterPan)
// and a forced move isn't explicitly requested, skip moving to the current frame.
// This preserves the user's panned position instead of resetting to the frame's default view.
if (
currentToolOption?.toolType === PresentTool &&
toolOptions?.restoredAfterPan &&
!forceMove
) {
// Clear the flag so future navigations behave normally
this.gfx.tool.setTool(PresentTool, {
...toolOptions,
restoredAfterPan: false,
});
return;
}
private _moveToCurrentFrame() {
const current = this._currentFrameIndex;
const viewport = this.gfx.viewport;
const frame = this._frames[current];
@@ -287,56 +263,28 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
_disposables.add(
effect(() => {
const currentToolOption = this.gfx.tool.currentToolOption$.value;
if (currentToolOption?.toolType === PresentTool) {
const opts = currentToolOption.options as
| ToolOptions<PresentTool>
| undefined;
const isAlreadyFullscreen = !!document.fullscreenElement;
if (!isAlreadyFullscreen) {
this._toggleFullScreen();
} else {
this._fullScreenMode = true;
}
const currentTool = this.gfx.tool.currentToolOption$.value;
const selection = this.gfx.selection;
if (currentTool?.toolType === PresentTool) {
this._cachedIndex = this._currentFrameIndex;
this._navigatorMode = opts?.mode ?? this._navigatorMode;
const selection = this.gfx.selection;
if (
selection.selectedElements.length > 0 &&
isFrameBlock(selection.selectedElements[0])
) {
const selectedFrameId = selection.selectedElements[0].id;
const indexOfSelectedFrame = this._frames.findIndex(
frame => frame.id === selectedFrameId
this._navigatorMode =
(currentTool.options as ToolOptions<PresentTool>)?.mode ??
this._navigatorMode;
if (isFrameBlock(selection.selectedElements[0])) {
this._cachedIndex = this._frames.findIndex(
frame => frame.id === selection.selectedElements[0].id
);
if (indexOfSelectedFrame !== -1) {
this._cachedIndex = indexOfSelectedFrame;
}
}
const store = this.edgeless.std.get(EditPropsStore);
if (this._frames.length === 0) {
if (!store.getStorage('presentNoFrameToastShown')) {
toast(
this.host,
'The presentation requires at least 1 frame. You can firstly create a frame.',
5000
);
store.setStorage('presentNoFrameToastShown', true);
}
} else {
// If frames exist, and the flag was set, reset it.
// This allows the toast to show again if all frames are subsequently deleted.
if (store.getStorage('presentNoFrameToastShown')) {
store.setStorage('presentNoFrameToastShown', false);
}
}
if (this._frames.length === 0)
toast(
this.host,
'The presentation requires at least 1 frame. You can firstly create a frame.',
5000
);
this._toggleFullScreen();
}
this.requestUpdate();
})
);
@@ -357,10 +305,12 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
_disposables.addFromEvent(document, 'fullscreenchange', () => {
if (document.fullscreenElement) {
// When enter fullscreen, we need to set current frame to the cached index
this._timer = setTimeout(() => {
this._currentFrameIndex = this._cachedIndex;
}, 400);
} else {
// When exit fullscreen, we need to clear the timer
clearTimeout(this._timer);
if (
this.edgelessTool.toolType === PresentTool &&
@@ -374,7 +324,7 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
}
}
setTimeout(() => this._moveToCurrentFrame(true), 400);
setTimeout(() => this._moveToCurrentFrame(), 400);
this.slots.fullScreenToggled.next();
});
@@ -480,29 +430,11 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
}
protected override updated(changedProperties: PropertyValues) {
const currentToolOption = this.gfx.tool.currentToolOption$.value;
const isPresentToolActive = currentToolOption?.toolType === PresentTool;
const toolOptions = currentToolOption?.options;
const isRestoredAfterPan = !!(
isPresentToolActive && toolOptions?.restoredAfterPan
);
if (changedProperties.has('_currentFrameIndex') && isPresentToolActive) {
// When the current frame index changes (e.g., user navigates), a viewport update is needed.
// However, if PresentTool is merely being restored after a pan (isRestoredAfterPan = true)
// without an explicit index change in this update cycle, we avoid forcing a move to preserve the panned position.
// Thus, `forceMove` is true unless it's a pan restoration.
const shouldForceMove = !isRestoredAfterPan;
this._moveToCurrentFrame(shouldForceMove);
} else if (isPresentToolActive && changedProperties.has('edgelessTool')) {
// Handles cases where the tool is set/switched to PresentTool (e.g., initial activation or returning from another tool).
// Similar to frame index changes, avoid forcing a viewport move if restoring after a pan.
const currentToolIsPresentTool =
this.edgelessTool.toolType === PresentTool;
if (currentToolIsPresentTool) {
const shouldForceMoveOnToolChange = !isRestoredAfterPan;
this._moveToCurrentFrame(shouldForceMoveOnToolChange);
}
if (
changedProperties.has('_currentFrameIndex') &&
this.edgelessTool.toolType === PresentTool
) {
this._moveToCurrentFrame();
}
}

View File

@@ -4,7 +4,6 @@ import type { NavigatorMode } from './frame-manager';
export type PresentToolOption = {
mode?: NavigatorMode;
restoredAfterPan?: boolean;
};
export class PresentTool extends BaseTool<PresentToolOption> {

View File

@@ -77,16 +77,18 @@ export class EdgelessNavigatorSettingButton extends WithDisposable(LitElement) {
slots.navigatorSettingUpdated.next({
blackBackground: this.blackBackground,
});
this.edgeless.std
.get(EditPropsStore)
.setStorage('presentBlackBackground', checked);
};
private _tryRestoreSettings() {
const blackBackground = this.edgeless.std
.get(EditPropsStore)
.getStorage('presentBlackBackground');
this.blackBackground = blackBackground ?? false;
this.blackBackground = blackBackground ?? true;
}
override connectedCallback() {
super.connectedCallback();
this._tryRestoreSettings();
}
override disconnectedCallback(): void {
@@ -95,7 +97,6 @@ export class EdgelessNavigatorSettingButton extends WithDisposable(LitElement) {
}
override firstUpdated() {
if (this.edgeless) this._tryRestoreSettings();
this._navigatorSettingPopper = createButtonPopper({
reference: this._navigatorSettingButton,
popperElement: this._navigatorSettingMenu,
@@ -132,7 +133,7 @@ export class EdgelessNavigatorSettingButton extends WithDisposable(LitElement) {
<div class="text">Black background</div>
<toggle-switch
.on=${this.blackBackground}
.subscribe=${this.blackBackground}
.onChange=${this._onBlackBackgroundChange}
>
</toggle-switch>
@@ -142,7 +143,7 @@ export class EdgelessNavigatorSettingButton extends WithDisposable(LitElement) {
<div class="text">Hide toolbar</div>
<toggle-switch
.on=${this.hideToolbar}
.subscribe=${this.hideToolbar}
.onChange=${(checked: boolean) => {
this.onHideToolbarChange && this.onHideToolbarChange(checked);
}}
@@ -172,7 +173,7 @@ export class EdgelessNavigatorSettingButton extends WithDisposable(LitElement) {
private accessor _navigatorSettingMenu!: HTMLElement;
@state()
accessor blackBackground = false;
accessor blackBackground = true;
@property({ attribute: false })
accessor edgeless!: BlockComponent;

View File

@@ -34,6 +34,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./turbo-painter": "./src/turbo/image-painter.worker.ts",
"./store": "./src/store.ts",
"./view": "./src/view.ts"

View File

@@ -7,7 +7,7 @@ import {
} from '@blocksuite/affine-shared/commands';
import { ImageSelection } from '@blocksuite/affine-shared/selection';
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
import { WithDisposable } from '@blocksuite/global/lit';
import type { BlockComponent, UIEventStateContext } from '@blocksuite/std';
import {
BlockSelection,
@@ -15,9 +15,8 @@ import {
TextSelection,
} from '@blocksuite/std';
import type { BaseSelection } from '@blocksuite/store';
import { computed } from '@preact/signals-core';
import { css, html, type PropertyValues } from 'lit';
import { property, query } from 'lit/decorators.js';
import { property, query, state } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js';
import { when } from 'lit/directives/when.js';
@@ -26,9 +25,7 @@ import { ImageResizeManager } from '../image-resize-manager';
import { shouldResizeImage } from '../utils';
import { ImageSelectedRect } from './image-selected-rect';
export class ImageBlockPageComponent extends SignalWatcher(
WithDisposable(ShadowlessElement)
) {
export class ImageBlockPageComponent extends WithDisposable(ShadowlessElement) {
static override styles = css`
affine-page-image {
position: relative;
@@ -71,8 +68,6 @@ export class ImageBlockPageComponent extends SignalWatcher(
}
`;
resizeable$ = computed(() => this.block.resizeable$.value);
private _isDragging = false;
private get _doc() {
@@ -139,21 +134,21 @@ export class ImageBlockPageComponent extends SignalWatcher(
return true;
},
Delete: ctx => {
if (this._host.store.readonly || !this.resizeable$.peek()) return;
if (this._host.store.readonly || !this._isSelected) return;
addParagraph(ctx);
this._doc.deleteBlock(this._model);
return true;
},
Backspace: ctx => {
if (this._host.store.readonly || !this.resizeable$.peek()) return;
if (this._host.store.readonly || !this._isSelected) return;
addParagraph(ctx);
this._doc.deleteBlock(this._model);
return true;
},
Enter: ctx => {
if (this._host.store.readonly || !this.resizeable$.peek()) return;
if (this._host.store.readonly || !this._isSelected) return;
addParagraph(ctx);
return true;
@@ -218,6 +213,19 @@ export class ImageBlockPageComponent extends SignalWatcher(
private _handleSelection() {
const selection = this._host.selection;
this._disposables.add(
selection.slots.changed.subscribe(selList => {
this._isSelected = selList.some(
sel => sel.blockId === this.block.blockId && sel.is(ImageSelection)
);
})
);
this._disposables.add(
this._model.propsUpdated.subscribe(() => {
this.requestUpdate();
})
);
this._disposables.addFromEvent(
this.resizeImg,
@@ -241,7 +249,7 @@ export class ImageBlockPageComponent extends SignalWatcher(
this.block.handleEvent(
'click',
() => {
if (!this.resizeable$.peek()) return;
if (!this._isSelected) return;
selection.update(selList =>
selList.filter(
@@ -348,7 +356,7 @@ export class ImageBlockPageComponent extends SignalWatcher(
override render() {
const imageSize = this._normalizeImageSize();
const imageSelectedRect = this.resizeable$.value
const imageSelectedRect = this._isSelected
? ImageSelectedRect(this._doc.readonly)
: null;
@@ -381,6 +389,9 @@ export class ImageBlockPageComponent extends SignalWatcher(
`;
}
@state()
accessor _isSelected = false;
@property({ attribute: false })
accessor block!: ImageBlockComponent;

View File

@@ -4,7 +4,6 @@ import { getLoadingIconWith } from '@blocksuite/affine-components/icons';
import { Peekable } from '@blocksuite/affine-components/peek';
import { ResourceController } from '@blocksuite/affine-components/resource';
import type { ImageBlockModel } from '@blocksuite/affine-model';
import { ImageSelection } from '@blocksuite/affine-shared/selection';
import {
ThemeProvider,
ToolbarRegistryIdentifier,
@@ -31,13 +30,6 @@ import {
enableOn: () => !IS_MOBILE,
})
export class ImageBlockComponent extends CaptionedBlockComponent<ImageBlockModel> {
resizeable$ = computed(() =>
this.std.selection.value.some(
selection =>
selection.is(ImageSelection) && selection.blockId === this.blockId
)
);
resourceController = new ResourceController(
computed(() => this.model.props.sourceId$.value),
'Image'
@@ -112,11 +104,7 @@ export class ImageBlockComponent extends CaptionedBlockComponent<ImageBlockModel
this.disposables.add(this.resourceController.subscribe());
this.disposables.add(this.resourceController);
this.disposables.add(
this.model.props.sourceId$.subscribe(() => {
this.refreshData();
})
);
this.refreshData();
}
override firstUpdated() {

View File

@@ -100,11 +100,7 @@ export class ImageEdgelessBlockComponent extends GfxBlockComponent<ImageBlockMod
this.disposables.add(this.resourceController.subscribe());
this.disposables.add(this.resourceController);
this.disposables.add(
this.model.props.sourceId$.subscribe(() => {
this.refreshData();
})
);
this.refreshData();
}
override renderGfxBlock() {

View File

@@ -37,6 +37,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./store": "./src/store.ts",
"./view": "./src/view.ts"
},

View File

@@ -36,6 +36,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./turbo-painter": "./src/turbo/list-painter.worker.ts",
"./view": "./src/view.ts",
"./store": "./src/store.ts"

View File

@@ -39,6 +39,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./turbo-painter": "./src/turbo/note-painter.worker.ts",
"./store": "./src/store.ts",
"./view": "./src/view.ts"

View File

@@ -32,6 +32,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./turbo-painter": "./src/turbo/paragraph-painter.worker.ts",
"./store": "./src/store.ts",
"./view": "./src/view.ts"

View File

@@ -15,6 +15,7 @@
"@blocksuite/affine-block-database": "workspace:*",
"@blocksuite/affine-block-edgeless-text": "workspace:*",
"@blocksuite/affine-block-embed": "workspace:*",
"@blocksuite/affine-block-embed-doc": "workspace:*",
"@blocksuite/affine-block-frame": "workspace:*",
"@blocksuite/affine-block-image": "workspace:*",
"@blocksuite/affine-block-note": "workspace:*",
@@ -34,7 +35,6 @@
"@blocksuite/affine-model": "workspace:*",
"@blocksuite/affine-rich-text": "workspace:*",
"@blocksuite/affine-shared": "workspace:*",
"@blocksuite/affine-widget-edgeless-selected-rect": "workspace:*",
"@blocksuite/affine-widget-edgeless-toolbar": "workspace:*",
"@blocksuite/data-view": "workspace:*",
"@blocksuite/global": "workspace:*",

View File

@@ -3,7 +3,6 @@ import {
pasteMiddleware,
replaceIdMiddleware,
surfaceRefToEmbed,
uploadMiddleware,
} from '@blocksuite/affine-shared/adapters';
import {
clearAndSelectFirstModelCommand,
@@ -35,17 +34,14 @@ export class PageClipboard extends ReadOnlyClipboard {
// When pastina a surface-ref block to another doc
const surfaceRefToEmbedMiddleware = surfaceRefToEmbed(this.std);
const replaceId = replaceIdMiddleware(this.std.store.workspace.idGenerator);
const upload = uploadMiddleware(this.std);
this.std.clipboard.use(paste);
this.std.clipboard.use(surfaceRefToEmbedMiddleware);
this.std.clipboard.use(replaceId);
this.std.clipboard.use(upload);
this._disposables.add({
dispose: () => {
this.std.clipboard.unuse(paste);
this.std.clipboard.unuse(surfaceRefToEmbedMiddleware);
this.std.clipboard.unuse(replaceId);
this.std.clipboard.unuse(upload);
},
});
};

View File

@@ -744,7 +744,7 @@ export class EdgelessAutoComplete extends WithDisposable(LitElement) {
if (
this._isMoving ||
(this._isHover && !isShape && !this._canAutoComplete())
(this._isHover && !isShape && this._canAutoComplete())
) {
this.removeOverlay();
return nothing;

View File

@@ -19,13 +19,7 @@ import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import { Bound, normalizeDegAngle, type XYWH } from '@blocksuite/global/gfx';
import { assertType } from '@blocksuite/global/utils';
import type { BlockComponent } from '@blocksuite/std';
import type {
CursorType,
GfxController,
GfxModel,
ResizeHandle,
StandardCursor,
} from '@blocksuite/std/gfx';
import type { GfxController, GfxModel } from '@blocksuite/std/gfx';
import * as Y from 'yjs';
export enum Direction {
@@ -354,63 +348,3 @@ export function createShapeElement(
}
return id;
}
const rotateCursorMap: {
[key in ResizeHandle]: number;
} = {
'top-right': 0,
'bottom-right': 90,
'bottom-left': 180,
'top-left': 270,
// not used
left: 0,
right: 0,
top: 0,
bottom: 0,
};
export function generateCursorUrl(
angle = 0,
handle: ResizeHandle,
fallback: StandardCursor = 'default'
): CursorType {
angle = ((angle % 360) + 360) % 360;
return `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cg transform='rotate(${rotateCursorMap[handle] + angle} 16 16)'%3E%3Cpath fill='white' d='M13.7,18.5h3.9l0-1.5c0-1.4-1.2-2.6-2.6-2.6h-1.5v3.9l-5.8-5.8l5.8-5.8v3.9h2.3c3.1,0,5.6,2.5,5.6,5.6v2.3h3.9l-5.8,5.8L13.7,18.5z'/%3E%3Cpath d='M20.4,19.4v-3.2c0-2.6-2.1-4.7-4.7-4.7h-3.2l0,0V9L9,12.6l3.6,3.6v-2.6l0,0H15c1.9,0,3.5,1.6,3.5,3.5v2.4l0,0h-2.6l3.6,3.6l3.6-3.6L20.4,19.4L20.4,19.4z'/%3E%3C/g%3E%3C/svg%3E") 16 16, ${fallback}`;
}
const handleToRotateMap: {
[key in ResizeHandle]: number;
} = {
'top-left': 45,
'top-right': 135,
'bottom-right': 45,
'bottom-left': 135,
left: 0,
right: 0,
top: 90,
bottom: 90,
};
const rotateToHandleMap: {
[key: number]: StandardCursor;
} = {
0: 'ew-resize',
45: 'nwse-resize',
90: 'ns-resize',
135: 'nesw-resize',
};
export function getRotatedResizeCursor(option: {
handle: ResizeHandle;
angle: number;
}) {
const angle =
(Math.round(
(handleToRotateMap[option.handle] + ((option.angle + 360) % 360)) / 45
) %
4) *
45;
return rotateToHandleMap[angle] || 'default';
}

View File

@@ -12,17 +12,17 @@ import {
import { EDGELESS_BLOCK_CHILD_PADDING } from '@blocksuite/affine-shared/consts';
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
import { getRectByBlockComponent } from '@blocksuite/affine-shared/utils';
import type { EdgelessSelectedRectWidget } from '@blocksuite/affine-widget-edgeless-selected-rect';
import { DisposableGroup } from '@blocksuite/global/disposable';
import { deserializeXYWH, Point, serializeXYWH } from '@blocksuite/global/gfx';
import { ScissorsIcon } from '@blocksuite/icons/lit';
import { WidgetComponent, WidgetViewExtension } from '@blocksuite/std';
import { WidgetComponent } from '@blocksuite/std';
import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
import { css, html, nothing, type PropertyValues } from 'lit';
import { state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { styleMap } from 'lit/directives/style-map.js';
import { literal, unsafeStatic } from 'lit/static-html.js';
import type { EdgelessSelectedRectWidget } from '../rects/edgeless-selected-rect';
const DIVIDING_LINE_OFFSET = 4;
const NEW_NOTE_GAP = 40;
@@ -443,9 +443,3 @@ export class NoteSlicer extends WidgetComponent<RootBlockModel> {
@state()
private accessor _isResizing = false;
}
export const noteSlicerWidget = WidgetViewExtension(
'affine:page',
NOTE_SLICER_WIDGET,
literal`${unsafeStatic(NOTE_SLICER_WIDGET)}`
);

View File

@@ -3,12 +3,11 @@ import {
DefaultTool,
} from '@blocksuite/affine-block-surface';
import type { RootBlockModel } from '@blocksuite/affine-model';
import { WidgetComponent, WidgetViewExtension } from '@blocksuite/std';
import { WidgetComponent } from '@blocksuite/std';
import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
import { cssVarV2 } from '@toeverything/theme/v2';
import { css, html, nothing, unsafeCSS } from 'lit';
import { styleMap } from 'lit/directives/style-map.js';
import { literal, unsafeStatic } from 'lit/static-html.js';
export const EDGELESS_DRAGGING_AREA_WIDGET = 'edgeless-dragging-area-rect';
@@ -60,9 +59,3 @@ export class EdgelessDraggingAreaRectWidget extends WidgetComponent<RootBlockMod
`;
}
}
export const edgelessDraggingAreaWidget = WidgetViewExtension(
'affine:page',
EDGELESS_DRAGGING_AREA_WIDGET,
literal`${unsafeStatic(EDGELESS_DRAGGING_AREA_WIDGET)}`
);

View File

@@ -1,8 +1,5 @@
import { type FrameOverlay } from '@blocksuite/affine-block-frame';
import {
EdgelessLegacySlotIdentifier,
OverlayIdentifier,
} from '@blocksuite/affine-block-surface';
import { OverlayIdentifier } from '@blocksuite/affine-block-surface';
import {
ConnectorElementModel,
type RootBlockModel,
@@ -30,12 +27,16 @@ import { repeat } from 'lit/directives/repeat.js';
import { styleMap } from 'lit/directives/style-map.js';
import { type Subscription } from 'rxjs';
import { RenderResizeHandles } from './resize-handles.js';
import { generateCursorUrl, getRotatedResizeCursor } from './utils.js';
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
import { RenderResizeHandles } from '../resize/resize-handles.js';
import { generateCursorUrl, getRotatedResizeCursor } from '../utils.js';
export const EDGELESS_SELECTED_RECT_WIDGET = 'edgeless-selected-rect';
export class EdgelessSelectedRectWidget extends WidgetComponent<RootBlockModel> {
export class EdgelessSelectedRectWidget extends WidgetComponent<
RootBlockModel,
EdgelessRootBlockComponent
> {
// disable change-in-update warning
static override enabledWarnings = [];
@@ -468,6 +469,10 @@ export class EdgelessSelectedRectWidget extends WidgetComponent<RootBlockModel>
};
}, this);
get edgelessSlots() {
return this.block?.slots;
}
get frameOverlay() {
return this.std.get(OverlayIdentifier('frame')) as FrameOverlay;
}
@@ -499,7 +504,7 @@ export class EdgelessSelectedRectWidget extends WidgetComponent<RootBlockModel>
}
override firstUpdated() {
const { _disposables, selection, gfx } = this;
const { _disposables, block, selection, gfx } = this;
_disposables.add(
// viewport zooming / scrolling
@@ -526,13 +531,15 @@ export class EdgelessSelectedRectWidget extends WidgetComponent<RootBlockModel>
selection.slots.updated.subscribe(this._updateOnSelectionChange)
);
_disposables.add(
this._slots.readonlyUpdated.subscribe(() => this.requestUpdate())
);
if (block) {
_disposables.add(
block.slots.readonlyUpdated.subscribe(() => this.requestUpdate())
);
_disposables.add(
this._slots.elementResizeEnd.subscribe(() => (this._isResizing = false))
);
_disposables.add(
block.slots.elementResizeEnd.subscribe(() => (this._isResizing = false))
);
}
if (this._interaction) {
_disposables.add(
@@ -547,9 +554,9 @@ export class EdgelessSelectedRectWidget extends WidgetComponent<RootBlockModel>
this._isResizing = newVal;
if (newVal) {
this._slots.elementResizeStart.next();
block?.slots.elementResizeStart.next();
} else {
this._slots.elementResizeEnd.next();
block?.slots.elementResizeEnd.next();
}
})
);
@@ -564,10 +571,6 @@ export class EdgelessSelectedRectWidget extends WidgetComponent<RootBlockModel>
return this.std.getOptional(InteractivityIdentifier);
}
private get _slots() {
return this.std.get(EdgelessLegacySlotIdentifier);
}
private _renderHandles() {
const { selection, gfx, block, store } = this;
const elements = selection.selectedElements;

View File

@@ -0,0 +1,65 @@
import type {
CursorType,
ResizeHandle,
StandardCursor,
} from '@blocksuite/std/gfx';
const rotateCursorMap: {
[key in ResizeHandle]: number;
} = {
'top-right': 0,
'bottom-right': 90,
'bottom-left': 180,
'top-left': 270,
// not used
left: 0,
right: 0,
top: 0,
bottom: 0,
};
export function generateCursorUrl(
angle = 0,
handle: ResizeHandle,
fallback: StandardCursor = 'default'
): CursorType {
angle = ((angle % 360) + 360) % 360;
return `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cg transform='rotate(${rotateCursorMap[handle] + angle} 16 16)'%3E%3Cpath fill='white' d='M13.7,18.5h3.9l0-1.5c0-1.4-1.2-2.6-2.6-2.6h-1.5v3.9l-5.8-5.8l5.8-5.8v3.9h2.3c3.1,0,5.6,2.5,5.6,5.6v2.3h3.9l-5.8,5.8L13.7,18.5z'/%3E%3Cpath d='M20.4,19.4v-3.2c0-2.6-2.1-4.7-4.7-4.7h-3.2l0,0V9L9,12.6l3.6,3.6v-2.6l0,0H15c1.9,0,3.5,1.6,3.5,3.5v2.4l0,0h-2.6l3.6,3.6l3.6-3.6L20.4,19.4L20.4,19.4z'/%3E%3C/g%3E%3C/svg%3E") 16 16, ${fallback}`;
}
const handleToRotateMap: {
[key in ResizeHandle]: number;
} = {
'top-left': 45,
'top-right': 135,
'bottom-right': 45,
'bottom-left': 135,
left: 0,
right: 0,
top: 90,
bottom: 90,
};
const rotateToHandleMap: {
[key: number]: StandardCursor;
} = {
0: 'ew-resize',
45: 'nwse-resize',
90: 'ns-resize',
135: 'nesw-resize',
};
export function getRotatedResizeCursor(option: {
handle: ResizeHandle;
angle: number;
}) {
const angle =
(Math.round(
(handleToRotateMap[option.handle] + ((option.angle + 360) % 360)) / 45
) %
4) *
45;
return rotateToHandleMap[angle] || 'default';
}

View File

@@ -1,11 +1,7 @@
import { insertLinkByQuickSearchCommand } from '@blocksuite/affine-block-bookmark';
import { EdgelessTextBlockComponent } from '@blocksuite/affine-block-edgeless-text';
import { FrameTool } from '@blocksuite/affine-block-frame';
import {
DefaultTool,
EdgelessLegacySlotIdentifier,
isNoteBlock,
} from '@blocksuite/affine-block-surface';
import { DefaultTool, isNoteBlock } from '@blocksuite/affine-block-surface';
import { toast } from '@blocksuite/affine-components/toast';
import {
BrushTool,
@@ -51,7 +47,6 @@ import { SurfaceSelection, TextSelection } from '@blocksuite/std';
import {
type BaseTool,
GfxBlockElementModel,
GfxControllerIdentifier,
type GfxPrimitiveElementModel,
isGfxGroupCompatibleModel,
type ToolOptions,
@@ -71,15 +66,7 @@ import { isCanvasElement } from './utils/query.js';
export class EdgelessPageKeyboardManager extends PageKeyboardManager {
get gfx() {
return this.std.get(GfxControllerIdentifier);
}
get slots() {
return this.std.get(EdgelessLegacySlotIdentifier);
}
get std() {
return this.rootComponent.std;
return this.rootComponent.gfx;
}
constructor(override rootComponent: EdgelessRootBlockComponent) {
@@ -131,7 +118,7 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager {
NoteBlockModel,
])
) {
this.slots.toggleNoteSlicer.next();
rootComponent.slots.toggleNoteSlicer.next();
}
},
f: () => {
@@ -166,7 +153,7 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager {
elements.length === 1 &&
isNoteBlock(elements[0])
) {
this.slots.toggleNoteSlicer.next();
rootComponent.slots.toggleNoteSlicer.next();
}
},
'@': () => {
@@ -467,9 +454,6 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager {
const selection = gfx.selection;
if (event.code === 'Space' && !event.repeat) {
const currentToolName =
this.rootComponent.gfx.tool.currentToolName$.peek();
if (currentToolName === 'frameNavigator') return false;
this._space(event);
} else if (
!selection.editing &&
@@ -507,12 +491,8 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager {
ctx => {
const event = ctx.get('keyboardState').raw;
if (event.code === 'Space' && !event.repeat) {
const currentToolName =
this.rootComponent.gfx.tool.currentToolName$.peek();
if (currentToolName === 'frameNavigator') return false;
this._space(event);
}
return false;
},
{ global: true }
);
@@ -725,7 +705,7 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager {
}
this._setEdgelessTool(PanTool, { panning: false });
this.std.event.disposables.addFromEvent(
edgeless.dispatcher.disposables.addFromEvent(
document,
'keyup',
revertToPrevTool

View File

@@ -1,6 +1,7 @@
import { NoteConfigExtension } from '@blocksuite/affine-block-note';
import {
DefaultTool,
EdgelessLegacySlotIdentifier,
getBgGridGap,
normalizeWheelDeltaY,
type SurfaceBlockComponent,
@@ -44,6 +45,7 @@ import { css, html } from 'lit';
import { query } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import type { EdgelessSelectedRectWidget } from './components/rects/edgeless-selected-rect.js';
import { EdgelessPageKeyboardManager } from './edgeless-keyboard.js';
import type { EdgelessRootService } from './edgeless-root-service.js';
import { isCanvasElement } from './utils/query.js';
@@ -119,27 +121,43 @@ export class EdgelessRootBlockComponent extends BlockComponent<
keyboardManager: EdgelessPageKeyboardManager | null = null;
get dispatcher() {
return this.std.event;
}
get fontLoader() {
return this.std.get(FontLoaderService);
}
get gfx() {
return this.std.get(GfxControllerIdentifier);
}
get selectedRectWidget() {
return this.host.view.getWidget(
'edgeless-selected-rect',
this.host.id
) as EdgelessSelectedRectWidget;
}
get slots() {
return this.std.get(EdgelessLegacySlotIdentifier);
}
get surfaceBlockModel() {
return this.model.children.find(
child => child.flavour === 'affine:surface'
) as SurfaceBlockModel;
}
private get _viewportElement(): HTMLElement {
get viewportElement(): HTMLElement {
return this.std.get(ViewportElementProvider).viewportElement;
}
get fontLoader() {
return this.std.get(FontLoaderService);
}
private _initFontLoader() {
this.fontLoader.ready
.then(() => {
this.std
.get(FontLoaderService)
.ready.then(() => {
this.surface.refresh();
})
.catch(console.error);
@@ -163,7 +181,7 @@ export class EdgelessRootBlockComponent extends BlockComponent<
private _initPanEvent() {
this.disposables.add(
this.std.event.add('pan', ctx => {
this.dispatcher.add('pan', ctx => {
const { viewport } = this.gfx;
if (viewport.locked) return;
@@ -187,7 +205,7 @@ export class EdgelessRootBlockComponent extends BlockComponent<
private _initPinchEvent() {
this.disposables.add(
this.std.event.add('pinch', ctx => {
this.dispatcher.add('pinch', ctx => {
const { viewport } = this.gfx;
if (viewport.locked) return;
@@ -267,7 +285,7 @@ export class EdgelessRootBlockComponent extends BlockComponent<
this.gfx.viewport.onResize();
});
resizeObserver.observe(this._viewportElement);
resizeObserver.observe(this.viewportElement);
this._resizeObserver = resizeObserver;
}
@@ -330,7 +348,7 @@ export class EdgelessRootBlockComponent extends BlockComponent<
private _initWheelEvent() {
this._disposables.add(
this.std.event.add('wheel', ctx => {
this.dispatcher.add('wheel', ctx => {
const config = this.std.getOptional(EditorSettingProvider)?.setting$;
const state = ctx.get('defaultState');
const e = state.event as WheelEvent;

View File

@@ -25,9 +25,13 @@ import { css, html } from 'lit';
import { query, state } from 'lit/decorators.js';
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
import { isCanvasElement } from '../edgeless/utils/query';
import type { EdgelessRootService } from './edgeless-root-service.js';
import { isCanvasElement } from './utils/query.js';
export class EdgelessRootPreviewBlockComponent extends BlockComponent<RootBlockModel> {
export class EdgelessRootPreviewBlockComponent extends BlockComponent<
RootBlockModel,
EdgelessRootService
> {
static override styles = css`
affine-edgeless-root-preview {
pointer-events: none;
@@ -62,13 +66,9 @@ export class EdgelessRootPreviewBlockComponent extends BlockComponent<RootBlockM
}
`;
private get _viewport() {
return this._gfx.viewport;
}
private readonly _refreshLayerViewport = requestThrottledConnectedFrame(
() => {
const { zoom, translateX, translateY } = this._viewport;
const { zoom, translateX, translateY } = this.service.viewport;
const gap = getBgGridGap(zoom);
this.backgroundStyle = {
@@ -81,6 +81,10 @@ export class EdgelessRootPreviewBlockComponent extends BlockComponent<RootBlockM
private _resizeObserver: ResizeObserver | null = null;
get dispatcher() {
return this.service?.uiEventDispatcher;
}
private get _gfx() {
return this.std.get(GfxControllerIdentifier);
}

View File

@@ -1,5 +1,26 @@
import { LifeCycleWatcher } from '@blocksuite/std';
import { LifeCycleWatcher, WidgetViewExtension } from '@blocksuite/std';
import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
import { literal, unsafeStatic } from 'lit/static-html.js';
import { NOTE_SLICER_WIDGET } from './components/note-slicer/index.js';
import { EDGELESS_DRAGGING_AREA_WIDGET } from './components/rects/edgeless-dragging-area-rect.js';
import { EDGELESS_SELECTED_RECT_WIDGET } from './components/rects/edgeless-selected-rect.js';
export const edgelessDraggingAreaWidget = WidgetViewExtension(
'affine:page',
EDGELESS_DRAGGING_AREA_WIDGET,
literal`${unsafeStatic(EDGELESS_DRAGGING_AREA_WIDGET)}`
);
export const noteSlicerWidget = WidgetViewExtension(
'affine:page',
NOTE_SLICER_WIDGET,
literal`${unsafeStatic(NOTE_SLICER_WIDGET)}`
);
export const edgelessSelectedRectWidget = WidgetViewExtension(
'affine:page',
EDGELESS_SELECTED_RECT_WIDGET,
literal`${unsafeStatic(EDGELESS_SELECTED_RECT_WIDGET)}`
);
export class EdgelessLocker extends LifeCycleWatcher {
static override key = 'edgeless-locker';

View File

@@ -1,7 +1,7 @@
export { EdgelessRootPreviewBlockComponent } from '../preview/edgeless-root-preview-block';
export * from './clipboard/clipboard';
export * from './clipboard/command';
export * from './edgeless-root-block.js';
export { EdgelessRootPreviewBlockComponent } from './edgeless-root-preview-block.js';
export { EdgelessRootService } from './edgeless-root-service.js';
export * from './utils/clipboard-utils.js';
export { sortEdgelessElements } from './utils/clone-utils.js';

View File

@@ -1,3 +1,17 @@
import { EdgelessAutoCompletePanel } from './edgeless/components/auto-complete/auto-complete-panel.js';
import { EdgelessAutoComplete } from './edgeless/components/auto-complete/edgeless-auto-complete.js';
import {
NOTE_SLICER_WIDGET,
NoteSlicer,
} from './edgeless/components/note-slicer/index.js';
import {
EDGELESS_DRAGGING_AREA_WIDGET,
EdgelessDraggingAreaRectWidget,
} from './edgeless/components/rects/edgeless-dragging-area-rect.js';
import {
EDGELESS_SELECTED_RECT_WIDGET,
EdgelessSelectedRectWidget,
} from './edgeless/components/rects/edgeless-selected-rect.js';
import {
EdgelessRootBlockComponent,
EdgelessRootPreviewBlockComponent,
@@ -8,6 +22,7 @@ import {
export function effects() {
// Register components by category
registerRootComponents();
registerMiscComponents();
}
function registerRootComponents() {
@@ -20,9 +35,37 @@ function registerRootComponents() {
);
}
function registerMiscComponents() {
// Auto-complete components
customElements.define(
'edgeless-auto-complete-panel',
EdgelessAutoCompletePanel
);
customElements.define('edgeless-auto-complete', EdgelessAutoComplete);
// Note and template components
customElements.define(NOTE_SLICER_WIDGET, NoteSlicer);
// Dragging area components
customElements.define(
EDGELESS_DRAGGING_AREA_WIDGET,
EdgelessDraggingAreaRectWidget
);
customElements.define(
EDGELESS_SELECTED_RECT_WIDGET,
EdgelessSelectedRectWidget
);
}
declare global {
interface HTMLElementTagNameMap {
'affine-edgeless-root': EdgelessRootBlockComponent;
'affine-edgeless-root-preview': EdgelessRootPreviewBlockComponent;
'edgeless-auto-complete-panel': EdgelessAutoCompletePanel;
'edgeless-auto-complete': EdgelessAutoComplete;
'note-slicer': NoteSlicer;
'edgeless-dragging-area-rect': EdgelessDraggingAreaRectWidget;
'edgeless-selected-rect': EdgelessSelectedRectWidget;
'affine-page-root': PageRootBlockComponent;
}
}

View File

@@ -18,7 +18,12 @@ import { PageClipboard, ReadOnlyClipboard } from './clipboard';
import { builtinToolbarConfig } from './configs/toolbar';
import { EdgelessClipboardController, EdgelessRootService } from './edgeless';
import { EdgelessElementToolbarExtension } from './edgeless/configs/toolbar';
import { EdgelessLocker } from './edgeless/edgeless-root-spec';
import {
edgelessDraggingAreaWidget,
EdgelessLocker,
edgelessSelectedRectWidget,
noteSlicerWidget,
} from './edgeless/edgeless-root-spec';
import { AltCloneExtension } from './edgeless/interact-extensions/clone-ext';
import { effects } from './effects';
import { fallbackKeymap } from './keyboard/keymap';
@@ -85,6 +90,9 @@ export class RootViewExtension extends ViewExtensionProvider {
}
context.register([
BlockViewExtension('affine:page', literal`affine-edgeless-root`),
edgelessDraggingAreaWidget,
noteSlicerWidget,
edgelessSelectedRectWidget,
EdgelessClipboardController,
AltCloneExtension,
]);

View File

@@ -12,6 +12,7 @@
{ "path": "../database" },
{ "path": "../edgeless-text" },
{ "path": "../embed" },
{ "path": "../embed-doc" },
{ "path": "../frame" },
{ "path": "../image" },
{ "path": "../note" },
@@ -31,7 +32,6 @@
{ "path": "../../model" },
{ "path": "../../rich-text" },
{ "path": "../../shared" },
{ "path": "../../widgets/edgeless-selected-rect" },
{ "path": "../../widgets/edgeless-toolbar" },
{ "path": "../../data-view" },
{ "path": "../../../framework/global" },

View File

@@ -36,6 +36,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./store": "./src/store.ts",
"./view": "./src/view.ts"
},

View File

@@ -37,6 +37,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./store": "./src/store.ts",
"./view": "./src/view.ts"
},

View File

@@ -28,7 +28,6 @@ export const SurfaceBlockSchema = defineBlockSchema({
'affine:attachment',
'affine:embed-*',
'affine:edgeless-text',
'affine:code',
],
},
transformer: transformerConfigs =>

View File

@@ -33,6 +33,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts",
"./store": "./src/store.ts"
},

View File

@@ -1,173 +0,0 @@
import {
FileIconAepIcon,
FileIconAiIcon,
FileIconAviIcon,
FileIconCssIcon,
FileIconCsvIcon,
FileIconDmgIcon,
FileIconDocIcon,
FileIconDocxIcon,
FileIconEpsIcon,
FileIconExeIcon,
FileIconFigIcon,
FileIconGifIcon,
FileIconHtmlIcon,
FileIconInddIcon,
FileIconJavaIcon,
FileIconJpegIcon,
FileIconJsIcon,
FileIconJsonIcon,
FileIconMkvIcon,
FileIconMp3Icon,
FileIconMp4Icon,
FileIconMpegIcon,
FileIconNoneIcon,
FileIconPdfIcon,
FileIconPngIcon,
FileIconPptIcon,
FileIconPptxIcon,
FileIconPsdIcon,
FileIconRarIcon,
FileIconRssIcon,
FileIconSqlIcon,
FileIconSvgIcon,
FileIconTiffIcon,
FileIconTxtIcon,
FileIconWavIcon,
FileIconWebpIcon,
FileIconXlsIcon,
FileIconXlsxIcon,
FileIconXmlIcon,
FileIconZipIcon,
ImageIcon,
} from '@blocksuite/icons/rc';
export function getAttachmentFileIconRC(filetype: string) {
switch (filetype) {
case 'img':
return ImageIcon;
case 'image/jpeg':
case 'jpg':
case 'jpeg':
return FileIconJpegIcon;
case 'image/png':
case 'png':
return FileIconPngIcon;
case 'image/webp':
case 'webp':
return FileIconWebpIcon;
case 'image/tiff':
case 'tiff':
return FileIconTiffIcon;
case 'image/gif':
case 'gif':
return FileIconGifIcon;
case 'image/svg':
case 'svg':
return FileIconSvgIcon;
case 'image/eps':
case 'eps':
return FileIconEpsIcon;
case 'application/pdf':
case 'pdf':
return FileIconPdfIcon;
case 'application/msword':
case 'application/x-iwork-pages-sffpages':
case 'doc':
return FileIconDocIcon;
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
case 'docx':
return FileIconDocxIcon;
case 'text/plain':
case 'txt':
return FileIconTxtIcon;
case 'csv':
return FileIconCsvIcon;
case 'application/vnd.ms-excel':
case 'xls':
return FileIconXlsIcon;
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
case 'application/x-iwork-numbers-sffnumbers':
case 'xlsx':
return FileIconXlsxIcon;
case 'application/vnd.ms-powerpoint':
case 'application/x-iwork-keynote-sffkeynote':
case 'ppt':
return FileIconPptIcon;
case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
case 'pptx':
return FileIconPptxIcon;
case 'application/illustrator':
case 'fig':
return FileIconFigIcon;
case 'application/postscript':
case 'ai':
return FileIconAiIcon;
case 'application/vnd.adobe.photoshop':
case 'psd':
return FileIconPsdIcon;
case 'application/vnd.adobe.indesign':
case 'indd':
return FileIconInddIcon;
case 'application/vnd.adobe.afterfx':
case 'aep':
return FileIconAepIcon;
case 'audio/mpeg':
case 'audio/mp3':
case 'mp3':
return FileIconMp3Icon;
case 'audio/wav':
case 'wav':
return FileIconWavIcon;
case 'video/mpeg':
case 'mpeg':
return FileIconMpegIcon;
case 'video/mp4':
case 'mp4':
return FileIconMp4Icon;
case 'video/avi':
case 'avi':
return FileIconAviIcon;
case 'video/mkv':
case 'mkv':
return FileIconMkvIcon;
case 'text/html':
case 'html':
return FileIconHtmlIcon;
case 'text/css':
case 'css':
return FileIconCssIcon;
case 'application/rss+xml':
case 'rss':
return FileIconRssIcon;
case 'application/sql':
case 'sql':
return FileIconSqlIcon;
case 'application/javascript':
case 'js':
return FileIconJsIcon;
case 'application/json':
case 'json':
return FileIconJsonIcon;
case 'application/java':
case 'java':
return FileIconJavaIcon;
case 'application/xml':
case 'xml':
return FileIconXmlIcon;
case 'application/x-msdos-program':
case 'exe':
return FileIconExeIcon;
case 'application/x-apple-diskimage':
case 'dmg':
return FileIconDmgIcon;
case 'application/zip':
case 'zip':
return FileIconZipIcon;
case 'application/x-rar-compressed':
case 'rar':
return FileIconRarIcon;
default:
return FileIconNoneIcon;
}
}

View File

@@ -1,6 +1,5 @@
export * from './ai.js';
export * from './file-icons.js';
export * from './file-icons-rc';
export * from './import-export.js';
export * from './list.js';
export * from './loading.js';

View File

@@ -35,7 +35,6 @@ export type ResolvedStateInfo = StateInfo & ResolvedStateInfoPart;
export class ResourceController implements Disposable {
readonly blobUrl$ = signal<string | null>(null);
// TODO(@fundon): default `loading` status.
readonly state$ = signal<Partial<BlobState>>({});
readonly resolvedState$ = computed<ResolvedStateInfoPart>(() => {

View File

@@ -32,6 +32,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts"
},
"files": [

View File

@@ -34,6 +34,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts"
},
"files": [

View File

@@ -33,6 +33,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts"
},
"files": [

View File

@@ -34,6 +34,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./store": "./src/store.ts",
"./view": "./src/view.ts"
},

View File

@@ -35,6 +35,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts",
"./store": "./src/store.ts"
},

View File

@@ -36,8 +36,6 @@ export class ConnectorFilter extends InteractivityExtension {
elements.sort((a, _) => (a instanceof ConnectorElementModel ? -1 : 1));
}
context.elements = elements;
return {};
});
}

View File

@@ -188,8 +188,6 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
});
this._resizeObserver.observe(this.richText);
this.connector.stash('labelXYWH');
this.updateComplete
.then(() => {
if (!this.inlineEditor) return;
@@ -259,8 +257,7 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
}
}
connector.labelEditing = false;
connector.pop('labelXYWH');
connector.lableEditing = false;
selection.set({
elements: [],
@@ -296,7 +293,7 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
}
);
connector.labelEditing = true;
connector.lableEditing = true;
})
.catch(console.error);
}

View File

@@ -142,8 +142,6 @@ export class ConnectorElementView extends GfxElementModelView<ConnectorElementMo
if (!curLabelElement) {
curLabelElement = labelElement;
labelElement.id = `#${this.model.id}-label`;
labelElement.creator = this.model;
labelElement.fillColor = 'transparent';
labelElement.strokeColor = 'transparent';
labelElement.strokeWidth = 0;

View File

@@ -35,6 +35,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts",
"./store": "./src/store.ts"
},

View File

@@ -37,6 +37,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts"
},
"files": [

View File

@@ -42,6 +42,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts",
"./store": "./src/store.ts"
},

View File

@@ -37,6 +37,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts"
},
"files": [

View File

@@ -34,6 +34,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts"
},
"files": [

View File

@@ -1,13 +1,8 @@
import { on } from '@blocksuite/affine-shared/utils';
import type { PointerEventState } from '@blocksuite/std';
import { BaseTool, MouseButton, type ToolOptions } from '@blocksuite/std/gfx';
import { BaseTool, MouseButton } from '@blocksuite/std/gfx';
import { Signal } from '@preact/signals-core';
interface RestorablePresentToolOptions {
mode?: string; // 'fit' | 'fill', simplified to string for local use
restoredAfterPan?: boolean;
}
export type PanToolOption = {
panning: boolean;
};
@@ -58,30 +53,14 @@ export class PanTool extends BaseTool<PanToolOption> {
evt.raw.preventDefault();
const selection = this.gfx.selection.surfaceSelections;
const currentTool = this.controller.currentToolOption$.peek();
const restoreToPrevious = () => {
const { toolType, options: originalToolOptions } = currentTool;
const selectionToRestore = this.gfx.selection.surfaceSelections;
if (!toolType) return;
let finalOptions: ToolOptions<BaseTool<any>> | undefined =
originalToolOptions;
const PRESENT_TOOL_NAME = 'frameNavigator';
if (toolType.toolName === PRESENT_TOOL_NAME) {
// When restoring PresentTool (frameNavigator) after a temporary pan (e.g., via middle mouse button),
// set 'restoredAfterPan' to true. This allows PresentTool to avoid an unwanted viewport reset
// and maintain the panned position.
const currentPresentOptions = originalToolOptions as
| RestorablePresentToolOptions
| undefined;
finalOptions = {
...currentPresentOptions,
restoredAfterPan: true,
} as RestorablePresentToolOptions;
const { toolType, options } = currentTool;
if (toolType && options) {
this.controller.setTool(toolType, options);
this.gfx.selection.set(selection);
}
this.controller.setTool(toolType, finalOptions);
this.gfx.selection.set(selectionToRestore);
};
this.controller.setTool(PanTool, {

View File

@@ -35,6 +35,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./store": "./src/store.ts",
"./view": "./src/view.ts"
},

View File

@@ -6,16 +6,10 @@ import { manageClassNames, setStyles } from './utils';
function applyShapeSpecificStyles(
model: ShapeElementModel,
element: HTMLElement,
zoom: number
element: HTMLElement
) {
if (model.shapeType === 'rect') {
const w = model.w * zoom;
const h = model.h * zoom;
const r = model.radius ?? 0;
const borderRadius =
r < 1 ? `${Math.min(w * r, h * r)}px` : `${r * zoom}px`;
element.style.borderRadius = borderRadius;
element.style.borderRadius = `${model.radius ?? 0}px`;
} else if (model.shapeType === 'ellipse') {
element.style.borderRadius = '50%';
} else {
@@ -26,12 +20,11 @@ function applyShapeSpecificStyles(
function applyBorderStyles(
model: ShapeElementModel,
element: HTMLElement,
strokeColor: string,
zoom: number
strokeColor: string
) {
element.style.border =
model.strokeStyle !== 'none'
? `${model.strokeWidth * zoom}px ${model.strokeStyle === 'dash' ? 'dashed' : 'solid'} ${strokeColor}`
? `${model.strokeWidth}px ${model.strokeStyle === 'dash' ? 'dashed' : 'solid'} ${strokeColor}`
: 'none';
}
@@ -92,11 +85,11 @@ export const shapeDomRenderer = (
element.style.width = `${model.w * zoom}px`;
element.style.height = `${model.h * zoom}px`;
applyShapeSpecificStyles(model, element, zoom);
applyShapeSpecificStyles(model, element);
element.style.backgroundColor = model.filled ? fillColor : 'transparent';
applyBorderStyles(model, element, strokeColor, zoom);
applyBorderStyles(model, element, strokeColor);
applyTransformStyles(model, element);
element.style.boxSizing = 'border-box';

View File

@@ -36,6 +36,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts"
},
"files": [

View File

@@ -34,6 +34,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts",
"./store": "./src/store.ts"
},

View File

@@ -35,6 +35,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts",
"./store": "./src/store.ts"
},

View File

@@ -41,6 +41,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts",
"./store": "./src/store.ts"
},

View File

@@ -35,6 +35,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./store": "./src/store.ts",
"./view": "./src/view.ts"
},

View File

@@ -34,6 +34,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./view": "./src/view.ts"
},
"files": [

View File

@@ -46,6 +46,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./store": "./src/store.ts",
"./view": "./src/view.ts"
},

View File

@@ -34,6 +34,7 @@
},
"exports": {
".": "./src/index.ts",
"./effects": "./src/effects.ts",
"./store": "./src/store.ts",
"./view": "./src/view.ts"
},

View File

@@ -1,8 +1,3 @@
import {
type GfxCommonBlockProps,
GfxCompatible,
type GfxElementGeometry,
} from '@blocksuite/std/gfx';
import {
BlockModel,
BlockSchemaExtension,
@@ -18,9 +13,7 @@ type CodeBlockProps = {
wrap: boolean;
caption: string;
preview?: boolean;
lineNumber?: boolean;
} & BlockMeta &
GfxCommonBlockProps;
} & BlockMeta;
export const CodeBlockSchema = defineBlockSchema({
flavour: 'affine:code',
@@ -31,15 +24,10 @@ export const CodeBlockSchema = defineBlockSchema({
wrap: false,
caption: '',
preview: undefined,
lineNumber: undefined,
'meta:createdAt': undefined,
'meta:createdBy': undefined,
'meta:updatedAt': undefined,
'meta:updatedBy': undefined,
xywh: '[0,0,16,16]',
index: 'a0',
scale: 1,
rotate: 0,
}) as CodeBlockProps,
metadata: {
version: 1,
@@ -49,7 +37,6 @@ export const CodeBlockSchema = defineBlockSchema({
'affine:paragraph',
'affine:list',
'affine:edgeless-text',
'affine:surface',
],
children: [],
},
@@ -58,6 +45,4 @@ export const CodeBlockSchema = defineBlockSchema({
export const CodeBlockSchemaExtension = BlockSchemaExtension(CodeBlockSchema);
export class CodeBlockModel
extends GfxCompatible<CodeBlockProps>(BlockModel)
implements GfxElementGeometry {}
export class CodeBlockModel extends BlockModel<CodeBlockProps> {}

View File

@@ -11,10 +11,6 @@ export type EmbedSyncedDocBlockProps = {
style: EmbedCardStyle;
caption?: string | null;
scale?: number;
/**
* Record the scaled height of the synced doc block when it is folded,
* a.k.a the fourth number of the `xywh`
*/
preFoldHeight?: number;
} & ReferenceInfo &
GfxCompatibleProps;

Some files were not shown because too many files have changed in this diff Show More