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