Refactoring

This commit is contained in:
Prozilla 2023-12-23 15:35:56 +01:00
parent f1966c2220
commit dd0590ef01
No known key found for this signature in database
GPG key ID: 5858DFE71CAF31EE
152 changed files with 410 additions and 406 deletions

View file

@ -11,7 +11,7 @@ To see the status and to-do's of each feature, check the [task board](https://pr
## Table of Contents
- [Actions](storage/README.md)
- [Applications](applications/README.md)
- [Apps](apps/README.md)
- [Modals](modals/README.md)
- [Settings](settings/README.md)
- [Storage](storage/README.md)

View file

@ -1,104 +0,0 @@
[← Back](../README.md)
# Applications
> Related document: [Windows](../windows/README.md)
Applications (sometimes shortened to apps) are processes that open a window when ran. The window allows the user to view and interact with the app. Apps have a `title`, `id` and a `windowContent` property that refers to the React component of the application interface.
> Some applications have a different name in the UI, these names are written between brackets and quotation marks. The other name is the one used in code and file/folder names.
## Table of Contents
- [<img src="../../../public/assets/applications/icons/terminal.svg" width=20 height=20 style="vertical-align: text-bottom; background: none;"/> Terminal ("Commands")](terminal/README.md)
- [<img src="../../../public/assets/applications/icons/file-explorer.svg" width=20 height=20 style="vertical-align: text-bottom; background: none;"/> File Explorer ("Files")](file-explorer/README.md)
- [<img src="../../../public/assets/applications/icons/media-viewer.svg" width=20 height=20 style="vertical-align: text-bottom; background: none;"/> Media Viewer ("Photos")](media-viewer/README.md)
- [<img src="../../../public/assets/applications/icons/text-editor.svg" width=20 height=20 style="vertical-align: text-bottom; background: none;"/> Text Editor ("Notes")](text-editor/README.md)
- [<img src="../../../public/assets/applications/icons/settings.svg" width=20 height=20 style="vertical-align: text-bottom; background: none;"/> Settings](settings/README.md)
## Common components
### Header menu
The header menu is a useful component that can be added to app windows for quick access to useful functions, like saving and opening files. The header menu can also be used to add shortcuts for functions.
#### Example
```js
// components/applications/.common/HeaderMenu.jsx
<HeaderMenu
options={{
"File": {
"New": () => {
// ...
},
"Save": () => {
// ...
},
"Exit": () => {
// ...
},
},
}}
shortcuts={{
"File": {
"New": ["Control", "e"],
"Save": ["Control", "s"],
"Exit": ["Control", "x"],
},
}}
/>
```
## App templates
### Webview
The webview template can be used to turn a webpage into an application by simply setting a source property.
#### Example
```js
// features/applications/applications.js
import { WebView } from "../../components/applications/templates/WebView.jsx";
export default class ApplicationsManager {
static APPLICATIONS = [
// ...
new Application("Web App", "web-app", WebView, { source: "https://prozilla.dev/" }),
]
// ...
}
```
## App examples
### Adding a new application
```js
// components/applications/example/Example.jsx
export function Example() {
return (
<p>Application interface goes here.</p>
)
}
```
```js
// features/applications/applications.js
import { Example } from "../../components/applications/example/Example.jsx";
export default class ApplicationsManager {
static APPLICATIONS = [
// ...
new Application("Example App", "example", Example),
]
// ...
}
```

View file

@ -1,9 +0,0 @@
[← Back](../README.md)
# <img src="../../../../public/assets/applications/icons/media-viewer.svg" width=30 height=30 style="vertical-align: middle; background: none;"/> Media Viewer ("Photos")
An application for viewing media like images and videos.
## Screenshot
![Media Viewer window showing Prozilla OS.png](screenshot.png)

View file

@ -1,9 +0,0 @@
[← Back](../README.md)
# <img src="../../../../public/assets/applications/icons/text-editor.svg" width=30 height=30 style="vertical-align: middle; background: none;"/> Text Editor ("Notes")
An application for editing text documents.
## Screenshot
![Text Editor window displaying info.md in preview mode](screenshot.png)

View file

