From e63e92e731ba2a769ee5207cf6a4eb38ba0482bb Mon Sep 17 00:00:00 2001 From: Anthony LC Date: Thu, 12 Sep 2024 11:38:33 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(frontend)=20add=20ai=20button=20to=20?= =?UTF-8?q?blocknote=20toolbar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a ai button to blocknote toolbar, which will trigger the ai rewrite function. --- .../docs/doc-editor/api/useAIRewrite.tsx | 38 ++++++++ .../features/docs/doc-editor/api/useDocAI.tsx | 37 ++++++++ .../docs/doc-editor/components/AIButton.tsx | 86 +++++++++++++++++++ .../components/BlockNoteToolbar.tsx | 5 ++ 4 files changed, 166 insertions(+) create mode 100644 src/frontend/apps/impress/src/features/docs/doc-editor/api/useAIRewrite.tsx create mode 100644 src/frontend/apps/impress/src/features/docs/doc-editor/api/useDocAI.tsx create mode 100644 src/frontend/apps/impress/src/features/docs/doc-editor/components/AIButton.tsx diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/api/useAIRewrite.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/api/useAIRewrite.tsx new file mode 100644 index 00000000..c3aa94fa --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/api/useAIRewrite.tsx @@ -0,0 +1,38 @@ +import { useMutation } from '@tanstack/react-query'; + +import { APIError, errorCauses, fetchAPI } from '@/api'; + +export type DocAIParams = { + docId: string; + text: string; + action: 'rephrase' | 'summarize'; +}; + +export type DocAIResponse = { + textAI: string; +}; + +export const DocAI = async ({ + docId, + ...params +}: DocAIParams): Promise => { + console.log('DocAI', docId, params); + const response = await fetchAPI(`documents/${docId}/ai/`, { + method: 'POST', + body: JSON.stringify({ + ...params, + }), + }); + + if (!response.ok) { + throw new APIError('Failed to get AI', await errorCauses(response)); + } + + return response.json() as Promise; +}; + +export function useAIRewrite() { + return useMutation({ + mutationFn: DocAI, + }); +} diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/api/useDocAI.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/api/useDocAI.tsx new file mode 100644 index 00000000..48c5ee61 --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/api/useDocAI.tsx @@ -0,0 +1,37 @@ +import { UseQueryOptions, useQuery } from '@tanstack/react-query'; + +import { APIError, errorCauses, fetchAPI } from '@/api'; + +export type DocParams = { + id: string; + text: string; + action: 'rephrase' | 'summarize'; +}; + +export type DocAIResponse = { + textAI: string; +}; + +export const getDocAI = async ({ id }: DocParams): Promise => { + const response = await fetchAPI(`documents/${id}/ai/`); + + if (!response.ok) { + throw new APIError('Failed to get the doc ai', await errorCauses(response)); + } + + return response.json() as Promise; +}; + +export const KEY_DOC = 'doc'; +export const KEY_DOC_VISIBILITY = 'doc-visibility'; + +export function useDocAI( + param: DocParams, + queryConfig?: UseQueryOptions, +) { + return useQuery({ + queryKey: [KEY_DOC, param], + queryFn: () => getDocAI(param), + ...queryConfig, + }); +} diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/AIButton.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/AIButton.tsx new file mode 100644 index 00000000..5bf93671 --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/AIButton.tsx @@ -0,0 +1,86 @@ +import { + useBlockNoteEditor, + useComponentsContext, + useEditorContentOrSelectionChange, + useEditorSelectionChange, + useSelectedBlocks, +} from '@blocknote/react'; +import { useMemo } from 'react'; + +import { useAIRewrite } from '../api/useAIRewrite'; + +/** + * Custom Formatting Toolbar Button to convert markdown to json. + */ +export function AIButton() { + const editor = useBlockNoteEditor(); + const Components = useComponentsContext(); + const selectedBlocks = useSelectedBlocks(editor); + useEditorSelectionChange(() => { + console.log('Selection changed'); + }, editor); + useEditorContentOrSelectionChange(() => { + console.log('Content or selection changed'); + }, editor); + const { mutateAsync: requestAI, data } = useAIRewrite(); + + console.log('Data', data); + + const handleRephraseAI = async () => { + console.log('Rephrase with AI', editor.getSelection()); + console.log('Rephrase with BLockkk', selectedBlocks); + + await requestAI({ + docId: '901ac9b3-97a4-4f37-adcf-1941746b3c61', + text: 'toto', + action: 'rephrase', + }); + + // Call backend endpoint to rephrase the selected block + + editor.replaceBlocks( + [selectedBlocks[0]], + [ + { + content: + 'This block was replaced at ' + new Date().toLocaleTimeString(), + }, + ], + ); + + // forEach(blocks, async (block) => { + // if (!isBlock(block as unknown as Block)) { + // return; + // } + + // try { + // const fullContent = recursiveContent( + // block.content as unknown as Block[], + // ); + + // const blockMarkdown = + // await editor.tryParseMarkdownToBlocks(fullContent); + // editor.replaceBlocks([block.id], blockMarkdown); + // } catch (error) { + // console.error('Error parsing Markdown:', error); + // } + // }); + }; + + const show = useMemo(() => { + return !!selectedBlocks.find((block) => block.content !== undefined); + }, [selectedBlocks]); + + if (!show || !editor.isEditable || !Components) { + return null; + } + + return ( + void handleRephraseAI()} + > + Rephrase + + ); +} diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolbar.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolbar.tsx index 785f84c2..ebed1ad2 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolbar.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolbar.tsx @@ -16,6 +16,8 @@ import { import { forEach, isArray } from 'lodash'; import React, { useMemo } from 'react'; +import { AIButton } from './AIButton'; + export const BlockNoteToolbar = () => { return ( { + {/* Extra button to convert from markdown to json */} + + {/* Extra button to convert from markdown to json */}