fix(editor): rework disable middle click settings for linux (#11556)
fix BS-3028 Unfortunately, I don't find out a way to disable this behavior on ff linux
This commit is contained in:
@@ -1,192 +0,0 @@
|
|||||||
import type {
|
|
||||||
EdgelessEditor,
|
|
||||||
PageEditor,
|
|
||||||
} from '@affine/core/blocksuite/editors';
|
|
||||||
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
|
||||||
import { appendParagraphCommand } from '@blocksuite/affine/blocks/paragraph';
|
|
||||||
import type { DocTitle } from '@blocksuite/affine/fragments/doc-title';
|
|
||||||
import type { DocMode, RootBlockModel } from '@blocksuite/affine/model';
|
|
||||||
import { focusBlockEnd } from '@blocksuite/affine/shared/commands';
|
|
||||||
import { getLastNoteBlock } from '@blocksuite/affine/shared/utils';
|
|
||||||
import type { BlockStdScope, EditorHost } from '@blocksuite/affine/std';
|
|
||||||
import { type Store } from '@blocksuite/affine/store';
|
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
|
||||||
import clsx from 'clsx';
|
|
||||||
import type React from 'react';
|
|
||||||
import {
|
|
||||||
forwardRef,
|
|
||||||
useCallback,
|
|
||||||
useImperativeHandle,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
import type { DefaultOpenProperty } from '../../components/doc-properties';
|
|
||||||
import { BlocksuiteDocEditor, BlocksuiteEdgelessEditor } from './lit-adaper';
|
|
||||||
import * as styles from './styles.css';
|
|
||||||
|
|
||||||
interface BlocksuiteEditorContainerProps
|
|
||||||
extends React.HTMLAttributes<HTMLDivElement> {
|
|
||||||
page: Store;
|
|
||||||
mode: DocMode;
|
|
||||||
shared?: boolean;
|
|
||||||
readonly?: boolean;
|
|
||||||
defaultOpenProperty?: DefaultOpenProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AffineEditorContainer extends HTMLElement {
|
|
||||||
page: Store;
|
|
||||||
doc: Store;
|
|
||||||
docTitle: DocTitle;
|
|
||||||
host?: EditorHost;
|
|
||||||
model: RootBlockModel | null;
|
|
||||||
updateComplete: Promise<boolean>;
|
|
||||||
mode: DocMode;
|
|
||||||
origin: HTMLDivElement;
|
|
||||||
std: BlockStdScope;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const BlocksuiteEditorContainer = forwardRef<
|
|
||||||
AffineEditorContainer,
|
|
||||||
BlocksuiteEditorContainerProps
|
|
||||||
>(function AffineEditorContainer(
|
|
||||||
{ page, mode, shared, readonly, defaultOpenProperty, ...props },
|
|
||||||
ref
|
|
||||||
) {
|
|
||||||
const rootRef = useRef<HTMLDivElement>(null);
|
|
||||||
const docRef = useRef<PageEditor>(null);
|
|
||||||
const docTitleRef = useRef<DocTitle>(null);
|
|
||||||
const edgelessRef = useRef<EdgelessEditor>(null);
|
|
||||||
const featureFlags = useService(FeatureFlagService).flags;
|
|
||||||
const enableEditorRTL = useLiveData(featureFlags.enable_editor_rtl.$);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mimic an AffineEditorContainer using proxy
|
|
||||||
*/
|
|
||||||
const affineEditorContainerProxy = useMemo(() => {
|
|
||||||
const api = {
|
|
||||||
get page() {
|
|
||||||
return page;
|
|
||||||
},
|
|
||||||
get doc() {
|
|
||||||
return page;
|
|
||||||
},
|
|
||||||
get docTitle() {
|
|
||||||
return docTitleRef.current;
|
|
||||||
},
|
|
||||||
get host() {
|
|
||||||
return (
|
|
||||||
(mode === 'page'
|
|
||||||
? docRef.current?.host
|
|
||||||
: edgelessRef.current?.host) ?? null
|
|
||||||
);
|
|
||||||
},
|
|
||||||
get model() {
|
|
||||||
return page.root as any;
|
|
||||||
},
|
|
||||||
get updateComplete() {
|
|
||||||
return mode === 'page'
|
|
||||||
? docRef.current?.updateComplete
|
|
||||||
: edgelessRef.current?.updateComplete;
|
|
||||||
},
|
|
||||||
get mode() {
|
|
||||||
return mode;
|
|
||||||
},
|
|
||||||
get origin() {
|
|
||||||
return rootRef.current;
|
|
||||||
},
|
|
||||||
get std() {
|
|
||||||
return mode === 'page' ? docRef.current?.std : edgelessRef.current?.std;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const proxy = new Proxy(api, {
|
|
||||||
has(_, prop) {
|
|
||||||
return (
|
|
||||||
Reflect.has(api, prop) ||
|
|
||||||
(rootRef.current ? Reflect.has(rootRef.current, prop) : false)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
get(_, prop) {
|
|
||||||
if (Reflect.has(api, prop)) {
|
|
||||||
return api[prop as keyof typeof api];
|
|
||||||
}
|
|
||||||
if (rootRef.current && Reflect.has(rootRef.current, prop)) {
|
|
||||||
const maybeFn = Reflect.get(rootRef.current, prop);
|
|
||||||
if (typeof maybeFn === 'function') {
|
|
||||||
return maybeFn.bind(rootRef.current);
|
|
||||||
} else {
|
|
||||||
return maybeFn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
},
|
|
||||||
}) as AffineEditorContainer;
|
|
||||||
|
|
||||||
return proxy;
|
|
||||||
}, [mode, page]);
|
|
||||||
|
|
||||||
useImperativeHandle(ref, () => affineEditorContainerProxy, [
|
|
||||||
affineEditorContainerProxy,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const handleClickPageModeBlank = useCallback(() => {
|
|
||||||
if (shared || readonly || page.readonly) return;
|
|
||||||
const std = affineEditorContainerProxy.host?.std;
|
|
||||||
if (!std) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const note = getLastNoteBlock(page);
|
|
||||||
if (note) {
|
|
||||||
const lastBlock = note.lastChild();
|
|
||||||
if (
|
|
||||||
lastBlock &&
|
|
||||||
lastBlock.flavour === 'affine:paragraph' &&
|
|
||||||
lastBlock.text?.length === 0
|
|
||||||
) {
|
|
||||||
const focusBlock = std.view.getBlock(lastBlock.id) ?? undefined;
|
|
||||||
std.command.exec(focusBlockEnd, {
|
|
||||||
focusBlock,
|
|
||||||
force: true,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std.command.exec(appendParagraphCommand);
|
|
||||||
}, [affineEditorContainerProxy.host?.std, page, readonly, shared]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
{...props}
|
|
||||||
data-testid={`editor-${page.id}`}
|
|
||||||
dir={enableEditorRTL ? 'rtl' : 'ltr'}
|
|
||||||
className={clsx(
|
|
||||||
`editor-wrapper ${mode}-mode`,
|
|
||||||
styles.docEditorRoot,
|
|
||||||
props.className
|
|
||||||
)}
|
|
||||||
data-affine-editor-container
|
|
||||||
style={props.style}
|
|
||||||
ref={rootRef}
|
|
||||||
>
|
|
||||||
{mode === 'page' ? (
|
|
||||||
<BlocksuiteDocEditor
|
|
||||||
shared={shared}
|
|
||||||
page={page}
|
|
||||||
ref={docRef}
|
|
||||||
readonly={readonly}
|
|
||||||
titleRef={docTitleRef}
|
|
||||||
onClickBlank={handleClickPageModeBlank}
|
|
||||||
defaultOpenProperty={defaultOpenProperty}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<BlocksuiteEdgelessEditor
|
|
||||||
shared={shared}
|
|
||||||
page={page}
|
|
||||||
ref={edgelessRef}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
@@ -1,30 +1,50 @@
|
|||||||
import { useRefEffect } from '@affine/component';
|
|
||||||
import { EditorLoading } from '@affine/component/page-detail-skeleton';
|
import { EditorLoading } from '@affine/component/page-detail-skeleton';
|
||||||
|
import type {
|
||||||
|
EdgelessEditor,
|
||||||
|
PageEditor,
|
||||||
|
} from '@affine/core/blocksuite/editors';
|
||||||
import { ServerService } from '@affine/core/modules/cloud';
|
import { ServerService } from '@affine/core/modules/cloud';
|
||||||
import {
|
import {
|
||||||
EditorSettingService,
|
EditorSettingService,
|
||||||
fontStyleOptions,
|
fontStyleOptions,
|
||||||
} from '@affine/core/modules/editor-setting';
|
} from '@affine/core/modules/editor-setting';
|
||||||
|
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||||
import {
|
import {
|
||||||
customImageProxyMiddleware,
|
customImageProxyMiddleware,
|
||||||
ImageProxyService,
|
ImageProxyService,
|
||||||
} from '@blocksuite/affine/blocks/image';
|
} from '@blocksuite/affine/blocks/image';
|
||||||
|
import { appendParagraphCommand } from '@blocksuite/affine/blocks/paragraph';
|
||||||
|
import type { DocTitle } from '@blocksuite/affine/fragments/doc-title';
|
||||||
import { DisposableGroup } from '@blocksuite/affine/global/disposable';
|
import { DisposableGroup } from '@blocksuite/affine/global/disposable';
|
||||||
import type { DocMode } from '@blocksuite/affine/model';
|
import type { DocMode, RootBlockModel } from '@blocksuite/affine/model';
|
||||||
|
import { focusBlockEnd } from '@blocksuite/affine/shared/commands';
|
||||||
import { LinkPreviewerService } from '@blocksuite/affine/shared/services';
|
import { LinkPreviewerService } from '@blocksuite/affine/shared/services';
|
||||||
|
import { getLastNoteBlock } from '@blocksuite/affine/shared/utils';
|
||||||
|
import type { BlockStdScope, EditorHost } from '@blocksuite/affine/std';
|
||||||
import type { Store } from '@blocksuite/affine/store';
|
import type { Store } from '@blocksuite/affine/store';
|
||||||
import { Slot } from '@radix-ui/react-slot';
|
import { Slot } from '@radix-ui/react-slot';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import { cssVar } from '@toeverything/theme';
|
import { cssVar } from '@toeverything/theme';
|
||||||
|
import clsx from 'clsx';
|
||||||
import type { CSSProperties, HTMLAttributes } from 'react';
|
import type { CSSProperties, HTMLAttributes } from 'react';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
import type { DefaultOpenProperty } from '../../components/doc-properties';
|
import type { DefaultOpenProperty } from '../../components/doc-properties';
|
||||||
import {
|
import { BlocksuiteDocEditor, BlocksuiteEdgelessEditor } from './lit-adaper';
|
||||||
type AffineEditorContainer,
|
|
||||||
BlocksuiteEditorContainer,
|
|
||||||
} from './blocksuite-editor-container';
|
|
||||||
import { NoPageRootError } from './no-page-error';
|
import { NoPageRootError } from './no-page-error';
|
||||||
|
import * as styles from './styles.css';
|
||||||
|
|
||||||
|
export interface AffineEditorContainer extends HTMLElement {
|
||||||
|
page: Store;
|
||||||
|
doc: Store;
|
||||||
|
docTitle: DocTitle;
|
||||||
|
host?: EditorHost;
|
||||||
|
model: RootBlockModel | null;
|
||||||
|
updateComplete: Promise<boolean>;
|
||||||
|
mode: DocMode;
|
||||||
|
origin: HTMLDivElement;
|
||||||
|
std: BlockStdScope;
|
||||||
|
}
|
||||||
|
|
||||||
export interface EditorProps extends HTMLAttributes<HTMLDivElement> {
|
export interface EditorProps extends HTMLAttributes<HTMLDivElement> {
|
||||||
page: Store;
|
page: Store;
|
||||||
@@ -47,6 +67,113 @@ const BlockSuiteEditorImpl = ({
|
|||||||
defaultOpenProperty,
|
defaultOpenProperty,
|
||||||
...props
|
...props
|
||||||
}: EditorProps) => {
|
}: EditorProps) => {
|
||||||
|
const rootRef = useRef<HTMLDivElement>(null);
|
||||||
|
const docRef = useRef<PageEditor>(null);
|
||||||
|
const docTitleRef = useRef<DocTitle>(null);
|
||||||
|
const edgelessRef = useRef<EdgelessEditor>(null);
|
||||||
|
const featureFlags = useService(FeatureFlagService).flags;
|
||||||
|
const enableEditorRTL = useLiveData(featureFlags.enable_editor_rtl.$);
|
||||||
|
const editorSetting = useService(EditorSettingService).editorSetting;
|
||||||
|
const server = useService(ServerService).server;
|
||||||
|
|
||||||
|
const { enableMiddleClickPaste } = useLiveData(
|
||||||
|
editorSetting.settings$.selector(s => ({
|
||||||
|
enableMiddleClickPaste: s.enableMiddleClickPaste,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mimic an AffineEditorContainer using proxy
|
||||||
|
*/
|
||||||
|
const affineEditorContainerProxy = useMemo(() => {
|
||||||
|
const api = {
|
||||||
|
get page() {
|
||||||
|
return page;
|
||||||
|
},
|
||||||
|
get doc() {
|
||||||
|
return page;
|
||||||
|
},
|
||||||
|
get docTitle() {
|
||||||
|
return docTitleRef.current;
|
||||||
|
},
|
||||||
|
get host() {
|
||||||
|
return (
|
||||||
|
(mode === 'page'
|
||||||
|
? docRef.current?.host
|
||||||
|
: edgelessRef.current?.host) ?? null
|
||||||
|
);
|
||||||
|
},
|
||||||
|
get model() {
|
||||||
|
return page.root as any;
|
||||||
|
},
|
||||||
|
get updateComplete() {
|
||||||
|
return mode === 'page'
|
||||||
|
? docRef.current?.updateComplete
|
||||||
|
: edgelessRef.current?.updateComplete;
|
||||||
|
},
|
||||||
|
get mode() {
|
||||||
|
return mode;
|
||||||
|
},
|
||||||
|
get origin() {
|
||||||
|
return rootRef.current;
|
||||||
|
},
|
||||||
|
get std() {
|
||||||
|
return mode === 'page' ? docRef.current?.std : edgelessRef.current?.std;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const proxy = new Proxy(api, {
|
||||||
|
has(_, prop) {
|
||||||
|
return (
|
||||||
|
Reflect.has(api, prop) ||
|
||||||
|
(rootRef.current ? Reflect.has(rootRef.current, prop) : false)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
get(_, prop) {
|
||||||
|
if (Reflect.has(api, prop)) {
|
||||||
|
return api[prop as keyof typeof api];
|
||||||
|
}
|
||||||
|
if (rootRef.current && Reflect.has(rootRef.current, prop)) {
|
||||||
|
const maybeFn = Reflect.get(rootRef.current, prop);
|
||||||
|
if (typeof maybeFn === 'function') {
|
||||||
|
return maybeFn.bind(rootRef.current);
|
||||||
|
} else {
|
||||||
|
return maybeFn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
}) as AffineEditorContainer;
|
||||||
|
|
||||||
|
return proxy;
|
||||||
|
}, [mode, page]);
|
||||||
|
|
||||||
|
const handleClickPageModeBlank = useCallback(() => {
|
||||||
|
if (shared || readonly || page.readonly) return;
|
||||||
|
const std = affineEditorContainerProxy.host?.std;
|
||||||
|
if (!std) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const note = getLastNoteBlock(page);
|
||||||
|
if (note) {
|
||||||
|
const lastBlock = note.lastChild();
|
||||||
|
if (
|
||||||
|
lastBlock &&
|
||||||
|
lastBlock.flavour === 'affine:paragraph' &&
|
||||||
|
lastBlock.text?.length === 0
|
||||||
|
) {
|
||||||
|
const focusBlock = std.view.getBlock(lastBlock.id) ?? undefined;
|
||||||
|
std.command.exec(focusBlockEnd, {
|
||||||
|
focusBlock,
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std.command.exec(appendParagraphCommand);
|
||||||
|
}, [affineEditorContainerProxy.host?.std, page, readonly, shared]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const disposable = page.slots.blockUpdated.subscribe(() => {
|
const disposable = page.slots.blockUpdated.subscribe(() => {
|
||||||
disposable.unsubscribe();
|
disposable.unsubscribe();
|
||||||
@@ -59,67 +186,104 @@ const BlockSuiteEditorImpl = ({
|
|||||||
};
|
};
|
||||||
}, [page]);
|
}, [page]);
|
||||||
|
|
||||||
const server = useService(ServerService).server;
|
useEffect(() => {
|
||||||
|
const editorContainer = rootRef.current;
|
||||||
const editorRef = useRefEffect(
|
if (editorContainer) {
|
||||||
(editor: AffineEditorContainer) => {
|
const handleMiddleClick = (e: MouseEvent) => {
|
||||||
globalThis.currentEditor = editor;
|
if (!enableMiddleClickPaste && e.button === 1) {
|
||||||
let canceled = false;
|
e.preventDefault();
|
||||||
const disposableGroup = new DisposableGroup();
|
}
|
||||||
|
|
||||||
// Invoke onLoad once the editor has been mounted to the DOM.
|
|
||||||
if (canceled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// provide image proxy endpoint to blocksuite
|
|
||||||
const imageProxyUrl = new URL(
|
|
||||||
BUILD_CONFIG.imageProxyUrl,
|
|
||||||
server.baseUrl
|
|
||||||
).toString();
|
|
||||||
|
|
||||||
const linkPreviewUrl = new URL(
|
|
||||||
BUILD_CONFIG.linkPreviewUrl,
|
|
||||||
server.baseUrl
|
|
||||||
).toString();
|
|
||||||
|
|
||||||
editor.std.clipboard.use(customImageProxyMiddleware(imageProxyUrl));
|
|
||||||
page.get(LinkPreviewerService).setEndpoint(linkPreviewUrl);
|
|
||||||
page.get(ImageProxyService).setImageProxyURL(imageProxyUrl);
|
|
||||||
|
|
||||||
editor.updateComplete
|
|
||||||
.then(() => {
|
|
||||||
if (onEditorReady) {
|
|
||||||
const dispose = onEditorReady(editor);
|
|
||||||
if (dispose) {
|
|
||||||
disposableGroup.add(dispose);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error updating editor', error);
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
canceled = true;
|
|
||||||
disposableGroup.dispose();
|
|
||||||
};
|
};
|
||||||
},
|
editorContainer.addEventListener('pointerup', handleMiddleClick, {
|
||||||
[onEditorReady, page, server]
|
capture: true,
|
||||||
);
|
});
|
||||||
|
editorContainer.addEventListener('auxclick', handleMiddleClick, {
|
||||||
|
capture: true,
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
editorContainer?.removeEventListener('pointerup', handleMiddleClick, {
|
||||||
|
capture: true,
|
||||||
|
});
|
||||||
|
editorContainer?.removeEventListener('auxclick', handleMiddleClick, {
|
||||||
|
capture: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}, [enableMiddleClickPaste]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const editor = affineEditorContainerProxy;
|
||||||
|
globalThis.currentEditor = editor;
|
||||||
|
const disposableGroup = new DisposableGroup();
|
||||||
|
let canceled = false;
|
||||||
|
|
||||||
|
// provide image proxy endpoint to blocksuite
|
||||||
|
const imageProxyUrl = new URL(
|
||||||
|
BUILD_CONFIG.imageProxyUrl,
|
||||||
|
server.baseUrl
|
||||||
|
).toString();
|
||||||
|
|
||||||
|
const linkPreviewUrl = new URL(
|
||||||
|
BUILD_CONFIG.linkPreviewUrl,
|
||||||
|
server.baseUrl
|
||||||
|
).toString();
|
||||||
|
|
||||||
|
editor.std.clipboard.use(customImageProxyMiddleware(imageProxyUrl));
|
||||||
|
page.get(LinkPreviewerService).setEndpoint(linkPreviewUrl);
|
||||||
|
page.get(ImageProxyService).setImageProxyURL(imageProxyUrl);
|
||||||
|
|
||||||
|
editor.updateComplete
|
||||||
|
.then(() => {
|
||||||
|
if (onEditorReady && !canceled) {
|
||||||
|
const dispose = onEditorReady(editor);
|
||||||
|
if (dispose) {
|
||||||
|
disposableGroup.add(dispose);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error updating editor', error);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
canceled = true;
|
||||||
|
disposableGroup.dispose();
|
||||||
|
};
|
||||||
|
}, [affineEditorContainerProxy, onEditorReady, page, server]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BlocksuiteEditorContainer
|
<div
|
||||||
{...props}
|
{...props}
|
||||||
mode={mode}
|
data-testid={`editor-${page.id}`}
|
||||||
page={page}
|
dir={enableEditorRTL ? 'rtl' : 'ltr'}
|
||||||
shared={shared}
|
className={clsx(
|
||||||
readonly={readonly}
|
`editor-wrapper ${mode}-mode`,
|
||||||
defaultOpenProperty={defaultOpenProperty}
|
styles.docEditorRoot,
|
||||||
ref={editorRef}
|
className
|
||||||
className={className}
|
)}
|
||||||
style={style}
|
style={style}
|
||||||
/>
|
data-affine-editor-container
|
||||||
|
ref={rootRef}
|
||||||
|
>
|
||||||
|
{mode === 'page' ? (
|
||||||
|
<BlocksuiteDocEditor
|
||||||
|
shared={shared}
|
||||||
|
page={page}
|
||||||
|
ref={docRef}
|
||||||
|
readonly={readonly}
|
||||||
|
titleRef={docTitleRef}
|
||||||
|
onClickBlank={handleClickPageModeBlank}
|
||||||
|
defaultOpenProperty={defaultOpenProperty}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<BlocksuiteEdgelessEditor
|
||||||
|
shared={shared}
|
||||||
|
page={page}
|
||||||
|
ref={edgelessRef}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -133,7 +297,6 @@ export const BlockSuiteEditor = (props: EditorProps) => {
|
|||||||
fontFamily: s.fontFamily,
|
fontFamily: s.fontFamily,
|
||||||
customFontFamily: s.customFontFamily,
|
customFontFamily: s.customFontFamily,
|
||||||
fullWidthLayout: s.fullWidthLayout,
|
fullWidthLayout: s.fullWidthLayout,
|
||||||
disableMiddleClickPaste: s.disableMiddleClickPaste,
|
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
const fontFamily = useMemo(() => {
|
const fontFamily = useMemo(() => {
|
||||||
@@ -170,24 +333,12 @@ export const BlockSuiteEditor = (props: EditorProps) => {
|
|||||||
};
|
};
|
||||||
}, [props.page]);
|
}, [props.page]);
|
||||||
|
|
||||||
const handleMouseDown = useCallback(
|
|
||||||
(e: React.MouseEvent<HTMLDivElement>) => {
|
|
||||||
if (settings.disableMiddleClickPaste && e.button === 1) {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[settings.disableMiddleClickPaste]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slot
|
<Slot style={{ '--affine-font-family': fontFamily } as CSSProperties}>
|
||||||
style={{ '--affine-font-family': fontFamily } as CSSProperties}
|
|
||||||
onMouseDown={handleMouseDown}
|
|
||||||
>
|
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<EditorLoading />
|
<EditorLoading />
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -10,4 +10,3 @@ registerAIEffects();
|
|||||||
registerTemplates();
|
registerTemplates();
|
||||||
|
|
||||||
export * from './blocksuite-editor';
|
export * from './blocksuite-editor';
|
||||||
export * from './blocksuite-editor-container';
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
pushGlobalLoadingEventAtom,
|
pushGlobalLoadingEventAtom,
|
||||||
resolveGlobalLoadingEventAtom,
|
resolveGlobalLoadingEventAtom,
|
||||||
} from '@affine/component/global-loading';
|
} from '@affine/component/global-loading';
|
||||||
|
import type { AffineEditorContainer } from '@affine/core/blocksuite/block-suite-editor/blocksuite-editor';
|
||||||
import { EditorService } from '@affine/core/modules/editor';
|
import { EditorService } from '@affine/core/modules/editor';
|
||||||
import { getAFFiNEWorkspaceSchema } from '@affine/core/modules/workspace/global-schema';
|
import { getAFFiNEWorkspaceSchema } from '@affine/core/modules/workspace/global-schema';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
@@ -29,7 +30,6 @@ import { useLiveData, useService } from '@toeverything/infra';
|
|||||||
import { useSetAtom } from 'jotai';
|
import { useSetAtom } from 'jotai';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
|
|
||||||
import type { AffineEditorContainer } from '../../../blocksuite/block-suite-editor/blocksuite-editor-container';
|
|
||||||
import { useAsyncCallback } from '../affine-async-hooks';
|
import { useAsyncCallback } from '../affine-async-hooks';
|
||||||
|
|
||||||
type ExportType = 'pdf' | 'html' | 'png' | 'markdown' | 'snapshot';
|
type ExportType = 'pdf' | 'html' | 'png' | 'markdown' | 'snapshot';
|
||||||
|
|||||||
@@ -493,10 +493,7 @@ const MiddleClickPasteSettings = () => {
|
|||||||
const settings = useLiveData(editorSettingService.editorSetting.settings$);
|
const settings = useLiveData(editorSettingService.editorSetting.settings$);
|
||||||
const onToggleMiddleClickPaste = useCallback(
|
const onToggleMiddleClickPaste = useCallback(
|
||||||
(checked: boolean) => {
|
(checked: boolean) => {
|
||||||
editorSettingService.editorSetting.set(
|
editorSettingService.editorSetting.set('enableMiddleClickPaste', checked);
|
||||||
'disableMiddleClickPaste',
|
|
||||||
checked
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
[editorSettingService.editorSetting]
|
[editorSettingService.editorSetting]
|
||||||
);
|
);
|
||||||
@@ -510,7 +507,7 @@ const MiddleClickPasteSettings = () => {
|
|||||||
]()}
|
]()}
|
||||||
>
|
>
|
||||||
<Switch
|
<Switch
|
||||||
checked={settings.disableMiddleClickPaste}
|
checked={settings.enableMiddleClickPaste}
|
||||||
onChange={onToggleMiddleClickPaste}
|
onChange={onToggleMiddleClickPaste}
|
||||||
/>
|
/>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const AffineEditorSettingSchema = z.object({
|
|||||||
])
|
])
|
||||||
.default('open-in-active-view'),
|
.default('open-in-active-view'),
|
||||||
// linux only:
|
// linux only:
|
||||||
disableMiddleClickPaste: z.boolean().default(false),
|
enableMiddleClickPaste: z.boolean().default(false),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const EditorSettingSchema = BSEditorSettingSchema.merge(
|
export const EditorSettingSchema = BSEditorSettingSchema.merge(
|
||||||
|
|||||||
@@ -5176,7 +5176,7 @@ export function useAFFiNEI18N(): {
|
|||||||
*/
|
*/
|
||||||
["com.affine.settings.editorSettings.general.middle-click-paste.title"](): string;
|
["com.affine.settings.editorSettings.general.middle-click-paste.title"](): string;
|
||||||
/**
|
/**
|
||||||
* `Disable default middle click paste behavior on Linux.`
|
* `Enable default middle click paste behavior on Linux.`
|
||||||
*/
|
*/
|
||||||
["com.affine.settings.editorSettings.general.middle-click-paste.description"](): string;
|
["com.affine.settings.editorSettings.general.middle-click-paste.description"](): string;
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1289,7 +1289,7 @@
|
|||||||
"com.affine.settings.editorSettings.general.spell-check.restart-hint": "Settings changed; please restart the app. <1>Restart</1>",
|
"com.affine.settings.editorSettings.general.spell-check.restart-hint": "Settings changed; please restart the app. <1>Restart</1>",
|
||||||
"com.affine.settings.editorSettings.page": "Page",
|
"com.affine.settings.editorSettings.page": "Page",
|
||||||
"com.affine.settings.editorSettings.general.middle-click-paste.title": "Middle click paste",
|
"com.affine.settings.editorSettings.general.middle-click-paste.title": "Middle click paste",
|
||||||
"com.affine.settings.editorSettings.general.middle-click-paste.description": "Disable default middle click paste behavior on Linux.",
|
"com.affine.settings.editorSettings.general.middle-click-paste.description": "Enable default middle click paste behavior on Linux.",
|
||||||
"com.affine.settings.editorSettings.page.display-bi-link.description": "Display bi-directional links on the doc.",
|
"com.affine.settings.editorSettings.page.display-bi-link.description": "Display bi-directional links on the doc.",
|
||||||
"com.affine.settings.editorSettings.page.display-bi-link.title": "Display bi-directional links",
|
"com.affine.settings.editorSettings.page.display-bi-link.title": "Display bi-directional links",
|
||||||
"com.affine.settings.editorSettings.page.display-doc-info.description": "Display document information on the doc.",
|
"com.affine.settings.editorSettings.page.display-doc-info.description": "Display document information on the doc.",
|
||||||
|
|||||||
Reference in New Issue
Block a user