@ -0,0 +1,104 @@
[← Back](../README.md)
# Apps
> Related document: [Windows](../windows/README.md)
Applications (often shortened to apps) are processes that open a window when ran. The window allows the user to view and interact with the app. Apps have a `title`, `id` and a `windowContent` property that refers to the React component of the application interface.
> Some apps have a different name in the UI, these names are written between brackets and quotation marks. The other name is the one used in code and file/folder names.
## Table of Contents
- [<img src="../../../public/assets/apps/icons/terminal.svg" width=20 height=20 style="vertical-align: text-bottom; background: none;"/> Terminal ("Commands")](terminal/README.md)
- [<img src="../../../public/assets/apps/icons/file-explorer.svg" width=20 height=20 style="vertical-align: text-bottom; background: none;"/> File Explorer ("Files")](file-explorer/README.md)
- [<img src="../../../public/assets/apps/icons/media-viewer.svg" width=20 height=20 style="vertical-align: text-bottom; background: none;"/> Media Viewer ("Photos")](media-viewer/README.md)
- [<img src="../../../public/assets/apps/icons/text-editor.svg" width=20 height=20 style="vertical-align: text-bottom; background: none;"/> Text Editor ("Notes")](text-editor/README.md)
- [<img src="../../../public/assets/apps/icons/settings.svg" width=20 height=20 style="vertical-align: text-bottom; background: none;"/> Settings](settings/README.md)
## Common components
### Header menu
The header menu is a useful component that can be added to app windows for quick access to useful functions, like saving and opening files. The header menu can also be used to add shortcuts for functions.
#### Example
```js
// components/apps/_common/HeaderMenu.jsx
<HeaderMenu
options={{
"File": {
"New": () => {
// ...
},
"Save": () => {
// ...
},
"Exit": () => {
// ...
},
},
}}
shortcuts={{
"File": {
"New": ["Control", "e"],
"Save": ["Control", "s"],
"Exit": ["Control", "x"],
},
}}
/>
```
## App templates
### Webview
The webview template can be used to turn a webpage into an application by simply setting a source property.
#### Example
```js
// features/apps/apps.js
import { WebView } from "../../components/apps/templates/WebView.jsx";
export default class AppsManager {
static APPLICATIONS = [
// ...
new App("Web App", "web-app", WebView, { source: "https://prozilla.dev/" }),
]
// ...
}
```
## App examples
### Adding a new app
```js
// components/apps/example/Example.jsx
export function Example() {
return (
<p>Application interface goes here.</p>
)
}
```
```js
// features/apps/apps.js
import { Example } from "../../components/apps/example/Example.jsx";
export default class AppsManager {
static APPLICATIONS = [
// ...
new App("Example App", "example", Example),
]
// ...
}
```

View file

@ -1,6 +1,6 @@
[← Back](../README.md)
# <img src="../../../../public/assets/applications/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

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View file

