🛂(frontend) bind ai_proxy abilities with AI feature
Bind ai_proxy abilities to the AI feature. If ai_proxy is false, the AI feature will not be available.
This commit is contained in:
@@ -696,6 +696,53 @@ test.describe('Doc Editor', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test(`it checks ai_proxy ability`, async ({ page, browserName }) => {
|
||||||
|
await mockedDocument(page, {
|
||||||
|
accesses: [
|
||||||
|
{
|
||||||
|
id: 'b0df4343-c8bd-4c20-9ff6-fbf94fc94egg',
|
||||||
|
role: 'owner',
|
||||||
|
user: {
|
||||||
|
email: 'super@owner.com',
|
||||||
|
full_name: 'Super Owner',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
abilities: {
|
||||||
|
destroy: true, // Means owner
|
||||||
|
link_configuration: true,
|
||||||
|
ai_proxy: false,
|
||||||
|
accesses_manage: true,
|
||||||
|
accesses_view: true,
|
||||||
|
update: true,
|
||||||
|
partial_update: true,
|
||||||
|
retrieve: true,
|
||||||
|
},
|
||||||
|
link_reach: 'restricted',
|
||||||
|
link_role: 'editor',
|
||||||
|
created_at: '2021-09-01T09:00:00Z',
|
||||||
|
title: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const [randomDoc] = await createDoc(
|
||||||
|
page,
|
||||||
|
'doc-editor-ai-proxy',
|
||||||
|
browserName,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
|
||||||
|
await verifyDocName(page, randomDoc);
|
||||||
|
|
||||||
|
await page.locator('.bn-block-outer').last().fill('Hello World');
|
||||||
|
|
||||||
|
const editor = page.locator('.ProseMirror');
|
||||||
|
await editor.getByText('Hello').selectText();
|
||||||
|
|
||||||
|
await expect(page.getByRole('button', { name: 'Ask AI' })).toBeHidden();
|
||||||
|
await page.locator('.bn-block-outer').last().fill('/');
|
||||||
|
await expect(page.getByText('Write with AI')).toBeHidden();
|
||||||
|
});
|
||||||
|
|
||||||
test('it downloads unsafe files', async ({ page, browserName }) => {
|
test('it downloads unsafe files', async ({ page, browserName }) => {
|
||||||
const [randomDoc] = await createDoc(page, 'doc-editor', browserName, 1);
|
const [randomDoc] = await createDoc(page, 'doc-editor', browserName, 1);
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,14 @@ import { fetchAPI } from '@/api';
|
|||||||
import { useConfig } from '@/core';
|
import { useConfig } from '@/core';
|
||||||
import { Doc } from '@/docs/doc-management';
|
import { Doc } from '@/docs/doc-management';
|
||||||
|
|
||||||
export const useAI = (docId: Doc['id']) => {
|
export const useAI = (docId: Doc['id'], aiAllowed: boolean) => {
|
||||||
const conf = useConfig().data;
|
const conf = useConfig().data;
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
|
if (!aiAllowed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const extension = AIExtension({
|
const extension = AIExtension({
|
||||||
transport: new DefaultChatTransport({
|
transport: new DefaultChatTransport({
|
||||||
fetch: (input, init) => {
|
fetch: (input, init) => {
|
||||||
@@ -27,5 +31,5 @@ export const useAI = (docId: Doc['id']) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return extension;
|
return extension;
|
||||||
}, [conf?.AI_BOT, docId]);
|
}, [conf?.AI_BOT, docId, aiAllowed]);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import type { Awareness } from 'y-protocols/awareness';
|
|||||||
import * as Y from 'yjs';
|
import * as Y from 'yjs';
|
||||||
|
|
||||||
import { Box, TextErrors } from '@/components';
|
import { Box, TextErrors } from '@/components';
|
||||||
|
import { useConfig } from '@/core';
|
||||||
import { useCunninghamTheme } from '@/cunningham';
|
import { useCunninghamTheme } from '@/cunningham';
|
||||||
import { Doc, useProviderStore } from '@/docs/doc-management';
|
import { Doc, useProviderStore } from '@/docs/doc-management';
|
||||||
import { avatarUrlFromName, useAuth } from '@/features/auth';
|
import { avatarUrlFromName, useAuth } from '@/features/auth';
|
||||||
@@ -46,7 +47,6 @@ import {
|
|||||||
PdfBlock,
|
PdfBlock,
|
||||||
UploadLoaderBlock,
|
UploadLoaderBlock,
|
||||||
} from './custom-blocks';
|
} from './custom-blocks';
|
||||||
|
|
||||||
const AIMenu = BlockNoteAI?.AIMenu;
|
const AIMenu = BlockNoteAI?.AIMenu;
|
||||||
const AIMenuController = BlockNoteAI?.AIMenuController;
|
const AIMenuController = BlockNoteAI?.AIMenuController;
|
||||||
const useAI = BlockNoteAI?.useAI;
|
const useAI = BlockNoteAI?.useAI;
|
||||||
@@ -104,7 +104,9 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { uploadFile, errorAttachment } = useUploadFile(doc.id);
|
const { uploadFile, errorAttachment } = useUploadFile(doc.id);
|
||||||
const aiExtension = useAI?.(doc.id);
|
const conf = useConfig().data;
|
||||||
|
const aiAllowed = !!(conf?.AI_FEATURE_ENABLED && doc.abilities?.ai_proxy);
|
||||||
|
const aiExtension = useAI?.(doc.id, aiAllowed);
|
||||||
|
|
||||||
const collabName = user?.full_name || user?.email;
|
const collabName = user?.full_name || user?.email;
|
||||||
const cursorName = collabName || t('Anonymous');
|
const cursorName = collabName || t('Anonymous');
|
||||||
@@ -266,11 +268,11 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
|||||||
comments={showComments}
|
comments={showComments}
|
||||||
aria-label={t('Document editor')}
|
aria-label={t('Document editor')}
|
||||||
>
|
>
|
||||||
{aiExtension && AIMenuController && AIMenu && (
|
{aiAllowed && AIMenuController && AIMenu && (
|
||||||
<AIMenuController aiMenu={AIMenu} />
|
<AIMenuController aiMenu={AIMenu} />
|
||||||
)}
|
)}
|
||||||
<BlockNoteSuggestionMenu />
|
<BlockNoteSuggestionMenu aiAllowed={aiAllowed} />
|
||||||
<BlockNoteToolbar />
|
<BlockNoteToolbar aiAllowed={aiAllowed} />
|
||||||
</BlockNoteView>
|
</BlockNoteView>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
@@ -337,7 +339,7 @@ export const BlockNoteReader = ({
|
|||||||
slashMenu={false}
|
slashMenu={false}
|
||||||
comments={false}
|
comments={false}
|
||||||
>
|
>
|
||||||
<BlockNoteToolbar />
|
<BlockNoteToolbar aiAllowed={false} />
|
||||||
</BlockNoteView>
|
</BlockNoteView>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ import {
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { useConfig } from '@/core';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DocsBlockSchema,
|
DocsBlockSchema,
|
||||||
DocsInlineContentSchema,
|
DocsInlineContentSchema,
|
||||||
@@ -32,7 +30,11 @@ const getMultiColumnSlashMenuItems =
|
|||||||
|
|
||||||
const getAISlashMenuItems = BlockNoteAI?.getAISlashMenuItems;
|
const getAISlashMenuItems = BlockNoteAI?.getAISlashMenuItems;
|
||||||
|
|
||||||
export const BlockNoteSuggestionMenu = () => {
|
export const BlockNoteSuggestionMenu = ({
|
||||||
|
aiAllowed,
|
||||||
|
}: {
|
||||||
|
aiAllowed: boolean;
|
||||||
|
}) => {
|
||||||
const editor = useBlockNoteEditor<
|
const editor = useBlockNoteEditor<
|
||||||
DocsBlockSchema,
|
DocsBlockSchema,
|
||||||
DocsInlineContentSchema,
|
DocsInlineContentSchema,
|
||||||
@@ -44,7 +46,6 @@ export const BlockNoteSuggestionMenu = () => {
|
|||||||
const fileBlocksName = dictionaryDate.slash_menu.file.group;
|
const fileBlocksName = dictionaryDate.slash_menu.file.group;
|
||||||
|
|
||||||
const getInterlinkingMenuItems = useGetInterlinkingMenuItems();
|
const getInterlinkingMenuItems = useGetInterlinkingMenuItems();
|
||||||
const { data: conf } = useConfig();
|
|
||||||
|
|
||||||
const getSlashMenuItems = useMemo(() => {
|
const getSlashMenuItems = useMemo(() => {
|
||||||
// We insert it after the "Code Block" item to have the interlinking block displayed after the basic blocks
|
// We insert it after the "Code Block" item to have the interlinking block displayed after the basic blocks
|
||||||
@@ -56,9 +57,7 @@ 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
|
aiAllowed && getAISlashMenuItems ? getAISlashMenuItems(editor) : [],
|
||||||
? getAISlashMenuItems(editor)
|
|
||||||
: [],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const index = combinedMenu.findIndex(
|
const index = combinedMenu.findIndex(
|
||||||
@@ -80,7 +79,7 @@ export const BlockNoteSuggestionMenu = () => {
|
|||||||
t,
|
t,
|
||||||
fileBlocksName,
|
fileBlocksName,
|
||||||
basicBlocksName,
|
basicBlocksName,
|
||||||
conf?.AI_FEATURE_ENABLED,
|
aiAllowed,
|
||||||
getInterlinkingMenuItems,
|
getInterlinkingMenuItems,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { ModalConfirmDownloadUnsafe } from './ModalConfirmDownloadUnsafe';
|
|||||||
|
|
||||||
const AIToolbarButton = BlockNoteAI?.AIToolbarButton;
|
const AIToolbarButton = BlockNoteAI?.AIToolbarButton;
|
||||||
|
|
||||||
export const BlockNoteToolbar = () => {
|
export const BlockNoteToolbar = ({ aiAllowed }: { aiAllowed: boolean }) => {
|
||||||
const dict = useDictionary();
|
const dict = useDictionary();
|
||||||
const [confirmOpen, setIsConfirmOpen] = useState(false);
|
const [confirmOpen, setIsConfirmOpen] = useState(false);
|
||||||
const [onConfirm, setOnConfirm] = useState<() => void | Promise<void>>();
|
const [onConfirm, setOnConfirm] = useState<() => void | Promise<void>>();
|
||||||
@@ -72,7 +72,7 @@ export const BlockNoteToolbar = () => {
|
|||||||
const formattingToolbar = useCallback(() => {
|
const formattingToolbar = useCallback(() => {
|
||||||
return (
|
return (
|
||||||
<FormattingToolbar>
|
<FormattingToolbar>
|
||||||
{conf?.AI_FEATURE_ENABLED && AIToolbarButton && <AIToolbarButton />}
|
{aiAllowed && AIToolbarButton && <AIToolbarButton />}
|
||||||
|
|
||||||
<CommentToolbarButton />
|
<CommentToolbarButton />
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ export const BlockNoteToolbar = () => {
|
|||||||
<MarkdownButton key="customButton" />
|
<MarkdownButton key="customButton" />
|
||||||
</FormattingToolbar>
|
</FormattingToolbar>
|
||||||
);
|
);
|
||||||
}, [toolbarItems, conf?.AI_FEATURE_ENABLED]);
|
}, [toolbarItems, aiAllowed, conf?.AI_FEATURE_ENABLED]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Reference in New Issue
Block a user