Skip to content

Math and graph integration into Docs (haXathon) #1061

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 35 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
aaaa7a3
added Latex Block
tidann Jun 2, 2025
737d9f2
added click to edit feature
Plouil Jun 2, 2025
3b49d70
Cleanup
tidann Jun 2, 2025
c0aa8f0
Revert "added click to edit feature"
tidann Jun 2, 2025
be80002
test
tidann Jun 2, 2025
1e12810
I did things
tidann Jun 2, 2025
aca4857
added codeeditor
tidann Jun 2, 2025
451a164
[temp] started syntax highlighting
tidann Jun 2, 2025
84db73a
refactoring
tidann Jun 2, 2025
ca14d68
refactor
tidann Jun 2, 2025
161b146
more refactor
tidann Jun 2, 2025
07a64db
better error handling and refactoring
tidann Jun 2, 2025
3447a34
Refactor LatexRenderer and MermaidRenderer to use shared styles for c…
tidann Jun 2, 2025
d0ae34a
Add text alignment and background color properties to LatexBlock and …
tidann Jun 2, 2025
44919f6
Add InlineLatex support to BlockNoteEditor and BlockNoteToolbar; mino…
tidann Jun 3, 2025
ddda804
Integrate useLatexDetection hook into BlockNoteEditor and update Inli…
tidann Jun 3, 2025
2713b33
Enhance InlineLatex component to manage focus state and update formul…
tidann Jun 3, 2025
29b2fe5
Implement Markdown parsing with LaTeX support in MarkdownButton compo…
tidann Jun 3, 2025
c7abf48
Remove debug logging from MarkdownButton and InlineLatex components t…
tidann Jun 3, 2025
b7812ee
Adding basic chart bloc
Math-s314 Jun 3, 2025
65bc6a6
Minor Corrections to Chart Block
Math-s314 Jun 3, 2025
4b57a79
Update package dependencies and refine ChartBlock configuration
tidann Jun 3, 2025
c4a3c9e
Fixed the gaussian integral
tidann Jun 3, 2025
666988f
Enhance InlineLatexButton and update icon in SlashMenuItems
tidann Jun 3, 2025
69d8037
Implement PDF export mappings for new block types and update dependen…
tidann Jun 3, 2025
c91ac7d
Enhance BlockNoteEditor with Markdown and LaTeX support
tidann Jun 3, 2025
a6eb8af
Remove debug logging from BlockNoteEditor and useLatexDetection for c…
tidann Jun 3, 2025
487607b
Refactor useLatexDetection by removing unused lastKeyRef logic for cl…
tidann Jun 3, 2025
eaf5638
Add SuggestionMenuController to BlockNoteEditor for inline LaTeX support
tidann Jun 3, 2025
01b8049
Add Backspace handling in LatexComponent for improved editing experience
tidann Jun 3, 2025
d650942
added inline latex pdf
tidann Jun 4, 2025
32d4b6c
Enhance DOCX export mappings by adding support for LaTeX, Mermaid, an…
tidann Jun 4, 2025
db4d492
Multiple functions chart and specific editor
Math-s314 Jun 4, 2025
686605c
Adding first version AI Latex creation
Math-s314 Jun 4, 2025
e1dc683
Mini corrections
Math-s314 Jun 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,7 @@ db.sqlite3
.vscode/
*.iml
.devcontainer

