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 tseslint from "typescript-eslint";
import react from "eslint-plugin-react"; import react from "eslint-plugin-react";
import reactRefresh from "eslint-plugin-react-refresh"; import reactRefresh from "eslint-plugin-react-refresh";
import * as pluginImport from "eslint-plugin-import";
export default tseslint.config( export default tseslint.config(
eslint.configs.recommended, eslint.configs.recommended,
@ -22,6 +23,9 @@ export default tseslint.config(
plugins: { plugins: {
react, react,
"react-refresh": reactRefresh, "react-refresh": reactRefresh,
import: {
rules: pluginImport.rules
}
}, },
rules: { rules: {
"quotes": "off", "quotes": "off",
@ -66,6 +70,7 @@ export default tseslint.config(
"warn", "warn",
{ "allowConstantExport": true } { "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/free-solid-svg-icons": "^6.4.0",
"@fortawesome/react-fontawesome": "^0.2.0", "@fortawesome/react-fontawesome": "^0.2.0",
"@prozilla-os/core": "^1.0.0", "@prozilla-os/core": "^1.0.0",
"@prozilla-os/file-explorer": "^1.0.0",
"@vitejs/plugin-react-swc": "^3.7.0", "@vitejs/plugin-react-swc": "^3.7.0",
"anser": "^2.1.1", "anser": "^2.1.1",
"escape-carriage": "^1.3.1", "escape-carriage": "^1.3.1",
@ -42,6 +43,7 @@
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"@types/react-syntax-highlighter": "^15.5.13", "@types/react-syntax-highlighter": "^15.5.13",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-react": "^7.34.1", "eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-refresh": "^0.4.7", "eslint-plugin-react-refresh": "^0.4.7",
"gh-pages": "^5.0.0", "gh-pages": "^5.0.0",
@ -1769,6 +1771,12 @@
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"dev": true "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": { "node_modules/@types/minimist": {
"version": "1.2.5", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz",
@ -2238,6 +2246,26 @@
"url": "https://github.com/sponsors/ljharb" "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": { "node_modules/array.prototype.flat": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz",
@ -3237,6 +3265,113 @@
"url": "https://opencollective.com/eslint" "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": { "node_modules/eslint-plugin-react": {
"version": "7.34.1", "version": "7.34.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz",
@ -5140,6 +5275,15 @@
"node": "*" "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": { "node_modules/minimist-options": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz",
@ -5312,6 +5456,20 @@
"url": "https://github.com/sponsors/ljharb" "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": { "node_modules/object.hasown": {
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", "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", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
"dev": true, "dev": true,
"optional": true,
"peer": true,
"dependencies": { "dependencies": {
"is-core-module": "^2.13.0", "is-core-module": "^2.13.0",
"path-parse": "^1.0.7", "path-parse": "^1.0.7",
@ -6662,6 +6818,15 @@
"node": ">=8" "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": { "node_modules/strip-indent": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
@ -7006,6 +7171,30 @@
"typescript": ">=4.2.0" "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": { "node_modules/tslib": {
"version": "2.6.3", "version": "2.6.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",

View file

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

View file

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

View file

@ -11,7 +11,11 @@
"scripts": { "scripts": {
"test": "tsc --watch" "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", "license": "MIT",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.5.2", "@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 { faArrowUp, faCaretLeft, faCaretRight, faCircleInfo, faCog, faDesktop, faFileLines, faHouse, faImage, faPlus, faSearch, faTrash } from "@fortawesome/free-solid-svg-icons";
import { QuickAccessButton } from "./QuickAccessButton"; import { QuickAccessButton } from "./QuickAccessButton";
import { ImportButton } from "./ImportButton"; 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 { SELECTOR_MODE } from "../constants/fileExplorer.const";
import { FileProperties } from "./modals/file-properties/FileProperties"; import { FileProperties } from "./modals/file-properties/FileProperties";
interface FileExplorerProps extends WindowProps { export interface FileExplorerProps extends WindowProps {
path?: string; path?: string;
selectorMode?: number; selectorMode?: number;
Footer: FC; Footer: FC;
@ -109,7 +109,7 @@ export function FileExplorer({ app, path: startPath, selectorMode, Footer, onSel
alert({ alert({
title: (error as Error).message, 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.", 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), size: new Vector2(300, 200),
single: true, single: true,
}); });
@ -138,7 +138,7 @@ export function FileExplorer({ app, path: startPath, selectorMode, Footer, onSel
if (directory == null) { if (directory == null) {
openWindowedModal({ openWindowedModal({
title: "Error", title: "Error",
iconUrl: app?.id != null ? AppsManager.getAppIconUrl(app.id) : undefined, iconUrl: app?.iconUrl as string | undefined,
size: new Vector2(300, 150), size: new Vector2(300, 150),
Modal: (props: {}) => Modal: (props: {}) =>
<DialogBox {...props}> <DialogBox {...props}>

View file

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

View file

@ -1,2 +1,10 @@
export { SELECTOR_MODE } from "./constants/fileExplorer.const"; import { App } from "@prozilla-os/core";
export { FileExplorer } from "./components/FileExplorer"; 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": { "scripts": {
"test": "tsc --watch" "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", "license": "MIT",
"dependencies": { "dependencies": {
"@prozilla-os/file-explorer": "^1.0.0",
"@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-regular-svg-icons": "^6.5.2", "@fortawesome/free-regular-svg-icons": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/free-solid-svg-icons": "^6.5.2",
@ -22,8 +27,7 @@
"react-draggable": "^4.4.6", "react-draggable": "^4.4.6",
"react-error-boundary": "^4.0.13", "react-error-boundary": "^4.0.13",
"react-ga4": "^2.1.0", "react-ga4": "^2.1.0",
"react-svg": "^16.1.34", "react-svg": "^16.1.34"
"@prozilla-os/file-explorer": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.14.2", "@types/node": "^20.14.2",
@ -31,5 +35,9 @@
}, },
"files": [ "files": [
"src/index.ts" "src/index.ts"
] ],
"publishConfig": {
"registry": "https://npm.pkg.github.com",
"access": "public"
}
} }

View file

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

View file

@ -10,13 +10,13 @@ import { RadioAction } from "../actions/actions/RadioAction";
import { Divider } from "../actions/actions/Divider"; import { Divider } from "../actions/actions/Divider";
import { Share } from "../modals/share/Share"; import { Share } from "../modals/share/Share";
import { ModalProps } from "../modals/ModalView"; import { ModalProps } from "../modals/ModalView";
import { APP_NAMES, APP_ICONS, APPS } from "../../constants/apps.const";
import { SettingsManager, reloadViewport, ModalsManager, Vector2, isValidInteger } from "../../features"; import { SettingsManager, reloadViewport, ModalsManager, Vector2, isValidInteger } from "../../features";
import { VirtualFile } from "../../features/virtual-drive/file"; import { VirtualFile } from "../../features/virtual-drive/file";
import { VirtualFolder, VirtualFolderLink } from "../../features/virtual-drive/folder"; import { VirtualFolder, VirtualFolderLink } from "../../features/virtual-drive/folder";
import { useSettingsManager, useWindowsManager, useVirtualRoot, useWindowedModal, useContextMenu, useSystemManager } from "../../hooks"; import { useSettingsManager, useWindowsManager, useVirtualRoot, useWindowedModal, useContextMenu, useSystemManager } from "../../hooks";
import { DirectoryList } from "../_utils"; import { DirectoryList } from "../_utils";
import { FileEventHandler, FolderEventHandler } from "../_utils/directory-list/DirectoryList"; import { FileEventHandler, FolderEventHandler } from "../_utils/directory-list/DirectoryList";
import { fileExplorer } from "@prozilla-os/file-explorer";
export const Desktop = memo(() => { export const Desktop = memo(() => {
const { desktopConfig } = useSystemManager(); const { desktopConfig } = useSystemManager();
@ -83,12 +83,12 @@ export const Desktop = memo(() => {
windowsManager?.open("settings", { tab: 2 }); windowsManager?.open("settings", { tab: 2 });
}}/> }}/>
<Divider/> <Divider/>
<ClickAction label={`Open in ${APP_NAMES.FILE_EXPLORER}`} icon={APP_ICONS.FILE_EXPLORER} onTrigger={() => { <ClickAction label={`Open in ${fileExplorer.name}`} icon={fileExplorer.iconUrl as string | undefined} onTrigger={() => {
windowsManager?.open(APPS.FILE_EXPLORER, { path: directory?.path }); 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 }); windowsManager?.open(APPS.TERMINAL, { path: directory?.path });
}}/> }}/> */}
<Divider/> <Divider/>
<ClickAction label={"Share"} icon={ModalsManager.getModalIconUrl("share")} onTrigger={() => { <ClickAction label={"Share"} icon={ModalsManager.getModalIconUrl("share")} onTrigger={() => {
openWindowedModal({ openWindowedModal({
@ -103,7 +103,7 @@ export const Desktop = memo(() => {
<ClickAction label="Open" onTrigger={(event, file) => { <ClickAction label="Open" onTrigger={(event, file) => {
if (windowsManager != null) (file as VirtualFile).open(windowsManager); 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); if (windowsManager != null) (file as VirtualFile).parent?.open(windowsManager);
}}/> }}/>
<ClickAction label="Delete" icon={faTrash} onTrigger={(event, file) => { <ClickAction label="Delete" icon={faTrash} onTrigger={(event, file) => {
@ -116,10 +116,10 @@ export const Desktop = memo(() => {
<ClickAction label="Open" onTrigger={(event, folder) => { <ClickAction label="Open" onTrigger={(event, folder) => {
if (windowsManager != null) (folder as VirtualFolder).open(windowsManager); 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 }); 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); if (windowsManager != null) (folder as VirtualFolder).parent?.open(windowsManager);
}}/> }}/>
<Divider/> <Divider/>
@ -183,7 +183,7 @@ export const Desktop = memo(() => {
windowsManager?.openFile(file, options); windowsManager?.openFile(file, options);
}} }}
onOpenFolder={(event, folder) => { onOpenFolder={(event, folder) => {
windowsManager?.open(APPS.FILE_EXPLORER, { windowsManager?.open(fileExplorer.id, {
path: (folder as VirtualFolderLink).linkedPath ?? folder.path 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 { WindowedModal } from "../_utils/WindowedModal";
import styles from "./FileSelector.module.css"; import styles from "./FileSelector.module.css";
import { ModalProps } from "../ModalView"; import { ModalProps } from "../ModalView";
import { APP_ICONS } from "../../../constants/apps.const";
import { VirtualFile } from "../../../features/virtual-drive/file"; import { VirtualFile } from "../../../features/virtual-drive/file";
import { VirtualFolder } from "../../../features/virtual-drive/folder"; 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 { interface FileSelectorProps extends ModalProps {
type: number; type: FileSelectorMode;
allowedFormats?: string[]; allowedFormats?: string[];
onFinish: (result: VirtualFile | VirtualFile[]) => void; onFinish: (result: VirtualFile | VirtualFile[]) => void;
} }
export function FileSelector({ modal, params, type, allowedFormats, onFinish, ...props }: FileSelectorProps) { 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 [selection, setSelection] = useState<string[] | null>(multi ? [] : null);
const [directory, setDirectory] = useState<VirtualFolder | null>(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={{ return <WindowedModal modal={modal} params={{
title: multi ? "Select files" : "Select a file", title: multi ? "Select files" : "Select a file",
iconUrl: APP_ICONS.FILE_EXPLORER, iconUrl: fileExplorer.iconUrl as string | undefined,
...params, ...params,
}} {...props}> }} {...props}>
<FileExplorer <fileExplorer.WindowContent
selectorMode={type} selectorMode={type}
Footer={() => Footer={() =>
<div className={styles.Footer}> <div className={styles.Footer}>

View file

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

View file

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

View file

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

View file

@ -1,12 +1,11 @@
import { FC, memo, MouseEvent } from "react"; 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 styles from "./AppIcon.module.css";
import { ReactSVG } from "react-svg"; import { ReactSVG } from "react-svg";
import { useSettingsManager } from "../../../hooks/settings/settingsManagerContext"; import { useSettingsManager } from "../../../hooks/settings/settingsManagerContext";
import { useContextMenu } from "../../../hooks/modals/contextMenu"; import { useContextMenu } from "../../../hooks/modals/contextMenu";
import { Actions } from "../../actions/Actions"; import { Actions } from "../../actions/Actions";
import { ClickAction } from "../../actions/actions/ClickAction"; import { ClickAction } from "../../actions/actions/ClickAction";
import { AppsManager } from "../../../features/apps/appsManager";
import { WindowsManager } from "../../../features/windows/windowsManager"; import { WindowsManager } from "../../../features/windows/windowsManager";
interface AppButtonProps { interface AppButtonProps {
@ -20,7 +19,7 @@ export const AppButton: FC<AppButtonProps> = memo(({ app, windowsManager, active
const settingsManager = useSettingsManager(); const settingsManager = useSettingsManager();
const { onContextMenu } = useContextMenu({ Actions: (props) => const { onContextMenu } = useContextMenu({ Actions: (props) =>
<Actions avoidTaskbar={false} {...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); windowsManager?.open(app.id);
}}/> }}/>
{/* <ClickAction label={isPinned ? "Unpin from taskbar" : "Pin to taskbar"} icon={faThumbTack} onTrigger={() => { {/* <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 { ReactSVG } from "react-svg";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { utilStyles } from "../../.."; import { utilStyles } from "../../..";
import { APPS } from "../../../constants/apps.const"; import { closeViewport, Vector2 } from "../../../features";
import { closeViewport, Vector2, AppsManager } from "../../../features";
import { useWindowsManager, useVirtualRoot, useKeyboardListener, useSystemManager } from "../../../hooks"; import { useWindowsManager, useVirtualRoot, useKeyboardListener, useSystemManager } from "../../../hooks";
import { fileExplorer } from "@prozilla-os/file-explorer";
interface HomeMenuProps { interface HomeMenuProps {
active: boolean; active: boolean;
@ -17,7 +17,7 @@ interface HomeMenuProps {
} }
export function HomeMenu({ active, setActive, search }: HomeMenuProps) { export function HomeMenu({ active, setActive, search }: HomeMenuProps) {
const { systemName } = useSystemManager(); const { systemName, appsConfig } = useSystemManager();
const windowsManager = useWindowsManager(); const windowsManager = useWindowsManager();
const virtualRoot = useVirtualRoot(); const virtualRoot = useVirtualRoot();
const [tabIndex, setTabIndex] = useState(active ? 0 : -1); const [tabIndex, setTabIndex] = useState(active ? 0 : -1);
@ -81,13 +81,13 @@ export function HomeMenu({ active, setActive, search }: HomeMenuProps) {
</button> </button>
<button title="Images" tabIndex={tabIndex} onClick={() => { <button title="Images" tabIndex={tabIndex} onClick={() => {
setActive(false); setActive(false);
windowsManager?.open(APPS.FILE_EXPLORER, { path: "~/Pictures" }); windowsManager?.open(fileExplorer.id, { path: "~/Pictures" });
}}> }}>
<FontAwesomeIcon icon={faImage}/> <FontAwesomeIcon icon={faImage}/>
</button> </button>
<button title="Documents" tabIndex={tabIndex} onClick={() => { <button title="Documents" tabIndex={tabIndex} onClick={() => {
setActive(false); setActive(false);
windowsManager?.open(APPS.FILE_EXPLORER, { path: "~/Documents" }); } windowsManager?.open(fileExplorer.id, { path: "~/Documents" }); }
}> }>
<FontAwesomeIcon icon={faFileLines}/> <FontAwesomeIcon icon={faFileLines}/>
</button> </button>
@ -95,7 +95,7 @@ export function HomeMenu({ active, setActive, search }: HomeMenuProps) {
<div className={styles.Apps}> <div className={styles.Apps}>
<h1 className={utilStyles.TextBold}>{systemName}</h1> <h1 className={utilStyles.TextBold}>{systemName}</h1>
<div className={appStyles.AppList}> <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 <button
key={id} key={id}
className={appStyles.AppButton} className={appStyles.AppButton}
@ -106,7 +106,7 @@ export function HomeMenu({ active, setActive, search }: HomeMenuProps) {
}} }}
title={name} title={name}
> >
<ReactSVG src={AppsManager.getAppIconUrl(id)}/> <ReactSVG src={iconUrl ?? ""}/>
<h2 className={utilStyles.TextRegular}>{name}</h2> <h2 className={utilStyles.TextRegular}>{name}</h2>
</button> </button>
)} )}

View file

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

View file

@ -1,8 +1,8 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useAlert } from "../../hooks/modals/alert"; import { useAlert } from "../../hooks/modals/alert";
import { AppsManager } from "../../features/apps/appsManager";
import { Vector2 } from "../../features/math/vector2"; 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 { export interface WindowFallbackViewProps {
error?: Error; 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 // 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 { export function WindowFallbackView({ error, resetErrorBoundary: _resetErrorBoundary, app, closeWindow }: WindowFallbackViewProps): undefined {
const { appsConfig } = useSystemManager();
const { alert } = useAlert(); const { alert } = useAlert();
const [alerted, setAlerted] = useState(false); const [alerted, setAlerted] = useState(false);
@ -27,7 +28,7 @@ export function WindowFallbackView({ error, resetErrorBoundary: _resetErrorBound
alert({ alert({
title: `${app.name} has stopped working`, title: `${app.name} has stopped working`,
text: `${error.name}: ${error.message}`, text: `${error.name}: ${error.message}`,
iconUrl: AppsManager.getAppIconUrl(app.id), iconUrl: appsConfig.getAppById(app.id)?.iconUrl as string | undefined,
size: new Vector2(350, 150), size: new Vector2(350, 150),
single: true single: true
}); });

View file

@ -9,7 +9,6 @@ import { Vector2 } from "../../features/math/vector2";
import { faWindowMaximize } from "@fortawesome/free-regular-svg-icons"; import { faWindowMaximize } from "@fortawesome/free-regular-svg-icons";
import utilStyles from "../../styles/utils.module.css"; import utilStyles from "../../styles/utils.module.css";
import { useContextMenu } from "../../hooks/modals/contextMenu"; import { useContextMenu } from "../../hooks/modals/contextMenu";
import { AppsManager } from "../../features/apps/appsManager";
import { ClickAction } from "../actions/actions/ClickAction"; import { ClickAction } from "../actions/actions/ClickAction";
import { Actions } from "../actions/Actions"; import { Actions } from "../actions/Actions";
import { useScreenDimensions } from "../../hooks/_utils/screen"; 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 }) => { 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 windowsManager = useWindowsManager();
const nodeRef = useRef(null); const nodeRef = useRef(null);
const { openWindowedModal } = useWindowedModal(); 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 [maximized, setMaximized] = useState(fullscreen ?? false);
const [screenWidth, screenHeight] = useScreenDimensions(); const [screenWidth, screenHeight] = useScreenDimensions();
const [title, setTitle] = useState(app?.name ?? ""); 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 zIndex = useZIndex({ groupIndex: ZIndexManager.GROUPS.WINDOWS, index: index ?? 0 });
const { onContextMenu, ShortcutsListener } = useContextMenu({ Actions: (props) => 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 { ModalsManager } from "./modals/modalsManager";
export { SettingsManager } from "./settings/settingsManager"; export { SettingsManager } from "./settings/settingsManager";
export { StorageManager } from "./storage/storageManager"; export { StorageManager } from "./storage/storageManager";
export { TrackingManager } from "./tracking/trackingManager"; export { TrackingManager } from "./tracking/trackingManager";
export { WindowsManager } from "./windows/windowsManager"; export { WindowsManager } from "./windows/windowsManager";
export { ZIndexManager } from "./z-index/zIndexManager"; 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"; 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 ".";
import { App } from "../../apps"; import { OptionalInterface } from "../../../types/util";
export interface AppsConfigOptions { export interface AppsConfigOptions {
apps: App[]; // eslint-disable-next-line @typescript-eslint/no-explicit-any
apps: App<any>[];
} }
export class AppsConfig { export class AppsConfig {
apps: AppsConfigOptions["apps"]; apps: AppsConfigOptions["apps"] = [];
constructor(options?: OptionalInterface<AppsConfigOptions>) { constructor(options: OptionalInterface<AppsConfigOptions> = {}) {
const { apps } = options as 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 { export interface DesktopConfigOptions {
/** Array of URLs of wallpaper images */ /** Array of URLs of wallpaper images */
@ -25,7 +25,7 @@ export class DesktopConfig {
defaultIconSize: DesktopConfigOptions["defaultIconSize"]; defaultIconSize: DesktopConfigOptions["defaultIconSize"];
defaultIconDirection: DesktopConfigOptions["defaultIconDirection"]; defaultIconDirection: DesktopConfigOptions["defaultIconDirection"];
constructor(options?: OptionalInterface<DesktopConfigOptions>) { constructor(options: OptionalInterface<DesktopConfigOptions> = {}) {
const { wallpapers, defaultWallpaper, defaultIconSize, defaultIconDirection } = options as DesktopConfigOptions; const { wallpapers, defaultWallpaper, defaultIconSize, defaultIconDirection } = options as DesktopConfigOptions;
this.wallpapers = wallpapers ?? [ this.wallpapers = wallpapers ?? [

View file

@ -1,7 +1,9 @@
export { App } from "./app";
export { AppsConfig } from "./appsConfig"; export { AppsConfig } from "./appsConfig";
export { DesktopConfig } from "./desktopConfig"; export { DesktopConfig } from "./desktopConfig";
export { MiscConfig } from "./miscConfig"; export { MiscConfig } from "./miscConfig";
export { ModalsConfig } from "./modalsConfig"; export { ModalsConfig } from "./modalsConfig";
export { TaskbarConfig } from "./taskbarConfig"; export { TaskbarConfig } from "./taskbarConfig";
export { TrackingConfig } from "./trackingConfig"; 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 { export interface MiscConfigOptions {
/** /**
@ -11,8 +11,9 @@ export interface MiscConfigOptions {
export class MiscConfig { export class MiscConfig {
doubleClickDelay: MiscConfigOptions["doubleClickDelay"]; doubleClickDelay: MiscConfigOptions["doubleClickDelay"];
constructor(options?: OptionalInterface<MiscConfigOptions>) { constructor(options: OptionalInterface<MiscConfigOptions> = {}) {
const { doubleClickDelay } = options as MiscConfigOptions; const { doubleClickDelay } = options as MiscConfigOptions;
this.doubleClickDelay = doubleClickDelay ?? 250; 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"; import { Vector2 } from "../../math/vector2";
export interface ModalsConfigOptions { export interface ModalsConfigOptions {
@ -19,8 +19,9 @@ export class ModalsConfig {
defaultDialogSize: ModalsConfigOptions["defaultDialogSize"]; defaultDialogSize: ModalsConfigOptions["defaultDialogSize"];
defaultFileSelectorSize: ModalsConfigOptions["defaultFileSelectorSize"]; defaultFileSelectorSize: ModalsConfigOptions["defaultFileSelectorSize"];
constructor(options?: OptionalInterface<ModalsConfigOptions>) { constructor(options: OptionalInterface<ModalsConfigOptions> = {}) {
const { defaultDialogSize, defaultFileSelectorSize } = options as ModalsConfigOptions; const { defaultDialogSize, defaultFileSelectorSize } = options as ModalsConfigOptions;
this.defaultDialogSize = defaultDialogSize ?? new Vector2(400, 200); this.defaultDialogSize = defaultDialogSize ?? new Vector2(400, 200);
this.defaultFileSelectorSize = defaultFileSelectorSize ?? new Vector2(700, 400); 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 { export interface TaskbarConfigOptions {
/** /**
@ -11,8 +11,9 @@ export interface TaskbarConfigOptions {
export class TaskbarConfig { export class TaskbarConfig {
height: TaskbarConfigOptions["height"]; height: TaskbarConfigOptions["height"];
constructor(options?: OptionalInterface<TaskbarConfigOptions>) { constructor(options: OptionalInterface<TaskbarConfigOptions> = {}) {
const { height } = options as TaskbarConfigOptions; const { height } = options as TaskbarConfigOptions;
this.height = height ?? 3 * 16; this.height = height ?? 3 * 16;
} }
} }

View file

@ -1,4 +1,4 @@
import { OptionalInterface } from "../../../types/global"; import { OptionalInterface } from "../../../types/util";
export interface TrackingConfigOptions { export interface TrackingConfigOptions {
/** /**
@ -15,8 +15,9 @@ export class TrackingConfig {
enabled: TrackingConfigOptions["enabled"]; enabled: TrackingConfigOptions["enabled"];
GAMeasurementId: TrackingConfigOptions["GAMeasurementId"]; GAMeasurementId: TrackingConfigOptions["GAMeasurementId"];
constructor(options?: OptionalInterface<TrackingConfigOptions>) { constructor(options: OptionalInterface<TrackingConfigOptions> = {}) {
const { enabled, GAMeasurementId } = options as TrackingConfigOptions; const { enabled, GAMeasurementId } = options as TrackingConfigOptions;
this.enabled = enabled ?? true; this.enabled = enabled ?? true;
this.GAMeasurementId = GAMeasurementId ?? "G-ZFQRR9DP3C"; 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"; import { Vector2 } from "../../math/vector2";
export interface WindowsConfigOptions { export interface WindowsConfigOptions {
@ -24,8 +24,9 @@ export class WindowsConfig {
titleSeparator: WindowsConfigOptions["titleSeparator"]; titleSeparator: WindowsConfigOptions["titleSeparator"];
minScreenSize: WindowsConfigOptions["minScreenSize"]; minScreenSize: WindowsConfigOptions["minScreenSize"];
constructor(options?: OptionalInterface<WindowsConfigOptions>) { constructor(options: OptionalInterface<WindowsConfigOptions> = {}) {
const { screenMargin, titleSeparator, minScreenSize } = options as WindowsConfigOptions; const { screenMargin, titleSeparator, minScreenSize } = options as WindowsConfigOptions;
this.screenMargin = screenMargin ?? 32; this.screenMargin = screenMargin ?? 32;
this.titleSeparator = titleSeparator ?? "-"; this.titleSeparator = titleSeparator ?? "-";
this.minScreenSize = minScreenSize ?? new Vector2(350, 350); this.minScreenSize = minScreenSize ?? new Vector2(350, 350);

View file

@ -1,4 +1,5 @@
import { formatRelativeTime } from "../_utils"; import { formatRelativeTime } from "../_utils";
import { VirtualDriveConfig } from "./configs";
import { AppsConfig } from "./configs/appsConfig"; import { AppsConfig } from "./configs/appsConfig";
import { DesktopConfig } from "./configs/desktopConfig"; import { DesktopConfig } from "./configs/desktopConfig";
import { MiscConfig } from "./configs/miscConfig"; import { MiscConfig } from "./configs/miscConfig";
@ -17,6 +18,7 @@ export interface SystemManagerParams {
taskbarConfig: TaskbarConfig; taskbarConfig: TaskbarConfig;
trackingConfig: TrackingConfig; trackingConfig: TrackingConfig;
windowsConfig: WindowsConfig; windowsConfig: WindowsConfig;
virtualDriveConfig: VirtualDriveConfig;
}; };
export class SystemManager { export class SystemManager {
@ -31,6 +33,7 @@ export class SystemManager {
taskbarConfig: TaskbarConfig; taskbarConfig: TaskbarConfig;
trackingConfig: TrackingConfig; trackingConfig: TrackingConfig;
windowsConfig: WindowsConfig; windowsConfig: WindowsConfig;
virtualDriveConfig: VirtualDriveConfig;
constructor({ constructor({
systemName, systemName,
@ -41,7 +44,8 @@ export class SystemManager {
modalsConfig, modalsConfig,
taskbarConfig, taskbarConfig,
trackingConfig, trackingConfig,
windowsConfig windowsConfig,
virtualDriveConfig
}: SystemManagerParams) { }: SystemManagerParams) {
this.systemName = systemName ?? "ProzillaOS"; this.systemName = systemName ?? "ProzillaOS";
this.tagLine = tagLine ?? "Web-based Operating System"; this.tagLine = tagLine ?? "Web-based Operating System";
@ -53,6 +57,7 @@ export class SystemManager {
this.taskbarConfig = taskbarConfig; this.taskbarConfig = taskbarConfig;
this.trackingConfig = trackingConfig; this.trackingConfig = trackingConfig;
this.windowsConfig = windowsConfig; this.windowsConfig = windowsConfig;
this.virtualDriveConfig = virtualDriveConfig;
this.#startDate = new Date(); 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 { IMAGE_FORMATS } from "../../../../../apps/media-viewer/config/mediaViewer.config";
import { AppsManager } from "../../apps/appsManager";
import { WindowsManager } from "../../windows/windowsManager"; import { WindowsManager } from "../../windows/windowsManager";
import { VirtualBase, VirtualBaseJson } from "../virtualBase"; import { VirtualBase, VirtualBaseJson } from "../virtualBase";
@ -107,7 +105,7 @@ export class VirtualFile extends VirtualBase {
return await fetch(this.source).then((response) => return await fetch(this.source).then((response) =>
response.text() response.text()
).catch((error) => { ).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; return null;
}) as string; }) as string;
} }
@ -125,10 +123,12 @@ export class VirtualFile extends VirtualBase {
if (this.source != null && this.extension != null && IMAGE_FORMATS.includes(this.extension)) if (this.source != null && this.extension != null && IMAGE_FORMATS.includes(this.extension))
return this.source; return this.source;
const { virtualDriveConfig } = this.getRoot().systemManager;
switch (this.extension) { switch (this.extension) {
case "txt": case "txt":
case "md": case "md":
iconUrl = AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "file-text"); iconUrl = virtualDriveConfig.textFileIcon;
break; break;
case "xml": case "xml":
case "js": case "js":
@ -139,10 +139,10 @@ export class VirtualFile extends VirtualBase {
case "css": case "css":
case "html": case "html":
case "yml": case "yml":
iconUrl = AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "file-code"); iconUrl = virtualDriveConfig.codeFileIcon;
break; break;
default: default:
iconUrl = AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "file"); iconUrl = virtualDriveConfig.fileIcon;
break; break;
} }

View file

@ -1,6 +1,4 @@
import { VirtualFile } from "."; import { VirtualFile } from ".";
import { APPS } from "../../../constants/apps.const";
import { AppsManager } from "../../apps/appsManager";
import { VirtualFileJson } from "./virtualFile"; import { VirtualFileJson } from "./virtualFile";
export interface VirtualFileLinkJson extends VirtualFileJson { export interface VirtualFileLinkJson extends VirtualFileJson {
@ -101,7 +99,9 @@ export class VirtualFileLink extends VirtualFile {
} }
getIconUrl(...args: Parameters<VirtualFile["getIconUrl"]>): ReturnType<VirtualFile["getIconUrl"]> { 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()) { if (this.isValid()) {
return this.iconUrl ?? this.linkedFile?.getIconUrl(...args) ?? defaultIconUrl; return this.iconUrl ?? this.linkedFile?.getIconUrl(...args) ?? defaultIconUrl;
} else { } 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 { removeFromArray } from "../../_utils/array.utils";
import { WindowsManager } from "../../windows/windowsManager"; import { WindowsManager } from "../../windows/windowsManager";
import { VirtualFileJson } from "../file/virtualFile"; import { VirtualFileJson } from "../file/virtualFile";
import { VirtualBase, VirtualBaseJson } from "../virtualBase"; import { VirtualBase, VirtualBaseJson } from "../virtualBase";
import { VirtualFolderLink } from "."; import { VirtualFolderLink } from ".";
import { VirtualFile, VirtualFileLink } from "../file"; import { VirtualFile, VirtualFileLink } from "../file";
import { fileExplorer } from "@prozilla-os/file-explorer";
export interface VirtualFolderJson extends VirtualBaseJson { export interface VirtualFolderJson extends VirtualBaseJson {
fls?: VirtualFileJson[]; fls?: VirtualFileJson[];
@ -289,7 +288,7 @@ export class VirtualFolder extends VirtualBase {
* Opens this folder in file explorer * Opens this folder in file explorer
*/ */
open(windowsManager: WindowsManager) { 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) if (this.iconUrl != null)
return this.iconUrl; return this.iconUrl;
return AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "folder"); const { virtualDriveConfig } = this.getRoot().systemManager;
return virtualDriveConfig.folderIcon;
} }
toJSON(): VirtualFolderJson | null { toJSON(): VirtualFolderJson | null {

View file

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

View file

@ -19,7 +19,7 @@ export class VirtualRoot extends VirtualFolder {
shortcuts: Record<string, VirtualFile | VirtualFileLink | VirtualFolder | VirtualFolderLink>; shortcuts: Record<string, VirtualFile | VirtualFileLink | VirtualFolder | VirtualFolderLink>;
initiated: boolean = false; initiated: boolean = false;
loadedDefaultData: boolean = false; loadedDefaultData: boolean = false;
#systemManager: SystemManager; systemManager: SystemManager;
static EVENT_NAMES = { static EVENT_NAMES = {
ERROR: "error" ERROR: "error"
@ -28,13 +28,13 @@ export class VirtualRoot extends VirtualFolder {
constructor(systemManager: SystemManager) { constructor(systemManager: SystemManager) {
super("root"); super("root");
this.root = this; this.root = this;
this.#systemManager = systemManager; this.systemManager = systemManager;
this.isRoot = true; this.isRoot = true;
this.shortcuts = {}; this.shortcuts = {};
} }
loadDefaultData() { loadDefaultData() {
loadDefaultData(this.#systemManager, this); loadDefaultData(this.systemManager, this);
} }
loadData() { 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 { EventEmitter, EventNamesMap } from "../_utils/event.utils";
import { VirtualRoot } from "./root/virtualRoot"; import { VirtualRoot } from "./root/virtualRoot";
import { VirtualFile } from "./file"; import { VirtualFile } from "./file";
@ -81,10 +79,12 @@ export class VirtualBase extends EventEmitter<EventNamesMap> {
} }
getIconUrl(): string { getIconUrl(): string {
return this.iconUrl if (this.iconUrl != null) return this.iconUrl;
?? this.linkedFile?.iconUrl if (this.linkedFile?.iconUrl != null) return this.linkedFile.iconUrl;
?? this.linkedFolder?.iconUrl if (this.linkedFolder?.iconUrl != null) return this.linkedFolder.iconUrl;
?? AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "file");
const { virtualDriveConfig } = this.getRoot().systemManager;
return virtualDriveConfig.fileIcon;
} }
getType(): string { getType(): string {

View file

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

View file

@ -1,5 +1,5 @@
import { useEffect, useState } from "react"; 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 { VirtualFolder } from "../../features/virtual-drive/folder";
import { useVirtualRoot } from "../virtual-drive/virtualRootContext"; import { useVirtualRoot } from "../virtual-drive/virtualRootContext";

View file

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

View file

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

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"; export type status = "error" | "info" | "file" | "success" | "start";

View file

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

View file

@ -2,13 +2,20 @@ import { ReactElement } from "react";
import { Router } from "./router/Router"; import { Router } from "./router/Router";
import { ProzillaOS } from "@prozilla-os/core"; import { ProzillaOS } from "@prozilla-os/core";
import { desktopConfig } from "./config/desktop.config"; 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 { export function Main(): ReactElement {
return <ProzillaOS return <ProzillaOS
systemName={NAME}
tagLine={TAG_LINE}
config={{ config={{
desktop: desktopConfig desktop: desktopConfig,
apps: appsConfig,
virtualDrive: virtualDriveConfig,
}} }}
> >
<Router/> <Router/>
</ProzillaOS>; </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 NAME = "ProzillaOS";
export const TAG_LINE = "Web-based Operating System"; 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 React from "react";
import ReactDOM from "react-dom/client"; import ReactDOM from "react-dom/client";
import "./styles/global.css";
import { Main } from "./Main"; import { Main } from "./Main";
import { ASCII_LOGO, NAME } from "./config/branding.config"; 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 { DefaultRoute } from "./routes/DefaultRoute";
import { StandaloneRoute } from "./routes/StandaloneRoute"; import { StandaloneRoute } from "./routes/StandaloneRoute";
import { NoRoute } from "./routes/NoRoute"; import { NoRoute } from "./routes/NoRoute";
import { App, AppsManager } from "@prozilla-os/core"; import { useSystemManager } from "@prozilla-os/core";
export function Router() { export function Router() {
const { appsConfig } = useSystemManager();
return <BrowserRouter> return <BrowserRouter>
<Routes> <Routes>
<Route path="/"> <Route path="/">
<Route index element={<DefaultRoute/>}/> <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={app.id} key={app.id} element={<StandaloneRoute app={app}/>}/>
)} )}
<Route path="*" element={<NoRoute/>}/> <Route path="*" element={<NoRoute/>}/>

View file

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

View file

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