Updated text editor app

This commit is contained in:
Prozilla 2023-12-26 14:35:02 +01:00
parent 3d7229a385
commit 133ce49855
No known key found for this signature in database
GPG key ID: 5858DFE71CAF31EE
18 changed files with 512 additions and 30 deletions

View file

@ -1,6 +1,6 @@
<img alt="License" src="https://img.shields.io/github/license/prozilla/Prozilla-OS?style=flat-square&color=ee5253&label=License"> <img alt="Stars" src="https://img.shields.io/github/stars/prozilla/prozilla-os?style=flat-square&color=feca57&label=%E2%AD%90"> <img alt="Forks" src="https://img.shields.io/github/forks/prozilla/prozilla-os?style=flat-square&color=54a0ff&label=Forks">
# <img src="public/assets/logo.svg" align="left" width=40 height=40 alt="Logo"/> Prozilla OS
# <img src="public/assets/logo.svg" align="left" width="40" height="40" alt="Logo"/> Prozilla OS
Prozilla OS is a web-based operating system inspired by Ubuntu Linux and Windows made with React by Prozilla.

View file

@ -1,6 +1,6 @@
[← Back](../README.md)
# <img src="../../../../public/assets/apps/icons/file-explorer.svg" width=30 height=30 style="vertical-align: middle; background: none;"/> File Explorer ("Files")
# <img src="../../../../public/assets/apps/icons/file-explorer.svg" width="30" height="30" style="vertical-align: middle; background: none;"/> File Explorer ("Files")
A tool for exploring and managing files and folders with a user interface.

View file

@ -1,6 +1,6 @@
[← Back](../README.md)
# <img src="../../../../public/assets/apps/icons/media-viewer.svg" width=30 height=30 style="vertical-align: middle; background: none;"/> Media Viewer ("Photos")
# <img src="../../../../public/assets/apps/icons/media-viewer.svg" width="30" height="30" style="vertical-align: middle; background: none;"/> Media Viewer ("Photos")
An app for viewing media like images and videos.

View file

@ -1,6 +1,6 @@
[← Back](../README.md)
# <img src="../../../../public/assets/apps/icons/settings.svg" width=30 height=30 style="vertical-align: middle; background: none;"/> Settings
# <img src="../../../../public/assets/apps/icons/settings.svg" width="30" height="30" style="vertical-align: middle; background: none;"/> Settings
Allows the user to change their settings and personalize their instance of Prozilla OS.

View file

@ -1,6 +1,6 @@
[← Back](../README.md)
# <img src="../../../../public/assets/apps/icons/terminal.svg" width=30 height=30 style="vertical-align: middle; background: none;"/> Terminal ("Commands")
# <img src="../../../../public/assets/apps/icons/terminal.svg" width="30" height="30" style="vertical-align: middle; background: none;"/> Terminal ("Commands")
A command line tool.

View file

@ -1,6 +1,6 @@
[← Back](../README.md)
# <img src="../../../../public/assets/apps/icons/text-editor.svg" width=30 height=30 style="vertical-align: middle; background: none;"/> Text Editor ("Notes")
# <img src="../../../../public/assets/apps/icons/text-editor.svg" width="30" height="30" style="vertical-align: middle; background: none;"/> Text Editor ("Notes")
An app for editing text documents.

247
package-lock.json generated
View file

@ -24,6 +24,7 @@
"react-draggable": "^4.4.5",
"react-scripts": "5.0.1",
"react-svg": "^16.1.18",
"react-syntax-highlighter": "^15.5.0",
"react-tabs": "^6.0.2",
"web-vitals": "^2.1.4"
},
@ -4148,6 +4149,14 @@
"@types/node": "*"
}
},
"node_modules/@types/hast": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.8.tgz",
"integrity": "sha512-aMIqAlFd2wTIDZuvLbhUT+TGvMxrNC8ECUIVtH6xxy0sQLs3iu6NO8Kp/VT5je7i5ufnebXzdV1dNDMnvaH6IQ==",
"dependencies": {
"@types/unist": "^2"
}
},
"node_modules/@types/html-minifier-terser": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
@ -4568,6 +4577,11 @@
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz",
"integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g=="
},
"node_modules/@types/unist": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz",
"integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA=="
},
"node_modules/@types/ws": {
"version": "8.5.5",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz",
@ -5992,6 +6006,33 @@
"node": ">=10"
}
},
"node_modules/character-entities": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz",
"integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/character-entities-legacy": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
"integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/character-reference-invalid": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz",
"integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/check-types": {
"version": "11.2.2",
"resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.2.tgz",
@ -6159,6 +6200,15 @@
"node": ">= 0.8"
}
},
"node_modules/comma-separated-tokens": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz",
"integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/commander": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
@ -8255,6 +8305,18 @@
"reusify": "^1.0.4"
}
},
"node_modules/fault": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz",
"integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==",
"dependencies": {
"format": "^0.2.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/faye-websocket": {
"version": "0.11.4",
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
@ -8650,6 +8712,14 @@
"node": ">= 6"
}
},
"node_modules/format": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz",
"integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==",
"engines": {
"node": ">=0.4.x"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@ -9131,6 +9201,31 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hast-util-parse-selector": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz",
"integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/hastscript": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz",
"integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==",
"dependencies": {
"@types/hast": "^2.0.0",
"comma-separated-tokens": "^1.0.0",
"hast-util-parse-selector": "^2.0.0",
"property-information": "^5.0.0",
"space-separated-tokens": "^1.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@ -9139,6 +9234,14 @@
"he": "bin/he"
}
},
"node_modules/highlight.js": {
"version": "10.7.3",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
"engines": {
"node": "*"
}
},
"node_modules/hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@ -9527,6 +9630,28 @@
"node": ">= 10"
}
},
"node_modules/is-alphabetical": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
"integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/is-alphanumerical": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz",
"integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==",
"dependencies": {
"is-alphabetical": "^1.0.0",
"is-decimal": "^1.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/is-arguments": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
@ -9648,6 +9773,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-decimal": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz",
"integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/is-docker": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
@ -9697,6 +9831,15 @@
"node": ">=0.10.0"
}
},
"node_modules/is-hexadecimal": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
"integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/is-map": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
@ -12310,6 +12453,19 @@
"tslib": "^2.0.3"
}
},
"node_modules/lowlight": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz",
"integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==",
"dependencies": {
"fault": "^1.0.0",
"highlight.js": "~10.7.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@ -13018,6 +13174,23 @@
"node": ">=6"
}
},
"node_modules/parse-entities": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
"integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
"dependencies": {
"character-entities": "^1.0.0",
"character-entities-legacy": "^1.0.0",
"character-reference-invalid": "^1.0.0",
"is-alphanumerical": "^1.0.0",
"is-decimal": "^1.0.0",
"is-hexadecimal": "^1.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@ -14514,6 +14687,14 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/prismjs": {
"version": "1.29.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
"integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==",
"engines": {
"node": ">=6"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@ -14554,6 +14735,18 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/property-information": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz",
"integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==",
"dependencies": {
"xtend": "^4.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@ -14973,6 +15166,21 @@
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-syntax-highlighter": {
"version": "15.5.0",
"resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz",
"integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"highlight.js": "^10.4.1",
"lowlight": "^1.17.0",
"prismjs": "^1.27.0",
"refractor": "^3.6.0"
},
"peerDependencies": {
"react": ">= 0.14.0"
}
},
"node_modules/react-tabs": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/react-tabs/-/react-tabs-6.0.2.tgz",
@ -15048,6 +15256,28 @@
"node": ">=8"
}
},
"node_modules/refractor": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz",
"integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==",
"dependencies": {
"hastscript": "^6.0.0",
"parse-entities": "^2.0.0",
"prismjs": "~1.27.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/refractor/node_modules/prismjs": {
"version": "1.27.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz",
"integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==",
"engines": {
"node": ">=6"
}
},
"node_modules/regenerate": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
@ -15848,6 +16078,15 @@
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
"deprecated": "Please use @jridgewell/sourcemap-codec instead"
},
"node_modules/space-separated-tokens": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz",
"integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/spdx-exceptions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
@ -17868,6 +18107,14 @@
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"engines": {
"node": ">=0.4"
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",

View file

@ -30,6 +30,7 @@
"react-draggable": "^4.4.5",
"react-scripts": "5.0.1",
"react-svg": "^16.1.18",
"react-syntax-highlighter": "^15.5.0",
"react-tabs": "^6.0.2",
"web-vitals": "^2.1.4"
},

View file

@ -4,6 +4,14 @@ import styles from "../Actions.module.css";
import { ImagePreview } from "../../apps/file-explorer/directory-list/ImagePreview.jsx";
import { memo } from "react";
/**
* @param {object} props
* @param {string} props.actionId
* @param {string} props.label
* @param {string[]} props.shortcut
* @param {Function} props.onTrigger
* @param {string|object} props.icon
*/
export const ClickAction = memo(({ actionId, label, shortcut, onTrigger, icon }) => {
return (<button key={actionId} className={styles.Button} tabIndex={0} onClick={onTrigger}>
<span className={styles.Label}>

View file

@ -3,6 +3,12 @@ import styles from "../Actions.module.css";
import { faCaretRight } from "@fortawesome/free-solid-svg-icons";
import { useState } from "react";
/**
* @param {object} props
* @param {string} props.label
* @param {string|object} props.icon
* @param {*} props.children
*/
export function DropdownAction({ label, icon, children }) {
const [showContent, setShowContent] = useState(false);

View file

@ -5,6 +5,14 @@ import { useState } from "react";
import { faSquare } from "@fortawesome/free-regular-svg-icons";
import { faSquareCheck } from "@fortawesome/free-solid-svg-icons";
/**
* @param {object} props
* @param {string} props.actionId
* @param {string} props.label
* @param {string[]} props.shortcut
* @param {Function} props.onTrigger
* @param {boolean} props.initialValue
*/
export function ToggleAction({ actionId, label, shortcut, initialValue, onTrigger }) {
const [active, setActive] = useState(initialValue ?? false);

View file

@ -20,6 +20,7 @@ import { TITLE_SEPARATOR } from "../../../config/windows.config.js";
import { FileProperties } from "../../modals/file-properties/FileProperties.jsx";
import { useHistory } from "../../../hooks/_utils/history.js";
import { Divider } from "../../actions/actions/Divider.jsx";
import { CODE_FORMATS } from "../../../config/apps/textEditor.config.js";
/**
* @param {import("../../windows/WindowView.jsx").windowProps} props
@ -223,7 +224,7 @@ export function FileExplorer({ startPath, app, modalsManager }) {
onClickFile={(event, file) => {
event.preventDefault();
const options = {};
if (file.extension === "md")
if (file.extension === "md" || CODE_FORMATS.includes(file.extension))
options.mode = "view";
windowsManager.openFile(file, options);
}}

View file

@ -1,15 +1,26 @@
import React, { useEffect, useState } from "react";
import { useEffect, useRef, useState } from "react";
import styles from "./TextEditor.module.css";
import { HeaderMenu } from "../_utils/header-menu/HeaderMenu.jsx";
import Markdown from "markdown-to-jsx";
import { DEFAULT_ZOOM, ZOOM_FACTOR } from "../../../config/apps/textEditor.config.js";
import { CODE_FORMATS, DEFAULT_ZOOM, EXTENSION_TO_LANGUAGE, ZOOM_FACTOR } from "../../../config/apps/textEditor.config.js";
import AppsManager from "../../../features/apps/appsManager.js";
import { TITLE_SEPARATOR } from "../../../config/windows.config.js";
import { MarkdownLink } from "./overrides/MarkdownLink.jsx";
import { MarkdownImage } from "./overrides/MarkdownImage.jsx";
import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext.js";
import SyntaxHighlighter from "react-syntax-highlighter";
const OVERRIDES = {
a: MarkdownLink,
img: MarkdownImage,
};
/**
* @param {import("../../windows/WindowView.jsx").windowProps} props
*/
export function TextEditor({ file, setTitle, setIconUrl, close, mode, app }) {
export function TextEditor({ file, setTitle, setIconUrl, close, mode, app, modalsManager }) {
const ref = useRef();
const windowsManager = useWindowsManager();
const [currentFile, setCurrentFile] = useState(file);
const [currentMode, setCurrentMode] = useState(mode);
const [content, setContent] = useState(file?.content ?? "");
@ -40,6 +51,10 @@ export function TextEditor({ file, setTitle, setIconUrl, close, mode, app }) {
}
setContent(newContent);
if (ref.current) {
ref.current.scrollTo(0, 0);
}
})();
}, [app.id, currentFile, setIconUrl]);
@ -86,6 +101,20 @@ export function TextEditor({ file, setTitle, setIconUrl, close, mode, app }) {
return setContent(value);
};
const overrides = {};
for (const [key, value] of Object.entries(OVERRIDES)) {
overrides[key] = {
component: value,
props: {
modalsManager,
setCurrentFile,
currentFile,
app,
windowsManager
}
};
}
return (
<div className={styles.Container} style={{ fontSize: zoom }}>
<HeaderMenu
@ -127,21 +156,21 @@ export function TextEditor({ file, setTitle, setIconUrl, close, mode, app }) {
}}
/>
{currentMode === "view"
? <div className={styles.View}>
{file?.extension === "md"
? <Markdown options={{ overrides: {
a: {
props: {
target: "_blank"
}
}
} }}>
{content}
</Markdown>
: <p>{content}</p>
}
</div>
? CODE_FORMATS.includes(file?.extension)
? <SyntaxHighlighter
language={EXTENSION_TO_LANGUAGE[file?.extension] ?? file?.extension}
className={styles.Code}
useInlineStyles={false}
showLineNumbers={true}
>{content}</SyntaxHighlighter>
: <div ref={ref} className={styles.View}>
{file?.extension === "md"
? <Markdown options={{ overrides }}>{content}</Markdown>
: <p>{content}</p>
}
</div>
: <textarea
ref={ref}
className={styles.View}
value={content}
onChange={onChange}

View file

@ -13,12 +13,12 @@
font-size: 1em;
}
.View {
.View, .Code {
width: 100%;
height: 100%;
padding: 1rem;
color: var(--foreground-color-a);
background-color: var(--background-color-c);
padding: 1rem !important;
color: var(--foreground-color-a) !important;
background-color: var(--background-color-c) !important;
border: none;
outline: none;
font-size: 1rem;
@ -30,5 +30,56 @@
.View img {
max-width: 100%;
object-fit: contain;
border-radius: 0.25rem;
margin-right: 0.5rem;
}
.View blockquote {
margin-left: 0;
padding: 0.5rem;
padding-left: 1.5rem;
border-left: 1rem solid var(--background-color-a);
background-color: var(--background-color-b);
border-radius: 0.5rem;
}
}
.View blockquote > p {
width: fit-content;
margin: 0;
}
.View code {
font-family: var(--mono-font-family);
border-radius: 0.5rem;
background-color: var(--background-color-b);
padding: 0.25rem;
}
.View table {
border-collapse: collapse;
}
.View table, .View th, .View td {
border: 0.2rem solid var(--background-color-a);
}
.View th, .View td {
padding: 0.5rem;
}
.Code {
margin: 0;
cursor: default;
}
.Code *[class*="hljs-string"] { color: var(--green-a); }
.Code *[class*="hljs-literal"] { color: var(--yellow-a); }
.Code *[class*="hljs-number"] { color: var(--yellow-a); }
.Code *[class*="hljs-keyword"] { color: var(--blue-a); }
.Code *[class*="hljs-title"] { color: var(--cyan-a); }
.Code *[class*="hljs-name"] { color: var(--red-a); }
.Code *[class*="hljs-attr"] { color: var(--blue-a); }
.Code *[class*="hljs-param"] { color: var(--red-a); }
.Code *[class*="hljs-comment"] { color: var(--dark-grey-a); }
.Code *[class*="linenumber"] { color: var(--dark-grey-a); }
.Code *[class*="hljs-meta"] { color: var(--blue-a); }

View file

@ -0,0 +1,30 @@
import { useMemo } from "react";
import { VirtualFile } from "../../../../features/virtual-drive/file/virtualFile.js";
import ModalsManager from "../../../../features/modals/modalsManager.js";
import App from "../../../../features/apps/app.js";
import WindowsManager from "../../../../features/windows/windowsManager.js";
/**
* @param {object} props
* @param {VirtualFile} props.currentFile
* @param {string} props.src
* @param {string} props.alt
* @param {ModalsManager} props.modalsManager
* @param {WindowsManager} props.windowsManager
* @param {App} props.app
*/
export function MarkdownImage({ currentFile, alt, src, modalsManager, windowsManager, app, ...props }) {
const source = useMemo(() => {
if (src.startsWith("public")) {
return src.replace(/^public\//g, `${process.env.PUBLIC_URL}/`);
}
return src;
}, [src]);
return <img
alt={alt}
{...props}
src={source}
/>;
}

View file

@ -0,0 +1,85 @@
import { useMemo } from "react";
import { faExternalLink } from "@fortawesome/free-solid-svg-icons";
import { useContextMenu } from "../../../../hooks/modals/contextMenu.js";
import { Actions } from "../../../actions/Actions.jsx";
import { ClickAction } from "../../../actions/actions/ClickAction.jsx";
import ModalsManager from "../../../../features/modals/modalsManager.js";
import { VirtualFile } from "../../../../features/virtual-drive/file/virtualFile.js";
import { useWindowedModal } from "../../../../hooks/modals/windowedModal.js";
import AppsManager from "../../../../features/apps/appsManager.js";
import App from "../../../../features/apps/app.js";
import { DialogBox } from "../../../modals/dialog-box/DialogBox.jsx";
import { DIALOG_CONTENT_TYPES } from "../../../../config/modals.config.js";
import Vector2 from "../../../../features/math/vector2.js";
import WindowsManager from "../../../../features/windows/windowsManager.js";
import { APPS } from "../../../../config/apps.config.js";
/**
* @param {object} props
* @param {string} props.href
* @param {*} props.children
* @param {ModalsManager} props.modalsManager
* @param {WindowsManager} props.windowsManager
* @param {Function} props.setCurrentFile
* @param {VirtualFile} props.currentFile
* @param {App} props.app
*/
export function MarkdownLink({ href, children, modalsManager, windowsManager, currentFile, setCurrentFile, app, ...props }) {
const { openWindowedModal } = useWindowedModal({ modalsManager });
const onClick = (event) => {
event.preventDefault();
if (!href.startsWith("http://") && !href.startsWith("https://")) {
const target = currentFile.parent.navigate(href);
if (target) {
if (target.isFile()) {
setCurrentFile(target);
} else {
windowsManager.open(APPS.FILE_EXPLORER, { startPath: target.path });
}
} else {
openWindowedModal({
iconUrl: AppsManager.getAppIconUrl(app.id),
title: "Failed to open link",
size: new Vector2(450, 150),
Modal: (props) =>
<DialogBox {...props}>
<p>Target not found: "{href}"</p>
<button data-type={DIALOG_CONTENT_TYPES.CloseButton}>Ok</button>
</DialogBox>
});
}
} else {
window.open(href, "_blank").focus();
}
};
const { onContextMenu } = useContextMenu({ modalsManager, Actions: (props) =>
<Actions {...props}>
<ClickAction label="Open link" icon={faExternalLink} onTrigger={onClick}/>
</Actions>
});
const title = useMemo(() => {
let title = null;
try {
title = new URL(href).hostname;
} catch (error) {
title = href.split("/").pop();
}
return title;
}, [href]);
return <a
target="_blank"
rel="noreferrer"
href={href}
onContextMenu={onContextMenu}
onClick={onClick}
{...props}
title={title}
>
{children}
</a>;
}

View file

@ -1,2 +1,17 @@
export const DEFAULT_ZOOM = 16;
export const ZOOM_FACTOR = 4;
export const ZOOM_FACTOR = 4;
export const CODE_FORMATS = [
"js",
"jsx",
"json",
"css",
"html",
"xml",
"yml"
];
export const EXTENSION_TO_LANGUAGE = {
"js": "javascript",
"jsx": "javascript",
"yml": "yaml",
};

View file

@ -9,6 +9,7 @@ html, body, #root {
}
html {
scroll-behavior: smooth;
overflow: hidden;
background-color: var(--background-color-c);
font-size: 16px;