Skip to content

Commit 80b43d4

Browse files
committed
feat: update macros katex
1 parent f05f785 commit 80b43d4

File tree

7 files changed

+152
-83
lines changed

7 files changed

+152
-83
lines changed

playground/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ import 'katex/dist/katex.min.css'
9090
import 'easydrawer/styles.css'
9191
import "@excalidraw/excalidraw/index.css";
9292

93+
import 'katex/contrib/mhchem'
94+
9395
// import Collaboration from '@tiptap/extension-collaboration'
9496
// import CollaborationCaret from '@tiptap/extension-collaboration-caret'
9597
// import { HocuspocusProvider } from '@hocuspocus/provider'

src/components/Bubble/RichTextBubbleKatex.tsx

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import katexLib from 'katex';
55
import { Pencil, Trash2 } from 'lucide-react';
66

77
import { ActionButton } from '@/components/ActionButton';
8-
import { Button } from '@/components/ui';
8+
import { Button, Label } from '@/components/ui';
99
import { Dialog, DialogContent, DialogFooter, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
1010
import { Textarea } from '@/components/ui/textarea';
1111
import { Katex } from '@/extensions/Katex';
@@ -14,6 +14,7 @@ import { useAttributes } from '@/hooks/useAttributes';
1414
import { useLocale } from '@/locales';
1515
import { useEditorInstance } from '@/store/editor';
1616
import { useEditableEditor } from '@/store/store';
17+
import { parseJSONString } from '@/utils/columns';
1718
import { deleteNode } from '@/utils/delete-node';
1819

1920
function ModalEditKatex({
@@ -27,36 +28,38 @@ function ModalEditKatex({
2728

2829
const attrs = useAttributes<IKatexAttrs>(editor, Katex.name, {
2930
text: '',
30-
defaultShowPicker: false,
31+
macros: '',
3132
});
32-
const { text, defaultShowPicker } = attrs;
3333

34-
const [currentValue, setCurrentValue] = useState(text || '');
34+
const { text, macros } = attrs;
3535

36-
useEffect(() => {
37-
if (visible) setCurrentValue(text || '');
38-
}, [visible]);
36+
const [currentValue, setCurrentValue] = useState(decodeURIComponent(text || ''));
37+
38+
const [currentMacros, setCurrentMacros] = useState(decodeURIComponent(macros || ''));
3939

4040
useEffect(() => {
41-
if (defaultShowPicker) {
42-
toggleVisible(true);
43-
editor.chain().updateAttributes(Katex.name, { defaultShowPicker: false }).focus().run();
41+
if (visible) {
42+
setCurrentValue(decodeURIComponent(text || ''));
43+
setCurrentMacros(decodeURIComponent(macros || ''));
4444
}
45-
}, [editor, defaultShowPicker, toggleVisible]);
45+
}, [visible]);
4646

4747
const submit = useCallback(() => {
48-
editor.chain().focus().setKatex({ text: currentValue }).run();
48+
editor.chain().focus().setKatex({ text: encodeURIComponent(currentValue), macros: encodeURIComponent(currentMacros) }).run();
4949
setCurrentValue('');
50+
setCurrentMacros('');
5051
toggleVisible(false);
51-
}, [editor, currentValue, toggleVisible]);
52+
}, [editor, currentValue, currentMacros, toggleVisible]);
5253

5354
const formatText = useMemo(() => {
5455
try {
55-
return katexLib.renderToString(`${currentValue}`);
56+
return katexLib.renderToString(currentValue, {
57+
macros: parseJSONString(currentMacros || '')
58+
});
5659
} catch {
5760
return currentValue;
5861
}
59-
}, [currentValue]);
62+
}, [currentValue, currentMacros]);
6063

6164
const previewContent = useMemo(
6265
() => {
@@ -89,19 +92,39 @@ function ModalEditKatex({
8992
style={{ height: '100%', border: '1px solid hsl(var(--border))' }}
9093
>
9194
<div className="richtext-flex richtext-gap-[10px] richtext-rounded-[10px] richtext-p-[10px]">
92-
<Textarea
93-
autoFocus
94-
className="richtext-flex-1"
95-
onChange={e => setCurrentValue(e.target.value)}
96-
placeholder="Text"
97-
98-
required
99-
rows={10}
100-
value={currentValue}
101-
style={{
102-
color: 'hsl(var(--foreground))',
103-
}}
104-
/>
95+
<div className='richtext-flex-1'>
96+
97+
<Label className="mb-[6px]">
98+
Expression
99+
</Label>
100+
101+
<Textarea
102+
autoFocus
103+
className="richtext-mb-[10px]"
104+
onChange={e => setCurrentValue(e.target.value)}
105+
placeholder="Text"
106+
required
107+
rows={10}
108+
value={currentValue}
109+
style={{
110+
color: 'hsl(var(--foreground))',
111+
}}
112+
/>
113+
114+
<Label className="mb-[6px]">
115+
Macros
116+
</Label>
117+
118+
<Textarea
119+
onChange={e => setCurrentMacros(e.target.value)}
120+
placeholder="Macros"
121+
rows={10}
122+
value={currentMacros}
123+
style={{
124+
color: 'hsl(var(--foreground))',
125+
}}
126+
/>
127+
</div>
105128

106129
<div
107130
className="richtext-flex richtext-flex-1 richtext-items-center richtext-justify-center richtext-rounded-[10px] richtext-p-[10px]"

src/components/ui/emoji-picker.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function EmojiPickerSearch({
3434
}: React.ComponentProps<typeof EmojiPickerPrimitive.Search>) {
3535
return (
3636
<div
37-
className={cn('!richtext-flex !richtext-h-9 !richtext-items-center !richtext-gap-2 !richtext-border-b !richtext-px-3', className)}
37+
className={cn('!richtext-flex !richtext-h-9 !richtext-items-center !richtext-gap-2 !richtext-border-b richtext-border-border !richtext-px-3', className)}
3838
data-slot="emoji-picker-search-wrapper"
3939
>
4040
<SearchIcon className="!richtext-size-4 !richtext-shrink-0 !richtext-opacity-50" />
@@ -138,7 +138,7 @@ function EmojiPickerFooter({
138138
<div
139139
data-slot="emoji-picker-footer"
140140
className={cn(
141-
'!richtext-max-w-(--frimousse-viewport-width) !richtext-flex !richtext-w-full !richtext-min-w-0 !richtext-items-center !richtext-gap-1 !richtext-border-t !richtext-p-2',
141+
'!richtext-max-w-(--frimousse-viewport-width) !richtext-flex !richtext-w-full !richtext-min-w-0 !richtext-items-center !richtext-gap-1 !richtext-border-t richtext-border-border !richtext-p-2',
142142
className
143143
)}
144144
{...props}

src/extensions/Katex/Katex.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export * from '@/extensions/Katex/components/RichTextKatex';
77

88
export interface IKatexAttrs {
99
text?: string
10-
defaultShowPicker?: boolean
10+
macros?: string
1111
}
1212

1313
interface IKatexOptions {
@@ -63,8 +63,9 @@ export const Katex = /* @__PURE__ */ Node.create<IKatexOptions>({
6363
default: '',
6464
parseHTML: getDatasetAttribute('text'),
6565
},
66-
defaultShowPicker: {
67-
default: false,
66+
macros: {
67+
default: '',
68+
parseHTML: getDatasetAttribute('macros'),
6869
},
6970
};
7071
},
@@ -95,9 +96,6 @@ export const Katex = /* @__PURE__ */ Node.create<IKatexOptions>({
9596
nodeInputRule({
9697
find: /^\$katex\$$/,
9798
type: this.type,
98-
getAttributes: () => {
99-
return { defaultShowPicker: true };
100-
},
10199
}),
102100
];
103101
},

src/extensions/Katex/components/KatexWrapper.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
11
import { useMemo } from 'react';
22

33
import { NodeViewWrapper } from '@tiptap/react';
4-
import katex from 'katex';
4+
import katexLib from 'katex';
5+
6+
import { parseJSONString } from '@/utils/columns';
57

68
export function KatexNodeView({ node }: any) {
7-
const { text } = node.attrs;
9+
const { text, macros } = node.attrs;
810

911
const formatText = useMemo(() => {
1012
try {
11-
return katex.renderToString(`${text}`);
13+
return katexLib.renderToString(decodeURIComponent(text || ''), {
14+
macros: parseJSONString(decodeURIComponent(macros || ''))
15+
});
1216
} catch {
1317
return text;
1418
}
15-
}, [text]);
19+
}, [text, macros]);
1620

1721
const content = useMemo(
1822
() =>
1923
text.trim()
2024
? (
2125
<span contentEditable={false}
22-
dangerouslySetInnerHTML={{ __html: formatText }}
26+
dangerouslySetInnerHTML={{ __html: formatText }}
2327
>
2428
</span>
2529
)

src/extensions/Katex/components/RichTextKatex.tsx

Lines changed: 68 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { useCallback, useEffect, useMemo, useState } from 'react';
1+
import { useCallback, useMemo, useState } from 'react';
22

33
import katexLib from 'katex';
44

5-
import { ActionButton, Button } from '@/components';
5+
import { ActionButton, Button, Label } from '@/components';
66
import { Dialog, DialogContent, DialogFooter, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
77
import { Textarea } from '@/components/ui/textarea';
88
import type { IKatexAttrs } from '@/extensions/Katex/Katex';
@@ -12,51 +12,55 @@ import { useAttributes } from '@/hooks/useAttributes';
1212
import { useButtonProps } from '@/hooks/useButtonProps';
1313
import { useLocale } from '@/locales';
1414
import { useEditorInstance } from '@/store/editor';
15+
import { parseJSONString } from '@/utils/columns';
1516

1617
export function RichTextKatex() {
1718
const { t } = useLocale();
1819
const [visible, toggleVisible] = useState(false);
1920

20-
const buttonProps = useButtonProps(Katex.name);
21+
const buttonProps = useButtonProps(Katex.name);
2122

22-
const {
23-
icon = undefined,
24-
tooltip = undefined,
25-
tooltipOptions = {},
26-
isActive = undefined,
27-
} = buttonProps?.componentProps ?? {};
23+
const {
24+
icon = undefined,
25+
tooltip = undefined,
26+
tooltipOptions = {},
27+
isActive = undefined,
28+
} = buttonProps?.componentProps ?? {};
2829

29-
const { editorDisabled } = useToggleActive(isActive);
30+
const { editorDisabled } = useToggleActive(isActive);
3031

3132
const editor = useEditorInstance();
3233

3334
const attrs = useAttributes<IKatexAttrs>(editor, Katex.name, {
3435
text: '',
35-
defaultShowPicker: false,
36+
macros: '',
3637
});
37-
const { text, defaultShowPicker } = attrs;
38+
const { text, macros } = attrs;
3839

39-
const [currentValue, setCurrentValue] = useState(text || '');
40+
const [currentValue, setCurrentValue] = useState(decodeURIComponent(text || ''));
41+
const [currentMacros, setCurrentMacros] = useState(decodeURIComponent(macros || ''));
4042

4143
const submit = useCallback(() => {
42-
editor.chain().focus().setKatex({ text: currentValue }).run();
44+
45+
editor.chain().focus().setKatex({
46+
text: encodeURIComponent(currentValue),
47+
macros: encodeURIComponent(currentMacros),
48+
}).run();
49+
4350
setCurrentValue('');
51+
setCurrentMacros('');
4452
toggleVisible(false);
45-
}, [editor, currentValue]);
46-
47-
useEffect(() => {
48-
if (defaultShowPicker) {
49-
editor.chain().updateAttributes(Katex.name, { defaultShowPicker: false }).focus().run();
50-
}
51-
}, [editor, defaultShowPicker]);
53+
}, [editor, currentValue, currentMacros]);
5254

5355
const formatText = useMemo(() => {
5456
try {
55-
return katexLib.renderToString(`${currentValue}`);
57+
return katexLib.renderToString(currentValue, {
58+
macros: parseJSONString(currentMacros)
59+
});
5660
} catch {
5761
return currentValue;
5862
}
59-
}, [currentValue]);
63+
}, [currentMacros, currentValue]);
6064

6165
const previewContent = useMemo(
6266
() => {
@@ -75,14 +79,14 @@ export function RichTextKatex() {
7579
open={visible}
7680
>
7781
<DialogTrigger
78-
asChild
79-
disabled={editorDisabled}
82+
asChild
83+
disabled={editorDisabled}
8084
>
8185
<ActionButton
8286
disabled={editorDisabled}
83-
icon={icon}
84-
tooltip={tooltip}
85-
tooltipOptions={tooltipOptions}
87+
icon={icon}
88+
tooltip={tooltip}
89+
tooltipOptions={tooltipOptions}
8690
action={() => {
8791
if (editorDisabled) return;
8892
toggleVisible(true);
@@ -92,26 +96,48 @@ export function RichTextKatex() {
9296

9397
<DialogContent className="richtext-z-[99999] !richtext-max-w-[1300px]">
9498
<DialogTitle>
95-
{t('editor.formula.dialog.text')}
99+
{t('editor.formula.dialog.text')}
96100
</DialogTitle>
97101

98102
<div
99103
style={{ height: '100%', border: '1px solid hsl(var(--border))' }}
100104
>
101105
<div className="richtext-flex richtext-gap-[10px] richtext-rounded-[10px] richtext-p-[10px]">
102-
<Textarea
103-
autoFocus
104-
className="richtext-flex-1"
105-
onChange={e => setCurrentValue(e.target.value)}
106-
placeholder="Text"
107-
108-
required
109-
rows={10}
110-
value={currentValue}
111-
style={{
112-
color: 'hsl(var(--foreground))',
113-
}}
114-
/>
106+
<div className='richtext-flex-1'>
107+
<Label className="mb-[6px]">
108+
Expression
109+
</Label>
110+
111+
<Textarea
112+
autoFocus
113+
className="richtext-mb-[10px]"
114+
onChange={e => setCurrentValue(e.target.value)}
115+
placeholder="Text"
116+
required
117+
rows={10}
118+
value={currentValue}
119+
style={{
120+
color: 'hsl(var(--foreground))',
121+
}}
122+
/>
123+
124+
<Label className="mb-[6px]">
125+
Macros
126+
</Label>
127+
128+
<Textarea
129+
className="richtext-flex-1"
130+
placeholder="Macros"
131+
rows={10}
132+
value={currentMacros}
133+
onChange={e => {
134+
setCurrentMacros(e.target.value);
135+
}}
136+
style={{
137+
color: 'hsl(var(--foreground))',
138+
}}
139+
/>
140+
</div>
115141

116142
<div
117143
className="richtext-flex richtext-flex-1 richtext-items-center richtext-justify-center richtext-rounded-[10px] richtext-p-[10px]"

0 commit comments

Comments
 (0)