📄(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:
Anthony LC
2025-06-10 12:36:06 +02:00
parent 1a022450c6
commit 5c8fff01a5
6 changed files with 88 additions and 23 deletions

View File

@@ -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,
}) => { }) => {

View File

@@ -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;

View File

@@ -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>

View File

@@ -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(

View File

@@ -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" />