Finished core and file-explorer packages

This commit is contained in:
Prozilla 2024-06-17 17:00:34 +02:00
parent 045ef2ba4e
commit ade2774b90
No known key found for this signature in database
GPG key ID: 5858DFE71CAF31EE
66 changed files with 581 additions and 282 deletions

View file

@ -4,6 +4,7 @@ import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
import react from "eslint-plugin-react";
import reactRefresh from "eslint-plugin-react-refresh";
import * as pluginImport from "eslint-plugin-import";
export default tseslint.config(
eslint.configs.recommended,
@ -22,6 +23,9 @@ export default tseslint.config(
plugins: {
react,
"react-refresh": reactRefresh,
import: {
rules: pluginImport.rules
}
},
rules: {
"quotes": "off",
@ -66,6 +70,7 @@ export default tseslint.config(
"warn",
{ "allowConstantExport": true }
],
"import/no-relative-packages": "error"
},
}
);

193
package-lock.json generated
View file

@ -17,6 +17,7 @@
"@fortawesome/free-solid-svg-icons": "^6.4.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@prozilla-os/core": "^1.0.0",
"@prozilla-os/file-explorer": "^1.0.0",
"@vitejs/plugin-react-swc": "^3.7.0",
"anser": "^2.1.1",
"escape-carriage": "^1.3.1",
@ -42,6 +43,7 @@
"@types/react-dom": "^18.3.0",
"@types/react-syntax-highlighter": "^15.5.13",
"eslint": "^8.57.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-refresh": "^0.4.7",
"gh-pages": "^5.0.0",
@ -1769,6 +1771,12 @@
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"dev": true
},
"node_modules/@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
"node_modules/@types/minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz",
@ -2238,6 +2246,26 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/array.prototype.findlastindex": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz",
"integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.7",
"define-properties": "^1.2.1",
"es-abstract": "^1.23.2",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.0.0",
"es-shim-unscopables": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/array.prototype.flat": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz",
@ -3237,6 +3265,113 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-import-resolver-node": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
"integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
"dev": true,
"dependencies": {
"debug": "^3.2.7",
"is-core-module": "^2.13.0",
"resolve": "^1.22.4"
}
},
"node_modules/eslint-import-resolver-node/node_modules/debug": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true,
"dependencies": {
"ms": "^2.1.1"
}
},
"node_modules/eslint-module-utils": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz",
"integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==",
"dev": true,
"dependencies": {
"debug": "^3.2.7"
},
"engines": {
"node": ">=4"
},
"peerDependenciesMeta": {
"eslint": {
"optional": true
}
}
},
"node_modules/eslint-module-utils/node_modules/debug": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true,
"dependencies": {
"ms": "^2.1.1"
}
},
"node_modules/eslint-plugin-import": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz",
"integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==",
"dev": true,
"dependencies": {
"array-includes": "^3.1.7",
"array.prototype.findlastindex": "^1.2.3",
"array.prototype.flat": "^1.3.2",
"array.prototype.flatmap": "^1.3.2",
"debug": "^3.2.7",
"doctrine": "^2.1.0",
"eslint-import-resolver-node": "^0.3.9",
"eslint-module-utils": "^2.8.0",
"hasown": "^2.0.0",
"is-core-module": "^2.13.1",
"is-glob": "^4.0.3",
"minimatch": "^3.1.2",
"object.fromentries": "^2.0.7",
"object.groupby": "^1.0.1",
"object.values": "^1.1.7",
"semver": "^6.3.1",
"tsconfig-paths": "^3.15.0"
},
"engines": {
"node": ">=4"
},
"peerDependencies": {
"eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8"
}
},
"node_modules/eslint-plugin-import/node_modules/debug": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true,
"dependencies": {
"ms": "^2.1.1"
}
},
"node_modules/eslint-plugin-import/node_modules/doctrine": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
"integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
"dev": true,
"dependencies": {
"esutils": "^2.0.2"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/eslint-plugin-import/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/eslint-plugin-react": {
"version": "7.34.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz",
@ -5140,6 +5275,15 @@
"node": "*"
}
},
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/minimist-options": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz",
@ -5312,6 +5456,20 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/object.groupby": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
"integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.7",
"define-properties": "^1.2.1",
"es-abstract": "^1.23.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/object.hasown": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz",
@ -6163,8 +6321,6 @@
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
@ -6662,6 +6818,15 @@
"node": ">=8"
}
},
"node_modules/strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/strip-indent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
@ -7006,6 +7171,30 @@
"typescript": ">=4.2.0"
}
},
"node_modules/tsconfig-paths": {
"version": "3.15.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
"integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
"dev": true,
"dependencies": {
"@types/json5": "^0.0.29",
"json5": "^1.0.2",
"minimist": "^1.2.6",
"strip-bom": "^3.0.0"
}
},
"node_modules/tsconfig-paths/node_modules/json5": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
"dev": true,
"dependencies": {
"minimist": "^1.2.0"
},
"bin": {
"json5": "lib/cli.js"
}
},
"node_modules/tslib": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",

View file

@ -1,6 +1,7 @@
{
"name": "@prozilla-os/prozilla-os",
"version": "0.1.0",
"private": true,
"author": {
"name": "Prozilla",
"email": "business@prozilla.dev",
@ -8,10 +9,6 @@
},
"homepage": "https://os.prozilla.dev/",
"repository": "https://github.com/Prozilla/ProzillaOS",
"workspaces": [
"packages/core",
"packages/apps/file-explorer"
],
"type": "module",
"scripts": {
"start": "vite",
@ -22,12 +19,17 @@
"deploy": "tsx scripts/deploy",
"fetch": "tsx scripts/fetchRepository"
},
"workspaces": [
"packages/core",
"packages/apps/file-explorer"
],
"dependencies": {
"@prozilla-os/core": "^1.0.0",
"@prozilla-os/file-explorer": "^1.0.0",
"@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-regular-svg-icons": "^6.4.0",
"@fortawesome/free-solid-svg-icons": "^6.4.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@prozilla-os/core": "^1.0.0",
"@vitejs/plugin-react-swc": "^3.7.0",
"anser": "^2.1.1",
"escape-carriage": "^1.3.1",
@ -53,6 +55,7 @@
"@types/react-dom": "^18.3.0",
"@types/react-syntax-highlighter": "^15.5.13",
"eslint": "^8.57.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-refresh": "^0.4.7",
"gh-pages": "^5.0.0",

View file

@ -1 +1 @@
# @ProzillaOS/file-explorer
# @prozilla-os/file-explorer

View file

@ -11,7 +11,11 @@
"scripts": {
"test": "tsc --watch"
},
"repository": "https://github.com/Prozilla/ProzillaOS",
"repository": {
"type": "git",
"url": "git+https://github.com/Prozilla/ProzillaOS.git",
"directory": "packages/apps/file-explorer"
},
"license": "MIT",
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.5.2",

View file

@ -4,11 +4,11 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faArrowUp, faCaretLeft, faCaretRight, faCircleInfo, faCog, faDesktop, faFileLines, faHouse, faImage, faPlus, faSearch, faTrash } from "@fortawesome/free-solid-svg-icons";
import { QuickAccessButton } from "./QuickAccessButton";
import { ImportButton } from "./ImportButton";
import { Actions, AppsManager, ClickAction, CODE_EXTENSIONS, DIALOG_CONTENT_TYPES, DialogBox, DirectoryList, Divider, FileEventHandler, FolderEventHandler, OnSelectionChangeParams, useAlert, useContextMenu, useHistory, useSystemManager, useVirtualRoot, useWindowedModal, useWindowsManager, utilStyles, Vector2, VirtualFile, VirtualFolder, VirtualFolderLink, VirtualRoot, WindowProps } from "@prozilla-os/core";
import { Actions, ClickAction, CODE_EXTENSIONS, DIALOG_CONTENT_TYPES, DialogBox, DirectoryList, Divider, FileEventHandler, FolderEventHandler, OnSelectionChangeParams, useAlert, useContextMenu, useHistory, useSystemManager, useVirtualRoot, useWindowedModal, useWindowsManager, utilStyles, Vector2, VirtualFile, VirtualFolder, VirtualFolderLink, VirtualRoot, WindowProps } from "@prozilla-os/core";
import { SELECTOR_MODE } from "../constants/fileExplorer.const";
import { FileProperties } from "./modals/file-properties/FileProperties";
interface FileExplorerProps extends WindowProps {
export interface FileExplorerProps extends WindowProps {
path?: string;
selectorMode?: number;
Footer: FC;
@ -109,7 +109,7 @@ export function FileExplorer({ app, path: startPath, selectorMode, Footer, onSel
alert({
title: (error as Error).message,
text: "You have exceeded the virtual drive capacity. Files and folders will not be saved until more storage is freed.",
iconUrl: app?.id != null ? AppsManager.getAppIconUrl(app.id) : undefined,
iconUrl: app?.iconUrl as string | undefined,
size: new Vector2(300, 200),
single: true,
});
@ -138,7 +138,7 @@ export function FileExplorer({ app, path: startPath, selectorMode, Footer, onSel
if (directory == null) {
openWindowedModal({
title: "Error",
iconUrl: app?.id != null ? AppsManager.getAppIconUrl(app.id) : undefined,
iconUrl: app?.iconUrl as string | undefined,
size: new Vector2(300, 150),
Modal: (props: {}) =>
<DialogBox {...props}>

View file

@ -1,13 +1,13 @@
import styles from "./FileProperties.module.css";
import utilStyles from "../../../styles/utils.module.css";
import { AppsManager, ImagePreview, ModalProps, StorageManager, VirtualFile, WindowedModal } from "@prozilla-os/core";
import { ImagePreview, ModalProps, StorageManager, useSystemManager, utilStyles, VirtualFile, WindowedModal } from "@prozilla-os/core";
interface FilePropetiesProps extends ModalProps {
file: VirtualFile;
}
export function FileProperties({ modal, params, file, ...props }: FilePropetiesProps) {
const associatedApp = file.extension != null ? AppsManager.getAppByFileExtension(file.extension) : null;
const { appsConfig } = useSystemManager();
const associatedApp = file.extension != null ? appsConfig.getAppByFileExtension(file.extension) : null;
return <WindowedModal className={styles.FileProperties} modal={modal} params={params} {...props}>
<span className={styles.Section}>
@ -19,7 +19,7 @@ export function FileProperties({ modal, params, file, ...props }: FilePropetiesP
{associatedApp != null &&
<span className={styles.Line}>
Opens with:
<ImagePreview className={styles.AppIcon} source={AppsManager.getAppIconUrl(associatedApp.id)}/>
<ImagePreview className={styles.AppIcon} source={associatedApp.iconUrl ?? ""}/>
{associatedApp.name}
</span>
}

View file

@ -1,2 +1,10 @@
export { SELECTOR_MODE } from "./constants/fileExplorer.const";
export { FileExplorer } from "./components/FileExplorer";
import { App } from "@prozilla-os/core";
import { FileExplorer } from "./components/FileExplorer";
import { VirtualBase } from "@prozilla-os/core/src/features/virtual-drive/virtualBase";
const fileExplorer = new App("File Explorer", "file-explorer", FileExplorer)
.setIconUrl("https://os.prozilla.dev/assets/apps/icons/file-explorer.svg");
export { fileExplorer };
export { FileSelectorMode } from "./types/index.d";
VirtualBase;

View file

@ -0,0 +1,5 @@
export enum FileSelectorMode {
None = 0,
Single = 1,
Multi = 2
}

View file

@ -1 +1 @@
# @ProzillaOS/core
# @prozilla-os/core

View file

@ -11,9 +11,14 @@
"scripts": {
"test": "tsc --watch"
},
"repository": "https://github.com/Prozilla/ProzillaOS",
"repository": {
"type": "git",
"url": "git+https://github.com/Prozilla/ProzillaOS.git",
"directory": "packages/core"
},
"license": "MIT",
"dependencies": {
"@prozilla-os/file-explorer": "^1.0.0",
"@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-regular-svg-icons": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.5.2",
@ -22,8 +27,7 @@
"react-draggable": "^4.4.6",
"react-error-boundary": "^4.0.13",
"react-ga4": "^2.1.0",
"react-svg": "^16.1.34",
"@prozilla-os/file-explorer": "^1.0.0"
"react-svg": "^16.1.34"
},
"devDependencies": {
"@types/node": "^20.14.2",
@ -31,5 +35,9 @@
},
"files": [
"src/index.ts"
]
],
"publishConfig": {
"registry": "https://npm.pkg.github.com",
"access": "public"
}
}

View file

@ -1,8 +1,7 @@
import { useState } from "react";
import styles from "./ImagePreview.module.css";
import { ReactSVG } from "react-svg";
import { AppsManager } from "../../../features";
import { APPS } from "../../../constants/apps.const";
import { useSystemManager } from "../../../hooks";
interface ImagePreviewProps {
source: string;
@ -11,6 +10,7 @@ interface ImagePreviewProps {
}
export function ImagePreview({ source, className, onError, ...props }: ImagePreviewProps) {
const { virtualDriveConfig } = useSystemManager();
const [loadingFailed, setLoadingFailed] = useState(false);
const onLoadingError = () => {
@ -24,7 +24,7 @@ export function ImagePreview({ source, className, onError, ...props }: ImagePrev
return (<div className={classNames.join(" ")} {...props}>
{loadingFailed
? <ReactSVG src={AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "file")}/>
? <ReactSVG src={virtualDriveConfig.fileIcon}/>
: source.endsWith(".svg")
? <ReactSVG src={source} onError={onLoadingError}/>
: <img src={source} onError={onLoadingError} alt="Preview" draggable="false"/>

View file

@ -31,7 +31,7 @@ export function Interactable({ onClick, onDoubleClick, children, ...props }: Int
setClicked(true);
onClick?.(event);
timeoutId = setTimeout(() => {
timeoutId = window.setTimeout(() => {
setClicked(false);
}, miscConfig.doubleClickDelay);
};

View file

@ -10,13 +10,13 @@ import { RadioAction } from "../actions/actions/RadioAction";
import { Divider } from "../actions/actions/Divider";
import { Share } from "../modals/share/Share";
import { ModalProps } from "../modals/ModalView";
import { APP_NAMES, APP_ICONS, APPS } from "../../constants/apps.const";
import { SettingsManager, reloadViewport, ModalsManager, Vector2, isValidInteger } from "../../features";
import { VirtualFile } from "../../features/virtual-drive/file";
import { VirtualFolder, VirtualFolderLink } from "../../features/virtual-drive/folder";
import { useSettingsManager, useWindowsManager, useVirtualRoot, useWindowedModal, useContextMenu, useSystemManager } from "../../hooks";
import { DirectoryList } from "../_utils";
import { FileEventHandler, FolderEventHandler } from "../_utils/directory-list/DirectoryList";
import { fileExplorer } from "@prozilla-os/file-explorer";
export const Desktop = memo(() => {
const { desktopConfig } = useSystemManager();
@ -83,12 +83,12 @@ export const Desktop = memo(() => {
windowsManager?.open("settings", { tab: 2 });
}}/>
<Divider/>
<ClickAction label={`Open in ${APP_NAMES.FILE_EXPLORER}`} icon={APP_ICONS.FILE_EXPLORER} onTrigger={() => {
windowsManager?.open(APPS.FILE_EXPLORER, { path: directory?.path });
<ClickAction label={`Open in ${fileExplorer.name}`} icon={fileExplorer.iconUrl as string | undefined} onTrigger={() => {
windowsManager?.open(fileExplorer.id, { path: directory?.path });
}}/>
<ClickAction label={`Open in ${APP_NAMES.TERMINAL}`} icon={APP_ICONS.TERMINAL} onTrigger={() => {
{/* <ClickAction label={`Open in ${"APP_NAMES.TERMINAL"}`} icon={APP_ICONS.TERMINAL} onTrigger={() => {
windowsManager?.open(APPS.TERMINAL, { path: directory?.path });
}}/>
}}/> */}
<Divider/>
<ClickAction label={"Share"} icon={ModalsManager.getModalIconUrl("share")} onTrigger={() => {
openWindowedModal({
@ -103,7 +103,7 @@ export const Desktop = memo(() => {
<ClickAction label="Open" onTrigger={(event, file) => {
if (windowsManager != null) (file as VirtualFile).open(windowsManager);
}}/>
<ClickAction label={`Reveal in ${APP_NAMES.FILE_EXPLORER}`} icon={faFolder} onTrigger={(event, file) => {
<ClickAction label={`Reveal in ${fileExplorer.name}`} icon={faFolder} onTrigger={(event, file) => {
if (windowsManager != null) (file as VirtualFile).parent?.open(windowsManager);
}}/>
<ClickAction label="Delete" icon={faTrash} onTrigger={(event, file) => {
@ -116,10 +116,10 @@ export const Desktop = memo(() => {
<ClickAction label="Open" onTrigger={(event, folder) => {
if (windowsManager != null) (folder as VirtualFolder).open(windowsManager);
}}/>
<ClickAction label={`Open in ${APP_NAMES.TERMINAL}`} icon={faTerminal} onTrigger={(event, folder) => {
{/* <ClickAction label={`Open in ${APP_NAMES.TERMINAL}`} icon={faTerminal} onTrigger={(event, folder) => {
windowsManager?.open(APPS.TERMINAL, { path: (folder as VirtualFolder).path });
}}/>
<ClickAction label={`Reveal in ${APP_NAMES.FILE_EXPLORER}`} icon={faFolder} onTrigger={(event, folder) => {
}}/> */}
<ClickAction label={`Reveal in ${fileExplorer.name}`} icon={faFolder} onTrigger={(event, folder) => {
if (windowsManager != null) (folder as VirtualFolder).parent?.open(windowsManager);
}}/>
<Divider/>
@ -183,7 +183,7 @@ export const Desktop = memo(() => {
windowsManager?.openFile(file, options);
}}
onOpenFolder={(event, folder) => {
windowsManager?.open(APPS.FILE_EXPLORER, {
windowsManager?.open(fileExplorer.id, {
path: (folder as VirtualFolderLink).linkedPath ?? folder.path
});
}}

View file

@ -3,19 +3,18 @@ import { Button } from "../../_utils/button/Button";
import { WindowedModal } from "../_utils/WindowedModal";
import styles from "./FileSelector.module.css";
import { ModalProps } from "../ModalView";
import { APP_ICONS } from "../../../constants/apps.const";
import { VirtualFile } from "../../../features/virtual-drive/file";
import { VirtualFolder } from "../../../features/virtual-drive/folder";
import { FileExplorer, SELECTOR_MODE } from "@prozilla-os/file-explorer";
import { FileSelectorMode, fileExplorer } from "@prozilla-os/file-explorer";
interface FileSelectorProps extends ModalProps {
type: number;
type: FileSelectorMode;
allowedFormats?: string[];
onFinish: (result: VirtualFile | VirtualFile[]) => void;
}
export function FileSelector({ modal, params, type, allowedFormats, onFinish, ...props }: FileSelectorProps) {
const multi = (type === SELECTOR_MODE.MULTIPLE);
const multi = (type == FileSelectorMode.Multi);
const [selection, setSelection] = useState<string[] | null>(multi ? [] : null);
const [directory, setDirectory] = useState<VirtualFolder | null>(null);
@ -45,10 +44,10 @@ export function FileSelector({ modal, params, type, allowedFormats, onFinish, ..
return <WindowedModal modal={modal} params={{
title: multi ? "Select files" : "Select a file",
iconUrl: APP_ICONS.FILE_EXPLORER,
iconUrl: fileExplorer.iconUrl as string | undefined,
...params,
}} {...props}>
<FileExplorer
<fileExplorer.WindowContent
selectorMode={type}
Footer={() =>
<div className={styles.Footer}>

View file

@ -3,7 +3,6 @@ import { ModalsManager } from "../../../features/modals/modalsManager";
import { WindowedModal } from "../_utils/WindowedModal";
import styles from "./Share.module.css";
import { copyToClipboard, generateUrl } from "../../../features/_utils/browser.utils";
import { AppsManager } from "../../../features/apps/appsManager";
import utilStyles from "../../../styles/utils.module.css";
import { Button } from "../../_utils/button/Button";
import { Option } from "./Option";
@ -13,6 +12,7 @@ import { faSquareCheck } from "@fortawesome/free-solid-svg-icons";
import { useAlert } from "../../../hooks/modals/alert";
import { ModalProps } from "../ModalView";
import { useScrollWithShadow } from "../../../hooks/_utils/scrollWithShadows";
import { useSystemManager } from "../../../hooks";
const APP_OPTIONS: Record<string, { label: string, name: string }[]> = {
"terminal": [
@ -46,6 +46,7 @@ const APP_OPTIONS: Record<string, { label: string, name: string }[]> = {
};
export function Share({ modal, params, ...props }: ModalProps) {
const { appsConfig } = useSystemManager();
const [appId, setAppId] = useState<string>(params?.appId ?? "");
const [fullscreen, setFullscreen] = useState<boolean>(params?.fullscreen ?? false);
const [standalone, setStandalone] = useState<boolean>(params?.standalone ?? false);
@ -123,7 +124,7 @@ export function Share({ modal, params, ...props }: ModalProps) {
<p>App:</p>
<select className={styles.Input} name="app" value={appId} onChange={onAppIdChange as unknown as ChangeEventHandler}>
<option value={""}>(None)</option>
{AppsManager.APPS.map(({ name, id }) =>
{appsConfig.apps.map(({ name, id }) =>
<option key={id} value={id}>{name}</option>
)}
</select>

View file

@ -15,8 +15,9 @@ import { ModalsConfig, ModalsConfigOptions } from "../../features/system/configs
import { TaskbarConfig, TaskbarConfigOptions } from "../../features/system/configs/taskbarConfig";
import { TrackingConfig, TrackingConfigOptions } from "../../features/system/configs/trackingConfig";
import { WindowsConfig, WindowsConfigOptions } from "../../features/system/configs/windowsConfig";
import { OptionalInterface } from "../../types/global";
import { OptionalInterface } from "../../types/util";
import { TrackingManagerProvider } from "../../hooks/tracking/trackingManagerProvider";
import { VirtualDriveConfig, VirtualDriveConfigOptions } from "../../features/system/configs/virtualDriveConfig";
interface ProzillaOSProps {
systemName?: string;
@ -29,6 +30,7 @@ interface ProzillaOSProps {
taskbar?: OptionalInterface<TaskbarConfigOptions>;
tracking?: OptionalInterface<TrackingConfigOptions>;
windows?: OptionalInterface<WindowsConfigOptions>;
virtualDrive?: OptionalInterface<VirtualDriveConfigOptions>;
}
children?: ReactNode;
}
@ -45,7 +47,8 @@ export const ProzillaOS = memo(function(props: ProzillaOSProps): ReactElement {
modalsConfig: new ModalsConfig(config?.modals),
taskbarConfig: new TaskbarConfig(config?.taskbar),
trackingConfig: new TrackingConfig(config?.tracking),
windowsConfig: new WindowsConfig(config?.windows)
windowsConfig: new WindowsConfig(config?.windows),
virtualDriveConfig: new VirtualDriveConfig(config?.virtualDrive),
} as SystemManagerParams;
return <SystemManagerProvider {...systemParams}>

View file

@ -2,7 +2,6 @@ import { CSSProperties, memo, MouseEvent, MutableRefObject, ReactEventHandler, U
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/apps/appsManager";
import { ReactSVG } from "react-svg";
import { HomeMenu } from "./menus/HomeMenu";
import { OutsideClickListener } from "../../hooks/_utils/outsideClick";
@ -12,19 +11,18 @@ import { AppButton } from "./app-icon/AppIcon";
import { useContextMenu } from "../../hooks/modals/contextMenu";
import { Actions } from "../actions/Actions";
import { ClickAction } from "../actions/actions/ClickAction";
import { APPS, APP_NAMES } from "../../constants/apps.const";
import { useWindowsManager } from "../../hooks/windows/windowsManagerContext";
import { useSettingsManager } from "../../hooks/settings/settingsManagerContext";
import { SettingsManager } from "../../features/settings/settingsManager";
import { useWindows } from "../../hooks/windows/windowsContext";
import { ZIndexManager } from "../../features/z-index/zIndexManager";
import { useZIndex } from "../../hooks/z-index/zIndex";
import { App } from "../../features/apps/app";
import { Battery, Calendar, Network, Volume } from "./indicators";
import { useSystemManager } from "../../hooks";
import { App } from "../../features";
export const Taskbar = memo(() => {
const { taskbarConfig } = useSystemManager();
const { taskbarConfig, appsConfig } = useSystemManager();
const ref = useRef<HTMLDivElement>(null);
const settingsManager = useSettingsManager();
const [showHome, setShowHome] = useState(false);
@ -42,8 +40,8 @@ export const Taskbar = memo(() => {
const windows = useWindows();
const { onContextMenu } = useContextMenu({ Actions: (props) =>
<Actions avoidTaskbar={false} {...props}>
<ClickAction label={`Open ${APP_NAMES.SETTINGS}`} icon={faCog} onTrigger={() => {
windowsManager?.open(APPS.SETTINGS);
<ClickAction label={`Open ${"Settings"}`} icon={faCog} onTrigger={() => {
windowsManager?.open("settings");
}}/>
</Actions>
});
@ -55,7 +53,7 @@ export const Taskbar = memo(() => {
void settings?.get("pins", (pinList: string) => {
const pins = pinList.split(",");
const newApps = AppsManager.APPS.sort((appA, appB) => {
const newApps = appsConfig.apps.sort((appA, appB) => {
const indexA = pins.indexOf(appA.id);
const indexB = pins.indexOf(appB.id);
if (indexA < 0 && indexB > 0) {

View file

@ -1,12 +1,11 @@
import { FC, memo, MouseEvent } from "react";
import { App } from "../../../features/apps/app";
import { App } from "../../../features/system/configs/app";
import styles from "./AppIcon.module.css";
import { ReactSVG } from "react-svg";
import { useSettingsManager } from "../../../hooks/settings/settingsManagerContext";
import { useContextMenu } from "../../../hooks/modals/contextMenu";
import { Actions } from "../../actions/Actions";
import { ClickAction } from "../../actions/actions/ClickAction";
import { AppsManager } from "../../../features/apps/appsManager";
import { WindowsManager } from "../../../features/windows/windowsManager";
interface AppButtonProps {
@ -20,7 +19,7 @@ export const AppButton: FC<AppButtonProps> = memo(({ app, windowsManager, active
const settingsManager = useSettingsManager();
const { onContextMenu } = useContextMenu({ Actions: (props) =>
<Actions avoidTaskbar={false} {...props}>
<ClickAction label={app.name} icon={AppsManager.getAppIconUrl(app.id)} onTrigger={() => {
<ClickAction label={app.name} icon={app.iconUrl as string | undefined} onTrigger={() => {
windowsManager?.open(app.id);
}}/>
{/* <ClickAction label={isPinned ? "Unpin from taskbar" : "Pin to taskbar"} icon={faThumbTack} onTrigger={() => {

View file

@ -6,9 +6,9 @@ import { faCircleInfo, faFileLines, faGear, faImage, faPowerOff } from "@fortawe
import { ReactSVG } from "react-svg";
import { useEffect, useState } from "react";
import { utilStyles } from "../../..";
import { APPS } from "../../../constants/apps.const";
import { closeViewport, Vector2, AppsManager } from "../../../features";
import { closeViewport, Vector2 } from "../../../features";
import { useWindowsManager, useVirtualRoot, useKeyboardListener, useSystemManager } from "../../../hooks";
import { fileExplorer } from "@prozilla-os/file-explorer";
interface HomeMenuProps {
active: boolean;
@ -17,7 +17,7 @@ interface HomeMenuProps {
}
export function HomeMenu({ active, setActive, search }: HomeMenuProps) {
const { systemName } = useSystemManager();
const { systemName, appsConfig } = useSystemManager();
const windowsManager = useWindowsManager();
const virtualRoot = useVirtualRoot();
const [tabIndex, setTabIndex] = useState(active ? 0 : -1);
@ -81,13 +81,13 @@ export function HomeMenu({ active, setActive, search }: HomeMenuProps) {
</button>
<button title="Images" tabIndex={tabIndex} onClick={() => {
setActive(false);
windowsManager?.open(APPS.FILE_EXPLORER, { path: "~/Pictures" });
windowsManager?.open(fileExplorer.id, { path: "~/Pictures" });
}}>
<FontAwesomeIcon icon={faImage}/>
</button>
<button title="Documents" tabIndex={tabIndex} onClick={() => {
setActive(false);
windowsManager?.open(APPS.FILE_EXPLORER, { path: "~/Documents" }); }
windowsManager?.open(fileExplorer.id, { path: "~/Documents" }); }
}>
<FontAwesomeIcon icon={faFileLines}/>
</button>
@ -95,7 +95,7 @@ export function HomeMenu({ active, setActive, search }: HomeMenuProps) {
<div className={styles.Apps}>
<h1 className={utilStyles.TextBold}>{systemName}</h1>
<div className={appStyles.AppList}>
{AppsManager.APPS.sort((a, b) => a.name.localeCompare(b.name)).map(({ name, id }) =>
{appsConfig.apps.sort((a, b) => a.name.localeCompare(b.name)).map(({ name, id, iconUrl }) =>
<button
key={id}
className={appStyles.AppButton}
@ -106,7 +106,7 @@ export function HomeMenu({ active, setActive, search }: HomeMenuProps) {
}}
title={name}
>
<ReactSVG src={AppsManager.getAppIconUrl(id)}/>
<ReactSVG src={iconUrl ?? ""}/>
<h2 className={utilStyles.TextRegular}>{name}</h2>
</button>
)}

View file

@ -1,11 +1,11 @@
import styles from "./SearchMenu.module.css";
import appStyles from "./AppList.module.css";
import { AppsManager } from "../../../features/apps/appsManager";
import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext";
import { ChangeEventHandler, MutableRefObject, useEffect, useState } from "react";
import { useKeyboardListener } from "../../../hooks/_utils/keyboard";
import { App } from "../../../features/apps/app";
import { App } from "../../../features/system/configs/app";
import { ReactSVG } from "react-svg";
import { useSystemManager } from "../../../hooks";
interface SearchMenuProps {
active: boolean;
@ -16,6 +16,7 @@ interface SearchMenuProps {
}
export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inputRef }: SearchMenuProps) {
const { appsConfig } = useSystemManager();
const windowsManager = useWindowsManager();
const [apps, setApps] = useState<App[] | null>(null);
const [tabIndex, setTabIndex] = useState(active ? 0 : -1);
@ -32,7 +33,7 @@ export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inp
}, [inputRef]);
useEffect(() => {
setApps(AppsManager.APPS.filter(({ name, id }) =>
setApps(appsConfig.apps.filter(({ name, id }) =>
name.toLowerCase().includes(searchQuery.toLowerCase().trim())
|| id.toLowerCase().includes(searchQuery.toLowerCase().trim())
).sort((a, b) =>
@ -79,7 +80,7 @@ export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inp
windowsManager?.open(id);
}}
>
<ReactSVG src={AppsManager.getAppIconUrl(id)}/>
<ReactSVG src={appsConfig.getAppById(id)?.iconUrl ?? ""}/>
<p>{name}</p>
</button>
)}

View file

@ -1,8 +1,8 @@
import { useEffect, useState } from "react";
import { useAlert } from "../../hooks/modals/alert";
import { AppsManager } from "../../features/apps/appsManager";
import { Vector2 } from "../../features/math/vector2";
import { App } from "../../features/apps/app";
import { App } from "../../features/system/configs/app";
import { useSystemManager } from "../../hooks";
export interface WindowFallbackViewProps {
error?: Error;
@ -13,6 +13,7 @@ export interface WindowFallbackViewProps {
// I don't know why this component's type needs to be ReactNode instead of FC, it has something to do with the way it's implemented
export function WindowFallbackView({ error, resetErrorBoundary: _resetErrorBoundary, app, closeWindow }: WindowFallbackViewProps): undefined {
const { appsConfig } = useSystemManager();
const { alert } = useAlert();
const [alerted, setAlerted] = useState(false);
@ -27,7 +28,7 @@ export function WindowFallbackView({ error, resetErrorBoundary: _resetErrorBound
alert({
title: `${app.name} has stopped working`,
text: `${error.name}: ${error.message}`,
iconUrl: AppsManager.getAppIconUrl(app.id),
iconUrl: appsConfig.getAppById(app.id)?.iconUrl as string | undefined,
size: new Vector2(350, 150),
single: true
});

View file

@ -9,7 +9,6 @@ import { Vector2 } from "../../features/math/vector2";
import { faWindowMaximize } from "@fortawesome/free-regular-svg-icons";
import utilStyles from "../../styles/utils.module.css";
import { useContextMenu } from "../../hooks/modals/contextMenu";
import { AppsManager } from "../../features/apps/appsManager";
import { ClickAction } from "../actions/actions/ClickAction";
import { Actions } from "../actions/Actions";
import { useScreenDimensions } from "../../hooks/_utils/screen";
@ -41,7 +40,7 @@ export interface WindowProps extends WindowOptions {
}
export const WindowView: FC<WindowProps> = memo(({ id, app, size, position, onInteract, options, active, fullscreen, minimized, toggleMinimized, index }) => {
const { systemName, windowsConfig } = useSystemManager();
const { systemName, windowsConfig, appsConfig } = useSystemManager();
const windowsManager = useWindowsManager();
const nodeRef = useRef(null);
const { openWindowedModal } = useWindowedModal();
@ -50,7 +49,7 @@ export const WindowView: FC<WindowProps> = memo(({ id, app, size, position, onIn
const [maximized, setMaximized] = useState(fullscreen ?? false);
const [screenWidth, screenHeight] = useScreenDimensions();
const [title, setTitle] = useState(app?.name ?? "");
const [iconUrl, setIconUrl] = useState(app ? AppsManager.getAppIconUrl(app?.id) : "");
const [iconUrl, setIconUrl] = useState<string>(app ? appsConfig.getAppById(app?.id)?.iconUrl ?? "" : "");
const zIndex = useZIndex({ groupIndex: ZIndexManager.GROUPS.WINDOWS, index: index ?? 0 });
const { onContextMenu, ShortcutsListener } = useContextMenu({ Actions: (props) =>

View file

@ -1,43 +0,0 @@
export const APPS = {
TERMINAL: "terminal",
SETTINGS: "settings",
MEDIA_VIEWER: "media-viewer",
TEXT_EDITOR: "text-editor",
FILE_EXPLORER: "file-explorer",
CALCULATOR: "calculator",
BROWSER: "browser",
LOGIC_SIM: "logic-sim",
};
export type AppKey = keyof typeof APPS;
export const APP_NAMES: Record<AppKey, string> = {
TERMINAL: "Commands",
SETTINGS: "Settings",
MEDIA_VIEWER: "Photos",
TEXT_EDITOR: "Notes",
FILE_EXPLORER: "Files",
CALCULATOR: "Maths",
BROWSER: "Browser",
LOGIC_SIM: "Logic Sim (WIP)"
};
export const APP_DESCRIPTIONS: Record<AppKey, string | null> = {
TERMINAL: "A command line tool inspired by the Unix shell that runs entirely in your browser and uses a virtual file system.",
SETTINGS: "Configure ProzillaOS' settings and customize your experience.",
TEXT_EDITOR: "Simple text editor for reading and writing text documents.",
FILE_EXPLORER: "Browse and manage your virtual files.",
CALCULATOR: "Simple calculator app.",
BROWSER: "Browse the internet.",
LOGIC_SIM: "Create digital logic circuits using the online simulator.",
MEDIA_VIEWER: null,
};
export const APP_ICONS = {
TERMINAL: `/assets/apps/icons/${APPS.TERMINAL}.svg`,
SETTINGS: `/assets/apps/icons/${APPS.SETTINGS}.svg`,
MEDIA_VIEWER: `/assets/apps/icons/${APPS.MEDIA_VIEWER}.svg`,
TEXT_EDITOR: `/assets/apps/icons/${APPS.TEXT_EDITOR}.svg`,
FILE_EXPLORER: `/assets/apps/icons/${APPS.FILE_EXPLORER}.svg`,
CALCULATOR: `/assets/apps/icons/${APPS.CALCULATOR}.svg`,
};

View file

@ -0,0 +1,3 @@
export { DIALOG_CONTENT_TYPES } from "./modals.const";
export { CODE_EXTENSIONS } from "./virtualDrive.const";
export { ANSI } from "./utils.const";

View file

@ -0,0 +1,19 @@
export const ANSI = {
fg: {
black: "\u001b[30m",
red: "\u001b[31m",
green: "\u001b[32m",
yellow: "\u001b[33m",
blue: "\u001b[34m",
magenta: "\u001b[35m",
cyan: "\u001b[36m",
white: "\u001b[37m",
},
bg: {
},
decoration: {
dim: "\u001b[2m",
},
reset: "\u001b[0m",
};

View file

@ -1,35 +0,0 @@
import { Vector2 } from "../math/vector2";
import { WindowProps } from "../../components/windows/WindowView";
import { FC } from "react";
export class App {
name: string = "App";
id: string = "app";
windowContent?: FC<JSX.IntrinsicAttributes & WindowProps>;
windowOptions?: {
size: Vector2;
[key: string]: unknown;
};
isActive: boolean = false;
isPinned?: boolean;
/**
* @param windowOptions - Default window options
*/
constructor(name: App["name"], id: App["id"], windowContent: App["windowContent"], windowOptions?: App["windowOptions"]) {
Object.assign(this, { name, id, windowContent, windowOptions });
if (this.windowContent == null)
console.warn(`${this.name} (${this.id}) is missing the windowContent property.`);
}
WindowContent = (props: JSX.IntrinsicAttributes & WindowProps) => {
props = { ...props, ...this.windowOptions };
if (this.windowContent == null) {
return null;
}
return <this.windowContent {...props}/>;
};
}

View file

@ -1,49 +0,0 @@
import { App } from "./app";
export class AppsManager {
static APPS: App[] = [];
static getAppById(id: string): App | null {
let application: App | null = null;
this.APPS.forEach((app) => {
if (app.id === id) {
application = app;
return;
}
});
return application;
}
/**
* Get the app associated with a file extension
*/
static getAppByFileExtension(fileExtension: string): App | null {
return null;
// let app: App | null = null;
// if (IMAGE_FORMATS.includes(fileExtension))
// return this.getAppById(APPS.MEDIA_VIEWER);
// switch (fileExtension) {
// default:
// 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
*/
static getAppIconUrl(appId: string, iconName?: string): string {
if (iconName == null) {
return `/assets/apps/icons/${appId}.svg`;
} else {
return `/assets/apps/${appId}/icons/${iconName}.svg`;
}
}
}

View file

@ -1,2 +0,0 @@
export { AppsManager } from "./appsManager";
export { App } from "./app";

View file

@ -1,11 +1,12 @@
export * from "./system/configs";
export * from "./virtual-drive";
export * from "./_utils";
export { ModalsManager } from "./modals/modalsManager";
export { SettingsManager } from "./settings/settingsManager";
export { StorageManager } from "./storage/storageManager";
export { TrackingManager } from "./tracking/trackingManager";
export { WindowsManager } from "./windows/windowsManager";
export { ZIndexManager } from "./z-index/zIndexManager";
export * from "./system/configs";
export * from "./_utils";
export * from "./apps";
export * from "./virtual-drive";
export { Vector2 } from "./math/vector2";

View file

@ -0,0 +1,60 @@
import { Vector2 } from "../../math/vector2";
import { WindowProps } from "../../../components/windows/WindowView";
import { FC } from "react";
const validIdRegex = /^[a-zA-Z0-9-]+$/;
export class App<AppProps extends WindowProps = WindowProps> {
name: string = "App";
id: string = "app";
windowContent: FC<AppProps>;
windowOptions?: {
size: Vector2;
[key: string]: unknown;
};
description: string | null = null;
iconUrl: string | null = null;
isActive: boolean = false;
isPinned?: boolean;
/**
* @param windowOptions - Default window options
*/
constructor(name: App["name"], id: App["id"], windowContent: App<AppProps>["windowContent"], windowOptions?: App["windowOptions"]) {
this.name = name;
this.id = id;
this.windowContent = windowContent;
this.windowOptions = windowOptions;
if (this.id.match(validIdRegex) == null)
throw new Error(`Invalid app ID found: ${this.id}`
+ "\nApp IDs may only contain letters (a-zA-Z), numbers (0-9) and dashes (-).");
}
WindowContent = (props: AppProps) => {
props = { ...props, ...this.windowOptions };
if (this.windowContent == null) {
return null;
}
return <this.windowContent {...props}/>;
};
setDescription(description: App["description"]): this {
this.description = description;
return this;
}
setIconUrl(iconUrl: App["iconUrl"]): this {
this.iconUrl = iconUrl;
return this;
}
setName(name: string): this {
this.name = name;
return this;
}
}

View file

@ -1,15 +1,58 @@
import { OptionalInterface } from "../../../types/global";
import { App } from "../../apps";
import { App } from ".";
import { OptionalInterface } from "../../../types/util";
export interface AppsConfigOptions {
apps: App[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
apps: App<any>[];
}
export class AppsConfig {
apps: AppsConfigOptions["apps"];
apps: AppsConfigOptions["apps"] = [];
constructor(options?: OptionalInterface<AppsConfigOptions>) {
constructor(options: OptionalInterface<AppsConfigOptions> = {}) {
const { apps } = options as AppsConfigOptions;
this.apps = apps ?? [];
const appIds: string[] = [];
apps.forEach((app) => {
if (appIds.includes(app.id))
throw new Error(`Duplicate app ID found: ${app.id}`
+ "\nApp IDs must be unique.");
this.apps.push(app);
appIds.push(app.id);
});
}
getAppById(id: string): App | null {
let resultApp: App | null = null;
this.apps.forEach((app) => {
if (app.id === id) {
resultApp = app;
return;
}
});
return resultApp;
}
/**
* Get the app associated with a file extension
*/
getAppByFileExtension(fileExtension: string): App | null {
return null;
// let app: App | null = null;
// if (IMAGE_FORMATS.includes(fileExtension))
// return this.getAppById(APPS.MEDIA_VIEWER);
// switch (fileExtension) {
// default:
// app = this.getAppById(APPS.TEXT_EDITOR);
// break;
// }
// return app;
}
}

View file

@ -1,4 +1,4 @@
import { OptionalInterface } from "../../../types/global";
import { OptionalInterface } from "../../../types/util";
export interface DesktopConfigOptions {
/** Array of URLs of wallpaper images */
@ -25,7 +25,7 @@ export class DesktopConfig {
defaultIconSize: DesktopConfigOptions["defaultIconSize"];
defaultIconDirection: DesktopConfigOptions["defaultIconDirection"];
constructor(options?: OptionalInterface<DesktopConfigOptions>) {
constructor(options: OptionalInterface<DesktopConfigOptions> = {}) {
const { wallpapers, defaultWallpaper, defaultIconSize, defaultIconDirection } = options as DesktopConfigOptions;
this.wallpapers = wallpapers ?? [

View file

@ -1,7 +1,9 @@
export { App } from "./app";
export { AppsConfig } from "./appsConfig";
export { DesktopConfig } from "./desktopConfig";
export { MiscConfig } from "./miscConfig";
export { ModalsConfig } from "./modalsConfig";
export { TaskbarConfig } from "./taskbarConfig";
export { TrackingConfig } from "./trackingConfig";
export { WindowsConfig } from "./windowsConfig";
export { WindowsConfig } from "./windowsConfig";
export { VirtualDriveConfig } from "./virtualDriveConfig";

View file

@ -1,4 +1,4 @@
import { OptionalInterface } from "../../../types/global";
import { OptionalInterface } from "../../../types/util";
export interface MiscConfigOptions {
/**
@ -11,8 +11,9 @@ export interface MiscConfigOptions {
export class MiscConfig {
doubleClickDelay: MiscConfigOptions["doubleClickDelay"];
constructor(options?: OptionalInterface<MiscConfigOptions>) {
constructor(options: OptionalInterface<MiscConfigOptions> = {}) {
const { doubleClickDelay } = options as MiscConfigOptions;
this.doubleClickDelay = doubleClickDelay ?? 250;
}
}

View file

@ -1,4 +1,4 @@
import { OptionalInterface } from "../../../types/global";
import { OptionalInterface } from "../../../types/util";
import { Vector2 } from "../../math/vector2";
export interface ModalsConfigOptions {
@ -19,8 +19,9 @@ export class ModalsConfig {
defaultDialogSize: ModalsConfigOptions["defaultDialogSize"];
defaultFileSelectorSize: ModalsConfigOptions["defaultFileSelectorSize"];
constructor(options?: OptionalInterface<ModalsConfigOptions>) {
constructor(options: OptionalInterface<ModalsConfigOptions> = {}) {
const { defaultDialogSize, defaultFileSelectorSize } = options as ModalsConfigOptions;
this.defaultDialogSize = defaultDialogSize ?? new Vector2(400, 200);
this.defaultFileSelectorSize = defaultFileSelectorSize ?? new Vector2(700, 400);
}

View file

@ -1,4 +1,4 @@
import { OptionalInterface } from "../../../types/global";
import { OptionalInterface } from "../../../types/util";
export interface TaskbarConfigOptions {
/**
@ -11,8 +11,9 @@ export interface TaskbarConfigOptions {
export class TaskbarConfig {
height: TaskbarConfigOptions["height"];
constructor(options?: OptionalInterface<TaskbarConfigOptions>) {
constructor(options: OptionalInterface<TaskbarConfigOptions> = {}) {
const { height } = options as TaskbarConfigOptions;
this.height = height ?? 3 * 16;
}
}

View file

@ -1,4 +1,4 @@
import { OptionalInterface } from "../../../types/global";
import { OptionalInterface } from "../../../types/util";
export interface TrackingConfigOptions {
/**
@ -15,8 +15,9 @@ export class TrackingConfig {
enabled: TrackingConfigOptions["enabled"];
GAMeasurementId: TrackingConfigOptions["GAMeasurementId"];
constructor(options?: OptionalInterface<TrackingConfigOptions>) {
constructor(options: OptionalInterface<TrackingConfigOptions> = {}) {
const { enabled, GAMeasurementId } = options as TrackingConfigOptions;
this.enabled = enabled ?? true;
this.GAMeasurementId = GAMeasurementId ?? "G-ZFQRR9DP3C";
}

View file

@ -0,0 +1,39 @@
import { OptionalInterface } from "../../../types/util";
export interface VirtualDriveConfigOptions {
fileIcon: string;
infoFileIcon: string;
textFileIcon: string;
codeFileIcon: string;
folderIcon: string;
imagesFolderIcon: string;
textFolderIcon: string;
folderLinkIcon: string;
}
export class VirtualDriveConfig {
fileIcon: VirtualDriveConfigOptions["fileIcon"];
infoFileIcon: VirtualDriveConfigOptions["infoFileIcon"];
textFileIcon: VirtualDriveConfigOptions["textFileIcon"];
codeFileIcon: VirtualDriveConfigOptions["codeFileIcon"];
folderIcon: VirtualDriveConfigOptions["folderIcon"];
imagesFolderIcon: VirtualDriveConfigOptions["imagesFolderIcon"];
textFolderIcon: VirtualDriveConfigOptions["textFolderIcon"];
folderLinkIcon: VirtualDriveConfigOptions["folderLinkIcon"];
constructor(options: OptionalInterface<VirtualDriveConfigOptions> = {}) {
const { fileIcon, infoFileIcon, textFileIcon, codeFileIcon, folderIcon, imagesFolderIcon, textFolderIcon, folderLinkIcon } = options as VirtualDriveConfigOptions;
this.fileIcon = fileIcon ?? "https://os.prozilla.dev/assets/apps/file-explorer/icons/file.svg";
this.infoFileIcon = infoFileIcon ?? "https://os.prozilla.dev/assets/apps/file-explorer/icons/file-info.svg";
this.textFileIcon = textFileIcon ?? "https://os.prozilla.dev/assets/apps/file-explorer/icons/file-text.svg";
this.codeFileIcon = codeFileIcon ?? "https://os.prozilla.dev/assets/apps/file-explorer/icons/file-code.svg";
this.folderIcon = folderIcon ?? "https://os.prozilla.dev/assets/apps/file-explorer/icons/folder.svg";
this.imagesFolderIcon = imagesFolderIcon ?? "https://os.prozilla.dev/assets/apps/file-explorer/icons/folder-images.svg";
this.textFolderIcon = textFolderIcon ?? "https://os.prozilla.dev/assets/apps/file-explorer/icons/folder-text.svg";
this.folderLinkIcon = folderLinkIcon ?? "https://os.prozilla.dev/assets/apps/file-explorer/icons/folder-link.svg";
}
}

View file

@ -1,4 +1,4 @@
import { OptionalInterface } from "../../../types/global";
import { OptionalInterface } from "../../../types/util";
import { Vector2 } from "../../math/vector2";
export interface WindowsConfigOptions {
@ -24,8 +24,9 @@ export class WindowsConfig {
titleSeparator: WindowsConfigOptions["titleSeparator"];
minScreenSize: WindowsConfigOptions["minScreenSize"];
constructor(options?: OptionalInterface<WindowsConfigOptions>) {
constructor(options: OptionalInterface<WindowsConfigOptions> = {}) {
const { screenMargin, titleSeparator, minScreenSize } = options as WindowsConfigOptions;
this.screenMargin = screenMargin ?? 32;
this.titleSeparator = titleSeparator ?? "-";
this.minScreenSize = minScreenSize ?? new Vector2(350, 350);

View file

@ -1,4 +1,5 @@
import { formatRelativeTime } from "../_utils";
import { VirtualDriveConfig } from "./configs";
import { AppsConfig } from "./configs/appsConfig";
import { DesktopConfig } from "./configs/desktopConfig";
import { MiscConfig } from "./configs/miscConfig";
@ -17,6 +18,7 @@ export interface SystemManagerParams {
taskbarConfig: TaskbarConfig;
trackingConfig: TrackingConfig;
windowsConfig: WindowsConfig;
virtualDriveConfig: VirtualDriveConfig;
};
export class SystemManager {
@ -31,6 +33,7 @@ export class SystemManager {
taskbarConfig: TaskbarConfig;
trackingConfig: TrackingConfig;
windowsConfig: WindowsConfig;
virtualDriveConfig: VirtualDriveConfig;
constructor({
systemName,
@ -41,7 +44,8 @@ export class SystemManager {
modalsConfig,
taskbarConfig,
trackingConfig,
windowsConfig
windowsConfig,
virtualDriveConfig
}: SystemManagerParams) {
this.systemName = systemName ?? "ProzillaOS";
this.tagLine = tagLine ?? "Web-based Operating System";
@ -53,6 +57,7 @@ export class SystemManager {
this.taskbarConfig = taskbarConfig;
this.trackingConfig = trackingConfig;
this.windowsConfig = windowsConfig;
this.virtualDriveConfig = virtualDriveConfig;
this.#startDate = new Date();
}

View file

@ -1,6 +1,4 @@
import { APPS } from "../../../constants/apps.const";
import { IMAGE_FORMATS } from "../../../../../apps/media-viewer/config/mediaViewer.config";
import { AppsManager } from "../../apps/appsManager";
import { WindowsManager } from "../../windows/windowsManager";
import { VirtualBase, VirtualBaseJson } from "../virtualBase";
@ -107,7 +105,7 @@ export class VirtualFile extends VirtualBase {
return await fetch(this.source).then((response) =>
response.text()
).catch((error) => {
console.error(`Error while reading file with id "${this.id}":`, error);
console.error(`Error while reading file with ID: ${this.id}\n`, error);
return null;
}) as string;
}
@ -125,10 +123,12 @@ export class VirtualFile extends VirtualBase {
if (this.source != null && this.extension != null && IMAGE_FORMATS.includes(this.extension))
return this.source;
const { virtualDriveConfig } = this.getRoot().systemManager;
switch (this.extension) {
case "txt":
case "md":
iconUrl = AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "file-text");
iconUrl = virtualDriveConfig.textFileIcon;
break;
case "xml":
case "js":
@ -139,10 +139,10 @@ export class VirtualFile extends VirtualBase {
case "css":
case "html":
case "yml":
iconUrl = AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "file-code");
iconUrl = virtualDriveConfig.codeFileIcon;
break;
default:
iconUrl = AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "file");
iconUrl = virtualDriveConfig.fileIcon;
break;
}

View file

@ -1,6 +1,4 @@
import { VirtualFile } from ".";
import { APPS } from "../../../constants/apps.const";
import { AppsManager } from "../../apps/appsManager";
import { VirtualFileJson } from "./virtualFile";
export interface VirtualFileLinkJson extends VirtualFileJson {
@ -101,7 +99,9 @@ export class VirtualFileLink extends VirtualFile {
}
getIconUrl(...args: Parameters<VirtualFile["getIconUrl"]>): ReturnType<VirtualFile["getIconUrl"]> {
const defaultIconUrl = AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "file");
const { virtualDriveConfig } = this.getRoot().systemManager;
const defaultIconUrl = virtualDriveConfig.fileIcon;
if (this.isValid()) {
return this.iconUrl ?? this.linkedFile?.getIconUrl(...args) ?? defaultIconUrl;
} else {

View file

@ -1,11 +1,10 @@
import { APPS } from "../../../constants/apps.const";
import { AppsManager } from "../../apps/appsManager";
import { removeFromArray } from "../../_utils/array.utils";
import { WindowsManager } from "../../windows/windowsManager";
import { VirtualFileJson } from "../file/virtualFile";
import { VirtualBase, VirtualBaseJson } from "../virtualBase";
import { VirtualFolderLink } from ".";
import { VirtualFile, VirtualFileLink } from "../file";
import { fileExplorer } from "@prozilla-os/file-explorer";
export interface VirtualFolderJson extends VirtualBaseJson {
fls?: VirtualFileJson[];
@ -289,7 +288,7 @@ export class VirtualFolder extends VirtualBase {
* Opens this folder in file explorer
*/
open(windowsManager: WindowsManager) {
return windowsManager.open(APPS.FILE_EXPLORER, { path: this.path });
return windowsManager.open(fileExplorer.id, { path: this.path });
}
/**
@ -353,7 +352,8 @@ export class VirtualFolder extends VirtualBase {
if (this.iconUrl != null)
return this.iconUrl;
return AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "folder");
const { virtualDriveConfig } = this.getRoot().systemManager;
return virtualDriveConfig.folderIcon;
}
toJSON(): VirtualFolderJson | null {

View file

@ -1,5 +1,3 @@
import { APPS } from "../../../constants/apps.const";
import { AppsManager } from "../../apps/appsManager";
import { VirtualFolder } from ".";
import { VirtualFolderJson } from "./virtualFolder";
@ -60,7 +58,8 @@ export class VirtualFolderLink extends VirtualFolder {
if (this.isValid() && this.linkedFolder?.iconUrl)
return this.linkedFolder.iconUrl;
return AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "folder-link");
const { virtualDriveConfig } = this.getRoot().systemManager;
return virtualDriveConfig.folderLinkIcon;
}
toJSON(): VirtualFolderLinkJson | null {

View file

@ -1,5 +1,3 @@
import { APPS } from "../../../constants/apps.const";
import { AppsManager } from "../../apps/appsManager";
import { SystemManager } from "../../system/systemManager";
import { VirtualFile, VirtualFileLink } from "../file";
import { VirtualFolder, VirtualFolderLink } from "../folder";
@ -9,7 +7,7 @@ import { VirtualRoot } from "./virtualRoot";
* Loads default data on the virtual root
*/
export function loadDefaultData(systemManager: SystemManager, virtualRoot: VirtualRoot) {
const { desktopConfig } = systemManager;
const { desktopConfig, virtualDriveConfig } = systemManager;
const linkedPaths: Record<string, string> = {};
virtualRoot.createFolder("home", (folder) => {
@ -27,7 +25,7 @@ export function loadDefaultData(systemManager: SystemManager, virtualRoot: Virtu
});
})
.createFolder("Pictures", (folder) => {
folder.setIconUrl(AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "folder-images"));
folder.setIconUrl(virtualDriveConfig.imagesFolderIcon);
folder.createFolder("Wallpapers", (folder) => {
folder.setProtected(true);
for (let i = 0; i < desktopConfig.wallpapers.length; i++) {
@ -52,13 +50,13 @@ export function loadDefaultData(systemManager: SystemManager, virtualRoot: Virtu
linkedPaths.images = folder.path;
})
.createFolder("Documents", (folder) => {
folder.setIconUrl(AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "folder-text"));
folder.setIconUrl(virtualDriveConfig.textFolderIcon);
folder.createFile("text", "txt", (file) => {
file.setContent("Hello world!");
}).createFile("Info", "md", (file) => {
file.setProtected(true)
.setSource("/documents/Info.md")
.setIconUrl(AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "file-info"));
.setIconUrl(virtualDriveConfig.infoFileIcon);
linkedPaths.info = file.path;
}).createFile("Prozilla", "md", (file) => {
file.setProtected(true)

View file

@ -19,7 +19,7 @@ export class VirtualRoot extends VirtualFolder {
shortcuts: Record<string, VirtualFile | VirtualFileLink | VirtualFolder | VirtualFolderLink>;
initiated: boolean = false;
loadedDefaultData: boolean = false;
#systemManager: SystemManager;
systemManager: SystemManager;
static EVENT_NAMES = {
ERROR: "error"
@ -28,13 +28,13 @@ export class VirtualRoot extends VirtualFolder {
constructor(systemManager: SystemManager) {
super("root");
this.root = this;
this.#systemManager = systemManager;
this.systemManager = systemManager;
this.isRoot = true;
this.shortcuts = {};
}
loadDefaultData() {
loadDefaultData(this.#systemManager, this);
loadDefaultData(this.systemManager, this);
}
loadData() {

View file

@ -1,5 +1,3 @@
import { APPS } from "../../constants/apps.const";
import { AppsManager } from "../apps/appsManager";
import { EventEmitter, EventNamesMap } from "../_utils/event.utils";
import { VirtualRoot } from "./root/virtualRoot";
import { VirtualFile } from "./file";
@ -81,10 +79,12 @@ export class VirtualBase extends EventEmitter<EventNamesMap> {
}
getIconUrl(): string {
return this.iconUrl
?? this.linkedFile?.iconUrl
?? this.linkedFolder?.iconUrl
?? AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "file");
if (this.iconUrl != null) return this.iconUrl;
if (this.linkedFile?.iconUrl != null) return this.linkedFile.iconUrl;
if (this.linkedFolder?.iconUrl != null) return this.linkedFolder.iconUrl;
const { virtualDriveConfig } = this.getRoot().systemManager;
return virtualDriveConfig.fileIcon;
}
getType(): string {

View file

@ -1,6 +1,5 @@
import { randomRange } from "../_utils/math.utils";
import { App } from "../apps/app";
import { AppsManager } from "../apps/appsManager";
import { App } from "../system/configs/app";
import { Vector2 } from "../math/vector2";
import { SystemManager } from "../system/systemManager";
import { TrackingManager } from "../tracking/trackingManager";
@ -36,7 +35,8 @@ export class WindowsManager {
}
open(appId: string, options?: WindowOptions | null): object | null {
const app = AppsManager.getAppById(appId);
const { appsConfig, windowsConfig, taskbarConfig } = this.#systemManager;
const app = appsConfig.getAppById(appId);
if (app == null) {
console.warn(`Failed to open app ${appId}: app not found`);
@ -44,8 +44,7 @@ export class WindowsManager {
}
const size = options?.size ?? app.windowOptions?.size ?? new Vector2(700, 400);
const { windowsConfig, taskbarConfig } = this.#systemManager;
const availableScreenSpace = new Vector2(
window.innerWidth - windowsConfig.screenMargin * 2,
window.innerHeight - windowsConfig.screenMargin * 2 - taskbarConfig.height
@ -112,7 +111,8 @@ export class WindowsManager {
openFile(file: VirtualFile, options: object = {}): object | null {
if (file.extension == null) return null;
const app = AppsManager.getAppByFileExtension(file?.extension);
const { appsConfig } = this.#systemManager;
const app = appsConfig.getAppByFileExtension(file?.extension);
if (app != null) {
return this.open(app.id, { file, ...options });
} else {

View file

@ -1,5 +1,5 @@
import { useEffect, useState } from "react";
import { App } from "../../features/apps/app";
import { App } from "../../features/system/configs/app";
import { VirtualFolder } from "../../features/virtual-drive/folder";
import { useVirtualRoot } from "../virtual-drive/virtualRootContext";

View file

@ -12,6 +12,7 @@ interface SystemManagerProviderProps {
taskbarConfig: SystemManagerParams["taskbarConfig"];
trackingConfig: SystemManagerParams["trackingConfig"];
windowsConfig: SystemManagerParams["windowsConfig"];
virtualDriveConfig: SystemManagerParams["virtualDriveConfig"];
children: ReactNode;
}

View file

@ -1,6 +1,5 @@
export * from "./components";
export * from "./features";
export * from "./styles";
export * from "./components";
export * from "./hooks";
export { default as utilStyles } from "./src/styles/utils.module.css";
export { DIALOG_CONTENT_TYPES } from "./constants/modals.const";
export { CODE_EXTENSIONS } from "./constants/virtualDrive.const";
export * from "./constants";

View file

@ -0,0 +1,3 @@
import "./global.css";
export { default as utilStyles } from "./utils.module.css";

View file

@ -1,4 +1,4 @@
import { ANSI } from "../../src/config/apps/terminal.config";
import { ANSI } from "@prozilla-os/core";
export type status = "error" | "info" | "file" | "success" | "start";

View file

@ -1,6 +1,6 @@
import fs from "node:fs";
import { ANSI } from "../src/config/apps/terminal.config";
import { REPO } from "../src/config/deploy.config";
import { ANSI } from "@prozilla-os/core";
const API_URL = "https://api.github.com/";
const TREE_PATH = "public/config/tree.json";

View file

@ -2,13 +2,20 @@ import { ReactElement } from "react";
import { Router } from "./router/Router";
import { ProzillaOS } from "@prozilla-os/core";
import { desktopConfig } from "./config/desktop.config";
import { NAME, TAG_LINE } from "./config/branding.config";
import { appsConfig } from "./config/apps.config";
import { virtualDriveConfig } from "./config/virtualDrive.config";
export function Main(): ReactElement {
return <ProzillaOS
systemName={NAME}
tagLine={TAG_LINE}
config={{
desktop: desktopConfig
desktop: desktopConfig,
apps: appsConfig,
virtualDrive: virtualDriveConfig,
}}
>
<Router/>
</ProzillaOS>;
};
}

View file

@ -0,0 +1,8 @@
import { AppsConfig } from "@prozilla-os/core";
import { fileExplorer } from "@prozilla-os/file-explorer";
export const appsConfig = new AppsConfig({
apps: [
fileExplorer.setName("Files"),
],
});

View file

@ -1,4 +1,4 @@
import { ANSI } from "./apps/terminal.config";
import { ANSI } from "@prozilla-os/core";
export const NAME = "ProzillaOS";
export const TAG_LINE = "Web-based Operating System";

View file

@ -0,0 +1,12 @@
import { VirtualDriveConfig } from "@prozilla-os/core";
export const virtualDriveConfig = new VirtualDriveConfig({
fileIcon: "/assets/apps/file-explorer/icons/file.svg",
infoFileIcon: "/assets/apps/file-explorer/icons/file-info.svg",
textFileIcon: "/assets/apps/file-explorer/icons/file-text.svg",
codeFileIcon: "/assets/apps/file-explorer/icons/file-code.svg",
folderIcon: "/assets/apps/file-explorer/icons/folder.svg",
imagesFolderIcon: "/assets/apps/file-explorer/icons/folder-images.svg",
textFolderIcon: "/assets/apps/file-explorer/icons/folder-text.svg",
folderLinkIcon: "/assets/apps/file-explorer/icons/folder-link.svg",
});

View file

@ -1,6 +1,5 @@
import React from "react";
import ReactDOM from "react-dom/client";
import "./styles/global.css";
import { Main } from "./Main";
import { ASCII_LOGO, NAME } from "./config/branding.config";

View file

@ -2,14 +2,16 @@ import { BrowserRouter, Route, Routes } from "react-router-dom";
import { DefaultRoute } from "./routes/DefaultRoute";
import { StandaloneRoute } from "./routes/StandaloneRoute";
import { NoRoute } from "./routes/NoRoute";
import { App, AppsManager } from "@prozilla-os/core";
import { useSystemManager } from "@prozilla-os/core";
export function Router() {
const { appsConfig } = useSystemManager();
return <BrowserRouter>
<Routes>
<Route path="/">
<Route index element={<DefaultRoute/>}/>
{AppsManager.APPS.map((app: App) =>
{appsConfig.apps.map((app) =>
<Route path={app.id} key={app.id} element={<StandaloneRoute app={app}/>}/>
)}
<Route path="*" element={<NoRoute/>}/>

View file

@ -1,8 +1,8 @@
import { useCallback, useEffect, useState } from "react";
import styles from "./StandaloneRoute.module.css";
import { NAME } from "../../config/branding.config";
import { App, AppsManager, generateUrl, getViewportParams, ModalsView, openUrl, setViewportIcon, setViewportTitle } from "@prozilla-os/core";
import { StandaloneHeader } from "../../components/standalone-header/StandaloneHeader";
import { App, generateUrl, getViewportParams, ModalsView, openUrl, setViewportIcon, setViewportTitle } from "@prozilla-os/core";
import { StandaloneHeader } from "../../components/standalone/StandaloneHeader";
interface StandaloneRouteProps {
app: App;
@ -10,7 +10,7 @@ interface StandaloneRouteProps {
export function StandaloneRoute({ app }: StandaloneRouteProps) {
const [title, setTitle] = useState(app.name);
const [iconUrl, setIconUrl] = useState(AppsManager.getAppIconUrl(app.id));
const [iconUrl, setIconUrl] = useState(app.iconUrl ?? "");
useEffect(() => {
// TO DO: don't overwrite metadata in production

View file

@ -29,7 +29,7 @@
"include": [
"src/**/*",
"vite.config.ts",
"scripts/**/*",
// "scripts/**/*", // TO DO: uncomment this line
],
"exclude": [
"./node_modules",