diff --git a/packages/common/env/src/global.ts b/packages/common/env/src/global.ts
index 87dc1b3f63..8709d98008 100644
--- a/packages/common/env/src/global.ts
+++ b/packages/common/env/src/global.ts
@@ -31,6 +31,7 @@ export const runtimeFlagsSchema = z.object({
enableEnhanceShareMode: z.boolean(),
enablePayment: z.boolean(),
enablePageHistory: z.boolean(),
+ enableCopilot: z.boolean(),
// this is for the electron app
serverUrlPrefix: z.string(),
enableMoveDatabase: z.boolean(),
diff --git a/packages/frontend/core/.webpack/runtime-config.ts b/packages/frontend/core/.webpack/runtime-config.ts
index da056b8cff..c899a40a90 100644
--- a/packages/frontend/core/.webpack/runtime-config.ts
+++ b/packages/frontend/core/.webpack/runtime-config.ts
@@ -32,6 +32,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
enableEnhanceShareMode: false,
enablePayment: true,
enablePageHistory: false,
+ enableCopilot: false,
serverUrlPrefix: 'https://insider.affine.pro', // Let insider be stable environment temporarily.
editorFlags,
appVersion: packageJson.version,
@@ -42,6 +43,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
return {
...this.stable,
enablePageHistory: false,
+ enableCopilot: false,
serverUrlPrefix: 'https://insider.affine.pro',
appBuildType: 'beta' as const,
};
@@ -80,6 +82,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
enableEnhanceShareMode: false,
enablePayment: true,
enablePageHistory: true,
+ enableCopilot: true,
serverUrlPrefix: 'https://affine.fail',
editorFlags,
appVersion: packageJson.version,
@@ -153,6 +156,11 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
: buildFlags.mode === 'development'
? true
: currentBuildPreset.enablePageHistory,
+ enableCopilot: process.env.ENABLE_COPILOT
+ ? process.env.ENABLE_COPILOT === 'true'
+ : buildFlags.mode === 'development'
+ ? true
+ : currentBuildPreset.enableCopilot,
};
if (buildFlags.mode === 'development') {
diff --git a/packages/frontend/core/src/bootstrap/register-blocksuite-components.ts b/packages/frontend/core/src/bootstrap/register-blocksuite-components.ts
index ce451b8d26..dbe4739f60 100644
--- a/packages/frontend/core/src/bootstrap/register-blocksuite-components.ts
+++ b/packages/frontend/core/src/bootstrap/register-blocksuite-components.ts
@@ -1,5 +1,7 @@
-import { registerTOCPanelComponents } from '@blocksuite/presets';
-import { registerFramePanelComponents } from '@blocksuite/presets';
+import {
+ registerFramePanelComponents,
+ registerTOCPanelComponents,
+} from '@blocksuite/presets';
registerTOCPanelComponents(components => {
for (const compName in components) {
diff --git a/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx b/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx
index b955b829f5..99216d5ad5 100644
--- a/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx
+++ b/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx
@@ -12,7 +12,7 @@ import type { Page, Workspace } from '@blocksuite/store';
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
import { useWorkspaceStatus } from '@toeverything/hooks/use-workspace-status';
import { appSettingAtom, currentPageIdAtom } from '@toeverything/infra/atom';
-import { useAtomValue, useSetAtom } from 'jotai';
+import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import {
memo,
type ReactElement,
@@ -43,7 +43,6 @@ import {
EditorSidebar,
editorSidebarOpenAtom,
editorSidebarResizingAtom,
- editorSidebarStateAtom,
editorSidebarWidthAtom,
} from './editor-sidebar';
@@ -64,17 +63,13 @@ const DetailPageLayout = ({
footer,
sidebar,
}: DetailPageLayoutProps): ReactElement => {
- const sidebarState = useAtomValue(editorSidebarStateAtom);
- const setSidebarWidth = useSetAtom(editorSidebarWidthAtom);
+ const [width, setWidth] = useAtom(editorSidebarWidthAtom);
const { clientBorder } = useAtomValue(appSettingAtom);
- const setResizing = useSetAtom(editorSidebarResizingAtom);
- const setOpen = useSetAtom(editorSidebarOpenAtom);
+ const [resizing, setResizing] = useAtom(editorSidebarResizingAtom);
+ const [open, setOpen] = useAtom(editorSidebarOpenAtom);
return (
-
+
{header}
{main}
@@ -85,13 +80,13 @@ const DetailPageLayout = ({
enableAnimation={false}
resizeHandlePos="left"
resizeHandleOffset={clientBorder ? 4 : 0}
- width={sidebarState.width}
+ width={width}
className={styles.sidebarContainer}
onResizing={setResizing}
- resizing={sidebarState.resizing}
- open={sidebarState.isOpen}
+ resizing={resizing}
+ open={open}
onOpen={setOpen}
- onWidthChange={setSidebarWidth}
+ onWidthChange={setWidth}
minWidth={MIN_SIDEBAR_WIDTH}
maxWidth={MAX_SIDEBAR_WIDTH}
>
diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/atoms.ts b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/atoms.ts
index 1685e5c088..f26511d258 100644
--- a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/atoms.ts
+++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/atoms.ts
@@ -3,6 +3,7 @@ import { assertExists } from '@blocksuite/global/utils';
import { atom } from 'jotai';
import { selectAtom } from 'jotai/utils';
+import { copilotExtension } from './extensions/copilot';
import { framePanelExtension } from './extensions/frame';
import { outlineExtension } from './extensions/outline';
import type { EditorExtension, EditorExtensionName } from './types';
@@ -12,6 +13,7 @@ import type { EditorExtension, EditorExtensionName } from './types';
export const extensions: EditorExtension[] = [
outlineExtension,
framePanelExtension,
+ copilotExtension,
];
export interface EditorSidebarState {
@@ -30,8 +32,6 @@ const baseStateAtom = atom
({
extensions: extensions, // todo: maybe should be dynamic (by feature flag?)
});
-export const editorSidebarStateAtom = atom(get => get(baseStateAtom));
-
const isOpenAtom = selectAtom(baseStateAtom, state => state.isOpen);
const resizingAtom = selectAtom(baseStateAtom, state => state.resizing);
const activeExtensionAtom = selectAtom(
@@ -40,9 +40,10 @@ const activeExtensionAtom = selectAtom(
);
const widthAtom = selectAtom(baseStateAtom, state => state.width);
-export const editorExtensionsAtom = selectAtom(
- baseStateAtom,
- state => state.extensions
+export const editorExtensionsAtom = selectAtom(baseStateAtom, state =>
+ state.extensions.filter(e => {
+ return e.name !== 'copilot' || runtimeConfig.enableCopilot;
+ })
);
// get/set sidebar open state
diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/editor-sidebar.tsx b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/editor-sidebar.tsx
index 31279f810e..b611beddd9 100644
--- a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/editor-sidebar.tsx
+++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/editor-sidebar.tsx
@@ -1,11 +1,11 @@
import { useAtomValue } from 'jotai';
-import { editorSidebarStateAtom } from './atoms';
+import { editorSidebarActiveExtensionAtom } from './atoms';
import * as styles from './editor-sidebar.css';
export const EditorSidebar = () => {
- const sidebarState = useAtomValue(editorSidebarStateAtom);
- const Component = sidebarState.activeExtension?.Component;
+ const activeExtension = useAtomValue(editorSidebarActiveExtensionAtom);
+ const Component = activeExtension?.Component;
return {Component ? : null}
;
};
diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/copilot.css.ts b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/copilot.css.ts
new file mode 100644
index 0000000000..0a2dbb4421
--- /dev/null
+++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/copilot.css.ts
@@ -0,0 +1,7 @@
+import { style } from '@vanilla-extract/css';
+
+export const root = style({
+ display: 'flex',
+ height: '100%',
+ width: '100%',
+});
diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/copilot.tsx b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/copilot.tsx
new file mode 100644
index 0000000000..db04dbce69
--- /dev/null
+++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/extensions/copilot.tsx
@@ -0,0 +1,46 @@
+import { editorContainerAtom } from '@affine/component/block-suite-editor';
+import { assertExists } from '@blocksuite/global/utils';
+import { AiIcon } from '@blocksuite/icons';
+import { CopilotPanel } from '@blocksuite/presets';
+import { useAtom } from 'jotai';
+import { useCallback, useRef } from 'react';
+
+import type { EditorExtension } from '../types';
+import * as styles from './outline.css';
+
+// A wrapper for CopilotPanel
+const EditorCopilotPanel = () => {
+ const copilotPanelRef = useRef(null);
+ const [editorContainer] = useAtom(editorContainerAtom);
+
+ const onRefChange = useCallback((container: HTMLDivElement | null) => {
+ if (container) {
+ assertExists(
+ copilotPanelRef.current,
+ 'copilot panel should be initialized'
+ );
+ container.append(copilotPanelRef.current);
+ }
+ }, []);
+
+ if (!editorContainer) {
+ return;
+ }
+
+ if (!copilotPanelRef.current) {
+ copilotPanelRef.current = new CopilotPanel();
+ }
+
+ if (editorContainer !== copilotPanelRef.current?.editor) {
+ (copilotPanelRef.current as CopilotPanel).editor = editorContainer;
+ // (copilotPanelRef.current as CopilotPanel).fitPadding = [20, 20, 20, 20];
+ }
+
+ return ;
+};
+
+export const copilotExtension: EditorExtension = {
+ name: 'copilot',
+ icon: ,
+ Component: EditorCopilotPanel,
+};
diff --git a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/types.ts b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/types.ts
index ab7ab4d043..b992ec9f9f 100644
--- a/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/types.ts
+++ b/packages/frontend/core/src/pages/workspace/detail-page/editor-sidebar/types.ts
@@ -1,4 +1,4 @@
-export type EditorExtensionName = 'outline' | 'frame';
+export type EditorExtensionName = 'outline' | 'frame' | 'copilot';
export interface EditorExtension {
name: EditorExtensionName;