Added markdown preview for text editor

This commit is contained in:
Prozilla 2023-07-31 19:09:50 +02:00
parent 73083693bb
commit a26e3a3809
No known key found for this signature in database
GPG key ID: 5858DFE71CAF31EE
8 changed files with 102 additions and 16 deletions

12
package-lock.json generated
View file

@ -16,6 +16,7 @@
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"core-js": "^3.31.1",
"markdown-to-jsx": "^7.2.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-draggable": "^4.4.5",
@ -12259,6 +12260,17 @@
"tmpl": "1.0.5"
}
},
"node_modules/markdown-to-jsx": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.2.1.tgz",
"integrity": "sha512-9HrdzBAo0+sFz9ZYAGT5fB8ilzTW+q6lPocRxrIesMO+aB40V9MgFfbfMXxlGjf22OpRy+IXlvVaQenicdpgbg==",
"engines": {
"node": ">= 10"
},
"peerDependencies": {
"react": ">= 0.14.0"
}
},
"node_modules/mdn-data": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz",

View file

@ -14,6 +14,7 @@
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"core-js": "^3.31.1",
"markdown-to-jsx": "^7.2.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-draggable": "^4.4.5",

13
public/documents/info.md Normal file
View file

@ -0,0 +1,13 @@
<img src="/media/Banner2.png" width="100%" height="200" alt="Banner with ProzillaOS' name and logo"/>
# Info
This is ProzillaOS, a web-based operating system made with React.js by Prozilla.
## Open Source
ProzillaOS is [open source](https://github.com/Prozilla/Prozilla-OS)! Feel free to fork this project and create your own OS.
## Support ProzillaOS
You can support this project by donating to [ko-fi.com/prozilla](https://ko-fi.com/prozilla).

View file

@ -21,6 +21,7 @@ function FilePreview({ file }) {
</div>)
break;
case "txt":
case "md":
preview = <FontAwesomeIcon icon={faFileLines}/>
break;
default:

View file

@ -1,28 +1,55 @@
import { useEffect, useState } from "react";
import React, { useEffect, useState } from "react";
// eslint-disable-next-line no-unused-vars
import { VirtualFile } from "../../../features/virtual-drive/virtual-file.js";
import styles from "./TextEditor.module.css";
import { HeaderMenu } from "../.common/HeaderMenu.jsx";
import Markdown from "markdown-to-jsx";
/**
* @param {Object} props
* @param {VirtualFile} props.file
*/
export function TextEditor({ file, setTitle, close }) {
export function TextEditor({ file, setTitle, close, mode }) {
const [currentFile, setCurrentFile] = useState(file);
const [content, setContent] = useState(file?.content);
const [currentMode, setCurrentMode] = useState(mode);
const [content, setContent] = useState(file?.content ?? "");
const [unsavedChanges, setUnsavedChanges] = useState(false);
useEffect(() => {
setContent(currentFile?.content ?? "");
(async () => {
let newContent = "";
if (currentFile) {
if (currentFile.content) {
newContent = currentFile.content;
} else if (currentFile.source) {
await fetch(currentFile.source).then((response) =>
response.text()
).then((response) => {
newContent = response;
});
}
}
setContent(newContent);
})();
}, [currentFile]);
useEffect(() => {
setTitle(`${currentFile?.id ?? "Untitled"}${unsavedChanges ? "*" : ""} - Text Editor`);
}, [currentFile, setTitle, unsavedChanges]);
let label = currentFile?.id ?? "Untitled";
if (unsavedChanges)
label += "*";
if (currentMode === "view")
label += " (preview)";
setTitle(`${label} - Text Editor`);
}, [currentFile, setTitle, unsavedChanges, currentMode]);
const newText = () => {
setCurrentFile(null);
setCurrentMode("edit");
}
const saveTextAs = () => {
@ -64,15 +91,28 @@ export function TextEditor({ file, setTitle, close }) {
onSaveAs={saveTextAs}
onExit={() => { close(); }}
/>
<textarea
className={styles.View}
value={content}
onChange={onChange}
onKeyDown={onKeyDown}
spellCheck={false}
autoComplete="off"
autoFocus
/>
{currentMode === "view"
? <div className={styles.View}>
<Markdown options={{ overrides: {
a: {
props: {
target: "_blank"
}
}
} }}>
{content}
</Markdown>
</div>
: <textarea
className={styles.View}
value={content}
onChange={onChange}
onKeyDown={onKeyDown}
spellCheck={false}
autoComplete="off"
autoFocus
/>
}
</div>
);
}

View file

@ -1,6 +1,9 @@
.Container {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
text-align: start;
}
.View {
@ -14,4 +17,11 @@
font-size: 1rem;
font-family: var(--body-font-family);
resize: none;
overflow: auto;
}
.View img {
max-width: 100%;
object-fit: cover;
border-radius: 0.5rem;
}

View file

@ -6,9 +6,11 @@ import ApplicationsManager from "../../features/applications/applications.js";
import { ReactSVG } from "react-svg";
import { closeTab } from "../../features/utils/browser.js";
import { useKeyboardListener } from "../../hooks/utils/keyboard.js";
import { useVirtualRoot } from "../../hooks/virtual-drive/VirtualRootContext.js";
export function HomeMenu({ active, setActive }) {
const windowsManager = useWindowsManager();
const virtualRoot = useVirtualRoot();
const classNames = [styles["Container-outer"]];
if (active)
@ -46,7 +48,13 @@ export function HomeMenu({ active, setActive }) {
<button title="Settings">
<FontAwesomeIcon icon={faGear}/>
</button>
<button title="Info">
<button title="Info" onClick={() => {
setActive(false);
windowsManager.open("text-editor", {
mode: "view",
file: virtualRoot.navigate("~/Documents").findFile("info", "md")
});
}}>
<FontAwesomeIcon icon={faCircleInfo}/>
</button>
<button title="Images" onClick={() => {

View file

@ -50,6 +50,7 @@ export default class ApplicationsManager {
app = this.getApplication("media-viewer");
break;
case "txt":
case "md":
app = this.getApplication("text-editor");
break;
}