fix mem leak & ui / preview for editor

This commit is contained in:
h z
2024-12-05 08:58:31 +00:00
parent 413896c54b
commit 3c53ef7a87
6 changed files with 2017 additions and 63 deletions

1783
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -12,10 +12,17 @@
"author": "",
"license": "ISC",
"dependencies": {
"bulma": "^1.0.2",
"katex": "^0.16.11",
"oidc-client-ts": "^3.1.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^7.0.1"
"react-markdown": "^9.0.1",
"react-router-dom": "^7.0.1",
"react-syntax-highlighter": "^15.6.1",
"rehype-katex": "^7.0.1",
"rehype-raw": "^7.0.0",
"remark-math": "^6.0.0"
},
"devDependencies": {
"@babel/core": "^7.26.0",

View File

@@ -1,4 +1,4 @@
import React, { createContext, useEffect, useState } from "react";
import React, {createContext, useEffect, useMemo, useState} from "react";
import { UserManager } from "oidc-client-ts";
import {appConfig} from "./confs/appConfig";
@@ -13,7 +13,11 @@ export const AuthContext = createContext({
const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [roles, setRoles] = useState([]);
const userManager = new UserManager(appConfig.oidcConfig);
const userManager =
useMemo(() => new UserManager(appConfig.oidcConfig), []);
//new UserManager(appConfig.oidcConfig);
useEffect(() => {
userManager.getUser()
@@ -24,7 +28,8 @@ const AuthProvider = ({ children }) => {
const clientRoles = user?.profile?.resource_access?.[appConfig.kc_client_id]?.roles || [];
setRoles(clientRoles);
} else if (user && user.expired) {
userManager.signinSilent()
userManager
.signinSilent()
.then((newUser) => {
setUser(newUser);
localStorage.setItem("accessToken", newUser.access_token);

View File

@@ -0,0 +1,102 @@
.markdown-editor-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f8f9fa;
border: 1px solid #e0e0e0;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.markdown-preview {
padding: 15px;
border: 1px solid #ddd;
border-radius: 8px;
background-color: #ffffff;
white-space: pre-wrap;
word-wrap: break-word;
overflow-x: auto;
}
.markdown-editor-header {
text-align: center;
font-size: 1.8rem;
font-weight: bold;
margin-bottom: 20px;
color: #363636;
}
.markdown-editor-form .field {
margin-bottom: 1.5rem;
}
.markdown-editor-form .label {
font-size: 1rem;
font-weight: 600;
color: #4a4a4a;
}
.markdown-editor-form .input,
.markdown-editor-form .textarea {
font-size: 1rem;
border-radius: 6px;
border: 1px solid #dcdcdc;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.markdown-editor-form .input:focus,
.markdown-editor-form .textarea:focus {
border-color: #3273dc;
box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25);
outline: none;
}
.markdown-editor-form .button {
width: 100%;
font-size: 1.1rem;
padding: 10px 15px;
border-radius: 6px;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.markdown-editor-form .button:hover {
background-color: #276cda;
transform: scale(1.02);
}
.markdown-editor-notification {
text-align: center;
font-size: 1.2rem;
font-weight: bold;
margin-bottom: 20px;
padding: 10px 15px;
border-radius: 6px;
}
.katex-display {
margin: 1em 0;
text-align: center;
}
.katex {
font-size: 1.2rem;
}
code {
font-family: 'Courier New', Courier, monospace;
background-color: #f4f4f4;
padding: 2px 4px;
border-radius: 4px;
}
pre {
background-color: #2d2d2d;
color: #f8f8f2;
padding: 10px;
border-radius: 6px;
overflow-x: auto;
}

View File

@@ -2,11 +2,19 @@ import React, {useContext, useEffect, useState} from "react";
import { AuthContext } from "../../AuthProvider";
import { useNavigate, useParams } from "react-router-dom";
import { fetchWithCache } from "../../utils/fetchWithCache";
import ReactMarkdown from "react-markdown";
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
import rehypeRaw from "rehype-raw";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { okaidia } from "react-syntax-highlighter/dist/esm/styles/prism";
import "katex/dist/katex.min.css"; // LaTeX 样式
import "./MarkdownEditor.css";
const MarkdownEditor = () => {
const { roles } = useContext(AuthContext);
const navigate = useNavigate();
const {id} = useParams()
const { id } = useParams();
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const [path, setPath] = useState("");
@@ -19,7 +27,9 @@ const MarkdownEditor = () => {
setContent(data.content);
setPath(data.path);
})
.catch((err) => {console.error("failed to load markdown", err)});
.catch((err) => {
console.error("failed to load markdown", err);
});
}
}, [id]);
@@ -40,58 +50,114 @@ const MarkdownEditor = () => {
} else {
return res.json().then((data) => {
throw new Error(data.error || "Failed to save markdown");
})
});
}
})
.catch((err) => {console.error("failed to load markdown", err)});
.catch((err) => {
console.error("failed to load markdown", err);
});
};
const hasPermission = roles.includes("admin") || roles.includes("creator");
if (!hasPermission)
return <div className="notification is-danger">Permission Denied</div>;
return (
<div> Can not crete(Permission Denied)</div>
);
return (
<div>
<h2>{id ? "Edit Markdown" : "Create Markdown"}</h2>
<div className="container mt-5 markdown-editor-container">
<h2 className="title is-4">{id ? "Edit Markdown" : "Create Markdown"}</h2>
<div className="columns">
{/* Editor Column */}
<div className="column is-half">
<form>
<div>
<label>
Title:
{/* Title Field */}
<div className="field">
<label className="label">Title</label>
<div className="control">
<input
className="input"
type="text"
placeholder="Enter title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</label>
</div>
<div>
<label>
Path:
</div>
{/* Path Field */}
<div className="field">
<label className="label">Path</label>
<div className="control">
<input
className="input"
type="text"
placeholder="Enter path"
value={path}
onChange={(e) => setPath(e.target.value)}
/>
</label>
</div>
<div>
<label>
Content:
</div>
{/* Content Field */}
<div className="field">
<label className="label">Content</label>
<div className="control">
<textarea
className="textarea"
placeholder="Enter Markdown content"
value={content}
onChange={(e) => setContent(e.target.value)}
/>
</label>
></textarea>
</div>
<button type="button" onClick={handleSave}>
</div>
{/* Save Button */}
<div className="field">
<div className="control">
<button
className="button is-primary"
type="button"
onClick={handleSave}
>
Save
</button>
</div>
</div>
</form>
</div>
{/* Preview Column */}
<div className="column is-half">
<h3 className="subtitle is-5">Preview</h3>
<div className="content markdown-preview">
<ReactMarkdown
children={content}
remarkPlugins={[remarkMath]}
rehypePlugins={[rehypeKatex, rehypeRaw]}
components={{
code({ node, inline, className, children, ...props }) {
const match = /language-(\w+)/.exec(className || "");
return !inline && match ? (
<SyntaxHighlighter
style={okaidia}
language={match[1]}
PreTag="div"
{...props}
>
{String(children).replace(/\n$/, "")}
</SyntaxHighlighter>
) : (
<code className={className} {...props}>
{children}
</code>
);
}
},
}}
/>
</div>
</div>
</div>
</div>
);
};
export default MarkdownEditor;

View File

@@ -3,6 +3,9 @@ import ReactDOM from "react-dom/client";
import App from "./App";
import AuthProvider from "./AuthProvider";
import {config} from "./confs/appConfig";
import "bulma/css/bulma.min.css";
//ReactDOM.render(<App />, document.getElementById("root"));