feat(core): impl AI switch (#8018)

close PD-1658

https://github.com/user-attachments/assets/2f3d1d26-1879-4d95-b80c-7c0965cefbd0
This commit is contained in:
JimmFly
2024-09-04 08:44:05 +00:00
parent eb16c273ee
commit f688c057eb
18 changed files with 180 additions and 59 deletions
@@ -26,15 +26,15 @@ import { assertInstanceOf } from '@blocksuite/global/utils';
import { literal, unsafeStatic } from 'lit/static-html.js'; import { literal, unsafeStatic } from 'lit/static-html.js';
import { buildAIPanelConfig } from './ai-panel'; import { buildAIPanelConfig } from './ai-panel';
import { setupCodeToolbarEntry } from './entries/code-toolbar/setup-code-toolbar'; import { setupCodeToolbarAIEntry } from './entries/code-toolbar/setup-code-toolbar';
import { import {
setupEdgelessCopilot, setupEdgelessCopilot,
setupEdgelessElementToolbarEntry, setupEdgelessElementToolbarAIEntry,
} from './entries/edgeless/index'; } from './entries/edgeless/index';
import { setupFormatBarEntry } from './entries/format-bar/setup-format-bar'; import { setupFormatBarAIEntry } from './entries/format-bar/setup-format-bar';
import { setupImageToolbarEntry } from './entries/image-toolbar/setup-image-toolbar'; import { setupImageToolbarAIEntry } from './entries/image-toolbar/setup-image-toolbar';
import { setupSlashMenuEntry } from './entries/slash-menu/setup-slash-menu'; import { setupSlashMenuAIEntry } from './entries/slash-menu/setup-slash-menu';
import { setupSpaceEntry } from './entries/space/setup-space'; import { setupSpaceAIEntry } from './entries/space/setup-space';
class AIPageRootWatcher extends BlockServiceWatcher { class AIPageRootWatcher extends BlockServiceWatcher {
static override readonly flavour = 'affine:page'; static override readonly flavour = 'affine:page';
@@ -45,15 +45,15 @@ class AIPageRootWatcher extends BlockServiceWatcher {
if (view.component instanceof AffineAIPanelWidget) { if (view.component instanceof AffineAIPanelWidget) {
view.component.style.width = '630px'; view.component.style.width = '630px';
view.component.config = buildAIPanelConfig(view.component); view.component.config = buildAIPanelConfig(view.component);
setupSpaceEntry(view.component); setupSpaceAIEntry(view.component);
} }
if (view.component instanceof AffineFormatBarWidget) { if (view.component instanceof AffineFormatBarWidget) {
setupFormatBarEntry(view.component); setupFormatBarAIEntry(view.component);
} }
if (view.component instanceof AffineSlashMenuWidget) { if (view.component instanceof AffineSlashMenuWidget) {
setupSlashMenuEntry(view.component); setupSlashMenuAIEntry(view.component);
} }
}); });
} }
@@ -85,7 +85,7 @@ class AIEdgelessRootWatcher extends BlockServiceWatcher {
if (view.component instanceof AffineAIPanelWidget) { if (view.component instanceof AffineAIPanelWidget) {
view.component.style.width = '430px'; view.component.style.width = '430px';
view.component.config = buildAIPanelConfig(view.component); view.component.config = buildAIPanelConfig(view.component);
setupSpaceEntry(view.component); setupSpaceAIEntry(view.component);
} }
if (view.component instanceof EdgelessCopilotWidget) { if (view.component instanceof EdgelessCopilotWidget) {
@@ -93,15 +93,15 @@ class AIEdgelessRootWatcher extends BlockServiceWatcher {
} }
if (view.component instanceof EdgelessElementToolbarWidget) { if (view.component instanceof EdgelessElementToolbarWidget) {
setupEdgelessElementToolbarEntry(view.component); setupEdgelessElementToolbarAIEntry(view.component);
} }
if (view.component instanceof AffineFormatBarWidget) { if (view.component instanceof AffineFormatBarWidget) {
setupFormatBarEntry(view.component); setupFormatBarAIEntry(view.component);
} }
if (view.component instanceof AffineSlashMenuWidget) { if (view.component instanceof AffineSlashMenuWidget) {
setupSlashMenuEntry(view.component); setupSlashMenuAIEntry(view.component);
} }
}); });
} }
@@ -166,7 +166,7 @@ class AICodeBlockWatcher extends BlockServiceWatcher {
const service = this.blockService; const service = this.blockService;
service.specSlots.widgetConnected.on(view => { service.specSlots.widgetConnected.on(view => {
if (view.component instanceof AffineCodeToolbarWidget) { if (view.component instanceof AffineCodeToolbarWidget) {
setupCodeToolbarEntry(view.component); setupCodeToolbarAIEntry(view.component);
} }
}); });
} }
@@ -184,7 +184,7 @@ class AIImageBlockWatcher extends BlockServiceWatcher {
super.mounted(); super.mounted();
this.blockService.specSlots.widgetConnected.on(view => { this.blockService.specSlots.widgetConnected.on(view => {
if (view.component instanceof AffineImageToolbarWidget) { if (view.component instanceof AffineImageToolbarWidget) {
setupImageToolbarEntry(view.component); setupImageToolbarAIEntry(view.component);
} }
}); });
} }
@@ -12,7 +12,7 @@ const buttonOptions: AskAIButtonOptions = {
import type { AskAIButtonOptions } from '../../_common/components/ask-ai-button'; import type { AskAIButtonOptions } from '../../_common/components/ask-ai-button';
import { buildAICodeItemGroups } from '../../_common/config'; import { buildAICodeItemGroups } from '../../_common/config';
export function setupCodeToolbarEntry(codeToolbar: AffineCodeToolbarWidget) { export function setupCodeToolbarAIEntry(codeToolbar: AffineCodeToolbarWidget) {
codeToolbar.addPrimaryItems([ codeToolbar.addPrimaryItems([
{ {
type: 'ask-ai', type: 'ask-ai',
@@ -17,7 +17,7 @@ export function setupEdgelessCopilot(widget: EdgelessCopilotWidget) {
widget.groups = edgelessActionGroups; widget.groups = edgelessActionGroups;
} }
export function setupEdgelessElementToolbarEntry( export function setupEdgelessElementToolbarAIEntry(
widget: EdgelessElementToolbarWidget widget: EdgelessElementToolbarWidget
) { ) {
widget.registerEntry({ widget.registerEntry({
@@ -8,7 +8,7 @@ import { html, type TemplateResult } from 'lit';
import { AIItemGroups } from '../../_common/config'; import { AIItemGroups } from '../../_common/config';
export function setupFormatBarEntry(formatBar: AffineFormatBarWidget) { export function setupFormatBarAIEntry(formatBar: AffineFormatBarWidget) {
toolbarDefaultConfig(formatBar); toolbarDefaultConfig(formatBar);
formatBar.addRawConfigItems( formatBar.addRawConfigItems(
[ [
@@ -13,7 +13,9 @@ const buttonOptions: AskAIButtonOptions = {
panelWidth: 300, panelWidth: 300,
}; };
export function setupImageToolbarEntry(imageToolbar: AffineImageToolbarWidget) { export function setupImageToolbarAIEntry(
imageToolbar: AffineImageToolbarWidget
) {
imageToolbar.addPrimaryItems( imageToolbar.addPrimaryItems(
[ [
{ {
@@ -20,7 +20,7 @@ import { AIItemGroups } from '../../_common/config';
import { handleInlineAskAIAction } from '../../actions/doc-handler'; import { handleInlineAskAIAction } from '../../actions/doc-handler';
import { AIProvider } from '../../provider'; import { AIProvider } from '../../provider';
export function setupSlashMenuEntry(slashMenu: AffineSlashMenuWidget) { export function setupSlashMenuAIEntry(slashMenu: AffineSlashMenuWidget) {
const AIItems = AIItemGroups.map(group => group.items).flat(); const AIItems = AIItemGroups.map(group => group.items).flat();
const iconWrapper = (icon: AIItemConfig['icon']) => { const iconWrapper = (icon: AIItemConfig['icon']) => {
@@ -3,7 +3,7 @@ import type { AffineAIPanelWidget } from '@blocksuite/blocks';
import { handleInlineAskAIAction } from '../../actions/doc-handler'; import { handleInlineAskAIAction } from '../../actions/doc-handler';
import { AIProvider } from '../../provider'; import { AIProvider } from '../../provider';
export function setupSpaceEntry(panel: AffineAIPanelWidget) { export function setupSpaceAIEntry(panel: AffineAIPanelWidget) {
panel.handleEvent('keyDown', ctx => { panel.handleEvent('keyDown', ctx => {
const host = panel.host; const host = panel.host;
const keyboardState = ctx.get('keyboardState'); const keyboardState = ctx.get('keyboardState');
@@ -1,3 +1,5 @@
import { EditorSettingService } from '@affine/core/modules/editor-settting';
import { useLiveData, useService } from '@toeverything/infra';
import { Suspense, useCallback, useEffect, useState } from 'react'; import { Suspense, useCallback, useEffect, useState } from 'react';
import { AIOnboardingEdgeless } from './edgeless.dialog'; import { AIOnboardingEdgeless } from './edgeless.dialog';
@@ -28,19 +30,29 @@ const useDismiss = (key: AIOnboardingType) => {
export const WorkspaceAIOnboarding = () => { export const WorkspaceAIOnboarding = () => {
const [dismissGeneral] = useDismiss(AIOnboardingType.GENERAL); const [dismissGeneral] = useDismiss(AIOnboardingType.GENERAL);
const [dismissLocal] = useDismiss(AIOnboardingType.LOCAL); const [dismissLocal] = useDismiss(AIOnboardingType.LOCAL);
const editorSettingService = useService(EditorSettingService);
const enableAI = useLiveData(
editorSettingService.editorSetting.settings$.map(s => s.enableAI)
);
return ( return (
<Suspense> <Suspense>
{dismissGeneral ? null : <AIOnboardingGeneral />} {!enableAI || dismissGeneral ? null : <AIOnboardingGeneral />}
{dismissLocal ? null : <AIOnboardingLocal />} {!enableAI || dismissLocal ? null : <AIOnboardingLocal />}
</Suspense> </Suspense>
); );
}; };
export const PageAIOnboarding = () => { export const PageAIOnboarding = () => {
const [dismissEdgeless] = useDismiss(AIOnboardingType.EDGELESS); const [dismissEdgeless] = useDismiss(AIOnboardingType.EDGELESS);
const editorSettingService = useService(EditorSettingService);
const enableAI = useLiveData(
editorSettingService.editorSetting.settings$.map(s => s.enableAI)
);
return ( return (
<Suspense>{dismissEdgeless ? null : <AIOnboardingEdgeless />}</Suspense> <Suspense>
{!enableAI || dismissEdgeless ? null : <AIOnboardingEdgeless />}
</Suspense>
); );
}; };
@@ -8,6 +8,7 @@ import {
type RadioItem, type RadioItem,
Scrollable, Scrollable,
Switch, Switch,
useConfirmModal,
} from '@affine/component'; } from '@affine/component';
import { import {
SettingRow, SettingRow,
@@ -393,16 +394,51 @@ export const SpellCheckSettings = () => {
); );
}; };
const AISettings = () => {
const t = useI18n();
const { openConfirmModal } = useConfirmModal();
const { editorSettingService } = useServices({ EditorSettingService });
const settings = useLiveData(editorSettingService.editorSetting.settings$);
const onAIChange = useCallback(
(checked: boolean) => {
editorSettingService.editorSetting.set('enableAI', checked);
},
[editorSettingService]
);
const onToggleAI = useCallback(
(checked: boolean) => {
openConfirmModal({
title: checked ? 'Enable AI?' : 'Disable AI?',
description: `Are you sure you want to ${checked ? 'enable' : 'disable'} AI?`,
confirmText: checked ? 'Enable' : 'Disable',
cancelText: 'Cancel',
onConfirm: () => onAIChange(checked),
confirmButtonOptions: {
variant: checked ? 'primary' : 'error',
},
});
},
[openConfirmModal, onAIChange]
);
return (
<SettingRow
name={t['com.affine.settings.editorSettings.general.ai.title']()}
desc={t['com.affine.settings.editorSettings.general.ai.description']()}
>
<Switch checked={settings.enableAI} onChange={onToggleAI} />
</SettingRow>
);
};
export const General = () => { export const General = () => {
const t = useI18n(); const t = useI18n();
return ( return (
<SettingWrapper title={t['com.affine.settings.editorSettings.general']()}> <SettingWrapper title={t['com.affine.settings.editorSettings.general']()}>
<SettingRow <AISettings />
name={t['com.affine.settings.editorSettings.general.ai.title']()}
desc={t['com.affine.settings.editorSettings.general.ai.description']()}
>
<Switch />
</SettingRow>
<FontFamilySettings /> <FontFamilySettings />
<CustomFontFamilySettings /> <CustomFontFamilySettings />
<NewDocDefaultModeSettings /> <NewDocDefaultModeSettings />
@@ -15,6 +15,7 @@ import {
useFramework, useFramework,
useLiveData, useLiveData,
useService, useService,
useServices,
} from '@toeverything/infra'; } from '@toeverything/infra';
import React, { import React, {
forwardRef, forwardRef,
@@ -65,9 +66,13 @@ interface BlocksuiteEditorProps {
const usePatchSpecs = (page: Doc, shared: boolean, mode: DocMode) => { const usePatchSpecs = (page: Doc, shared: boolean, mode: DocMode) => {
const [reactToLit, portals] = useLitPortalFactory(); const [reactToLit, portals] = useLitPortalFactory();
const peekViewService = useService(PeekViewService); const { peekViewService, docService, docsService, editorSettingService } =
const docService = useService(DocService); useServices({
const docsService = useService(DocsService); PeekViewService,
DocService,
DocsService,
EditorSettingService,
});
const framework = useFramework(); const framework = useFramework();
const referenceRenderer: ReferenceReactRenderer = useMemo(() => { const referenceRenderer: ReferenceReactRenderer = useMemo(() => {
return function customReference(reference) { return function customReference(reference) {
@@ -89,10 +94,12 @@ const usePatchSpecs = (page: Doc, shared: boolean, mode: DocMode) => {
}, [mode, page.collection]); }, [mode, page.collection]);
const specs = useMemo(() => { const specs = useMemo(() => {
const enableAI =
editorSettingService.editorSetting.settings$.value.enableAI;
return mode === 'edgeless' return mode === 'edgeless'
? createEdgelessModeSpecs(framework) ? createEdgelessModeSpecs(framework, enableAI)
: createPageModeSpecs(framework); : createPageModeSpecs(framework, enableAI);
}, [mode, framework]); }, [editorSettingService, mode, framework]);
const confirmModal = useConfirmModal(); const confirmModal = useConfirmModal();
const patchedSpecs = useMemo(() => { const patchedSpecs = useMemo(() => {
@@ -6,6 +6,7 @@ import {
import type { ExtensionType } from '@blocksuite/block-std'; import type { ExtensionType } from '@blocksuite/block-std';
import { import {
BookmarkBlockSpec, BookmarkBlockSpec,
CodeBlockSpec,
DatabaseBlockSpec, DatabaseBlockSpec,
DataViewBlockSpec, DataViewBlockSpec,
DividerBlockSpec, DividerBlockSpec,
@@ -16,12 +17,15 @@ import {
EmbedLoomBlockSpec, EmbedLoomBlockSpec,
EmbedSyncedDocBlockSpec, EmbedSyncedDocBlockSpec,
EmbedYoutubeBlockSpec, EmbedYoutubeBlockSpec,
ImageBlockSpec,
ListBlockSpec, ListBlockSpec,
ParagraphBlockSpec,
} from '@blocksuite/blocks'; } from '@blocksuite/blocks';
import { AIChatBlockSpec } from '@blocksuite/presets';
import { CustomAttachmentBlockSpec } from './custom/attachment-block'; import { CustomAttachmentBlockSpec } from './custom/attachment-block';
export const CommonBlockSpecs: ExtensionType[] = [ const CommonBlockSpecs: ExtensionType[] = [
ListBlockSpec, ListBlockSpec,
DatabaseBlockSpec, DatabaseBlockSpec,
DataViewBlockSpec, DataViewBlockSpec,
@@ -36,7 +40,19 @@ export const CommonBlockSpecs: ExtensionType[] = [
EmbedLinkedDocBlockSpec, EmbedLinkedDocBlockSpec,
// special // special
CustomAttachmentBlockSpec, CustomAttachmentBlockSpec,
].flat();
export const DefaultBlockSpecs: ExtensionType[] = [
CodeBlockSpec,
ImageBlockSpec,
ParagraphBlockSpec,
...CommonBlockSpecs,
].flat();
export const AIBlockSpecs: ExtensionType[] = [
AICodeBlockSpec, AICodeBlockSpec,
AIImageBlockSpec, AIImageBlockSpec,
AIParagraphBlockSpec, AIParagraphBlockSpec,
AIChatBlockSpec,
...CommonBlockSpecs,
].flat(); ].flat();
@@ -14,7 +14,9 @@ import {
import type { RootService, TelemetryEventMap } from '@blocksuite/blocks'; import type { RootService, TelemetryEventMap } from '@blocksuite/blocks';
import { import {
AffineCanvasTextFonts, AffineCanvasTextFonts,
EdgelessRootBlockSpec,
EdgelessRootService, EdgelessRootService,
PageRootBlockSpec,
PageRootService, PageRootService,
} from '@blocksuite/blocks'; } from '@blocksuite/blocks';
import { type FrameworkProvider } from '@toeverything/infra'; import { type FrameworkProvider } from '@toeverything/infra';
@@ -55,11 +57,12 @@ function withAffineRootService(Service: typeof PageRootService) {
} }
export function createPageRootBlockSpec( export function createPageRootBlockSpec(
framework: FrameworkProvider framework: FrameworkProvider,
enableAI: boolean
): ExtensionType[] { ): ExtensionType[] {
const editorSettingService = framework.get(EditorSettingService); const editorSettingService = framework.get(EditorSettingService);
return [ return [
...AIPageRootBlockSpec, ...(enableAI ? AIPageRootBlockSpec : PageRootBlockSpec),
{ {
setup: di => { setup: di => {
di.override( di.override(
@@ -79,11 +82,12 @@ export function createPageRootBlockSpec(
} }
export function createEdgelessRootBlockSpec( export function createEdgelessRootBlockSpec(
framework: FrameworkProvider framework: FrameworkProvider,
enableAI: boolean
): ExtensionType[] { ): ExtensionType[] {
const editorSettingService = framework.get(EditorSettingService); const editorSettingService = framework.get(EditorSettingService);
return [ return [
...AIEdgelessRootBlockSpec, ...(enableAI ? AIEdgelessRootBlockSpec : EdgelessRootBlockSpec),
{ {
setup: di => { setup: di => {
di.override( di.override(
@@ -6,24 +6,23 @@ import {
EdgelessTextBlockSpec, EdgelessTextBlockSpec,
FrameBlockSpec, FrameBlockSpec,
} from '@blocksuite/blocks'; } from '@blocksuite/blocks';
import { AIChatBlockSpec } from '@blocksuite/presets';
import type { FrameworkProvider } from '@toeverything/infra'; import type { FrameworkProvider } from '@toeverything/infra';
import { CommonBlockSpecs } from './common'; import { AIBlockSpecs, DefaultBlockSpecs } from './common';
import { createEdgelessRootBlockSpec } from './custom/root-block'; import { createEdgelessRootBlockSpec } from './custom/root-block';
export function createEdgelessModeSpecs( export function createEdgelessModeSpecs(
framework: FrameworkProvider framework: FrameworkProvider,
enableAI: boolean
): ExtensionType[] { ): ExtensionType[] {
return [ return [
...CommonBlockSpecs, ...(enableAI ? AIBlockSpecs : DefaultBlockSpecs),
EdgelessSurfaceBlockSpec, EdgelessSurfaceBlockSpec,
EdgelessSurfaceRefBlockSpec, EdgelessSurfaceRefBlockSpec,
FrameBlockSpec, FrameBlockSpec,
EdgelessTextBlockSpec, EdgelessTextBlockSpec,
EdgelessNoteBlockSpec, EdgelessNoteBlockSpec,
AIChatBlockSpec,
// special // special
createEdgelessRootBlockSpec(framework), createEdgelessRootBlockSpec(framework, enableAI),
].flat(); ].flat();
} }
@@ -4,22 +4,21 @@ import {
PageSurfaceBlockSpec, PageSurfaceBlockSpec,
PageSurfaceRefBlockSpec, PageSurfaceRefBlockSpec,
} from '@blocksuite/blocks'; } from '@blocksuite/blocks';
import { AIChatBlockSpec } from '@blocksuite/presets';
import { type FrameworkProvider } from '@toeverything/infra'; import { type FrameworkProvider } from '@toeverything/infra';
import { CommonBlockSpecs } from './common'; import { AIBlockSpecs, DefaultBlockSpecs } from './common';
import { createPageRootBlockSpec } from './custom/root-block'; import { createPageRootBlockSpec } from './custom/root-block';
export function createPageModeSpecs( export function createPageModeSpecs(
framework: FrameworkProvider framework: FrameworkProvider,
enableAI: boolean
): ExtensionType[] { ): ExtensionType[] {
return [ return [
...CommonBlockSpecs, ...(enableAI ? AIBlockSpecs : DefaultBlockSpecs),
PageSurfaceBlockSpec, PageSurfaceBlockSpec,
PageSurfaceRefBlockSpec, PageSurfaceRefBlockSpec,
NoteBlockSpec, NoteBlockSpec,
AIChatBlockSpec,
// special // special
createPageRootBlockSpec(framework), createPageRootBlockSpec(framework, enableAI),
].flat(); ].flat();
} }
@@ -29,6 +29,7 @@ export const fontStyleOptions = [
}[]; }[];
const AffineEditorSettingSchema = z.object({ const AffineEditorSettingSchema = z.object({
enableAI: z.boolean().default(true),
fontFamily: z.enum(['Sans', 'Serif', 'Mono', 'Custom']).default('Sans'), fontFamily: z.enum(['Sans', 'Serif', 'Mono', 'Custom']).default('Sans'),
customFontFamily: z.string().default(''), customFontFamily: z.string().default(''),
newDocDefaultMode: z.enum(['edgeless', 'page']).default('page'), newDocDefaultMode: z.enum(['edgeless', 'page']).default('page'),
@@ -7,6 +7,7 @@ import { EditorOutlineViewer } from '@affine/core/components/blocksuite/outline-
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper'; import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
import { useDocMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta'; import { useDocMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta';
import { EditorService } from '@affine/core/modules/editor'; import { EditorService } from '@affine/core/modules/editor';
import { EditorSettingService } from '@affine/core/modules/editor-settting';
import { RecentDocsService } from '@affine/core/modules/quicksearch'; import { RecentDocsService } from '@affine/core/modules/quicksearch';
import { ViewService } from '@affine/core/modules/workbench/services/view'; import { ViewService } from '@affine/core/modules/workbench/services/view';
import type { PageRootService } from '@blocksuite/blocks'; import type { PageRootService } from '@blocksuite/blocks';
@@ -71,6 +72,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
docService, docService,
workspaceService, workspaceService,
globalContextService, globalContextService,
editorSettingService,
} = useServices({ } = useServices({
WorkbenchService, WorkbenchService,
ViewService, ViewService,
@@ -78,6 +80,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
DocService, DocService,
WorkspaceService, WorkspaceService,
GlobalContextService, GlobalContextService,
EditorSettingService,
}); });
const workbench = workbenchService.workbench; const workbench = workbenchService.workbench;
const editor = editorService.editor; const editor = editorService.editor;
@@ -307,9 +310,15 @@ const DetailPageImpl = memo(function DetailPageImpl() {
</div> </div>
</ViewBody> </ViewBody>
<ViewSidebarTab tabId="chat" icon={<AiIcon />} unmountOnInactive={false}> {editorSettingService.editorSetting.settings$.value.enableAI && (
<EditorChatPanel editor={editorContainer} ref={chatPanelRef} /> <ViewSidebarTab
</ViewSidebarTab> tabId="chat"
icon={<AiIcon />}
unmountOnInactive={false}
>
<EditorChatPanel editor={editorContainer} ref={chatPanelRef} />
</ViewSidebarTab>
)}
<ViewSidebarTab tabId="journal" icon={<TodayIcon />}> <ViewSidebarTab tabId="journal" icon={<TodayIcon />}>
<EditorJournalPanel /> <EditorJournalPanel />
@@ -1,4 +1,4 @@
import { NotificationCenter, notify } from '@affine/component'; import { ConfirmModal, NotificationCenter, notify } from '@affine/component';
import { events } from '@affine/electron-api'; import { events } from '@affine/electron-api';
import { WorkspaceFlavour } from '@affine/env/workspace'; import { WorkspaceFlavour } from '@affine/env/workspace';
import { import {
@@ -10,7 +10,7 @@ import {
} from '@toeverything/infra'; } from '@toeverything/infra';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import type { ReactElement } from 'react'; import type { ReactElement } from 'react';
import { useCallback, useEffect } from 'react'; import { useCallback, useEffect, useState } from 'react';
import type { SettingAtom } from '../atoms'; import type { SettingAtom } from '../atoms';
import { openSettingModalAtom, openSignOutModalAtom } from '../atoms'; import { openSettingModalAtom, openSignOutModalAtom } from '../atoms';
@@ -31,6 +31,7 @@ import { useAsyncCallback } from '../hooks/affine-async-hooks';
import { useNavigateHelper } from '../hooks/use-navigate-helper'; import { useNavigateHelper } from '../hooks/use-navigate-helper';
import { AuthService } from '../modules/cloud/services/auth'; import { AuthService } from '../modules/cloud/services/auth';
import { CreateWorkspaceDialogProvider } from '../modules/create-workspace'; import { CreateWorkspaceDialogProvider } from '../modules/create-workspace';
import { EditorSettingService } from '../modules/editor-settting';
import { FindInPageModal } from '../modules/find-in-page/view/find-in-page-modal'; import { FindInPageModal } from '../modules/find-in-page/view/find-in-page-modal';
import { ImportTemplateDialogProvider } from '../modules/import-template'; import { ImportTemplateDialogProvider } from '../modules/import-template';
import { PeekViewManagerModal } from '../modules/peek-view'; import { PeekViewManagerModal } from '../modules/peek-view';
@@ -183,6 +184,40 @@ export const SignOutConfirmModal = () => {
); );
}; };
export const AIReloadConfirmModal = () => {
const editorSettingService = useService(EditorSettingService);
const enableAI = useLiveData(
editorSettingService.editorSetting.settings$
).enableAI;
const [aiState] = useState(enableAI);
const [open, setOpen] = useState(false);
useEffect(() => {
setOpen(enableAI !== aiState);
}, [aiState, enableAI]);
const onConfirm = useCallback(() => {
window.location.reload();
}, []);
return (
<ConfirmModal
open={open}
onOpenChange={setOpen}
onConfirm={onConfirm}
confirmButtonOptions={{
variant: 'primary',
}}
title={'You need to reload the page'}
description={
'AI settings have been updated. Please reload the page to apply the changes.'
}
cancelText={'Cancel'}
confirmText={'Reload'}
/>
);
};
export const AllWorkspaceModals = (): ReactElement => { export const AllWorkspaceModals = (): ReactElement => {
return ( return (
<> <>
@@ -191,6 +226,7 @@ export const AllWorkspaceModals = (): ReactElement => {
<CreateWorkspaceDialogProvider /> <CreateWorkspaceDialogProvider />
<AuthModal /> <AuthModal />
<SignOutConfirmModal /> <SignOutConfirmModal />
<AIReloadConfirmModal />
</> </>
); );
}; };
+1 -1
View File
@@ -1225,7 +1225,7 @@
"com.affine.settings.auto-check-description": "If enabled, it will automatically check for new versions at regular intervals.", "com.affine.settings.auto-check-description": "If enabled, it will automatically check for new versions at regular intervals.",
"com.affine.settings.auto-download-description": "If enabled, new versions will be automatically downloaded to the current device.", "com.affine.settings.auto-download-description": "If enabled, new versions will be automatically downloaded to the current device.",
"com.affine.settings.editorSettings": "Editor", "com.affine.settings.editorSettings": "Editor",
"com.affine.settings.editorSettings.title": "Editor Settings", "com.affine.settings.editorSettings.title": "Editor settings",
"com.affine.settings.editorSettings.subtitle": "Configure your own editor.", "com.affine.settings.editorSettings.subtitle": "Configure your own editor.",
"com.affine.settings.editorSettings.general": "General", "com.affine.settings.editorSettings.general": "General",
"com.affine.settings.editorSettings.general.ai.title": "AFFiNE AI", "com.affine.settings.editorSettings.general.ai.title": "AFFiNE AI",