smartrte-react 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/QuillEditor.d.ts +8 -0
- package/dist/QuillEditor.js +34 -0
- package/dist/app.d.ts +6 -0
- package/dist/app.js +6 -0
- package/dist/blots/CommentBlot.d.ts +8 -0
- package/dist/blots/CommentBlot.js +17 -0
- package/dist/blots/FormulaBlot.d.ts +12 -0
- package/dist/blots/FormulaBlot.js +36 -0
- package/dist/blots/MediaBlot.d.ts +11 -0
- package/dist/blots/MediaBlot.js +37 -0
- package/dist/blots/TableBlot.d.ts +10 -0
- package/dist/blots/TableBlot.js +54 -0
- package/dist/blots/index.d.ts +5 -0
- package/dist/blots/index.js +12 -0
- package/dist/components/ClassicEditor.d.ts +10 -0
- package/dist/components/ClassicEditor.js +1066 -0
- package/dist/components/DiagramEditor.d.ts +5 -0
- package/dist/components/DiagramEditor.js +73 -0
- package/dist/components/FormulaEditor.d.ts +6 -0
- package/dist/components/FormulaEditor.js +86 -0
- package/dist/components/InfoBox.d.ts +7 -0
- package/dist/components/InfoBox.js +18 -0
- package/dist/components/MCQBlock.d.ts +13 -0
- package/dist/components/MCQBlock.js +29 -0
- package/dist/components/SmartEditor.d.ts +0 -0
- package/dist/components/SmartEditor.js +1 -0
- package/dist/components/SmartTable.d.ts +22 -0
- package/dist/components/SmartTable.js +629 -0
- package/dist/components/TableContextMenu.d.ts +11 -0
- package/dist/components/TableContextMenu.js +15 -0
- package/dist/components/TableInsertDialog.d.ts +7 -0
- package/dist/components/TableInsertDialog.js +42 -0
- package/dist/hooks/useEditorSync.d.ts +5 -0
- package/dist/hooks/useEditorSync.js +53 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/smart-editor.d.ts +0 -0
- package/dist/smart-editor.js +1 -0
- package/dist/standalone/classic-editor-embed.d.ts +12 -0
- package/dist/standalone/classic-editor-embed.js +108 -0
- package/dist/standalone/editor.js +241 -0
- package/package.json +46 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
export function DiagramEditor({ open, onClose, onInsert, }) {
|
|
4
|
+
const [code, setCode] = useState("graph TD\nA[Start] --> B{Decision}\nB -- Yes --> C[Do thing]\nB -- No --> D[Stop]");
|
|
5
|
+
const [svg, setSvg] = useState("");
|
|
6
|
+
const mounted = useRef(false);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
mounted.current = true;
|
|
9
|
+
return () => {
|
|
10
|
+
mounted.current = false;
|
|
11
|
+
};
|
|
12
|
+
}, []);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (!open)
|
|
15
|
+
return;
|
|
16
|
+
(async () => {
|
|
17
|
+
const loadMermaid = async () => {
|
|
18
|
+
// Use existing global if present
|
|
19
|
+
if (window.mermaid)
|
|
20
|
+
return window.mermaid;
|
|
21
|
+
// Load from CDN lazily to avoid bundler resolution
|
|
22
|
+
await new Promise((resolve) => {
|
|
23
|
+
const s = document.createElement("script");
|
|
24
|
+
s.src = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js";
|
|
25
|
+
s.async = true;
|
|
26
|
+
s.onload = () => resolve();
|
|
27
|
+
s.onerror = () => resolve();
|
|
28
|
+
document.head.appendChild(s);
|
|
29
|
+
});
|
|
30
|
+
return window.mermaid;
|
|
31
|
+
};
|
|
32
|
+
try {
|
|
33
|
+
const mermaid = await loadMermaid();
|
|
34
|
+
if (mermaid && mermaid.initialize) {
|
|
35
|
+
mermaid.initialize({ startOnLoad: false });
|
|
36
|
+
const { svg } = await mermaid.render("diagram-preview", code);
|
|
37
|
+
if (mounted.current)
|
|
38
|
+
setSvg(svg);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
setSvg(`<svg xmlns='http://www.w3.org/2000/svg' width='400' height='80'><text x='10' y='40' font-family='monospace'>Mermaid not available</text></svg>`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
setSvg(`<svg xmlns='http://www.w3.org/2000/svg' width='400' height='80'><text x='10' y='40' font-family='monospace'>Mermaid not available</text></svg>`);
|
|
46
|
+
}
|
|
47
|
+
})();
|
|
48
|
+
}, [open, code]);
|
|
49
|
+
if (!open)
|
|
50
|
+
return null;
|
|
51
|
+
const toDataUrl = (svgText) => `data:image/svg+xml;utf8,${encodeURIComponent(svgText)}`;
|
|
52
|
+
return (_jsx("div", { style: {
|
|
53
|
+
position: "fixed",
|
|
54
|
+
inset: 0,
|
|
55
|
+
background: "rgba(0,0,0,0.4)",
|
|
56
|
+
display: "flex",
|
|
57
|
+
alignItems: "center",
|
|
58
|
+
justifyContent: "center",
|
|
59
|
+
zIndex: 100,
|
|
60
|
+
}, onClick: onClose, children: _jsxs("div", { style: {
|
|
61
|
+
background: "#fff",
|
|
62
|
+
padding: 16,
|
|
63
|
+
borderRadius: 8,
|
|
64
|
+
minWidth: 520,
|
|
65
|
+
maxWidth: 820,
|
|
66
|
+
width: "90%",
|
|
67
|
+
}, onClick: (e) => e.stopPropagation(), children: [_jsx("div", { style: { fontWeight: 600, marginBottom: 8 }, children: "Insert diagram (Mermaid)" }), _jsxs("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }, children: [_jsx("textarea", { value: code, onChange: (e) => setCode(e.target.value), rows: 12, style: { width: "100%", fontFamily: "monospace" } }), _jsx("div", { style: { border: "1px solid #eee", padding: 8, overflow: "auto" }, dangerouslySetInnerHTML: { __html: svg } })] }), _jsxs("div", { style: {
|
|
68
|
+
display: "flex",
|
|
69
|
+
gap: 8,
|
|
70
|
+
justifyContent: "flex-end",
|
|
71
|
+
marginTop: 12,
|
|
72
|
+
}, children: [_jsx("button", { onClick: onClose, children: "Cancel" }), _jsx("button", { onClick: () => onInsert(toDataUrl(svg || "")), children: "Insert" })] })] }) }));
|
|
73
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import katex from "katex";
|
|
4
|
+
import "katex/dist/katex.min.css";
|
|
5
|
+
export function FormulaEditor({ open, onClose, onInsert, }) {
|
|
6
|
+
const [tex, setTex] = useState("a^2 + b^2 = c^2");
|
|
7
|
+
const [block, setBlock] = useState(false);
|
|
8
|
+
const presets = [
|
|
9
|
+
{
|
|
10
|
+
label: "Quadratic Formula",
|
|
11
|
+
tex: "x = \\frac{-b \\pm \\sqrt{b^2-4ac}}{2a}",
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
label: "Binomial Theorem",
|
|
15
|
+
tex: "(a+b)^n = \\sum_{k=0}^n \\binom{n}{k} a^{n-k} b^k",
|
|
16
|
+
},
|
|
17
|
+
{ label: "Euler's Identity", tex: "e^{i\\pi}+1=0" },
|
|
18
|
+
{ label: "Pythagoras", tex: "a^2 + b^2 = c^2" },
|
|
19
|
+
{ label: "Derivative", tex: "\\frac{d}{dx} x^n = n x^{n-1}" },
|
|
20
|
+
{ label: "Integral", tex: "\\int_a^b f(x)\\,dx" },
|
|
21
|
+
{ label: "Limit", tex: "\\lim_{x\\to 0} \\frac{\\sin x}{x} = 1" },
|
|
22
|
+
{
|
|
23
|
+
label: "Matrix 2x2",
|
|
24
|
+
tex: "\\begin{pmatrix} a & b \\ \\ c & d \\end{pmatrix}",
|
|
25
|
+
},
|
|
26
|
+
{ label: "Chem: Water", tex: "\\ce{H2O}" },
|
|
27
|
+
{ label: "Chem: Combustion", tex: "\\ce{CH4 + 2 O2 -> CO2 + 2 H2O}" },
|
|
28
|
+
];
|
|
29
|
+
const palette = [
|
|
30
|
+
"\\frac{a}{b}",
|
|
31
|
+
"\\sqrt{x}",
|
|
32
|
+
"\\int_a^b",
|
|
33
|
+
"\\sum_{i=1}^n",
|
|
34
|
+
"\\prod_{i=1}^n",
|
|
35
|
+
"\\lim_{x\\to 0}",
|
|
36
|
+
"x^{2}",
|
|
37
|
+
"x_{i}",
|
|
38
|
+
"\\alpha",
|
|
39
|
+
"\\beta",
|
|
40
|
+
"\\gamma",
|
|
41
|
+
"\\Delta",
|
|
42
|
+
"\\ce{H2O}",
|
|
43
|
+
"\\ce{CO2}",
|
|
44
|
+
];
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (!open)
|
|
47
|
+
return;
|
|
48
|
+
try {
|
|
49
|
+
const el = document.getElementById("katex-preview");
|
|
50
|
+
if (el)
|
|
51
|
+
katex.render(tex, el, {
|
|
52
|
+
throwOnError: false,
|
|
53
|
+
displayMode: block,
|
|
54
|
+
macros: { "\\ce": "\\mhchem" },
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch { }
|
|
58
|
+
}, [open, tex, block]);
|
|
59
|
+
if (!open)
|
|
60
|
+
return null;
|
|
61
|
+
return (_jsx("div", { style: {
|
|
62
|
+
position: "fixed",
|
|
63
|
+
inset: 0,
|
|
64
|
+
background: "rgba(0,0,0,0.4)",
|
|
65
|
+
display: "flex",
|
|
66
|
+
alignItems: "center",
|
|
67
|
+
justifyContent: "center",
|
|
68
|
+
zIndex: 100,
|
|
69
|
+
}, onClick: onClose, children: _jsxs("div", { style: {
|
|
70
|
+
background: "#fff",
|
|
71
|
+
color: "black",
|
|
72
|
+
padding: 16,
|
|
73
|
+
borderRadius: 8,
|
|
74
|
+
minWidth: 420,
|
|
75
|
+
}, onClick: (e) => e.stopPropagation(), children: [_jsx("div", { style: { fontWeight: 600, marginBottom: 8 }, children: "Insert formula" }), _jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, marginBottom: 8 }, children: palette.map((t) => (_jsx("button", { onClick: () => setTex((prev) => (prev ? prev + " " + t : t)), title: t, children: t }, t))) }), _jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 8, marginBottom: 8 }, children: presets.map((p) => (_jsx("button", { onClick: () => setTex(p.tex), title: p.tex, style: { fontSize: 12 }, children: p.label }, p.label))) }), _jsx("textarea", { value: tex, onChange: (e) => setTex(e.target.value), rows: 4, style: { width: "100%", fontFamily: "monospace", marginBottom: 8 } }), _jsxs("label", { style: {
|
|
76
|
+
display: "flex",
|
|
77
|
+
alignItems: "center",
|
|
78
|
+
gap: 8,
|
|
79
|
+
marginBottom: 8,
|
|
80
|
+
}, children: [_jsx("input", { type: "checkbox", checked: block, onChange: (e) => setBlock(e.target.checked) }), " ", "Block"] }), _jsx("div", { id: "katex-preview", style: {
|
|
81
|
+
padding: 12,
|
|
82
|
+
border: "1px solid #eee",
|
|
83
|
+
minHeight: 40,
|
|
84
|
+
marginBottom: 12,
|
|
85
|
+
} }), _jsxs("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" }, children: [_jsx("button", { onClick: onClose, children: "Cancel" }), _jsx("button", { onClick: () => onInsert(tex, block), children: "Insert" })] })] }) }));
|
|
86
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
const COLORS = {
|
|
3
|
+
info: "#e3f2fd",
|
|
4
|
+
warning: "#fff8e1",
|
|
5
|
+
success: "#e8f5e9",
|
|
6
|
+
danger: "#ffebee",
|
|
7
|
+
};
|
|
8
|
+
export function InfoBox({ box, onChange, }) {
|
|
9
|
+
const bg = COLORS[box.kind] || COLORS.info;
|
|
10
|
+
return (_jsxs("div", { style: {
|
|
11
|
+
background: bg,
|
|
12
|
+
border: "1px solid #e5e7eb",
|
|
13
|
+
padding: 12,
|
|
14
|
+
borderRadius: 8,
|
|
15
|
+
margin: "8px 0",
|
|
16
|
+
}, children: [_jsxs("select", { value: box.kind, onChange: (e) => onChange && onChange({ ...box, kind: e.target.value }), style: { marginBottom: 8 }, children: [_jsx("option", { value: "info", children: "Blue" }), _jsx("option", { value: "warning", children: "Yellow" }), _jsx("option", { value: "success", children: "Green" }), _jsx("option", { value: "danger", children: "Red" })] }), _jsx("div", { contentEditable: true, suppressContentEditableWarning: true, onInput: (e) => onChange &&
|
|
17
|
+
onChange({ ...box, text: e.target.innerText }), children: box.text })] }));
|
|
18
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type MCQOption = {
|
|
2
|
+
text: string;
|
|
3
|
+
correct?: boolean;
|
|
4
|
+
};
|
|
5
|
+
export declare function MCQBlock({ block, onChange, }: {
|
|
6
|
+
block: {
|
|
7
|
+
question: string;
|
|
8
|
+
options: MCQOption[];
|
|
9
|
+
multiple?: boolean;
|
|
10
|
+
};
|
|
11
|
+
onChange?: (next: any) => void;
|
|
12
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export function MCQBlock({ block, onChange, }) {
|
|
3
|
+
return (_jsxs("div", { style: {
|
|
4
|
+
border: "1px solid #e5e7eb",
|
|
5
|
+
padding: 12,
|
|
6
|
+
borderRadius: 8,
|
|
7
|
+
margin: "8px 0",
|
|
8
|
+
}, children: [_jsx("div", { contentEditable: true, suppressContentEditableWarning: true, style: { fontWeight: 600, marginBottom: 8 }, onInput: (e) => onChange &&
|
|
9
|
+
onChange({ ...block, question: e.target.innerText }), children: block.question }), _jsx("ul", { style: { listStyle: "none", padding: 0, margin: 0 }, children: block.options.map((opt, i) => (_jsxs("li", { style: {
|
|
10
|
+
display: "flex",
|
|
11
|
+
alignItems: "center",
|
|
12
|
+
gap: 8,
|
|
13
|
+
marginBottom: 6,
|
|
14
|
+
}, children: [_jsx("input", { type: block.multiple ? "checkbox" : "radio", checked: !!opt.correct, onChange: (e) => onChange &&
|
|
15
|
+
onChange({
|
|
16
|
+
...block,
|
|
17
|
+
options: block.options.map((o, idx) => idx === i
|
|
18
|
+
? { ...o, correct: e.target.checked }
|
|
19
|
+
: block.multiple
|
|
20
|
+
? o
|
|
21
|
+
: { ...o, correct: false }),
|
|
22
|
+
}) }), _jsx("div", { contentEditable: true, suppressContentEditableWarning: true, style: { flex: 1, borderBottom: "1px dashed #ddd" }, onInput: (e) => onChange &&
|
|
23
|
+
onChange({
|
|
24
|
+
...block,
|
|
25
|
+
options: block.options.map((o, idx) => idx === i
|
|
26
|
+
? { ...o, text: e.target.innerText }
|
|
27
|
+
: o),
|
|
28
|
+
}), children: opt.text })] }, i))) })] }));
|
|
29
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// Removed: SmartEditor component is not part of the public API.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
type TableNode = {
|
|
2
|
+
rows: Array<{
|
|
3
|
+
cells: Array<any>;
|
|
4
|
+
}>;
|
|
5
|
+
column_widths?: number[];
|
|
6
|
+
freeze_header?: boolean;
|
|
7
|
+
freeze_first_col?: boolean;
|
|
8
|
+
};
|
|
9
|
+
type Props = {
|
|
10
|
+
editor: any;
|
|
11
|
+
tableNode: TableNode;
|
|
12
|
+
tableIdx: number;
|
|
13
|
+
onChange?: () => void;
|
|
14
|
+
onCaretUpdate?: (ctx: {
|
|
15
|
+
tableIdx: number;
|
|
16
|
+
r: number;
|
|
17
|
+
c: number;
|
|
18
|
+
offset: number;
|
|
19
|
+
}) => void;
|
|
20
|
+
};
|
|
21
|
+
export declare function SmartTable({ editor, tableNode, tableIdx, onChange, onCaretUpdate, }: Props): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export {};
|