Added markdown preview for text editor
This commit is contained in:
parent
73083693bb
commit
a26e3a3809
8 changed files with 102 additions and 16 deletions
12
package-lock.json
generated
12
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
13
public/documents/info.md
Normal 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).
|
||||
|
|
@ -21,6 +21,7 @@ function FilePreview({ file }) {
|
|||
</div>)
|
||||
break;
|
||||
case "txt":
|
||||
case "md":
|
||||
preview = <FontAwesomeIcon icon={faFileLines}/>
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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={() => {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ export default class ApplicationsManager {
|
|||
app = this.getApplication("media-viewer");
|
||||
break;
|
||||
case "txt":
|
||||
case "md":
|
||||
app = this.getApplication("text-editor");
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue