mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-21 16:26:58 +08:00
feat(core): ai poc (#5317)
This commit is contained in:
1
packages/common/env/src/global.ts
vendored
1
packages/common/env/src/global.ts
vendored
@@ -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(),
|
||||||
|
|||||||
@@ -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') {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
export const root = style({
|
||||||
|
display: 'flex',
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
});
|
||||||
@@ -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,
|
||||||
|
};
|
||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user