**/package-lock.json
src/frontend/package-lock.json
src/frontend/yarn.lock
11 changes: 10 additions & 1 deletion src/frontend/apps/impress/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,21 @@
"@blocknote/react": "0.31.1",
"@blocknote/xl-docx-exporter": "0.31.1",
"@blocknote/xl-pdf-exporter": "0.31.1",
"@cortex-js/compute-engine": "^0.29.1",
"@emoji-mart/data": "1.2.1",
"@emoji-mart/react": "1.1.1",
"@fontsource/material-icons": "5.2.5",
"@gouvfr-lasuite/integration": "1.0.3",
"@gouvfr-lasuite/ui-kit": "0.7.0",
"@hocuspocus/provider": "2.15.2",
"@monaco-editor/react": "^4.7.0",
"@openfun/cunningham-react": "3.1.0",
"@react-pdf/renderer": "4.3.0",
"@sentry/nextjs": "9.22.0",
"@tanstack/react-query": "5.77.1",
"canvg": "4.0.3",
"@types/katex": "^0.16.7",
"canvg": "^4.0.3",
"chart.js": "^4.4.9",
"clsx": "2.1.1",
"cmdk": "1.1.1",
"crisp-sdk-web": "1.0.25",
Expand All @@ -41,17 +45,22 @@
"i18next": "25.2.1",
"i18next-browser-languagedetector": "8.1.0",
"idb": "8.0.3",
"katex": "^0.16.22",
"lodash": "4.17.21",
"luxon": "3.6.1",
"mermaid": "^11.6.0",
"next": "15.3.2",
"posthog-js": "1.246.0",
"react": "*",
"react-aria-components": "1.9.0",
"react-chartjs-2": "^5.3.0",
"react-dom": "*",
"react-i18next": "15.5.2",
"react-intersection-observer": "9.16.0",
"react-select": "5.10.1",
"shiki": "^3.4.2",
"styled-components": "6.1.18",
"tex-to-svg": "^0.2.0",
"use-debounce": "10.0.4",
"y-protocols": "1.0.6",
"yjs": "*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { codeBlock } from '@blocknote/code-block';
import {
BlockNoteSchema,
defaultBlockSpecs,
defaultInlineContentSpecs,
withPageBreak,
} from '@blocknote/core';
import '@blocknote/core/fonts/inter.css';
import * as locales from '@blocknote/core/locales';
import { BlockNoteView } from '@blocknote/mantine';
import '@blocknote/mantine/style.css';
import { useCreateBlockNote } from '@blocknote/react';
import { SuggestionMenuController, useCreateBlockNote } from '@blocknote/react';
import { HocuspocusProvider } from '@hocuspocus/provider';
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
Expand All @@ -27,14 +28,35 @@ import { randomColor } from '../utils';

import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu';
import { BlockNoteToolbar } from './BlockNoteToolBar/BlockNoteToolbar';
import { CalloutBlock, DividerBlock } from './custom-blocks';
import parseMarkdownWithLatex from './BlockNoteToolBar/utils';
import { InlineLatex } from './InlineLatex/';
import getInlineLatexMenuItems from './InlineLatex/components/InlineLatexContentSpec';
import { useLatexDetection } from './InlineLatex/hooks/useLatexDetection';
import {
CalloutBlock,
DividerBlock,
LatexBlock,
MermaidBlock,
ChartBlock,
LatexAIBlock,
MermaidAIBlock
} from './custom-blocks';

export const blockNoteSchema = withPageBreak(
BlockNoteSchema.create({
blockSpecs: {
...defaultBlockSpecs,
callout: CalloutBlock,
divider: DividerBlock,
latex: LatexBlock,
mermaid: MermaidBlock,
chart: ChartBlock,
latexai: LatexAIBlock,
mermaidai: MermaidAIBlock
},
inlineContentSpecs: {
...defaultInlineContentSpecs,
inlineLatex: InlineLatex,
},
}),
);
Expand Down Expand Up @@ -63,6 +85,8 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
: user?.full_name || user?.email || t('Anonymous');
const showCursorLabels: 'always' | 'activity' | (string & {}) = 'activity';

let editorWillPaste = false;

const editor: DocsBlockNoteEditor = useCreateBlockNote(
{
codeBlock,
Expand Down Expand Up @@ -124,12 +148,35 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
},
uploadFile,
schema: blockNoteSchema,
pasteHandler: ({ event, editor }) => {
const text = event.clipboardData?.getData('text/plain');
if (text) {
editorWillPaste = true;
void editor.pasteMarkdown(text);
}
return true;
},
},
[collabName, lang, provider, uploadFile],
);

editor.onChange((editor, { getChanges }) => {
if (editorWillPaste) {
editorWillPaste = false;

const changes = getChanges();
changes.forEach((change) => {
if (change.type === 'update' || change.type === 'insert') {
const [newBlock] = parseMarkdownWithLatex([change.block]);
editor.updateBlock(change.block.id, newBlock);
}
});
}
});

useHeadings(editor);
useUploadStatus(editor);
useLatexDetection(editor);

