feat(core): ai poc (#5317)

This commit is contained in:
Peng Xiao
2023-12-19 05:13:28 +00:00
parent 408b84109b
commit a815fd6b9a
9 changed files with 85 additions and 25 deletions

View File

@@ -31,6 +31,7 @@ export const runtimeFlagsSchema = z.object({
enableEnhanceShareMode: z.boolean(), enableEnhanceShareMode: z.boolean(),
enablePayment: z.boolean(), enablePayment: z.boolean(),
enablePageHistory: z.boolean(), enablePageHistory: z.boolean(),
enableCopilot: z.boolean(),
// this is for the electron app // this is for the electron app
serverUrlPrefix: z.string(), serverUrlPrefix: z.string(),
enableMoveDatabase: z.boolean(), enableMoveDatabase: z.boolean(),

View File

@@ -32,6 +32,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
enableEnhanceShareMode: false, enableEnhanceShareMode: false,
enablePayment: true, enablePayment: true,
enablePageHistory: false, enablePageHistory: false,
enableCopilot: false,
serverUrlPrefix: 'https://insider.affine.pro', // Let insider be stable environment temporarily. serverUrlPrefix: 'https://insider.affine.pro', // Let insider be stable environment temporarily.
editorFlags, editorFlags,
appVersion: packageJson.version, appVersion: packageJson.version,
@@ -42,6 +43,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
return { return {
...this.stable, ...this.stable,
enablePageHistory: false, enablePageHistory: false,
enableCopilot: false,
serverUrlPrefix: 'https://insider.affine.pro', serverUrlPrefix: 'https://insider.affine.pro',
appBuildType: 'beta' as const, appBuildType: 'beta' as const,
}; };
@@ -80,6 +82,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
enableEnhanceShareMode: false, enableEnhanceShareMode: false,
enablePayment: true, enablePayment: true,
enablePageHistory: true, enablePageHistory: true,
enableCopilot: true,
serverUrlPrefix: 'https://affine.fail', serverUrlPrefix: 'https://affine.fail',
editorFlags, editorFlags,
appVersion: packageJson.version, appVersion: packageJson.version,
@@ -153,6 +156,11 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
: buildFlags.mode === 'development' : buildFlags.mode === 'development'
? true ? true
: currentBuildPreset.enablePageHistory, : currentBuildPreset.enablePageHistory,
enableCopilot: process.env.ENABLE_COPILOT
? process.env.ENABLE_COPILOT === 'true'
: buildFlags.mode === 'development'
? true
: currentBuildPreset.enableCopilot,
}; };
if (buildFlags.mode === 'development') { if (buildFlags.mode === 'development') {

View File

@@ -1,5 +1,7 @@
import { registerTOCPanelComponents } from '@blocksuite/presets'; import {
import { registerFramePanelComponents } from '@blocksuite/presets'; registerFramePanelComponents,
registerTOCPanelComponents,
} from '@blocksuite/presets';
registerTOCPanelComponents(components => { registerTOCPanelComponents(components => {
for (const compName in components) { for (const compName in components) {

View File

@@ -12,7 +12,7 @@ import type { Page, Workspace } from '@blocksuite/store';
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
import { useWorkspaceStatus } from '@toeverything/hooks/use-workspace-status'; import { useWorkspaceStatus } from '@toeverything/hooks/use-workspace-status';
import { appSettingAtom, currentPageIdAtom } from '@toeverything/infra/atom'; import { appSettingAtom, currentPageIdAtom } from '@toeverything/infra/atom';
import { useAtomValue, useSetAtom } from 'jotai'; import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import { import {
memo, memo,
type ReactElement, type ReactElement,
@@ -43,7 +43,6 @@ import {
EditorSidebar, EditorSidebar,
editorSidebarOpenAtom, editorSidebarOpenAtom,
editorSidebarResizingAtom, editorSidebarResizingAtom,
editorSidebarStateAtom,
editorSidebarWidthAtom, editorSidebarWidthAtom,
} from './editor-sidebar'; } from './editor-sidebar';
@@ -64,17 +63,13 @@ const DetailPageLayout = ({
footer, footer,
sidebar, sidebar,
}: DetailPageLayoutProps): ReactElement => { }: DetailPageLayoutProps): ReactElement => {
const sidebarState = useAtomValue(editorSidebarStateAtom); const [width, setWidth] = useAtom(editorSidebarWidthAtom);
const setSidebarWidth = useSetAtom(editorSidebarWidthAtom);
const { clientBorder } = useAtomValue(appSettingAtom); const { clientBorder } = useAtomValue(appSettingAtom);
const setResizing = useSetAtom(editorSidebarResizingAtom); const [resizing, setResizing] = useAtom(editorSidebarResizingAtom);
const setOpen = useSetAtom(editorSidebarOpenAtom); const [open, setOpen] = useAtom(editorSidebarOpenAtom);
return ( return (
<div <div className={styles.root} data-client-border={clientBorder && open}>
className={styles.root}
data-client-border={clientBorder && sidebarState.isOpen}
>
<div className={styles.mainContainer}> <div className={styles.mainContainer}>
{header} {header}
{main} {main}
@@ -85,13 +80,13 @@ const DetailPageLayout = ({
enableAnimation={false} enableAnimation={false}
resizeHandlePos="left" resizeHandlePos="left"
resizeHandleOffset={clientBorder ? 4 : 0} resizeHandleOffset={clientBorder ? 4 : 0}
width={sidebarState.width} width={width}
className={styles.sidebarContainer} className={styles.sidebarContainer}
onResizing={setResizing} onResizing={setResizing}
resizing={sidebarState.resizing} resizing={resizing}
open={sidebarState.isOpen} open={open}
onOpen={setOpen} onOpen={setOpen}
onWidthChange={setSidebarWidth} onWidthChange={setWidth}
minWidth={MIN_SIDEBAR_WIDTH} minWidth={MIN_SIDEBAR_WIDTH}
maxWidth={MAX_SIDEBAR_WIDTH} maxWidth={MAX_SIDEBAR_WIDTH}
> >

View File

@@ -3,6 +3,7 @@ import { assertExists } from '@blocksuite/global/utils';
import { atom } from 'jotai'; import { atom } from 'jotai';
import { selectAtom } from 'jotai/utils'; import { selectAtom } from 'jotai/utils';
import { copilotExtension } from './extensions/copilot';
import { framePanelExtension } from './extensions/frame'; import { framePanelExtension } from './extensions/frame';
import { outlineExtension } from './extensions/outline'; import { outlineExtension } from './extensions/outline';
import type { EditorExtension, EditorExtensionName } from './types'; import type { EditorExtension, EditorExtensionName } from './types';
@@ -12,6 +13,7 @@ import type { EditorExtension, EditorExtensionName } from './types';
export const extensions: EditorExtension[] = [ export const extensions: EditorExtension[] = [
outlineExtension, outlineExtension,
framePanelExtension, framePanelExtension,
copilotExtension,
]; ];
export interface EditorSidebarState { export interface EditorSidebarState {
@@ -30,8 +32,6 @@ const baseStateAtom = atom<EditorSidebarState>({
extensions: extensions, // todo: maybe should be dynamic (by feature flag?) 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 isOpenAtom = selectAtom(baseStateAtom, state => state.isOpen);
const resizingAtom = selectAtom(baseStateAtom, state => state.resizing); const resizingAtom = selectAtom(baseStateAtom, state => state.resizing);
const activeExtensionAtom = selectAtom( const activeExtensionAtom = selectAtom(
@@ -40,9 +40,10 @@ const activeExtensionAtom = selectAtom(
); );
const widthAtom = selectAtom(baseStateAtom, state => state.width); const widthAtom = selectAtom(baseStateAtom, state => state.width);
export const editorExtensionsAtom = selectAtom( export const editorExtensionsAtom = selectAtom(baseStateAtom, state =>
baseStateAtom, state.extensions.filter(e => {
state => state.extensions return e.name !== 'copilot' || runtimeConfig.enableCopilot;
})
); );
// get/set sidebar open state // get/set sidebar open state

View File

@@ -1,11 +1,11 @@
import { useAtomValue } from 'jotai'; import { useAtomValue } from 'jotai';
import { editorSidebarStateAtom } from './atoms'; import { editorSidebarActiveExtensionAtom } from './atoms';
import * as styles from './editor-sidebar.css'; import * as styles from './editor-sidebar.css';
export const EditorSidebar = () => { export const EditorSidebar = () => {
const sidebarState = useAtomValue(editorSidebarStateAtom); const activeExtension = useAtomValue(editorSidebarActiveExtensionAtom);
const Component = sidebarState.activeExtension?.Component; const Component = activeExtension?.Component;
return <div className={styles.root}>{Component ? <Component /> : null}</div>; return <div className={styles.root}>{Component ? <Component /> : null}</div>;
}; };

View File

@@ -0,0 +1,7 @@
import { style } from '@vanilla-extract/css';
export const root = style({
display: 'flex',
height: '100%',
width: '100%',
});

View File

@@ -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<CopilotPanel | null>(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 <div className={styles.root} ref={onRefChange} />;
};
export const copilotExtension: EditorExtension = {
name: 'copilot',
icon: <AiIcon />,
Component: EditorCopilotPanel,
};

View File

@@ -1,4 +1,4 @@
export type EditorExtensionName = 'outline' | 'frame'; export type EditorExtensionName = 'outline' | 'frame' | 'copilot';
export interface EditorExtension { export interface EditorExtension {
name: EditorExtensionName; name: EditorExtensionName;