@haklex/rich-renderer-katex 0.0.1
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/LICENSE +28 -0
- package/README.md +41 -0
- package/dist/KaTeXBlockEditNode.d.ts +9 -0
- package/dist/KaTeXBlockEditNode.d.ts.map +1 -0
- package/dist/KaTeXEditDecorator.d.ts +10 -0
- package/dist/KaTeXEditDecorator.d.ts.map +1 -0
- package/dist/KaTeXInlineEditNode.d.ts +9 -0
- package/dist/KaTeXInlineEditNode.d.ts.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +264 -0
- package/dist/rich-renderer-katex.css +256 -0
- package/dist/styles.css.d.ts +2 -0
- package/dist/styles.css.d.ts.map +1 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Innei
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
Additional Terms and Conditions
|
|
25
|
+
|
|
26
|
+
----------------
|
|
27
|
+
|
|
28
|
+
Use of this software is governed by the terms of MIT and, in addition, by the terms and conditions described in the additional file (ADDITIONAL_TERMS.md). By using this software, you agree to abide by these additional terms and conditions.
|
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# @haklex/rich-renderer-katex
|
|
2
|
+
|
|
3
|
+
KaTeX 数学公式编辑节点。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @haklex/rich-renderer-katex @haklex/rich-editor katex
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 导出
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
export { KaTeXBlockEditNode } from './KaTeXBlockEditNode'
|
|
15
|
+
export { KaTeXInlineEditNode } from './KaTeXInlineEditNode'
|
|
16
|
+
export { KaTeXEditDecorator } from './KaTeXEditDecorator'
|
|
17
|
+
|
|
18
|
+
export const katexEditNodes: Array<Klass<LexicalNode>>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 使用
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { katexEditNodes } from '@haklex/rich-renderer-katex'
|
|
25
|
+
import { RichEditor } from '@haklex/rich-editor'
|
|
26
|
+
|
|
27
|
+
<RichEditor extraNodes={[...katexEditNodes]} />
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 依赖
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"@haklex/rich-editor": "workspace:*",
|
|
35
|
+
"katex": ">=0.16.0"
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## License
|
|
40
|
+
|
|
41
|
+
MIT
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { KaTeXBlockNode, SerializedKaTeXBlockNode } from '@haklex/rich-editor';
|
|
2
|
+
import { EditorConfig, LexicalEditor } from 'lexical';
|
|
3
|
+
import { ReactElement } from 'react';
|
|
4
|
+
export declare class KaTeXBlockEditNode extends KaTeXBlockNode {
|
|
5
|
+
static clone(node: KaTeXBlockEditNode): KaTeXBlockEditNode;
|
|
6
|
+
static importJSON(serializedNode: SerializedKaTeXBlockNode): KaTeXBlockEditNode;
|
|
7
|
+
decorate(_editor: LexicalEditor, _config: EditorConfig): ReactElement;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=KaTeXBlockEditNode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KaTeXBlockEditNode.d.ts","sourceRoot":"","sources":["../src/KaTeXBlockEditNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,cAAc,EAEd,KAAK,wBAAwB,EAC9B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AAKzC,qBAAa,kBAAmB,SAAQ,cAAc;IACpD,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,kBAAkB,GAAG,kBAAkB;IAI1D,MAAM,CAAC,UAAU,CACf,cAAc,EAAE,wBAAwB,GACvC,kBAAkB;IAIrB,QAAQ,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,YAAY,GAAG,YAAY;CAYtE"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
interface KaTeXEditDecoratorProps {
|
|
3
|
+
nodeKey: string;
|
|
4
|
+
equation: string;
|
|
5
|
+
displayMode: boolean;
|
|
6
|
+
children: ReactElement;
|
|
7
|
+
}
|
|
8
|
+
export declare function KaTeXEditDecorator({ nodeKey, equation, displayMode, children, }: KaTeXEditDecoratorProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=KaTeXEditDecorator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KaTeXEditDecorator.d.ts","sourceRoot":"","sources":["../src/KaTeXEditDecorator.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AAGzC,UAAU,uBAAuB;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,OAAO,CAAA;IACpB,QAAQ,EAAE,YAAY,CAAA;CACvB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,OAAO,EACP,QAAQ,EACR,WAAW,EACX,QAAQ,GACT,EAAE,uBAAuB,2CAqOzB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { KaTeXInlineNode, SerializedKaTeXInlineNode } from '@haklex/rich-editor';
|
|
2
|
+
import { EditorConfig, LexicalEditor } from 'lexical';
|
|
3
|
+
import { ReactElement } from 'react';
|
|
4
|
+
export declare class KaTeXInlineEditNode extends KaTeXInlineNode {
|
|
5
|
+
static clone(node: KaTeXInlineEditNode): KaTeXInlineEditNode;
|
|
6
|
+
static importJSON(serializedNode: SerializedKaTeXInlineNode): KaTeXInlineEditNode;
|
|
7
|
+
decorate(_editor: LexicalEditor, _config: EditorConfig): ReactElement;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=KaTeXInlineEditNode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KaTeXInlineEditNode.d.ts","sourceRoot":"","sources":["../src/KaTeXInlineEditNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,eAAe,EAEf,KAAK,yBAAyB,EAC/B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AAKzC,qBAAa,mBAAoB,SAAQ,eAAe;IACtD,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,mBAAmB,GAAG,mBAAmB;IAI5D,MAAM,CAAC,UAAU,CACf,cAAc,EAAE,yBAAyB,GACxC,mBAAmB;IAItB,QAAQ,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,YAAY,GAAG,YAAY;CAYtE"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Klass, LexicalNode } from 'lexical';
|
|
2
|
+
export { KaTeXBlockEditNode } from './KaTeXBlockEditNode';
|
|
3
|
+
export { KaTeXEditDecorator } from './KaTeXEditDecorator';
|
|
4
|
+
export { KaTeXInlineEditNode } from './KaTeXInlineEditNode';
|
|
5
|
+
export declare const katexEditNodes: Array<Klass<LexicalNode>>;
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAA;AAErB,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAKjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAE3D,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAGpD,CAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { $isKaTeXInlineNode, $isKaTeXBlockNode, KaTeXRenderer, KaTeXBlockNode, createRendererDecoration, KaTeXInlineNode } from "@haklex/rich-editor";
|
|
2
|
+
import { useState, useRef, useEffect, useCallback, createElement } from "react";
|
|
3
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
4
|
+
import { Popover, PopoverTrigger, PopoverPanel } from "@haklex/rich-editor-ui";
|
|
5
|
+
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
6
|
+
import { $getNodeByKey } from "lexical";
|
|
7
|
+
import { Terminal, Trash2, Check, Copy, RotateCcw } from "lucide-react";
|
|
8
|
+
function KaTeXEditDecorator({
|
|
9
|
+
nodeKey,
|
|
10
|
+
equation,
|
|
11
|
+
displayMode,
|
|
12
|
+
children
|
|
13
|
+
}) {
|
|
14
|
+
const [editor] = useLexicalComposerContext();
|
|
15
|
+
const editable = editor.isEditable();
|
|
16
|
+
const [open, setOpen] = useState(false);
|
|
17
|
+
const [value, setValue] = useState(equation);
|
|
18
|
+
const [copied, setCopied] = useState(false);
|
|
19
|
+
const [activeTab, setActiveTab] = useState("editor");
|
|
20
|
+
const inputRef = useRef(null);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
setValue(equation);
|
|
23
|
+
}, [equation]);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (open && inputRef.current) {
|
|
26
|
+
inputRef.current.focus();
|
|
27
|
+
inputRef.current.select();
|
|
28
|
+
}
|
|
29
|
+
}, [open]);
|
|
30
|
+
const commit = useCallback(() => {
|
|
31
|
+
const trimmed = value.trim();
|
|
32
|
+
if (!trimmed) return;
|
|
33
|
+
editor.update(() => {
|
|
34
|
+
const node = $getNodeByKey(nodeKey);
|
|
35
|
+
if ($isKaTeXInlineNode(node) && node.getEquation() !== trimmed) {
|
|
36
|
+
node.setEquation(trimmed);
|
|
37
|
+
} else if ($isKaTeXBlockNode(node) && node.getEquation() !== trimmed) {
|
|
38
|
+
node.setEquation(trimmed);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
setOpen(false);
|
|
42
|
+
}, [editor, nodeKey, value]);
|
|
43
|
+
const cancel = useCallback(() => {
|
|
44
|
+
setValue(equation);
|
|
45
|
+
setOpen(false);
|
|
46
|
+
}, [equation]);
|
|
47
|
+
const handleDelete = useCallback(() => {
|
|
48
|
+
editor.update(() => {
|
|
49
|
+
const node = $getNodeByKey(nodeKey);
|
|
50
|
+
if (node) node.remove();
|
|
51
|
+
});
|
|
52
|
+
}, [editor, nodeKey]);
|
|
53
|
+
const handleCopy = useCallback(() => {
|
|
54
|
+
navigator.clipboard.writeText(value);
|
|
55
|
+
setCopied(true);
|
|
56
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
57
|
+
}, [value]);
|
|
58
|
+
const handleReset = useCallback(() => {
|
|
59
|
+
setValue(equation);
|
|
60
|
+
inputRef.current?.focus();
|
|
61
|
+
}, [equation]);
|
|
62
|
+
const handleKeyDown = useCallback(
|
|
63
|
+
(e) => {
|
|
64
|
+
if (e.key === "Escape") {
|
|
65
|
+
e.preventDefault();
|
|
66
|
+
cancel();
|
|
67
|
+
} else if (displayMode && e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
commit();
|
|
70
|
+
} else if (!displayMode && e.key === "Enter" && !e.shiftKey) {
|
|
71
|
+
e.preventDefault();
|
|
72
|
+
commit();
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
[commit, cancel, displayMode]
|
|
76
|
+
);
|
|
77
|
+
if (!editable) {
|
|
78
|
+
return children;
|
|
79
|
+
}
|
|
80
|
+
return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
|
|
81
|
+
/* @__PURE__ */ jsx(
|
|
82
|
+
PopoverTrigger,
|
|
83
|
+
{
|
|
84
|
+
render: /* @__PURE__ */ jsx("span", { className: "rich-katex-edit-wrapper", title: "Click to edit" }),
|
|
85
|
+
children
|
|
86
|
+
}
|
|
87
|
+
),
|
|
88
|
+
/* @__PURE__ */ jsxs(
|
|
89
|
+
PopoverPanel,
|
|
90
|
+
{
|
|
91
|
+
side: "bottom",
|
|
92
|
+
sideOffset: 8,
|
|
93
|
+
className: "rich-katex-editor-panel",
|
|
94
|
+
children: [
|
|
95
|
+
/* @__PURE__ */ jsxs("div", { className: "rich-katex-editor-header", children: [
|
|
96
|
+
/* @__PURE__ */ jsxs("div", { className: "rich-katex-editor-header-left", children: [
|
|
97
|
+
/* @__PURE__ */ jsx("div", { className: "rich-katex-editor-icon-wrap", children: /* @__PURE__ */ jsx(Terminal, { size: 12 }) }),
|
|
98
|
+
/* @__PURE__ */ jsx("span", { className: "rich-katex-editor-title", children: "Math Editor" })
|
|
99
|
+
] }),
|
|
100
|
+
/* @__PURE__ */ jsxs("div", { className: "rich-katex-editor-header-right", children: [
|
|
101
|
+
/* @__PURE__ */ jsx("span", { className: "rich-katex-editor-mode", children: displayMode ? "Block" : "Inline" }),
|
|
102
|
+
/* @__PURE__ */ jsx(
|
|
103
|
+
"button",
|
|
104
|
+
{
|
|
105
|
+
className: "rich-katex-editor-delete",
|
|
106
|
+
type: "button",
|
|
107
|
+
onClick: handleDelete,
|
|
108
|
+
title: "Delete node",
|
|
109
|
+
children: /* @__PURE__ */ jsx(Trash2, { size: 14 })
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
] })
|
|
113
|
+
] }),
|
|
114
|
+
/* @__PURE__ */ jsxs("div", { className: "rich-katex-editor-tabs", children: [
|
|
115
|
+
/* @__PURE__ */ jsx(
|
|
116
|
+
"button",
|
|
117
|
+
{
|
|
118
|
+
type: "button",
|
|
119
|
+
className: `rich-katex-editor-tab ${activeTab === "editor" ? "rich-katex-editor-tab-active" : ""}`,
|
|
120
|
+
onClick: () => setActiveTab("editor"),
|
|
121
|
+
children: "Editor"
|
|
122
|
+
}
|
|
123
|
+
),
|
|
124
|
+
/* @__PURE__ */ jsx(
|
|
125
|
+
"button",
|
|
126
|
+
{
|
|
127
|
+
type: "button",
|
|
128
|
+
className: `rich-katex-editor-tab ${activeTab === "snippets" ? "rich-katex-editor-tab-active" : ""}`,
|
|
129
|
+
onClick: () => setActiveTab("snippets"),
|
|
130
|
+
children: "Snippets"
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
] }),
|
|
134
|
+
activeTab === "editor" && /* @__PURE__ */ jsxs("div", { className: "rich-katex-editor-body", children: [
|
|
135
|
+
/* @__PURE__ */ jsxs("div", { className: "rich-katex-editor-field", children: [
|
|
136
|
+
/* @__PURE__ */ jsx("label", { className: "rich-katex-editor-label", children: "LaTeX Input" }),
|
|
137
|
+
/* @__PURE__ */ jsxs("div", { className: "rich-katex-editor-input-wrap", children: [
|
|
138
|
+
/* @__PURE__ */ jsx(
|
|
139
|
+
"textarea",
|
|
140
|
+
{
|
|
141
|
+
ref: inputRef,
|
|
142
|
+
className: "rich-katex-editor-textarea",
|
|
143
|
+
value,
|
|
144
|
+
onChange: (e) => setValue(e.target.value),
|
|
145
|
+
onKeyDown: handleKeyDown,
|
|
146
|
+
rows: displayMode ? 4 : 3,
|
|
147
|
+
spellCheck: false,
|
|
148
|
+
autoComplete: "off",
|
|
149
|
+
autoCorrect: "off",
|
|
150
|
+
autoCapitalize: "off",
|
|
151
|
+
placeholder: "e.g. \\frac{-b \\pm \\sqrt{b^2-4ac}}{2a}"
|
|
152
|
+
}
|
|
153
|
+
),
|
|
154
|
+
/* @__PURE__ */ jsxs("div", { className: "rich-katex-editor-input-actions", children: [
|
|
155
|
+
/* @__PURE__ */ jsx(
|
|
156
|
+
"button",
|
|
157
|
+
{
|
|
158
|
+
className: "rich-katex-editor-action-btn",
|
|
159
|
+
type: "button",
|
|
160
|
+
onClick: handleCopy,
|
|
161
|
+
title: "Copy LaTeX",
|
|
162
|
+
disabled: !value.trim(),
|
|
163
|
+
children: copied ? /* @__PURE__ */ jsx(Check, { size: 12, style: { color: "#22c55e" } }) : /* @__PURE__ */ jsx(Copy, { size: 12 })
|
|
164
|
+
}
|
|
165
|
+
),
|
|
166
|
+
/* @__PURE__ */ jsx(
|
|
167
|
+
"button",
|
|
168
|
+
{
|
|
169
|
+
className: "rich-katex-editor-action-btn",
|
|
170
|
+
type: "button",
|
|
171
|
+
onClick: handleReset,
|
|
172
|
+
title: "Reset",
|
|
173
|
+
disabled: value === equation,
|
|
174
|
+
children: /* @__PURE__ */ jsx(RotateCcw, { size: 12 })
|
|
175
|
+
}
|
|
176
|
+
)
|
|
177
|
+
] })
|
|
178
|
+
] })
|
|
179
|
+
] }),
|
|
180
|
+
/* @__PURE__ */ jsxs("div", { className: "rich-katex-editor-field", children: [
|
|
181
|
+
/* @__PURE__ */ jsx("label", { className: "rich-katex-editor-label", children: "Preview" }),
|
|
182
|
+
/* @__PURE__ */ jsx("div", { className: "rich-katex-editor-preview", children: value.trim() ? /* @__PURE__ */ jsx(KaTeXRenderer, { equation: value, displayMode }) : /* @__PURE__ */ jsx("span", { className: "rich-katex-editor-preview-empty", children: "Enter a formula" }) })
|
|
183
|
+
] })
|
|
184
|
+
] }),
|
|
185
|
+
activeTab === "snippets" && /* @__PURE__ */ jsx("div", { className: "rich-katex-editor-body", children: /* @__PURE__ */ jsx("div", { className: "rich-katex-editor-snippets-placeholder", children: "Snippets coming soon" }) }),
|
|
186
|
+
/* @__PURE__ */ jsxs("div", { className: "rich-katex-editor-footer", children: [
|
|
187
|
+
/* @__PURE__ */ jsx("span", { className: "rich-katex-editor-charcount", children: value.trim() ? `${value.length} chars` : "Enter a formula" }),
|
|
188
|
+
/* @__PURE__ */ jsxs("div", { className: "rich-katex-editor-footer-actions", children: [
|
|
189
|
+
/* @__PURE__ */ jsx(
|
|
190
|
+
"button",
|
|
191
|
+
{
|
|
192
|
+
className: "rich-katex-editor-btn",
|
|
193
|
+
type: "button",
|
|
194
|
+
onClick: cancel,
|
|
195
|
+
children: "Cancel"
|
|
196
|
+
}
|
|
197
|
+
),
|
|
198
|
+
/* @__PURE__ */ jsx(
|
|
199
|
+
"button",
|
|
200
|
+
{
|
|
201
|
+
className: "rich-katex-editor-btn rich-katex-editor-btn-primary",
|
|
202
|
+
type: "button",
|
|
203
|
+
onClick: commit,
|
|
204
|
+
disabled: !value.trim(),
|
|
205
|
+
children: "Save"
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
] })
|
|
209
|
+
] })
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
)
|
|
213
|
+
] });
|
|
214
|
+
}
|
|
215
|
+
class KaTeXBlockEditNode extends KaTeXBlockNode {
|
|
216
|
+
static clone(node) {
|
|
217
|
+
return new KaTeXBlockEditNode(node.__equation, node.__key);
|
|
218
|
+
}
|
|
219
|
+
static importJSON(serializedNode) {
|
|
220
|
+
return new KaTeXBlockEditNode(serializedNode.equation);
|
|
221
|
+
}
|
|
222
|
+
decorate(_editor, _config) {
|
|
223
|
+
const rendererEl = createRendererDecoration("KaTeX", KaTeXRenderer, {
|
|
224
|
+
equation: this.__equation,
|
|
225
|
+
displayMode: true
|
|
226
|
+
});
|
|
227
|
+
return createElement(KaTeXEditDecorator, {
|
|
228
|
+
nodeKey: this.__key,
|
|
229
|
+
equation: this.__equation,
|
|
230
|
+
displayMode: true,
|
|
231
|
+
children: rendererEl
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
class KaTeXInlineEditNode extends KaTeXInlineNode {
|
|
236
|
+
static clone(node) {
|
|
237
|
+
return new KaTeXInlineEditNode(node.__equation, node.__key);
|
|
238
|
+
}
|
|
239
|
+
static importJSON(serializedNode) {
|
|
240
|
+
return new KaTeXInlineEditNode(serializedNode.equation);
|
|
241
|
+
}
|
|
242
|
+
decorate(_editor, _config) {
|
|
243
|
+
const rendererEl = createRendererDecoration("KaTeX", KaTeXRenderer, {
|
|
244
|
+
equation: this.__equation,
|
|
245
|
+
displayMode: false
|
|
246
|
+
});
|
|
247
|
+
return createElement(KaTeXEditDecorator, {
|
|
248
|
+
nodeKey: this.__key,
|
|
249
|
+
equation: this.__equation,
|
|
250
|
+
displayMode: false,
|
|
251
|
+
children: rendererEl
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const katexEditNodes = [
|
|
256
|
+
KaTeXBlockEditNode,
|
|
257
|
+
KaTeXInlineEditNode
|
|
258
|
+
];
|
|
259
|
+
export {
|
|
260
|
+
KaTeXBlockEditNode,
|
|
261
|
+
KaTeXEditDecorator,
|
|
262
|
+
KaTeXInlineEditNode,
|
|
263
|
+
katexEditNodes
|
|
264
|
+
};
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
.rich-katex-edit-wrapper {
|
|
2
|
+
cursor: pointer;
|
|
3
|
+
border-radius: var(--rc-radius-sm);
|
|
4
|
+
transition: background-color 0.15s ease;
|
|
5
|
+
}
|
|
6
|
+
.rich-katex-edit-wrapper:hover {
|
|
7
|
+
background-color: var(--rc-accent-light);
|
|
8
|
+
}
|
|
9
|
+
.rich-katex-editor-panel {
|
|
10
|
+
width: min(420px, 90vw);
|
|
11
|
+
padding: 0;
|
|
12
|
+
overflow: hidden;
|
|
13
|
+
box-shadow: 0 25px 50px -12px rgba(0,0,0,0.2);
|
|
14
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
15
|
+
}
|
|
16
|
+
.rich-katex-editor-header {
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
justify-content: space-between;
|
|
20
|
+
padding: 10px 16px;
|
|
21
|
+
border-bottom: 1px solid color-mix(in srgb, var(--rc-border) 60%, transparent);
|
|
22
|
+
}
|
|
23
|
+
.rich-katex-editor-header-left {
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
gap: 8px;
|
|
27
|
+
}
|
|
28
|
+
.rich-katex-editor-icon-wrap {
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
justify-content: center;
|
|
32
|
+
width: 20px;
|
|
33
|
+
height: 20px;
|
|
34
|
+
border-radius: var(--rc-radius-sm);
|
|
35
|
+
background-color: var(--rc-accent-light);
|
|
36
|
+
color: var(--rc-accent);
|
|
37
|
+
}
|
|
38
|
+
.rich-katex-editor-title {
|
|
39
|
+
font-weight: 600;
|
|
40
|
+
font-size: var(--rc-font-size-small);
|
|
41
|
+
color: var(--rc-text);
|
|
42
|
+
}
|
|
43
|
+
.rich-katex-editor-header-right {
|
|
44
|
+
display: flex;
|
|
45
|
+
align-items: center;
|
|
46
|
+
gap: 4px;
|
|
47
|
+
}
|
|
48
|
+
.rich-katex-editor-mode {
|
|
49
|
+
font-size: 10px;
|
|
50
|
+
font-weight: 500;
|
|
51
|
+
text-transform: uppercase;
|
|
52
|
+
letter-spacing: 0.04em;
|
|
53
|
+
color: var(--rc-text-secondary);
|
|
54
|
+
padding: 1px 6px;
|
|
55
|
+
border-radius: var(--rc-radius-sm);
|
|
56
|
+
background-color: var(--rc-bg-secondary);
|
|
57
|
+
}
|
|
58
|
+
.rich-katex-editor-delete {
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
justify-content: center;
|
|
62
|
+
appearance: none;
|
|
63
|
+
border: none;
|
|
64
|
+
background: none;
|
|
65
|
+
color: var(--rc-text-secondary);
|
|
66
|
+
cursor: pointer;
|
|
67
|
+
width: 28px;
|
|
68
|
+
height: 28px;
|
|
69
|
+
border-radius: var(--rc-radius-sm);
|
|
70
|
+
transition: color 0.15s ease, background-color 0.15s ease;
|
|
71
|
+
flex-shrink: 0;
|
|
72
|
+
}
|
|
73
|
+
.rich-katex-editor-delete:hover {
|
|
74
|
+
color: var(--rc-alert-caution);
|
|
75
|
+
background-color: color-mix(in srgb, var(--rc-alert-caution) 10%, transparent);
|
|
76
|
+
}
|
|
77
|
+
.rich-katex-editor-tabs {
|
|
78
|
+
display: flex;
|
|
79
|
+
padding: 0 16px;
|
|
80
|
+
border-bottom: 1px solid color-mix(in srgb, var(--rc-border) 60%, transparent);
|
|
81
|
+
gap: 0;
|
|
82
|
+
}
|
|
83
|
+
.rich-katex-editor-tab {
|
|
84
|
+
position: relative;
|
|
85
|
+
appearance: none;
|
|
86
|
+
border: none;
|
|
87
|
+
border-bottom: 2px solid transparent;
|
|
88
|
+
background: none;
|
|
89
|
+
color: var(--rc-text-secondary);
|
|
90
|
+
cursor: pointer;
|
|
91
|
+
padding: 0 12px;
|
|
92
|
+
height: 36px;
|
|
93
|
+
font-size: 12px;
|
|
94
|
+
font-weight: 500;
|
|
95
|
+
transition: color 0.15s ease;
|
|
96
|
+
}
|
|
97
|
+
.rich-katex-editor-tab:hover {
|
|
98
|
+
color: var(--rc-text);
|
|
99
|
+
}
|
|
100
|
+
.rich-katex-editor-tab-active {
|
|
101
|
+
color: var(--rc-accent);
|
|
102
|
+
border-bottom-color: var(--rc-accent);
|
|
103
|
+
}
|
|
104
|
+
.rich-katex-editor-body {
|
|
105
|
+
padding: 12px;
|
|
106
|
+
display: flex;
|
|
107
|
+
flex-direction: column;
|
|
108
|
+
gap: 12px;
|
|
109
|
+
}
|
|
110
|
+
.rich-katex-editor-field {
|
|
111
|
+
display: flex;
|
|
112
|
+
flex-direction: column;
|
|
113
|
+
}
|
|
114
|
+
.rich-katex-editor-label {
|
|
115
|
+
font-size: 10px;
|
|
116
|
+
font-weight: 500;
|
|
117
|
+
text-transform: uppercase;
|
|
118
|
+
letter-spacing: 0.06em;
|
|
119
|
+
color: var(--rc-text-secondary);
|
|
120
|
+
margin-bottom: 6px;
|
|
121
|
+
padding-left: 2px;
|
|
122
|
+
}
|
|
123
|
+
.rich-katex-editor-input-wrap {
|
|
124
|
+
position: relative;
|
|
125
|
+
}
|
|
126
|
+
.rich-katex-editor-textarea {
|
|
127
|
+
display: block;
|
|
128
|
+
width: 100%;
|
|
129
|
+
padding: 10px 12px;
|
|
130
|
+
padding-right: 56px;
|
|
131
|
+
font-family: var(--rc-font-mono);
|
|
132
|
+
font-size: var(--rc-font-size-small);
|
|
133
|
+
line-height: 1.5;
|
|
134
|
+
background-color: color-mix(in srgb, var(--rc-bg-secondary) 30%, transparent);
|
|
135
|
+
color: var(--rc-text);
|
|
136
|
+
border: 1px solid color-mix(in srgb, var(--rc-border) 60%, transparent);
|
|
137
|
+
border-radius: var(--rc-radius-md);
|
|
138
|
+
outline: none;
|
|
139
|
+
resize: none;
|
|
140
|
+
box-sizing: border-box;
|
|
141
|
+
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
|
142
|
+
}
|
|
143
|
+
.rich-katex-editor-textarea::placeholder {
|
|
144
|
+
color: color-mix(in srgb, var(--rc-text-secondary) 50%, transparent);
|
|
145
|
+
}
|
|
146
|
+
.rich-katex-editor-textarea:focus {
|
|
147
|
+
border-color: color-mix(in srgb, var(--rc-accent) 50%, transparent);
|
|
148
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--rc-accent) 25%, transparent);
|
|
149
|
+
}
|
|
150
|
+
.rich-katex-editor-input-actions {
|
|
151
|
+
position: absolute;
|
|
152
|
+
right: 6px;
|
|
153
|
+
top: 6px;
|
|
154
|
+
display: flex;
|
|
155
|
+
gap: 2px;
|
|
156
|
+
}
|
|
157
|
+
.rich-katex-editor-action-btn {
|
|
158
|
+
display: flex;
|
|
159
|
+
align-items: center;
|
|
160
|
+
justify-content: center;
|
|
161
|
+
appearance: none;
|
|
162
|
+
border: none;
|
|
163
|
+
background: none;
|
|
164
|
+
color: color-mix(in srgb, var(--rc-text-secondary) 60%, transparent);
|
|
165
|
+
cursor: pointer;
|
|
166
|
+
width: 24px;
|
|
167
|
+
height: 24px;
|
|
168
|
+
border-radius: var(--rc-radius-sm);
|
|
169
|
+
transition: color 0.15s ease, background-color 0.15s ease;
|
|
170
|
+
}
|
|
171
|
+
.rich-katex-editor-action-btn:hover {
|
|
172
|
+
color: var(--rc-text);
|
|
173
|
+
background-color: var(--rc-bg-secondary);
|
|
174
|
+
}
|
|
175
|
+
.rich-katex-editor-action-btn:disabled {
|
|
176
|
+
opacity: 0.3;
|
|
177
|
+
cursor: default;
|
|
178
|
+
pointer-events: none;
|
|
179
|
+
}
|
|
180
|
+
.rich-katex-editor-preview {
|
|
181
|
+
border: 1px solid color-mix(in srgb, var(--rc-border) 60%, transparent);
|
|
182
|
+
border-radius: var(--rc-radius-md);
|
|
183
|
+
padding: var(--rc-space-md);
|
|
184
|
+
min-height: 60px;
|
|
185
|
+
display: flex;
|
|
186
|
+
align-items: center;
|
|
187
|
+
justify-content: center;
|
|
188
|
+
overflow-x: auto;
|
|
189
|
+
background-color: var(--rc-bg);
|
|
190
|
+
}
|
|
191
|
+
.rich-katex-editor-preview-empty {
|
|
192
|
+
color: color-mix(in srgb, var(--rc-text-secondary) 50%, transparent);
|
|
193
|
+
font-size: var(--rc-font-size-small);
|
|
194
|
+
}
|
|
195
|
+
.rich-katex-editor-snippets-placeholder {
|
|
196
|
+
color: var(--rc-text-secondary);
|
|
197
|
+
font-size: var(--rc-font-size-small);
|
|
198
|
+
text-align: center;
|
|
199
|
+
padding: var(--rc-space-lg) 0;
|
|
200
|
+
}
|
|
201
|
+
.rich-katex-editor-footer {
|
|
202
|
+
display: flex;
|
|
203
|
+
align-items: center;
|
|
204
|
+
justify-content: space-between;
|
|
205
|
+
padding: 10px 16px;
|
|
206
|
+
border-top: 1px solid color-mix(in srgb, var(--rc-border) 60%, transparent);
|
|
207
|
+
background-color: color-mix(in srgb, var(--rc-bg-secondary) 20%, transparent);
|
|
208
|
+
}
|
|
209
|
+
.rich-katex-editor-charcount {
|
|
210
|
+
color: var(--rc-text-secondary);
|
|
211
|
+
font-size: 10px;
|
|
212
|
+
font-family: var(--rc-font-mono);
|
|
213
|
+
overflow: hidden;
|
|
214
|
+
text-overflow: ellipsis;
|
|
215
|
+
white-space: nowrap;
|
|
216
|
+
max-width: 200px;
|
|
217
|
+
}
|
|
218
|
+
.rich-katex-editor-footer-actions {
|
|
219
|
+
display: flex;
|
|
220
|
+
align-items: center;
|
|
221
|
+
gap: 8px;
|
|
222
|
+
}
|
|
223
|
+
.rich-katex-editor-btn {
|
|
224
|
+
appearance: none;
|
|
225
|
+
border: none;
|
|
226
|
+
background: none;
|
|
227
|
+
color: var(--rc-text-secondary);
|
|
228
|
+
cursor: pointer;
|
|
229
|
+
padding: 4px 10px;
|
|
230
|
+
border-radius: var(--rc-radius-sm);
|
|
231
|
+
font-size: 12px;
|
|
232
|
+
font-weight: 500;
|
|
233
|
+
line-height: 1.4;
|
|
234
|
+
height: 28px;
|
|
235
|
+
display: inline-flex;
|
|
236
|
+
align-items: center;
|
|
237
|
+
transition: color 0.15s ease, background-color 0.15s ease;
|
|
238
|
+
}
|
|
239
|
+
.rich-katex-editor-btn:hover {
|
|
240
|
+
color: var(--rc-text);
|
|
241
|
+
background-color: var(--rc-bg-secondary);
|
|
242
|
+
}
|
|
243
|
+
.rich-katex-editor-btn-primary {
|
|
244
|
+
background-color: var(--rc-accent);
|
|
245
|
+
color: #fff;
|
|
246
|
+
}
|
|
247
|
+
.rich-katex-editor-btn-primary:hover {
|
|
248
|
+
background-color: var(--rc-accent);
|
|
249
|
+
filter: brightness(0.9);
|
|
250
|
+
color: #fff;
|
|
251
|
+
}
|
|
252
|
+
.rich-katex-editor-btn-primary:disabled {
|
|
253
|
+
opacity: 0.5;
|
|
254
|
+
cursor: default;
|
|
255
|
+
pointer-events: none;
|
|
256
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.css.d.ts","sourceRoot":"","sources":["../src/styles.css.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@haklex/rich-renderer-katex",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "KaTeX math renderer for haklex rich editor",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.mjs",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./style.css": "./dist/rich-renderer-katex.css"
|
|
13
|
+
},
|
|
14
|
+
"main": "./dist/index.mjs",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"lucide-react": "^0.574.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@lexical/react": "^0.39.0",
|
|
23
|
+
"@types/react": "^19.2.9",
|
|
24
|
+
"@types/react-dom": "^19.2.3",
|
|
25
|
+
"@vanilla-extract/css": "^1.17.1",
|
|
26
|
+
"@vanilla-extract/vite-plugin": "^4.0.20",
|
|
27
|
+
"katex": ">=0.16.0",
|
|
28
|
+
"lexical": "^0.39.0",
|
|
29
|
+
"react": ">=19",
|
|
30
|
+
"react-dom": ">=19",
|
|
31
|
+
"typescript": "^5.9.0",
|
|
32
|
+
"vite": "^7.3.1",
|
|
33
|
+
"vite-plugin-dts": "^4.5.0",
|
|
34
|
+
"@haklex/rich-editor": "0.0.1",
|
|
35
|
+
"@haklex/rich-editor-ui": "0.0.1",
|
|
36
|
+
"@haklex/rich-style-token": "0.0.1"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"@lexical/react": "^0.39.0",
|
|
40
|
+
"katex": ">=0.16.0",
|
|
41
|
+
"lexical": "^0.39.0",
|
|
42
|
+
"react": ">=19",
|
|
43
|
+
"react-dom": ">=19",
|
|
44
|
+
"@haklex/rich-editor": "0.0.1",
|
|
45
|
+
"@haklex/rich-editor-ui": "0.0.1",
|
|
46
|
+
"@haklex/rich-style-token": "0.0.1"
|
|
47
|
+
},
|
|
48
|
+
"peerDependenciesMeta": {
|
|
49
|
+
"katex": {
|
|
50
|
+
"optional": true
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "vite build",
|
|
58
|
+
"dev:build": "vite build --watch"
|
|
59
|
+
},
|
|
60
|
+
"types": "./dist/index.d.ts"
|
|
61
|
+
}
|