@ -0,0 +1,9 @@
[← 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")
An app for viewing media like images and videos.
## Screenshot
![Media Viewer window showing Prozilla OS.png](screenshot.png)

View file

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 210 KiB

View file

@ -1,6 +1,6 @@
[← Back](../README.md)
# <img src="../../../../public/assets/applications/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

Before

Width:  |  Height:  |  Size: 177 KiB

After

Width:  |  Height:  |  Size: 177 KiB

View file

@ -1,6 +1,6 @@
[← Back](../README.md)
# <img src="../../../../public/assets/applications/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.
@ -10,14 +10,14 @@ A command line tool.
## Commands
See [features/applications/terminal/commands.js](../../../../src/features/applications/terminal/commands.js) for a list of commands. You can edit this file to add/remove/edit commands.
See [features/apps/terminal/commands.js](../../../../src/features/apps/terminal/commands.js) for a list of commands. You can edit this file to add/remove/edit commands.
## Examples
### Touch command
```js
// features/applications/terminal/commands/touch.js
// features/apps/terminal/commands/touch.js
export const touch = new Command("touch")
.setRequireArgs(true)

View file

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

View file

@ -0,0 +1,9 @@
[← 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")
An app for editing text documents.
## Screenshot
![Text Editor window displaying info.md in preview mode](screenshot.png)

View file

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 118 KiB

View file

@ -11,7 +11,7 @@ The virtual drive is a virtual file and directory system. The root directory is
### Component interacting with virtual drive
```js
// components/applications/example/Example.jsx
// components/apps/example/Example.jsx
export function Example() {
const virtualRoot = useVirtualRoot();

View file

@ -2,6 +2,6 @@
# Windows
> Related document: [Applications](../applications/README.md)
> Related document: [Apps](../apps/README.md)
The windows components are used to view and interact with running applications. Each application opens a window when ran.
The windows components are used to view and interact with running applications. Each app opens a window when ran.

View file

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 3 KiB

View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1,7 +1,7 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { formatShortcut } from "../../../features/utils/string.js";
import { formatShortcut } from "../../../features/utils/string.utils.js";
import styles from "../Actions.module.css";
import { ImagePreview } from "../../applications/file-explorer/directory-list/ImagePreview.jsx";
import { ImagePreview } from "../../apps/file-explorer/directory-list/ImagePreview.jsx";
import { memo } from "react";
export const ClickAction = memo(({ actionId, label, shortcut, onTrigger, icon }) => {

View file

@ -1,5 +1,5 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { formatShortcut } from "../../../features/utils/string.js";
import { formatShortcut } from "../../../features/utils/string.utils.js";
import styles from "../Actions.module.css";
import { faCircleDot } from "@fortawesome/free-solid-svg-icons";
import { useState } from "react";

View file

@ -1,5 +1,5 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { formatShortcut } from "../../../features/utils/string.js";
import { formatShortcut } from "../../../features/utils/string.utils.js";
import styles from "../Actions.module.css";
import { useState } from "react";
import { faSquare } from "@fortawesome/free-regular-svg-icons";

View file

@ -1,23 +0,0 @@
import { useState } from "react";
import styles from "./ImagePreview.module.css";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faFile } from "@fortawesome/free-solid-svg-icons";
import { ReactSVG } from "react-svg";
export function ImagePreview({ source, className, ...props }) {
const [loadingFailed, setLoadingFailed] = useState(false);
if (loadingFailed)
return <FontAwesomeIcon icon={faFile}/>;
const classNames = [styles["Image-preview"]];
if (className != null)
classNames.push(className);
return (<div className={classNames.join(" ")} {...props}>
{source.endsWith(".svg")
? <ReactSVG src={source}/>
: <img src={source} onError={() => { setLoadingFailed(true); }} alt="Preview" draggable="false"/>
}
</div>);
}

View file

@ -3,7 +3,7 @@ import { DropdownButton } from "../../utils/dropdown-button/DropdownButton.jsx";
import styles from "./HeaderMenu.module.css";
/**
* @param {Object} props
* @param {object} props
* @param {Object<string, Object<string, Function>>} props.options
* @param {Object<string, Object<string, string[]>>} props.shortcuts
*/

View file

@ -2,8 +2,8 @@ import { forwardRef, useEffect, useState } from "react";
import styles from "./WebView.module.css";
/**
* @param {Object} props
* @param {String} props.source
* @param {object} props
* @param {string} props.source
*/
export const WebView = forwardRef(({ source, focus, ...props }, ref) => {
const [hovered, setHovered] = useState(false);
@ -22,7 +22,7 @@ export const WebView = forwardRef(({ source, focus, ...props }, ref) => {
return () => {
window.removeEventListener("blur", onBlur);
};
}, [hovered]);
}, [focus, hovered]);
const onMouseOver = () => {
setHovered(true);

View file

@ -1,10 +1,10 @@
import { useEffect, useRef, useState } from "react";
import styles from "./Browser.module.css";
import { WebView } from "../.templates/WebView.jsx";
import { WebView } from "../_templates/WebView.jsx";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCaretLeft, faCaretRight, faHome, faRotateRight } from "@fortawesome/free-solid-svg-icons";
import { HOME_URL, SEARCH_URL } from "../../../constants/applications/browser.js";
import { isValidUrl } from "../../../features/utils/browser.js";
import { HOME_URL, SEARCH_URL } from "../../../config/apps/browser.config.js";
import { isValidUrl } from "../../../features/utils/browser.utils.js";
import { useHistory } from "../../../hooks/utils/history.js";
/** @type {import("../../windows/WindowView.jsx").windowProps} */

View file

@ -8,15 +8,15 @@ import { useContextMenu } from "../../../hooks/modals/contextMenu.js";
import { QuickAccessButton } from "./QuickAccessButton.jsx";
import { useWindowedModal } from "../../../hooks/modals/windowedModal.js";
import Vector2 from "../../../features/math/vector2.js";
import { DIALOG_CONTENT_TYPES } from "../../../constants/modals.js";
import { DIALOG_CONTENT_TYPES } from "../../../config/modals.config.js";
import { DirectoryList } from "./directory-list/DirectoryList.jsx";
import { Actions } from "../../actions/Actions.jsx";
import { ClickAction } from "../../actions/actions/ClickAction.jsx";
import utilStyles from "../../../styles/utils.module.css";
import { DialogBox } from "../../modals/dialog-box/DialogBox.jsx";
import AppsManager from "../../../features/applications/applications.js";
import { APPS, APP_ICONS, APP_NAMES } from "../../../constants/applications.js";
import { TITLE_SEPARATOR } from "../../../constants/windows.js";
import AppsManager from "../../../features/apps/appsManager.js";
import { APPS, APP_ICONS, APP_NAMES } from "../../../config/apps.config.js";
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";
@ -83,8 +83,6 @@ export function FileExplorer({ startPath, app, modalsManager }) {
const directory = absolute ? virtualRoot.navigate(path) : currentDirectory.navigate(path);
console.debug(directory);
if (directory != null) {
setCurrentDirectory(directory);
setPath(directory.root ? "/" : directory.path);
@ -164,6 +162,7 @@ export function FileExplorer({ startPath, app, modalsManager }) {
tabIndex={0}
className={styles["Icon-button"]}
onClick={() => { changeDirectory(".."); }}
disabled={currentDirectory.root}
>
<FontAwesomeIcon icon={faArrowUp}/>
</button>
@ -189,6 +188,7 @@ export function FileExplorer({ startPath, app, modalsManager }) {
// }
}}
disabled={!currentDirectory.canBeEdited}
>
<FontAwesomeIcon icon={faPlus}/>
</button>

View file

@ -1,6 +1,6 @@
import { useEffect, useRef, useState } from "react";
import { VirtualFile } from "../../../../features/virtual-drive/virtualFile.js";
import { VirtualFolder } from "../../../../features/virtual-drive/virtualFolder.js";
import { VirtualFile } from "../../../../features/virtual-drive/file/virtualFile.js";
import { VirtualFolder } from "../../../../features/virtual-drive/folder/virtualFolder.js";
import { Interactable } from "../../../utils/interactable/Interactable.jsx";
import styles from "./DirectoryList.module.css";
import { ImagePreview } from "./ImagePreview.jsx";
@ -159,7 +159,7 @@ export function DirectoryList({ directory, showHidden = false, folderClassName,
}}
>
<div className={styles["Folder-icon"]}>
<ImagePreview source={folder.getIconUrl()}/>
<ImagePreview source={folder.getIconUrl()} onError={() => { folder.setIconUrl(null); }}/>
</div>
<p>{folder.name}</p>
</Interactable>
@ -181,7 +181,7 @@ export function DirectoryList({ directory, showHidden = false, folderClassName,
}}
>
<div className={styles["File-icon"]}>
<ImagePreview source={file.getIconUrl()}/>
<ImagePreview source={file.getIconUrl()} onError={() => { file.setIconUrl(null); }}/>
</div>
<p>{file.id}</p>
</Interactable>

View file

@ -0,0 +1,27 @@
import { useState } from "react";
import styles from "./ImagePreview.module.css";
import { ReactSVG } from "react-svg";
import AppsManager from "../../../../features/apps/appsManager.js";
import { APPS } from "../../../../config/apps.config.js";
export function ImagePreview({ source, className, onError, ...props }) {
const [loadingFailed, setLoadingFailed] = useState(false);
const onLoadingError = () => {
setLoadingFailed(true);
onError?.();
};
const classNames = [styles["Image-preview"]];
if (className != null)
classNames.push(className);
return (<div className={classNames.join(" ")} {...props}>
{loadingFailed
? <ReactSVG src={AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "file")}/>
: source.endsWith(".svg")
? <ReactSVG src={source} onError={onLoadingError}/>
: <img src={source} onError={onLoadingError} alt="Preview" draggable="false"/>
}
</div>);
}

View file

@ -4,6 +4,7 @@
align-items: center;
height: 100%;
overflow: hidden;
pointer-events: none;
}
.Image-preview > * {

View file

@ -2,7 +2,7 @@
import { useEffect } from "react";
import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext.js";
import styles from "./MediaViewer.module.css";
import { APPS } from "../../../constants/applications.js";
import { APPS } from "../../../config/apps.config.js";
/**
* @param {import("../../windows/WindowView.jsx").windowProps} props

View file

@ -4,7 +4,7 @@ import utilStyles from "../../../styles/utils.module.css";
import Vector2 from "../../../features/math/vector2.js";
import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext.js";
import { useVirtualRoot } from "../../../hooks/virtual-drive/virtualRootContext.js";
import { NAME } from "../../../constants/branding.js";
import { NAME } from "../../../config/branding.config.js";
export function AboutSettings() {
const windowsManager = useWindowsManager();
@ -21,7 +21,7 @@ export function AboutSettings() {
event.preventDefault();
windowsManager.open("text-editor", {
mode: "view",
file: virtualRoot.navigate("~/Documents").findFile("info", "md"),
file: virtualRoot.navigate("~/Documents/info.md"),
size: new Vector2(575, 675),
});
}}

View file

@ -3,7 +3,7 @@ import { SettingsManager } from "../../../features/settings/settingsManager.js";
import styles from "./Settings.module.css";
import { useVirtualRoot } from "../../../hooks/virtual-drive/virtualRootContext.js";
import { useSettingsManager } from "../../../hooks/settings/settingsManagerContext.js";
import { WALLPAPERS_PATH } from "../../../constants/applications/settings.js";
import { WALLPAPERS_PATH } from "../../../config/apps/settings.config.js";
export function AppearanceSettings() {
const virtualRoot = useVirtualRoot();

View file

@ -4,9 +4,9 @@ import { useVirtualRoot } from "../../../hooks/virtual-drive/virtualRootContext.
import { clamp } from "../../../features/math/clamp.js";
import { OutputLine } from "./OutputLine.jsx";
import { InputLine } from "./InputLine.jsx";
import { ANSI, HOSTNAME, USERNAME } from "../../../constants/applications/terminal.js";
import CommandsManager from "../../../features/applications/terminal/commands.js";
import { removeFromArray } from "../../../features/utils/array.js";
import { ANSI, HOSTNAME, USERNAME } from "../../../config/apps/terminal.config.js";
import CommandsManager from "../../../features/apps/terminal/commands.js";
import { removeFromArray } from "../../../features/utils/array.utils.js";
/**
* @param {import("../../windows/WindowView.jsx").windowProps} props

View file

@ -1,10 +1,10 @@
import React, { useEffect, useState } from "react";
import styles from "./TextEditor.module.css";
import { HeaderMenu } from "../.common/HeaderMenu.jsx";
import { HeaderMenu } from "../_common/HeaderMenu.jsx";
import Markdown from "markdown-to-jsx";
import { DEFAULT_ZOOM, ZOOM_FACTOR } from "../../../constants/applications/textEditor.js";
import AppsManager from "../../../features/applications/applications.js";
import { TITLE_SEPARATOR } from "../../../constants/windows.js";
import { DEFAULT_ZOOM, ZOOM_FACTOR } from "../../../config/apps/textEditor.config.js";
import AppsManager from "../../../features/apps/appsManager.js";
import { TITLE_SEPARATOR } from "../../../config/windows.config.js";
/**
* @param {import("../../windows/WindowView.jsx").windowProps} props

View file

@ -7,11 +7,11 @@ import { useModals } from "../../hooks/modals/modals.js";
import { ModalsView } from "../modals/ModalsView.jsx";
import { useWindowsManager } from "../../hooks/windows/windowsManagerContext.js";
import { useContextMenu } from "../../hooks/modals/contextMenu.js";
import { FALLBACK_ICON_SIZE, FALLBACK_WALLPAPER } from "../../constants/desktop.js";
import { reloadViewport } from "../../features/utils/browser.js";
import { FALLBACK_ICON_SIZE, FALLBACK_WALLPAPER } from "../../config/desktop.config.js";
import { reloadViewport } from "../../features/utils/browser.utils.js";
import { useVirtualRoot } from "../../hooks/virtual-drive/virtualRootContext.js";
import { DirectoryList } from "../applications/file-explorer/directory-list/DirectoryList.jsx";
import { APPS, APP_ICONS, APP_NAMES } from "../../constants/applications.js";
import { DirectoryList } from "../apps/file-explorer/directory-list/DirectoryList.jsx";
import { APPS, APP_ICONS, APP_NAMES } from "../../config/apps.config.js";
import Vector2 from "../../features/math/vector2.js";
import { Actions } from "../actions/Actions.jsx";
import { ClickAction } from "../actions/actions/ClickAction.jsx";
@ -20,7 +20,7 @@ import { ToggleAction } from "../actions/actions/ToggleAction.jsx";
import { DropdownAction } from "../actions/actions/DropdownAction.jsx";
import { RadioAction } from "../actions/actions/RadioAction.jsx";
import { Divider } from "../actions/actions/Divider.jsx";
import { isValidInteger } from "../../features/utils/number.js";
import { isValidInteger } from "../../features/utils/number.utils.js";
export const Desktop = memo(() => {
const settingsManager = useSettingsManager();

View file

@ -1,6 +1,6 @@
import { memo, useEffect, useRef } from "react";
import { Modal } from "../../features/modals/modal.js";
import ModalsManager from "../../features/modals/modals.js";
import ModalsManager from "../../features/modals/modalsManager.js";
import { ModalView } from "./ModalView.jsx";
import styles from "./ModalsView.module.css";

View file

@ -1,5 +1,5 @@
import { DIALOG_CONTENT_TYPES } from "../../../constants/modals.js";
import { WindowedModal } from "../.templates/WindowedModal.jsx";
import { DIALOG_CONTENT_TYPES } from "../../../config/modals.config.js";
import { WindowedModal } from "../_templates/WindowedModal.jsx";
export function DialogBox({ modal, params, children, ...props }) {
const onClick = (event) => {

View file

@ -1,11 +1,11 @@
import { ImagePreview } from "../../applications/file-explorer/directory-list/ImagePreview.jsx";
import { WindowedModal } from "../.templates/WindowedModal.jsx";
import { ImagePreview } from "../../apps/file-explorer/directory-list/ImagePreview.jsx";
import { WindowedModal } from "../_templates/WindowedModal.jsx";
import styles from "./FileProperties.module.css";
import utilStyles from "../../../styles/utils.module.css";
import Modal from "../../../features/modals/modal.js";
import { VirtualFile } from "../../../features/virtual-drive/virtualFile.js";
import { VirtualFile } from "../../../features/virtual-drive/file/virtualFile.js";
import { StorageManager } from "../../../features/storage/storageManager.js";
import AppsManager from "../../../features/applications/applications.js";
import AppsManager from "../../../features/apps/appsManager.js";
/**
* @param {object} props
@ -14,7 +14,7 @@ import AppsManager from "../../../features/applications/applications.js";
* @param {VirtualFile} props.file
*/
export function FileProperties({ modal, params, file, ...props }) {
const associatedApp = AppsManager.getFileApp(file.extension);
const associatedApp = AppsManager.getAppByFileExtension(file.extension);
return <WindowedModal className={styles.Container} modal={modal} params={params} {...props}>
<span className={styles["Section"]}>

View file

@ -2,7 +2,7 @@ import { memo, useEffect, useMemo, useRef, useState } from "react";
import styles from "./Taskbar.module.css";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCog, faSearch } from "@fortawesome/free-solid-svg-icons";
import AppsManager from "../../features/applications/applications.js";
import AppsManager from "../../features/apps/appsManager.js";
import { ReactSVG } from "react-svg";
import { HomeMenu } from "./menus/HomeMenu.jsx";
import OutsideClickListener from "../../hooks/utils/outsideClick.js";
@ -17,10 +17,10 @@ import { useContextMenu } from "../../hooks/modals/contextMenu.js";
import { Actions } from "../actions/Actions.jsx";
import { useModals } from "../../hooks/modals/modals.js";
import { ClickAction } from "../actions/actions/ClickAction.jsx";
import { APPS, APP_NAMES } from "../../constants/applications.js";
import { APPS, APP_NAMES } from "../../config/apps.config.js";
import { useWindowsManager } from "../../hooks/windows/windowsManagerContext.js";
import { ModalsView } from "../modals/ModalsView.jsx";
import { TASKBAR_HEIGHT } from "../../constants/taskBar.js";
import { TASKBAR_HEIGHT } from "../../config/taskbar.config.js";
import { useSettingsManager } from "../../hooks/settings/settingsManagerContext.js";
import { SettingsManager } from "../../features/settings/settingsManager.js";
import { useWindows } from "../../hooks/windows/windowsContext.js";
@ -50,7 +50,7 @@ export const Taskbar = memo(() => {
});
const [pins, setPins] = useState([]);
const apps = useMemo(() => AppsManager.APPLICATIONS.sort((appA, appB) => {
const apps = useMemo(() => AppsManager.APPS.sort((appA, appB) => {
const indexA = pins.indexOf(appA.id);
const indexB = pins.indexOf(appB.id);
if (indexA < 0 && indexB > 0) {

View file

@ -1,20 +1,20 @@
import { memo } from "react";
import Application from "../../../features/applications/application.js";
import App from "../../../features/apps/app.js";
import styles from "./AppIcon.module.css";
import { ReactSVG } from "react-svg";
import { useSettingsManager } from "../../../hooks/settings/settingsManagerContext.js";
import { useContextMenu } from "../../../hooks/modals/contextMenu.js";
import ModalsManager from "../../../features/modals/modals.js";
import ModalsManager from "../../../features/modals/modalsManager.js";
import { Actions } from "../../actions/Actions.jsx";
import { ClickAction } from "../../actions/actions/ClickAction.jsx";
import { faThumbTack, faTimes } from "@fortawesome/free-solid-svg-icons";
import { SettingsManager } from "../../../features/settings/settingsManager.js";
import { removeFromArray } from "../../../features/utils/array.js";
import AppsManager from "../../../features/applications/applications.js";
import { removeFromArray } from "../../../features/utils/array.utils.js";
import AppsManager from "../../../features/apps/appsManager.js";
/**
* @param {object} props
* @param {Application} props.app
* @param {App} props.app
* @param {ModalsManager} props.modalsManager
* @param {string[]} props.pins
* @param {boolean} props.focused
@ -76,7 +76,7 @@ export const AppButton = memo(({ app, windowsManager, modalsManager, pins, activ
}}
title={app.name}
>
<ReactSVG src={`${process.env.PUBLIC_URL}/assets/applications/icons/${app.id}.svg`}/>
<ReactSVG src={`${process.env.PUBLIC_URL}/assets/apps/icons/${app.id}.svg`}/>
</button>
);
});

View file

@ -3,16 +3,16 @@ import styles from "./HomeMenu.module.css";
import appStyles from "./AppList.module.css";
import { faCircleInfo, faFileLines, faGear, faImage, faPowerOff } from "@fortawesome/free-solid-svg-icons";
import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext.js";
import AppsManager from "../../../features/applications/applications.js";
import AppsManager from "../../../features/apps/appsManager.js";
import { ReactSVG } from "react-svg";
import { closeViewport } from "../../../features/utils/browser.js";
import { closeViewport } from "../../../features/utils/browser.utils.js";
import { useKeyboardListener } from "../../../hooks/utils/keyboard.js";
import { useVirtualRoot } from "../../../hooks/virtual-drive/virtualRootContext.js";
import { useEffect, useState } from "react";
import Vector2 from "../../../features/math/vector2.js";
import utilStyles from "../../../styles/utils.module.css";
import { APPS } from "../../../constants/applications.js";
import { NAME } from "../../../constants/branding.js";
import { APPS } from "../../../config/apps.config.js";
import { NAME } from "../../../config/branding.config.js";
/**
* @param {object} props
@ -76,7 +76,7 @@ export function HomeMenu({ active, setActive, search }) {
setActive(false);
windowsManager.open("text-editor", {
mode: "view",
file: virtualRoot.navigate("~/Documents").findFile("info", "md"),
file: virtualRoot.navigate("~/Documents/info.md"),
size: new Vector2(575, 675),
});
}}>
@ -98,7 +98,7 @@ export function HomeMenu({ active, setActive, search }) {
<div className={styles.Apps}>
<h1 className={utilStyles["Text-bold"]}>{NAME}</h1>
<div className={appStyles["App-list"]}>
{AppsManager.APPLICATIONS.map(({ name, id }) =>
{AppsManager.APPS.map(({ name, id }) =>
<button
key={id}
className={appStyles["App-button"]}

View file

@ -1,10 +1,9 @@
import styles from "./SearchMenu.module.css";
import appStyles from "./AppList.module.css";
import AppsManager from "../../../features/applications/applications.js";
import AppsManager from "../../../features/apps/appsManager.js";
import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext.js";
import { ReactSVG } from "react-svg";
import { useEffect } from "react";
import { useState } from "react";
import { useEffect, useState } from "react";
import { useKeyboardListener } from "../../../hooks/utils/keyboard.js";
/**
@ -32,7 +31,7 @@ export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inp
}, [inputRef]);
useEffect(() => {
setApps(AppsManager.APPLICATIONS.filter(({ name }) =>
setApps(AppsManager.APPS.filter(({ name }) =>
name.toLowerCase().includes(searchQuery.toLowerCase().trim())
));
}, [searchQuery]);

View file

@ -1,7 +1,7 @@
import { useEffect, useState } from "react";
import styles from "./DropdownButton.module.css";
import OutsideClickListener from "../../../hooks/utils/outsideClick.js";
import { formatShortcut } from "../../../features/utils/string.js";
import { formatShortcut } from "../../../features/utils/string.utils.js";
/**
* @param {object} props

View file

@ -1,5 +1,5 @@
import { useState } from "react";
import { INTERACTIBLE_DOUBLE_CLICK_DELAY } from "../../../constants/utils.js";
import { INTERACTIBLE_DOUBLE_CLICK_DELAY } from "../../../config/utils.config.js";
let timeoutId = null;

View file

@ -5,33 +5,33 @@ import { ReactSVG } from "react-svg";
import { useWindowsManager } from "../../hooks/windows/windowsManagerContext.js";
import Draggable from "react-draggable";
import { memo, useEffect, useRef, useState } from "react";
import Application from "../../features/applications/application.js";
import App from "../../features/apps/app.js";
import Vector2 from "../../features/math/vector2.js";
import { faWindowMaximize } from "@fortawesome/free-regular-svg-icons";
import utilStyles from "../../styles/utils.module.css";
import { useModals } from "../../hooks/modals/modals.js";
import { ModalsView } from "../modals/ModalsView.jsx";
import { useContextMenu } from "../../hooks/modals/contextMenu.js";
import AppsManager from "../../features/applications/applications.js";
import AppsManager from "../../features/apps/appsManager.js";
import { ClickAction } from "../actions/actions/ClickAction.jsx";
import { Actions } from "../actions/Actions.jsx";
import { useScreenDimensions } from "../../hooks/utils/screen.js";
/**
* @typedef {object} windowProps
* @property {Application} app
* @property {App} app
* @property {Function} setTitle
* @property {Function} setIconUrl
* @property {Function} close
* @property {Function} focus
* @property {boolean} active
* @property {import("../../features/modals/modals.js").default} modalsManager
* @property {import("../../features/modals/modalsManager.js").default} modalsManager
*/
/**
* @param {object} props
* @param {string} props.id
* @param {Application} props.app
* @param {App} props.app
* @param {Vector2} props.size
* @param {Vector2} props.position
* @param {boolean} props.focused

View file

@ -20,7 +20,7 @@ export const WindowsView = memo(() => {
// Launch startup apps
useEffect(() => {
const settings = settingsManager.get(SettingsManager.VIRTUAL_PATHS.applications);
const settings = settingsManager.get(SettingsManager.VIRTUAL_PATHS.apps);
settings.get("startup", (value) => {
if (value !== "")
windowsManager.startup(value?.split(","));

23
src/config/apps.config.js Normal file
View file

@ -0,0 +1,23 @@
export const APPS = {
TERMINAL: "terminal",
SETTINGS: "settings",
MEDIA_VIEWER: "media-viewer",
TEXT_EDITOR: "text-editor",
FILE_EXPLORER: "file-explorer"
};
export const APP_NAMES = {
TERMINAL: "Commands",
SETTINGS: "Settings",
MEDIA_VIEWER: "Photos",
TEXT_EDITOR: "Notes",
FILE_EXPLORER: "Files"
};
export const APP_ICONS = {
TERMINAL: `${process.env.PUBLIC_URL}/assets/apps/icons/${APPS.TERMINAL}.svg`,
SETTINGS: `${process.env.PUBLIC_URL}/assets/apps/icons/${APPS.SETTINGS}.svg`,
MEDIA_VIEWER: `${process.env.PUBLIC_URL}/assets/apps/icons/${APPS.MEDIA_VIEWER}.svg`,
TEXT_EDITOR: `${process.env.PUBLIC_URL}/assets/apps/icons/${APPS.TEXT_EDITOR}.svg`,
FILE_EXPLORER: `${process.env.PUBLIC_URL}/assets/apps/icons/${APPS.FILE_EXPLORER}.svg`,
};

View file

@ -1,4 +1,4 @@
import { ANSI } from "./applications/terminal.js";
import { ANSI } from "./apps/terminal.config.js";
export const NAME = "Prozilla OS";

View file

@ -1,23 +0,0 @@
export const APPS = {
TERMINAL: "terminal",
SETTINGS: "settings",
MEDIA_VIEWER: "media-viewer",
TEXT_EDITOR: "text-editor",
FILE_EXPLORER: "file-explorer"
};
export const APP_NAMES = {
TERMINAL: "Commands",
SETTINGS: "Settings",
MEDIA_VIEWER: "Photos",
TEXT_EDITOR: "Notes",
FILE_EXPLORER: "Files"
};
export const APP_ICONS = {
TERMINAL: `${process.env.PUBLIC_URL}/assets/applications/icons/${APPS.TERMINAL}.svg`,
SETTINGS: `${process.env.PUBLIC_URL}/assets/applications/icons/${APPS.SETTINGS}.svg`,
MEDIA_VIEWER: `${process.env.PUBLIC_URL}/assets/applications/icons/${APPS.MEDIA_VIEWER}.svg`,
TEXT_EDITOR: `${process.env.PUBLIC_URL}/assets/applications/icons/${APPS.TEXT_EDITOR}.svg`,
FILE_EXPLORER: `${process.env.PUBLIC_URL}/assets/applications/icons/${APPS.FILE_EXPLORER}.svg`,
};

View file

@ -1,93 +0,0 @@
import Application from "./application.js";
import { FileExplorer } from "../../components/applications/file-explorer/FileExplorer.jsx";
import { MediaViewer } from "../../components/applications/media-viewer/MediaViewer.jsx";
import { WebView } from "../../components/applications/.templates/WebView.jsx";
import { Terminal } from "../../components/applications/terminal/Terminal.jsx";
import { TextEditor } from "../../components/applications/text-editor/TextEditor.jsx";
import { Settings } from "../../components/applications/settings/Settings.jsx";
// import { Calculator } from "../../components/applications/calculator/Calculator.jsx";
import Vector2 from "../math/vector2.js";
import { APPS } from "../../constants/applications.js";
import { Browser } from "../../components/applications/browser/Browser.jsx";
export default class AppsManager {
static APPLICATIONS = [
new Application("Commands", APPS.TERMINAL, Terminal),
new Application("Settings", APPS.SETTINGS, Settings),
new Application("Photos", APPS.MEDIA_VIEWER, MediaViewer),
// new Application("Browser", "browser"),
// new Application("Calculator", "calculator", Calculator, { size: new Vector2(400, 600) }),
new Application("Notes", APPS.TEXT_EDITOR, TextEditor),
// new Application("Code Editor", "code-editor"),
new Application("Files", APPS.FILE_EXPLORER, FileExplorer),
new Application("Wordle", "wordle", WebView, {
source: "https://prozilla.dev/wordle",
size: new Vector2(400, 650)
}),
new Application("Balls", "balls", WebView, {
source: "https://prozilla.dev/ball-maze",
size: new Vector2(600, 600)
}),
new Application("Minesweeper", "minesweeper", WebView, {
source: "https://prozilla.dev/minesweeper",
size: new Vector2(500, 580)
}),
new Application("Browser", "browser", Browser, {
size: new Vector2(700, 500)
}),
];
/**
* @param {string} id
* @returns {Application | null}
*/
static getApp(id) {
let application = null;
this.APPLICATIONS.forEach((app) => {
if (app.id === id) {
application = app;
return;
}
});
return application;
}
/**
* Get the application associated with a file extension
* @param {string} fileExtension
* @returns {Application}
*/
static getFileApp(fileExtension) {
let app = null;
// eslint-disable-next-line default-case
switch (fileExtension) {
case "png":
app = this.getApp(APPS.MEDIA_VIEWER);
break;
case "txt":
case "md":
case "xml":
app = this.getApp(APPS.TEXT_EDITOR);
break;
}
return app;
}
/**
* Returns the url of an icon inside the icons folder or the default icon of an app
* @param {string} appId
* @param {string | null} iconName
* @returns {string}
*/
static getAppIconUrl(appId, iconName) {
if (iconName == null) {
return `${process.env.PUBLIC_URL}/assets/applications/icons/${appId}.svg`;
} else {
return `${process.env.PUBLIC_URL}/assets/applications/${appId}/icons/${iconName}.svg`;
}
}
}

View file

@ -1,6 +1,6 @@
import React from "react";
export default class Application {
export default class App {
/**
* @param {string} name
* @param {string} id

View file

@ -0,0 +1,93 @@
import App from "./app.js";
import { FileExplorer } from "../../components/apps/file-explorer/FileExplorer.jsx";
import { MediaViewer } from "../../components/apps/media-viewer/MediaViewer.jsx";
import { WebView } from "../../components/apps/_templates/WebView.jsx";
import { Terminal } from "../../components/apps/terminal/Terminal.jsx";
import { TextEditor } from "../../components/apps/text-editor/TextEditor.jsx";
import { Settings } from "../../components/apps/settings/Settings.jsx";
// import { Calculator } from "../../components/apps/calculator/Calculator.jsx";
import Vector2 from "../math/vector2.js";
import { APPS } from "../../config/apps.config.js";
import { Browser } from "../../components/apps/browser/Browser.jsx";
export default class AppsManager {
static APPS = [
new App("Commands", APPS.TERMINAL, Terminal),
new App("Settings", APPS.SETTINGS, Settings),
new App("Photos", APPS.MEDIA_VIEWER, MediaViewer),
// new App("Browser", "browser"),
// new App("Calculator", "calculator", Calculator, { size: new Vector2(400, 600) }),
new App("Notes", APPS.TEXT_EDITOR, TextEditor),
// new App("Code Editor", "code-editor"),
new App("Files", APPS.FILE_EXPLORER, FileExplorer),
new App("Wordle", "wordle", WebView, {
source: "https://prozilla.dev/wordle",
size: new Vector2(400, 650)
}),
new App("Balls", "balls", WebView, {
source: "https://prozilla.dev/ball-maze",
size: new Vector2(600, 600)
}),
new App("Minesweeper", "minesweeper", WebView, {
source: "https://prozilla.dev/minesweeper",
size: new Vector2(500, 580)
}),
new App("Browser", "browser", Browser, {
size: new Vector2(700, 500)
}),
];
/**
* @param {string} id
* @returns {App | null}
*/
static getAppById(id) {
let application = null;
this.APPS.forEach((app) => {
if (app.id === id) {
application = app;
return;
}
});
return application;
}
/**
* Get the app associated with a file extension
* @param {string} fileExtension
* @returns {App}
*/
static getAppByFileExtension(fileExtension) {
let app = null;
// eslint-disable-next-line default-case
switch (fileExtension) {
case "png":
app = this.getAppById(APPS.MEDIA_VIEWER);
break;
case "txt":
case "md":
case "xml":
app = this.getAppById(APPS.TEXT_EDITOR);
break;
}
return app;
}
/**
* Returns the url of an icon inside the icons folder or the default icon of an app
* @param {string} appId
* @param {string | null} iconName
* @returns {string}
*/
static getAppIconUrl(appId, iconName) {
if (iconName == null) {
return `${process.env.PUBLIC_URL}/assets/apps/icons/${appId}.svg`;
} else {
return `${process.env.PUBLIC_URL}/assets/apps/${appId}/icons/${iconName}.svg`;
}
}
}

View file

@ -1,5 +1,5 @@
import { VirtualFolder } from "../../virtual-drive/virtualFolder.js";
import { VirtualRoot } from "../../virtual-drive/virtualRoot.js";
import { VirtualFolder } from "../../virtual-drive/folder/virtualFolder.js";
import { VirtualRoot } from "../../virtual-drive/root/virtualRoot.js";
/**
* @callback executeType

Some files were not shown because too many files have changed in this diff Show more