📄(frontend) remove AI feature when MIT
AI feature is under AGPL license, so it is removed when the project is under MIT license. NEXT_PUBLIC_PUBLISH_AS_MIT manage this.
This commit is contained in:
@@ -487,7 +487,16 @@ test.describe('Doc Editor', () => {
|
|||||||
await expect(editor.getByText('Hello World')).toBeVisible();
|
await expect(editor.getByText('Hello World')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it checks the AI buttons feature', async ({ page, browserName }) => {
|
/**
|
||||||
|
* We have to skip this test for the CI for now, we cannot assert
|
||||||
|
* it because of `process.env.NEXT_PUBLIC_PUBLISH_AS_MIT` that is set
|
||||||
|
* at build time.
|
||||||
|
* It can be interesting to keep it for local tests.
|
||||||
|
*/
|
||||||
|
test.skip('it checks the AI buttons feature', async ({
|
||||||
|
page,
|
||||||
|
browserName,
|
||||||
|
}) => {
|
||||||
await page.route(/.*\/ai-translate\//, async (route) => {
|
await page.route(/.*\/ai-translate\//, async (route) => {
|
||||||
const request = route.request();
|
const request = route.request();
|
||||||
if (request.method().includes('POST')) {
|
if (request.method().includes('POST')) {
|
||||||
@@ -540,7 +549,13 @@ test.describe('Doc Editor', () => {
|
|||||||
await expect(editor.getByText('Bonjour le monde')).toBeVisible();
|
await expect(editor.getByText('Bonjour le monde')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it checks the AI buttons', async ({ page, browserName }) => {
|
/**
|
||||||
|
* We have to skip this test for the CI for now, we cannot assert
|
||||||
|
* it because of `process.env.NEXT_PUBLIC_PUBLISH_AS_MIT` that is set
|
||||||
|
* at build time.
|
||||||
|
* It can be interesting to keep it for local tests.
|
||||||
|
*/
|
||||||
|
test.skip('it checks the AI buttons', async ({ page, browserName }) => {
|
||||||
await page.route(/.*\/ai-translate\//, async (route) => {
|
await page.route(/.*\/ai-translate\//, async (route) => {
|
||||||
const request = route.request();
|
const request = route.request();
|
||||||
if (request.method().includes('POST')) {
|
if (request.method().includes('POST')) {
|
||||||
@@ -593,12 +608,18 @@ test.describe('Doc Editor', () => {
|
|||||||
await expect(editor.getByText('Bonjour le monde')).toBeVisible();
|
await expect(editor.getByText('Bonjour le monde')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We have to skip this test for the CI for now, we cannot assert
|
||||||
|
* it because of `process.env.NEXT_PUBLIC_PUBLISH_AS_MIT` that is set
|
||||||
|
* at build time.
|
||||||
|
* It can be interesting to keep it for local tests.
|
||||||
|
*/
|
||||||
[
|
[
|
||||||
{ ai_transform: false, ai_translate: false },
|
{ ai_transform: false, ai_translate: false },
|
||||||
{ ai_transform: true, ai_translate: false },
|
{ ai_transform: true, ai_translate: false },
|
||||||
{ ai_transform: false, ai_translate: true },
|
{ ai_transform: false, ai_translate: true },
|
||||||
].forEach(({ ai_transform, ai_translate }) => {
|
].forEach(({ ai_transform, ai_translate }) => {
|
||||||
test(`it checks AI buttons when can transform is at "${ai_transform}" and can translate is at "${ai_translate}"`, async ({
|
test.skip(`it checks AI buttons when can transform is at "${ai_transform}" and can translate is at "${ai_translate}"`, async ({
|
||||||
page,
|
page,
|
||||||
browserName,
|
browserName,
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -1,3 +1,28 @@
|
|||||||
export * from './AIMenu';
|
/**
|
||||||
export * from './AIToolbarButton';
|
* To import AI modules you must import from the index file.
|
||||||
export * from './useAI';
|
* This is to ensure that the AI modules are only loaded when
|
||||||
|
* the application is not published as MIT.
|
||||||
|
*/
|
||||||
|
import * as XLAI from '@blocknote/xl-ai';
|
||||||
|
import * as localesAI from '@blocknote/xl-ai/locales';
|
||||||
|
|
||||||
|
import * as AIMenu from './AIMenu';
|
||||||
|
import * as AIToolbarButton from './AIToolbarButton';
|
||||||
|
import * as useAI from './useAI';
|
||||||
|
|
||||||
|
let modulesAI = undefined;
|
||||||
|
if (process.env.NEXT_PUBLIC_PUBLISH_AS_MIT === 'false') {
|
||||||
|
modulesAI = {
|
||||||
|
...XLAI,
|
||||||
|
...AIToolbarButton,
|
||||||
|
...AIMenu,
|
||||||
|
localesAI: localesAI,
|
||||||
|
...useAI,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModulesAI = typeof XLAI &
|
||||||
|
typeof AIToolbarButton &
|
||||||
|
typeof AIMenu & { localesAI: typeof localesAI } & typeof useAI;
|
||||||
|
|
||||||
|
export default modulesAI as ModulesAI;
|
||||||
|
|||||||
@@ -12,9 +12,6 @@ import * as locales from '@blocknote/core/locales';
|
|||||||
import { BlockNoteView } from '@blocknote/mantine';
|
import { BlockNoteView } from '@blocknote/mantine';
|
||||||
import '@blocknote/mantine/style.css';
|
import '@blocknote/mantine/style.css';
|
||||||
import { useCreateBlockNote } from '@blocknote/react';
|
import { useCreateBlockNote } from '@blocknote/react';
|
||||||
import { AIMenuController } from '@blocknote/xl-ai';
|
|
||||||
import { en as aiEn } from '@blocknote/xl-ai/locales';
|
|
||||||
import '@blocknote/xl-ai/style.css';
|
|
||||||
import { HocuspocusProvider } from '@hocuspocus/provider';
|
import { HocuspocusProvider } from '@hocuspocus/provider';
|
||||||
import { useEffect, useMemo, useRef } from 'react';
|
import { useEffect, useMemo, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -39,7 +36,7 @@ import { cssEditor } from '../styles';
|
|||||||
import { DocsBlockNoteEditor } from '../types';
|
import { DocsBlockNoteEditor } from '../types';
|
||||||
import { randomColor } from '../utils';
|
import { randomColor } from '../utils';
|
||||||
|
|
||||||
import { AIMenu, useAI } from './AI';
|
import BlockNoteAI from './AI';
|
||||||
import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu';
|
import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu';
|
||||||
import { BlockNoteToolbar } from './BlockNoteToolBar/BlockNoteToolbar';
|
import { BlockNoteToolbar } from './BlockNoteToolBar/BlockNoteToolbar';
|
||||||
import { cssComments, useComments } from './comments/';
|
import { cssComments, useComments } from './comments/';
|
||||||
@@ -49,6 +46,11 @@ import {
|
|||||||
PdfBlock,
|
PdfBlock,
|
||||||
UploadLoaderBlock,
|
UploadLoaderBlock,
|
||||||
} from './custom-blocks';
|
} from './custom-blocks';
|
||||||
|
|
||||||
|
const AIMenu = BlockNoteAI?.AIMenu;
|
||||||
|
const AIMenuController = BlockNoteAI?.AIMenuController;
|
||||||
|
const useAI = BlockNoteAI?.useAI;
|
||||||
|
const localesAI = BlockNoteAI?.localesAI;
|
||||||
import {
|
import {
|
||||||
InterlinkingLinkInlineContent,
|
InterlinkingLinkInlineContent,
|
||||||
InterlinkingSearchInlineContent,
|
InterlinkingSearchInlineContent,
|
||||||
@@ -87,7 +89,6 @@ interface BlockNoteEditorProps {
|
|||||||
export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { setEditor } = useEditorStore();
|
const { setEditor } = useEditorStore();
|
||||||
const { t } = useTranslation();
|
|
||||||
const { themeTokens } = useCunninghamTheme();
|
const { themeTokens } = useCunninghamTheme();
|
||||||
const { isSynced: isConnectedToCollabServer } = useProviderStore();
|
const { isSynced: isConnectedToCollabServer } = useProviderStore();
|
||||||
const refEditorContainer = useRef<HTMLDivElement>(null);
|
const refEditorContainer = useRef<HTMLDivElement>(null);
|
||||||
@@ -96,14 +97,14 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
|||||||
const showComments = canSeeComment;
|
const showComments = canSeeComment;
|
||||||
|
|
||||||
useSaveDoc(doc.id, provider.document, isConnectedToCollabServer);
|
useSaveDoc(doc.id, provider.document, isConnectedToCollabServer);
|
||||||
const { i18n } = useTranslation();
|
const { i18n, t } = useTranslation();
|
||||||
let lang = i18n.resolvedLanguage;
|
let lang = i18n.resolvedLanguage;
|
||||||
if (!lang || !(lang in locales)) {
|
if (!lang || !(lang in locales)) {
|
||||||
lang = 'en';
|
lang = 'en';
|
||||||
}
|
}
|
||||||
|
|
||||||
const { uploadFile, errorAttachment } = useUploadFile(doc.id);
|
const { uploadFile, errorAttachment } = useUploadFile(doc.id);
|
||||||
const aiExtension = useAI(doc.id);
|
const aiExtension = useAI?.(doc.id);
|
||||||
|
|
||||||
const collabName = user?.full_name || user?.email;
|
const collabName = user?.full_name || user?.email;
|
||||||
const cursorName = collabName || t('Anonymous');
|
const cursorName = collabName || t('Anonymous');
|
||||||
@@ -173,7 +174,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
|||||||
...(multiColumnLocales && {
|
...(multiColumnLocales && {
|
||||||
multi_column:
|
multi_column:
|
||||||
multiColumnLocales[lang as keyof typeof multiColumnLocales],
|
multiColumnLocales[lang as keyof typeof multiColumnLocales],
|
||||||
ai: aiEn,
|
ai: localesAI?.[lang as keyof typeof localesAI],
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
pasteHandler: ({ event, defaultPasteHandler }) => {
|
pasteHandler: ({ event, defaultPasteHandler }) => {
|
||||||
@@ -214,7 +215,15 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
|||||||
uploadFile,
|
uploadFile,
|
||||||
schema: blockNoteSchema,
|
schema: blockNoteSchema,
|
||||||
},
|
},
|
||||||
[cursorName, lang, provider, uploadFile, threadStore, resolveUsers],
|
[
|
||||||
|
aiExtension,
|
||||||
|
cursorName,
|
||||||
|
lang,
|
||||||
|
provider,
|
||||||
|
uploadFile,
|
||||||
|
threadStore,
|
||||||
|
resolveUsers,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
useHeadings(editor);
|
useHeadings(editor);
|
||||||
@@ -257,7 +266,9 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
|||||||
comments={showComments}
|
comments={showComments}
|
||||||
aria-label={t('Document editor')}
|
aria-label={t('Document editor')}
|
||||||
>
|
>
|
||||||
{aiExtension && <AIMenuController aiMenu={AIMenu} />}
|
{aiExtension && AIMenuController && AIMenu && (
|
||||||
|
<AIMenuController aiMenu={AIMenu} />
|
||||||
|
)}
|
||||||
<BlockNoteSuggestionMenu />
|
<BlockNoteSuggestionMenu />
|
||||||
<BlockNoteToolbar />
|
<BlockNoteToolbar />
|
||||||
</BlockNoteView>
|
</BlockNoteView>
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
useBlockNoteEditor,
|
useBlockNoteEditor,
|
||||||
useDictionary,
|
useDictionary,
|
||||||
} from '@blocknote/react';
|
} from '@blocknote/react';
|
||||||
import { getAISlashMenuItems } from '@blocknote/xl-ai';
|
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@@ -20,6 +19,7 @@ import {
|
|||||||
DocsStyleSchema,
|
DocsStyleSchema,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
|
import BlockNoteAI from './AI';
|
||||||
import {
|
import {
|
||||||
getCalloutReactSlashMenuItems,
|
getCalloutReactSlashMenuItems,
|
||||||
getPdfReactSlashMenuItems,
|
getPdfReactSlashMenuItems,
|
||||||
@@ -30,6 +30,8 @@ import XLMultiColumn from './xl-multi-column';
|
|||||||
const getMultiColumnSlashMenuItems =
|
const getMultiColumnSlashMenuItems =
|
||||||
XLMultiColumn?.getMultiColumnSlashMenuItems;
|
XLMultiColumn?.getMultiColumnSlashMenuItems;
|
||||||
|
|
||||||
|
const getAISlashMenuItems = BlockNoteAI?.getAISlashMenuItems;
|
||||||
|
|
||||||
export const BlockNoteSuggestionMenu = () => {
|
export const BlockNoteSuggestionMenu = () => {
|
||||||
const editor = useBlockNoteEditor<
|
const editor = useBlockNoteEditor<
|
||||||
DocsBlockSchema,
|
DocsBlockSchema,
|
||||||
@@ -54,7 +56,9 @@ export const BlockNoteSuggestionMenu = () => {
|
|||||||
getMultiColumnSlashMenuItems?.(editor) || [],
|
getMultiColumnSlashMenuItems?.(editor) || [],
|
||||||
getPdfReactSlashMenuItems(editor, t, fileBlocksName),
|
getPdfReactSlashMenuItems(editor, t, fileBlocksName),
|
||||||
getCalloutReactSlashMenuItems(editor, t, basicBlocksName),
|
getCalloutReactSlashMenuItems(editor, t, basicBlocksName),
|
||||||
conf?.AI_FEATURE_ENABLED ? getAISlashMenuItems(editor) : [],
|
conf?.AI_FEATURE_ENABLED && getAISlashMenuItems
|
||||||
|
? getAISlashMenuItems(editor)
|
||||||
|
: [],
|
||||||
);
|
);
|
||||||
|
|
||||||
const index = combinedMenu.findIndex(
|
const index = combinedMenu.findIndex(
|
||||||
|
|||||||
@@ -10,15 +10,17 @@ import { useTranslation } from 'react-i18next';
|
|||||||
|
|
||||||
import { useConfig } from '@/core/config/api';
|
import { useConfig } from '@/core/config/api';
|
||||||
|
|
||||||
import { AIToolbarButton } from '../AI/';
|
import BlockNoteAI from '../AI/';
|
||||||
|
import { AIGroupButton } from '../AI/AIButtonMIT';
|
||||||
import { CommentToolbarButton } from '../comments/CommentToolbarButton';
|
import { CommentToolbarButton } from '../comments/CommentToolbarButton';
|
||||||
import { getCalloutFormattingToolbarItems } from '../custom-blocks';
|
import { getCalloutFormattingToolbarItems } from '../custom-blocks';
|
||||||
|
|
||||||
import { AIGroupButton } from './AIButton';
|
|
||||||
import { FileDownloadButton } from './FileDownloadButton';
|
import { FileDownloadButton } from './FileDownloadButton';
|
||||||
import { MarkdownButton } from './MarkdownButton';
|
import { MarkdownButton } from './MarkdownButton';
|
||||||
import { ModalConfirmDownloadUnsafe } from './ModalConfirmDownloadUnsafe';
|
import { ModalConfirmDownloadUnsafe } from './ModalConfirmDownloadUnsafe';
|
||||||
|
|
||||||
|
const AIToolbarButton = BlockNoteAI?.AIToolbarButton;
|
||||||
|
|
||||||
export const BlockNoteToolbar = () => {
|
export const BlockNoteToolbar = () => {
|
||||||
const dict = useDictionary();
|
const dict = useDictionary();
|
||||||
const [confirmOpen, setIsConfirmOpen] = useState(false);
|
const [confirmOpen, setIsConfirmOpen] = useState(false);
|
||||||
@@ -70,14 +72,16 @@ export const BlockNoteToolbar = () => {
|
|||||||
const formattingToolbar = useCallback(() => {
|
const formattingToolbar = useCallback(() => {
|
||||||
return (
|
return (
|
||||||
<FormattingToolbar>
|
<FormattingToolbar>
|
||||||
{conf?.AI_FEATURE_ENABLED && <AIToolbarButton />}
|
{conf?.AI_FEATURE_ENABLED && AIToolbarButton && <AIToolbarButton />}
|
||||||
|
|
||||||
<CommentToolbarButton />
|
<CommentToolbarButton />
|
||||||
|
|
||||||
{toolbarItems}
|
{toolbarItems}
|
||||||
|
|
||||||
{/* Extra button to do some AI powered actions */}
|
{/* Extra button to do some AI powered actions - only if AIToolbarButton is not available because of MIT license */}
|
||||||
{conf?.AI_FEATURE_ENABLED && <AIGroupButton key="AIButton" />}
|
{conf?.AI_FEATURE_ENABLED && !AIToolbarButton && (
|
||||||
|
<AIGroupButton key="AIButton" />
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Extra button to convert from markdown to json */}
|
{/* Extra button to convert from markdown to json */}
|
||||||
<MarkdownButton key="customButton" />
|
<MarkdownButton key="customButton" />
|
||||||
|
|||||||
Reference in New Issue
Block a user