useEffect(() => {
setEditor(editor);
Expand Down Expand Up @@ -164,7 +211,13 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
theme="light"
>
<BlockNoteSuggestionMenu />

<BlockNoteToolbar />

<SuggestionMenuController
triggerCharacter="$"
getItems={async (query) => getInlineLatexMenuItems(editor, query)}
/>
</BlockNoteView>
</Box>
);
Expand Down Expand Up @@ -206,6 +259,10 @@ export const BlockNoteEditorVersion = ({
return (
<Box $css={cssEditor(readOnly)} className="--docs--editor-container">
<BlockNoteView editor={editor} editable={!readOnly} theme="light" />
<SuggestionMenuController
triggerCharacter="$"
getItems={async (query) => getInlineLatexMenuItems(editor)}
/>
</Box>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ import { DocsBlockSchema } from '../types';
import {
getCalloutReactSlashMenuItems,
getDividerReactSlashMenuItems,
getLatexReactSlashMenuItems,
getMermaidReactSlashMenuItems,
getChartReactSlashMenuItems,
getLatexAIReactSlashMenuItems,
getMermaidAIReactSlashMenuItems
} from './custom-blocks';

export const BlockNoteSuggestionMenu = () => {
Expand All @@ -30,6 +35,11 @@ export const BlockNoteSuggestionMenu = () => {
getPageBreakReactSlashMenuItems(editor),
getCalloutReactSlashMenuItems(editor, t, basicBlocksName),
getDividerReactSlashMenuItems(editor, t, basicBlocksName),
getLatexReactSlashMenuItems(editor, t, basicBlocksName),
getMermaidReactSlashMenuItems(editor, t, basicBlocksName),
getChartReactSlashMenuItems(editor, t, basicBlocksName),
getLatexAIReactSlashMenuItems(editor, t, basicBlocksName),
getMermaidAIReactSlashMenuItems(editor, t, basicBlocksName),
),
query,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useTranslation } from 'react-i18next';

import { useConfig } from '@/core/config/api';

import { InlineLatexButton } from '../InlineLatex/components/InlineLatexButton';
import { getCalloutFormattingToolbarItems } from '../custom-blocks';

import { AIGroupButton } from './AIButton';
Expand Down Expand Up @@ -58,6 +59,9 @@ export const BlockNoteToolbar = () => {
<FormattingToolbar>
{toolbarItems}

{/* Extra button to insert latex */}
<InlineLatexButton key="InlineLatexButton" />

{/* Extra button to do some AI powered actions */}
{conf?.AI_FEATURE_ENABLED && <AIGroupButton key="AIButton" />}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { forEach, isArray } from 'lodash';
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';

import parseMarkdownWithLatex from './utils';

type Block = {
type: string;
text: string;
Expand Down Expand Up @@ -63,7 +65,9 @@ export function MarkdownButton() {

const blockMarkdown =
await editor.tryParseMarkdownToBlocks(fullContent);
editor.replaceBlocks([block.id], blockMarkdown);
const blockMarkdownWithLatex = parseMarkdownWithLatex(blockMarkdown);

editor.replaceBlocks([block.id], blockMarkdownWithLatex);
} catch (error) {
console.error('Error parsing Markdown:', error);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/* eslint-disable prettier/prettier */
/* eslint-disable @typescript-eslint/restrict-plus-operands */
import { Block, StyleSchema, StyledText } from '@blocknote/core';

export default function parseMarkdownWithLatex(blocks: Block[]): Block[] {
return blocks.map((block) => {
if (
(block.type === 'paragraph' ||
block.type == 'bulletListItem' ||
block.type == 'numberedListItem' ||
block.type == 'heading') &&
block.content
) {
const newContent: StyledText<StyleSchema>[] = [];
let currentText = '';
let isInLatex = false;
let latexContent = '';

// Process each content element
for (let i = 0; i < block.content.length; i++) {
const content = block.content[i];
if (content.type === 'text') {
// Process each character in the text content
for (let j = 0; j < content.text.length; j++) {
const char = content.text[j];
if (char === '$') {
if (isInLatex) {
// End of LaTeX block
if (currentText) {
newContent.push({
type: 'text',
text: currentText,
styles: content.styles,
});
currentText = '';
}
newContent.push({
type: 'inlineLatex',
props: {
formula: latexContent,
},
});
latexContent = '';
isInLatex = false;
} else {
// Start of LaTeX block
if (currentText) {
newContent.push({
type: 'text',
text: currentText,
styles: content.styles,
});
currentText = '';
}
isInLatex = true;
}
} else {
if (isInLatex) {
latexContent += char;
} else {
currentText += char;
}
}
}
} else {
// Handle non-text content
if (currentText) {
newContent.push({
type: 'text',
text: currentText,
styles: {},
});
currentText = '';
}
// Skip non-text content for now
continue;
}
}

// Add any remaining text
if (currentText) {
newContent.push({
type: 'text',
text: currentText,
styles: {},
});
}

// If we're still in a LaTeX block at the end, treat it as regular text
if (isInLatex) {
newContent.push({
type: 'text',
text: '$' + latexContent,
styles: {},
});
}

return {
...block,
content: newContent,
};
}
return block;
});
}
Loading