Migration to TypeScript - Stage 2
This commit is contained in:
parent
f449e6cb68
commit
79f23694cd
174 changed files with 1625 additions and 2020 deletions
|
|
@ -60,7 +60,7 @@ See [docs/configuration](configuration/README.md) for more information.
|
|||
|
||||
Contains the static build files after [building](#building--deploying) the application. This directory is not included in the GitHub repository.
|
||||
|
||||
### Coding style
|
||||
### Code style
|
||||
|
||||
Type | Case | Example
|
||||
--- | --- | ---
|
||||
|
|
@ -75,6 +75,7 @@ Global constant variables | MACRO_CASE | ```export const NAME = "Prozilla OS";``
|
|||
Classes | PascalCase | ```class WindowsManager { }```
|
||||
React components | PascalCase | ```export function WindowsView({ }) { }```
|
||||
Functions | camelCase | ```function focusWindow() { }```
|
||||
Types | PascalCase | ```type FooBar = boolean;```
|
||||
|
||||
### External files
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ To see the status and to-do's of each feature, check the [task board](https://pr
|
|||
|
||||
## Table of Contents
|
||||
|
||||
- [Actions](storage/README.md)
|
||||
- [Actions](actions/README.md)
|
||||
- [Apps](apps/README.md)
|
||||
- [Modals](modals/README.md)
|
||||
- [Settings](settings/README.md)
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ The webview template can be used to turn a webpage into an application by simply
|
|||
```js
|
||||
// features/apps/apps.js
|
||||
|
||||
import { WebView } from "../../components/apps/templates/WebView.jsx";
|
||||
import { WebView } from "../../components/apps/templates/WebView";
|
||||
|
||||
export default class AppsManager {
|
||||
static APPLICATIONS = [
|
||||
|
|
@ -91,7 +91,7 @@ export function Example() {
|
|||
```js
|
||||
// features/apps/apps.js
|
||||
|
||||
import { Example } from "../../components/apps/example/Example.jsx";
|
||||
import { Example } from "../../components/apps/example/Example";
|
||||
|
||||
export default class AppsManager {
|
||||
static APPLICATIONS = [
|
||||
|
|
|
|||
126
package-lock.json
generated
126
package-lock.json
generated
|
|
@ -133,7 +133,7 @@
|
|||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/eslint-parser": {
|
||||
|
|
@ -166,7 +166,7 @@
|
|||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
|
|
@ -228,7 +228,7 @@
|
|||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-create-class-features-plugin": {
|
||||
|
|
@ -258,7 +258,7 @@
|
|||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-create-regexp-features-plugin": {
|
||||
|
|
@ -282,7 +282,7 @@
|
|||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-define-polyfill-provider": {
|
||||
|
|
@ -523,7 +523,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz",
|
||||
"integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==",
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
"parser": "bin/babel-parser"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
|
|
@ -1678,7 +1678,7 @@
|
|||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-shorthand-properties": {
|
||||
|
|
@ -1937,7 +1937,7 @@
|
|||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/preset-modules": {
|
||||
|
|
@ -2414,7 +2414,7 @@
|
|||
"argparse": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
"js-yaml": "bin/js-yaml"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/type-fest": {
|
||||
|
|
@ -3300,7 +3300,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz",
|
||||
"integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
|
|
@ -5806,7 +5806,7 @@
|
|||
"dependencies": {
|
||||
"browserslist": "^4.21.5",
|
||||
"caniuse-lite": "^1.0.30001464",
|
||||
"fraction.js": "^4.2.0",
|
||||
"fraction": "^4.2.0",
|
||||
"normalize-range": "^0.1.2",
|
||||
"picocolors": "^1.0.0",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
|
|
@ -6147,7 +6147,7 @@
|
|||
"node": ">= 8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/big.js": {
|
||||
"node_modules/big": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
|
||||
|
|
@ -6289,7 +6289,7 @@
|
|||
"update-browserslist-db": "^1.0.11"
|
||||
},
|
||||
"bin": {
|
||||
"browserslist": "cli.js"
|
||||
"browserslist": "cli"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||
|
|
@ -7329,7 +7329,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/decimal.js": {
|
||||
"node_modules/decimal": {
|
||||
"version": "10.4.3",
|
||||
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
|
||||
"integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA=="
|
||||
|
|
@ -7677,7 +7677,7 @@
|
|||
"jake": "^10.8.5"
|
||||
},
|
||||
"bin": {
|
||||
"ejs": "bin/cli.js"
|
||||
"ejs": "bin/cli"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
|
|
@ -7977,8 +7977,8 @@
|
|||
"esutils": "^2.0.2"
|
||||
},
|
||||
"bin": {
|
||||
"escodegen": "bin/escodegen.js",
|
||||
"esgenerate": "bin/esgenerate.js"
|
||||
"escodegen": "bin/escodegen",
|
||||
"esgenerate": "bin/esgenerate"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
|
|
@ -8041,7 +8041,7 @@
|
|||
"text-table": "^0.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"eslint": "bin/eslint.js"
|
||||
"eslint": "bin/eslint"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
|
|
@ -8296,7 +8296,7 @@
|
|||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-jsdoc": {
|
||||
|
|
@ -8368,7 +8368,7 @@
|
|||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-react": {
|
||||
|
|
@ -8445,7 +8445,7 @@
|
|||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-testing-library": {
|
||||
|
|
@ -8687,7 +8687,7 @@
|
|||
"argparse": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
"js-yaml": "bin/js-yaml"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/supports-color": {
|
||||
|
|
@ -8733,8 +8733,8 @@
|
|||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||
"bin": {
|
||||
"esparse": "bin/esparse.js",
|
||||
"esvalidate": "bin/esvalidate.js"
|
||||
"esparse": "bin/esparse",
|
||||
"esvalidate": "bin/esvalidate"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
|
|
@ -9379,7 +9379,7 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/fraction.js": {
|
||||
"node_modules/fraction": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
|
||||
"integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==",
|
||||
|
|
@ -9557,8 +9557,8 @@
|
|||
"globby": "^6.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"gh-pages": "bin/gh-pages.js",
|
||||
"gh-pages-clean": "bin/gh-pages-clean.js"
|
||||
"gh-pages": "bin/gh-pages",
|
||||
"gh-pages-clean": "bin/gh-pages-clean"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
|
|
@ -9904,7 +9904,7 @@
|
|||
"he": "bin/he"
|
||||
}
|
||||
},
|
||||
"node_modules/highlight.js": {
|
||||
"node_modules/highlight": {
|
||||
"version": "10.7.3",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
|
||||
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
|
||||
|
|
@ -9920,7 +9920,7 @@
|
|||
"node": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hpack.js": {
|
||||
"node_modules/hpack": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
|
||||
"integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==",
|
||||
|
|
@ -10008,7 +10008,7 @@
|
|||
"terser": "^5.10.0"
|
||||
},
|
||||
"bin": {
|
||||
"html-minifier-terser": "cli.js"
|
||||
"html-minifier-terser": "cli"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
|
|
@ -10235,7 +10235,7 @@
|
|||
"resolve-cwd": "^3.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"import-local-fixture": "fixtures/cli.js"
|
||||
"import-local-fixture": "fixtures/cli"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
|
|
@ -10292,7 +10292,7 @@
|
|||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"node_modules/ipaddr": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz",
|
||||
"integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==",
|
||||
|
|
@ -10487,7 +10487,7 @@
|
|||
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
|
||||
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
|
||||
"bin": {
|
||||
"is-docker": "cli.js"
|
||||
"is-docker": "cli"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
|
|
@ -10834,7 +10834,7 @@
|
|||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/istanbul-lib-report": {
|
||||
|
|
@ -10925,7 +10925,7 @@
|
|||
"minimatch": "^3.1.2"
|
||||
},
|
||||
"bin": {
|
||||
"jake": "bin/cli.js"
|
||||
"jake": "bin/cli"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
|
|
@ -11005,7 +11005,7 @@
|
|||
"jest-cli": "^27.5.1"
|
||||
},
|
||||
"bin": {
|
||||
"jest": "bin/jest.js"
|
||||
"jest": "bin/jest"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
|
||||
|
|
@ -11144,7 +11144,7 @@
|
|||
"yargs": "^16.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"jest": "bin/jest.js"
|
||||
"jest": "bin/jest"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
|
||||
|
|
@ -12877,7 +12877,7 @@
|
|||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz",
|
||||
"integrity": "sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==",
|
||||
"bin": {
|
||||
"jiti": "bin/jiti.js"
|
||||
"jiti": "bin/jiti"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
|
|
@ -12894,7 +12894,7 @@
|
|||
"esprima": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
"js-yaml": "bin/js-yaml"
|
||||
}
|
||||
},
|
||||
"node_modules/jsdoc-type-pratt-parser": {
|
||||
|
|
@ -12917,7 +12917,7 @@
|
|||
"cssom": "^0.4.4",
|
||||
"cssstyle": "^2.3.0",
|
||||
"data-urls": "^2.0.0",
|
||||
"decimal.js": "^10.2.1",
|
||||
"decimal": "^10.2.1",
|
||||
"domexception": "^2.0.1",
|
||||
"escodegen": "^2.0.0",
|
||||
"form-data": "^3.0.0",
|
||||
|
|
@ -12987,7 +12987,7 @@
|
|||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"bin": {
|
||||
"json5": "lib/cli.js"
|
||||
"json5": "lib/cli"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
|
|
@ -13118,7 +13118,7 @@
|
|||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
|
||||
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
|
||||
"dependencies": {
|
||||
"big.js": "^5.2.2",
|
||||
"big": "^5.2.2",
|
||||
"emojis-list": "^3.0.0",
|
||||
"json5": "^2.1.2"
|
||||
},
|
||||
|
|
@ -13178,7 +13178,7 @@
|
|||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"loose-envify": "cli.js"
|
||||
"loose-envify": "cli"
|
||||
}
|
||||
},
|
||||
"node_modules/lower-case": {
|
||||
|
|
@ -13195,7 +13195,7 @@
|
|||
"integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==",
|
||||
"dependencies": {
|
||||
"fault": "^1.0.0",
|
||||
"highlight.js": "~10.7.0"
|
||||
"highlight": "~10.7.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
|
@ -13215,7 +13215,7 @@
|
|||
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
|
||||
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
|
||||
"bin": {
|
||||
"lz-string": "bin/bin.js"
|
||||
"lz-string": "bin/bin"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
|
|
@ -13245,7 +13245,7 @@
|
|||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/makeerror": {
|
||||
|
|
@ -13334,7 +13334,7 @@
|
|||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
"mime": "cli"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
|
|
@ -13474,7 +13474,7 @@
|
|||
"minimist": "^1.2.6"
|
||||
},
|
||||
"bin": {
|
||||
"mkdirp": "bin/cmd.js"
|
||||
"mkdirp": "bin/cmd"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
|
|
@ -13491,7 +13491,7 @@
|
|||
"thunky": "^1.0.2"
|
||||
},
|
||||
"bin": {
|
||||
"multicast-dns": "cli.js"
|
||||
"multicast-dns": "cli"
|
||||
}
|
||||
},
|
||||
"node_modules/mz": {
|
||||
|
|
@ -15502,13 +15502,13 @@
|
|||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
||||
"dependencies": {
|
||||
"forwarded": "0.2.0",
|
||||
"ipaddr.js": "1.9.1"
|
||||
"ipaddr": "1.9.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr/node_modules/ipaddr.js": {
|
||||
"node_modules/proxy-addr/node_modules/ipaddr": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
||||
|
|
@ -15890,7 +15890,7 @@
|
|||
"workbox-webpack-plugin": "^6.4.1"
|
||||
},
|
||||
"bin": {
|
||||
"react-scripts": "bin/react-scripts.js"
|
||||
"react-scripts": "bin/react-scripts"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
|
|
@ -15937,7 +15937,7 @@
|
|||
"integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"highlight.js": "^10.4.1",
|
||||
"highlight": "^10.4.1",
|
||||
"lowlight": "^1.17.0",
|
||||
"prismjs": "^1.27.0",
|
||||
"refractor": "^3.6.0"
|
||||
|
|
@ -16314,7 +16314,7 @@
|
|||
"glob": "^7.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
"rimraf": "bin"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
|
|
@ -16575,7 +16575,7 @@
|
|||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
"semver": "bin/semver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
|
|
@ -16954,7 +16954,7 @@
|
|||
"dependencies": {
|
||||
"debug": "^4.1.0",
|
||||
"detect-node": "^2.0.4",
|
||||
"hpack.js": "^2.1.6",
|
||||
"hpack": "^2.1.6",
|
||||
"obuf": "^1.1.2",
|
||||
"readable-stream": "^3.0.6",
|
||||
"wbuf": "^1.7.3"
|
||||
|
|
@ -17460,8 +17460,8 @@
|
|||
"sucrase": "^3.32.0"
|
||||
},
|
||||
"bin": {
|
||||
"tailwind": "lib/cli.js",
|
||||
"tailwindcss": "lib/cli.js"
|
||||
"tailwind": "lib/cli",
|
||||
"tailwindcss": "lib/cli"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
|
|
@ -17746,7 +17746,7 @@
|
|||
"minimist": "^1.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"json5": "lib/cli.js"
|
||||
"json5": "lib/cli"
|
||||
}
|
||||
},
|
||||
"node_modules/tsconfig-paths/node_modules/strip-bom": {
|
||||
|
|
@ -18031,7 +18031,7 @@
|
|||
"picocolors": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"update-browserslist-db": "cli.js"
|
||||
"update-browserslist-db": "cli"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"browserslist": ">= 4.21.0"
|
||||
|
|
@ -18207,7 +18207,7 @@
|
|||
"webpack-sources": "^3.2.3"
|
||||
},
|
||||
"bin": {
|
||||
"webpack": "bin/webpack.js"
|
||||
"webpack": "bin/webpack"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
|
|
@ -18316,7 +18316,7 @@
|
|||
"graceful-fs": "^4.2.6",
|
||||
"html-entities": "^2.3.2",
|
||||
"http-proxy-middleware": "^2.0.3",
|
||||
"ipaddr.js": "^2.0.1",
|
||||
"ipaddr": "^2.0.1",
|
||||
"launch-editor": "^2.6.0",
|
||||
"open": "^8.0.9",
|
||||
"p-retry": "^4.5.0",
|
||||
|
|
@ -18330,7 +18330,7 @@
|
|||
"ws": "^8.13.0"
|
||||
},
|
||||
"bin": {
|
||||
"webpack-dev-server": "bin/webpack-dev-server.js"
|
||||
"webpack-dev-server": "bin/webpack-dev-server"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.13.0"
|
||||
|
|
|
|||
|
|
@ -95,8 +95,6 @@
|
|||
],
|
||||
"jsdoc/check-values": "warn",
|
||||
"jsdoc/empty-tags": "warn",
|
||||
"jsdoc/check-param-names": "warn",
|
||||
"jsdoc/check-property-names": "warn",
|
||||
"jsdoc/check-access": "warn",
|
||||
"jsdoc/check-alignment": "warn",
|
||||
"jsdoc/multiline-blocks": "warn",
|
||||
|
|
|
|||
12
src/App.tsx
12
src/App.tsx
|
|
@ -1,14 +1,14 @@
|
|||
import styles from "./App.module.css";
|
||||
import { Taskbar } from "./components/taskbar/Taskbar.jsx";
|
||||
import { Taskbar } from "./components/taskbar/Taskbar";
|
||||
import { WindowsManagerProvider } from "./hooks/windows/windowsManagerContext";
|
||||
import { WindowsView } from "./components/windows/WindowsView.jsx";
|
||||
import { WindowsView } from "./components/windows/WindowsView";
|
||||
import { VirtualRootProvider } from "./hooks/virtual-drive/virtualRootContext";
|
||||
import { Desktop } from "./components/desktop/Desktop.jsx";
|
||||
import { Desktop } from "./components/desktop/Desktop";
|
||||
import { SettingsManagerProvider } from "./hooks/settings/settingsManagerContext";
|
||||
import { ModalsView } from "./components/modals/ModalsView.jsx";
|
||||
import React, { FC, useEffect } from "react";
|
||||
import { ModalsView } from "./components/modals/ModalsView";
|
||||
import { FC, useEffect } from "react";
|
||||
import { ZIndexManagerProvider } from "./hooks/z-index/zIndexManagerContext";
|
||||
import { TrackingManager } from "./features/tracking/trackingManager.js";
|
||||
import { TrackingManager } from "./features/tracking/trackingManager";
|
||||
import { ModalsManagerProvider } from "./hooks/modals/modalsManagerContext";
|
||||
|
||||
TrackingManager.initialize();
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import styles from "./Button.module.css";
|
||||
import { faExternalLink } from "@fortawesome/free-solid-svg-icons";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {string} props.className
|
||||
* @param {string} props.href
|
||||
*/
|
||||
export function Button(props) {
|
||||
interface ButtonProps {
|
||||
className?: string;
|
||||
href?: string;
|
||||
children?: ReactNode;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export function Button(props: ButtonProps) {
|
||||
let { className = "" } = props;
|
||||
const { href, children } = props;
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import styles from "./DropdownButton.module.css";
|
||||
import OutsideClickListener from "../../../hooks/_utils/outsideClick.js";
|
||||
import { formatShortcut } from "../../../features/_utils/string.utils.js";
|
||||
import OutsideClickListener from "../../../hooks/_utils/outsideClick";
|
||||
import { formatShortcut } from "../../../features/_utils/string.utils";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
|
|
@ -25,7 +25,7 @@ export function DropdownButton({ label, options, shortcuts }) {
|
|||
</button>
|
||||
{open && options
|
||||
? (<div className={styles.Dropdown}>
|
||||
{Object.entries(options).map(([label, callback]) =>
|
||||
{Object.entries(options).map(([label, callback]: [label: string, callback: Function]) =>
|
||||
<button key={label} tabIndex={tabIndex} onClick={() => {
|
||||
setOpen(false);
|
||||
callback();
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { useState } from "react";
|
||||
import { INTERACTIBLE_DOUBLE_CLICK_DELAY } from "../../../config/utils.config.js";
|
||||
import { INTERACTIBLE_DOUBLE_CLICK_DELAY } from "../../../config/utils.config";
|
||||
|
||||
let timeoutId = null;
|
||||
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { clamp } from "../../../features/math/clamp.js";
|
||||
import { CSSProperties } from "react";
|
||||
import { clamp } from "../../../features/math/clamp";
|
||||
import styles from "./ProgressBar.module.css";
|
||||
|
||||
/**
|
||||
|
|
@ -9,12 +10,21 @@ import styles from "./ProgressBar.module.css";
|
|||
* @param {string} props.align
|
||||
* @param {string} props.className
|
||||
*/
|
||||
export function ProgressBar({ fillPercentage, fillColor, backgroundColor, align = "left", className = "" }) {
|
||||
|
||||
interface ProgressBarProps {
|
||||
fillPercentage: number;
|
||||
fillColor?: string;
|
||||
backgroundColor?: string;
|
||||
align?: "left";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function ProgressBar({ fillPercentage, fillColor, backgroundColor, align = "left", className = "" }: ProgressBarProps) {
|
||||
return (
|
||||
<div className={`${styles.Container} ${className}`} style={{ backgroundColor: backgroundColor }}>
|
||||
<div
|
||||
className={`${styles.Fill} ${align}`}
|
||||
style={{ backgroundColor: fillColor, "--fill": `${clamp(fillPercentage, 0.1, 100)}%` }}
|
||||
style={{ backgroundColor: fillColor, "--fill": `${clamp(fillPercentage, 0.1, 100)}%` } as CSSProperties}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -1,25 +1,31 @@
|
|||
import { Children, cloneElement, isValidElement } from "react";
|
||||
import { useShortcuts } from "../../hooks/_utils/keyboard.js";
|
||||
import { Children, cloneElement, isValidElement, ReactNode } from "react";
|
||||
import { useShortcuts } from "../../hooks/_utils/keyboard";
|
||||
import styles from "./Actions.module.css";
|
||||
import { useScreenBounds } from "../../hooks/_utils/screen.js";
|
||||
import { useScreenBounds } from "../../hooks/_utils/screen";
|
||||
|
||||
export const STYLES = {
|
||||
CONTEXT_MENU: styles["Context-menu"],
|
||||
SHORTCUTS_LISTENER: styles["Shortcuts-listener"],
|
||||
};
|
||||
|
||||
/**
|
||||
* @callback actionsType
|
||||
* @param {object} props
|
||||
* @param {string=} props.className
|
||||
* @param {Function} props.onAnyTrigger
|
||||
* @param {import("react").ElementType} props.children
|
||||
* @param {*} props.triggerParams
|
||||
* @param {boolean} props.avoidTaskbar
|
||||
*/
|
||||
export interface ActionProps {
|
||||
actionId?: string;
|
||||
label?: string;
|
||||
icon?: string|object;
|
||||
shortcut?: string[];
|
||||
onTrigger?: (event: Event, triggerParams: any, ...args: any[]) => void;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export interface ActionsProps {
|
||||
className?: string;
|
||||
onAnyTrigger?: (event: Event, triggerParams: any, ...args: any[]) => void;
|
||||
children?: ReactNode;
|
||||
triggerParams?: any;
|
||||
avoidTaskbar?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {actionsType}
|
||||
* @example
|
||||
* <ClickAction
|
||||
* label="Reload"
|
||||
|
|
@ -30,7 +36,7 @@ export const STYLES = {
|
|||
* }}
|
||||
* />
|
||||
*/
|
||||
export function Actions({ children, className, onAnyTrigger, triggerParams, avoidTaskbar = true }) {
|
||||
export function Actions({ children, className, onAnyTrigger, triggerParams, avoidTaskbar = true }: ActionsProps): ReactNode {
|
||||
const isListener = (className === STYLES.SHORTCUTS_LISTENER);
|
||||
|
||||
const { ref, initiated, alignLeft, alignTop } = useScreenBounds({ avoidTaskbar });
|
||||
|
|
@ -38,7 +44,7 @@ export function Actions({ children, className, onAnyTrigger, triggerParams, avoi
|
|||
const options = {};
|
||||
const shortcuts = {};
|
||||
|
||||
const iterateOverChildren = (children) => {
|
||||
const iterateOverChildren = (children: ReactNode) => {
|
||||
let actionId = 0;
|
||||
const newChildren = Children.map(children, (child) => {
|
||||
if (!isValidElement(child))
|
||||
|
|
@ -88,7 +94,7 @@ export function Actions({ children, className, onAnyTrigger, triggerParams, avoi
|
|||
if (!initiated)
|
||||
classNames.push(styles.Uninitiated);
|
||||
|
||||
return (<div ref={ref} className={classNames.join(" ")}>
|
||||
return <div ref={ref} className={classNames.join(" ")}>
|
||||
{iterateOverChildren(children)}
|
||||
</div>);
|
||||
</div>;
|
||||
}
|
||||
|
|
@ -1,24 +1,22 @@
|
|||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { formatShortcut } from "../../../features/_utils/string.utils.js";
|
||||
import { formatShortcut } from "../../../features/_utils/string.utils";
|
||||
import styles from "../Actions.module.css";
|
||||
import { ImagePreview } from "../../apps/file-explorer/directory-list/ImagePreview.jsx";
|
||||
import { memo } from "react";
|
||||
import { ImagePreview } from "../../apps/file-explorer/directory-list/ImagePreview";
|
||||
import { memo, MouseEventHandler } from "react";
|
||||
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||
import { ActionProps } from "../Actions";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {string} props.actionId
|
||||
* @param {string} props.label
|
||||
* @param {string[]} props.shortcut
|
||||
* @param {Function} props.onTrigger
|
||||
* @param {string|object} props.icon
|
||||
*/
|
||||
export const ClickAction = memo(({ actionId, label, shortcut, onTrigger, icon }) => {
|
||||
return (<button key={actionId} className={styles.Button} tabIndex={0} onClick={onTrigger}>
|
||||
interface ClickActionProps extends ActionProps {
|
||||
icon?: string | object;
|
||||
}
|
||||
|
||||
export const ClickAction = memo(({ actionId, label, shortcut, onTrigger, icon }: ClickActionProps) => {
|
||||
return (<button key={actionId} className={styles.Button} tabIndex={0} onClick={onTrigger as unknown as MouseEventHandler}>
|
||||
<span className={styles.Label}>
|
||||
{icon && <div className={styles.Icon}>
|
||||
{typeof icon == "string"
|
||||
? <ImagePreview source={icon} className={styles["Image-icon"]}/>
|
||||
: <FontAwesomeIcon icon={icon}/>
|
||||
: <FontAwesomeIcon icon={icon as unknown as IconProp}/>
|
||||
}
|
||||
</div>}
|
||||
<p>{label}</p>
|
||||
|
|
@ -1,22 +1,17 @@
|
|||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import styles from "../Actions.module.css";
|
||||
import { faCaretRight } from "@fortawesome/free-solid-svg-icons";
|
||||
import { useState } from "react";
|
||||
import { faCaretRight, IconDefinition } from "@fortawesome/free-solid-svg-icons";
|
||||
import { ReactNode, useState } from "react";
|
||||
import { ActionProps } from "../Actions";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {string} props.label
|
||||
* @param {string|object} props.icon
|
||||
* @param {*} props.children
|
||||
*/
|
||||
export function DropdownAction({ label, icon, children }) {
|
||||
export function DropdownAction({ label, icon, children }: ActionProps): ReactNode {
|
||||
const [showContent, setShowContent] = useState(false);
|
||||
|
||||
const classNames = [styles.Dropdown];
|
||||
if (showContent)
|
||||
classNames.push(styles.Active);
|
||||
|
||||
return (<div
|
||||
return <div
|
||||
key={label}
|
||||
className={classNames.join(" ")}
|
||||
tabIndex={0}
|
||||
|
|
@ -24,12 +19,12 @@ export function DropdownAction({ label, icon, children }) {
|
|||
onMouseLeave={() => { setShowContent(false); }}
|
||||
>
|
||||
<span className={styles.Label}>
|
||||
{icon && <div className={styles.Icon}><FontAwesomeIcon icon={icon}/></div>}
|
||||
{icon && <div className={styles.Icon}><FontAwesomeIcon icon={icon as IconDefinition}/></div>}
|
||||
<p>{label}</p>
|
||||
</span>
|
||||
<div className={styles["Dropdown-arrow"]}><FontAwesomeIcon icon={faCaretRight}/></div>
|
||||
<div className={styles["Dropdown-content"]}>
|
||||
{children}
|
||||
</div>
|
||||
</div>);
|
||||
</div>;
|
||||
}
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { formatShortcut } from "../../../features/_utils/string.utils.js";
|
||||
import { formatShortcut } from "../../../features/_utils/string.utils";
|
||||
import styles from "../Actions.module.css";
|
||||
import { faCircleDot } from "@fortawesome/free-solid-svg-icons";
|
||||
import { useState } from "react";
|
||||
import { ReactNode, useState } from "react";
|
||||
import { faCircle } from "@fortawesome/free-regular-svg-icons";
|
||||
import { ActionProps } from "../Actions";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
|
|
@ -15,14 +16,23 @@ import { faCircle } from "@fortawesome/free-regular-svg-icons";
|
|||
* @param {number} props.initialIndex
|
||||
* @param {Function} props.onTrigger
|
||||
*/
|
||||
export function RadioAction({ actionId, options, initialIndex, onTrigger }) {
|
||||
|
||||
interface RadioActionProps extends ActionProps {
|
||||
options: {
|
||||
label: string;
|
||||
shortcut?: string[]
|
||||
}[];
|
||||
initialIndex: number;
|
||||
}
|
||||
|
||||
export function RadioAction({ actionId, options, initialIndex, onTrigger }: RadioActionProps): ReactNode {
|
||||
const [activeIndex, setActiveIndex] = useState(initialIndex ?? 0);
|
||||
|
||||
return (<div key={actionId}>
|
||||
{options.map(({ label, shortcut }, index) =>
|
||||
<button key={label} className={styles.Button} tabIndex={0} onClick={(event) => {
|
||||
setActiveIndex(index);
|
||||
onTrigger(event, index);
|
||||
onTrigger(event as unknown as Event, index);
|
||||
}}>
|
||||
<span className={styles.Label}>
|
||||
<div className={styles.Icon}>
|
||||
|
|
@ -1,23 +1,20 @@
|
|||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { formatShortcut } from "../../../features/_utils/string.utils.js";
|
||||
import { formatShortcut } from "../../../features/_utils/string.utils";
|
||||
import styles from "../Actions.module.css";
|
||||
import { useState } from "react";
|
||||
import { ReactNode, useState } from "react";
|
||||
import { faSquare } from "@fortawesome/free-regular-svg-icons";
|
||||
import { faSquareCheck } from "@fortawesome/free-solid-svg-icons";
|
||||
import { ActionProps } from "../Actions";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {string} props.actionId
|
||||
* @param {string} props.label
|
||||
* @param {string[]} props.shortcut
|
||||
* @param {Function} props.onTrigger
|
||||
* @param {boolean} props.initialValue
|
||||
*/
|
||||
export function ToggleAction({ actionId, label, shortcut, initialValue, onTrigger }) {
|
||||
interface ToggleActionProps extends ActionProps {
|
||||
initialValue: boolean;
|
||||
}
|
||||
|
||||
export function ToggleAction({ actionId, label, shortcut, initialValue, onTrigger }: ToggleActionProps): ReactNode {
|
||||
const [active, setActive] = useState(initialValue ?? false);
|
||||
|
||||
return (<button key={actionId} className={styles.Button} tabIndex={0} onClick={(event) => {
|
||||
onTrigger(event, !active);
|
||||
onTrigger(event as unknown as Event, !active);
|
||||
setActive(!active);
|
||||
}}>
|
||||
<span className={styles.Label}>
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
import { useShortcuts } from "../../../../hooks/_utils/keyboard.js";
|
||||
import { DropdownButton } from "../../../_utils/dropdown-button/DropdownButton.jsx";
|
||||
import { useShortcuts } from "../../../../hooks/_utils/keyboard";
|
||||
import { DropdownButton } from "../../../_utils/dropdown-button/DropdownButton";
|
||||
import styles from "./HeaderMenu.module.css";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {Object<string, Object<string, Function>>} props.options
|
||||
* @param {Object<string, Object<string, string[]>>} props.shortcuts
|
||||
*/
|
||||
export function HeaderMenu({ options, shortcuts }) {
|
||||
interface HeaderMenuProps {
|
||||
options: Record<string, Record<string, Function>> | Record<string, Function>;
|
||||
shortcuts: Record<string, Record<string, string[]>> | Record<string, string[]>;
|
||||
}
|
||||
|
||||
export function HeaderMenu({ options, shortcuts }: HeaderMenuProps) {
|
||||
useShortcuts({ options, shortcuts });
|
||||
|
||||
return (
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
import { forwardRef, useEffect, useState } from "react";
|
||||
import styles from "./WebView.module.css";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {string} props.source
|
||||
*/
|
||||
export const WebView = forwardRef(({ source, focus, ...props }, ref) => {
|
||||
const [hovered, setHovered] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
window.focus();
|
||||
|
||||
const onBlur = (event) => {
|
||||
if (hovered) {
|
||||
focus?.(event);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("blur", onBlur);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("blur", onBlur);
|
||||
};
|
||||
}, [focus, hovered]);
|
||||
|
||||
const onMouseOver = () => {
|
||||
setHovered(true);
|
||||
};
|
||||
|
||||
const onMouseOut = () => {
|
||||
window.focus();
|
||||
setHovered(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.Container} onMouseOver={onMouseOver} onMouseOut={onMouseOut}>
|
||||
<iframe
|
||||
ref={ref}
|
||||
src={source}
|
||||
title={props.title ?? "Web view"}
|
||||
className={styles["Web-view"]}
|
||||
referrerPolicy="no-referrer"
|
||||
sandbox="allow-downloads allow-forms allow-modals allow-pointer-lock allow-popups allow-presentation allow-same-origin allow-scripts"
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
|
||||
);
|
||||
});
|
||||
48
src/components/apps/_utils/web-view/WebView.tsx
Normal file
48
src/components/apps/_utils/web-view/WebView.tsx
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import { FC, forwardRef, useEffect, useState } from "react";
|
||||
import styles from "./WebView.module.css";
|
||||
import { WindowProps } from "../../../windows/WindowView";
|
||||
|
||||
interface WebViewProps extends WindowProps {
|
||||
source: string;
|
||||
}
|
||||
|
||||
export const WebView: FC<WebViewProps> = forwardRef<HTMLIFrameElement>(({ source, focus, ...props }: WebViewProps, ref) => {
|
||||
const [hovered, setHovered] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
window.focus();
|
||||
|
||||
const onBlur = (event) => {
|
||||
if (hovered) {
|
||||
focus?.(event);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("blur", onBlur);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("blur", onBlur);
|
||||
};
|
||||
}, [focus, hovered]);
|
||||
|
||||
const onMouseOver = () => {
|
||||
setHovered(true);
|
||||
};
|
||||
|
||||
const onMouseOut = () => {
|
||||
window.focus();
|
||||
setHovered(false);
|
||||
};
|
||||
|
||||
return <div className={styles.Container} onMouseOver={onMouseOver} onMouseOut={onMouseOut}>
|
||||
<iframe
|
||||
ref={ref}
|
||||
src={source}
|
||||
title={props.title ?? "Web view"}
|
||||
className={styles["Web-view"]}
|
||||
referrerPolicy="no-referrer"
|
||||
sandbox="allow-downloads allow-forms allow-modals allow-pointer-lock allow-popups allow-presentation allow-same-origin allow-scripts"
|
||||
{...props}
|
||||
/>
|
||||
</div>;
|
||||
}) as FC;
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import styles from "./Browser.module.css";
|
||||
import { WebView } from "../_utils/web-view/WebView.jsx";
|
||||
import { WebView } from "../_utils/web-view/WebView";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faCaretLeft, faCaretRight, faHome, faRotateRight } from "@fortawesome/free-solid-svg-icons";
|
||||
import { HOME_URL, SEARCH_URL } from "../../../config/apps/browser.config.js";
|
||||
import { isValidUrl } from "../../../features/_utils/browser.utils.js";
|
||||
import { useHistory } from "../../../hooks/_utils/history.js";
|
||||
import { HOME_URL, SEARCH_URL } from "../../../config/apps/browser.config";
|
||||
import { isValidUrl } from "../../../features/_utils/browser.utils";
|
||||
import { useHistory } from "../../../hooks/_utils/history";
|
||||
|
||||
/** @type {import("../../windows/WindowView.jsx").windowProps} */
|
||||
export function Browser({ startUrl, focus }) {
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Button } from "../../_utils/button/Button.jsx";
|
||||
import { Button } from "../../_utils/button/Button";
|
||||
import styles from "./Calculator.module.css";
|
||||
|
||||
/** @type {import("../../windows/WindowView.jsx").windowProps} */
|
||||
|
|
@ -1,41 +1,45 @@
|
|||
import { useCallback, useEffect, useState } from "react";
|
||||
import { FC, useCallback, useEffect, useState } from "react";
|
||||
import { useVirtualRoot } from "../../../hooks/virtual-drive/virtualRootContext";
|
||||
import styles from "./FileExplorer.module.css";
|
||||
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 { useWindowsManager } from "../../../hooks/windows/windowsManagerContext";
|
||||
import { useContextMenu } from "../../../hooks/modals/contextMenu.js";
|
||||
import { QuickAccessButton } from "./QuickAccessButton.jsx";
|
||||
import { useWindowedModal } from "../../../hooks/modals/windowedModal.js";
|
||||
import Vector2 from "../../../features/math/vector2.js";
|
||||
import { DIALOG_CONTENT_TYPES } from "../../../config/modals.config.js";
|
||||
import { DirectoryList } from "./directory-list/DirectoryList.jsx";
|
||||
import { Actions } from "../../actions/Actions.jsx";
|
||||
import { ClickAction } from "../../actions/actions/ClickAction.jsx";
|
||||
import { useContextMenu } from "../../../hooks/modals/contextMenu";
|
||||
import { QuickAccessButton } from "./QuickAccessButton";
|
||||
import { useWindowedModal } from "../../../hooks/modals/windowedModal";
|
||||
import Vector2 from "../../../features/math/vector2";
|
||||
import { DIALOG_CONTENT_TYPES } from "../../../config/modals.config";
|
||||
import { DirectoryList, OnSelectionChangeParams } from "./directory-list/DirectoryList";
|
||||
import { Actions } from "../../actions/Actions";
|
||||
import { ClickAction } from "../../actions/actions/ClickAction";
|
||||
import utilStyles from "../../../styles/utils.module.css";
|
||||
import { DialogBox } from "../../modals/dialog-box/DialogBox.jsx";
|
||||
import AppsManager from "../../../features/apps/appsManager.js";
|
||||
import { APPS, APP_ICONS, APP_NAMES } from "../../../config/apps.config.js";
|
||||
import { TITLE_SEPARATOR } from "../../../config/windows.config.js";
|
||||
import { FileProperties } from "../../modals/file-properties/FileProperties.jsx";
|
||||
import { useHistory } from "../../../hooks/_utils/history.js";
|
||||
import { Divider } from "../../actions/actions/Divider.jsx";
|
||||
import { CODE_FORMATS } from "../../../config/apps/textEditor.config.js";
|
||||
import { SELECTOR_MODE } from "../../../config/apps/fileExplorer.config.js";
|
||||
import { DialogBox } from "../../modals/dialog-box/DialogBox";
|
||||
import AppsManager from "../../../features/apps/appsManager";
|
||||
import { APPS, APP_ICONS, APP_NAMES } from "../../../config/apps.config";
|
||||
import { TITLE_SEPARATOR } from "../../../config/windows.config";
|
||||
import { FileProperties } from "../../modals/file-properties/FileProperties";
|
||||
import { useHistory } from "../../../hooks/_utils/history";
|
||||
import { Divider } from "../../actions/actions/Divider";
|
||||
import { CODE_FORMATS } from "../../../config/apps/textEditor.config";
|
||||
import { SELECTOR_MODE } from "../../../config/apps/fileExplorer.config";
|
||||
import { WindowProps } from "../../windows/WindowView";
|
||||
import { VirtualFolder } from "../../../features/virtual-drive/folder/virtualFolder";
|
||||
import { VirtualFile } from "../../../features/virtual-drive/file/virtualFile";
|
||||
import { VirtualFolderLink } from "../../../features/virtual-drive/folder/virtualFolderLink";
|
||||
|
||||
/**
|
||||
* @param {import("../../windows/WindowView.jsx").windowProps} props
|
||||
* @param {string} props.startPath
|
||||
* @param {*} props.Footer
|
||||
* @param {number} props.selectorMode
|
||||
* @param {import("./directory-list/DirectoryList.jsx").onSelectionChange} props.onSelectionChange
|
||||
* @param {Function} props.onSelectionFinish
|
||||
*/
|
||||
export function FileExplorer({ startPath, selectorMode, Footer, onSelectionChange, onSelectionFinish }) {
|
||||
interface FileExplorerProps extends WindowProps {
|
||||
startPath?: string;
|
||||
selectorMode?: number;
|
||||
Footer: FC;
|
||||
onSelectionChange: (params: OnSelectionChangeParams) => void;
|
||||
onSelectionFinish: Function;
|
||||
}
|
||||
|
||||
export function FileExplorer({ startPath, selectorMode, Footer, onSelectionChange, onSelectionFinish }: FileExplorerProps) {
|
||||
const isSelector = (Footer != null && selectorMode != null && selectorMode !== SELECTOR_MODE.NONE);
|
||||
|
||||
const virtualRoot = useVirtualRoot();
|
||||
const [currentDirectory, setCurrentDirectory] = useState(virtualRoot.navigate(startPath ?? "~"));
|
||||
const [currentDirectory, setCurrentDirectory] = useState<VirtualFolder>(virtualRoot.navigate(startPath ?? "~") as VirtualFolder);
|
||||
const [path, setPath] = useState(currentDirectory?.path ?? "");
|
||||
const windowsManager = useWindowsManager();
|
||||
const [showHidden] = useState(true);
|
||||
|
|
@ -44,7 +48,7 @@ export function FileExplorer({ startPath, selectorMode, Footer, onSelectionChang
|
|||
const { openWindowedModal } = useWindowedModal();
|
||||
const { onContextMenu: onContextMenuFile } = useContextMenu({ Actions: (props) =>
|
||||
<Actions {...props}>
|
||||
<ClickAction label={!isSelector ? "Open" : "Select"} onTrigger={(event, file) => {
|
||||
<ClickAction label={!isSelector ? "Open" : "Select"} onTrigger={(event, file: VirtualFile) => {
|
||||
if (isSelector) {
|
||||
onSelectionChange?.({ files: [file.id], directory: currentDirectory });
|
||||
onSelectionFinish?.();
|
||||
|
|
@ -97,7 +101,7 @@ export function FileExplorer({ startPath, selectorMode, Footer, onSelectionChang
|
|||
const directory = absolute ? virtualRoot.navigate(path) : currentDirectory.navigate(path);
|
||||
|
||||
if (directory != null) {
|
||||
setCurrentDirectory(directory);
|
||||
setCurrentDirectory(directory as VirtualFolder);
|
||||
setPath(directory.root ? "/" : directory.path);
|
||||
pushState(directory.path);
|
||||
}
|
||||
|
|
@ -110,7 +114,7 @@ export function FileExplorer({ startPath, selectorMode, Footer, onSelectionChang
|
|||
const path = history[stateIndex];
|
||||
const directory = virtualRoot.navigate(path);
|
||||
if (directory != null) {
|
||||
setCurrentDirectory(directory);
|
||||
setCurrentDirectory(directory as VirtualFolder);
|
||||
setPath(directory.root ? "/" : directory.path);
|
||||
}
|
||||
}, [history, stateIndex, virtualRoot]);
|
||||
|
|
@ -142,7 +146,7 @@ export function FileExplorer({ startPath, selectorMode, Footer, onSelectionChang
|
|||
return;
|
||||
}
|
||||
|
||||
setCurrentDirectory(directory);
|
||||
setCurrentDirectory(directory as VirtualFolder);
|
||||
setPath(directory.root ? "/" : directory.path);
|
||||
}
|
||||
};
|
||||
|
|
@ -175,7 +179,7 @@ export function FileExplorer({ startPath, selectorMode, Footer, onSelectionChang
|
|||
tabIndex={0}
|
||||
className={styles["Icon-button"]}
|
||||
onClick={() => { changeDirectory(".."); }}
|
||||
disabled={currentDirectory.root}
|
||||
disabled={currentDirectory.isRoot}
|
||||
>
|
||||
<FontAwesomeIcon icon={faArrowUp}/>
|
||||
</button>
|
||||
|
|
@ -235,16 +239,16 @@ export function FileExplorer({ startPath, selectorMode, Footer, onSelectionChang
|
|||
className={styles.Main}
|
||||
showHidden={showHidden}
|
||||
onOpenFile={(event, file) => {
|
||||
event.preventDefault();
|
||||
(event as Event).preventDefault();
|
||||
if (isSelector)
|
||||
return onSelectionFinish?.();
|
||||
const options = {};
|
||||
const options: Record<string, string> = {};
|
||||
if (file.extension === "md" || CODE_FORMATS.includes(file.extension))
|
||||
options.mode = "view";
|
||||
windowsManager.openFile(file, options);
|
||||
}}
|
||||
onOpenFolder={(event, folder) => {
|
||||
changeDirectory(folder.linkedPath ?? folder.name);
|
||||
changeDirectory((folder as VirtualFolderLink).linkedPath ?? folder.name);
|
||||
}}
|
||||
onContextMenuFile={onContextMenuFile}
|
||||
onContextMenuFolder={onContextMenuFolder}
|
||||
|
|
@ -1,45 +1,36 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import { VirtualFile } from "../../../../features/virtual-drive/file/virtualFile.js";
|
||||
import { VirtualFolder } from "../../../../features/virtual-drive/folder/virtualFolder.js";
|
||||
import { Interactable } from "../../../_utils/interactable/Interactable.jsx";
|
||||
import { ReactNode, useEffect, useRef, useState } from "react";
|
||||
import { VirtualFile } from "../../../../features/virtual-drive/file/virtualFile";
|
||||
import { VirtualFolder } from "../../../../features/virtual-drive/folder/virtualFolder";
|
||||
import { Interactable } from "../../../_utils/interactable/Interactable";
|
||||
import styles from "./DirectoryList.module.css";
|
||||
import { ImagePreview } from "./ImagePreview.jsx";
|
||||
import { ImagePreview } from "./ImagePreview";
|
||||
|
||||
/**
|
||||
* @callback fileEvent
|
||||
* @param {object} event
|
||||
* @param {VirtualFile} file
|
||||
*/
|
||||
export interface OnSelectionChangeParams {
|
||||
files?: string[];
|
||||
folders?: string[];
|
||||
directory?: VirtualFolder;
|
||||
};
|
||||
|
||||
/**
|
||||
* @callback folderEvent
|
||||
* @param {object} event
|
||||
* @param {VirtualFolder} folder
|
||||
*/
|
||||
type FileEventHandler = (event: object, file: VirtualFile) => void;
|
||||
type FolderEventHandler = (event: object, folder: VirtualFolder) => void;
|
||||
|
||||
/**
|
||||
* @callback onSelectionChange
|
||||
* @param {object} selection
|
||||
* @param {string[]} selection.files
|
||||
* @param {string[]} selection.folders
|
||||
*/
|
||||
interface DirectoryListProps {
|
||||
directory: VirtualFolder;
|
||||
showHidden?: boolean;
|
||||
folderClassName?: string;
|
||||
fileClassName?: string;
|
||||
className?: string;
|
||||
onContextMenuFile?: FileEventHandler;
|
||||
onContextMenuFolder?: FolderEventHandler;
|
||||
onOpenFile?: FileEventHandler;
|
||||
onOpenFolder?: FolderEventHandler;
|
||||
allowMultiSelect?: boolean;
|
||||
onSelectionChange?: (params: OnSelectionChangeParams) => void;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {VirtualFolder} props.directory
|
||||
* @param {boolean} props.showHidden
|
||||
* @param {string} props.folderClassName
|
||||
* @param {string} props.fileClassName
|
||||
* @param {string} props.className
|
||||
* @param {fileEvent} props.onContextMenuFile
|
||||
* @param {folderEvent} props.onContextMenuFolder
|
||||
* @param {fileEvent} props.onOpenFile
|
||||
* @param {folderEvent} props.onOpenFolder
|
||||
* @param {boolean} props.allowMultiSelect
|
||||
* @param {onSelectionChange} props.onSelectionChange
|
||||
*/
|
||||
export function DirectoryList({ directory, showHidden = false, folderClassName, fileClassName, className,
|
||||
onContextMenuFile, onContextMenuFolder, onOpenFile, onOpenFolder, allowMultiSelect = true, onSelectionChange, ...props }) {
|
||||
onContextMenuFile, onContextMenuFolder, onOpenFile, onOpenFolder, allowMultiSelect = true, onSelectionChange, ...props }: DirectoryListProps): ReactNode {
|
||||
const [selectedFolders, setSelectedFolders] = useState([]);
|
||||
const [selectedFiles, setSelectedFiles] = useState([]);
|
||||
|
||||
|
|
@ -1,10 +1,16 @@
|
|||
import { useState } from "react";
|
||||
import styles from "./ImagePreview.module.css";
|
||||
import { ReactSVG } from "react-svg";
|
||||
import AppsManager from "../../../../features/apps/appsManager.js";
|
||||
import { APPS } from "../../../../config/apps.config.js";
|
||||
import AppsManager from "../../../../features/apps/appsManager";
|
||||
import { APPS } from "../../../../config/apps.config";
|
||||
|
||||
export function ImagePreview({ source, className, onError, ...props }) {
|
||||
interface ImagePreviewProps {
|
||||
source: string;
|
||||
className?: string;
|
||||
onError?: Function;
|
||||
}
|
||||
|
||||
export function ImagePreview({ source, className, onError, ...props }: ImagePreviewProps) {
|
||||
const [loadingFailed, setLoadingFailed] = useState(false);
|
||||
|
||||
const onLoadingError = () => {
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
import { useEffect } from "react";
|
||||
import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext";
|
||||
import styles from "./MediaViewer.module.css";
|
||||
import { APPS } from "../../../config/apps.config.js";
|
||||
import { IMAGE_FORMATS } from "../../../config/apps/mediaViewer.config.js";
|
||||
import { APPS } from "../../../config/apps.config";
|
||||
import { IMAGE_FORMATS } from "../../../config/apps/mediaViewer.config";
|
||||
|
||||
/**
|
||||
* @param {import("../../windows/WindowView.jsx").windowProps} props
|
||||
|
|
@ -3,10 +3,10 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||
import { faCircleInfo, faHardDrive, faPalette, faShapes } from "@fortawesome/free-solid-svg-icons";
|
||||
import utilStyles from "../../../styles/utils.module.css";
|
||||
import { Tab, TabList, TabPanel, Tabs } from "react-tabs";
|
||||
import { AppearanceSettings } from "./tabs/AppearanceSettings.jsx";
|
||||
import { AboutSettings } from "./tabs/AboutSettings.jsx";
|
||||
import { StorageTab } from "./tabs/StorageSettings.jsx";
|
||||
import { AppsSettings } from "./tabs/AppsSettings.jsx";
|
||||
import { AppearanceSettings } from "./tabs/AppearanceSettings";
|
||||
import { AboutSettings } from "./tabs/AboutSettings";
|
||||
import { StorageTab } from "./tabs/StorageSettings";
|
||||
import { AppsSettings } from "./tabs/AppsSettings";
|
||||
|
||||
/**
|
||||
* @param {import("../../windows/WindowView.jsx").windowProps} props
|
||||
|
|
@ -38,10 +38,10 @@ export function Settings({ tab, modalsManager }) {
|
|||
</Tab>
|
||||
</TabList>
|
||||
<TabPanel className={styles["Tab-panel"]}>
|
||||
<AppsSettings modalsManager={modalsManager}/>
|
||||
<AppsSettings/>
|
||||
</TabPanel>
|
||||
<TabPanel className={styles["Tab-panel"]}>
|
||||
<AppearanceSettings modalsManager={modalsManager}/>
|
||||
<AppearanceSettings/>
|
||||
</TabPanel>
|
||||
<TabPanel className={styles["Tab-panel"]}>
|
||||
<StorageTab/>
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import { Button } from "../../../_utils/button/Button.jsx";
|
||||
import { Button } from "../../../_utils/button/Button";
|
||||
import styles from "../Settings.module.css";
|
||||
import utilStyles from "../../../../styles/utils.module.css";
|
||||
import Vector2 from "../../../../features/math/vector2.js";
|
||||
import Vector2 from "../../../../features/math/vector2";
|
||||
import { useWindowsManager } from "../../../../hooks/windows/windowsManagerContext";
|
||||
import { useVirtualRoot } from "../../../../hooks/virtual-drive/virtualRootContext";
|
||||
import { NAME } from "../../../../config/branding.config.js";
|
||||
import { NAME } from "../../../../config/branding.config";
|
||||
|
||||
export function AboutSettings() {
|
||||
const windowsManager = useWindowsManager();
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import AppsManager from "../../../../features/apps/appsManager.js";
|
||||
import { ImagePreview } from "../../file-explorer/directory-list/ImagePreview.jsx";
|
||||
import AppsManager from "../../../../features/apps/appsManager";
|
||||
import { ImagePreview } from "../../file-explorer/directory-list/ImagePreview";
|
||||
import styles from "../Settings.module.css";
|
||||
import { faEllipsisVertical, faThumbTack } from "@fortawesome/free-solid-svg-icons";
|
||||
import { useWindowsManager } from "../../../../hooks/windows/windowsManagerContext";
|
||||
import { useContextMenu } from "../../../../hooks/modals/contextMenu.js";
|
||||
import { Actions } from "../../../actions/Actions.jsx";
|
||||
import { ClickAction } from "../../../actions/actions/ClickAction.jsx";
|
||||
import { removeFromArray } from "../../../../features/_utils/array.utils.js";
|
||||
import { useContextMenu } from "../../../../hooks/modals/contextMenu";
|
||||
import { Actions } from "../../../actions/Actions";
|
||||
import { ClickAction } from "../../../actions/actions/ClickAction";
|
||||
import { removeFromArray } from "../../../../features/_utils/array.utils";
|
||||
import { useSettingsManager } from "../../../../hooks/settings/settingsManagerContext";
|
||||
import { SettingsManager } from "../../../../features/settings/settingsManager.js";
|
||||
import { SettingsManager } from "../../../../features/settings/settingsManager";
|
||||
|
||||
export function AppOption({ app, pins, setPins }) {
|
||||
const isPinned = pins.includes(app.id);
|
||||
|
|
@ -1,23 +1,20 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { SettingsManager } from "../../../../features/settings/settingsManager.js";
|
||||
import { SettingsManager } from "../../../../features/settings/settingsManager";
|
||||
import styles from "../Settings.module.css";
|
||||
import utilStyles from "../../../../styles/utils.module.css";
|
||||
import { useVirtualRoot } from "../../../../hooks/virtual-drive/virtualRootContext";
|
||||
import { useSettingsManager } from "../../../../hooks/settings/settingsManagerContext";
|
||||
import { WALLPAPERS_PATH } from "../../../../config/apps/settings.config.js";
|
||||
import { useWindowedModal } from "../../../../hooks/modals/windowedModal.js";
|
||||
import ModalsManager from "../../../../features/modals/modalsManager.js";
|
||||
import { Button } from "../../../_utils/button/Button.jsx";
|
||||
import { FileSelector } from "../../../modals/file-selector/FileSelector.jsx";
|
||||
import { SELECTOR_MODE } from "../../../../config/apps/fileExplorer.config.js";
|
||||
import { DEFAULT_FILE_SELECTOR_SIZE } from "../../../../config/modals.config.js";
|
||||
import { IMAGE_FORMATS } from "../../../../config/apps/mediaViewer.config.js";
|
||||
import { WALLPAPERS_PATH } from "../../../../config/apps/settings.config";
|
||||
import { useWindowedModal } from "../../../../hooks/modals/windowedModal";
|
||||
import { Button } from "../../../_utils/button/Button";
|
||||
import { FileSelector } from "../../../modals/file-selector/FileSelector";
|
||||
import { SELECTOR_MODE } from "../../../../config/apps/fileExplorer.config";
|
||||
import { DEFAULT_FILE_SELECTOR_SIZE } from "../../../../config/modals.config";
|
||||
import { IMAGE_FORMATS } from "../../../../config/apps/mediaViewer.config";
|
||||
import { VirtualFile } from "../../../../features/virtual-drive/file/virtualFile";
|
||||
import { VirtualFolder } from "../../../../features/virtual-drive/folder/virtualFolder";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {ModalsManager} props.modalsManager
|
||||
*/
|
||||
export function AppearanceSettings({ modalsManager }) {
|
||||
export function AppearanceSettings() {
|
||||
const virtualRoot = useVirtualRoot();
|
||||
const settingsManager = useSettingsManager();
|
||||
const [wallpaper, setWallpaper] = useState(null);
|
||||
|
|
@ -44,7 +41,7 @@ export function AppearanceSettings({ modalsManager }) {
|
|||
Modal: (props) => <FileSelector
|
||||
type={SELECTOR_MODE.SINGLE}
|
||||
allowedFormats={IMAGE_FORMATS}
|
||||
onFinish={(file) => {
|
||||
onFinish={(file: VirtualFile) => {
|
||||
settings.set("wallpaper", file.source);
|
||||
}}
|
||||
{...props}
|
||||
|
|
@ -55,13 +52,13 @@ export function AppearanceSettings({ modalsManager }) {
|
|||
Browse
|
||||
</Button>
|
||||
<div className={styles["Input"]}>
|
||||
{virtualRoot.navigate(WALLPAPERS_PATH)?.getFiles()?.toReversed().map(({ id, source }) =>
|
||||
{(virtualRoot.navigate(WALLPAPERS_PATH) as VirtualFolder)?.getFiles()?.toReversed().map(({ id, source }) =>
|
||||
<label className={styles["Image-select"]} key={id}>
|
||||
<input
|
||||
type="radio"
|
||||
value={source}
|
||||
aria-label="Wallpaper image"
|
||||
checked={source === wallpaper ? "checked" : ""}
|
||||
checked={source === wallpaper}
|
||||
onChange={onChange}
|
||||
tabIndex={0}
|
||||
/>
|
||||
|
|
@ -1,16 +1,11 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import styles from "../Settings.module.css";
|
||||
import AppsManager from "../../../../features/apps/appsManager.js";
|
||||
import AppsManager from "../../../../features/apps/appsManager";
|
||||
import { useSettingsManager } from "../../../../hooks/settings/settingsManagerContext";
|
||||
import { SettingsManager } from "../../../../features/settings/settingsManager.js";
|
||||
import ModalsManager from "../../../../features/modals/modalsManager.js";
|
||||
import { AppOption } from "./AppOption.jsx";
|
||||
import { SettingsManager } from "../../../../features/settings/settingsManager";
|
||||
import { AppOption } from "./AppOption";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {ModalsManager} props.modalsManager
|
||||
*/
|
||||
export function AppsSettings({ modalsManager }) {
|
||||
export function AppsSettings() {
|
||||
const settingsManager = useSettingsManager();
|
||||
const [pins, setPins] = useState([]);
|
||||
|
||||
|
|
@ -26,7 +21,7 @@ export function AppsSettings({ modalsManager }) {
|
|||
{AppsManager.APPS.sort((a, b) =>
|
||||
a.name.toLowerCase().localeCompare(b.name.toLowerCase())
|
||||
).map((app) =>
|
||||
<AppOption key={app.id} app={app} modalsManager={modalsManager} pins={pins} setPins={setPins}/>
|
||||
<AppOption key={app.id} app={app} pins={pins} setPins={setPins}/>
|
||||
)}
|
||||
</div>;
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import styles from "../Settings.module.css";
|
||||
import utilStyles from "../../../../styles/utils.module.css";
|
||||
import { round } from "../../../../features/math/round.js";
|
||||
import { ProgressBar } from "../../../_utils/progress-bar/ProgressBar.jsx";
|
||||
import { Button } from "../../../_utils/button/Button.jsx";
|
||||
import { round } from "../../../../features/math/round";
|
||||
import { ProgressBar } from "../../../_utils/progress-bar/ProgressBar";
|
||||
import { Button } from "../../../_utils/button/Button";
|
||||
import { useVirtualRoot } from "../../../../hooks/virtual-drive/virtualRootContext";
|
||||
import { StorageManager } from "../../../../features/storage/storageManager.js";
|
||||
import { StorageManager } from "../../../../features/storage/storageManager";
|
||||
|
||||
export function StorageTab() {
|
||||
const virtualRoot = useVirtualRoot();
|
||||
|
|
@ -57,7 +57,7 @@ function createClass(bundle) {
|
|||
* @returns {object} returns the style object
|
||||
*/
|
||||
function createStyle(bundle) {
|
||||
const style = {};
|
||||
const style: Record<string, string> = {};
|
||||
if (bundle.bg) {
|
||||
style.backgroundColor = `rgb(${bundle.bg})`;
|
||||
}
|
||||
|
|
@ -1,17 +1,18 @@
|
|||
import { useState } from "react";
|
||||
import { CSSProperties, MutableRefObject, useState } from "react";
|
||||
import styles from "./Terminal.module.css";
|
||||
import Ansi from "./Ansi.jsx";
|
||||
import Ansi from "./Ansi";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {string} props.value
|
||||
* @param {string} props.prefix
|
||||
* @param {Function} props.onChange
|
||||
* @param {Function} props.onKeyUp
|
||||
* @param {Function} props.onKeyDown
|
||||
* @param {import("react").MutableRefObject} props.inputRef
|
||||
*/
|
||||
export function InputLine({ value, prefix, onChange, onKeyUp, onKeyDown, inputRef }) {
|
||||
|
||||
interface InputLineProps {
|
||||
value: string;
|
||||
prefix: string;
|
||||
onChange: Function;
|
||||
onKeyUp?: Function;
|
||||
onKeyDown?: Function;
|
||||
inputRef: MutableRefObject<HTMLInputElement>;
|
||||
}
|
||||
|
||||
export function InputLine({ value, prefix, onChange, onKeyUp, onKeyDown, inputRef }: InputLineProps) {
|
||||
const [cursorPosition, setCursorPosition] = useState(0);
|
||||
|
||||
const checkCursorPosition = () => {
|
||||
|
|
@ -21,7 +22,7 @@ export function InputLine({ value, prefix, onChange, onKeyUp, onKeyDown, inputRe
|
|||
return (
|
||||
<span className={styles.Input}>
|
||||
{prefix && <Ansi className={[styles.Prefix]} useClasses>{prefix}</Ansi>}
|
||||
<span className={styles["Input-container"]} style={{ "--cursor-offset": cursorPosition }}>
|
||||
<span className={styles["Input-container"]} style={{ "--cursor-offset": cursorPosition } as CSSProperties}>
|
||||
<span aria-hidden="true">{value}</span>
|
||||
<input
|
||||
id="input"
|
||||
|
|
@ -32,9 +33,9 @@ export function InputLine({ value, prefix, onChange, onKeyUp, onKeyDown, inputRe
|
|||
checkCursorPosition();
|
||||
}}
|
||||
ref={inputRef}
|
||||
onKeyUp={onKeyUp}
|
||||
onKeyUp={(event) => { onKeyUp?.(event); }}
|
||||
onKeyDown={(event) => {
|
||||
onKeyDown(event);
|
||||
onKeyDown?.(event);
|
||||
checkCursorPosition();
|
||||
}}
|
||||
onClick={checkCursorPosition}
|
||||
|
|
@ -46,7 +47,6 @@ export function InputLine({ value, prefix, onChange, onKeyUp, onKeyDown, inputRe
|
|||
spellCheck={false}
|
||||
autoComplete="off"
|
||||
autoFocus
|
||||
size=""
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import { forwardRef } from "react";
|
||||
import Ansi from "./Ansi.jsx";
|
||||
import styles from "./Terminal.module.css";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {string} props.text
|
||||
* @param {*} props.ref
|
||||
*/
|
||||
export const OutputLine = forwardRef(({ text }, ref) => {
|
||||
const lines = text?.split("\n");
|
||||
|
||||
return (<div ref={ref}>
|
||||
{lines.map((line, index) =>
|
||||
<Ansi key={index} className={styles.Output} useClasses>{line === "" ? " " : line}</Ansi>
|
||||
)}
|
||||
</div>);
|
||||
});
|
||||
17
src/components/apps/terminal/OutputLine.tsx
Normal file
17
src/components/apps/terminal/OutputLine.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { FC, forwardRef } from "react";
|
||||
import Ansi from "./Ansi";
|
||||
import styles from "./Terminal.module.css";
|
||||
|
||||
interface OutputLineProps {
|
||||
text: string;
|
||||
}
|
||||
|
||||
export const OutputLine: FC<OutputLineProps> = forwardRef<HTMLDivElement>(({ text }: OutputLineProps, ref) => {
|
||||
const lines = text?.split("\n");
|
||||
|
||||
return <div ref={ref}>
|
||||
{lines.map((line, index) =>
|
||||
<Ansi key={index} className={styles.Output} useClasses>{line === "" ? " " : line}</Ansi>
|
||||
)}
|
||||
</div>;
|
||||
}) as FC;
|
||||
|
|
@ -1,19 +1,23 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import styles from "./Terminal.module.css";
|
||||
import { useVirtualRoot } from "../../../hooks/virtual-drive/virtualRootContext";
|
||||
import { clamp } from "../../../features/math/clamp.js";
|
||||
import { OutputLine } from "./OutputLine.jsx";
|
||||
import { InputLine } from "./InputLine.jsx";
|
||||
import { ANSI, HOSTNAME, USERNAME } from "../../../config/apps/terminal.config.js";
|
||||
import CommandsManager from "../../../features/apps/terminal/commands.js";
|
||||
import { removeFromArray } from "../../../features/_utils/array.utils.js";
|
||||
import Stream from "../../../features/apps/terminal/stream.js";
|
||||
import { formatError } from "../../../features/apps/terminal/_utils/terminal.utils.js";
|
||||
import { clamp } from "../../../features/math/clamp";
|
||||
import { OutputLine } from "./OutputLine";
|
||||
import { InputLine } from "./InputLine";
|
||||
import { ANSI, HOSTNAME, USERNAME } from "../../../config/apps/terminal.config";
|
||||
import CommandsManager from "../../../features/apps/terminal/commands";
|
||||
import { removeFromArray } from "../../../features/_utils/array.utils";
|
||||
import Stream from "../../../features/apps/terminal/stream";
|
||||
import { formatError } from "../../../features/apps/terminal/_utils/terminal.utils";
|
||||
import { WindowProps } from "../../windows/WindowView";
|
||||
import { VirtualFolder } from "../../../features/virtual-drive/folder/virtualFolder";
|
||||
|
||||
/**
|
||||
* @param {import("../../windows/WindowView.jsx").windowProps} props
|
||||
*/
|
||||
export function Terminal({ startPath, input, setTitle, close: exit, active }) {
|
||||
interface TerminalProps extends WindowProps {
|
||||
startPath: string;
|
||||
input: string;
|
||||
}
|
||||
|
||||
export function Terminal({ startPath, input, setTitle, close: exit, active }: TerminalProps) {
|
||||
const [inputKey, setInputKey] = useState(0);
|
||||
const [inputValue, setInputValue] = useState(input ?? "");
|
||||
const [history, setHistory] = useState([]);
|
||||
|
|
@ -73,7 +77,7 @@ export function Terminal({ startPath, input, setTitle, close: exit, active }) {
|
|||
|
||||
let lastOutput = null;
|
||||
|
||||
stream.on(Stream.EVENT_NAMES.NEW, (text) => {
|
||||
stream.on(Stream.EVENT_NAMES.new, (text) => {
|
||||
let output = text;
|
||||
pipes.forEach((pipe) => {
|
||||
if (output instanceof Stream)
|
||||
|
|
@ -93,7 +97,7 @@ export function Terminal({ startPath, input, setTitle, close: exit, active }) {
|
|||
setStreamOutput(output);
|
||||
});
|
||||
|
||||
stream.on(Stream.EVENT_NAMES.STOP, () => {
|
||||
stream.on(Stream.EVENT_NAMES.stop, () => {
|
||||
document.removeEventListener("keydown", onKeyDown);
|
||||
|
||||
promptOutput(lastOutput);
|
||||
|
|
@ -175,7 +179,7 @@ export function Terminal({ startPath, input, setTitle, close: exit, active }) {
|
|||
promptOutput,
|
||||
pushHistory,
|
||||
virtualRoot,
|
||||
currentDirectory,
|
||||
currentDirectory: currentDirectory as VirtualFolder,
|
||||
setCurrentDirectory,
|
||||
username: USERNAME,
|
||||
hostname: HOSTNAME,
|
||||
|
|
@ -328,7 +332,6 @@ export function Terminal({ startPath, input, setTitle, close: exit, active }) {
|
|||
onKeyDown={onKeyDown}
|
||||
onChange={onChange}
|
||||
inputRef={inputRef}
|
||||
history={history}
|
||||
/>
|
||||
: <OutputLine text={streamOutput ?? ""}/>
|
||||
}
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import styles from "./TextEditor.module.css";
|
||||
import { HeaderMenu } from "../_utils/header-menu/HeaderMenu.jsx";
|
||||
import { HeaderMenu } from "../_utils/header-menu/HeaderMenu";
|
||||
import Markdown from "markdown-to-jsx";
|
||||
import { CODE_FORMATS, DEFAULT_ZOOM, EXTENSION_TO_LANGUAGE, ZOOM_FACTOR } from "../../../config/apps/textEditor.config.js";
|
||||
import AppsManager from "../../../features/apps/appsManager.js";
|
||||
import { TITLE_SEPARATOR } from "../../../config/windows.config.js";
|
||||
import { MarkdownLink } from "./overrides/MarkdownLink.jsx";
|
||||
import { MarkdownImage } from "./overrides/MarkdownImage.jsx";
|
||||
import { CODE_FORMATS, DEFAULT_ZOOM, EXTENSION_TO_LANGUAGE, ZOOM_FACTOR } from "../../../config/apps/textEditor.config";
|
||||
import AppsManager from "../../../features/apps/appsManager";
|
||||
import { TITLE_SEPARATOR } from "../../../config/windows.config";
|
||||
import { MarkdownLink } from "./overrides/MarkdownLink";
|
||||
import { MarkdownImage } from "./overrides/MarkdownImage";
|
||||
import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext";
|
||||
import SyntaxHighlighter from "react-syntax-highlighter";
|
||||
import { useWindowedModal } from "../../../hooks/modals/windowedModal.js";
|
||||
import { DEFAULT_FILE_SELECTOR_SIZE } from "../../../config/modals.config.js";
|
||||
import { FileSelector } from "../../modals/file-selector/FileSelector.jsx";
|
||||
import { SELECTOR_MODE } from "../../../config/apps/fileExplorer.config.js";
|
||||
import { useWindowedModal } from "../../../hooks/modals/windowedModal";
|
||||
import { DEFAULT_FILE_SELECTOR_SIZE } from "../../../config/modals.config";
|
||||
import { FileSelector } from "../../modals/file-selector/FileSelector";
|
||||
import { SELECTOR_MODE } from "../../../config/apps/fileExplorer.config";
|
||||
|
||||
const OVERRIDES = {
|
||||
a: MarkdownLink,
|
||||
|
|
@ -58,7 +58,7 @@ export function TextEditor({ file, setTitle, setIconUrl, close, mode, app, modal
|
|||
setContent(newContent);
|
||||
|
||||
if (ref.current) {
|
||||
ref.current.scrollTo(0, 0);
|
||||
(ref.current as HTMLElement).scrollTo(0, 0);
|
||||
}
|
||||
})();
|
||||
}, [app.id, currentFile, setIconUrl]);
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { useMemo } from "react";
|
||||
import { VirtualFile } from "../../../../features/virtual-drive/file/virtualFile.js";
|
||||
import ModalsManager from "../../../../features/modals/modalsManager.js";
|
||||
import App from "../../../../features/apps/app.js";
|
||||
import WindowsManager from "../../../../features/windows/windowsManager.js";
|
||||
import { VirtualFile } from "../../../../features/virtual-drive/file/virtualFile";
|
||||
import ModalsManager from "../../../../features/modals/modalsManager";
|
||||
import App from "../../../../features/apps/app";
|
||||
import WindowsManager from "../../../../features/windows/windowsManager";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
|
|
@ -1,17 +1,17 @@
|
|||
import { useMemo } from "react";
|
||||
import { faExternalLink } from "@fortawesome/free-solid-svg-icons";
|
||||
import { useContextMenu } from "../../../../hooks/modals/contextMenu.js";
|
||||
import { Actions } from "../../../actions/Actions.jsx";
|
||||
import { ClickAction } from "../../../actions/actions/ClickAction.jsx";
|
||||
import { VirtualFile } from "../../../../features/virtual-drive/file/virtualFile.js";
|
||||
import { useWindowedModal } from "../../../../hooks/modals/windowedModal.js";
|
||||
import AppsManager from "../../../../features/apps/appsManager.js";
|
||||
import App from "../../../../features/apps/app.js";
|
||||
import { DialogBox } from "../../../modals/dialog-box/DialogBox.jsx";
|
||||
import { DIALOG_CONTENT_TYPES } from "../../../../config/modals.config.js";
|
||||
import Vector2 from "../../../../features/math/vector2.js";
|
||||
import WindowsManager from "../../../../features/windows/windowsManager.js";
|
||||
import { APPS } from "../../../../config/apps.config.js";
|
||||
import { useContextMenu } from "../../../../hooks/modals/contextMenu";
|
||||
import { Actions } from "../../../actions/Actions";
|
||||
import { ClickAction } from "../../../actions/actions/ClickAction";
|
||||
import { VirtualFile } from "../../../../features/virtual-drive/file/virtualFile";
|
||||
import { useWindowedModal } from "../../../../hooks/modals/windowedModal";
|
||||
import AppsManager from "../../../../features/apps/appsManager";
|
||||
import App from "../../../../features/apps/app";
|
||||
import { DialogBox } from "../../../modals/dialog-box/DialogBox";
|
||||
import { DIALOG_CONTENT_TYPES } from "../../../../config/modals.config";
|
||||
import Vector2 from "../../../../features/math/vector2";
|
||||
import WindowsManager from "../../../../features/windows/windowsManager";
|
||||
import { APPS } from "../../../../config/apps.config";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
|
|
@ -1,47 +1,47 @@
|
|||
import { memo, useState } from "react";
|
||||
import { SettingsManager } from "../../features/settings/settingsManager.js";
|
||||
import { SettingsManager } from "../../features/settings/settingsManager";
|
||||
import { useSettingsManager } from "../../hooks/settings/settingsManagerContext";
|
||||
import styles from "./Desktop.module.css";
|
||||
import { useEffect } from "react";
|
||||
import { useWindowsManager } from "../../hooks/windows/windowsManagerContext";
|
||||
import { useContextMenu } from "../../hooks/modals/contextMenu.js";
|
||||
import { FALLBACK_ICON_SIZE, FALLBACK_WALLPAPER } from "../../config/desktop.config.js";
|
||||
import { reloadViewport } from "../../features/_utils/browser.utils.js";
|
||||
import { useContextMenu } from "../../hooks/modals/contextMenu";
|
||||
import { FALLBACK_ICON_SIZE, FALLBACK_WALLPAPER } from "../../config/desktop.config";
|
||||
import { reloadViewport } from "../../features/_utils/browser.utils";
|
||||
import { useVirtualRoot } from "../../hooks/virtual-drive/virtualRootContext";
|
||||
import { DirectoryList } from "../apps/file-explorer/directory-list/DirectoryList.jsx";
|
||||
import { APPS, APP_ICONS, APP_NAMES } from "../../config/apps.config.js";
|
||||
import Vector2 from "../../features/math/vector2.js";
|
||||
import { Actions } from "../actions/Actions.jsx";
|
||||
import { ClickAction } from "../actions/actions/ClickAction.jsx";
|
||||
import { DirectoryList } from "../apps/file-explorer/directory-list/DirectoryList";
|
||||
import { APPS, APP_ICONS, APP_NAMES } from "../../config/apps.config";
|
||||
import Vector2 from "../../features/math/vector2";
|
||||
import { Actions } from "../actions/Actions";
|
||||
import { ClickAction } from "../actions/actions/ClickAction";
|
||||
import { faArrowsRotate, faEye, faFolder, faPaintBrush, faTerminal, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||
import { ToggleAction } from "../actions/actions/ToggleAction.jsx";
|
||||
import { DropdownAction } from "../actions/actions/DropdownAction.jsx";
|
||||
import { RadioAction } from "../actions/actions/RadioAction.jsx";
|
||||
import { Divider } from "../actions/actions/Divider.jsx";
|
||||
import { isValidInteger } from "../../features/_utils/number.utils.js";
|
||||
import { useWindowedModal } from "../../hooks/modals/windowedModal.js";
|
||||
import { Share } from "../modals/share/Share.jsx";
|
||||
import { ToggleAction } from "../actions/actions/ToggleAction";
|
||||
import { DropdownAction } from "../actions/actions/DropdownAction";
|
||||
import { RadioAction } from "../actions/actions/RadioAction";
|
||||
import { Divider } from "../actions/actions/Divider";
|
||||
import { isValidInteger } from "../../features/_utils/number.utils";
|
||||
import { useWindowedModal } from "../../hooks/modals/windowedModal";
|
||||
import { Share } from "../modals/share/Share";
|
||||
import ModalsManager from "../../features/modals/modalsManager";
|
||||
import { useModalsManager } from "../../hooks/modals/modalsManagerContext";
|
||||
import { VirtualFolder } from "../../features/virtual-drive/folder/virtualFolder";
|
||||
import { VirtualFolderLink } from "../../features/virtual-drive/folder/virtualFolderLink";
|
||||
|
||||
export const Desktop = memo(() => {
|
||||
const settingsManager = useSettingsManager();
|
||||
const [wallpaper, setWallpaper] = useState(null);
|
||||
const modalsManager = useModalsManager();
|
||||
const windowsManager = useWindowsManager();
|
||||
const virtualRoot = useVirtualRoot();
|
||||
const [showIcons, setShowIcons] = useState(false);
|
||||
const [iconSize, setIconSize] = useState(FALLBACK_ICON_SIZE);
|
||||
const { openWindowedModal } = useWindowedModal({ modalsManager });
|
||||
const { openWindowedModal } = useWindowedModal();
|
||||
|
||||
const directory = virtualRoot.navigate("~/Desktop");
|
||||
|
||||
const { onContextMenu, ShortcutsListener } = useContextMenu({ modalsManager, Actions: (props) =>
|
||||
const { onContextMenu, ShortcutsListener } = useContextMenu({ Actions: (props) =>
|
||||
<Actions {...props}>
|
||||
<DropdownAction label="View" icon={faEye}>
|
||||
<RadioAction initialIndex={iconSize} onTrigger={(event, params, value) => {
|
||||
<RadioAction initialIndex={iconSize} onTrigger={(event, params, value: string) => {
|
||||
const settings = settingsManager.get(SettingsManager.VIRTUAL_PATHS.desktop);
|
||||
settings.set("icon-size", parseInt(value));
|
||||
settings.set("icon-size", value);
|
||||
}} options={[
|
||||
{ label: "Small icons" },
|
||||
{ label: "Medium icons" },
|
||||
|
|
@ -75,7 +75,7 @@ export const Desktop = memo(() => {
|
|||
}}/>
|
||||
</Actions>
|
||||
});
|
||||
const { onContextMenu: onContextMenuFile } = useContextMenu({ modalsManager, Actions: (props) =>
|
||||
const { onContextMenu: onContextMenuFile } = useContextMenu({ Actions: (props) =>
|
||||
<Actions {...props}>
|
||||
<ClickAction label="Open" onTrigger={(event, file) => {
|
||||
file.open(windowsManager);
|
||||
|
|
@ -88,7 +88,7 @@ export const Desktop = memo(() => {
|
|||
}}/>
|
||||
</Actions>
|
||||
});
|
||||
const { onContextMenu: onContextMenuFolder } = useContextMenu({ modalsManager, Actions: (props) =>
|
||||
const { onContextMenu: onContextMenuFolder } = useContextMenu({ Actions: (props) =>
|
||||
<Actions {...props}>
|
||||
<ClickAction label="Open" onTrigger={(event, folder) => {
|
||||
folder.open(windowsManager);
|
||||
|
|
@ -138,7 +138,7 @@ export const Desktop = memo(() => {
|
|||
onContextMenu={onContextMenu}
|
||||
>
|
||||
{showIcons && <DirectoryList
|
||||
directory={directory}
|
||||
directory={directory as VirtualFolder}
|
||||
className={styles.Content}
|
||||
style={{
|
||||
"--scale": `${iconScale}rem`
|
||||
|
|
@ -146,9 +146,9 @@ export const Desktop = memo(() => {
|
|||
fileClassName={styles["Item"]}
|
||||
folderClassName={styles["Item"]}
|
||||
onOpenFile={(event, file) => {
|
||||
event.preventDefault();
|
||||
(event as Event).preventDefault();
|
||||
|
||||
const options = {};
|
||||
const options: Record<string, any> = {};
|
||||
if (file.name === "info.md")
|
||||
options.size = new Vector2(575, 675);
|
||||
if (file.extension === "md")
|
||||
|
|
@ -156,7 +156,7 @@ export const Desktop = memo(() => {
|
|||
|
||||
windowsManager.openFile(file, options);
|
||||
}}
|
||||
onOpenFolder={(event, { linkedPath, path }) => {
|
||||
onOpenFolder={(event, { linkedPath, path }: VirtualFolderLink & VirtualFolder) => {
|
||||
windowsManager.open(APPS.FILE_EXPLORER, { startPath: linkedPath ?? path });
|
||||
}}
|
||||
onContextMenuFile={onContextMenuFile}
|
||||
|
|
@ -1,22 +1,25 @@
|
|||
import { memo } from "react";
|
||||
import { Modal as ModalType } from "../../features/modals/modal.js";
|
||||
import OutsideClickListener from "../../hooks/_utils/outsideClick.js";
|
||||
import { CSSProperties, FC, memo, ReactNode } from "react";
|
||||
import Modal from "../../features/modals/modal";
|
||||
import OutsideClickListener from "../../hooks/_utils/outsideClick";
|
||||
import styles from "./ModalView.module.css";
|
||||
import { useEffect } from "react";
|
||||
|
||||
/**
|
||||
* @typedef {object} modalProps
|
||||
* @param {object} props
|
||||
* @param {ModalType} props.modal
|
||||
* @param {Modal} props.modal
|
||||
* @param {*} props.params
|
||||
* @param {Function} props.onFinish
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {object} root
|
||||
* @param {ModalType} root.modal
|
||||
*/
|
||||
export const ModalView = memo(({ modal }) => {
|
||||
export interface ModalProps {
|
||||
modal: Modal;
|
||||
params?: Record<string, any>;
|
||||
children?: ReactNode;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export const ModalView: FC<ModalProps> = memo(({ modal }) => {
|
||||
useEffect(() => {
|
||||
const onDismiss = (event) => {
|
||||
if (event.key === "Escape")
|
||||
|
|
@ -32,7 +35,7 @@ export const ModalView = memo(({ modal }) => {
|
|||
|
||||
const Container = () => (<div
|
||||
className={styles.Container}
|
||||
style={{ "--position-x": modal.position.x, "--position-y": modal.position.y }}
|
||||
style={{ "--position-x": modal.position.x, "--position-y": modal.position.y } as CSSProperties}
|
||||
>
|
||||
<modal.element modal={modal} {...modal.props}/>
|
||||
</div>);
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { memo, useEffect, useRef, useState } from "react";
|
||||
import { ModalView } from "./ModalView.jsx";
|
||||
import { ModalView } from "./ModalView";
|
||||
import styles from "./ModalsView.module.css";
|
||||
import { useModals } from "../../hooks/modals/modalsContext";
|
||||
import { useModalsManager } from "../../hooks/modals/modalsManagerContext";
|
||||
|
|
@ -1,13 +1,14 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import { useScreenDimensions } from "../../../hooks/_utils/screen.js";
|
||||
import Vector2 from "../../../features/math/vector2.js";
|
||||
import { useScreenDimensions } from "../../../hooks/_utils/screen";
|
||||
import Vector2 from "../../../features/math/vector2";
|
||||
import styles from "./WindowedModal.module.css";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import Draggable from "react-draggable";
|
||||
import { ReactSVG } from "react-svg";
|
||||
import utilStyles from "../../../styles/utils.module.css";
|
||||
import Modal from "../../../features/modals/modal.js";
|
||||
import Modal from "../../../features/modals/modal";
|
||||
import { ModalProps } from "../ModalView";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
|
|
@ -15,7 +16,7 @@ import Modal from "../../../features/modals/modal.js";
|
|||
* @param {*} props.params
|
||||
* @param {*} props.children
|
||||
*/
|
||||
export function WindowedModal({ modal, params, children, ...props }) {
|
||||
export function WindowedModal({ modal, params, children, ...props }: ModalProps) {
|
||||
const { iconUrl, title } = params;
|
||||
|
||||
const nodeRef = useRef(null);
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
import { DIALOG_CONTENT_TYPES } from "../../../config/modals.config.js";
|
||||
import { WindowedModal } from "../_utils/WindowedModal.jsx";
|
||||
import { DIALOG_CONTENT_TYPES } from "../../../config/modals.config";
|
||||
import { WindowedModal } from "../_utils/WindowedModal";
|
||||
import { ModalProps } from "../ModalView";
|
||||
import styles from "./DialogBox.module.css";
|
||||
|
||||
export function DialogBox({ modal, params, children, ...props }) {
|
||||
const onClick = (event) => {
|
||||
export function DialogBox({ modal, params, children, ...props }: ModalProps) {
|
||||
const onClick = (event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
const type = parseInt(event.target.getAttribute("data-type"));
|
||||
const type = parseInt((event.target as HTMLElement).getAttribute("data-type"));
|
||||
|
||||
switch (type) {
|
||||
case DIALOG_CONTENT_TYPES.CloseButton:
|
||||
|
|
@ -1,19 +1,17 @@
|
|||
import { ImagePreview } from "../../apps/file-explorer/directory-list/ImagePreview.jsx";
|
||||
import { WindowedModal } from "../_utils/WindowedModal.jsx";
|
||||
import { ImagePreview } from "../../apps/file-explorer/directory-list/ImagePreview";
|
||||
import { WindowedModal } from "../_utils/WindowedModal";
|
||||
import styles from "./FileProperties.module.css";
|
||||
import utilStyles from "../../../styles/utils.module.css";
|
||||
import Modal from "../../../features/modals/modal.js";
|
||||
import { StorageManager } from "../../../features/storage/storageManager";
|
||||
import AppsManager from "../../../features/apps/appsManager";
|
||||
import { ModalProps } from "../ModalView.js";
|
||||
import { VirtualFile } from "../../../features/virtual-drive/file/virtualFile.js";
|
||||
import { StorageManager } from "../../../features/storage/storageManager.js";
|
||||
import AppsManager from "../../../features/apps/appsManager.js";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {Modal} props.modal
|
||||
* @param {object} props.params
|
||||
* @param {VirtualFile} props.file
|
||||
*/
|
||||
export function FileProperties({ modal, params, file, ...props }) {
|
||||
interface FilePropetiesProps extends ModalProps {
|
||||
file: VirtualFile;
|
||||
}
|
||||
|
||||
export function FileProperties({ modal, params, file, ...props }: FilePropetiesProps) {
|
||||
const associatedApp = AppsManager.getAppByFileExtension(file.extension);
|
||||
|
||||
return <WindowedModal className={styles.Container} modal={modal} params={params} {...props}>
|
||||
|
|
@ -1,31 +1,22 @@
|
|||
import { useState } from "react";
|
||||
import { APP_ICONS } from "../../../config/apps.config.js";
|
||||
import { SELECTOR_MODE } from "../../../config/apps/fileExplorer.config.js";
|
||||
import { Button } from "../../_utils/button/Button.jsx";
|
||||
import { FileExplorer } from "../../apps/file-explorer/FileExplorer.jsx";
|
||||
import { WindowedModal } from "../_utils/WindowedModal.jsx";
|
||||
import { APP_ICONS } from "../../../config/apps.config";
|
||||
import { SELECTOR_MODE } from "../../../config/apps/fileExplorer.config";
|
||||
import { Button } from "../../_utils/button/Button";
|
||||
import { FileExplorer } from "../../apps/file-explorer/FileExplorer";
|
||||
import { WindowedModal } from "../_utils/WindowedModal";
|
||||
import styles from "./FileSelector.module.css";
|
||||
import { VirtualFile } from "../../../features/virtual-drive/file/virtualFile.js";
|
||||
import Modal from "../../../features/modals/modal.js";
|
||||
import { useModalsManager } from "../../../hooks/modals/modalsManagerContext";
|
||||
import { VirtualFile } from "../../../features/virtual-drive/file/virtualFile";
|
||||
import { ModalProps } from "../ModalView";
|
||||
|
||||
/**
|
||||
* @callback onFinish
|
||||
* @param {VirtualFile|VirtualFile[]} result
|
||||
*/
|
||||
interface FileSelectorProps extends ModalProps {
|
||||
type: number;
|
||||
allowedFormats?: string[];
|
||||
onFinish: (result: VirtualFile | VirtualFile[]) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {Modal} props.modal
|
||||
* @param {*} props.params
|
||||
* @param {number} props.type
|
||||
* @param {string[]=} props.allowedFormats
|
||||
* @param {onFinish} props.onFinish
|
||||
*/
|
||||
export function FileSelector({ modal, params, type, allowedFormats, onFinish, ...props }) {
|
||||
export function FileSelector({ modal, params, type, allowedFormats, onFinish, ...props }: FileSelectorProps) {
|
||||
const multi = (type === SELECTOR_MODE.MULTIPLE);
|
||||
|
||||
const modalsManager = useModalsManager();
|
||||
const [selection, setSelection] = useState(multi ? [] : null);
|
||||
const [directory, setDirectory] = useState(null);
|
||||
|
||||
|
|
@ -58,7 +49,6 @@ export function FileSelector({ modal, params, type, allowedFormats, onFinish, ..
|
|||
...params,
|
||||
}} {...props}>
|
||||
<FileExplorer
|
||||
modalsManager={modalsManager}
|
||||
selectorMode={type}
|
||||
Footer={() =>
|
||||
<div className={styles.Footer}>
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import ModalsManager from "../../../features/modals/modalsManager.js";
|
||||
import { WindowedModal } from "../_utils/WindowedModal.jsx";
|
||||
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.js";
|
||||
import AppsManager from "../../../features/apps/appsManager.js";
|
||||
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.jsx";
|
||||
import Option from "./Option.jsx";
|
||||
import { Button } from "../../_utils/button/Button";
|
||||
import Option from "./Option";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faSquare } from "@fortawesome/free-regular-svg-icons";
|
||||
import { faSquareCheck } from "@fortawesome/free-solid-svg-icons";
|
||||
import { useAlert } from "../../../hooks/modals/alert.js";
|
||||
import { useAlert } from "../../../hooks/modals/alert";
|
||||
|
||||
const APP_OPTIONS = {
|
||||
"terminal": [
|
||||
|
|
@ -39,7 +39,7 @@ export function Share({ modal, params, ...props }) {
|
|||
const [fullscreen, setFullscreen] = useState(params.fullscreen ?? false);
|
||||
const [options, setOptions] = useState({});
|
||||
const [url, setUrl] = useState(null);
|
||||
const { alert } = useAlert({ modalsManager: modal.modalsManager });
|
||||
const { alert } = useAlert();
|
||||
|
||||
useEffect(() => {
|
||||
setUrl(generateUrl({
|
||||
|
|
@ -1,30 +1,29 @@
|
|||
import { memo, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { CSSProperties, memo, useEffect, useMemo, useRef, useState } from "react";
|
||||
import styles from "./Taskbar.module.css";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faCog, faSearch } from "@fortawesome/free-solid-svg-icons";
|
||||
import AppsManager from "../../features/apps/appsManager.js";
|
||||
import AppsManager from "../../features/apps/appsManager";
|
||||
import { ReactSVG } from "react-svg";
|
||||
import { HomeMenu } from "./menus/HomeMenu.jsx";
|
||||
import OutsideClickListener from "../../hooks/_utils/outsideClick.js";
|
||||
import { Battery } from "./indicators/Battery.jsx";
|
||||
import { Network } from "./indicators/Network.jsx";
|
||||
import { Volume } from "./indicators/Volume.jsx";
|
||||
import { SearchMenu } from "./menus/SearchMenu.jsx";
|
||||
import { Calendar } from "./indicators/Calendar.jsx";
|
||||
import { useScrollWithShadow } from "../../hooks/_utils/scrollWithShadows.js";
|
||||
import { AppButton } from "./app-icon/AppIcon.jsx";
|
||||
import { useContextMenu } from "../../hooks/modals/contextMenu.js";
|
||||
import { Actions } from "../actions/Actions.jsx";
|
||||
import { ClickAction } from "../actions/actions/ClickAction.jsx";
|
||||
import { APPS, APP_NAMES } from "../../config/apps.config.js";
|
||||
import { HomeMenu } from "./menus/HomeMenu";
|
||||
import OutsideClickListener from "../../hooks/_utils/outsideClick";
|
||||
import { Battery } from "./indicators/Battery";
|
||||
import { Network } from "./indicators/Network";
|
||||
import { Volume } from "./indicators/Volume";
|
||||
import { SearchMenu } from "./menus/SearchMenu";
|
||||
import { Calendar } from "./indicators/Calendar";
|
||||
import { useScrollWithShadow } from "../../hooks/_utils/scrollWithShadows";
|
||||
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 "../../config/apps.config";
|
||||
import { useWindowsManager } from "../../hooks/windows/windowsManagerContext";
|
||||
import { TASKBAR_HEIGHT } from "../../config/taskbar.config.js";
|
||||
import { TASKBAR_HEIGHT } from "../../config/taskbar.config";
|
||||
import { useSettingsManager } from "../../hooks/settings/settingsManagerContext";
|
||||
import { SettingsManager } from "../../features/settings/settingsManager.js";
|
||||
import { SettingsManager } from "../../features/settings/settingsManager";
|
||||
import { useWindows } from "../../hooks/windows/windowsContext";
|
||||
import { ZIndexManager } from "../../features/z-index/zIndexManager.js";
|
||||
import { useZIndex } from "../../hooks/z-index/zIndex.js";
|
||||
import { useModalsManager } from "../../hooks/modals/modalsManagerContext";
|
||||
import { ZIndexManager } from "../../features/z-index/zIndexManager";
|
||||
import { useZIndex } from "../../hooks/z-index/zIndex";
|
||||
|
||||
export const Taskbar = memo(() => {
|
||||
const ref = useRef(null);
|
||||
|
|
@ -40,10 +39,9 @@ export const Taskbar = memo(() => {
|
|||
color: { a: 25 }
|
||||
} });
|
||||
const inputRef = useRef(null);
|
||||
const modalsManager = useModalsManager();
|
||||
const windowsManager = useWindowsManager();
|
||||
const windows = useWindows();
|
||||
const { onContextMenu } = useContextMenu({ modalsManager, Actions: (props) =>
|
||||
const { onContextMenu } = useContextMenu({ Actions: (props) =>
|
||||
<Actions avoidTaskbar={false} {...props}>
|
||||
<ClickAction label={`Open ${APP_NAMES.SETTINGS}`} icon={faCog} onTrigger={() => {
|
||||
windowsManager.open(APPS.SETTINGS);
|
||||
|
|
@ -70,14 +68,13 @@ export const Taskbar = memo(() => {
|
|||
const shouldBeShown = (pins.includes(app.id) || isActive);
|
||||
return (<AppButton
|
||||
windowsManager={windowsManager}
|
||||
modalsManager={modalsManager}
|
||||
pins={pins}
|
||||
app={app}
|
||||
key={app.id}
|
||||
active={isActive}
|
||||
visible={shouldBeShown}
|
||||
/>);
|
||||
}), [modalsManager, pins, windows, windowsManager]);
|
||||
}), [pins, windows, windowsManager]);
|
||||
|
||||
useEffect(() => {
|
||||
const settings = settingsManager.get(SettingsManager.VIRTUAL_PATHS.taskbar);
|
||||
|
|
@ -131,11 +128,11 @@ export const Taskbar = memo(() => {
|
|||
|
||||
return (<>
|
||||
<div
|
||||
style={{ "--taskbar-height": `${TASKBAR_HEIGHT}px`, zIndex }}
|
||||
style={{ "--taskbar-height": `${TASKBAR_HEIGHT}px`, zIndex } as CSSProperties}
|
||||
className={styles["Taskbar"]}
|
||||
data-allow-context-menu={true}
|
||||
onContextMenu={(event) => {
|
||||
if (event.target.getAttribute("data-allow-context-menu"))
|
||||
if ((event.target as HTMLElement).getAttribute("data-allow-context-menu"))
|
||||
onContextMenu(event);
|
||||
}}
|
||||
>
|
||||
|
|
@ -193,4 +190,4 @@ export const Taskbar = memo(() => {
|
|||
</div>
|
||||
</div>
|
||||
</>);
|
||||
}, []);
|
||||
});
|
||||
|
|
@ -1,29 +1,30 @@
|
|||
import { memo } from "react";
|
||||
import App from "../../../features/apps/app.js";
|
||||
import { FC, memo } from "react";
|
||||
import App from "../../../features/apps/app";
|
||||
import styles from "./AppIcon.module.css";
|
||||
import { ReactSVG } from "react-svg";
|
||||
import { useSettingsManager } from "../../../hooks/settings/settingsManagerContext";
|
||||
import { useContextMenu } from "../../../hooks/modals/contextMenu.js";
|
||||
import ModalsManager from "../../../features/modals/modalsManager.js";
|
||||
import { Actions } from "../../actions/Actions.jsx";
|
||||
import { ClickAction } from "../../actions/actions/ClickAction.jsx";
|
||||
import { useContextMenu } from "../../../hooks/modals/contextMenu";
|
||||
import { Actions } from "../../actions/Actions";
|
||||
import { ClickAction } from "../../actions/actions/ClickAction";
|
||||
import { faThumbTack, faTimes } from "@fortawesome/free-solid-svg-icons";
|
||||
import { SettingsManager } from "../../../features/settings/settingsManager.js";
|
||||
import { removeFromArray } from "../../../features/_utils/array.utils.js";
|
||||
import AppsManager from "../../../features/apps/appsManager.js";
|
||||
import { SettingsManager } from "../../../features/settings/settingsManager";
|
||||
import { removeFromArray } from "../../../features/_utils/array.utils";
|
||||
import AppsManager from "../../../features/apps/appsManager";
|
||||
import WindowsManager from "../../../features/windows/windowsManager";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {App} props.app
|
||||
* @param {ModalsManager} props.modalsManager
|
||||
* @param {string[]} props.pins
|
||||
* @param {boolean} props.focused
|
||||
*/
|
||||
export const AppButton = memo(({ app, windowsManager, modalsManager, pins, active, visible }) => {
|
||||
interface AppButtonProps {
|
||||
app: App;
|
||||
windowsManager: WindowsManager;
|
||||
pins: string[];
|
||||
active: boolean;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
export const AppButton: FC<AppButtonProps> = memo(({ app, windowsManager, pins, active, visible }: AppButtonProps) => {
|
||||
const isPinned = pins.includes(app.id);
|
||||
|
||||
const settingsManager = useSettingsManager();
|
||||
const { onContextMenu } = useContextMenu({ modalsManager, Actions: (props) =>
|
||||
const { onContextMenu } = useContextMenu({ Actions: (props) =>
|
||||
<Actions avoidTaskbar={false} {...props}>
|
||||
<ClickAction label={app.name} icon={AppsManager.getAppIconUrl(app.id)} onTrigger={() => {
|
||||
windowsManager.open(app.id);
|
||||
|
|
@ -2,15 +2,15 @@ import { faBatteryEmpty, faBatteryFull, faBatteryHalf, faBatteryQuarter, faBatte
|
|||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useEffect, useState } from "react";
|
||||
import styles from "./Battery.module.css";
|
||||
import { UtilMenu } from "../menus/UtilMenu.jsx";
|
||||
import OutsideClickListener from "../../../hooks/_utils/outsideClick.js";
|
||||
import { UtilMenu } from "../menus/UtilMenu";
|
||||
import OutsideClickListener from "../../../hooks/_utils/outsideClick";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {boolean} props.hideUtilMenus
|
||||
* @param {Function} props.showUtilMenu
|
||||
*/
|
||||
export function Battery({ hideUtilMenus, showUtilMenu }) {
|
||||
interface BatteryProps {
|
||||
hideUtilMenus: boolean;
|
||||
showUtilMenu: Function;
|
||||
}
|
||||
|
||||
export function Battery({ hideUtilMenus, showUtilMenu }: BatteryProps) {
|
||||
const [isCharging, setIsCharging] = useState(true);
|
||||
const [percentage, setPercentage] = useState(100);
|
||||
const [showMenu, setShowMenu] = useState(false);
|
||||
|
|
@ -18,7 +18,7 @@ export function Battery({ hideUtilMenus, showUtilMenu }) {
|
|||
// const [dischargingTime, setDischargingTime] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
navigator.getBattery?.()?.then((battery) => {
|
||||
(navigator as any).getBattery?.()?.then((battery) => {
|
||||
const updateIsCharging = () => {
|
||||
setIsCharging(battery.charging);
|
||||
};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import styles from "./Calendar.module.css";
|
||||
import OutsideClickListener from "../../../hooks/_utils/outsideClick.js";
|
||||
import { UtilMenu } from "../menus/UtilMenu.jsx";
|
||||
import OutsideClickListener from "../../../hooks/_utils/outsideClick";
|
||||
import { UtilMenu } from "../menus/UtilMenu";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { faWifi } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useEffect, useState } from "react";
|
||||
import OutsideClickListener from "../../../hooks/_utils/outsideClick.js";
|
||||
import { UtilMenu } from "../menus/UtilMenu.jsx";
|
||||
import OutsideClickListener from "../../../hooks/_utils/outsideClick";
|
||||
import { UtilMenu } from "../menus/UtilMenu";
|
||||
import styles from "./Network.module.css";
|
||||
|
||||
/**
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { faVolumeHigh } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useEffect, useState } from "react";
|
||||
import OutsideClickListener from "../../../hooks/_utils/outsideClick.js";
|
||||
import { UtilMenu } from "../menus/UtilMenu.jsx";
|
||||
import OutsideClickListener from "../../../hooks/_utils/outsideClick";
|
||||
import { UtilMenu } from "../menus/UtilMenu";
|
||||
import styles from "./Volume.module.css";
|
||||
|
||||
/**
|
||||
|
|
@ -3,16 +3,16 @@ import styles from "./HomeMenu.module.css";
|
|||
import appStyles from "./AppList.module.css";
|
||||
import { faCircleInfo, faFileLines, faGear, faImage, faPowerOff } from "@fortawesome/free-solid-svg-icons";
|
||||
import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext";
|
||||
import AppsManager from "../../../features/apps/appsManager.js";
|
||||
import AppsManager from "../../../features/apps/appsManager";
|
||||
import { ReactSVG } from "react-svg";
|
||||
import { closeViewport } from "../../../features/_utils/browser.utils.js";
|
||||
import { useKeyboardListener } from "../../../hooks/_utils/keyboard.js";
|
||||
import { closeViewport } from "../../../features/_utils/browser.utils";
|
||||
import { useKeyboardListener } from "../../../hooks/_utils/keyboard";
|
||||
import { useVirtualRoot } from "../../../hooks/virtual-drive/virtualRootContext";
|
||||
import { useEffect, useState } from "react";
|
||||
import Vector2 from "../../../features/math/vector2.js";
|
||||
import Vector2 from "../../../features/math/vector2";
|
||||
import utilStyles from "../../../styles/utils.module.css";
|
||||
import { APPS } from "../../../config/apps.config.js";
|
||||
import { NAME } from "../../../config/branding.config.js";
|
||||
import { APPS } from "../../../config/apps.config";
|
||||
import { NAME } from "../../../config/branding.config";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import styles from "./SearchMenu.module.css";
|
||||
import appStyles from "./AppList.module.css";
|
||||
import AppsManager from "../../../features/apps/appsManager.js";
|
||||
import AppsManager from "../../../features/apps/appsManager";
|
||||
import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext";
|
||||
import { ReactSVG } from "react-svg";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useKeyboardListener } from "../../../hooks/_utils/keyboard.js";
|
||||
import { useKeyboardListener } from "../../../hooks/_utils/keyboard";
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { useModalsManager } from "../../hooks/modals/modalsManagerContext";
|
||||
import { useAlert } from "../../hooks/modals/alert.js";
|
||||
import AppsManager from "../../features/apps/appsManager.js";
|
||||
import Vector2 from "../../features/math/vector2.js";
|
||||
|
||||
export default function WindowFallbackView({ error, resetErrorBoundary, app, closeWindow }) {
|
||||
const modalsManager = useModalsManager();
|
||||
const { alert } = useAlert({ modalsManager });
|
||||
const [alerted, setAlerted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (alerted)
|
||||
return;
|
||||
|
||||
setAlerted(true);
|
||||
closeWindow();
|
||||
alert({
|
||||
title: `${app.name} has stopped working`,
|
||||
text: `${error.name}: ${error.message}`,
|
||||
iconUrl: AppsManager.getAppIconUrl(app.id),
|
||||
size: new Vector2(350, 150),
|
||||
single: true
|
||||
});
|
||||
}, [alerted, alert, app.id, app.name, error.message, closeWindow, error.name]);
|
||||
|
||||
return;
|
||||
}
|
||||
35
src/components/windows/WindowFallbackView.tsx
Normal file
35
src/components/windows/WindowFallbackView.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { ReactNode, 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";
|
||||
|
||||
export interface WindowFallbackViewProps {
|
||||
error?: Error;
|
||||
resetErrorBoundary?: any;
|
||||
app?: App;
|
||||
closeWindow?: Function
|
||||
}
|
||||
|
||||
// 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 default function WindowFallbackView({ error, resetErrorBoundary, app, closeWindow }: WindowFallbackViewProps): ReactNode {
|
||||
const { alert } = useAlert();
|
||||
const [alerted, setAlerted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (alerted)
|
||||
return;
|
||||
|
||||
setAlerted(true);
|
||||
closeWindow();
|
||||
alert({
|
||||
title: `${app.name} has stopped working`,
|
||||
text: `${error.name}: ${error.message}`,
|
||||
iconUrl: AppsManager.getAppIconUrl(app.id),
|
||||
size: new Vector2(350, 150),
|
||||
single: true
|
||||
});
|
||||
}, [alerted, alert, app.id, app.name, error.message, closeWindow, error.name]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -4,52 +4,41 @@ import { faExpand, faMinus, faWindowMaximize as fasWindowMaximize, faTimes, faXm
|
|||
import { ReactSVG } from "react-svg";
|
||||
import { useWindowsManager } from "../../hooks/windows/windowsManagerContext";
|
||||
import Draggable from "react-draggable";
|
||||
import { memo, useEffect, useRef, useState } from "react";
|
||||
import App from "../../features/apps/app.js";
|
||||
import Vector2 from "../../features/math/vector2.js";
|
||||
import { FC, memo, MouseEventHandler, useEffect, useRef, useState } from "react";
|
||||
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.js";
|
||||
import AppsManager from "../../features/apps/appsManager.js";
|
||||
import { ClickAction } from "../actions/actions/ClickAction.jsx";
|
||||
import { Actions } from "../actions/Actions.jsx";
|
||||
import { useScreenDimensions } from "../../hooks/_utils/screen.js";
|
||||
import { NAME } from "../../config/branding.config.js";
|
||||
import { setViewportIcon, setViewportTitle } from "../../features/_utils/browser.utils.js";
|
||||
import { ZIndexManager } from "../../features/z-index/zIndexManager.js";
|
||||
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";
|
||||
import { NAME } from "../../config/branding.config";
|
||||
import { setViewportIcon, setViewportTitle } from "../../features/_utils/browser.utils";
|
||||
import { ZIndexManager } from "../../features/z-index/zIndexManager";
|
||||
import { useZIndex } from "../../hooks/z-index/zIndex";
|
||||
import { useWindowedModal } from "../../hooks/modals/windowedModal.js";
|
||||
import { Divider } from "../actions/actions/Divider.jsx";
|
||||
import ModalsManager from "../../features/modals/modalsManager.js";
|
||||
import { Share } from "../modals/share/Share.jsx";
|
||||
import { useWindowedModal } from "../../hooks/modals/windowedModal";
|
||||
import { Divider } from "../actions/actions/Divider";
|
||||
import ModalsManager from "../../features/modals/modalsManager";
|
||||
import { Share } from "../modals/share/Share";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import WindowFallbackView from "./WindowFallbackView.jsx";
|
||||
import WindowFallbackView from "./WindowFallbackView";
|
||||
import { WindowOptions } from "../../features/windows/windowsManager";
|
||||
|
||||
/**
|
||||
* @typedef {object} windowProps
|
||||
* @property {App} app
|
||||
* @property {Function} setTitle
|
||||
* @property {Function} setIconUrl
|
||||
* @property {Function} close
|
||||
* @property {Function} focus
|
||||
* @property {boolean} active
|
||||
* @property {import("../../features/modals/modalsManager.js").default} modalsManager
|
||||
*/
|
||||
export interface WindowProps extends WindowOptions {
|
||||
fullscreen?: boolean;
|
||||
onInteract?: Function
|
||||
setTitle?: Function;
|
||||
setIconUrl?: Function;
|
||||
close?: Function;
|
||||
focus?: Function;
|
||||
active?: boolean;
|
||||
minimized?: boolean;
|
||||
toggleMinimized?: Function;
|
||||
index?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {string} props.id
|
||||
* @param {App} props.app
|
||||
* @param {Vector2} props.size
|
||||
* @param {Vector2} props.position
|
||||
* @param {boolean} props.focused
|
||||
* @param {Function} props.onInteract
|
||||
* @param {object} props.options
|
||||
* @param {boolean} props.active
|
||||
* @param {boolean} props.minimized
|
||||
* @param {Function} props.toggleMinimized
|
||||
*/
|
||||
export const WindowView = 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 windowsManager = useWindowsManager();
|
||||
const nodeRef = useRef(null);
|
||||
const { openWindowedModal } = useWindowedModal();
|
||||
|
|
@ -64,7 +53,7 @@ export const WindowView = memo(({ id, app, size, position, onInteract, options,
|
|||
|
||||
const { onContextMenu, ShortcutsListener } = useContextMenu({ Actions: (props) =>
|
||||
<Actions {...props}>
|
||||
<ClickAction label="Minimize" icon={faMinus} onTrigger={toggleMinimized}/>
|
||||
<ClickAction label="Minimize" icon={faMinus} onTrigger={() => { toggleMinimized(); }}/>
|
||||
<ClickAction label="Maximize" icon={faExpand} shortcut={["F11"]} onTrigger={() => {
|
||||
setMaximized(!maximized);
|
||||
}}/>
|
||||
|
|
@ -117,7 +106,7 @@ export const WindowView = memo(({ id, app, size, position, onInteract, options,
|
|||
};
|
||||
}, [active, minimized, iconUrl, title]);
|
||||
|
||||
const close = (event) => {
|
||||
const close = (event?: Event) => {
|
||||
event?.preventDefault();
|
||||
windowsManager.close(id);
|
||||
};
|
||||
|
|
@ -157,7 +146,7 @@ export const WindowView = memo(({ id, app, size, position, onInteract, options,
|
|||
cancel="button"
|
||||
nodeRef={nodeRef}
|
||||
disabled={maximized}
|
||||
onStart={focus}
|
||||
onStart={(event) => { focus(event); }}
|
||||
grid={[1, 1]}
|
||||
>
|
||||
<div
|
||||
|
|
@ -182,7 +171,7 @@ export const WindowView = memo(({ id, app, size, position, onInteract, options,
|
|||
/>
|
||||
<p className={utilStyles["Text-semibold"]}>{title}</p>
|
||||
<button aria-label="Minimize" className={styles["Header-button"]} tabIndex={0} id="minimize-window"
|
||||
onClick={toggleMinimized}
|
||||
onClick={toggleMinimized as MouseEventHandler}
|
||||
>
|
||||
<FontAwesomeIcon icon={faMinus}/>
|
||||
</button>
|
||||
|
|
@ -196,7 +185,7 @@ export const WindowView = memo(({ id, app, size, position, onInteract, options,
|
|||
<FontAwesomeIcon icon={maximized ? fasWindowMaximize : faWindowMaximize}/>
|
||||
</button>
|
||||
<button aria-label="Close" className={`${styles["Header-button"]} ${styles["Exit-button"]}`} tabIndex={0} id="close-window"
|
||||
onClick={close}>
|
||||
onClick={(event) => { close(event as unknown as Event); }}>
|
||||
<FontAwesomeIcon icon={faXmark}/>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -1,23 +1,23 @@
|
|||
import { useWindows } from "../../hooks/windows/windowsContext";
|
||||
import { useWindowsManager } from "../../hooks/windows/windowsManagerContext";
|
||||
import { memo, useEffect, useState } from "react";
|
||||
import { WindowView } from "./WindowView.jsx";
|
||||
import { FC, memo, useEffect, useState } from "react";
|
||||
import { WindowProps, WindowView } from "./WindowView";
|
||||
import { useSettingsManager } from "../../hooks/settings/settingsManagerContext";
|
||||
import { SettingsManager } from "../../features/settings/settingsManager";
|
||||
import { NAME, TAG_LINE } from "../../config/branding.config.js";
|
||||
import { getViewportParams, setViewportIcon, setViewportTitle } from "../../features/_utils/browser.utils.js";
|
||||
import { removeDuplicatesFromArray } from "../../features/_utils/array.utils.js";
|
||||
import { NAME, TAG_LINE } from "../../config/branding.config";
|
||||
import { getViewportParams, setViewportIcon, setViewportTitle } from "../../features/_utils/browser.utils";
|
||||
import { removeDuplicatesFromArray } from "../../features/_utils/array.utils";
|
||||
import { WindowOptions } from "../../features/windows/windowsManager";
|
||||
|
||||
export const WindowsView = memo(() => {
|
||||
export const WindowsView: FC = memo(() => {
|
||||
const settingsManager = useSettingsManager();
|
||||
const windows = useWindows();
|
||||
const windowsManager = useWindowsManager();
|
||||
const [sortedWindows, setSortedWindows] = useState([]);
|
||||
const [sortedWindows, setSortedWindows] = useState<WindowProps[]>([]);
|
||||
|
||||
// Sort windows
|
||||
useEffect(() => {
|
||||
setSortedWindows([...windows].sort((windowA, windowB) =>
|
||||
// @ts-ignore
|
||||
setSortedWindows([...windows].sort((windowA: WindowOptions, windowB: WindowOptions) =>
|
||||
windowA.lastInteraction - windowB.lastInteraction
|
||||
));
|
||||
}, [windows]);
|
||||
|
|
@ -65,7 +65,7 @@ export const WindowsView = memo(() => {
|
|||
}, [settingsManager, windowsManager]);
|
||||
|
||||
return (<div>
|
||||
{windows.map((window) => {
|
||||
{windows.map((window: WindowProps) => {
|
||||
const { id, app, size, position, options, minimized, fullscreen } = window;
|
||||
const index = sortedWindows.indexOf(window);
|
||||
return <WindowView
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
export const HOME_URL = "https://os.prozilla.dev/";
|
||||
export const HOME_URL = "https://prozilla.dev/";
|
||||
export const SEARCH_URL = "https://www.google.com/search?igu=1";
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { ANSI } from "./apps/terminal.config.js";
|
||||
import { ANSI } from "./apps/terminal.config";
|
||||
|
||||
export const NAME = "Prozilla OS";
|
||||
export const TAG_LINE = "Web-based Operating System";
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import Vector2 from "../features/math/vector2.js";
|
||||
import Vector2 from "../features/math/vector2";
|
||||
|
||||
export const DIALOG_CONTENT_TYPES = {
|
||||
CloseButton: 0
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/**
|
||||
* @param {*} item
|
||||
* @param {*[]} array
|
||||
*/
|
||||
export function removeFromArray(item, array) {
|
||||
const index = array.indexOf(item);
|
||||
if (index !== -1) {
|
||||
array.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {*[]} array
|
||||
* @returns {*}
|
||||
*/
|
||||
export function randomFromArray(array) {
|
||||
return array[Math.floor(Math.random() * array.length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {*[]} array
|
||||
* @returns {*[]}
|
||||
*/
|
||||
export function removeDuplicatesFromArray(array) {
|
||||
return array.filter((item, index) => array.indexOf(item) === index);
|
||||
}
|
||||
14
src/features/_utils/array.utils.ts
Normal file
14
src/features/_utils/array.utils.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
export function removeFromArray(item: any, array: any[]) {
|
||||
const index = array.indexOf(item);
|
||||
if (index !== -1) {
|
||||
array.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
export function randomFromArray(array: any[]): any {
|
||||
return array[Math.floor(Math.random() * array.length)];
|
||||
}
|
||||
|
||||
export function removeDuplicatesFromArray(array: any[]): any[] {
|
||||
return array.filter((item, index) => array.indexOf(item) === index);
|
||||
}
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
import { NAME } from "../../config/branding.config.js";
|
||||
import { NAME } from "../../config/branding.config";
|
||||
|
||||
/**
|
||||
* Simulates closing the viewport by opening a blank page
|
||||
* @param {boolean} requireConfirmation
|
||||
*/
|
||||
export function closeViewport(requireConfirmation = false) {
|
||||
export function closeViewport(requireConfirmation: boolean = false) {
|
||||
if (requireConfirmation && window.confirm(`Are you sure you want to shut down ${NAME}?`)) {
|
||||
console.info("Closing viewport");
|
||||
window.open("about:blank", "_self");
|
||||
|
|
@ -13,18 +12,13 @@ export function closeViewport(requireConfirmation = false) {
|
|||
|
||||
/**
|
||||
* Reloads the viewport
|
||||
* @param {boolean} bypassCache
|
||||
*/
|
||||
export function reloadViewport(bypassCache = false) {
|
||||
export function reloadViewport() {
|
||||
console.info("Reloading viewport");
|
||||
window.location.reload(bypassCache);
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} string
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isValidUrl(string) {
|
||||
export function isValidUrl(string: string): boolean {
|
||||
try {
|
||||
new URL(string);
|
||||
return true;
|
||||
|
|
@ -33,18 +27,12 @@ export function isValidUrl(string) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} title
|
||||
*/
|
||||
export function setViewportTitle(title) {
|
||||
export function setViewportTitle(title: string) {
|
||||
document.title = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
*/
|
||||
export function setViewportIcon(url) {
|
||||
let link = document.querySelector("link[rel~='icon']");
|
||||
export function setViewportIcon(url: string) {
|
||||
let link: HTMLLinkElement = document.querySelector("link[rel~='icon']");
|
||||
if (!link) {
|
||||
link = document.createElement("link");
|
||||
link.rel = "icon";
|
||||
|
|
@ -53,10 +41,7 @@ export function setViewportIcon(url) {
|
|||
link.href = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {URLSearchParams}
|
||||
*/
|
||||
export function getViewportParams() {
|
||||
export function getViewportParams(): Record<string, string> {
|
||||
const query = window.location.search.slice(1);
|
||||
|
||||
const params = {};
|
||||
|
|
@ -70,12 +55,7 @@ export function getViewportParams() {
|
|||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} options
|
||||
* @param {string} options.appId
|
||||
* @param {boolean} options.fullscreen
|
||||
*/
|
||||
export function generateUrl(options) {
|
||||
export function generateUrl(options: { appId: string; fullscreen: boolean; }) {
|
||||
const baseUrl = window.location.origin + "/";
|
||||
|
||||
if (!options || Object.keys(options).length === 0)
|
||||
|
|
@ -92,7 +72,7 @@ export function generateUrl(options) {
|
|||
if (extraOptions && Object.keys(extraOptions).length > 0) {
|
||||
Object.entries(extraOptions).forEach(([key, value]) => {
|
||||
if (key && value)
|
||||
params.set(key, encodeURIComponent(value));
|
||||
params.set(key, encodeURIComponent(value as string));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -103,11 +83,6 @@ export function generateUrl(options) {
|
|||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} string
|
||||
* @param {Function} onSuccess
|
||||
* @param {Function} onFail
|
||||
*/
|
||||
export function copyToClipboard(string, onSuccess, onFail) {
|
||||
export function copyToClipboard(string: string, onSuccess: (value: void) => void, onFail: (value: void) => void) {
|
||||
navigator.clipboard.writeText(string).then(onSuccess, onFail);
|
||||
}
|
||||
|
|
@ -11,12 +11,10 @@ const TIME_INDICATORS = {
|
|||
|
||||
/**
|
||||
* Format a time
|
||||
* @param {number} time - Time in milliseconds
|
||||
* @param {number} maxLength - The maximum amount of units, e.g.: 3 => years, months, days
|
||||
* @param {boolean} allowAffixes
|
||||
* @returns {string}
|
||||
* @param time - Time in milliseconds
|
||||
* @param maxLength - The maximum amount of units, e.g.: 3 => years, months, days
|
||||
*/
|
||||
export const formatTime = (time, maxLength = 3, allowAffixes) => {
|
||||
export function formatTime(time: number, maxLength: number = 3, allowAffixes: boolean): string {
|
||||
const result = [];
|
||||
|
||||
const formatResult = (result, inPast) => {
|
||||
|
|
@ -83,12 +81,10 @@ export const formatTime = (time, maxLength = 3, allowAffixes) => {
|
|||
|
||||
/**
|
||||
* Format a time relative to now
|
||||
* @param {Date} date - The date
|
||||
* @param {number} maxLength - The maximum amount of units, e.g.: 3 => years, months, days
|
||||
* @param {boolean} allowAffixes
|
||||
* @returns {string}
|
||||
* @param date - The date
|
||||
* @param maxLength - The maximum amount of units, e.g.: 3 => years, months, days
|
||||
*/
|
||||
export const formatRelativeTime = (date, maxLength = 3, allowAffixes) => {
|
||||
const difference = date - new Date();
|
||||
export function formatRelativeTime(date: Date, maxLength: number = 3, allowAffixes: boolean): string {
|
||||
const difference = date.valueOf() - Date.now();
|
||||
return formatTime(difference, maxLength, allowAffixes);
|
||||
};
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
export class EventEmitter {
|
||||
/**
|
||||
* @type {Object<string, string>}
|
||||
*/
|
||||
static EVENT_NAMES = {};
|
||||
|
||||
/**
|
||||
* @type {Object<string, Array<Function>>}
|
||||
*/
|
||||
#events = {};
|
||||
|
||||
/**
|
||||
* Add event listener for an event
|
||||
* @param {EventEmitter.EVENT_NAMES} eventName
|
||||
* @param {Function} callback
|
||||
*/
|
||||
on(eventName, callback) {
|
||||
if (!this.#events[eventName]) {
|
||||
this.#events[eventName] = [];
|
||||
}
|
||||
this.#events[eventName].push(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove event listener for an event
|
||||
* @param {string} eventName
|
||||
* @param {Function} callback
|
||||
*/
|
||||
off(eventName, callback) {
|
||||
if (this.#events[eventName]) {
|
||||
this.#events[eventName] = this.#events[eventName].filter(
|
||||
(listener) => listener !== callback
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch event
|
||||
* @param {string} eventName
|
||||
* @param {*} data
|
||||
*/
|
||||
emit(eventName, data) {
|
||||
if (this.#events[eventName]) {
|
||||
this.#events[eventName].forEach((listener) => {
|
||||
listener(data);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
39
src/features/_utils/event.utils.ts
Normal file
39
src/features/_utils/event.utils.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
export type EventNamesMap = Record<string, string>;
|
||||
|
||||
export class EventEmitter<EventMap extends EventNamesMap> {
|
||||
static EVENT_NAMES: EventNamesMap = {};
|
||||
|
||||
#events: Record<string, Array<Function>> = {};
|
||||
|
||||
/**
|
||||
* Add event listener for an event
|
||||
*/
|
||||
on<Key extends keyof EventMap>(eventName: Key, callback: (data: any) => void) {
|
||||
if (!this.#events[eventName as string]) {
|
||||
this.#events[eventName as string] = [];
|
||||
}
|
||||
this.#events[eventName as string].push(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove event listener for an event
|
||||
*/
|
||||
off<Key extends keyof EventMap>(eventName: Key, callback: (data: any) => void) {
|
||||
if (this.#events[eventName as string]) {
|
||||
this.#events[eventName as string] = this.#events[eventName as string].filter(
|
||||
(listener) => listener !== callback
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch event
|
||||
*/
|
||||
emit<Key extends keyof EventMap>(eventName: Key, data?: any) {
|
||||
if (this.#events[eventName as string]) {
|
||||
this.#events[eventName as string].forEach((listener) => {
|
||||
listener(data);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export function isValidInteger(number) {
|
||||
return (parseInt(number) || parseInt(number) === 0);
|
||||
}
|
||||
3
src/features/_utils/number.utils.ts
Normal file
3
src/features/_utils/number.utils.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export function isValidInteger(number: number | string): number| boolean {
|
||||
return (typeof number === "number" || parseInt(number) || parseInt(number) === 0);
|
||||
}
|
||||
|
|
@ -1,8 +1,4 @@
|
|||
/**
|
||||
* @param {string[]} shortcut
|
||||
* @returns {string}
|
||||
*/
|
||||
export function formatShortcut(shortcut) {
|
||||
export function formatShortcut(shortcut: string[]): string {
|
||||
const specialKeys = [];
|
||||
const singleKeys = [];
|
||||
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
import React from "react";
|
||||
|
||||
export default class App {
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string} id
|
||||
* @param {React.ReactElement} windowContent
|
||||
* @param {object | null} windowOptions - Default window options
|
||||
*/
|
||||
constructor(name, id, windowContent, windowOptions) {
|
||||
Object.assign(this, { name, id, windowContent, windowOptions });
|
||||
|
||||
if (this.windowContent == null)
|
||||
console.warn(`App (${this.id}) is missing the windowContent property.`);
|
||||
}
|
||||
|
||||
WindowContent = (props) => {
|
||||
props = { ...props, ...this.windowOptions };
|
||||
|
||||
if (this.windowContent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <this.windowContent {...props}/>;
|
||||
};
|
||||
}
|
||||
33
src/features/apps/app.tsx
Normal file
33
src/features/apps/app.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import React from "react";
|
||||
import Vector2 from "../math/vector2";
|
||||
import { WindowProps } from "../../components/windows/WindowView";
|
||||
|
||||
export default class App {
|
||||
name: string;
|
||||
id: string;
|
||||
windowContent: React.FC;
|
||||
windowOptions?: {
|
||||
size: Vector2
|
||||
};
|
||||
isActive: boolean = false;
|
||||
|
||||
/**
|
||||
* @param windowOptions - Default window options
|
||||
*/
|
||||
constructor(name: string, id: string, windowContent: React.FC, windowOptions?: object | null) {
|
||||
Object.assign(this, { name, id, windowContent, windowOptions });
|
||||
|
||||
if (this.windowContent == null)
|
||||
console.warn(`App (${this.id}) is missing the windowContent property.`);
|
||||
}
|
||||
|
||||
WindowContent = (props: React.JSX.IntrinsicAttributes & WindowProps) => {
|
||||
props = { ...props, ...this.windowOptions };
|
||||
|
||||
if (this.windowContent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <this.windowContent {...props}/>;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,23 +1,21 @@
|
|||
import App from "./app.js";
|
||||
import { FileExplorer } from "../../components/apps/file-explorer/FileExplorer.jsx";
|
||||
import { MediaViewer } from "../../components/apps/media-viewer/MediaViewer.jsx";
|
||||
import { WebView } from "../../components/apps/_utils/web-view/WebView.jsx";
|
||||
import { Terminal } from "../../components/apps/terminal/Terminal.jsx";
|
||||
import { TextEditor } from "../../components/apps/text-editor/TextEditor.jsx";
|
||||
import { Settings } from "../../components/apps/settings/Settings.jsx";
|
||||
import { Calculator } from "../../components/apps/calculator/Calculator.jsx";
|
||||
import Vector2 from "../math/vector2.js";
|
||||
import { APPS, APP_NAMES } from "../../config/apps.config.js";
|
||||
import { Browser } from "../../components/apps/browser/Browser.jsx";
|
||||
import { IMAGE_FORMATS } from "../../config/apps/mediaViewer.config.js";
|
||||
import App from "./app";
|
||||
import { FileExplorer } from "../../components/apps/file-explorer/FileExplorer";
|
||||
import { MediaViewer } from "../../components/apps/media-viewer/MediaViewer";
|
||||
import { WebView } from "../../components/apps/_utils/web-view/WebView";
|
||||
import { Terminal } from "../../components/apps/terminal/Terminal";
|
||||
import { TextEditor } from "../../components/apps/text-editor/TextEditor";
|
||||
import { Settings } from "../../components/apps/settings/Settings";
|
||||
import { Calculator } from "../../components/apps/calculator/Calculator";
|
||||
import Vector2 from "../math/vector2";
|
||||
import { APPS, APP_NAMES } from "../../config/apps.config";
|
||||
import { Browser } from "../../components/apps/browser/Browser";
|
||||
import { IMAGE_FORMATS } from "../../config/apps/mediaViewer.config";
|
||||
|
||||
export default class AppsManager {
|
||||
/** @type {App[]} */
|
||||
static APPS = [
|
||||
static APPS: App[] = [
|
||||
new App(APP_NAMES.TERMINAL, APPS.TERMINAL, Terminal),
|
||||
new App(APP_NAMES.SETTINGS, APPS.SETTINGS, Settings),
|
||||
new App(APP_NAMES.MEDIA_VIEWER, APPS.MEDIA_VIEWER, MediaViewer),
|
||||
// new App("Browser", "browser"),
|
||||
new App(APP_NAMES.CALCULATOR, APPS.CALCULATOR, Calculator, { size: new Vector2(400, 600) }),
|
||||
new App(APP_NAMES.TEXT_EDITOR, APPS.TEXT_EDITOR, TextEditor),
|
||||
// new App("Code Editor", "code-editor"),
|
||||
|
|
@ -39,11 +37,7 @@ export default class AppsManager {
|
|||
}),
|
||||
];
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
* @returns {App | null}
|
||||
*/
|
||||
static getAppById(id) {
|
||||
static getAppById(id: string): App | null {
|
||||
let application = null;
|
||||
|
||||
this.APPS.forEach((app) => {
|
||||
|
|
@ -58,10 +52,8 @@ export default class AppsManager {
|
|||
|
||||
/**
|
||||
* Get the app associated with a file extension
|
||||
* @param {string} fileExtension
|
||||
* @returns {App}
|
||||
*/
|
||||
static getAppByFileExtension(fileExtension) {
|
||||
static getAppByFileExtension(fileExtension: string): App {
|
||||
let app = null;
|
||||
|
||||
if (IMAGE_FORMATS.includes(fileExtension))
|
||||
|
|
@ -78,11 +70,8 @@ export default class AppsManager {
|
|||
|
||||
/**
|
||||
* Returns the url of an icon inside the icons folder or the default icon of an app
|
||||
* @param {string} appId
|
||||
* @param {string | null} iconName
|
||||
* @returns {string}
|
||||
*/
|
||||
static getAppIconUrl(appId, iconName) {
|
||||
static getAppIconUrl(appId: string, iconName?: string): string {
|
||||
if (iconName == null) {
|
||||
return `${process.env.PUBLIC_URL}/assets/apps/icons/${appId}.svg`;
|
||||
} else {
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
import { ANSI } from "../../../../config/apps/terminal.config.js";
|
||||
|
||||
/**
|
||||
* @param {string} commandName
|
||||
* @param {string} error
|
||||
* @returns {string}
|
||||
*/
|
||||
export function formatError(commandName, error) {
|
||||
return `${ANSI.fg.red}${commandName}: ${error}${ANSI.reset}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} string
|
||||
* @returns {string}
|
||||
*/
|
||||
export function removeAnsi(string) {
|
||||
// eslint-disable-next-line no-control-regex
|
||||
return string.replace(/\u001b\[([0-9]+)m/gm, "");
|
||||
}
|
||||
10
src/features/apps/terminal/_utils/terminal.utils.ts
Normal file
10
src/features/apps/terminal/_utils/terminal.utils.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { ANSI } from "../../../../config/apps/terminal.config";
|
||||
|
||||
export function formatError(commandName: string, error: string): string {
|
||||
return `${ANSI.fg.red}${commandName}: ${error}${ANSI.reset}`;
|
||||
}
|
||||
|
||||
export function removeAnsi(string: string): string {
|
||||
// eslint-disable-next-line no-control-regex
|
||||
return string.replace(/\u001b\[([0-9]+)m/gm, "");
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { VirtualFolder } from "../../virtual-drive/folder/virtualFolder.js";
|
||||
import { VirtualRoot } from "../../virtual-drive/root/virtualRoot.js";
|
||||
import { VirtualFolder } from "../../virtual-drive/folder/virtualFolder";
|
||||
import { VirtualRoot } from "../../virtual-drive/root/virtualRoot";
|
||||
import Stream from "./stream";
|
||||
|
||||
type Option = {
|
||||
long: string,
|
||||
|
|
@ -7,19 +8,20 @@ type Option = {
|
|||
isInput: boolean
|
||||
};
|
||||
|
||||
type Execute = (args: string[], options: {
|
||||
promptOutput: void,
|
||||
pushHistory: void,
|
||||
virtualRoot: VirtualRoot,
|
||||
currentDirectory: VirtualFolder,
|
||||
setCurrentDirectory: void,
|
||||
username: string,
|
||||
hostname: string,
|
||||
rawInputValue: string,
|
||||
options: string[],
|
||||
exit: void,
|
||||
inputs: string[]
|
||||
}) => string | { blank: boolean } | void;
|
||||
type Execute = (args?: string[], options?: {
|
||||
promptOutput?: Function,
|
||||
pushHistory?: Function,
|
||||
virtualRoot?: VirtualRoot,
|
||||
currentDirectory?: VirtualFolder,
|
||||
setCurrentDirectory?: Function,
|
||||
username?: string,
|
||||
hostname?: string,
|
||||
rawInputValue?: string,
|
||||
options?: string[],
|
||||
exit?: Function,
|
||||
inputs?: Record<string, string>;
|
||||
timestamp: number,
|
||||
}) => string | { blank: boolean } | void | Stream;
|
||||
|
||||
type Manual = {
|
||||
purpose?: string,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ let commands = [];
|
|||
*/
|
||||
const loadCommands = () => {
|
||||
commands = [];
|
||||
const context = require.context("./commands", false, /\.js$/);
|
||||
const context = require.context("./commands", false, /\.ts$/);
|
||||
context.keys().forEach((key) => {
|
||||
const commandModule = context(key);
|
||||
const commandName = Object.keys(commandModule)[0];
|
||||
|
|
@ -24,11 +24,7 @@ loadCommands();
|
|||
export default class CommandsManager {
|
||||
static COMMANDS = commands;
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns {Command}
|
||||
*/
|
||||
static find(name) {
|
||||
static find(name: string): Command {
|
||||
let matchCommand = null;
|
||||
|
||||
this.COMMANDS.forEach((command) => {
|
||||
|
|
@ -41,11 +37,7 @@ export default class CommandsManager {
|
|||
return matchCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} pattern
|
||||
* @returns {Command[]}
|
||||
*/
|
||||
static search(pattern) {
|
||||
static search(pattern: string): Command[] {
|
||||
const matches = this.COMMANDS.filter((command) => command.name.match(pattern));
|
||||
return matches;
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { VirtualFile } from "../../../virtual-drive/file/virtualFile.js";
|
||||
import { formatError } from "../_utils/terminal.utils.js";
|
||||
import { VirtualFile } from "../../../virtual-drive/file/virtualFile";
|
||||
import { formatError } from "../_utils/terminal.utils";
|
||||
import Command from "../command";
|
||||
|
||||
export const cat = new Command()
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { formatError } from "../_utils/terminal.utils.js";
|
||||
import { formatError } from "../_utils/terminal.utils";
|
||||
import Command from "../command";
|
||||
|
||||
export const cd = new Command()
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import Command from "../command";
|
||||
import CommandsManager from "../commands.js";
|
||||
import CommandsManager from "../commands";
|
||||
|
||||
export const compgen = new Command()
|
||||
.setManual({
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { MAX_WIDTH } from "../../../../config/apps/terminal.config.js";
|
||||
import { MAX_WIDTH } from "../../../../config/apps/terminal.config";
|
||||
import Command from "../command";
|
||||
|
||||
const COW = `
|
||||
|
|
@ -29,7 +29,7 @@ export const cowsay = new Command()
|
|||
maxLineWidth = line.length;
|
||||
};
|
||||
|
||||
const nextLine = (word) => {
|
||||
const nextLine = (word?: string) => {
|
||||
addLine(currentLine);
|
||||
|
||||
if (word) {
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue