Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Multiple functions chart and specific editor
  • Loading branch information
Math-s314 committed Jun 7, 2025
commit db4d492d54e1d6462e0ff4730a2539590a59f4b4
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { loader } from '@monaco-editor/react';
import type { editor } from 'monaco-editor';

export const initializeMonaco = async () => {
const monaco = await loader.init();

// Register LaTeX language
monaco.languages.register({ id: 'latex' });
monaco.languages.setMonarchTokensProvider('latex', {
tokenizer: {
root: [
// Commands
[/\\[a-zA-Z]+/, 'keyword'],
// Math delimiters
[/\$.*?\$/, 'string'],
[/\\\(.*?\\\)/, 'string'],
[/\\\[.*?\\\]/, 'string'],
// Comments
[/%.*$/, 'comment'],
// Brackets
[/[{}[\]]/, 'delimiter.bracket'],
// Numbers
[/\d+/, 'number'],
],
},
});

// Register Mermaid language
monaco.languages.register({ id: 'mermaid' });
monaco.languages.setMonarchTokensProvider('mermaid', {
tokenizer: {
root: [
// Graph types
[
/^(graph|flowchart|sequenceDiagram|classDiagram|stateDiagram|gantt|pie|erDiagram|journey)\s/,
'keyword',
],
// Direction
[/^(TD|BT|RL|LR)\s/, 'keyword'],
// Node definitions
[/[A-Za-z0-9_]+\[.*?\]/, 'string'],
[/[A-Za-z0-9_]+\(.*?\)/, 'string'],
[/[A-Za-z0-9_]+{.*?}/, 'string'],
[/[A-Za-z0-9_]+>.*?</, 'string'],
[/[A-Za-z0-9_]+\[.*?\]>.*?</, 'string'],
// Arrows
[/-->|==>|-.->|==>|--o|--x|--|==|===|----|====/, 'operator'],
// Comments
[/%%/, 'comment'],
[/%%[^%]*/, 'comment'],
// Numbers
[/\d+/, 'number'],
// Brackets
[/[{}[\]]/, 'delimiter.bracket'],
// Strings
[/"[^"]*"/, 'string'],
// Keywords
[
/^(subgraph|end|class|state|note|participant|actor|as|title|dateFormat|axisFormat|section|task|milestone|entity|relationship|journey|section|task|milestone)\b/,
'keyword',
],
],
},
});
};

export const getEditorOptions = (
language: string = 'latex',
): editor.IStandaloneEditorConstructionOptions => ({
minimap: { enabled: false },
scrollBeyondLastLine: false,
lineNumbers: 'off' as const,
folding: false,
lineDecorationsWidth: 0,
lineNumbersMinChars: 0,
glyphMargin: false,
contextmenu: false,
scrollbar: {
vertical: 'hidden',
horizontal: 'hidden'
},
fontSize: 14,
fontFamily: 'monospace',
wordWrap: 'on',
wrappingStrategy: 'advanced',
wrappingIndent: 'same',
language,
renderLineHighlight: "none",
overviewRulerLanes: 0,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useEffect, useRef } from 'react';

export const useClickOutside = (onClickOutside: () => void) => {
const ref = useRef<HTMLDivElement>(null);

useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
onClickOutside();
}
};

document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [onClickOutside]);

return ref;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useEffect, useState } from 'react';

import type { EditorDimensions } from '../types';

export const useEditorDimensions = (
parentRef?: React.RefObject<HTMLDivElement | null>,
) => {
const [dimensions, setDimensions] = useState<EditorDimensions>({
height: 0,
width: 0,
});

useEffect(() => {
const updateDimensions = () => {
if (parentRef?.current) {
const rect = parentRef.current.getBoundingClientRect();
setDimensions({
height: rect.height,
width: rect.width,
});
}
};

updateDimensions();
window.addEventListener('resize', updateDimensions);
return () => {
window.removeEventListener('resize', updateDimensions);
};
}, [parentRef]);

return dimensions;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import Editor from '@monaco-editor/react';
import { Button } from '@openfun/cunningham-react';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';

import { Box } from '@/components';

import { getEditorOptions, initializeMonaco } from './config/editorConfig';
import { useClickOutside } from './hooks/useClickOutside';
import { useEditorDimensions } from './hooks/useEditorDimensions';
import { containerStyles, editorContainerStyles, functionEditorsStyle, inputStyle, rightButtonsContainer, leftButtonsContainer, functionEditorsContainer, divContainer } from './styles/editorStyles';
import type { FunctionEditorProps } from './types';
import { winterCGFetchIntegration } from '@sentry/nextjs';
import { Icon } from '@/components';
import katex from 'katex';
import 'katex/dist/katex.min.css';

const defaultNewFunction = '\\cos(3*x)';

const formatNum = (nV : number) => {
let res = Math.round(nV);
return res < 2 ? 2 : res;
}

export const FunctionEditor = ({
functions,
min,
max,
num,
onChange,
onClickOutside,
parentRef
}: FunctionEditorProps) => {
const { t } = useTranslation();
const { height: parentHeight, width: parentWidth } = useEditorDimensions(parentRef);
const editorRef = useClickOutside(onClickOutside);
const [localFunctions, setLocalFunctions] = useState(functions);
const [localMin, setLocalMin] = useState(min);
const [localMax, setLocalMax] = useState(max);
const [localNum, setLocalNum] = useState(num);

useEffect(() => {
setLocalFunctions(functions);
setLocalMin(min);
setLocalMax(max);
setLocalNum(num);
}, [min, max, num, functions]);

useEffect(() => {
void initializeMonaco();
}, []);

const containerWidth = `${parentWidth}px`;
const containerMargin = `${parentHeight}px 0 0 -16px`;

let functionEditors = localFunctions.map((fun,i) =>
<Box style={editorContainerStyles}>
<div style={functionEditorsStyle}>
<p style={{display : 'inline-block', width : '15%', verticalAlign : 'top', textAlign : 'center'}}><div dangerouslySetInnerHTML={{__html: katex.renderToString(`f_{${i+1}}(x)=`)}}></div></p>
<div style={{width : '85%', display : 'inline-block', verticalAlign : 'top', marginBottom : '5px'}}>
<Editor
language={'latex'}
height="1.5em"
value={fun}
onChange={(val) => setLocalFunctions(localFunctions.map((x,j)=>i==j ? (val || '') : x))}
options={getEditorOptions()}
theme="vs-light"

/>
</div>
</div>
</Box>
)

return (
<Box
ref={editorRef}
$position="absolute"
$zIndex={1000}
$margin={containerMargin}
$padding="1rem"
$background="white"
style={{
...containerStyles,
width: containerWidth,
}}
>
<div style={divContainer}>
<div style={leftButtonsContainer}>

<Button style={{background : 'none'}} onClick={() => { setLocalFunctions(prev => [...prev, defaultNewFunction]); }}><Icon iconName="add" $size="30px" $color="black" /></Button>
<br></br>
<Button style={{background : 'none'}} onClick={() => { setLocalFunctions(prev => (prev.length > 1) ? prev.slice(0, -1) : prev); }}><Icon iconName="remove" $size="30px" $color="black" /></Button>
</div>
<div style={functionEditorsContainer}>
{functionEditors}
<br />
<p style={{width : '32%', display : 'inline-block', marginLeft : '0%'}}><div dangerouslySetInnerHTML={{__html: katex.renderToString(`x_{\\text{min}}`)}}></div></p>
<p style={{width : '32%', display : 'inline-block', marginLeft : '2%'}}><div dangerouslySetInnerHTML={{__html: katex.renderToString(`x_{\\text{max}}`)}}></div></p>
<p style={{width : '32%', display : 'inline-block', marginLeft : '2%'}}><div dangerouslySetInnerHTML={{__html: katex.renderToString(`n_{\\text{points}}`)}}></div></p>
<input type='number' style={{padding : '5px', width : '32%', borderRadius: '5px', border : "1px solid grey"}} value={localMin} onChange={(e) => setLocalMin(Number(e.target.value)) }/>
<input type='number' style={{width : '32%', marginLeft : '2%', padding : '5px', borderRadius: '5px', border : "1px solid grey"}} value={localMax} onChange={(e) => setLocalMax(Number(e.target.value)) }/>
<input type='number' style={{width : '32%', marginLeft : '2%', padding : '5px', borderRadius: '5px', border : "1px solid grey"}} value={localNum} onChange={(e) => setLocalNum(formatNum(Number(e.target.value))) }/>
</div>

<div style={rightButtonsContainer}>
<Button style={{display : 'inline-block', verticalAlign : 'top'}} onClick={() => onChange(localFunctions, localMin, localMax, localNum)}>{t('Validate')}</Button>
</div>
</div>

</Box>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { VerticalAlign } from "docx";

export const containerStyles = {
position: 'absolute',
zIndex: 1000,
padding: '1rem',
background: 'white',
border: '1px solid #e0e0e0',
borderRadius: '4px',
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
display: 'flex',
flexDirection: 'row',
alignItems: 'flex-start',
boxSizing: 'border-box',
} as const;

export const editorContainerStyles = {
width: '100%',
marginRight: '1rem',
position : 'relative',
} as const;

export const functionEditorsStyle = {
position : 'relative',
width : '100%',
display : 'block',
VerticalAlign : 'center',
} as const;

export const divContainer = {
width : '100%',
alignItems : 'top',
} as const;

export const functionEditorsContainer = {
width : '70%',
display : 'inline-block',
} as const;

export const leftButtonsContainer = {
width : '10%',
display : 'inline-block',
verticalAlign : 'top',
textAlign : 'center',
} as const;

export const rightButtonsContainer = {
width : '20%',
display : 'inline-block',
verticalAlign : 'top',
textAlign : 'center',
} as const;

export const inputStyle = {
borderRadius : '5px',
} as const
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface FunctionEditorProps {
functions: string[];
min: number;
max: number;
num: number;
onChange: (fun: string[], min : number, max : number, num : number) => void;
onClickOutside: () => void;
parentRef?: React.RefObject<HTMLDivElement | null>;
}

export interface EditorDimensions {
height: number;
width: number;
}
Loading