refactor(core): remove outline plugin and layout atom (#5326)

@affine/outline is no longer used, this PR deletes this plugin and deletes the code that is no longer used
This commit is contained in:
EYHN 2023-12-25 03:24:12 +00:00
parent a10aeca820
commit 05025bf59a
No known key found for this signature in database
GPG Key ID: 46C9E26A75AB276C
16 changed files with 3 additions and 380 deletions

View File

@ -126,7 +126,6 @@ If you have questions, you are welcome to contact us. One of the best places to
| ---------------------------------------------------------------- | ----------------------------------------- | ------ |
| [@affine/copilot-plugin](./packages/plugins/copilot) | AI Copilot that help you document writing | 🚧 |
| [@affine/image-preview-plugin](./packages/plugins/image-preview) | Component for previewing an image | ✅ |
| [@affine/outline](./packages/plugins/outline) | Outline for your document | ✅ |
## Upstreams

View File

@ -36,7 +36,6 @@ export const pluginPackageJson = atom<
export const enabledPluginAtom = atomWithStorage('affine-enabled-plugin', [
'@affine/image-preview-plugin',
'@affine/outline-plugin',
]);
export const pluginHeaderItemAtom = atom<

View File

@ -2,7 +2,6 @@ import { atom } from 'jotai';
export const loadedPluginNameAtom = atom<string[]>([]);
export * from './layout';
export * from './root-store';
export * from './settings';
export * from './workspace';

View File

@ -1,34 +0,0 @@
import type { ExpectedLayout } from '@affine/sdk/entry';
import { atom } from 'jotai';
const contentLayoutBaseAtom = atom<ExpectedLayout>('editor');
type SetStateAction<Value> = Value | ((prev: Value) => Value);
export const contentLayoutAtom = atom<
ExpectedLayout,
[SetStateAction<ExpectedLayout>],
void
>(
get => get(contentLayoutBaseAtom),
(_, set, layout) => {
set(contentLayoutBaseAtom, prev => {
let setV: (prev: ExpectedLayout) => ExpectedLayout;
if (typeof layout !== 'function') {
setV = () => layout;
} else {
setV = layout;
}
const nextValue = setV(prev);
if (nextValue === 'editor') {
return nextValue;
}
if (nextValue.first !== 'editor') {
throw new Error('The first element of the layout should be editor.');
}
if (nextValue.splitPercentage && nextValue.splitPercentage < 70) {
throw new Error('The split percentage should be greater than 70.');
}
return nextValue;
});
}
);

View File

@ -1,6 +1,4 @@
import type { ExpectedLayout } from '@affine/sdk/entry';
import type Buffer from 'buffer';
import type { WritableAtom } from 'jotai';
import { z } from 'zod';
import type { AppConfigSchema } from './app-config-storage.js';
@ -33,14 +31,6 @@ export const packageJsonOutputSchema = z.object({
}),
});
type SetStateAction<Value> = Value | ((prev: Value) => Value);
export type ContentLayoutAtom = WritableAtom<
ExpectedLayout,
[SetStateAction<ExpectedLayout>],
void
>;
export abstract class HandlerManager<
Namespace extends string,
Handlers extends Record<string, PrimitiveHandlers>,

View File

@ -63,7 +63,6 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
'/plugins/hello-world',
'/plugins/image-preview',
'/plugins/vue-hello-world',
'/plugins/outline',
],
enableTestProperties: true,
enableBroadcastChannelProvider: true,
@ -105,7 +104,6 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
'/plugins/hello-world',
'/plugins/image-preview',
'/plugins/vue-hello-world',
'/plugins/outline',
],
enablePlugin: process.env.ENABLE_PLUGIN
? process.env.ENABLE_PLUGIN === 'true'

View File

