refactor(core): image block use peek view workflow (#7329)

depends on https://github.com/toeverything/blocksuite/pull/7424
This commit is contained in:
pengx17
2024-06-26 07:49:25 +00:00
parent 092c639b0a
commit 7baa260e97
20 changed files with 340 additions and 305 deletions
+2 -2
View File
@@ -3,8 +3,8 @@
"private": true, "private": true,
"type": "module", "type": "module",
"devDependencies": { "devDependencies": {
"@blocksuite/global": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/global": "0.15.0-canary-202406260417-3b9fb16",
"@blocksuite/store": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/store": "0.15.0-canary-202406260417-3b9fb16",
"react": "18.3.1", "react": "18.3.1",
"react-dom": "18.3.1", "react-dom": "18.3.1",
"vitest": "1.6.0" "vitest": "1.6.0"
+5 -5
View File
@@ -13,9 +13,9 @@
"@affine/debug": "workspace:*", "@affine/debug": "workspace:*",
"@affine/env": "workspace:*", "@affine/env": "workspace:*",
"@affine/templates": "workspace:*", "@affine/templates": "workspace:*",
"@blocksuite/blocks": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/blocks": "0.15.0-canary-202406260417-3b9fb16",
"@blocksuite/global": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/global": "0.15.0-canary-202406260417-3b9fb16",
"@blocksuite/store": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/store": "0.15.0-canary-202406260417-3b9fb16",
"@datastructures-js/binary-search-tree": "^5.3.2", "@datastructures-js/binary-search-tree": "^5.3.2",
"foxact": "^0.2.33", "foxact": "^0.2.33",
"jotai": "^2.8.0", "jotai": "^2.8.0",
@@ -29,8 +29,8 @@
"devDependencies": { "devDependencies": {
"@affine-test/fixtures": "workspace:*", "@affine-test/fixtures": "workspace:*",
"@affine/templates": "workspace:*", "@affine/templates": "workspace:*",
"@blocksuite/block-std": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/block-std": "0.15.0-canary-202406260417-3b9fb16",
"@blocksuite/presets": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/presets": "0.15.0-canary-202406260417-3b9fb16",
"@testing-library/react": "^16.0.0", "@testing-library/react": "^16.0.0",
"async-call-rpc": "^6.4.0", "async-call-rpc": "^6.4.0",
"react": "^18.2.0", "react": "^18.2.0",
+5 -5
View File
@@ -75,12 +75,12 @@
"zod": "^3.22.4" "zod": "^3.22.4"
}, },
"devDependencies": { "devDependencies": {
"@blocksuite/block-std": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/block-std": "0.15.0-canary-202406260417-3b9fb16",
"@blocksuite/blocks": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/blocks": "0.15.0-canary-202406260417-3b9fb16",
"@blocksuite/global": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/global": "0.15.0-canary-202406260417-3b9fb16",
"@blocksuite/icons": "2.1.58", "@blocksuite/icons": "2.1.58",
"@blocksuite/presets": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/presets": "0.15.0-canary-202406260417-3b9fb16",
"@blocksuite/store": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/store": "0.15.0-canary-202406260417-3b9fb16",
"@storybook/addon-actions": "^7.6.17", "@storybook/addon-actions": "^7.6.17",
"@storybook/addon-essentials": "^7.6.17", "@storybook/addon-essentials": "^7.6.17",
"@storybook/addon-interactions": "^7.6.17", "@storybook/addon-interactions": "^7.6.17",
+6 -6
View File
@@ -19,13 +19,13 @@
"@affine/graphql": "workspace:*", "@affine/graphql": "workspace:*",
"@affine/i18n": "workspace:*", "@affine/i18n": "workspace:*",
"@affine/templates": "workspace:*", "@affine/templates": "workspace:*",
"@blocksuite/block-std": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/block-std": "0.15.0-canary-202406260417-3b9fb16",
"@blocksuite/blocks": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/blocks": "0.15.0-canary-202406260417-3b9fb16",
"@blocksuite/global": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/global": "0.15.0-canary-202406260417-3b9fb16",
"@blocksuite/icons": "2.1.58", "@blocksuite/icons": "2.1.58",
"@blocksuite/inline": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/inline": "0.15.0-canary-202406260417-3b9fb16",
"@blocksuite/presets": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/presets": "0.15.0-canary-202406260417-3b9fb16",
"@blocksuite/store": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/store": "0.15.0-canary-202406260417-3b9fb16",
"@dnd-kit/core": "^6.1.0", "@dnd-kit/core": "^6.1.0",
"@dnd-kit/modifiers": "^7.0.0", "@dnd-kit/modifiers": "^7.0.0",
"@dnd-kit/sortable": "^8.0.0", "@dnd-kit/sortable": "^8.0.0",
@@ -3,6 +3,7 @@ import {
AffineReference, AffineReference,
type EmbedLinkedDocModel, type EmbedLinkedDocModel,
type EmbedSyncedDocModel, type EmbedSyncedDocModel,
type ImageBlockModel,
type SurfaceRefBlockComponent, type SurfaceRefBlockComponent,
type SurfaceRefBlockModel, type SurfaceRefBlockModel,
} from '@blocksuite/blocks'; } from '@blocksuite/blocks';
@@ -12,6 +13,7 @@ import type { TemplateResult } from 'lit';
import { firstValueFrom, map, race } from 'rxjs'; import { firstValueFrom, map, race } from 'rxjs';
import { resolveLinkToDoc } from '../../navigation'; import { resolveLinkToDoc } from '../../navigation';
import type { WorkbenchService } from '../../workbench';
export type PeekViewTarget = export type PeekViewTarget =
| HTMLElement | HTMLElement
@@ -20,17 +22,28 @@ export type PeekViewTarget =
| HTMLAnchorElement | HTMLAnchorElement
| { docId: string; blockId?: string }; | { docId: string; blockId?: string };
export type DocPeekViewInfo = { export interface DocPeekViewInfo {
type: 'doc';
docId: string; docId: string;
blockId?: string; blockId?: string;
mode?: DocMode; mode?: DocMode;
xywh?: `[${number},${number},${number},${number}]`; xywh?: `[${number},${number},${number},${number}]`;
}
export type ImagePeekViewInfo = {
type: 'image';
docId: string;
blockId: string;
};
export type CustomTemplatePeekViewInfo = {
type: 'template';
template: TemplateResult;
}; };
export type ActivePeekView = { export type ActivePeekView = {
target: PeekViewTarget; target: PeekViewTarget;
info?: DocPeekViewInfo; info: DocPeekViewInfo | ImagePeekViewInfo | CustomTemplatePeekViewInfo;
template?: TemplateResult;
}; };
const EMBED_DOC_FLAVOURS = [ const EMBED_DOC_FLAVOURS = [
@@ -44,6 +57,12 @@ const isEmbedDocModel = (
return EMBED_DOC_FLAVOURS.includes(blockModel.flavour); return EMBED_DOC_FLAVOURS.includes(blockModel.flavour);
}; };
const isImageBlockModel = (
blockModel: BlockModel
): blockModel is ImageBlockModel => {
return blockModel.flavour === 'affine:image';
};
const isSurfaceRefModel = ( const isSurfaceRefModel = (
blockModel: BlockModel blockModel: BlockModel
): blockModel is SurfaceRefBlockModel => { ): blockModel is SurfaceRefBlockModel => {
@@ -51,12 +70,20 @@ const isSurfaceRefModel = (
}; };
function resolvePeekInfoFromPeekTarget( function resolvePeekInfoFromPeekTarget(
peekTarget?: PeekViewTarget peekTarget: PeekViewTarget,
): DocPeekViewInfo | undefined { template?: TemplateResult
if (!peekTarget) return; ): ActivePeekView['info'] | undefined {
if (template) {
return {
type: 'template',
template,
};
}
if (peekTarget instanceof AffineReference) { if (peekTarget instanceof AffineReference) {
if (peekTarget.refMeta) { if (peekTarget.refMeta) {
return { return {
type: 'doc',
docId: peekTarget.refMeta.id, docId: peekTarget.refMeta.id,
}; };
} }
@@ -64,6 +91,7 @@ function resolvePeekInfoFromPeekTarget(
const blockModel = peekTarget.model; const blockModel = peekTarget.model;
if (isEmbedDocModel(blockModel)) { if (isEmbedDocModel(blockModel)) {
return { return {
type: 'doc',
docId: blockModel.pageId, docId: blockModel.pageId,
}; };
} else if (isSurfaceRefModel(blockModel)) { } else if (isSurfaceRefModel(blockModel)) {
@@ -73,22 +101,31 @@ function resolvePeekInfoFromPeekTarget(
const docId = const docId =
'doc' in refModel ? refModel.doc.id : refModel.surface.doc.id; 'doc' in refModel ? refModel.doc.id : refModel.surface.doc.id;
return { return {
type: 'doc',
docId, docId,
mode: 'edgeless', mode: 'edgeless',
xywh: refModel.xywh, xywh: refModel.xywh,
}; };
} }
} else if (isImageBlockModel(blockModel)) {
return {
type: 'image',
docId: blockModel.doc.id,
blockId: blockModel.id,
};
} }
} else if (peekTarget instanceof HTMLAnchorElement) { } else if (peekTarget instanceof HTMLAnchorElement) {
const maybeDoc = resolveLinkToDoc(peekTarget.href); const maybeDoc = resolveLinkToDoc(peekTarget.href);
if (maybeDoc) { if (maybeDoc) {
return { return {
type: 'doc',
docId: maybeDoc.docId, docId: maybeDoc.docId,
blockId: maybeDoc.blockId, blockId: maybeDoc.blockId,
}; };
} }
} else if ('docId' in peekTarget) { } else if ('docId' in peekTarget) {
return { return {
type: 'doc',
docId: peekTarget.docId, docId: peekTarget.docId,
blockId: peekTarget.blockId, blockId: peekTarget.blockId,
}; };
@@ -100,6 +137,10 @@ export class PeekViewEntity extends Entity {
private readonly _active$ = new LiveData<ActivePeekView | null>(null); private readonly _active$ = new LiveData<ActivePeekView | null>(null);
private readonly _show$ = new LiveData<boolean>(false); private readonly _show$ = new LiveData<boolean>(false);
constructor(private readonly workbenchService: WorkbenchService) {
super();
}
active$ = this._active$.distinctUntilChanged(); active$ = this._active$.distinctUntilChanged();
show$ = this._show$ show$ = this._show$
.map(show => show && this._active$.value !== null) .map(show => show && this._active$.value !== null)
@@ -110,11 +151,20 @@ export class PeekViewEntity extends Entity {
target: ActivePeekView['target'], target: ActivePeekView['target'],
template?: TemplateResult template?: TemplateResult
) => { ) => {
const resolvedInfo = resolvePeekInfoFromPeekTarget(target); const resolvedInfo = resolvePeekInfoFromPeekTarget(target, template);
if (!resolvedInfo && !template) { if (!resolvedInfo) {
return; return;
} }
this._active$.next({ target, info: resolvedInfo, template });
const active = this._active$.value;
// if there is an active peek view and it is a doc peek view, we will navigate it first
if (active?.info.type === 'doc' && this.show$.value) {
// TODO(@pengx17): scroll to the viewing position?
this.workbenchService.workbench.openPage(active.info.docId);
}
this._active$.next({ target, info: resolvedInfo });
this._show$.next(true); this._show$.next(true);
return firstValueFrom(race(this._active$, this.show$).pipe(map(() => {}))); return firstValueFrom(race(this._active$, this.show$).pipe(map(() => {})));
}; };
@@ -1,10 +1,14 @@
import type { Framework } from '@toeverything/infra'; import { type Framework, WorkspaceScope } from '@toeverything/infra';
import { WorkbenchService } from '../workbench';
import { PeekViewEntity } from './entities/peek-view'; import { PeekViewEntity } from './entities/peek-view';
import { PeekViewService } from './services/peek-view'; import { PeekViewService } from './services/peek-view';
export function configurePeekViewModule(framework: Framework) { export function configurePeekViewModule(framework: Framework) {
framework.service(PeekViewService).entity(PeekViewEntity); framework
.scope(WorkspaceScope)
.service(PeekViewService)
.entity(PeekViewEntity, [WorkbenchService]);
} }
export { PeekViewEntity, PeekViewService }; export { PeekViewEntity, PeekViewService };
@@ -3,7 +3,6 @@ import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton';
import { AIProvider } from '@affine/core/blocksuite/presets/ai'; import { AIProvider } from '@affine/core/blocksuite/presets/ai';
import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary'; import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary';
import { BlockSuiteEditor } from '@affine/core/components/blocksuite/block-suite-editor'; import { BlockSuiteEditor } from '@affine/core/components/blocksuite/block-suite-editor';
import { ImagePreviewModal } from '@affine/core/components/image-preview';
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper'; import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
import { PageNotFound } from '@affine/core/pages/404'; import { PageNotFound } from '@affine/core/pages/404';
import { Bound, type EdgelessRootService } from '@blocksuite/blocks'; import { Bound, type EdgelessRootService } from '@blocksuite/blocks';
@@ -14,10 +13,10 @@ import { DocsService, FrameworkScope, useService } from '@toeverything/infra';
import clsx from 'clsx'; import clsx from 'clsx';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { WorkbenchService } from '../../workbench'; import { WorkbenchService } from '../../../workbench';
import { PeekViewService } from '../services/peek-view'; import { PeekViewService } from '../../services/peek-view';
import { useDoc } from '../utils';
import * as styles from './doc-peek-view.css'; import * as styles from './doc-peek-view.css';
import { useDoc } from './utils';
function fitViewport( function fitViewport(
editor: AffineEditorContainer, editor: AffineEditorContainer,
@@ -160,13 +159,6 @@ export function DocPeekPreview({
defaultSelectedBlockId={blockId} defaultSelectedBlockId={blockId}
page={doc.blockSuiteDoc} page={doc.blockSuiteDoc}
/> />
{editor?.host ? (
<ImagePreviewModal
pageId={doc.id}
docCollection={doc.blockSuiteDoc.collection}
host={editor.host}
/>
) : null}
</FrameworkScope> </FrameworkScope>
</Scrollable.Viewport> </Scrollable.Viewport>
<Scrollable.Scrollbar /> <Scrollable.Scrollbar />
@@ -0,0 +1 @@
export { DocPeekPreview } from './doc-peek-view';
@@ -2,7 +2,6 @@ import { toast } from '@affine/component';
import { Button, IconButton } from '@affine/component/ui/button'; import { Button, IconButton } from '@affine/component/ui/button';
import { Tooltip } from '@affine/component/ui/tooltip'; import { Tooltip } from '@affine/component/ui/tooltip';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { PeekViewModalContainer } from '@affine/core/modules/peek-view/view/modal-container';
import type { ImageBlockModel } from '@blocksuite/blocks'; import type { ImageBlockModel } from '@blocksuite/blocks';
import { assertExists } from '@blocksuite/global/utils'; import { assertExists } from '@blocksuite/global/utils';
import { import {
@@ -16,7 +15,8 @@ import {
PlusIcon, PlusIcon,
ViewBarIcon, ViewBarIcon,
} from '@blocksuite/icons/rc'; } from '@blocksuite/icons/rc';
import type { BlockModel, DocCollection } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
import { useService } from '@toeverything/infra';
import clsx from 'clsx'; import clsx from 'clsx';
import { useErrorBoundary } from 'foxact/use-error-boundary'; import { useErrorBoundary } from 'foxact/use-error-boundary';
import type { PropsWithChildren, ReactElement } from 'react'; import type { PropsWithChildren, ReactElement } from 'react';
@@ -24,7 +24,6 @@ import {
Suspense, Suspense,
useCallback, useCallback,
useEffect, useEffect,
useLayoutEffect,
useMemo, useMemo,
useRef, useRef,
useState, useState,
@@ -33,6 +32,8 @@ import type { FallbackProps } from 'react-error-boundary';
import { ErrorBoundary } from 'react-error-boundary'; import { ErrorBoundary } from 'react-error-boundary';
import useSWR from 'swr'; import useSWR from 'swr';
import { PeekViewService } from '../../services/peek-view';
import { useDoc } from '../utils';
import { useZoomControls } from './hooks/use-zoom'; import { useZoomControls } from './hooks/use-zoom';
import * as styles from './index.css'; import * as styles from './index.css';
@@ -120,9 +121,8 @@ async function saveBufferToFile(url: string, filename: string) {
} }
export type ImagePreviewModalProps = { export type ImagePreviewModalProps = {
docCollection: DocCollection; docId: string;
pageId: string; blockId: string;
host: HTMLElement;
}; };
const ButtonWithTooltip = ({ const ButtonWithTooltip = ({
@@ -149,24 +149,25 @@ const ButtonWithTooltip = ({
} }
}; };
const ImagePreviewModalImpl = ( const ImagePreviewModalImpl = ({
props: ImagePreviewModalProps & { docId,
blockId: string; blockId,
onBlockIdChange: (blockId: string | null) => void; onBlockIdChange,
onClose: () => void; onClose,
animating: boolean; }: ImagePreviewModalProps & {
} onBlockIdChange: (blockId: string) => void;
): ReactElement | null => { onClose: () => void;
const page = useMemo(() => { }): ReactElement | null => {
return props.docCollection.getDoc(props.pageId); const { doc, workspace } = useDoc(docId);
}, [props.docCollection, props.pageId]); const blocksuiteDoc = doc?.blockSuiteDoc;
const docCollection = workspace.docCollection;
const blockModel = useMemo(() => { const blockModel = useMemo(() => {
const block = page?.getBlock(props.blockId); const block = blocksuiteDoc?.getBlock(blockId);
if (!block) { if (!block) {
return null; return null;
} }
return block.model as ImageBlockModel; return block.model as ImageBlockModel;
}, [page, props.blockId]); }, [blockId, blocksuiteDoc]);
const caption = useMemo(() => { const caption = useMemo(() => {
return blockModel?.caption ?? ''; return blockModel?.caption ?? '';
}, [blockModel?.caption]); }, [blockModel?.caption]);
@@ -188,8 +189,7 @@ const ImagePreviewModalImpl = (
const goto = useCallback( const goto = useCallback(
(index: number) => { (index: number) => {
const workspace = props.docCollection; const page = docCollection.getDoc(docId);
const page = workspace.getDoc(props.pageId);
assertExists(page); assertExists(page);
const block = blocks[index]; const block = blocks[index];
@@ -197,18 +197,17 @@ const ImagePreviewModalImpl = (
if (!block) return; if (!block) return;
setCursor(index); setCursor(index);
props.onBlockIdChange(block.id); onBlockIdChange(block.id);
resetZoom(); resetZoom();
}, },
[props, blocks, resetZoom] [docCollection, docId, blocks, onBlockIdChange, resetZoom]
); );
const deleteHandler = useCallback( const deleteHandler = useCallback(
(index: number) => { (index: number) => {
const { pageId, docCollection: workspace, onClose } = props; if (!blocksuiteDoc) {
return;
const page = workspace.getDoc(pageId); }
assertExists(page);
let block = blocks[index]; let block = blocks[index];
@@ -216,7 +215,7 @@ const ImagePreviewModalImpl = (
const newBlocks = blocks.toSpliced(index, 1); const newBlocks = blocks.toSpliced(index, 1);
setBlocks(newBlocks); setBlocks(newBlocks);
page.deleteBlock(block); blocksuiteDoc.deleteBlock(block);
// next // next
block = newBlocks[index]; block = newBlocks[index];
@@ -234,11 +233,11 @@ const ImagePreviewModalImpl = (
setCursor(index); setCursor(index);
} }
props.onBlockIdChange(block.id); onBlockIdChange(block.id);
resetZoom(); resetZoom();
}, },
[props, blocks, setBlocks, setCursor, resetZoom] [blocksuiteDoc, blocks, onBlockIdChange, resetZoom, onClose]
); );
const downloadHandler = useAsyncCallback(async () => { const downloadHandler = useAsyncCallback(async () => {
@@ -256,66 +255,58 @@ const ImagePreviewModalImpl = (
}, []); }, []);
useEffect(() => { useEffect(() => {
const page = props.docCollection.getDoc(props.pageId); if (!blockModel || !blocksuiteDoc) {
assertExists(page);
const block = page.getBlock(props.blockId);
if (!block) {
return; return;
} }
const blockModel = block.model as ImageBlockModel;
const prevs = page.getPrevs(blockModel).filter(filterImageBlock); const prevs = blocksuiteDoc.getPrevs(blockModel).filter(filterImageBlock);
const nexts = page.getNexts(blockModel).filter(filterImageBlock); const nexts = blocksuiteDoc.getNexts(blockModel).filter(filterImageBlock);
const blocks = [...prevs, blockModel, ...nexts]; const blocks = [...prevs, blockModel, ...nexts];
setBlocks(blocks); setBlocks(blocks);
setCursor(blocks.length ? prevs.length : 0); setCursor(blocks.length ? prevs.length : 0);
}, [props.blockId, props.pageId, props.docCollection, setBlocks]); }, [setBlocks, blockModel, blocksuiteDoc]);
const { data, error } = useSWR( const { data, error } = useSWR(['workspace', 'image', docId, blockId], {
['workspace', 'image', props.pageId, props.blockId], fetcher: ([_, __, pageId, blockId]) => {
{ const page = docCollection.getDoc(pageId);
fetcher: ([_, __, pageId, blockId]) => { assertExists(page);
const page = props.docCollection.getDoc(pageId);
assertExists(page);
const block = page.getBlock(blockId); const block = page.getBlock(blockId);
if (!block) { if (!block) {
return null; return null;
} }
const blockModel = block.model as ImageBlockModel; const blockModel = block.model as ImageBlockModel;
return props.docCollection.blobSync.get(blockModel.sourceId as string); return docCollection.blobSync.get(blockModel.sourceId as string);
}, },
suspense: true, suspense: true,
} });
);
useEffect(() => { useEffect(() => {
const handleKeyUp = (event: KeyboardEvent) => { const handleKeyUp = (event: KeyboardEvent) => {
if (!page || !blockModel) { if (!blocksuiteDoc || !blockModel) {
return; return;
} }
if (event.key === 'ArrowLeft') { if (event.key === 'ArrowLeft') {
const prevBlock = page const prevBlock = blocksuiteDoc
.getPrevs(blockModel) .getPrevs(blockModel)
.findLast( .findLast(
(block): block is ImageBlockModel => (block): block is ImageBlockModel =>
block.flavour === 'affine:image' block.flavour === 'affine:image'
); );
if (prevBlock) { if (prevBlock) {
props.onBlockIdChange(prevBlock.id); onBlockIdChange(prevBlock.id);
} }
} else if (event.key === 'ArrowRight') { } else if (event.key === 'ArrowRight') {
const nextBlock = page const nextBlock = blocksuiteDoc
.getNexts(blockModel) .getNexts(blockModel)
.find( .find(
(block): block is ImageBlockModel => (block): block is ImageBlockModel =>
block.flavour === 'affine:image' block.flavour === 'affine:image'
); );
if (nextBlock) { if (nextBlock) {
props.onBlockIdChange(nextBlock.id); onBlockIdChange(nextBlock.id);
} }
} else { } else {
return; return;
@@ -328,7 +319,7 @@ const ImagePreviewModalImpl = (
return () => { return () => {
document.removeEventListener('keyup', handleKeyUp); document.removeEventListener('keyup', handleKeyUp);
}; };
}, [blockModel, page, props]); }, [blockModel, blocksuiteDoc, onBlockIdChange]);
useErrorBoundary(error); useErrorBoundary(error);
@@ -351,8 +342,11 @@ const ImagePreviewModalImpl = (
return null; return null;
} }
return ( return (
<div className={styles.imagePreviewModalStyle}> <div
<div className={styles.imagePreviewTrap} onClick={props.onClose} /> data-testid="image-preview-modal"
className={styles.imagePreviewModalStyle}
>
<div className={styles.imagePreviewTrap} onClick={onClose} />
<div className={styles.imagePreviewModalContainerStyle}> <div className={styles.imagePreviewModalContainerStyle}>
<div <div
className={clsx('zoom-area', { 'zoomed-bigger': isZoomedBigger })} className={clsx('zoom-area', { 'zoomed-bigger': isZoomedBigger })}
@@ -360,7 +354,7 @@ const ImagePreviewModalImpl = (
> >
<div className={styles.imagePreviewModalCenterStyle}> <div className={styles.imagePreviewModalCenterStyle}>
<img <img
data-blob-id={props.blockId} data-blob-id={blockId}
data-testid="image-content" data-testid="image-content"
src={url} src={url}
alt={caption} alt={caption}
@@ -397,7 +391,7 @@ const ImagePreviewModalImpl = (
data-testid="previous-image-button" data-testid="previous-image-button"
tooltip="Previous" tooltip="Previous"
icon={<ArrowLeftSmallIcon />} icon={<ArrowLeftSmallIcon />}
disabled={props.animating || cursor < 1} disabled={cursor < 1}
onClick={() => goto(cursor - 1)} onClick={() => goto(cursor - 1)}
/> />
<div className={styles.cursorStyle}> <div className={styles.cursorStyle}>
@@ -407,7 +401,7 @@ const ImagePreviewModalImpl = (
data-testid="next-image-button" data-testid="next-image-button"
tooltip="Next" tooltip="Next"
icon={<ArrowRightSmallIcon />} icon={<ArrowRightSmallIcon />}
disabled={props.animating || cursor + 1 === blocks.length} disabled={cursor + 1 === blocks.length}
onClick={() => goto(cursor + 1)} onClick={() => goto(cursor + 1)}
/> />
<div className={styles.dividerStyle}></div> <div className={styles.dividerStyle}></div>
@@ -415,21 +409,18 @@ const ImagePreviewModalImpl = (
data-testid="fit-to-screen-button" data-testid="fit-to-screen-button"
tooltip="Fit to screen" tooltip="Fit to screen"
icon={<ViewBarIcon />} icon={<ViewBarIcon />}
disabled={props.animating}
onClick={() => resetZoom()} onClick={() => resetZoom()}
/> />
<ButtonWithTooltip <ButtonWithTooltip
data-testid="zoom-out-button" data-testid="zoom-out-button"
tooltip="Zoom out" tooltip="Zoom out"
icon={<MinusIcon />} icon={<MinusIcon />}
disabled={props.animating}
onClick={zoomOut} onClick={zoomOut}
/> />
<ButtonWithTooltip <ButtonWithTooltip
data-testid="reset-scale-button" data-testid="reset-scale-button"
tooltip="Reset scale" tooltip="Reset scale"
onClick={resetScale} onClick={resetScale}
disabled={props.animating}
> >
{`${(currentScale * 100).toFixed(0)}%`} {`${(currentScale * 100).toFixed(0)}%`}
</ButtonWithTooltip> </ButtonWithTooltip>
@@ -438,7 +429,6 @@ const ImagePreviewModalImpl = (
data-testid="zoom-in-button" data-testid="zoom-in-button"
tooltip="Zoom in" tooltip="Zoom in"
icon={<PlusIcon />} icon={<PlusIcon />}
disabled={props.animating}
onClick={zoomIn} onClick={zoomIn}
/> />
<div className={styles.dividerStyle}></div> <div className={styles.dividerStyle}></div>
@@ -446,14 +436,12 @@ const ImagePreviewModalImpl = (
data-testid="download-button" data-testid="download-button"
tooltip="Download" tooltip="Download"
icon={<DownloadIcon />} icon={<DownloadIcon />}
disabled={props.animating}
onClick={downloadHandler} onClick={downloadHandler}
/> />
<ButtonWithTooltip <ButtonWithTooltip
data-testid="copy-to-clipboard-button" data-testid="copy-to-clipboard-button"
tooltip="Copy to clipboard" tooltip="Copy to clipboard"
icon={<CopyIcon />} icon={<CopyIcon />}
disabled={props.animating}
onClick={copyHandler} onClick={copyHandler}
/> />
<div className={styles.dividerStyle}></div> <div className={styles.dividerStyle}></div>
@@ -461,7 +449,7 @@ const ImagePreviewModalImpl = (
data-testid="delete-button" data-testid="delete-button"
tooltip="Delete" tooltip="Delete"
icon={<DeleteIcon />} icon={<DeleteIcon />}
disabled={props.animating || blocks.length === 0} disabled={blocks.length === 0}
onClick={() => deleteHandler(cursor)} onClick={() => deleteHandler(cursor)}
/> />
</div> </div>
@@ -483,67 +471,38 @@ export const ImagePreviewErrorBoundary = (
); );
}; };
export const ImagePreviewModal = ( export const ImagePreviewPeekView = (
props: ImagePreviewModalProps props: ImagePreviewModalProps
): ReactElement | null => { ): ReactElement | null => {
const [show, setShow] = useState(false); const [blockId, setBlockId] = useState<string | null>(props.blockId);
const [blockId, setBlockId] = useState<string | null>(null); const peekView = useService(PeekViewService).peekView;
const isOpen = show && !!blockId; const onClose = peekView.close;
const buttonRef = useRef<HTMLButtonElement | null>(null);
// todo: refactor this to use peek view service useEffect(() => {
useLayoutEffect(() => { setBlockId(props.blockId);
const handleDblClick = (event: MouseEvent) => { }, [props.blockId]);
const target = event.target as HTMLElement | null;
if (target?.tagName === 'IMG') {
const imageBlock = target.closest('affine-image');
if (imageBlock) {
const blockId = imageBlock.dataset.blockId;
if (!blockId) return;
setBlockId(blockId);
setShow(true);
}
}
};
props.host.addEventListener('dblclick', handleDblClick);
return () => {
props.host.removeEventListener('dblclick', handleDblClick);
};
}, [props.host]);
const [animating, setAnimating] = useState(false);
return ( return (
<PeekViewModalContainer <ImagePreviewErrorBoundary>
padding={false} <Suspense>
onOpenChange={setShow} {blockId ? (
open={isOpen} <ImagePreviewModalImpl
animation="fade" {...props}
onAnimationStart={() => setAnimating(true)} onClose={onClose}
onAnimateEnd={() => setAnimating(false)} blockId={blockId}
testId="image-preview-modal" onBlockIdChange={setBlockId}
> />
<ImagePreviewErrorBoundary> ) : null}
<Suspense> <button
{blockId ? ( ref={buttonRef}
<ImagePreviewModalImpl data-testid="image-preview-close-button"
{...props} onClick={onClose}
animating={animating} className={styles.imagePreviewModalCloseButtonStyle}
blockId={blockId} >
onBlockIdChange={setBlockId} <CloseIcon />
onClose={() => setShow(false)} </button>
/> </Suspense>
) : null} </ImagePreviewErrorBoundary>
<button
data-testid="image-preview-close-button"
onClick={() => {
setShow(false);
}}
className={styles.imagePreviewModalCloseButtonStyle}
>
<CloseIcon />
</button>
</Suspense>
</ImagePreviewErrorBoundary>
</PeekViewModalContainer>
); );
}; };
@@ -150,6 +150,11 @@ export const modalContent = style({
// :focus-visible will set outline // :focus-visible will set outline
outline: 'none', outline: 'none',
position: 'relative', position: 'relative',
selectors: {
'&[data-no-interaction=true]': {
pointerEvents: 'none',
},
},
}); });
export const modalControls = style({ export const modalControls = style({
@@ -3,6 +3,7 @@ import { assignInlineVars } from '@vanilla-extract/dynamic';
import clsx from 'clsx'; import clsx from 'clsx';
import { import {
createContext, createContext,
forwardRef,
type PropsWithChildren, type PropsWithChildren,
useContext, useContext,
useEffect, useEffect,
@@ -57,30 +58,38 @@ function getElementScreenPositionCenter(target: HTMLElement) {
}; };
} }
export const PeekViewModalContainer = ({ export type PeekViewModalContainerProps = PropsWithChildren<{
onOpenChange,
open,
target,
controls,
children,
hideOnEntering,
onAnimationStart,
onAnimateEnd,
animation = 'zoom',
padding = true,
testId,
}: PropsWithChildren<{
open: boolean;
hideOnEntering?: boolean;
target?: HTMLElement;
onOpenChange: (open: boolean) => void; onOpenChange: (open: boolean) => void;
open: boolean;
target?: HTMLElement;
controls?: React.ReactNode; controls?: React.ReactNode;
hideOnEntering?: boolean;
onAnimationStart?: () => void; onAnimationStart?: () => void;
onAnimateEnd?: () => void; onAnimateEnd?: () => void;
padding?: boolean; padding?: boolean;
animation?: 'fade' | 'zoom'; animation?: 'fade' | 'zoom';
testId?: string; testId?: string;
}>) => { }>;
export const PeekViewModalContainer = forwardRef<
HTMLDivElement,
PeekViewModalContainerProps
>(function PeekViewModalContainer(
{
onOpenChange,
open,
target,
controls,
children,
hideOnEntering,
onAnimationStart,
onAnimateEnd,
animation = 'zoom',
padding = true,
testId,
},
ref
) {
const [{ status }, toggle] = useTransition({ const [{ status }, toggle] = useTransition({
timeout: animationTimeout, timeout: animationTimeout,
}); });
@@ -112,6 +121,7 @@ export const PeekViewModalContainer = ({
onAnimationEnd={onAnimateEnd} onAnimationEnd={onAnimateEnd}
/> />
<div <div
ref={ref}
data-testid={testId} data-testid={testId}
data-peek-view-wrapper data-peek-view-wrapper
className={styles.modalContentWrapper} className={styles.modalContentWrapper}
@@ -133,6 +143,7 @@ export const PeekViewModalContainer = ({
> >
<Dialog.Content <Dialog.Content
{...contentOptions} {...contentOptions}
data-no-interaction={status !== 'entered'}
className={styles.modalContent} className={styles.modalContent}
> >
{hideOnEntering && status === 'entering' ? null : children} {hideOnEntering && status === 'entering' ? null : children}
@@ -148,4 +159,4 @@ export const PeekViewModalContainer = ({
</Dialog.Root> </Dialog.Root>
</PeekViewContext.Provider> </PeekViewContext.Provider>
); );
}; });
@@ -5,34 +5,41 @@ import { useEffect, useMemo } from 'react';
import type { ActivePeekView } from '../entities/peek-view'; import type { ActivePeekView } from '../entities/peek-view';
import { PeekViewService } from '../services/peek-view'; import { PeekViewService } from '../services/peek-view';
import { DocPeekPreview } from './doc-peek-view'; import { DocPeekPreview } from './doc-preview';
import { PeekViewModalContainer } from './modal-container'; import { ImagePreviewPeekView } from './image-preview';
import {
PeekViewModalContainer,
type PeekViewModalContainerProps,
} from './modal-container';
import { import {
DefaultPeekViewControls, DefaultPeekViewControls,
DocPeekViewControls, DocPeekViewControls,
} from './peek-view-controls'; } from './peek-view-controls';
function renderPeekView({ info, template }: ActivePeekView) { function renderPeekView({ info }: ActivePeekView) {
if (template) { if (info.type === 'template') {
return toReactNode(template); return toReactNode(info.template);
}
if (info.type === 'doc') {
return (
<DocPeekPreview
mode={info.mode}
xywh={info.xywh}
docId={info.docId}
blockId={info.blockId}
/>
);
} }
if (!info) { if (info.type === 'image') {
return null; return <ImagePreviewPeekView docId={info.docId} blockId={info.blockId} />;
} }
return ( return null; // unreachable
<DocPeekPreview
mode={info.mode}
xywh={info.xywh}
docId={info.docId}
blockId={info.blockId}
/>
);
} }
const renderControls = ({ info }: ActivePeekView) => { const renderControls = ({ info }: ActivePeekView) => {
if (info && 'docId' in info) { if (info.type === 'doc') {
return ( return (
<DocPeekViewControls <DocPeekViewControls
mode={info.mode} mode={info.mode}
@@ -42,20 +49,44 @@ const renderControls = ({ info }: ActivePeekView) => {
); );
} }
if (info.type === 'image') {
return null; // image controls are rendered in the image preview
}
return <DefaultPeekViewControls />; return <DefaultPeekViewControls />;
}; };
const getRendererProps = (
activePeekView?: ActivePeekView
): Partial<PeekViewModalContainerProps> | undefined => {
if (!activePeekView) {
return;
}
const preview = renderPeekView(activePeekView);
const controls = renderControls(activePeekView);
return {
children: preview,
controls,
target:
activePeekView?.target instanceof HTMLElement
? activePeekView.target
: undefined,
padding: activePeekView.info.type === 'doc',
animation: activePeekView.info.type === 'image' ? 'fade' : 'zoom',
};
};
export const PeekViewManagerModal = () => { export const PeekViewManagerModal = () => {
const peekViewEntity = useService(PeekViewService).peekView; const peekViewEntity = useService(PeekViewService).peekView;
const activePeekView = useLiveData(peekViewEntity.active$); const activePeekView = useLiveData(peekViewEntity.active$);
const show = useLiveData(peekViewEntity.show$); const show = useLiveData(peekViewEntity.show$);
const preview = useMemo(() => { const renderProps = useMemo(() => {
return activePeekView ? renderPeekView(activePeekView) : null; if (!activePeekView) {
}, [activePeekView]); return;
}
const controls = useMemo(() => { return getRendererProps(activePeekView);
return activePeekView ? renderControls(activePeekView) : null;
}, [activePeekView]); }, [activePeekView]);
useEffect(() => { useEffect(() => {
@@ -72,20 +103,15 @@ export const PeekViewManagerModal = () => {
return ( return (
<PeekViewModalContainer <PeekViewModalContainer
open={show && !!preview} {...renderProps}
target={ open={show && !!renderProps}
activePeekView?.target instanceof HTMLElement
? activePeekView.target
: undefined
}
controls={controls}
onOpenChange={open => { onOpenChange={open => {
if (!open) { if (!open) {
peekViewEntity.close(); peekViewEntity.close();
} }
}} }}
> >
{preview} {renderProps?.children}
</PeekViewModalContainer> </PeekViewModalContainer>
); );
}; };
@@ -36,7 +36,6 @@ import type { Map as YMap } from 'yjs';
import { AffineErrorBoundary } from '../../../components/affine/affine-error-boundary'; import { AffineErrorBoundary } from '../../../components/affine/affine-error-boundary';
import { GlobalPageHistoryModal } from '../../../components/affine/page-history-modal'; import { GlobalPageHistoryModal } from '../../../components/affine/page-history-modal';
import { ImagePreviewModal } from '../../../components/image-preview';
import { PageDetailEditor } from '../../../components/page-detail-editor'; import { PageDetailEditor } from '../../../components/page-detail-editor';
import { TrashPageFooter } from '../../../components/pure/trash-page-footer'; import { TrashPageFooter } from '../../../components/pure/trash-page-footer';
import { TopTip } from '../../../components/top-tip'; import { TopTip } from '../../../components/top-tip';
@@ -311,14 +310,6 @@ const DetailPageImpl = memo(function DetailPageImpl() {
</MultiTabSidebarBody> </MultiTabSidebarBody>
} }
/> />
{editor?.host ? (
<ImagePreviewModal
pageId={doc.id}
docCollection={docCollection}
host={editor.host}
/>
) : null}
<GlobalPageHistoryModal /> <GlobalPageHistoryModal />
<PageAIOnboarding /> <PageAIOnboarding />
</> </>
+4 -4
View File
@@ -29,10 +29,10 @@
"@affine/env": "workspace:*", "@affine/env": "workspace:*",
"@affine/i18n": "workspace:*", "@affine/i18n": "workspace:*",
"@affine/native": "workspace:*", "@affine/native": "workspace:*",
"@blocksuite/block-std": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/block-std": "0.15.0-canary-202406260417-3b9fb16",
"@blocksuite/blocks": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/blocks": "0.15.0-canary-202406260417-3b9fb16",
"@blocksuite/presets": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/presets": "0.15.0-canary-202406260417-3b9fb16",
"@blocksuite/store": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/store": "0.15.0-canary-202406260417-3b9fb16",
"@electron-forge/cli": "^7.3.0", "@electron-forge/cli": "^7.3.0",
"@electron-forge/core": "^7.3.0", "@electron-forge/core": "^7.3.0",
"@electron-forge/core-utils": "^7.3.0", "@electron-forge/core-utils": "^7.3.0",
+1 -5
View File
@@ -38,11 +38,7 @@ async function importImage(page: Page, url: string) {
} }
async function closeImagePreviewModal(page: Page) { async function closeImagePreviewModal(page: Page) {
await page await page.getByTestId('image-preview-close-button').first().click();
.getByTestId('image-preview-modal')
.getByTestId('image-preview-close-button')
.first()
.click();
await page.waitForTimeout(500); await page.waitForTimeout(500);
} }
+1 -1
View File
@@ -6,7 +6,7 @@
"@affine/env": "workspace:*", "@affine/env": "workspace:*",
"@affine/templates": "workspace:*", "@affine/templates": "workspace:*",
"@aws-sdk/client-s3": "3.592.0", "@aws-sdk/client-s3": "3.592.0",
"@blocksuite/presets": "0.15.0-canary-202406252341-172c4b8", "@blocksuite/presets": "0.15.0-canary-202406260417-3b9fb16",
"@clack/core": "^0.3.4", "@clack/core": "^0.3.4",
"@clack/prompts": "^0.7.0", "@clack/prompts": "^0.7.0",
"@magic-works/i18n-codegen": "^0.6.0", "@magic-works/i18n-codegen": "^0.6.0",
+68 -68
View File
@@ -226,7 +226,7 @@ __metadata:
"@affine/env": "workspace:*" "@affine/env": "workspace:*"
"@affine/templates": "workspace:*" "@affine/templates": "workspace:*"
"@aws-sdk/client-s3": "npm:3.592.0" "@aws-sdk/client-s3": "npm:3.592.0"
"@blocksuite/presets": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/presets": "npm:0.15.0-canary-202406260417-3b9fb16"
"@clack/core": "npm:^0.3.4" "@clack/core": "npm:^0.3.4"
"@clack/prompts": "npm:^0.7.0" "@clack/prompts": "npm:^0.7.0"
"@magic-works/i18n-codegen": "npm:^0.6.0" "@magic-works/i18n-codegen": "npm:^0.6.0"
@@ -282,12 +282,12 @@ __metadata:
"@affine/electron-api": "workspace:*" "@affine/electron-api": "workspace:*"
"@affine/graphql": "workspace:*" "@affine/graphql": "workspace:*"
"@affine/i18n": "workspace:*" "@affine/i18n": "workspace:*"
"@blocksuite/block-std": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/block-std": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/blocks": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/blocks": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/global": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/global": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/icons": "npm:2.1.58" "@blocksuite/icons": "npm:2.1.58"
"@blocksuite/presets": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/presets": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/store": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/store": "npm:0.15.0-canary-202406260417-3b9fb16"
"@dnd-kit/core": "npm:^6.1.0" "@dnd-kit/core": "npm:^6.1.0"
"@dnd-kit/modifiers": "npm:^7.0.0" "@dnd-kit/modifiers": "npm:^7.0.0"
"@dnd-kit/sortable": "npm:^8.0.0" "@dnd-kit/sortable": "npm:^8.0.0"
@@ -383,13 +383,13 @@ __metadata:
"@affine/graphql": "workspace:*" "@affine/graphql": "workspace:*"
"@affine/i18n": "workspace:*" "@affine/i18n": "workspace:*"
"@affine/templates": "workspace:*" "@affine/templates": "workspace:*"
"@blocksuite/block-std": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/block-std": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/blocks": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/blocks": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/global": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/global": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/icons": "npm:2.1.58" "@blocksuite/icons": "npm:2.1.58"
"@blocksuite/inline": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/inline": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/presets": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/presets": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/store": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/store": "npm:0.15.0-canary-202406260417-3b9fb16"
"@dnd-kit/core": "npm:^6.1.0" "@dnd-kit/core": "npm:^6.1.0"
"@dnd-kit/modifiers": "npm:^7.0.0" "@dnd-kit/modifiers": "npm:^7.0.0"
"@dnd-kit/sortable": "npm:^8.0.0" "@dnd-kit/sortable": "npm:^8.0.0"
@@ -514,10 +514,10 @@ __metadata:
"@affine/env": "workspace:*" "@affine/env": "workspace:*"
"@affine/i18n": "workspace:*" "@affine/i18n": "workspace:*"
"@affine/native": "workspace:*" "@affine/native": "workspace:*"
"@blocksuite/block-std": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/block-std": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/blocks": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/blocks": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/presets": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/presets": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/store": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/store": "npm:0.15.0-canary-202406260417-3b9fb16"
"@electron-forge/cli": "npm:^7.3.0" "@electron-forge/cli": "npm:^7.3.0"
"@electron-forge/core": "npm:^7.3.0" "@electron-forge/core": "npm:^7.3.0"
"@electron-forge/core-utils": "npm:^7.3.0" "@electron-forge/core-utils": "npm:^7.3.0"
@@ -573,8 +573,8 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@affine/env@workspace:packages/common/env" resolution: "@affine/env@workspace:packages/common/env"
dependencies: dependencies:
"@blocksuite/global": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/global": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/store": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/store": "npm:0.15.0-canary-202406260417-3b9fb16"
lit: "npm:^3.1.2" lit: "npm:^3.1.2"
react: "npm:18.3.1" react: "npm:18.3.1"
react-dom: "npm:18.3.1" react-dom: "npm:18.3.1"
@@ -4105,30 +4105,30 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@blocksuite/block-std@npm:0.15.0-canary-202406252341-172c4b8": "@blocksuite/block-std@npm:0.15.0-canary-202406260417-3b9fb16":
version: 0.15.0-canary-202406252341-172c4b8 version: 0.15.0-canary-202406260417-3b9fb16
resolution: "@blocksuite/block-std@npm:0.15.0-canary-202406252341-172c4b8" resolution: "@blocksuite/block-std@npm:0.15.0-canary-202406260417-3b9fb16"
dependencies: dependencies:
"@blocksuite/global": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/global": "npm:0.15.0-canary-202406260417-3b9fb16"
lit: "npm:^3.1.3" lit: "npm:^3.1.3"
lz-string: "npm:^1.5.0" lz-string: "npm:^1.5.0"
w3c-keyname: "npm:^2.2.8" w3c-keyname: "npm:^2.2.8"
zod: "npm:^3.23.8" zod: "npm:^3.23.8"
peerDependencies: peerDependencies:
"@blocksuite/inline": 0.15.0-canary-202406252341-172c4b8 "@blocksuite/inline": 0.15.0-canary-202406260417-3b9fb16
"@blocksuite/store": 0.15.0-canary-202406252341-172c4b8 "@blocksuite/store": 0.15.0-canary-202406260417-3b9fb16
checksum: 10/b3019cd56ac99e3474929a2a6bd9959035dc1901d4e6ab2c3e9911fd8ead94ba0b6025b8fa88d9987f5731c7f331e539e4402a3a0188531d77ae57f0df928d5c checksum: 10/5f39220f61a25205740097265610ee87faf6285a8ad6b1a0959ddfebda5c13df6d23de87af2e5d497c5e805ca546b0030328918e8ec7c8052721cdc72d44a547
languageName: node languageName: node
linkType: hard linkType: hard
"@blocksuite/blocks@npm:0.15.0-canary-202406252341-172c4b8": "@blocksuite/blocks@npm:0.15.0-canary-202406260417-3b9fb16":
version: 0.15.0-canary-202406252341-172c4b8 version: 0.15.0-canary-202406260417-3b9fb16
resolution: "@blocksuite/blocks@npm:0.15.0-canary-202406252341-172c4b8" resolution: "@blocksuite/blocks@npm:0.15.0-canary-202406260417-3b9fb16"
dependencies: dependencies:
"@blocksuite/block-std": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/block-std": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/global": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/global": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/inline": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/inline": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/store": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/store": "npm:0.15.0-canary-202406260417-3b9fb16"
"@dotlottie/player-component": "npm:^2.7.12" "@dotlottie/player-component": "npm:^2.7.12"
"@floating-ui/dom": "npm:^1.6.5" "@floating-ui/dom": "npm:^1.6.5"
"@lit/context": "npm:^1.1.1" "@lit/context": "npm:^1.1.1"
@@ -4165,17 +4165,17 @@ __metadata:
sortablejs: "npm:^1.15.2" sortablejs: "npm:^1.15.2"
unified: "npm:^11.0.4" unified: "npm:^11.0.4"
zod: "npm:^3.23.8" zod: "npm:^3.23.8"
checksum: 10/4a35f9ecf79be53cafd53c197b8f1335f2615e91f68b417473a8681e5c57bc0c62954eb1a5925c0e49899144dd6a95b10900ef0bdbae1709fc20118797f09b58 checksum: 10/e96521e726df32722c044be42355a3161251afb1ef77e19396fe20d5593b05e3a6a54bb57791c80521b43078d5573374286a6b7f0874f75f9f7a2dd5c9741dee
languageName: node languageName: node
linkType: hard linkType: hard
"@blocksuite/global@npm:0.15.0-canary-202406252341-172c4b8": "@blocksuite/global@npm:0.15.0-canary-202406260417-3b9fb16":
version: 0.15.0-canary-202406252341-172c4b8 version: 0.15.0-canary-202406260417-3b9fb16
resolution: "@blocksuite/global@npm:0.15.0-canary-202406252341-172c4b8" resolution: "@blocksuite/global@npm:0.15.0-canary-202406260417-3b9fb16"
dependencies: dependencies:
lib0: "npm:^0.2.94" lib0: "npm:^0.2.94"
zod: "npm:^3.23.8" zod: "npm:^3.23.8"
checksum: 10/e4804b03a03fbd019616c54729563f2fd087d302034529829a1ad7dd8343ac405ee18776cd3f36409e447e4f07b7a8b7099d915af5fb834db21d7be4e0eb3f08 checksum: 10/2d4717ef757045ae494df389b78548d873accd294d8ae9742e789a4e759cda3c180bad2063015b8d804d5120c1ee6910ad3409dcf68347a001742a5d0a452452
languageName: node languageName: node
linkType: hard linkType: hard
@@ -4195,45 +4195,45 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@blocksuite/inline@npm:0.15.0-canary-202406252341-172c4b8": "@blocksuite/inline@npm:0.15.0-canary-202406260417-3b9fb16":
version: 0.15.0-canary-202406252341-172c4b8 version: 0.15.0-canary-202406260417-3b9fb16
resolution: "@blocksuite/inline@npm:0.15.0-canary-202406252341-172c4b8" resolution: "@blocksuite/inline@npm:0.15.0-canary-202406260417-3b9fb16"
dependencies: dependencies:
"@blocksuite/global": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/global": "npm:0.15.0-canary-202406260417-3b9fb16"
zod: "npm:^3.23.8" zod: "npm:^3.23.8"
peerDependencies: peerDependencies:
lit: ^3.1.1 lit: ^3.1.1
yjs: ^13.6.15 yjs: ^13.6.15
checksum: 10/766524a5c923bc2be852763173042d59bd1dbe7736c37fb096a23bab580fa2baa85e768333f72ccdf3d362ba91ec19cff0cda4a8ef072489581cbf846859bc38 checksum: 10/8c4387116ddc3f65408cc15b4ef1e63f089d8b18891ac79a78c8707c6e88c0d8d6017768c38877e38e73d977f7ca4618d8fe09da8b9cc5e429273dbcd066e176
languageName: node languageName: node
linkType: hard linkType: hard
"@blocksuite/presets@npm:0.15.0-canary-202406252341-172c4b8": "@blocksuite/presets@npm:0.15.0-canary-202406260417-3b9fb16":
version: 0.15.0-canary-202406252341-172c4b8 version: 0.15.0-canary-202406260417-3b9fb16
resolution: "@blocksuite/presets@npm:0.15.0-canary-202406252341-172c4b8" resolution: "@blocksuite/presets@npm:0.15.0-canary-202406260417-3b9fb16"
dependencies: dependencies:
"@blocksuite/block-std": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/block-std": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/blocks": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/blocks": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/global": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/global": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/inline": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/inline": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/store": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/store": "npm:0.15.0-canary-202406260417-3b9fb16"
"@dotlottie/player-component": "npm:^2.7.12" "@dotlottie/player-component": "npm:^2.7.12"
"@fal-ai/serverless-client": "npm:^0.10.0" "@fal-ai/serverless-client": "npm:^0.10.0"
"@floating-ui/dom": "npm:^1.6.5" "@floating-ui/dom": "npm:^1.6.5"
"@toeverything/theme": "npm:^0.7.32" "@toeverything/theme": "npm:^0.7.32"
lit: "npm:^3.1.3" lit: "npm:^3.1.3"
openai: "npm:^4.47.2" openai: "npm:^4.47.2"
checksum: 10/f15f9cf8293c132df0cf48b83021a328a45a29e8426a2ab28878e18e80e23aebd1d3c8a8429af49df97c3c178bb71be47a15cf2559672ff6de901508a599324d checksum: 10/7ca99e06c13edbb2585dbc2fc79a2f3f231e17698d361407f4921947cf460efaac8eb63c8c28817d085507139171ebb6179b2fb781dba648785337aba07728e3
languageName: node languageName: node
linkType: hard linkType: hard
"@blocksuite/store@npm:0.15.0-canary-202406252341-172c4b8": "@blocksuite/store@npm:0.15.0-canary-202406260417-3b9fb16":
version: 0.15.0-canary-202406252341-172c4b8 version: 0.15.0-canary-202406260417-3b9fb16
resolution: "@blocksuite/store@npm:0.15.0-canary-202406252341-172c4b8" resolution: "@blocksuite/store@npm:0.15.0-canary-202406260417-3b9fb16"
dependencies: dependencies:
"@blocksuite/global": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/global": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/inline": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/inline": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/sync": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/sync": "npm:0.15.0-canary-202406260417-3b9fb16"
"@types/flexsearch": "npm:^0.7.6" "@types/flexsearch": "npm:^0.7.6"
flexsearch: "npm:0.7.43" flexsearch: "npm:0.7.43"
lib0: "npm:^0.2.94" lib0: "npm:^0.2.94"
@@ -4244,21 +4244,21 @@ __metadata:
zod: "npm:^3.23.8" zod: "npm:^3.23.8"
peerDependencies: peerDependencies:
yjs: ^13.6.15 yjs: ^13.6.15
checksum: 10/fc63b49298debf3afbbed25e0671418ea2b0e1963f955c2ca51d7f8f86994b7ff83fbbbb1e89a78eb2c9e5643ecbd27f1e1565a13ba3d9dd5710a1efb0132755 checksum: 10/7e5aa60619d6cac4619d390391d190c2401e47d12cc7df5db315e492a2fda701aa93aa791bcef8bf73fdfc9180d54f0bb1d007b81755f0c12f29d0379827fa77
languageName: node languageName: node
linkType: hard linkType: hard
"@blocksuite/sync@npm:0.15.0-canary-202406252341-172c4b8": "@blocksuite/sync@npm:0.15.0-canary-202406260417-3b9fb16":
version: 0.15.0-canary-202406252341-172c4b8 version: 0.15.0-canary-202406260417-3b9fb16
resolution: "@blocksuite/sync@npm:0.15.0-canary-202406252341-172c4b8" resolution: "@blocksuite/sync@npm:0.15.0-canary-202406260417-3b9fb16"
dependencies: dependencies:
"@blocksuite/global": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/global": "npm:0.15.0-canary-202406260417-3b9fb16"
idb: "npm:^8.0.0" idb: "npm:^8.0.0"
idb-keyval: "npm:^6.2.1" idb-keyval: "npm:^6.2.1"
y-protocols: "npm:^1.0.6" y-protocols: "npm:^1.0.6"
peerDependencies: peerDependencies:
yjs: ^13.6.15 yjs: ^13.6.15
checksum: 10/22a320983945d14f28fdd83baf219e8a2e3b37ead6556cff58557b1822b3f84ec841e6c1373cf62ae536923d45baea007c6e155839fbde5539105304aeb7da83 checksum: 10/f1493bf7d54e1ea2990ba6898c4df0cc3fb2f193e81281bf3fbbe0093e6252e655d69b3af059a09a6f35180490d2291beb25b2f7e9d0208b1fd0e7b509504c2f
languageName: node languageName: node
linkType: hard linkType: hard
@@ -15649,11 +15649,11 @@ __metadata:
"@affine/debug": "workspace:*" "@affine/debug": "workspace:*"
"@affine/env": "workspace:*" "@affine/env": "workspace:*"
"@affine/templates": "workspace:*" "@affine/templates": "workspace:*"
"@blocksuite/block-std": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/block-std": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/blocks": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/blocks": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/global": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/global": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/presets": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/presets": "npm:0.15.0-canary-202406260417-3b9fb16"
"@blocksuite/store": "npm:0.15.0-canary-202406252341-172c4b8" "@blocksuite/store": "npm:0.15.0-canary-202406260417-3b9fb16"
"@datastructures-js/binary-search-tree": "npm:^5.3.2" "@datastructures-js/binary-search-tree": "npm:^5.3.2"
"@testing-library/react": "npm:^16.0.0" "@testing-library/react": "npm:^16.0.0"
async-call-rpc: "npm:^6.4.0" async-call-rpc: "npm:^6.4.0"