@ -1,10 +1,5 @@
import { DebugLogger } from '@affine/debug';
import type {
CallbackMap,
ExpectedLayout,
LayoutNode,
PluginContext,
} from '@affine/sdk/entry';
import type { CallbackMap, PluginContext } from '@affine/sdk/entry';
import { AffineFormatBarWidget } from '@blocksuite/blocks';
import { assertExists } from '@blocksuite/global/utils';
import {
@ -12,10 +7,7 @@ import {
pluginEditorAtom,
pluginHeaderItemAtom,
pluginSettingAtom,
pluginWindowAtom,
} from '@toeverything/infra/__internal__/plugin';
import { contentLayoutAtom, currentPageIdAtom } from '@toeverything/infra/atom';
import { atom } from 'jotai';
import { Provider } from 'jotai/react';
import type { createStore } from 'jotai/vanilla';
import { createElement, type PropsWithChildren } from 'react';
@ -31,83 +23,6 @@ const permissionLogger = new DebugLogger('plugins:permission');
const importLogger = new DebugLogger('plugins:import');
const entryLogger = new DebugLogger('plugins:entry');
const pushLayoutAtom = atom<
null,
// fixme: check plugin name here
[
pluginName: string,
create: (root: HTMLElement) => () => void,
options:
| {
maxWidth: (number | undefined)[];
}
| undefined,
],
void
>(null, (_, set, pluginName, callback, options) => {
set(pluginWindowAtom, items => ({
...items,
[pluginName]: callback,
}));
set(contentLayoutAtom, layout => {
if (layout === 'editor') {
return {
direction: 'horizontal',
first: 'editor',
second: pluginName,
splitPercentage: 70,
maxWidth: options?.maxWidth,
};
} else {
return {
direction: 'horizontal',
first: 'editor',
splitPercentage: 70,
second: {
direction: 'horizontal',
first: pluginName,
second: layout.second,
splitPercentage: 50,
},
} satisfies ExpectedLayout;
}
});
addCleanup(pluginName, () => {
set(deleteLayoutAtom, pluginName);
});
});
const deleteLayoutAtom = atom<null, [string], void>(null, (_, set, id) => {
set(pluginWindowAtom, items => {
const newItems = { ...items };
delete newItems[id];
return newItems;
});
const removeLayout = (layout: LayoutNode): LayoutNode | string => {
if (typeof layout === 'string') {
return layout;
}
if (layout.first === id) {
return layout.second;
} else if (layout.second === id) {
return layout.first;
} else {
return {
...layout,
second: removeLayout(layout.second),
};
}
};
set(contentLayoutAtom, layout => {
if (layout === 'editor') {
return 'editor';
} else {
return removeLayout(layout) as ExpectedLayout;
}
});
});
const setupWeakMap = new WeakMap<
ReturnType<typeof createStore>,
ReturnType<typeof createSetupImpl>
@ -124,11 +39,6 @@ export function createSetup(rootStore: ReturnType<typeof createStore>) {
}
function createSetupImpl(rootStore: ReturnType<typeof createStore>) {
// clean up plugin windows when switching to other pages
rootStore.sub(currentPageIdAtom, () => {
rootStore.set(contentLayoutAtom, 'editor');
});
// module -> importName -> updater[]
const _rootImportsMap = new Map<string, Map<string, any>>();
const rootImportsMapSetupPromise = setupImportsMap(_rootImportsMap, {
@ -145,9 +55,6 @@ function createSetupImpl(rootStore: ReturnType<typeof createStore>) {
'@blocksuite/inline': import('@blocksuite/inline'),
'@affine/sdk/entry': {
rootStore,
currentPageIdAtom: currentPageIdAtom,
pushLayoutAtom: pushLayoutAtom,
deleteLayoutAtom: deleteLayoutAtom,
},
'@blocksuite/global/utils': import('@blocksuite/global/utils'),
'@toeverything/infra/atom': import('@toeverything/infra/atom'),

View File

@ -28,7 +28,6 @@
"@affine/hello-world-plugin": "workspace:*",
"@affine/image-preview-plugin": "workspace:*",
"@affine/native": "workspace:*",
"@affine/outline-plugin": "workspace:*",
"@affine/sdk": "workspace:*",
"@affine/templates": "workspace:*",
"@affine/vue-hello-world-plugin": "workspace:*",

View File

@ -1,28 +0,0 @@
{
"name": "@affine/outline-plugin",
"type": "module",
"private": true,
"description": "Outline plugin",
"version": "0.11.0",
"scripts": {
"dev": "af dev",
"build": "af build"
},
"affinePlugin": {
"release": "development",
"entry": {
"core": "./src/index.ts"
}
},
"dependencies": {
"@affine/component": "workspace:*",
"@affine/sdk": "workspace:*",
"@blocksuite/icons": "2.1.36"
},
"devDependencies": {
"@affine/plugin-cli": "workspace:*",
"jotai": "^2.5.1",
"react": "18.2.0",
"react-dom": "18.2.0"
}
}

View File

@ -1,26 +0,0 @@
{
"name": "@affine/outline-plugin",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"namedInputs": {
"default": [
"{projectRoot}/**/*",
"{workspaceRoot}/tools/plugin-cli/src/**/*",
"sharedGlobals"
]
},
"targets": {
"build": {
"executor": "nx:run-script",
"options": {
"script": "build"
},
"dependsOn": ["^build"],
"inputs": ["default"],
"outputs": [
"{workspaceRoot}/packages/frontend/core/public/plugins/outline",
"{workspaceRoot}/packages/frontend/electron/dist/plugins/outline"
]
}
},
"tags": ["plugin"]
}

View File

@ -1,107 +0,0 @@
import { IconButton } from '@affine/component/ui/button';
import { Tooltip } from '@affine/component/ui/tooltip';
import {
currentPageIdAtom,
currentWorkspaceAtom,
deleteLayoutAtom,
pushLayoutAtom,
} from '@affine/sdk/entry';
import { TOCNotesPanel } from '@blocksuite/blocks';
import { assertExists } from '@blocksuite/global/utils';
import { RightSidebarIcon } from '@blocksuite/icons';
import { useAtomValue, useSetAtom } from 'jotai';
import type { ComponentType, PropsWithChildren } from 'react';
import { useCallback, useRef, useState } from 'react';
import { createRoot } from 'react-dom/client';
const Outline = () => {
const tocPanelRef = useRef<TOCNotesPanel | null>(null);
const currentPageId = useAtomValue(currentPageIdAtom);
assertExists(currentPageId, 'current page id');
const currentWorkspace = useAtomValue(currentWorkspaceAtom);
const currentPage = currentWorkspace.getPage(currentPageId);
assertExists(currentPage, 'current page');
if (!tocPanelRef.current) {
tocPanelRef.current = new TOCNotesPanel();
}
if (currentPage !== tocPanelRef.current?.page) {
(tocPanelRef.current as TOCNotesPanel).page = currentPage;
}
return (
<div
className={`outline-wrapper`}
style={{
height: '100%',
borderLeft: `1px solid var(--affine-border-color)`,
}}
ref={useCallback((container: HTMLDivElement | null) => {
if (container) {
assertExists(tocPanelRef.current);
container.append(tocPanelRef.current);
}
}, [])}
/>
);
};
export const HeaderItem = ({
Provider,
}: {
Provider: ComponentType<PropsWithChildren>;
}) => {
const [open, setOpen] = useState(false);
const pushLayout = useSetAtom(pushLayoutAtom);
const deleteLayout = useSetAtom(deleteLayoutAtom);
const [container, setContainer] = useState<HTMLButtonElement | null>(null);
return (
<Tooltip
content={`${open ? 'Collapse' : 'Expand'} table of contents`}
portalOptions={{
container,
}}
>
<IconButton
size="large"
ref={setContainer}
style={{
width: '32px',
fontSize: '24px',
}}
onClick={useCallback(() => {
if (!open) {
setOpen(true);
pushLayout(
'@affine/outline-plugin',
div => {
const root = createRoot(div);
div.style.height = '100%';
root.render(
<Provider>
<Outline />
</Provider>
);
return () => {
root.unmount();
};
},
{
maxWidth: [undefined, 300],
}
);
} else {
setOpen(false);
deleteLayout('@affine/outline-plugin');
}
}, [Provider, deleteLayout, open, pushLayout])}
>
<RightSidebarIcon />
</IconButton>
</Tooltip>
);
};

View File

@ -1,41 +0,0 @@
import type { PluginContext } from '@affine/sdk/entry';
import { registerTOCComponents } from '@blocksuite/blocks';
import { createElement } from 'react';
import { createRoot } from 'react-dom/client';
import { HeaderItem } from './app';
export const entry = (context: PluginContext) => {
console.log('register outline');
context.register('headerItem', div => {
registerTOCComponents(components => {
for (const compName in components) {
if (window.customElements.get(compName)) continue;
window.customElements.define(
compName,
components[compName as keyof typeof components]
);
}
});
div.style.height = '100%';
const root = createRoot(div);
root.render(
createElement(
context.utils.PluginProvider,
{},
createElement(HeaderItem, {
Provider: context.utils.PluginProvider,
})
)
);
return () => {
root.unmount();
};
});
return () => {};
};

View File

@ -1,17 +0,0 @@
{
"extends": "../../../tsconfig.json",
"include": ["./src"],
"compilerOptions": {
"noEmit": false,
"outDir": "lib",
"jsx": "preserve"
},
"references": [
{
"path": "../../common/sdk"
},
{
"path": "../../frontend/component"
}
]
}

View File

@ -236,7 +236,8 @@ test('can sync svg between different browsers', async ({ page, browser }) => {
const image = page.locator('affine-image');
page.evaluate(async () => {
window.showOpenFilePicker = undefined;
// https://github.com/toeverything/blocksuite/blob/master/packages/blocks/src/_common/utils/filesys.ts#L20
(window as any).showOpenFilePicker = undefined;
});
const title = getBlockSuiteEditorTitle(page);

View File

@ -26,7 +26,6 @@ test('plugin should exist', async ({ page }) => {
'@affine/hello-world-plugin',
'@affine/image-preview-plugin',
'@affine/vue-hello-world-plugin',
'@affine/outline-plugin',
];
expect(packageJson).toEqual(
plugins

View File

@ -464,7 +464,6 @@ __metadata:
"@affine/hello-world-plugin": "workspace:*"
"@affine/image-preview-plugin": "workspace:*"
"@affine/native": "workspace:*"
"@affine/outline-plugin": "workspace:*"
"@affine/sdk": "workspace:*"
"@affine/templates": "workspace:*"
"@affine/vue-hello-world-plugin": "workspace:*"
@ -675,20 +674,6 @@ __metadata:
languageName: unknown
linkType: soft
"@affine/outline-plugin@workspace:*, @affine/outline-plugin@workspace:packages/plugins/outline":
version: 0.0.0-use.local
resolution: "@affine/outline-plugin@workspace:packages/plugins/outline"
dependencies:
"@affine/component": "workspace:*"
"@affine/plugin-cli": "workspace:*"
"@affine/sdk": "workspace:*"
"@blocksuite/icons": "npm:2.1.36"
jotai: "npm:^2.5.1"
react: "npm:18.2.0"
react-dom: "npm:18.2.0"
languageName: unknown
linkType: soft
"@affine/plugin-cli@workspace:*, @affine/plugin-cli@workspace:tools/plugin-cli":
version: 0.0.0-use.local
resolution: "@affine/plugin-cli@workspace:tools/plugin-cli"