├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── public
├── CNAME
├── _redirects
├── favicon.png
├── index.html
├── manifest.json
├── me.png
├── robots.txt
└── service-worker.js
├── src
├── App.test.js
├── actions
│ ├── activityActions.js
│ ├── createActivityAction.js
│ ├── defaultApps.js
│ ├── desktopActions.js
│ ├── fileSystemActions.js
│ └── types.js
├── assets
│ ├── applications
│ │ ├── browser.css
│ │ ├── camera.css
│ │ ├── settings.css
│ │ └── terminal.css
│ ├── background
│ │ ├── wall-1.svg
│ │ ├── wall-2.svg
│ │ ├── wall-3.svg
│ │ ├── wall-4.svg
│ │ ├── wall-5.svg
│ │ └── wall-6.svg
│ ├── common
│ │ └── toggleButton.css
│ ├── default.css
│ ├── desktop
│ │ ├── desktop.css
│ │ ├── desktopIcon.css
│ │ ├── desktopWorkingArea.css
│ │ ├── dialogBox.css
│ │ ├── dropdown.css
│ │ ├── explorer.css
│ │ ├── lowerDesktop.css
│ │ ├── powerOff.css
│ │ ├── startMenu.css
│ │ ├── taskList.css
│ │ └── taskbar.css
│ └── icons
│ │ ├── archive.svg
│ │ ├── battery.svg
│ │ ├── brightness.svg
│ │ ├── browser.svg
│ │ ├── camera.svg
│ │ ├── codepen.svg
│ │ ├── download.svg
│ │ ├── dropdown-point.svg
│ │ ├── dropdown-white.svg
│ │ ├── dropdown.svg
│ │ ├── facebook.svg
│ │ ├── file.svg
│ │ ├── folder.svg
│ │ ├── gmail.svg
│ │ ├── home.svg
│ │ ├── instagram.svg
│ │ ├── lighting.svg
│ │ ├── linkedin.svg
│ │ ├── medium.svg
│ │ ├── octocat.svg
│ │ ├── profile.svg
│ │ ├── project.svg
│ │ ├── search.svg
│ │ ├── setting.svg
│ │ ├── sponsorship.png
│ │ ├── stopwatch.svg
│ │ ├── terminal.png
│ │ ├── terminal.svg
│ │ ├── transfer.png
│ │ ├── twitter.svg
│ │ ├── user.png
│ │ └── wifi.svg
├── components
│ ├── applications
│ │ ├── browser
│ │ │ └── browser.jsx
│ │ ├── camera
│ │ │ └── camera.jsx
│ │ ├── settings
│ │ │ ├── components
│ │ │ │ ├── FontChanger.jsx
│ │ │ │ ├── Personalise.jsx
│ │ │ │ └── ThemeChanger.jsx
│ │ │ └── settings.jsx
│ │ ├── terminal
│ │ │ └── terminal.jsx
│ │ └── textEditor
│ │ │ └── textEditor.jsx
│ ├── common
│ │ └── ToggleButton.jsx
│ ├── desktop
│ │ ├── Desktop.jsx
│ │ ├── desktopWorkingArea
│ │ │ ├── ContextMenu.jsx
│ │ │ ├── desktopIcon.jsx
│ │ │ └── desktopWorkingArea.jsx
│ │ ├── dialogBox
│ │ │ └── dialogBox.jsx
│ │ ├── dropdown
│ │ │ └── dropdown.jsx
│ │ ├── explorer
│ │ │ └── explorer.jsx
│ │ ├── lowerDesktop
│ │ │ ├── lowerDesktop.jsx
│ │ │ └── navItem.jsx
│ │ ├── powerOff
│ │ │ └── powerOff.jsx
│ │ ├── startMenu
│ │ │ ├── startItem.jsx
│ │ │ └── startMenu.jsx
│ │ └── taskbar
│ │ │ ├── RightTaskPane.jsx
│ │ │ ├── date.jsx
│ │ │ ├── taskList.jsx
│ │ │ └── taskbar.jsx
│ └── notFound
│ │ ├── error.css
│ │ └── error.jsx
├── index.js
├── reducers
│ ├── activityReducers.js
│ ├── combinedReducers.js
│ ├── desktopReducers.js
│ └── fileSystemReducers.js
├── serviceWorkerRegistration.js
├── setupTests.js
└── store.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-applications/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | .eslintcache
22 |
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Raghav Dhingra
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://app.netlify.com/sites/raghavdhingra/deploys)
2 |
3 | Test live: https://raghavdhinrga.web.app
4 |
5 | Production build: https://raghavdhingra.com
6 |
7 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
8 |
9 | Developers favourite operating system Linux is now available for Web. Check out linux based portfolio here - https://raghavdhingra.com
10 |
11 | ## Available Scripts
12 |
13 | In the project directory, you can run:
14 |
15 | ### `yarn start`
16 |
17 | Runs the app in the development mode.
18 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
19 |
20 | The page will reload if you make edits.
21 | You will also see any lint errors in the console.
22 |
23 | ### `yarn test`
24 |
25 | Launches the test runner in the interactive watch mode.
26 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
27 |
28 | ### `yarn build`
29 |
30 | Builds the app for production to the `build` folder.
31 | It correctly bundles React in production mode and optimizes the build for the best performance.
32 |
33 | The build is minified and the filenames include the hashes.
34 | Your app is ready to be deployed!
35 |
36 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
37 |
38 | ### `yarn eject`
39 |
40 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
41 |
42 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
43 |
44 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
45 |
46 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
47 |
48 | ## Learn More
49 |
50 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
51 |
52 | To learn React, check out the [React documentation](https://reactjs.org/).
53 |
54 | ### Code Splitting
55 |
56 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
57 |
58 | ### Analyzing the Bundle Size
59 |
60 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
61 |
62 | ### Making a Progressive Web App
63 |
64 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
65 |
66 | ### Advanced Configuration
67 |
68 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
69 |
70 | ### Deployment
71 |
72 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
73 |
74 | ### `yarn build` fails to minify
75 |
76 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
77 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "os-portfolio-2",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@reduxjs/toolkit": "^1.9.5",
7 | "@testing-library/jest-dom": "^5.16.5",
8 | "@testing-library/react": "^14.0.0",
9 | "@testing-library/user-event": "^14.4.3",
10 | "react": "^18.2.0",
11 | "react-dom": "^18.2.0",
12 | "react-redux": "^8.1.1",
13 | "react-router-dom": "^6.14.1",
14 | "react-scripts": "^5.0.1",
15 | "redux": "^4.2.1",
16 | "redux-thunk": "^2.4.2"
17 | },
18 | "scripts": {
19 | "start": "react-scripts start",
20 | "build": "react-scripts build",
21 | "test": "react-scripts test",
22 | "eject": "react-scripts eject"
23 | },
24 | "eslintConfig": {
25 | "extends": "react-app"
26 | },
27 | "browserslist": {
28 | "production": [
29 | ">0.2%",
30 | "not dead",
31 | "not op_mini all"
32 | ],
33 | "development": [
34 | "last 1 chrome version",
35 | "last 1 firefox version",
36 | "last 1 safari version"
37 | ]
38 | },
39 | "devDependencies": {
40 | "react-error-overlay": "6.0.9"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/public/CNAME:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raghavdhingra/Web-OS/b9ab826d6761759b27439ac853a3d190ecf51ed0/public/favicon.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
14 |
15 |
19 |
20 |
21 |
22 | Portfolio OS | Raghav Dhingra | Full Stack Developer
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "me.png",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon",
9 | "purpose": "any maskable"
10 | },
11 | {
12 | "src": "me.png",
13 | "type": "image/png",
14 | "sizes": "192x192",
15 | "purpose": "any maskable"
16 | },
17 | {
18 | "src": "me.png",
19 | "type": "image/png",
20 | "sizes": "512x512",
21 | "purpose": "any maskable"
22 | }
23 | ],
24 | "start_url": "/",
25 | "theme_color": "#F4BD42",
26 | "background_color": "#2B2929",
27 | "display": "standalone",
28 | "scope": "/"
29 | }
30 |
--------------------------------------------------------------------------------
/public/me.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raghavdhingra/Web-OS/b9ab826d6761759b27439ac853a3d190ecf51ed0/public/me.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/service-worker.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-restricted-globals */
2 |
3 | // This service worker can be customized!
4 | // See https://developers.google.com/web/tools/workbox/modules
5 | // for the list of available Workbox modules, or add any other
6 | // code you'd like.
7 | // You can also remove this file if you'd prefer not to use a
8 | // service worker, and the Workbox build step will be skipped.
9 |
10 | import { clientsClaim } from "workbox-core";
11 | import { ExpirationPlugin } from "workbox-expiration";
12 | import { precacheAndRoute, createHandlerBoundToURL } from "workbox-precaching";
13 | import { registerRoute } from "workbox-routing";
14 | import { StaleWhileRevalidate } from "workbox-strategies";
15 |
16 | clientsClaim();
17 |
18 | // Precache all of the assets generated by your build process.
19 | // Their URLs are injected into the manifest variable below.
20 | // This variable must be present somewhere in your service worker file,
21 | // even if you decide not to use precaching. See https://cra.link/PWA
22 | precacheAndRoute(self.__WB_MANIFEST);
23 |
24 | // Set up App Shell-style routing, so that all navigation requests
25 | // are fulfilled with your index.html shell. Learn more at
26 | // https://developers.google.com/web/fundamentals/architecture/app-shell
27 | const fileExtensionRegexp = new RegExp("/[^/?]+\\.[^/]+$");
28 | registerRoute(
29 | // Return false to exempt requests from being fulfilled by index.html.
30 | ({ request, url }) => {
31 | // If this isn't a navigation, skip.
32 | if (request.mode !== "navigate") {
33 | return false;
34 | } // If this is a URL that starts with /_, skip.
35 |
36 | if (url.pathname.startsWith("/_")) {
37 | return false;
38 | } // If this looks like a URL for a resource, because it contains // a file extension, skip.
39 |
40 | if (url.pathname.match(fileExtensionRegexp)) {
41 | return false;
42 | } // Return true to signal that we want to use the handler.
43 |
44 | return true;
45 | },
46 | createHandlerBoundToURL(process?.env.PUBLIC_URL + "/index.html")
47 | );
48 |
49 | // An example runtime caching route for requests that aren't handled by the
50 | // precache, in this case same-origin .png requests like those from in public/
51 | registerRoute(
52 | // Add in any other file extensions or routing criteria as needed.
53 | ({ url }) =>
54 | url.origin === self.location.origin && url.pathname.endsWith(".png"), // Customize this strategy as needed, e.g., by changing to CacheFirst.
55 | new StaleWhileRevalidate({
56 | cacheName: "images",
57 | plugins: [
58 | // Ensure that once this runtime cache reaches a maximum size the
59 | // least-recently used images are removed.
60 | new ExpirationPlugin({ maxEntries: 50 }),
61 | ],
62 | })
63 | );
64 |
65 | // This allows the web app to trigger skipWaiting via
66 | // registration.waiting.postMessage({type: 'SKIP_WAITING'})
67 | self.addEventListener("message", (event) => {
68 | if (event.data && event.data.type === "SKIP_WAITING") {
69 | self.skipWaiting();
70 | }
71 | });
72 |
73 | // Any other custom service worker logic can go here.
74 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | const { getByText } = render();
7 | const linkElement = getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/src/actions/activityActions.js:
--------------------------------------------------------------------------------
1 | import * as actions from "./types";
2 |
3 | export const removeActivity = (payload) => async (dispatch) => {
4 | try {
5 | await dispatch({
6 | type: actions.UPDATE_ACTIVITY_TRIGGER,
7 | payload: { activityIndex: payload, isTriggered: true },
8 | });
9 | await dispatch({
10 | type: actions.TOGGLE_LOADING_ACTIVITY,
11 | payload: { activityIndex: payload, isLoading: true },
12 | });
13 | setTimeout(async () => {
14 | await dispatch({
15 | type: actions.TOGGLE_LOADING_ACTIVITY,
16 | payload: { activityIndex: payload, isLoading: true },
17 | });
18 | await dispatch({
19 | type: actions.REMOVE_ACTIVITY,
20 | payload: {
21 | activityIndex: payload,
22 | },
23 | });
24 | await dispatch({ type: actions.REMOVE_ACTIVITY_TRIGGER });
25 | }, 100);
26 | } catch (err) {
27 | console.log(err);
28 | }
29 | };
30 | export const toggleActivityMaximise = (payload) => async (dispatch) => {
31 | try {
32 | dispatch({
33 | type: actions.TOGGLE_ACTIVITY_MAXIMISE,
34 | payload,
35 | });
36 | } catch (err) {
37 | console.log(err);
38 | }
39 | };
40 | export const updateZIndexActivity = (payload) => async (dispatch) => {
41 | try {
42 | dispatch({
43 | type: actions.UPDATE_ZINDEX_ACTIVITY,
44 | payload: {
45 | activityIndex: payload,
46 | },
47 | });
48 | } catch (err) {
49 | console.log(err);
50 | }
51 | };
52 | export const toggleActivityLoading = (payload) => async (dispatch) => {
53 | try {
54 | dispatch({
55 | type: actions.TOGGLE_LOADING_ACTIVITY,
56 | payload,
57 | });
58 | } catch (err) {
59 | console.log(err);
60 | }
61 | };
62 | export const updatePositionActivity = (payload) => async (dispatch) => {
63 | try {
64 | dispatch({
65 | type: actions.UPDATE_ACTIVITY_POSITION,
66 | payload,
67 | });
68 | } catch (err) {
69 | console.log(err);
70 | }
71 | };
72 | export const updateDimensionActivity = (payload) => async (dispatch) => {
73 | try {
74 | dispatch({
75 | type: actions.UPDATE_ACTIVITY_DIMENSION,
76 | payload,
77 | });
78 | } catch (err) {
79 | console.log(err);
80 | }
81 | };
82 |
--------------------------------------------------------------------------------
/src/actions/createActivityAction.js:
--------------------------------------------------------------------------------
1 | import { applications } from './defaultApps';
2 | import * as actions from './types';
3 |
4 | export const createActivity = (payload) => async (dispatch) => {
5 | try {
6 | let { name, newApp } = payload;
7 | let app;
8 | if (newApp) {
9 | let { image, footer, child } = payload;
10 | app = {
11 | name,
12 | image,
13 | width: '40px',
14 | key: name,
15 | child,
16 | footer,
17 | };
18 | } else {
19 | app = applications.defaultApps.find((app) => app.key === name);
20 | }
21 | await dispatch({
22 | type: actions.CREATE_ACTIVITY,
23 | payload: {
24 | activity: {
25 | name: app.name,
26 | isLoading: false,
27 | date: new Date(),
28 | isExplorerOpened: false,
29 | isMaximise: false,
30 | child: app.child,
31 | footer: app.footer,
32 | image: app.image,
33 | zIndex: 4,
34 | top: '34px',
35 | left: '60px',
36 | height: '500px',
37 | width: '750px',
38 | triggeredFunction: false,
39 | },
40 | },
41 | });
42 | } catch (err) {
43 | console.log(err);
44 | }
45 | };
46 |
--------------------------------------------------------------------------------
/src/actions/defaultApps.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import TerminalImage from "../assets/icons/terminal.svg";
3 | import CameraImage from "../assets/icons/camera.svg";
4 | import BrowserImage from "../assets/icons/browser.svg";
5 | import SettingsImage from "../assets/icons/setting.svg";
6 | import TerminalWindow from "../components/applications/terminal/terminal";
7 | import Camera from "../components/applications/camera/camera";
8 | import Browser from "../components/applications/browser/browser";
9 | import Settings from "../components/applications/settings/settings";
10 |
11 | import PROFILE_IMAGE from "../assets/icons/profile.svg";
12 | import PROJECT_IMAGE from "../assets/icons/project.svg";
13 | import ARCHIVE_IMAGE from "../assets/icons/archive.svg";
14 | import GITHUB_IMAGE from "../assets/icons/octocat.svg";
15 | import FACEBOOK_IMAGE from "../assets/icons/facebook.svg";
16 | import TWITTER_IMAGE from "../assets/icons/twitter.svg";
17 | import INSTAGRAM_IMAGE from "../assets/icons/instagram.svg";
18 | import LINKEDIN_IMAGE from "../assets/icons/linkedin.svg";
19 | import CODEPEN_IMAGE from "../assets/icons/codepen.svg";
20 | import GMAIL_IMAGE from "../assets/icons/gmail.svg";
21 | import MEDIUM_IMAGE from "../assets/icons/medium.svg";
22 | import SPONSOR_IMAGE from "../assets/icons/sponsorship.png";
23 |
24 | // const nullFunction = (supplement) => null;
25 | const width1 = "40px";
26 | const width2 = "50px";
27 | const width3 = "60px";
28 | const width4 = "70px";
29 |
30 | export const applications = {
31 | allApplications: [
32 | {
33 | name: "Browser",
34 | image: BrowserImage,
35 | width: width2,
36 | bigWidth: width4,
37 | key: "browser",
38 | child: (supplement) => ,
39 | },
40 | {
41 | name: "Camera",
42 | image: CameraImage,
43 | width: width1,
44 | bigWidth: width3,
45 | key: "camera",
46 | child: (supplement) => ,
47 | },
48 | {
49 | name: "Settings",
50 | image: SettingsImage,
51 | width: width2,
52 | bigWidth: width4,
53 | key: "settings",
54 | child: (supplement) => ,
55 | },
56 | {
57 | name: "Terminal",
58 | image: TerminalImage,
59 | width: width1,
60 | bigWidth: width3,
61 | key: "terminal",
62 | child: (supplement) => (
63 |
67 | ),
68 | // footer: nullFunction,
69 | },
70 | ],
71 | socialApps: [
72 | {
73 | name: "Archive",
74 | key: "archive",
75 | image: ARCHIVE_IMAGE,
76 | width: width1,
77 | bigWidth: width3,
78 | link: "https://archive.raghavdhingra.com",
79 | },
80 | {
81 | name: "Portfolio",
82 | key: "portfolio",
83 | width: width1,
84 | bigWidth: width3,
85 | image: PROFILE_IMAGE,
86 | link: "https://portfolio.raghavdhingra.com",
87 | },
88 | {
89 | name: "Projects",
90 | key: "projects",
91 | width: width1,
92 | bigWidth: width3,
93 | image: PROJECT_IMAGE,
94 | link: "https://portfolio.raghavdhingra.com/projects",
95 | },
96 | {
97 | name: "GitHub",
98 | key: "github",
99 | width: width1,
100 | bigWidth: width3,
101 | image: GITHUB_IMAGE,
102 | link: "https://github.com/raghavdhingra",
103 | },
104 | {
105 | name: "Facebook",
106 | key: "facebook",
107 | width: width1,
108 | bigWidth: width3,
109 | image: FACEBOOK_IMAGE,
110 | link: "https://www.facebook.com/raghav.dhingra15",
111 | },
112 | {
113 | name: "Twitter",
114 | key: "twitter",
115 | width: width1,
116 | bigWidth: width3,
117 | image: TWITTER_IMAGE,
118 | link: "https://twitter.com/raghavdhingra15",
119 | },
120 | {
121 | name: "Instagram",
122 | key: "instagram",
123 | width: width1,
124 | bigWidth: width3,
125 | image: INSTAGRAM_IMAGE,
126 | link: "https://www.instagram.com/raghav.dhingra15/",
127 | },
128 | {
129 | name: "Linkedin",
130 | key: "linkedin",
131 | width: width1,
132 | bigWidth: width3,
133 | image: LINKEDIN_IMAGE,
134 | link: "https://www.linkedin.com/in/raghav-dhingra/",
135 | },
136 | {
137 | name: "Codepen",
138 | key: "codepen",
139 | width: width1,
140 | bigWidth: width3,
141 | image: CODEPEN_IMAGE,
142 | link: "https://codepen.io/raghav-dhingra",
143 | },
144 | {
145 | name: "G-Mail",
146 | key: "gmail",
147 | width: width1,
148 | bigWidth: width3,
149 | image: GMAIL_IMAGE,
150 | link: "mailto:admin@raghavdhingra.com",
151 | },
152 | {
153 | name: "Medium",
154 | key: "medium",
155 | width: width1,
156 | bigWidth: width3,
157 | image: MEDIUM_IMAGE,
158 | link: "https://medium.com/@raghav.dhingra15",
159 | },
160 | {
161 | name: "Sponsor",
162 | key: "sponsor",
163 | width: width1,
164 | bigWidth: width3,
165 | image: SPONSOR_IMAGE,
166 | link: "https://github.com/sponsors/raghavdhingra",
167 | },
168 | ],
169 | defaultApps: [
170 | {
171 | name: "Terminal",
172 | image: TerminalImage,
173 | width: width1,
174 | key: "terminal",
175 | child: (supplement) => (
176 |
180 | ),
181 | // footer: nullFunction,
182 | },
183 | {
184 | name: "Browser",
185 | image: BrowserImage,
186 | width: width2,
187 | key: "browser",
188 | child: (supplement) => ,
189 | },
190 | {
191 | name: "Camera",
192 | image: CameraImage,
193 | width: width1,
194 | key: "camera",
195 | child: (supplement) => ,
196 | },
197 | {
198 | name: "Settings",
199 | image: SettingsImage,
200 | width: width2,
201 | key: "settings",
202 | child: (supplement) => ,
203 | },
204 | ],
205 | };
206 |
--------------------------------------------------------------------------------
/src/actions/desktopActions.js:
--------------------------------------------------------------------------------
1 | import * as actions from "./types";
2 |
3 | export const changeBackImage = (payload) => async (dispatch) => {
4 | try {
5 | await dispatch({
6 | type: actions.BACK_IMAGE_CHANGE,
7 | payload: {
8 | background: payload,
9 | },
10 | });
11 | } catch (err) {
12 | console.log(err);
13 | }
14 | };
15 | export const changeStartMenu = (payload) => async (dispatch) => {
16 | try {
17 | await dispatch({
18 | type: actions.TOGGLE_START_MENU,
19 | payload: {
20 | startMenuOpen: payload,
21 | },
22 | });
23 | } catch (err) {
24 | console.log(err);
25 | }
26 | };
27 | export const changeSingleClickIcon = (payload) => async (dispatch) => {
28 | try {
29 | await dispatch({
30 | type: actions.SINGLE_CLICK_ICON_CHANGE,
31 | payload: {
32 | singleClickIcon: payload,
33 | },
34 | });
35 | } catch (err) {
36 | console.log(err);
37 | }
38 | };
39 | export const changeFontStyle = (payload) => async (dispatch) => {
40 | try {
41 | await dispatch({
42 | type: actions.FONT_STYLE_CHANGE,
43 | payload: {
44 | fontStyle: payload,
45 | },
46 | });
47 | } catch (err) {
48 | console.log(err);
49 | }
50 | };
51 | export const toggleFullScreen = () => async (dispatch) => {
52 | try {
53 | await dispatch({ type: actions.TOGGLE_FULL_SCREEN });
54 | } catch (err) {
55 | console.log(err);
56 | }
57 | };
58 | export const resetToDefault = () => async (dispatch) => {
59 | try {
60 | localStorage.clear();
61 | await dispatch({ type: actions.RESET_TO_DEFAULT });
62 | } catch (err) {
63 | console.log(err);
64 | }
65 | };
66 | export const changeBrightness = (payload) => async (dispatch) => {
67 | try {
68 | await dispatch({
69 | type: actions.BRIGHTNESS_CHANGE,
70 | payload: {
71 | brightness: payload,
72 | },
73 | });
74 | } catch (err) {
75 | console.log(err);
76 | }
77 | };
78 | export const dropDownToggle = (payload) => async (dispatch) => {
79 | try {
80 | dispatch({
81 | type: actions.TOGGLE_DROP_DOWN,
82 | payload: {
83 | dropDownOpen: payload,
84 | },
85 | });
86 | } catch (err) {
87 | console.log(err);
88 | }
89 | };
90 | export const activityDropDownToggle = (payload) => async (dispatch) => {
91 | try {
92 | dispatch({
93 | type: actions.ACTIVITY_TOGGLE_DROP_DOWN,
94 | payload: {
95 | activityDropDown: payload,
96 | },
97 | });
98 | } catch (err) {
99 | console.log(err);
100 | }
101 | };
102 | export const batteryStatus = (payload) => async (dispatch) => {
103 | try {
104 | dispatch({
105 | type: actions.BATTERY_STATUS,
106 | payload: {
107 | battery: payload,
108 | },
109 | });
110 | } catch (err) {
111 | console.log(err);
112 | }
113 | };
114 | export const onlineStatus = (payload) => async (dispatch) => {
115 | try {
116 | dispatch({
117 | type: actions.ONLINE_STATUS,
118 | payload: {
119 | isOnline: payload,
120 | },
121 | });
122 | } catch (err) {
123 | console.log(err);
124 | }
125 | };
126 | export const networkType = (payload) => async (dispatch) => {
127 | try {
128 | dispatch({
129 | type: actions.NETWORK_TYPE,
130 | payload: {
131 | networkType: payload,
132 | },
133 | });
134 | } catch (err) {
135 | console.log(err);
136 | }
137 | };
138 | export const dateStatus = (payload) => async (dispatch) => {
139 | try {
140 | dispatch({
141 | type: actions.DATE_STATUS,
142 | payload: {
143 | date: payload,
144 | },
145 | });
146 | } catch (err) {
147 | console.log(err);
148 | }
149 | };
150 | export const powerOffStatus = (payload) => async (dispatch) => {
151 | try {
152 | const { active, timer } = payload;
153 | dispatch({
154 | type: actions.POWER_OFF_STATUS,
155 | payload: {
156 | powerOff: {
157 | active,
158 | timer,
159 | },
160 | },
161 | });
162 | } catch (err) {
163 | console.log(err);
164 | }
165 | };
166 |
--------------------------------------------------------------------------------
/src/actions/fileSystemActions.js:
--------------------------------------------------------------------------------
1 | import * as actions from "./types";
2 |
3 | export const makeDirectoryAction = (payload) => async (dispatch) => {
4 | try {
5 | dispatch({
6 | type: actions.MAKE_DIRECTORY_IN_SYSTEM,
7 | payload,
8 | });
9 | } catch (err) {
10 | console.log(err);
11 | }
12 | };
13 | export const makeFileAction = (payload) => async (dispatch) => {
14 | try {
15 | dispatch({
16 | type: actions.MAKE_FILE_IN_SYSTEM,
17 | payload,
18 | });
19 | } catch (err) {
20 | console.log(err);
21 | }
22 | };
23 |
24 | export const removeDirectoryAction = (payload) => async (dispatch) => {
25 | try {
26 | dispatch({
27 | type: actions.REMOVE_DIRECTORY_IN_SYSTEM,
28 | payload,
29 | });
30 | } catch (err) {
31 | console.log(err);
32 | }
33 | };
34 | export const changeTextInFile = (payload) => async (dispatch) => {
35 | try {
36 | dispatch({ type: actions.CHANGE_TEXT_IN_FILE, payload });
37 | } catch (err) {
38 | console.log(err);
39 | }
40 | };
41 | export const previousStateSet = () => async (dispatch) => {
42 | try {
43 | await dispatch({ type: actions.PREVIOUS_STATE_SET });
44 | } catch (err) {
45 | console.log(err);
46 | }
47 | };
48 |
--------------------------------------------------------------------------------
/src/actions/types.js:
--------------------------------------------------------------------------------
1 | // Desktop Types
2 | export const BACK_IMAGE_CHANGE = "BACK_IMAGE_CHANGE";
3 | export const BRIGHTNESS_CHANGE = "BRIGHTNESS_CHANGE";
4 | export const DATE_STATUS = "DATE_STATUS";
5 | export const TOGGLE_DROP_DOWN = "TOGGLE_DROP_DOWN";
6 | export const BATTERY_STATUS = "BATTERY_STATUS";
7 | export const ONLINE_STATUS = "ONLINE_STATUS";
8 | export const NETWORK_TYPE = "NETWORK_TYPE";
9 | export const POWER_OFF_STATUS = "POWER_OFF_STATUS";
10 | export const RESET_TO_DEFAULT = "RESET_TO_DEFAULT";
11 | export const FONT_STYLE_CHANGE = "FONT_STYLE_CHANGE";
12 | export const SINGLE_CLICK_ICON_CHANGE = "SINGLE_CLICK_ICON_CHANGE";
13 | export const TOGGLE_FULL_SCREEN = "TOGGLE_FULL_SCREEN";
14 | export const TOGGLE_START_MENU = "TOGGLE_START_MENU";
15 |
16 | // Activities Types
17 | export const CREATE_ACTIVITY = "CREATE_ACTIVITY";
18 | export const REMOVE_ACTIVITY = "REMOVE_ACTIVITY";
19 | export const UPDATE_ZINDEX_ACTIVITY = "UPDATE_ZINDEX_ACTIVITY";
20 | export const ACTIVITY_TOGGLE_DROP_DOWN = "ACTIVITY_TOGGLE_DROP_DOWN";
21 | export const TOGGLE_ACTIVITY_MAXIMISE = "TOGGLE_ACTIVITY_MAXIMISE";
22 | export const TOGGLE_LOADING_ACTIVITY = "TOGGLE_LOADING_ACTIVITY";
23 | export const UPDATE_ACTIVITY_POSITION = "UPDATE_ACTIVITY_POSITION";
24 | export const UPDATE_ACTIVITY_DIMENSION = "UPDATE_ACTIVITY_DIMENSION";
25 | export const UPDATE_ACTIVITY_TRIGGER = "UPDATE_ACTIVITY_TRIGGER";
26 | export const REMOVE_ACTIVITY_TRIGGER = "REMOVE_ACTIVITY_TRIGGER";
27 |
28 | // File System Types
29 | export const MAKE_DIRECTORY_IN_SYSTEM = "MAKE_DIRECTORY_IN_SYSTEM";
30 | export const REMOVE_DIRECTORY_IN_SYSTEM = "REMOVE_DIRECTORY_IN_SYSTEM";
31 | export const MAKE_FILE_IN_SYSTEM = "MAKE_FILE_IN_SYSTEM";
32 | export const CHANGE_TEXT_IN_FILE = "CHANGE_TEXT_IN_FILE";
33 | export const PREVIOUS_STATE_SET = "PREVIOUS_STATE_SET";
34 |
--------------------------------------------------------------------------------
/src/assets/applications/browser.css:
--------------------------------------------------------------------------------
1 | .browser-container {
2 | height: 100%;
3 | width: 100%;
4 | display: grid;
5 | grid-template-rows: auto 1fr;
6 | }
7 | .browser-task-container {
8 | /* background-color: aqua; */
9 | border-bottom: 0.05rem solid #00000015;
10 | padding: 0.3rem 0.5rem;
11 | display: grid;
12 | box-sizing: border-box;
13 | grid-template-columns: auto 1fr auto;
14 | }
15 | .browser-task-image-container > img {
16 | width: 15px;
17 | cursor: pointer;
18 | }
19 | .browser-container > iframe {
20 | height: 100%;
21 | width: 100%;
22 | border: none;
23 | }
24 | .browser-loader {
25 | height: calc(100% - 66px);
26 | width: 100%;
27 | position: absolute;
28 | top: 66px;
29 | text-align: center;
30 | z-index: 100000;
31 | background-color: #eee;
32 | }
33 | .loader-lg-browser {
34 | r: 20px;
35 | cy: 40px;
36 | cx: 40px;
37 | stroke: #0005;
38 | stroke-width: 5px;
39 | fill: #0000;
40 | stroke-dasharray: 60 40;
41 | }
42 | input.browser-search-input {
43 | border: 1px solid #0002;
44 | padding: 0.2rem 0.4rem;
45 | margin: 0 1rem;
46 | box-sizing: border-box;
47 | border-radius: 30px !important;
48 | }
49 | .browser-search-input:focus {
50 | outline: none;
51 | }
52 |
--------------------------------------------------------------------------------
/src/assets/applications/camera.css:
--------------------------------------------------------------------------------
1 | .camera-container {
2 | height: 100%;
3 | width: 100%;
4 | }
5 | .camera-video-container {
6 | object-fit: contain;
7 | height: 100%;
8 | width: 100%;
9 | transform: rotateY(180deg);
10 | }
11 | .camera-container-overlay {
12 | height: calc(100% - 31px);
13 | width: 100%;
14 | position: absolute;
15 | top: 31px;
16 | display: grid;
17 | grid-template-columns: 1fr auto;
18 | }
19 | .camera-button-container {
20 | display: grid;
21 | height: 100%;
22 | grid-template-rows: 1fr auto 1fr;
23 | padding: 0 0.5rem;
24 | box-sizing: border-box;
25 | }
26 | .camera-capture-button-svg {
27 | cursor: pointer;
28 | height: 4rem;
29 | width: 4rem;
30 | transition: 0.2s ease;
31 | filter: drop-shadow(0 0 3px #0003);
32 | }
33 | .camera-capture-button {
34 | stroke: white;
35 | stroke-width: 0.3rem;
36 | transition: 0.2s ease-in-out;
37 | fill: #0000;
38 | }
39 |
40 | .camera-capture-button:hover {
41 | stroke-width: 0.5rem;
42 | }
43 |
44 | .camera-capture-button-svg:active {
45 | transform: scale(0.9);
46 | }
47 |
48 | .captured-image {
49 | position: absolute;
50 | top: 40px;
51 | left: 10px;
52 | height: 200px;
53 | width: 350px;
54 | animation: move-amin 5s ease forwards;
55 | filter: drop-shadow(0 0 5px #0009);
56 | border: 0.5rem solid #fff8;
57 | border-radius: 10px;
58 | }
59 | .captured-image-canvas {
60 | height: 100%;
61 | width: 100%;
62 | /* transform: rotateY(180deg); */
63 | }
64 | .captured-image:hover .captured-image::after {
65 | position: absolute;
66 | content: "";
67 | background-color: #0005;
68 | top: 0;
69 | height: 100%;
70 | width: 100%;
71 | }
72 | @keyframes move-amin {
73 | 0% {
74 | transform: translateY(20px);
75 | }
76 | 20% {
77 | transform: translateY(0);
78 | }
79 | 40% {
80 | transform: translateY(20px);
81 | }
82 | 60% {
83 | transform: translateY(0);
84 | }
85 | 80% {
86 | transform: translateY(10px);
87 | }
88 | 100% {
89 | transform: translateY(0);
90 | }
91 | }
92 | .image-download-button-canvas {
93 | position: absolute;
94 | z-index: 1;
95 | top: 5px;
96 | left: 5px;
97 | height: 25px;
98 | width: 25px;
99 | background-color: #fff;
100 | box-shadow: 0 0 3px 0 #0003;
101 | cursor: pointer;
102 | display: flex;
103 | flex-direction: column;
104 | justify-content: center;
105 | border-radius: 50%;
106 | }
107 | .image-close-button-canvas {
108 | position: absolute;
109 | top: 0;
110 | right: 10px;
111 | z-index: 1;
112 | cursor: pointer;
113 | color: #fff;
114 | font-size: 2rem;
115 | filter: drop-shadow(0 0 2px #0005);
116 | }
117 |
118 | .image-download-button-canvas > img {
119 | height: auto;
120 | width: 14px;
121 | margin: 0 auto;
122 | }
123 | .camera-timer-container {
124 | text-align: center;
125 | color: #fff;
126 | cursor: pointer;
127 | }
128 | .camera-timer-container > img {
129 | filter: drop-shadow(0 0 2px #0005);
130 | width: 30px;
131 | margin-top: 1rem;
132 | }
133 | .timer-container {
134 | position: absolute;
135 | height: calc(100% - 31px);
136 | width: 100%;
137 | top: 31px;
138 | display: flex;
139 | flex-direction: column;
140 | justify-content: center;
141 | text-align: center;
142 | color: #fff;
143 | font-size: 4rem;
144 | }
145 | .fade-out-anim {
146 | animation: fadeOut 1s ease forwards;
147 | }
148 | @keyframes fadeOut {
149 | from {
150 | opacity: 1;
151 | transform: scale(1);
152 | }
153 | to {
154 | opacity: 0;
155 | transform: scale(3);
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/assets/applications/settings.css:
--------------------------------------------------------------------------------
1 | .settings-container {
2 | height: 100%;
3 | width: 100%;
4 | display: grid;
5 | grid-template-columns: 200px 1fr;
6 | }
7 | .settings-left-container {
8 | overflow-y: auto;
9 | overflow-x: hidden;
10 | padding: 0.5rem 0;
11 | box-sizing: border-box;
12 | border-right: 1px solid rgb(207, 207, 207);
13 | }
14 | .settings-left-button {
15 | padding: 0.5rem 1rem;
16 | box-sizing: border-box;
17 | cursor: pointer;
18 | }
19 | .settings-left-button-active {
20 | background-color: rgb(234, 84, 33, 0.2);
21 | }
22 | .settings-left-button:hover {
23 | background-color: rgb(234, 84, 33);
24 | color: #fff;
25 | }
26 | .settings-left-button:active {
27 | filter: brightness(0.9);
28 | }
29 | .settings-right-container {
30 | background-color: rgb(247, 247, 247);
31 | }
32 | .settings-right-inner-container {
33 | width: 90% !important;
34 | margin: 0 auto;
35 | }
36 | .settings-right-inner-container-header {
37 | font-size: 1rem;
38 | font-weight: bold;
39 | margin: 1rem 0 0.5rem 0;
40 | }
41 | .settings-right-inner-container-bottom {
42 | width: 100%;
43 | background-color: #fff;
44 | padding: 0.5rem 1rem;
45 | box-sizing: border-box;
46 | border: 1px solid rgb(207, 207, 207);
47 | border-radius: 3px;
48 | }
49 |
50 | /* Theme */
51 | .theme-change-container {
52 | display: grid;
53 | grid-template-columns: repeat(3, 1fr);
54 | gap: 1rem;
55 | /* display: flex;
56 | width: 100%;
57 | flex-shrink: 0;
58 | justify-content: space-between; */
59 | }
60 | .theme-change-division {
61 | padding: 0.5rem;
62 | cursor: pointer;
63 | border-radius: 5px;
64 | box-sizing: border-box;
65 | margin: 0 auto;
66 | width: 130px;
67 | }
68 | .theme-change-division:hover {
69 | background-color: rgb(26, 115, 232, 0.8);
70 | color: #fff;
71 | }
72 | .theme-change-background {
73 | border-radius: 5px;
74 | height: 90px;
75 | width: 100%;
76 | text-align: center;
77 | font-size: 2rem;
78 | background-color: aliceblue;
79 | color: #666;
80 | transition: 0.2s ease;
81 | }
82 | .theme-change-division-active {
83 | background-color: rgb(26, 115, 232, 0.3);
84 | }
85 | .theme-change-name {
86 | margin: 0.2rem 0;
87 | text-align: center;
88 | }
89 |
90 | /* Reset To Default */
91 | .reset-button {
92 | width: max-content;
93 | padding: 0.2rem 0.6rem;
94 | margin: 0.5rem 0;
95 | border-radius: 3px;
96 | box-sizing: border-box;
97 | cursor: pointer;
98 | border: 1px solid #0004;
99 | }
100 | .reset-button:hover {
101 | background-color: #0001;
102 | }
103 | .reset-button:active {
104 | background-color: #0002;
105 | }
106 |
107 | /* Personalise */
108 | .personalise-2-grid {
109 | display: grid;
110 | grid-template-columns: 1fr auto;
111 | gap: 1rem;
112 | margin: 1rem 0;
113 | }
114 | .personalise-2-grid:first-child {
115 | margin-top: 0;
116 | }
117 | .personalise-2-grid:last-child {
118 | margin-bottom: 0;
119 | }
120 | .personalise-text {
121 | font-size: 1rem;
122 | color: #000c;
123 | }
124 |
--------------------------------------------------------------------------------
/src/assets/applications/terminal.css:
--------------------------------------------------------------------------------
1 | .terminal-editable-container {
2 | height: 100%;
3 | width: 100%;
4 | background-color: rgb(45, 9, 34);
5 | color: #fff;
6 | overflow-y: auto;
7 | overflow-x: hidden;
8 | font-size: 0.9rem;
9 | cursor: text;
10 | }
11 | .terminal-editable-container::-webkit-scrollbar {
12 | width: 5px;
13 | }
14 | .terminal-editable-container::-webkit-scrollbar-thumb {
15 | border-radius: 10px;
16 | background-color: #888;
17 | }
18 | .terminal-green {
19 | color: rgba(124, 227, 53);
20 | }
21 | .terminal-blue {
22 | color: rgb(86, 137, 211);
23 | }
24 | .terminal-main-content {
25 | margin: 0.5rem 0;
26 | }
27 | .terminal-text-editor {
28 | padding: 0 0.5rem;
29 | box-sizing: border-box;
30 | }
31 | .terminal-text-editor:focus {
32 | outline: none;
33 | }
34 | .terminal-red {
35 | color: rgb(255, 0, 25);
36 | }
37 | .terminal-output {
38 | margin: 0.3rem 0;
39 | font-size: 0.9rem;
40 | letter-spacing: 0.05rem;
41 | }
42 | .terminal-help-grid {
43 | display: grid;
44 | grid-template-columns: 0.7fr auto 2fr;
45 | gap: 0.5rem;
46 | }
47 | .terminal-file-system-grid {
48 | display: grid;
49 | gap: 1rem;
50 | grid-template-columns: repeat(2, 1fr);
51 | }
52 | @media screen and (min-width: 500px) {
53 | .terminal-file-system-grid {
54 | grid-template-columns: repeat(4, 1fr);
55 | }
56 | }
57 | @media screen and (min-width: 800px) {
58 | .terminal-file-system-grid {
59 | grid-template-columns: repeat(6, 1fr);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/assets/background/wall-1.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/background/wall-2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/background/wall-3.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/background/wall-4.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/background/wall-5.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/background/wall-6.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/common/toggleButton.css:
--------------------------------------------------------------------------------
1 | .toggle-button-container {
2 | width: 50px;
3 | height: 30px;
4 | border-radius: 30px;
5 | box-sizing: border-box;
6 | background-color: #0002;
7 | cursor: pointer;
8 | transition: 0.2s ease;
9 | }
10 | .toggle-button {
11 | --dimension: 24px;
12 | transition: 0.2s ease;
13 | margin: 3px;
14 | height: var(--dimension);
15 | width: var(--dimension);
16 | background-color: #ffffff;
17 | box-shadow: 0 0 2px 0 rgb(0, 0, 0, 0.2);
18 | border-radius: 50%;
19 | }
20 | .toggle-button-container-active {
21 | background-color: rgb(234, 84, 33, 1);
22 | }
23 | .toggle-button-active {
24 | transform: translateX(20px);
25 | }
26 |
--------------------------------------------------------------------------------
/src/assets/default.css:
--------------------------------------------------------------------------------
1 | /* @import url("https://fonts.googleapis.com/css2?family=Roboto:wght@100;400;700&display=swap"); */
2 | @import url("https://fonts.googleapis.com/css2?family=Potta+One&display=swap");
3 | @import url("https://fonts.googleapis.com/css2?family=Raleway&display=swap");
4 | @import url("https://fonts.googleapis.com/css2?family=Lobster&display=swap");
5 |
6 | * {
7 | margin: 0;
8 | padding: 0;
9 | -webkit-user-select: none;
10 | -khtml-user-select: none;
11 | -moz-user-select: none;
12 | -o-user-select: none;
13 | user-select: none;
14 | --darkColor: #393f3f;
15 | --lightColor: #bbc2c0;
16 | --orangeColor: #ea5421f2;
17 | --white: #fff;
18 | --black: #000;
19 | --transparent: #0000;
20 | }
21 | .font-roboto {
22 | font-family: "Roboto", sans-serif;
23 | }
24 | .font-potta {
25 | font-family: "Potta One", cursive;
26 | }
27 | .font-raleway {
28 | font-family: "Raleway", sans-serif;
29 | }
30 | .font-lobster {
31 | font-family: "Lobster", cursive;
32 | }
33 | .font-times {
34 | font-family: "Times New Roman", Times, serif;
35 | }
36 | .font-courier {
37 | font-family: "Courier New", Courier, monospace;
38 | }
39 | .centralise {
40 | display: flex;
41 | flex-direction: column;
42 | justify-content: center;
43 | }
44 | .display-none {
45 | display: none;
46 | }
47 | .cursor-pointer {
48 | cursor: pointer;
49 | }
50 | .text-area-editor {
51 | height: 100%;
52 | width: 100%;
53 | resize: none;
54 | border: none;
55 | box-sizing: border-box;
56 | padding: 0.2rem 0.4rem;
57 | }
58 | .text-area-editor:focus {
59 | outline: none;
60 | }
61 | .text-area-editor::-webkit-scrollbar {
62 | width: 10px;
63 | }
64 | .text-area-editor::-webkit-scrollbar-thumb {
65 | width: 10px;
66 | background-color: rgba(234, 84, 33, 0.9);
67 | border-radius: 10px;
68 | }
69 | .button-base {
70 | border: none;
71 | outline: none;
72 | cursor: pointer;
73 | background-color: #0000;
74 | }
75 |
76 | .container-center {
77 | padding-right: 15px;
78 | padding-left: 15px;
79 | margin-right: auto;
80 | margin-left: auto;
81 | box-sizing: border-box;
82 | }
83 | .container {
84 | padding-right: 15px;
85 | padding-left: 15px;
86 | margin-right: auto;
87 | margin-left: auto;
88 | }
89 | @media (min-width: 768px) {
90 | .container {
91 | width: 750px;
92 | }
93 | }
94 | @media (min-width: 992px) {
95 | .container {
96 | width: 970px;
97 | }
98 | .container-center {
99 | width: 970px;
100 | }
101 | }
102 | @media (min-width: 1200px) {
103 | .container {
104 | width: 1170px;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/assets/desktop/desktop.css:
--------------------------------------------------------------------------------
1 | .desktop-container {
2 | height: 100vh;
3 | width: 100vw;
4 | overflow: hidden;
5 | color: white;
6 | }
7 | .image-cover {
8 | background-position: center;
9 | background-size: cover;
10 | background-repeat: no-repeat;
11 | }
12 | .desktop-taskbar-grid {
13 | display: grid;
14 | grid-template-rows: auto 1fr;
15 | height: 100%;
16 | }
17 |
--------------------------------------------------------------------------------
/src/assets/desktop/desktopIcon.css:
--------------------------------------------------------------------------------
1 | .desktop-icon-container {
2 | height: 90px;
3 | width: 80px;
4 | margin: 0 0.65rem 0.65rem 0;
5 | box-sizing: border-box;
6 | border-radius: 3px;
7 | transition: 0.1s ease;
8 | }
9 | .desktop-icon-container:hover {
10 | border: 1px solid #fff7;
11 | background-color: #fff2;
12 | }
13 | .desktop-icon-container:active {
14 | background-color: #fff5;
15 | }
16 | .desktop-icon {
17 | height: 100%;
18 | cursor: pointer;
19 | text-align: center;
20 | display: flex;
21 | flex-direction: column;
22 | justify-content: center;
23 | }
24 | .desktop-icon-text {
25 | /* display: inline-block; */
26 | font-size: 14px;
27 | width: 75px;
28 | margin: 0 auto;
29 | white-space: nowrap;
30 | overflow: hidden !important;
31 | text-overflow: ellipsis;
32 | }
33 |
--------------------------------------------------------------------------------
/src/assets/desktop/desktopWorkingArea.css:
--------------------------------------------------------------------------------
1 | .desktop-area-container {
2 | height: 100%;
3 | width: 100%;
4 | padding: 0.8rem;
5 | box-sizing: border-box;
6 | display: grid;
7 | grid-template-columns: repeat(10, auto) 1fr;
8 | animation: fade-in 0.2s ease-in forwards;
9 | opacity: 0;
10 | }
11 | @keyframes fade-in {
12 | from {
13 | opacity: 0;
14 | }
15 | to {
16 | opacity: 1;
17 | }
18 | }
19 | .link-footer > a {
20 | color: #666;
21 | text-decoration: none;
22 | }
23 | .link-footer > a:hover {
24 | text-decoration: underline;
25 | }
26 | .portfolio-container-iframe {
27 | height: 100%;
28 | width: 100%;
29 | border: none;
30 | }
31 | .new-file-folder-input {
32 | border: none;
33 | padding: 0.4rem 0.6rem;
34 | border: 1px solid #0002;
35 | border-radius: 3px;
36 | box-sizing: border-box;
37 | font-size: 1rem;
38 | width: 100%;
39 | }
40 | .new-file-folder-input:focus {
41 | outline: none;
42 | }
43 |
44 | /* Context Menu */
45 | .context-menu-container {
46 | position: absolute;
47 | height: 300px;
48 | width: 230px;
49 | color: var(--lightColor);
50 | background-color: var(--darkColor);
51 | border-radius: 5px;
52 | box-shadow: 0 0 10px 0 #0006;
53 | animation: opacAnim 0.2s ease;
54 | overflow: hidden;
55 | }
56 | @keyframes opacAnim {
57 | from {
58 | transform: scaleY(0.8);
59 | opacity: 0;
60 | }
61 | to {
62 | transform: scaleY(1);
63 | opacity: 1;
64 | }
65 | }
66 | .context-menu-content {
67 | padding: 0.5rem 1rem;
68 | box-sizing: border-box;
69 | cursor: pointer;
70 | transition: 0.2s ease;
71 | border-radius: 5px;
72 | }
73 | .context-menu-content:hover {
74 | background-color: var(--orangeColor);
75 | color: var(--white);
76 | }
77 | .context-menu-content:active {
78 | filter: brightness(0.8);
79 | }
80 |
--------------------------------------------------------------------------------
/src/assets/desktop/dialogBox.css:
--------------------------------------------------------------------------------
1 | .dialog-overlay {
2 | top: 0;
3 | left: 0;
4 | height: 100vh;
5 | width: 100vw;
6 | position: absolute;
7 | }
8 | .dialog-overlay {
9 | background-color: #0004;
10 | z-index: 20;
11 | }
12 | .dialog-container {
13 | position: absolute;
14 | top: 50%;
15 | left: 50%;
16 | transform: translate(-50%, -50%);
17 | background-color: #fff;
18 | max-width: 500px;
19 | margin: 0 auto;
20 | color: #212121;
21 | border-radius: 5px;
22 | z-index: 21;
23 | width: 100%;
24 | max-width: 500px;
25 | border: 1px solid #0002;
26 | box-sizing: border-box;
27 | }
28 | .dialog-header {
29 | background-color: #00000008;
30 | border-bottom: 1px solid #0002;
31 | padding: 0.6rem 0.8rem;
32 | font-size: 1.1rem;
33 | font-weight: bolder;
34 | letter-spacing: 0.05rem;
35 | box-sizing: border-box;
36 | }
37 | .dialog-body {
38 | padding: 1rem;
39 | box-sizing: border-box;
40 | border-bottom: 1px solid #0002;
41 | }
42 | .dialog-footer {
43 | font-weight: bolder;
44 | background-color: #00000008;
45 | display: grid;
46 | grid-template-columns: repeat(2, 1fr);
47 | }
48 | .dialog-footer-btn {
49 | padding: 0.5rem;
50 | text-align: center;
51 | cursor: pointer;
52 | transition: 0.2s ease;
53 | color: #212121;
54 | box-sizing: border-box;
55 | }
56 | .dialog-footer-btn:first-child {
57 | border-right: 1px solid #0002;
58 | }
59 | .dialog-footer-btn:hover {
60 | background-color: #00000020;
61 | }
62 |
--------------------------------------------------------------------------------
/src/assets/desktop/dropdown.css:
--------------------------------------------------------------------------------
1 | .drop-down-container {
2 | position: absolute;
3 | right: 10px;
4 | top: 34px;
5 | width: 100%;
6 | max-width: 300px;
7 | display: grid;
8 | grid-template-rows: auto 1fr;
9 | z-index: 10;
10 | }
11 | .drop-drop-caret-pointed-container {
12 | transform: rotate(180deg);
13 | }
14 | .flex-end {
15 | display: grid;
16 | grid-template-columns: 1fr auto;
17 | margin-left: 1.5rem;
18 | }
19 | .drop-drop-caret-pointed {
20 | transform: translateX(10px);
21 | }
22 | .drop-down-inner-container {
23 | box-shadow: 0 2px 5px 0 #0002;
24 | background-color: var(--darkColor);
25 | color: var(--lightColor);
26 | border-radius: 5px;
27 | transform: translateY(-7px);
28 | padding: 0.8rem 0;
29 | box-sizing: border-box;
30 | }
31 | .drop-down-items {
32 | max-height: 30.4px;
33 | box-sizing: border-box;
34 | padding: 0.4rem 1rem;
35 | transition: 0.2s ease;
36 | }
37 | .drop-down-items:hover {
38 | background-color: #0002;
39 | }
40 | .network-dot {
41 | height: 10px;
42 | width: 10px;
43 | margin-right: 0.5rem;
44 | border-radius: 50%;
45 | display: inline-block;
46 | }
47 | .dropdown-hr {
48 | height: 1px;
49 | width: 30px;
50 | background-color: #aaa;
51 | margin: 0.5rem auto;
52 | }
53 | .drop-down-grid {
54 | display: grid;
55 | grid-template-columns: auto 1fr;
56 | gap: 1rem;
57 | }
58 | .brightness-scroll-line {
59 | height: 2px;
60 | width: 100%;
61 | background-color: #666;
62 | display: flex;
63 | }
64 | .no-cursor {
65 | cursor: default;
66 | }
67 | .brightness-scroll-line::-webkit-slider-thumb {
68 | height: 15px;
69 | border-radius: 50%;
70 | width: 15px;
71 | background: #fff;
72 | box-shadow: 0 0 2px 0 #777;
73 | cursor: pointer;
74 | }
75 |
--------------------------------------------------------------------------------
/src/assets/desktop/explorer.css:
--------------------------------------------------------------------------------
1 | .explorer-container {
2 | position: absolute;
3 | display: grid;
4 | grid-template-rows: auto 1fr auto;
5 | border-radius: 3px;
6 | overflow: hidden;
7 | box-shadow: 0 0 10px 1px #0002;
8 | }
9 | .explorer-heading-name-icon-container {
10 | display: grid;
11 | height: 100%;
12 | grid-template-columns: auto 1fr;
13 | gap: 0.5rem;
14 | }
15 | .explorer-header {
16 | background: linear-gradient(rgb(53, 49, 50), rgb(50, 49, 50));
17 | display: grid;
18 | grid-template-columns: 1fr auto;
19 | gap: 1rem;
20 | cursor: grab;
21 | }
22 | .explorer-header:active {
23 | cursor: grabbing;
24 | }
25 | .explorer-header-heading {
26 | margin: 0.2rem 0.5rem;
27 | }
28 | .explorer-header-btn-container {
29 | display: grid;
30 | grid-template-columns: repeat(3, 1fr);
31 | gap: 0.5rem;
32 | margin: 0.4rem 0.5rem;
33 | }
34 | .explorer-close-btn {
35 | height: 18px;
36 | width: 18px;
37 | display: flex;
38 | flex-direction: column;
39 | text-align: center;
40 | justify-content: center;
41 | cursor: pointer;
42 | border-radius: 50%;
43 | transition: 0.2s ease;
44 | font-size: 1.1rem;
45 | }
46 | .explorer-close-color {
47 | background-color: rgb(238, 81, 35);
48 | }
49 | .explorer-close-btn:hover {
50 | background-color: #fff4;
51 | }
52 | .explorer-close-icon-translate {
53 | transform: translateY(-2px);
54 | }
55 | .explorer-body {
56 | background-color: #fff;
57 | color: #212121;
58 | overflow: hidden;
59 | box-sizing: border-box;
60 | }
61 | .explorer-footer {
62 | border-top: 1px solid #0003;
63 | background-color: rgb(247, 247, 247);
64 | color: #212121;
65 | padding: 0.2rem 0.5rem;
66 | box-sizing: border-box;
67 | }
68 | .sc-EHOje.dMFuoo {
69 | overflow-x: hidden;
70 | }
71 |
--------------------------------------------------------------------------------
/src/assets/desktop/lowerDesktop.css:
--------------------------------------------------------------------------------
1 | .lower-desktop-grid {
2 | display: grid;
3 | grid-template-columns: auto 1fr;
4 | }
5 | .left-navigation-bar {
6 | width: 60px;
7 | display: grid;
8 | grid-template-rows: 1fr auto;
9 | box-sizing: border-box;
10 | background-color: #0004;
11 | }
12 | .left-nav-item {
13 | cursor: pointer;
14 | height: 50px;
15 | width: 50px;
16 | margin: 0.5rem auto;
17 | border-radius: 5px;
18 | text-align: center;
19 | transition: 0.1s ease-in-out;
20 | }
21 | .left-nav-item:hover {
22 | background-color: #fff4;
23 | }
24 | .left-nav-item-active {
25 | background-color: #fff2;
26 | }
27 | .left-nav-item:active {
28 | transform: scale(0.95);
29 | }
30 | .start-icon-svg {
31 | stroke: white;
32 | stroke-width: 4;
33 | transition: 0.2s ease-in-out;
34 | fill: #0000;
35 | r: 15px;
36 | }
37 | .start-icon-container:hover .start-icon-svg {
38 | r: 10px;
39 | stroke-width: 20;
40 | }
41 | .nav-item-image {
42 | height: auto;
43 | /* width: 40px; */
44 | margin: 0 auto;
45 | }
46 |
--------------------------------------------------------------------------------
/src/assets/desktop/powerOff.css:
--------------------------------------------------------------------------------
1 | .power-off-container {
2 | position: fixed;
3 | top: 0;
4 | left: 0;
5 | height: 100vh;
6 | width: 100vw;
7 | z-index: 50;
8 | opacity: 0;
9 | animation: powerOff 1s ease forwards;
10 | }
11 | .power-off-inner-container {
12 | height: 100%;
13 | width: 100%;
14 | color: #fff;
15 | opacity: 0;
16 | animation: powerOff 1s ease forwards;
17 | animation-delay: 1s;
18 | box-sizing: border-box;
19 | padding: 0 1rem;
20 | }
21 | .power-off-loader-container {
22 | display: grid;
23 | grid-template-columns: auto auto;
24 | gap: 1rem;
25 | width: max-content;
26 | margin: 0 auto;
27 | }
28 | .shutdown-loader-text {
29 | font-size: 1.8rem;
30 | font-weight: bolder;
31 | letter-spacing: 0.05rem;
32 | }
33 | @keyframes powerOff {
34 | from {
35 | opacity: 0;
36 | }
37 | to {
38 | opacity: 1;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/assets/desktop/startMenu.css:
--------------------------------------------------------------------------------
1 | .start-menu-container {
2 | background-color: #0005;
3 | animation: fade-in 0.2s ease-in forwards;
4 | opacity: 0;
5 | height: calc(100vh - 30px);
6 | }
7 | @keyframes fade-in {
8 | from {
9 | opacity: 0;
10 | }
11 | to {
12 | opacity: 1;
13 | }
14 | }
15 | .search-field-container {
16 | margin: 1rem 0;
17 | text-align: center;
18 | }
19 | .start-menu-container-grid {
20 | display: grid;
21 | gap: 1rem;
22 | grid-template-rows: auto 1fr auto;
23 | height: calc(100vh - 31px);
24 | }
25 | input.search-field {
26 | margin: 0 auto;
27 | border-radius: 0 30px 30px 0;
28 | box-sizing: border-box;
29 | border: none;
30 | width: 100%;
31 | color: #555;
32 | padding: 0.3rem 0;
33 | padding-right: 0.6rem;
34 | }
35 | input.search-field::placeholder {
36 | color: #0006;
37 | }
38 | input.search-field:focus {
39 | outline: none;
40 | }
41 | .search-field-grid {
42 | display: grid;
43 | width: 100%;
44 | max-width: 240px;
45 | margin: 0 auto;
46 | grid-template-columns: auto 1fr;
47 | }
48 | .search-bar-icon-container {
49 | border-radius: 30px 0 0 30px;
50 | background-color: #fff;
51 | text-align: center;
52 | box-sizing: border-box;
53 | padding: auto 0.8rem;
54 | }
55 | .search-bar-icon {
56 | margin-left: 0.6rem;
57 | margin-right: 0.5rem;
58 | cursor: text;
59 | }
60 | .start-application-container {
61 | box-sizing: border-box;
62 | display: grid;
63 | grid-template-columns: repeat(2, 1fr);
64 | }
65 | .start-menu-item {
66 | height: 120px;
67 | width: 110px;
68 | padding: 1rem;
69 | box-sizing: border-box;
70 | cursor: pointer;
71 | text-align: center;
72 | margin: 0 auto;
73 | display: grid;
74 | grid-template-rows: 1fr auto;
75 | gap: 0.5rem;
76 | transition: 0.2s ease;
77 | font-size: 1.2rem;
78 | border-radius: 3px;
79 | }
80 | .start-menu-item:hover {
81 | background-color: #ffffff15;
82 | box-shadow: inset 1px 2px 10px 0 #ffffff15;
83 | }
84 | .start-menu-footer-grid {
85 | display: grid;
86 | width: max-content;
87 | gap: 1rem;
88 | margin: 1rem auto;
89 | grid-template-columns: repeat(2, auto);
90 | }
91 | .start-menu-footer-item {
92 | border-bottom: 0.2rem solid #0000;
93 | padding: 0.5rem;
94 | box-sizing: border-box;
95 | cursor: pointer;
96 | color: #fff8;
97 | transition: 0.2s ease;
98 | }
99 | .start-menu-footer-item:hover {
100 | color: #fff;
101 | }
102 | .start-menu-footer-item-active {
103 | color: #fff;
104 | border-color: #ea5421;
105 | }
106 | @media screen and (min-width: 600px) {
107 | .start-application-container {
108 | grid-template-columns: repeat(3, 1fr);
109 | }
110 | }
111 | @media screen and (min-width: 1000px) {
112 | .start-application-container {
113 | grid-template-columns: repeat(4, 1fr);
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/assets/desktop/taskList.css:
--------------------------------------------------------------------------------
1 | .heading {
2 | padding-right: 1rem;
3 | /* border-right: 1px dashed white; */
4 | }
5 | .task-list-container {
6 | display: flex;
7 | overflow: hidden;
8 | }
9 | .task-listing-activity {
10 | margin: 0 0.2rem;
11 | padding: 0.02rem 0.5rem;
12 | border-radius: 5px;
13 | box-sizing: border-box;
14 | cursor: default;
15 | transition: 0.2s;
16 | border-radius: 2px;
17 | display: grid;
18 | grid-template-columns: auto 1fr;
19 | gap: 0.3rem;
20 | }
21 | .task-listing-activity:hover {
22 | background-color: #fff3;
23 | }
24 | .task-listing-activity-active {
25 | background-color: #fff2;
26 | }
27 | .task-list-inner-grid {
28 | display: grid;
29 | grid-template-columns: auto 1fr auto;
30 | gap: 0.2rem;
31 | }
32 | .loader-sm {
33 | r: 5px;
34 | cy: 10px;
35 | cx: 10px;
36 | stroke: var(--black);
37 | stroke-width: 2px;
38 | stroke-dasharray: 20 8;
39 | fill: var(--transparent);
40 | }
41 | .loader-sm-2 {
42 | r: 5px;
43 | cy: 8px;
44 | cx: 8px;
45 | stroke: var(--white);
46 | stroke-width: 2px;
47 | stroke-dasharray: 20 8;
48 | fill: var(--transparent);
49 | }
50 | .loader-lg {
51 | r: 10px;
52 | cy: 20px;
53 | cx: 20px;
54 | stroke: var(--white);
55 | stroke-width: 5px;
56 | fill: #0000;
57 | stroke-dasharray: 30 20;
58 | }
59 | .loader-rotate {
60 | animation: rotate 1s linear infinite;
61 | }
62 | @keyframes rotate {
63 | from {
64 | transform: rotate(0);
65 | }
66 | to {
67 | transform: rotate(360deg);
68 | }
69 | }
70 | .activity-inner-container {
71 | border-radius: 3px;
72 | padding: 0.5rem 0 !important;
73 | box-sizing: border-box;
74 | transform: translateY(-3px) !important;
75 | }
76 | .activity-list-container {
77 | max-width: 200px !important;
78 | top: 34px !important;
79 | left: 66px !important;
80 | }
81 | .activity-list-nav-grid {
82 | display: grid;
83 | grid-template-columns: auto auto;
84 | gap: 0.5rem;
85 | }
86 | .down-caret-arrow-translate {
87 | transform: translateY(-2px);
88 | }
89 | .activity-close-btn {
90 | height: 16px;
91 | width: 16px;
92 | text-align: center;
93 | border-radius: 50%;
94 | color: var(--lightColor);
95 | cursor: pointer;
96 | transition: 0.2s ease;
97 | }
98 | .activity-close-btn:hover {
99 | background-color: var(--orangeColor);
100 | color: var(--white);
101 | }
102 |
--------------------------------------------------------------------------------
/src/assets/desktop/taskbar.css:
--------------------------------------------------------------------------------
1 | .taskbar-container {
2 | width: 100vw;
3 | background-color: rgba(0, 0, 0, 0.6);
4 | box-shadow: 0 0 7px 2px #0009;
5 | font-size: 0.9rem;
6 | padding: 0.4rem 0.5rem;
7 | box-sizing: border-box;
8 | display: grid;
9 | grid-template-columns: 1fr auto 1fr;
10 | gap: 0.5rem;
11 | }
12 | .right-task-item-container {
13 | display: flex;
14 | flex-direction: row;
15 | justify-content: flex-end;
16 | }
17 | .right-displayed-container {
18 | cursor: pointer;
19 | transition: 0.2s ease;
20 | display: grid;
21 | gap: 0.5rem;
22 | grid-template-columns: repeat(5, auto);
23 | }
24 | .right-displayed-container-after {
25 | content: "";
26 | display: block;
27 | background-color: #fff;
28 | width: 125px;
29 | height: 3px;
30 | position: absolute;
31 | border-radius: 10px;
32 | transform: translateY(5px);
33 | }
34 | .drop-caret {
35 | transition: 0.2s ease;
36 | }
37 | .drop-caret-up {
38 | transform: rotate(180deg);
39 | }
40 |
--------------------------------------------------------------------------------
/src/assets/icons/archive.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/battery.svg:
--------------------------------------------------------------------------------
1 |
2 |
44 |
--------------------------------------------------------------------------------
/src/assets/icons/brightness.svg:
--------------------------------------------------------------------------------
1 |
2 |
64 |
--------------------------------------------------------------------------------
/src/assets/icons/browser.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/camera.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/codepen.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/download.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/dropdown-point.svg:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/dropdown-white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
42 |
--------------------------------------------------------------------------------
/src/assets/icons/dropdown.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
42 |
--------------------------------------------------------------------------------
/src/assets/icons/facebook.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/folder.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/gmail.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/home.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
49 |
--------------------------------------------------------------------------------
/src/assets/icons/instagram.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/lighting.svg:
--------------------------------------------------------------------------------
1 |
2 |
40 |
--------------------------------------------------------------------------------
/src/assets/icons/linkedin.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/medium.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/octocat.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/profile.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/project.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/search.svg:
--------------------------------------------------------------------------------
1 |
2 |
66 |
--------------------------------------------------------------------------------
/src/assets/icons/setting.svg:
--------------------------------------------------------------------------------
1 |
2 |
12 |
--------------------------------------------------------------------------------
/src/assets/icons/sponsorship.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raghavdhingra/Web-OS/b9ab826d6761759b27439ac853a3d190ecf51ed0/src/assets/icons/sponsorship.png
--------------------------------------------------------------------------------
/src/assets/icons/stopwatch.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/terminal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raghavdhingra/Web-OS/b9ab826d6761759b27439ac853a3d190ecf51ed0/src/assets/icons/terminal.png
--------------------------------------------------------------------------------
/src/assets/icons/terminal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/transfer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raghavdhingra/Web-OS/b9ab826d6761759b27439ac853a3d190ecf51ed0/src/assets/icons/transfer.png
--------------------------------------------------------------------------------
/src/assets/icons/twitter.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raghavdhingra/Web-OS/b9ab826d6761759b27439ac853a3d190ecf51ed0/src/assets/icons/user.png
--------------------------------------------------------------------------------
/src/assets/icons/wifi.svg:
--------------------------------------------------------------------------------
1 |
2 |
50 |
--------------------------------------------------------------------------------
/src/components/applications/browser/browser.jsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useState } from "react";
2 | import HOME_ICON from "../../../assets/icons/home.svg";
3 | import SEARCH_ICON from "../../../assets/icons/search.svg";
4 | import "../../../assets/applications/browser.css";
5 |
6 | const Browser = () => {
7 | const [browserLink, setBrowserLink] = useState("https://ekoru.org");
8 | const [searchStr, setSearchStr] = useState("");
9 | const [isLoading, setIsLoading] = useState(true);
10 |
11 | const searchBrowser = useCallback(() => {
12 | if (searchStr) {
13 | setIsLoading(true);
14 | setBrowserLink(`https://ekoru.org/?q=${searchStr}`);
15 | setSearchStr("");
16 | }
17 | }, [searchStr]);
18 |
19 | const changeSearchStr = useCallback((e) => {
20 | e.preventDefault();
21 | setSearchStr(e.target.value);
22 | }, []);
23 |
24 | const homeLink = useCallback(() => {
25 | setBrowserLink("");
26 | setTimeout(() => {
27 | setIsLoading(true);
28 | setBrowserLink("https://ekoru.org");
29 | }, 50);
30 | }, []);
31 |
32 | return (
33 |
34 |
35 |
36 |

37 |
38 |
e.keyCode === 13 && searchBrowser()}
44 | onChange={changeSearchStr}
45 | />
46 |
47 |

48 |
49 |
50 |
68 | );
69 | };
70 |
71 | export default Browser;
72 |
--------------------------------------------------------------------------------
/src/components/applications/camera/camera.jsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useEffect, useRef, useState } from "react";
2 | import { connect } from "react-redux";
3 | import "../../../assets/applications/camera.css";
4 | import DOWNLOAD_ICON from "../../../assets/icons/download.svg";
5 | import STOP_WATCH from "../../../assets/icons/stopwatch.svg";
6 |
7 | const Camera = ({ supplement, triggerIndex, isTriggered, activityList }) => {
8 | const videoRef = useRef(null);
9 | const canvasRef = useRef(null);
10 | const runningTimer = useRef(null);
11 |
12 | const [timer, setTimer] = useState(0);
13 | const [isCaptured, setIsCaptured] = useState(false);
14 | const [mediaObject, setMediaObject] = useState(null);
15 | const [isCapturing, setIsCapturing] = useState(false);
16 |
17 | const drawOnCanvas = useCallback(() => {
18 | try {
19 | const canvas = canvasRef.current;
20 | const ctx = canvas.getContext("2d");
21 | const width = videoRef.current.offsetWidth;
22 | const height = videoRef.current.offsetHeight;
23 | const WHRatio = width / height;
24 | ctx.drawImage(videoRef.current, 0, 0, 300, 300 / WHRatio);
25 |
26 | const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
27 | const data = imageData.data;
28 |
29 | for (let y = 0; y < canvas.height; y++) {
30 | for (let x = 0; x < canvas.width / 2; x++) {
31 | const i = (y * canvas.width + x) * 4;
32 | const mirrorI = (y * canvas.width + (canvas.width - 1 - x)) * 4;
33 |
34 | // Swap pixel values between the original and mirrored positions
35 | for (let j = 0; j < 4; j++) {
36 | const temp = data[i + j];
37 | data[i + j] = data[mirrorI + j];
38 | data[mirrorI + j] = temp;
39 | }
40 | }
41 | }
42 | ctx.putImageData(imageData, 0, 0);
43 |
44 | setIsCapturing(false);
45 | } catch (err) {
46 | return null;
47 | }
48 | }, []);
49 |
50 | const captureMoment = useCallback(() => {
51 | setIsCapturing(true);
52 | if (isCaptured) {
53 | setIsCaptured(false);
54 | }
55 | setTimeout(() => {
56 | drawOnCanvas();
57 | setIsCaptured(true);
58 | }, timer * 1000);
59 | }, [drawOnCanvas, isCaptured, timer]);
60 |
61 | const stopCamera = useCallback(
62 | () => mediaObject && mediaObject.stop(),
63 | [mediaObject]
64 | );
65 |
66 | const changeTimer = useCallback(() => {
67 | let timeArray = [0, 2, 5, 10];
68 | let index = timeArray.indexOf(timer);
69 | setTimer(
70 | index === timeArray.length - 1 ? timeArray[0] : timeArray[index + 1]
71 | );
72 | }, [timer]);
73 |
74 | const downloadImage = useCallback(() => {
75 | const link = document.createElement("a");
76 | link.download = "camera-screenshot.png";
77 | link.href = canvasRef.current.toDataURL();
78 | link.click();
79 | }, []);
80 |
81 | useEffect(() => {
82 | let indexToRemove = activityList.findIndex(
83 | (e) => e.date === supplement.activity.date
84 | );
85 | if (indexToRemove === triggerIndex && isTriggered) stopCamera();
86 | }, [triggerIndex, isTriggered, activityList, stopCamera, supplement]);
87 |
88 | useEffect(() => {
89 | let videoStream;
90 | if (videoRef) {
91 | const getVideoStream = async () => {
92 | try {
93 | videoStream = await navigator.mediaDevices.getUserMedia({
94 | video: true,
95 | audio: false,
96 | });
97 | videoRef.current.srcObject = videoStream;
98 | videoRef.current?.play();
99 | setMediaObject(videoStream.getTracks()[0]);
100 | } catch {
101 | console.log("Camera permission not given");
102 | }
103 | };
104 | getVideoStream();
105 | }
106 | return () => {
107 | if (videoStream) videoStream.getTracks()[0].stop();
108 | };
109 | }, []);
110 |
111 | useEffect(() => {
112 | if (isCapturing && timer) {
113 | let latestTimer = timer;
114 | runningTimer.current.innerHTML = `${timer}`;
115 | let interval = setInterval(() => {
116 | if (latestTimer === 0) clearInterval(interval);
117 | else {
118 | latestTimer = latestTimer - 1;
119 | try {
120 | runningTimer.current.innerHTML = `${latestTimer}`;
121 | } catch (err) {
122 | return null;
123 | }
124 | }
125 | }, [1000]);
126 | }
127 | }, [isCapturing, timer]);
128 |
129 | return (
130 | <>
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |

140 | {timer}s
141 |
142 |
143 |
154 |
155 |
156 |
157 |
158 | {isCapturing && (
159 |
160 | )}
161 |
162 |
166 | {isCaptured && (
167 |
178 | )}
179 | {isCaptured && (
180 |
186 | )}
187 |
188 |
189 | >
190 | );
191 | };
192 |
193 | const mapStateToProps = (state) => ({
194 | triggerIndex: state.activityReducers.triggerIndex,
195 | isTriggered: state.activityReducers.isTriggered,
196 | activityList: state.activityReducers.activity,
197 | });
198 | export default connect(mapStateToProps)(Camera);
199 |
--------------------------------------------------------------------------------
/src/components/applications/settings/components/FontChanger.jsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo } from "react";
2 | import { connect } from "react-redux";
3 | import { changeFontStyle } from "../../../../actions/desktopActions";
4 |
5 | const FontChanger = ({ changeFontStyle, font }) => {
6 | const fontStyleArray = useMemo(
7 | () => [
8 | { name: "Roboto", className: "font-roboto" },
9 | { name: "Potta One", className: "font-potta" },
10 | { name: "Raleway", className: "font-raleway" },
11 | { name: "Lobster", className: "font-lobster" },
12 | { name: "Times", className: "font-times" },
13 | { name: "Courier", className: "font-courier" },
14 | ],
15 | []
16 | );
17 | return (
18 | <>
19 |
20 | {fontStyleArray.map((fontStyle, index) => (
21 |
changeFontStyle(index + 1)}
27 | >
28 |
Aa
29 |
{fontStyle.name}
30 |
31 | ))}
32 |
33 | >
34 | );
35 | };
36 |
37 | const mapStateToProps = (state) => ({
38 | font: state.desktopReducers.fontStyle,
39 | });
40 | export default connect(mapStateToProps, { changeFontStyle })(FontChanger);
41 |
--------------------------------------------------------------------------------
/src/components/applications/settings/components/Personalise.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { connect } from "react-redux";
3 | import {
4 | changeSingleClickIcon,
5 | toggleFullScreen,
6 | } from "../../../../actions/desktopActions";
7 | import ToggleButton from "../../../common/ToggleButton";
8 |
9 | const Personalise = ({
10 | singleClickIcon,
11 | changeSingleClickIcon,
12 | isFullScreen,
13 | toggleFullScreen,
14 | }) => {
15 | return (
16 | <>
17 |
18 |
19 | Desktop icons open on single click
20 |
21 |
changeSingleClickIcon(!singleClickIcon)}
23 | toggleOn={singleClickIcon}
24 | />
25 |
26 |
27 |
Full Screen mode
28 |
toggleFullScreen()}
30 | toggleOn={isFullScreen}
31 | />
32 |
33 | >
34 | );
35 | };
36 |
37 | const mapStateToProps = (state) => ({
38 | singleClickIcon: state.desktopReducers.singleClickIcon,
39 | isFullScreen: state.desktopReducers.isFullScreen,
40 | });
41 | export default connect(mapStateToProps, {
42 | changeSingleClickIcon,
43 | toggleFullScreen,
44 | })(Personalise);
45 |
--------------------------------------------------------------------------------
/src/components/applications/settings/components/ThemeChanger.jsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo } from "react";
2 | import { connect } from "react-redux";
3 | import { changeBackImage } from "../../../../actions/desktopActions";
4 | import Back1 from "../../../../assets/background/wall-1.svg";
5 | import Back2 from "../../../../assets/background/wall-2.svg";
6 | import Back3 from "../../../../assets/background/wall-3.svg";
7 | import Back4 from "../../../../assets/background/wall-4.svg";
8 | import Back5 from "../../../../assets/background/wall-5.svg";
9 | import Back6 from "../../../../assets/background/wall-6.svg";
10 |
11 | const ThemeChanger = ({ background, changeBackImage }) => {
12 | const themeArray = useMemo(
13 | () => [
14 | { name: "Eternal", img: Back1, cover: true },
15 | { name: "Temporal", img: Back2, cover: true },
16 | { name: "Speck", img: Back3, cover: false },
17 | { name: "Chime", img: Back4, cover: true },
18 | { name: "Karma", img: Back5, cover: true },
19 | { name: "Plates", img: Back6, cover: false },
20 | ],
21 | []
22 | );
23 | return (
24 | <>
25 |
26 | {themeArray.map((theme, index) => (
27 |
changeBackImage(index + 1)}
33 | >
34 |
42 |
{theme.name}
43 |
44 | ))}
45 |
46 | >
47 | );
48 | };
49 |
50 | const mapStateToProps = (state) => ({
51 | background: state.desktopReducers.background,
52 | });
53 | export default connect(mapStateToProps, { changeBackImage })(ThemeChanger);
54 |
--------------------------------------------------------------------------------
/src/components/applications/settings/settings.jsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useState } from 'react';
2 | import ThemeChanger from './components/ThemeChanger';
3 | import FontChanger from './components/FontChanger';
4 | import Personalise from './components/Personalise';
5 | import { resetToDefault } from '../../../actions/desktopActions';
6 | import DialogBox from '../../desktop/dialogBox/dialogBox';
7 | import '../../../assets/applications/settings.css';
8 | import { connect } from 'react-redux';
9 |
10 | const Settings = ({ resetToDefault }) => {
11 | const [settingIndex, setSettingIndex] = useState(0);
12 | const [isDialogOpen, setIsDialogOpen] = useState(false);
13 |
14 | const settingsArray = useMemo(
15 | () => [
16 | { name: 'Theme', component: },
17 | { name: 'Font Style', component: },
18 | { name: 'Personalise', component: },
19 | {
20 | name: 'Reset to Default',
21 | onClick: () => setIsDialogOpen(true),
22 | },
23 | ],
24 | []
25 | );
26 | const resetSuccess = () => {
27 | setIsDialogOpen(false);
28 | resetToDefault();
29 | };
30 | return (
31 | <>
32 | setIsDialogOpen(false)}
35 | isOpen={isDialogOpen}
36 | successText={'Reset'}
37 | heading={'Reset Settings'}
38 | body={'Sure! You want to reset to your default settings?'}
39 | />
40 |
41 |
42 | {settingsArray.map((settings, index) => (
43 |
49 | settings.component ? setSettingIndex(index) : settings.onClick()
50 | }
51 | >
52 | {settings.name}
53 |
54 | ))}
55 |
56 |
57 |
58 |
59 | {settingsArray[settingIndex].name}
60 |
61 |
62 | {settingsArray[settingIndex].component}
63 |
64 |
65 |
66 |
67 | >
68 | );
69 | };
70 |
71 | export default connect(null, { resetToDefault })(Settings);
72 |
--------------------------------------------------------------------------------
/src/components/applications/terminal/terminal.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState, useRef } from "react";
2 | import { connect } from "react-redux";
3 | import { removeActivity } from "../../../actions/activityActions";
4 | import { resetToDefault } from "../../../actions/desktopActions";
5 | import {
6 | makeDirectoryAction,
7 | removeDirectoryAction,
8 | } from "../../../actions/fileSystemActions";
9 | import "../../../assets/applications/terminal.css";
10 |
11 | const OutputDivision = ({ inputPath, command, error, success, startState }) => {
12 | if (startState)
13 | return (
14 | <>
15 |
16 | Welcome to Web OS
17 |
18 | Type "help" for all the commands
19 | >
20 | );
21 |
22 | return (
23 |
24 |
raghavdhingra@web-os:
25 |
{inputPath}$
26 |
{command}
27 | {error &&
{error}
}
28 | {success &&
{success}
}
29 |
30 | );
31 | };
32 |
33 | const TerminalWindow = ({
34 | fileSystem,
35 | activityList,
36 | removeActivity,
37 | makeDirectoryAction,
38 | removeDirectoryAction,
39 | resetToDefault,
40 | supplement: { terminalLocation },
41 | }) => {
42 | const printOutput = ({ inputPath, command, error, success, startState }) => {
43 | let outputCommand = (
44 |
51 | );
52 | setCommandOutput([...commandOutput, outputCommand]);
53 | };
54 | const [inputPath, setInputPath] = useState("/");
55 | const [historyCommands, setHistoryCommand] = useState(["help"]);
56 | const [historyIndex, setHistoryIndex] = useState(0);
57 | const [commandOutput, setCommandOutput] = useState([
58 | ,
59 | ]);
60 | const TextRef = useRef(null);
61 |
62 | const emptyTextRef = () => {
63 | setTimeout(() => {
64 | if (TextRef.current) TextRef.current.innerText = "";
65 | }, 10);
66 | };
67 |
68 | const clearScreen = () =>
69 | setCommandOutput([]);
70 |
71 | const echoOnScreen = ({ command, tokens, isSudo }) => {
72 | if (isSudo) tokens.shift();
73 | tokens.shift();
74 | return printOutput({ inputPath, command, success: tokens.join(" ") });
75 | };
76 | const setInputPathConditionally = (pathArr) => {
77 | pathArr = pathArr.filter((system) => !!system);
78 | if (pathArr.length) setInputPath(`/${pathArr.join("/")}/`);
79 | else setInputPath("/");
80 | };
81 | const changeDirectory = ({ command, tokens, isSudo }) => {
82 | if (isSudo) tokens.shift();
83 | try {
84 | if (tokens.length > 2) {
85 | return printOutput({
86 | inputPath,
87 | command,
88 | error: `"cd" command can't have more than 1 parameter`,
89 | });
90 | } else {
91 | if (tokens[1] === "/") {
92 | setInputPath("/");
93 | return printOutput({ inputPath, command });
94 | }
95 | let fullPath = inputPath.split("/").filter((path) => !!path);
96 | let givenDirList = tokens[1].split("/").filter((path) => !!path);
97 | for (let i in givenDirList) {
98 | if (givenDirList[i] === ".") break;
99 | else if (givenDirList[i] === "..") {
100 | if (fullPath.length) fullPath.pop();
101 | else
102 | return printOutput({
103 | inputPath,
104 | command,
105 | error: "Already on the base directory",
106 | });
107 | } else fullPath.push(givenDirList[i]);
108 | }
109 | let curDir = fileSystem;
110 | for (let j in fullPath) {
111 | try {
112 | curDir = curDir.find(
113 | (dir) => dir.name === fullPath[j] && dir.type === "folder"
114 | );
115 | curDir = curDir.child;
116 | } catch (err) {
117 | return printOutput({
118 | inputPath,
119 | command,
120 | error: "No such directory exists",
121 | });
122 | }
123 | }
124 | setInputPathConditionally(fullPath);
125 | return printOutput({ inputPath, command });
126 | }
127 | } catch (err) {
128 | printOutput({ inputPath, command, error: "Please specify a folder" });
129 | }
130 | };
131 | const exitTerminal = ({ command, tokens }) => {
132 | if (tokens.length > 1) {
133 | return printOutput({
134 | inputPath,
135 | command,
136 | error: `"exit" command can't have more than 1 parameter`,
137 | });
138 | }
139 | let activityIndex = activityList.findIndex(
140 | (activity) => activity.name === "Terminal"
141 | );
142 | removeActivity(activityIndex);
143 | };
144 | const HelpTerminal = ({ command }) => {
145 | let childParameter = (
146 |
147 | {commandList.map((com, index) => (
148 |
149 | {com.invoke}
150 | -
151 | {com.description}
152 |
153 | ))}
154 |
155 | );
156 | return printOutput({ inputPath, command, success: childParameter });
157 | };
158 | const listInDirectory = ({ command }) => {
159 | let pathArray = inputPath.split("/").filter((paths) => !!paths);
160 | let childParameter;
161 | try {
162 | let currentDir = fileSystem;
163 | pathArray.forEach(
164 | (path) =>
165 | (currentDir = currentDir.find((system) => system.name === path).child)
166 | );
167 | childParameter = (
168 |
169 | {currentDir.map((system, index) => (
170 |
171 |
172 | {system.name}
173 |
174 |
175 | ))}
176 |
177 | );
178 | return printOutput({ inputPath, command, success: childParameter });
179 | } catch (err) {
180 | return printOutput({
181 | inputPath,
182 | command,
183 | error: "No such directory exists",
184 | });
185 | }
186 | };
187 | const pwdCommand = ({ command }) =>
188 | printOutput({ inputPath, command, success: inputPath });
189 |
190 | const makeDirectory = ({ command, tokens, isSudo }) => {
191 | if (isSudo) tokens.shift();
192 | if (tokens.length > 2)
193 | return printOutput({
194 | inputPath,
195 | command,
196 | error: "Folder name should not have space between them",
197 | });
198 | else if (tokens.length === 1)
199 | return printOutput({
200 | inputPath,
201 | command,
202 | error: "Please specify a folder name",
203 | });
204 | else {
205 | let pathArr = inputPath.split("/").filter((path) => !!path);
206 | let curDir = fileSystem;
207 | pathArr.forEach(
208 | (path) => (curDir = curDir.find((system) => system.name === path).child)
209 | );
210 | let newFolderName = tokens[1];
211 | let index = curDir.filter(
212 | (system) => system.type === "folder" && system.name === newFolderName
213 | );
214 | if (index.length > 0)
215 | return printOutput({
216 | inputPath,
217 | command,
218 | error: "Folder with same name exist",
219 | });
220 | makeDirectoryAction({ pathArray: pathArr, folderName: newFolderName });
221 | printOutput({ inputPath, command });
222 | }
223 | };
224 | const removeDirectory = ({ command, tokens, isSudo }) => {
225 | if (isSudo) {
226 | tokens.shift();
227 | }
228 | if (tokens.length > 2) {
229 | return printOutput({
230 | inputPath,
231 | command,
232 | error: "Folder name should not have space between them",
233 | });
234 | } else if (tokens.length === 1) {
235 | return printOutput({
236 | inputPath,
237 | command,
238 | error: "Please specify a folder name",
239 | });
240 | } else {
241 | let pathArr = inputPath.split("/").filter((path) => !!path);
242 | let curDir = fileSystem;
243 | pathArr.forEach(
244 | (path) => (curDir = curDir.find((system) => system.name === path).child)
245 | );
246 | let newFolderName = tokens[1];
247 | let index = curDir.filter(
248 | (system) => system.type === "folder" && system.name === newFolderName
249 | );
250 | if (index.length === 0)
251 | return printOutput({
252 | inputPath,
253 | command,
254 | error: "Folder with the given name does not exist",
255 | });
256 | removeDirectoryAction({ pathArray: pathArr, folderName: newFolderName });
257 | printOutput({ inputPath, command });
258 | }
259 | };
260 | const resetCommand = ({ inputPath, command }) => {
261 | printOutput({
262 | inputPath,
263 | command,
264 | success: "System settings and file system have been reset",
265 | });
266 | resetToDefault();
267 | };
268 |
269 | const commandList = [
270 | {
271 | invoke: "help",
272 | onActive: HelpTerminal,
273 | description:
274 | "Return the list of commands that you can run on terminal | No parameter",
275 | },
276 | {
277 | invoke: "ls",
278 | onActive: listInDirectory,
279 | description:
280 | "Return the list of all files and folder in current or specified directory | One parameter (optional)",
281 | },
282 | {
283 | invoke: "clear",
284 | onActive: clearScreen,
285 | description: "Clears the terminal | No parameters",
286 | },
287 | {
288 | invoke: "echo",
289 | onActive: echoOnScreen,
290 | description: "Prints the word or the line on the terminal",
291 | },
292 | {
293 | invoke: "cd",
294 | onActive: changeDirectory,
295 | description:
296 | "Change the directory of the terminal | One parameter (required)",
297 | },
298 | {
299 | invoke: "mkdir",
300 | onActive: makeDirectory,
301 | description:
302 | "Make a directory within current folder | One parameter (required)",
303 | },
304 | {
305 | invoke: "rm",
306 | onActive: removeDirectory,
307 | description:
308 | "Remove a directory within current folder | One parameter (required)",
309 | },
310 | {
311 | invoke: "pwd",
312 | onActive: pwdCommand,
313 | description: "Returns the working directory of the terminal",
314 | },
315 | {
316 | invoke: "reset",
317 | onActive: resetCommand,
318 | description: "Resets everything (settings and file system)",
319 | },
320 | {
321 | invoke: "exit",
322 | onActive: exitTerminal,
323 | description: "Exits the terminal | No parameters",
324 | },
325 | ];
326 |
327 | const focusTextRef = () => TextRef.current.focus();
328 | const keyPress = (e) => {
329 | if (e.keyCode === 13) {
330 | setHistoryIndex(0);
331 | let command = TextRef.current.innerText;
332 | setHistoryCommand([...historyCommands, command]);
333 | let tokens = command.trim().replace(/\s\s+/g, " ").split(" ");
334 | let isSudo = false;
335 | if (tokens[0] === "sudo") isSudo = true;
336 | else {
337 | let commandObj = commandList.find(
338 | (com) => com.invoke === tokens[0].toLowerCase()
339 | );
340 | if (commandObj) commandObj.onActive({ command, tokens, isSudo });
341 | else
342 | printOutput({
343 | inputPath,
344 | command,
345 | error: `Error: "${tokens[0]}" is not a command`,
346 | });
347 | }
348 | emptyTextRef();
349 | } else if (e.keyCode === 38) {
350 | let lastIndex = historyCommands.length - 1;
351 | if (historyIndex === lastIndex) setHistoryIndex(lastIndex);
352 | else setHistoryIndex(historyIndex + 1);
353 | TextRef.current.innerText = historyCommands[lastIndex - historyIndex];
354 | } else if (e.keyCode === 40) {
355 | let lastIndex = historyCommands.length - 1;
356 | if (historyIndex === 0) setHistoryIndex(0);
357 | else setHistoryIndex(historyIndex - 1);
358 | TextRef.current.innerText = historyCommands[lastIndex - historyIndex];
359 | }
360 | };
361 | useEffect(() => {
362 | focusTextRef();
363 | }, []);
364 | useEffect(() => {
365 | if (terminalLocation && terminalLocation.length) {
366 | let innerPath = terminalLocation.join("/");
367 | setInputPath(`/${innerPath}/`);
368 | }
369 | // eslint-disable-next-line
370 | }, []);
371 |
372 | return (
373 | <>
374 |
375 |
376 | {commandOutput.map((OutputComp, index) => (
377 |
378 | {OutputComp}
379 |
380 | ))}
381 |
382 |
383 | raghavdhingra@web-os:
384 | {inputPath}$
385 |
392 |
393 |
394 | >
395 | );
396 | };
397 |
398 | const mapStateToProps = (state) => ({
399 | fileSystem: state.fileSystemReducers.fileSystem,
400 | activityList: state.activityReducers.activity,
401 | });
402 | export default connect(mapStateToProps, {
403 | removeActivity,
404 | makeDirectoryAction,
405 | removeDirectoryAction,
406 | resetToDefault,
407 | })(TerminalWindow);
408 |
--------------------------------------------------------------------------------
/src/components/applications/textEditor/textEditor.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef, useState } from "react";
2 | import { connect } from "react-redux";
3 | import { changeTextInFile } from "../../../actions/fileSystemActions";
4 |
5 | const TextEditor = ({ system, fileSystem, changeTextInFile }) => {
6 | const textAreaRef = useRef(null);
7 | const [text, setText] = useState("");
8 |
9 | useEffect(() => textAreaRef.current.focus(), []);
10 | useEffect(() => {
11 | let curDir = fileSystem.fileSystem;
12 | system.location.forEach(
13 | (path) => (curDir = curDir.find((system) => system.name === path).child)
14 | );
15 | let changedFile = curDir.find((sys) => system.name === sys.name);
16 | setText(changedFile.child);
17 | }, [fileSystem, system]);
18 | const changeText = (e) =>
19 | changeTextInFile({
20 | pathArray: system.location,
21 | name: system.name,
22 | child: e.target.value,
23 | });
24 |
25 | return (
26 | <>
27 |
33 | >
34 | );
35 | };
36 |
37 | const mapStateToProps = (state) => ({
38 | fileSystem: state.fileSystemReducers,
39 | });
40 | export default connect(mapStateToProps, { changeTextInFile })(TextEditor);
41 |
--------------------------------------------------------------------------------
/src/components/common/ToggleButton.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import '../../assets/common/toggleButton.css';
3 |
4 | const ToggleButton = ({ toggleOn, toggleAction }) => {
5 | return (
6 |
16 | );
17 | };
18 |
19 | export default ToggleButton;
20 |
--------------------------------------------------------------------------------
/src/components/desktop/Desktop.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useMemo } from "react";
2 | import { connect } from "react-redux";
3 | import { changeBackImage, changeFontStyle } from "../../actions/desktopActions";
4 | import { previousStateSet } from "../../actions/fileSystemActions";
5 | import Taskbar from "./taskbar/taskbar";
6 | import PowerOff from "./powerOff/powerOff";
7 | import LowerDesktop from "./lowerDesktop/lowerDesktop.jsx";
8 | import Back1 from "../../assets/background/wall-1.svg";
9 | import Back2 from "../../assets/background/wall-2.svg";
10 | import Back3 from "../../assets/background/wall-3.svg";
11 | import Back4 from "../../assets/background/wall-4.svg";
12 | import Back5 from "../../assets/background/wall-5.svg";
13 | import Back6 from "../../assets/background/wall-6.svg";
14 | import "../../assets/desktop/desktop.css";
15 |
16 | const Desktop = ({
17 | brightness,
18 | background,
19 | fontStyle,
20 | previousStateSet,
21 | isFullScreen,
22 | }) => {
23 | useEffect(() => {
24 | previousStateSet();
25 | }, [previousStateSet]);
26 | const backgroundArray = useMemo(
27 | () => [
28 | { img: Back1, cover: true },
29 | { img: Back2, cover: true },
30 | { img: Back3, cover: false },
31 | { img: Back4, cover: true },
32 | { img: Back5, cover: true },
33 | { img: Back6, cover: false },
34 | ],
35 | []
36 | );
37 | const fontStyleArray = useMemo(
38 | () => [
39 | { name: "Roboto", className: "font-roboto" },
40 | { name: "Potta One", className: "font-potta" },
41 | { name: "Raleway", className: "font-raleway" },
42 | { name: "Lobster", className: "font-lobster" },
43 | { name: "Times", className: "font-times" },
44 | { name: "Courier", className: "font-courier" },
45 | ],
46 | []
47 | );
48 | useEffect(() => {
49 | const fullScrrenToggle = async () => {
50 | try {
51 | if (isFullScreen) {
52 | let bodyE = document.documentElement;
53 | if (bodyE.requestFullscreen) await document.body.requestFullscreen();
54 | else if (bodyE.msRequestFullscreen)
55 | await document.body.msRequestFullscreen();
56 | else if (bodyE.webkitRequestFullscreen)
57 | await document.body.webkitRequestFullscreen();
58 | else if (bodyE.mozRequestFullScreen)
59 | await document.body.mozRequestFullScreen();
60 | } else {
61 | if (document.exitFullscreen) await document.exitFullscreen();
62 | else if (document.webkitExitFullscreen)
63 | await document.webkitExitFullscreen();
64 | else if (document.msExitFullscreen) await document.msExitFullscreen();
65 | }
66 | } catch (err) {
67 | return null;
68 | }
69 | };
70 | fullScrrenToggle();
71 | }, [isFullScreen]);
72 | return (
73 | <>
74 |
88 |
89 | >
90 | );
91 | };
92 |
93 | const mapStateToProps = (state) => ({
94 | background: state.desktopReducers.background,
95 | brightness: state.desktopReducers.brightness,
96 | fontStyle: state.desktopReducers.fontStyle,
97 | isFullScreen: state.desktopReducers.isFullScreen,
98 | });
99 |
100 | export default connect(mapStateToProps, {
101 | changeBackImage,
102 | changeFontStyle,
103 | previousStateSet,
104 | })(Desktop);
105 |
--------------------------------------------------------------------------------
/src/components/desktop/desktopWorkingArea/ContextMenu.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from "react";
2 |
3 | const ContextMenu = ({ isOpen, top, left, close, height, contextArray }) => {
4 | const contextMenuRef = useRef(null);
5 | useEffect(() => {
6 | window.addEventListener("click", (e) => {
7 | try {
8 | if (!contextMenuRef.current.contains(e.target)) close();
9 | } catch (err) {
10 | return null;
11 | }
12 | });
13 | }, [close]);
14 | const contextTaskPerform = (task) => {
15 | task.onClick();
16 | close();
17 | };
18 | if (isOpen)
19 | return (
20 | <>
21 |
26 | {contextArray && contextArray.length
27 | ? contextArray.map((context, index) => (
28 |
contextTaskPerform(context)}
32 | >
33 | {context.name}
34 |
35 | ))
36 | : null}
37 |
38 | >
39 | );
40 | else return null;
41 | };
42 |
43 | export default ContextMenu;
44 |
--------------------------------------------------------------------------------
/src/components/desktop/desktopWorkingArea/desktopIcon.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { connect } from "react-redux";
3 | import "../../../assets/desktop/desktopIcon.css";
4 |
5 | const DesktopIcon = ({ name, icon, width, clickTask, singleClickIcon }) => {
6 | return (
7 | null}
10 | onDoubleClick={!singleClickIcon ? clickTask : () => null}
11 | >
12 |
13 |

14 |
{name}
15 |
16 |
17 | );
18 | };
19 |
20 | const mapStateToProps = (state) => ({
21 | singleClickIcon: state.desktopReducers.singleClickIcon,
22 | });
23 | export default connect(mapStateToProps)(DesktopIcon);
24 |
--------------------------------------------------------------------------------
/src/components/desktop/desktopWorkingArea/desktopWorkingArea.jsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useEffect, useRef, useState } from "react";
2 | import DesktopIcon from "./desktopIcon";
3 | import ContextMenu from "./ContextMenu";
4 | import { connect } from "react-redux";
5 | import Explorer from "../explorer/explorer";
6 | import TextEditor from "../../applications/textEditor/textEditor";
7 | import FOLDER_IMAGE from "../../../assets/icons/folder.svg";
8 | import FILE_IMAGE from "../../../assets/icons/file.svg";
9 | import { createActivity } from "../../../actions/createActivityAction";
10 | import "../../../assets/desktop/desktopWorkingArea.css";
11 | import {
12 | resetToDefault,
13 | changeStartMenu,
14 | toggleFullScreen,
15 | } from "../../../actions/desktopActions";
16 | import DialogBox from "../dialogBox/dialogBox";
17 | import {
18 | makeDirectoryAction,
19 | makeFileAction,
20 | } from "../../../actions/fileSystemActions";
21 |
22 | const LinkFooter = ({ system }) => {
23 | return (
24 |
30 | );
31 | };
32 | const IframeContainer = ({ system }) => {
33 | return (
34 |
39 | );
40 | };
41 |
42 | const DesktopWorkingArea = ({
43 | activityList,
44 | fileSystems,
45 | createActivity,
46 | resetToDefault,
47 | makeFileAction,
48 | makeDirectoryAction,
49 | isFullScreen,
50 | toggleFullScreen,
51 | isStartMenuOpen,
52 | changeStartMenu,
53 | }) => {
54 | const desktopWorkingRef = useRef(null);
55 | const [newDir, setNewDir] = useState({
56 | open: false,
57 | isFolder: false,
58 | name: "",
59 | });
60 | const [contextShown, setContextShown] = useState(false);
61 | const [resetSettingsOpen, setResetSettingsOpen] = useState(false);
62 | const [contextPosition, setContextPosition] = useState({ top: 0, left: 0 });
63 | const [workingAreaHeight, setWorkingAreaHeight] = useState(0);
64 | useEffect(() => {
65 | if (desktopWorkingRef && desktopWorkingRef.current)
66 | setWorkingAreaHeight(desktopWorkingRef.current.clientHeight);
67 | }, [desktopWorkingRef]);
68 | const contextMenuHeight = 238;
69 |
70 | const contextArray = [
71 | { name: "Menu", onClick: () => changeStartMenu(!isStartMenuOpen) },
72 | { name: "Terminal", onClick: () => createActivity({ name: "terminal" }) },
73 | {
74 | name: "New File",
75 | onClick: () => setNewDir({ open: true, isFolder: false, name: "" }),
76 | },
77 | {
78 | name: "New Folder",
79 | onClick: () => setNewDir({ open: true, isFolder: true, name: "" }),
80 | },
81 | {
82 | name: "Customise Display",
83 | onClick: () => createActivity({ name: "settings" }),
84 | },
85 | {
86 | name: isFullScreen ? "Exit Full Screen" : "Enter Full Screen",
87 | onClick: () => toggleFullScreen(),
88 | },
89 | { name: "Reset Settings", onClick: () => setResetSettingsOpen(true) },
90 | ];
91 |
92 | const resetSuccess = () => {
93 | setResetSettingsOpen(false);
94 | resetToDefault();
95 | };
96 | const iconChanger = (system) => {
97 | return system.icon
98 | ? system.icon
99 | : system.type === "folder"
100 | ? FOLDER_IMAGE
101 | : FILE_IMAGE;
102 | };
103 | const startTask = (system) => {
104 | if (system.type === "file") {
105 | if (system.link) {
106 | if (system.inPage) {
107 | createActivity({
108 | name: system.name,
109 | newApp: true,
110 | image: iconChanger(system),
111 | footer: ,
112 | child: () => ,
113 | });
114 | } else {
115 | window.open(system.link);
116 | }
117 | } else {
118 | createActivity({
119 | name: system.name,
120 | newApp: true,
121 | image: iconChanger(system),
122 | footer: <>Auto Save>,
123 | child: () => ,
124 | });
125 | }
126 | } else {
127 | // File Explorer Event
128 | console.log("Open File Explorer");
129 | }
130 | };
131 | useEffect(() => {
132 | desktopWorkingRef.current.addEventListener("contextmenu", (e) => {
133 | try {
134 | e.preventDefault();
135 | setContextShown(false);
136 | setTimeout(() => {
137 | setContextShown(true);
138 | let posX = e.clientX;
139 | let posY = e.clientY;
140 | let winWidth = window.innerWidth;
141 | let winHeight = window.innerHeight;
142 | if (winWidth - 235 < posX) posX = winWidth - 235;
143 | if (winHeight - (contextMenuHeight + 5) < posY)
144 | posY = winHeight - (contextMenuHeight + 5);
145 | setContextPosition({ top: posY, left: posX });
146 | }, 50);
147 | } catch (err) {
148 | return null;
149 | }
150 | });
151 | }, []);
152 | const makeNewDir = () => {
153 | if (newDir.name) {
154 | if (newDir.isFolder)
155 | makeDirectoryAction({
156 | pathArray: ["desktop"],
157 | folderName: newDir.name,
158 | });
159 | else
160 | makeFileAction({
161 | pathArray: ["desktop"],
162 | fileName: newDir.name,
163 | });
164 | setNewDir({ ...newDir, name: "", open: false, isFolder: false });
165 | } else alert("Please Enter a name");
166 | };
167 | const renderDesktopIcons = useCallback(
168 | ({ allIcons }) => {
169 | let desktopIconHTML = [];
170 | let outerIconsArray = [];
171 | if (workingAreaHeight) {
172 | let desktopIcons = [...allIcons[0].child];
173 | let numberOfIcons = parseInt((workingAreaHeight - 30) / 90) - 1;
174 | if (desktopIcons.length > numberOfIcons) {
175 | let initialSplitIndex = 0;
176 | let numOfSplits = Math.ceil(desktopIcons.length / numberOfIcons);
177 | for (let i = 1; i <= numOfSplits; i++) {
178 | let arr = desktopIcons.splice(
179 | initialSplitIndex,
180 | initialSplitIndex + numberOfIcons
181 | );
182 | outerIconsArray.push(arr);
183 | }
184 | } else outerIconsArray.push(desktopIcons);
185 | desktopIconHTML = outerIconsArray.map((desktopIcon, ind) => (
186 |
187 | {desktopIcon.map(
188 | (system, index) =>
189 | system && (
190 | startTask(system)}
196 | />
197 | )
198 | )}
199 |
200 | ));
201 | }
202 | return <>{desktopIconHTML}>;
203 | },
204 | // eslint-disable-next-line
205 | [workingAreaHeight]
206 | );
207 | return (
208 | // No Parent component Other than the main div
209 |
210 | setResetSettingsOpen(false)}
213 | isOpen={resetSettingsOpen}
214 | successText={"Reset"}
215 | heading={"Reset Settings"}
216 | body={"Sure! You want to reset to your default settings?"}
217 | />
218 | setNewDir(false)}
221 | isOpen={newDir.open}
222 | successText={"Save"}
223 | heading={`New ${newDir.isFolder ? "Folder" : "File"}`}
224 | body={
225 | setNewDir({ ...newDir, name: e.target.value })}
230 | />
231 | }
232 | />
233 | setContextShown(false)}
236 | top={contextPosition.top}
237 | left={contextPosition.left}
238 | contextArray={contextArray}
239 | height={contextMenuHeight}
240 | />
241 | {activityList.map(
242 | (activity, index) =>
243 | activity && (
244 |
249 | )
250 | )}
251 | {fileSystems &&
252 | fileSystems.fileSystem &&
253 | fileSystems.fileSystem.length &&
254 | renderDesktopIcons({
255 | allIcons: fileSystems.fileSystem,
256 | })}
257 |
258 | );
259 | };
260 |
261 | const mapStateToProps = (state) => ({
262 | activityList: state.activityReducers.activity,
263 | isFullScreen: state.desktopReducers.isFullScreen,
264 | isStartMenuOpen: state.desktopReducers.isStartMenuOpen,
265 | fileSystems: state.fileSystemReducers,
266 | });
267 |
268 | export default connect(mapStateToProps, {
269 | createActivity,
270 | makeDirectoryAction,
271 | resetToDefault,
272 | toggleFullScreen,
273 | makeFileAction,
274 | changeStartMenu,
275 | })(DesktopWorkingArea);
276 |
--------------------------------------------------------------------------------
/src/components/desktop/dialogBox/dialogBox.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "../../../assets/desktop/dialogBox.css";
3 |
4 | const DialogBox = ({
5 | onSuccess,
6 | onCancel,
7 | heading,
8 | body,
9 | isOpen,
10 | successText,
11 | }) => {
12 | if (isOpen)
13 | return (
14 | <>
15 |
16 |
17 |
18 | {heading ? heading : "Confirmation"}
19 |
20 |
{body ? body : "Are You Sure?"}
21 |
22 |
23 | Cancel
24 |
25 |
26 | {successText ? successText : "Proceed"}
27 |
28 |
29 |
30 | >
31 | );
32 | else return null;
33 | };
34 |
35 | export default DialogBox;
36 |
--------------------------------------------------------------------------------
/src/components/desktop/dropdown/dropdown.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import DropDownCaret from "../../../assets/icons/dropdown.svg";
3 | import DialogBox from "../dialogBox/dialogBox";
4 | import Brightness from "../../../assets/icons/brightness.svg";
5 | import {
6 | changeBrightness,
7 | powerOffStatus,
8 | toggleFullScreen,
9 | } from "../../../actions/desktopActions";
10 | import { connect } from "react-redux";
11 | import "../../../assets/desktop/dropdown.css";
12 |
13 | const DropDown = ({
14 | isOnline,
15 | networkType,
16 | battery,
17 | changeBrightness,
18 | brightness,
19 | powerOffStatus,
20 | isFullScreen,
21 | toggleFullScreen,
22 | }) => {
23 | const [isPowerDialogOpen, setPowerDialog] = useState(false);
24 | const togglePowerDialog = () => setPowerDialog(!isPowerDialogOpen);
25 | const closeWindow = () => powerOffStatus({ active: true, timer: 0 });
26 | return (
27 | <>
28 |
36 |
37 |
38 |

44 |
45 |
46 |
47 |
48 |

49 |
50 | changeBrightness(e.target.value / 100)}
57 | >
58 |
59 |
60 |
61 |
62 |
63 |
69 | {isOnline ? `Connected (${networkType})` : "Not Connected"}
70 |
71 |
72 |
80 | Battery: {parseInt(battery.level * 100)}% (
81 | {battery.charging ? "Charging" : "Not charging"})
82 |
83 |
84 |
toggleFullScreen()}>
85 | {isFullScreen ? "Exit Full Screen" : "Enter Full Screen"}
86 |
87 |
88 |
Log out
89 |
93 | Power off
94 |
95 |
96 |
97 | >
98 | );
99 | };
100 |
101 | const mapStateToProps = (state) => ({
102 | brightness: state.desktopReducers.brightness,
103 | battery: state.desktopReducers.battery,
104 | isOnline: state.desktopReducers.isOnline,
105 | networkType: state.desktopReducers.networkType,
106 | isFullScreen: state.desktopReducers.isFullScreen,
107 | });
108 |
109 | export default connect(mapStateToProps, {
110 | changeBrightness,
111 | powerOffStatus,
112 | toggleFullScreen,
113 | })(DropDown);
114 |
--------------------------------------------------------------------------------
/src/components/desktop/explorer/explorer.jsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useEffect, useRef } from "react";
2 | import { connect } from "react-redux";
3 | import {
4 | updateZIndexActivity,
5 | removeActivity,
6 | toggleActivityMaximise,
7 | updatePositionActivity,
8 | updateDimensionActivity,
9 | } from "../../../actions/activityActions";
10 | import fileImage from "../../../assets/icons/file.svg";
11 | import "../../../assets/desktop/explorer.css";
12 |
13 | const Explorer = ({
14 | activity,
15 | updateZIndexActivity,
16 | updateDimensionActivity,
17 | explorerIndex,
18 | removeActivity,
19 | toggleActivityMaximise,
20 | updatePositionActivity,
21 | }) => {
22 | const explorerRef = useRef(null);
23 | const elementToDrag = useRef(null);
24 |
25 | const updateZIndex = () => updateZIndexActivity(explorerIndex);
26 | const closeActivity = () => removeActivity(explorerIndex);
27 |
28 | // const updateDimension = (height, width) =>
29 | // updateDimensionActivity({ height, width, activityIndex: explorerIndex });
30 |
31 | const dragElement = useCallback(() => {
32 | try {
33 | let pos1 = 0,
34 | pos2 = 0,
35 | pos3 = 0,
36 | pos4 = 0;
37 | if (explorerRef.current)
38 | explorerRef.current.addEventListener("mousedown", () =>
39 | dragMouseDown()
40 | );
41 | else
42 | elementToDrag.current.addEventListener("mousedown", () =>
43 | dragMouseDown()
44 | );
45 | function dragMouseDown(e) {
46 | try {
47 | e = e || window.event;
48 | e.preventDefault();
49 | pos3 = e.clientX;
50 | pos4 = e.clientY;
51 | document.onmouseup = closeDragElement;
52 | document.onmousemove = elementDrag;
53 | } catch (err) {
54 | return null;
55 | }
56 | }
57 | function elementDrag(e) {
58 | try {
59 | e = e || window.event;
60 | e.preventDefault();
61 | // calculate the new cursor position:
62 | pos1 = pos3 - e.clientX;
63 | pos2 = pos4 - e.clientY;
64 | pos3 = e.clientX;
65 | pos4 = e.clientY;
66 | // set the element's new position:
67 | let elementHeight = elementToDrag.current.offsetHeight;
68 | let elementWidth = elementToDrag.current.offsetWidth;
69 | let elementTopOffset = elementToDrag.current.offsetTop;
70 | let elementLeftOffset = elementToDrag.current.offsetLeft;
71 | let topVal = elementTopOffset - pos2;
72 | let leftVal = elementLeftOffset - pos1;
73 |
74 | // Condition For Keeping Explorer in Window
75 | if (topVal < 34) topVal = 34;
76 | if (leftVal < 60) leftVal = 60;
77 |
78 | let windowHeight = window.innerHeight;
79 | let windowWidth = window.innerWidth;
80 |
81 | if (topVal + elementHeight > windowHeight)
82 | topVal = windowHeight - elementHeight;
83 |
84 | if (leftVal + elementWidth > windowWidth)
85 | leftVal = windowWidth - elementWidth;
86 | updatePositionActivity({
87 | top: topVal,
88 | left: leftVal,
89 | activityIndex: explorerIndex,
90 | });
91 | } catch (err) {
92 | return null;
93 | }
94 | }
95 | function closeDragElement() {
96 | // stop moving when mouse button is released:
97 | document.onmouseup = null;
98 | document.onmousemove = null;
99 | }
100 | } catch (err) {
101 | return null;
102 | }
103 | }, [updatePositionActivity, explorerIndex]);
104 | const toggleMaximise = () =>
105 | toggleActivityMaximise({
106 | activityIndex: explorerIndex,
107 | isMaximise: !activity.isMaximise,
108 | });
109 |
110 | useEffect(() => {
111 | if (!activity.isMaximise) dragElement();
112 | }, [dragElement, activity]);
113 | return (
114 |
126 |
127 |
132 |
133 |
134 |

140 |
141 |
142 | {activity && activity.name}
143 |
144 |
145 |
146 |
147 |
-
148 |
149 |
160 |
161 |
167 |
168 |
169 |
170 | {activity && activity.child({ activity })}
171 |
172 | {activity && activity.footer ? (
173 |
{activity.footer}
174 | ) : null}
175 |
176 | );
177 | };
178 |
179 | const mapStateToProps = (state) => ({
180 | activityList: state.activityReducers.activity,
181 | });
182 |
183 | export default connect(mapStateToProps, {
184 | updateZIndexActivity,
185 | removeActivity,
186 | toggleActivityMaximise,
187 | updatePositionActivity,
188 | updateDimensionActivity,
189 | })(Explorer);
190 |
--------------------------------------------------------------------------------
/src/components/desktop/lowerDesktop/lowerDesktop.jsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from 'react';
2 | import { connect } from 'react-redux';
3 | import {
4 | dropDownToggle,
5 | activityDropDownToggle,
6 | changeStartMenu,
7 | } from '../../../actions/desktopActions';
8 | import { createActivity } from '../../../actions/createActivityAction';
9 | import NavItem from './navItem';
10 | import DesktopWorkingArea from '../desktopWorkingArea/desktopWorkingArea';
11 | import { applications } from '../../../actions/defaultApps';
12 | import StartMenu from '../startMenu/startMenu';
13 | import '../../../assets/desktop/lowerDesktop.css';
14 |
15 | const LowerDesktop = ({
16 | activityDropDown,
17 | dropDownOpen,
18 | dropDownToggle,
19 | activityDropDownToggle,
20 | createActivity,
21 | isStartMenuOpen,
22 | changeStartMenu,
23 | activityList,
24 | }) => {
25 | const closeDropDown = () => {
26 | if (dropDownOpen || activityDropDown) {
27 | dropDownToggle(false);
28 | activityDropDownToggle(false);
29 | }
30 | };
31 | const hightlightApp = useCallback(
32 | (name) => {
33 | let isShown = false;
34 | activityList.forEach((act) => {
35 | if (act.name === name) isShown = true;
36 | });
37 | return isShown;
38 | },
39 | [activityList]
40 | );
41 |
42 | return (
43 |
44 |
45 |
46 | {applications.defaultApps.map((app, index) => (
47 |
{
50 | changeStartMenu(false);
51 | createActivity({ name: app.key });
52 | }}
53 | hightlight={hightlightApp(app.name)}
54 | >
55 |
61 |
62 | ))}
63 |
64 |
65 |
66 |
74 |
75 |
76 |
77 | {isStartMenuOpen ?
:
}
78 |
79 | );
80 | };
81 |
82 | const mapStateToProps = (state) => ({
83 | dropDownOpen: state.desktopReducers.dropDownOpen,
84 | activityDropDown: state.desktopReducers.activityDropDown,
85 | isStartMenuOpen: state.desktopReducers.isStartMenuOpen,
86 | activityList: state.activityReducers.activity,
87 | });
88 | export default connect(mapStateToProps, {
89 | dropDownToggle,
90 | createActivity,
91 | activityDropDownToggle,
92 | changeStartMenu,
93 | })(LowerDesktop);
94 |
--------------------------------------------------------------------------------
/src/components/desktop/lowerDesktop/navItem.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const NavItem = ({ children, clickTask, hightlight }) => {
4 | return (
5 | <>
6 |
12 | {children}
13 |
14 | >
15 | );
16 | };
17 | export default NavItem;
18 |
--------------------------------------------------------------------------------
/src/components/desktop/powerOff/powerOff.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { connect } from "react-redux";
3 | import "../../../assets/desktop/powerOff.css";
4 |
5 | const PowerOff = ({ backImage, powerOff }) => {
6 | const [isActive, setIsActive] = useState(false);
7 |
8 | useEffect(() => {
9 | if (powerOff.active) {
10 | setTimeout(() => {
11 | setIsActive(true);
12 | setTimeout(() => {
13 | window.close();
14 | }, 4000);
15 | }, powerOff.timer * 1000);
16 | }
17 | }, [powerOff]);
18 |
19 | return (
20 |
26 |
27 |
28 |
29 |
32 |
33 |
SHUTTING DOWN
34 |
35 |
36 |
37 | );
38 | };
39 |
40 | const mapStateToProps = (state) => ({
41 | powerOff: state.desktopReducers.powerOff,
42 | });
43 |
44 | export default connect(mapStateToProps)(PowerOff);
45 |
--------------------------------------------------------------------------------
/src/components/desktop/startMenu/startItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const StartItem = ({ children, clickTask }) => {
4 | return (
5 | <>
6 |
7 | {children}
8 |
9 | >
10 | );
11 | };
12 | export default StartItem;
13 |
--------------------------------------------------------------------------------
/src/components/desktop/startMenu/startMenu.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef, useState } from 'react';
2 | import { connect } from 'react-redux';
3 | import { applications } from '../../../actions/defaultApps';
4 | import { createActivity } from '../../../actions/createActivityAction';
5 | import SEARCH_ICON from '../../../assets/icons/search.svg';
6 | import StartItem from './startItem';
7 | import { changeStartMenu } from '../../../actions/desktopActions';
8 | import '../../../assets/desktop/startMenu.css';
9 |
10 | const StartMenu = ({ createActivity, changeStartMenu }) => {
11 | const searchRef = useRef(null);
12 | const [searchString, setSearchString] = useState('');
13 | const [applicationArray, setApplicationArray] = useState([]);
14 | const [startSection, setStartSection] = useState('application');
15 |
16 | const focusOnField = () => {
17 | searchRef.current.focus();
18 | };
19 | useEffect(() => {
20 | if (startSection === 'application') {
21 | if (searchString) {
22 | let searchedApps = applications.allApplications.map((app) => {
23 | let searchRegexKey = new RegExp(searchString.trim(), 'ig');
24 | if (searchRegexKey.test(app.key)) return app;
25 | else return null;
26 | });
27 | setApplicationArray([...searchedApps]);
28 | } else setApplicationArray([...applications.allApplications]);
29 | } else if (startSection === 'social') {
30 | if (searchString) {
31 | let searchedApps = applications.socialApps.map((app) => {
32 | let searchRegexKey = new RegExp(searchString.trim(), 'ig');
33 | if (searchRegexKey.test(app.key)) return app;
34 | else return null;
35 | });
36 | setApplicationArray([...searchedApps]);
37 | } else setApplicationArray([...applications.socialApps]);
38 | } else return null;
39 | }, [searchString, startSection]);
40 | useEffect(() => {
41 | focusOnField();
42 | setApplicationArray([...applications.allApplications]);
43 | }, []);
44 | const startItemClick = (app) => {
45 | if (startSection === 'application') {
46 | createActivity({ name: app.key });
47 | changeStartMenu(false);
48 | } else if (startSection === 'social') {
49 | window.open(app.link);
50 | } else {
51 | return null;
52 | }
53 | };
54 | return (
55 |
56 |
57 |
58 |
59 |
60 |
61 |

68 |
69 |
setSearchString(e.target.value)}
76 | />
77 |
78 |
79 |
80 |
81 | {applicationArray && applicationArray.length ? (
82 | applicationArray.map(
83 | (app, index) =>
84 | app && (
85 |
startItemClick(app)}
88 | >
89 |
95 | {app.name}
96 |
97 | )
98 | )
99 | ) : (
100 |
No Application Found
101 | )}
102 |
103 |
104 |
105 |
setStartSection('application')}
112 | >
113 | Applications
114 |
115 |
setStartSection('social')}
120 | >
121 | Social
122 |
123 |
124 |
125 |
126 |
127 | );
128 | };
129 |
130 | const mapStateToProps = (state) => ({
131 | // background: state.desktopReducers.background,
132 | });
133 |
134 | export default connect(mapStateToProps, { createActivity, changeStartMenu })(
135 | StartMenu
136 | );
137 |
--------------------------------------------------------------------------------
/src/components/desktop/taskbar/RightTaskPane.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { connect } from 'react-redux';
3 | import DropDown from '../dropdown/dropdown';
4 | import {
5 | batteryStatus,
6 | onlineStatus,
7 | networkType,
8 | dropDownToggle,
9 | dateStatus,
10 | } from '../../../actions/desktopActions';
11 | import Wifi from '../../../assets/icons/wifi.svg';
12 | import Battery from '../../../assets/icons/battery.svg';
13 | import Charging from '../../../assets/icons/lighting.svg';
14 | import DropDownCaret from '../../../assets/icons/dropdown-white.svg';
15 |
16 | const RightTaskPane = ({
17 | battery,
18 | dropDownOpen,
19 | batteryStatus,
20 | onlineStatus,
21 | networkType,
22 | dateStatus,
23 | dropDownToggle,
24 | }) => {
25 | const toggleDropDown = () => {
26 | dropDownToggle(!dropDownOpen);
27 | };
28 | const getStatus = async () => {
29 | // battery status
30 | let batteryObj;
31 | if (navigator.getBattery) batteryObj = await navigator.getBattery();
32 | else batteryObj = { level: 1, charging: true };
33 | const { level, charging } = batteryObj;
34 | batteryStatus({ level, charging });
35 |
36 | //online status
37 | onlineStatus(navigator.onLine);
38 |
39 | // connection type
40 | let connection =
41 | navigator.connection ||
42 | navigator.mozConnection ||
43 | navigator.webkitConnection;
44 |
45 | networkType(connection ? connection.effectiveType : '4g');
46 |
47 | // date status
48 | dateStatus(new Date());
49 |
50 | setTimeout(() => {
51 | getStatus();
52 | }, 5000);
53 | };
54 | useEffect(() => {
55 | getStatus();
56 | // eslint-disable-next-line
57 | }, []);
58 | return (
59 |
60 |
61 |
62 |
63 |

64 |
65 |
66 | {battery && battery.charging ? (
67 |

73 | ) : null}
74 |
75 |
76 |

77 |
78 |
79 |
{battery && parseInt(battery.level * 100)}%
80 |
81 |
82 |
87 |

88 |
89 |
90 |
91 | {dropDownOpen ? (
92 |
93 | ) : null}
94 |
95 | {dropDownOpen &&
}
96 |
97 | );
98 | };
99 |
100 | const mapStateToProps = (state) => ({
101 | battery: state.desktopReducers.battery,
102 | dropDownOpen: state.desktopReducers.dropDownOpen,
103 | });
104 |
105 | export default connect(mapStateToProps, {
106 | batteryStatus,
107 | networkType,
108 | onlineStatus,
109 | dropDownToggle,
110 | dateStatus,
111 | })(RightTaskPane);
112 |
--------------------------------------------------------------------------------
/src/components/desktop/taskbar/date.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { connect } from "react-redux";
3 |
4 | const TaskDate = ({ date }) => {
5 | const formatter = (val) => (val < 10 ? `0${val}` : val);
6 | const weekList = ["Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat", "Sun"];
7 | const monthList = [
8 | "Jan",
9 | "Feb",
10 | "Mar",
11 | "Apr",
12 | "May",
13 | "Jun",
14 | "Jul",
15 | "Aug",
16 | "Sep",
17 | "Oct",
18 | "Nov",
19 | "Dec",
20 | ];
21 |
22 | return (
23 |
24 | {weekList[date.getDay()]}, {monthList[date.getMonth()]}{" "}
25 | {formatter(date.getDate())}
26 | {formatter(date.getHours())}:{formatter(date.getMinutes())}
27 |
28 | );
29 | };
30 |
31 | const mapStateToProps = (state) => ({
32 | date: state.desktopReducers.date,
33 | });
34 |
35 | export default connect(mapStateToProps)(TaskDate);
36 |
--------------------------------------------------------------------------------
/src/components/desktop/taskbar/taskList.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import { connect } from 'react-redux';
3 | import DropDownCaret from '../../../assets/icons/dropdown.svg';
4 | import { removeActivity } from '../../../actions/activityActions';
5 | import { activityDropDownToggle } from '../../../actions/desktopActions';
6 | import DialogBox from '../dialogBox/dialogBox';
7 | import '../../../assets/desktop/taskList.css';
8 |
9 | const TaskList = ({
10 | removeActivity,
11 | activityList,
12 | activityDropDown,
13 | activityDropDownToggle,
14 | }) => {
15 | const [activityName, setActivityName] = useState('');
16 | const [dialogOpen, setDialogOpen] = useState(false);
17 | const showDialog = (name, isOpen) => {
18 | setActivityName(name);
19 | setDialogOpen(isOpen);
20 | };
21 | const [isLoading, setIsLoading] = useState(false);
22 | useEffect(() => {
23 | let indexTrue = activityList.findIndex((e) => e.isLoading === true);
24 | if (indexTrue !== -1) setIsLoading(true);
25 | else setIsLoading(false);
26 | }, [activityList]);
27 | const toggleActivity = () => {
28 | let activityIndex = activityList.findIndex(
29 | (activity) => activity && activity.name === activityName
30 | );
31 | removeActivity(activityIndex);
32 | setDialogOpen(false);
33 | };
34 |
35 | return (
36 | <>
37 | showDialog('', false)}
40 | isOpen={dialogOpen}
41 | successText={'End Task'}
42 | heading={activityName}
43 | body={'Are you sure, you want to end the task?'}
44 | />
45 |
46 |
Tasks
47 |
activityDropDownToggle(!activityDropDown)}
52 | >
53 | {isLoading && (
54 |
55 |
58 |
59 | )}
60 |
61 |
Activity List
62 |
65 |
66 |
67 | {activityDropDown && (
68 | <>
69 |
70 |
71 |
72 |

78 |
79 |
80 | {activityList.length ? (
81 | activityList.map(
82 | (activity, index) =>
83 | activity && (
84 |
88 |
89 | {activity.isLoading ? (
90 |
97 | ) : (
98 |
99 | )}
100 |
{activity.name}
101 |
showDialog(activity.name, true)}
104 | >
105 | ×
106 |
107 |
108 |
109 | )
110 | )
111 | ) : (
112 |
No Activity
113 | )}
114 |
115 |
116 | >
117 | )}
118 |
119 | >
120 | );
121 | };
122 |
123 | const mapStateToProps = (state) => ({
124 | activityList: state.activityReducers.activity,
125 | activityDropDown: state.desktopReducers.activityDropDown,
126 | });
127 |
128 | export default connect(mapStateToProps, {
129 | removeActivity,
130 | activityDropDownToggle,
131 | })(TaskList);
132 |
--------------------------------------------------------------------------------
/src/components/desktop/taskbar/taskbar.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import TaskList from "./taskList";
3 | import Date from "./date";
4 | import RightTaskPane from "./RightTaskPane";
5 | import "../../../assets/desktop/taskbar.css";
6 |
7 | const Taskbar = () => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 | );
15 | };
16 | export default Taskbar;
17 |
--------------------------------------------------------------------------------
/src/components/notFound/error.css:
--------------------------------------------------------------------------------
1 | .error-section {
2 | background-color: #e7ffff;
3 | height: 100vh;
4 | text-align: center;
5 | }
6 |
7 | .error-section .error {
8 | font-size: 150px;
9 | color: #008b62;
10 | text-shadow: 1px 1px 1px #00593e, 2px 2px 1px #00593e, 3px 3px 1px #00593e,
11 | 4px 4px 1px #00593e, 5px 5px 1px #00593e, 6px 6px 1px #00593e,
12 | 7px 7px 1px #00593e, 8px 8px 1px #00593e, 25px 25px 8px rgba(0, 0, 0, 0.2);
13 | }
14 |
15 | .error-section .page {
16 | margin: 2rem 0;
17 | font-size: 20px;
18 | font-weight: 600;
19 | color: #444;
20 | }
21 |
22 | .error-section .back-home {
23 | display: inline-block;
24 | border: 2px solid #222;
25 | color: #222;
26 | text-transform: uppercase;
27 | font-weight: 600;
28 | padding: 0.75rem 1rem 0.6rem;
29 | transition: color 0.2s linear, background-color 0.2s linear;
30 | box-shadow: 0 3px 8px rgba(0, 0, 0, 0.3);
31 | }
32 |
33 | .error-section .back-home:hover {
34 | background-color: #222;
35 | color: #ddd;
36 | }
37 |
38 | @media screen and (max-width: 768px) {
39 | .error-section .error {
40 | font-size: 4rem;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/components/notFound/error.jsx:
--------------------------------------------------------------------------------
1 | import { useRouteError } from "react-router-dom";
2 |
3 | import "./error.css";
4 |
5 | export default function ErrorPage() {
6 | const error = useRouteError();
7 | console.error(error);
8 |
9 | return (
10 |
11 |
❗️
12 |
404
13 |
Security Breach
14 |
Ooops!!! The page you are looking for is not found
15 |
16 | Back to home
17 |
18 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Provider } from "react-redux";
3 | import ReactDOM from "react-dom/client";
4 | import { createBrowserRouter, RouterProvider } from "react-router-dom";
5 |
6 | import store from "./store";
7 | import Desktop from "./components/desktop/Desktop";
8 | import ErrorPage from "./components/notFound/error";
9 | import * as serviceWorkerRegistration from "./serviceWorkerRegistration";
10 |
11 | import "./assets/default.css";
12 |
13 | const router = createBrowserRouter([
14 | {
15 | path: "/",
16 | element: ,
17 | errorElement: ,
18 | },
19 | ]);
20 |
21 | ReactDOM.createRoot(document.getElementById("root")).render(
22 |
23 |
24 |
25 |
26 |
27 | );
28 |
29 | // ReactDOM.render(, document.getElementById("root"));
30 |
31 | // If you want your app to work offline and load faster, you can change
32 | // unregister() to register() below. Note this comes with some pitfalls.
33 | // Learn more about service workers: https://bit.ly/CRA-PWA
34 | serviceWorkerRegistration.register();
35 |
--------------------------------------------------------------------------------
/src/reducers/activityReducers.js:
--------------------------------------------------------------------------------
1 | import * as actions from "../actions/types";
2 |
3 | const initialState = {
4 | triggerIndex: -1,
5 | isTriggered: false,
6 | activity: [],
7 | };
8 |
9 | const activityReducers = (state = initialState, action) => {
10 | const { type, payload } = action;
11 | switch (type) {
12 | case actions.CREATE_ACTIVITY: {
13 | let { activity } = payload;
14 | return { ...state, activity: [...state.activity, activity] };
15 | }
16 | case actions.UPDATE_ACTIVITY_TRIGGER: {
17 | let { activityIndex, isTriggered } = payload;
18 | return { ...state, triggerIndex: activityIndex, isTriggered };
19 | }
20 | case actions.REMOVE_ACTIVITY_TRIGGER: {
21 | return { ...state, triggerIndex: -1, isTriggered: false };
22 | }
23 | case actions.REMOVE_ACTIVITY: {
24 | let { activityIndex } = payload;
25 | state.activity.splice(activityIndex, 1);
26 | return { ...state, activity: [...state.activity] };
27 | }
28 | case actions.UPDATE_ZINDEX_ACTIVITY: {
29 | let { activityIndex } = payload;
30 | state.activity.forEach((act) => (act.zIndex = 2));
31 | state.activity[activityIndex] = {
32 | ...state.activity[activityIndex],
33 | zIndex: 3,
34 | };
35 | return { ...state, activity: [...state.activity] };
36 | }
37 | case actions.TOGGLE_LOADING_ACTIVITY: {
38 | let { activityIndex, isLoading } = payload;
39 | state.activity[activityIndex] = {
40 | ...state.activity[activityIndex],
41 | isLoading,
42 | };
43 | return { ...state, activity: [...state.activity] };
44 | }
45 | case actions.TOGGLE_ACTIVITY_MAXIMISE: {
46 | let { activityIndex, isMaximise } = payload;
47 | state.activity[activityIndex] = {
48 | ...state.activity[activityIndex],
49 | isMaximise,
50 | };
51 | return { ...state, activity: [...state.activity] };
52 | }
53 | case actions.UPDATE_ACTIVITY_POSITION: {
54 | let { top, left, activityIndex } = payload;
55 | state.activity[activityIndex] = {
56 | ...state.activity[activityIndex],
57 | top,
58 | left,
59 | };
60 | return { ...state, activity: [...state.activity] };
61 | }
62 | case actions.UPDATE_ACTIVITY_DIMENSION: {
63 | let { height, width, activityIndex } = payload;
64 | state.activity[activityIndex] = {
65 | ...state.activity[activityIndex],
66 | height,
67 | width,
68 | };
69 | return { ...state, activity: [...state.activity] };
70 | }
71 | default:
72 | return state;
73 | }
74 | };
75 |
76 | export default activityReducers;
77 |
--------------------------------------------------------------------------------
/src/reducers/combinedReducers.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from "redux";
2 |
3 | import desktopReducers from "./desktopReducers";
4 | import activityReducers from "./activityReducers";
5 | import fileSystemReducers from "./fileSystemReducers";
6 |
7 | export default combineReducers({
8 | activityReducers,
9 | desktopReducers,
10 | fileSystemReducers,
11 | });
12 |
--------------------------------------------------------------------------------
/src/reducers/desktopReducers.js:
--------------------------------------------------------------------------------
1 | import * as actions from "../actions/types";
2 |
3 | const initialState = {
4 | background: 3,
5 | fontStyle: 1,
6 | brightness: 1,
7 | dropDownOpen: false,
8 | singleClickIcon: true,
9 | isFullScreen: false,
10 | isStartMenuOpen: false,
11 | battery: {
12 | level: 0,
13 | charging: false,
14 | },
15 | isOnline: true,
16 | networkType: "4g",
17 | date: new Date(),
18 | activityDropDown: false,
19 | powerOff: {
20 | active: false,
21 | timer: 0,
22 | },
23 | };
24 | const localSave = (key, val) => localStorage.setItem(key, JSON.stringify(val));
25 |
26 | const desktopReducers = (state = initialState, action) => {
27 | const { type, payload } = action;
28 | switch (type) {
29 | case actions.TOGGLE_DROP_DOWN: {
30 | let { dropDownOpen } = payload;
31 | return { ...state, dropDownOpen };
32 | }
33 | case actions.TOGGLE_START_MENU: {
34 | let { startMenuOpen } = payload;
35 | return { ...state, isStartMenuOpen: startMenuOpen };
36 | }
37 | case actions.TOGGLE_FULL_SCREEN: {
38 | return { ...state, isFullScreen: !state.isFullScreen };
39 | }
40 | case actions.PREVIOUS_STATE_SET: {
41 | let previousState = JSON.parse(localStorage.getItem("desktop"));
42 | if (previousState) return { ...previousState, date: new Date() };
43 | else return state;
44 | }
45 | case actions.SINGLE_CLICK_ICON_CHANGE: {
46 | let { singleClickIcon } = payload;
47 | let newState = { ...state, singleClickIcon };
48 | localSave("desktop", newState);
49 | return newState;
50 | }
51 | case actions.RESET_TO_DEFAULT: {
52 | return {
53 | ...state,
54 | background: 3,
55 | brightness: 1,
56 | fontStyle: 1,
57 | dropDownOpen: false,
58 | activityDropDown: false,
59 | singleClickIcon: true,
60 | isFullScreen: false,
61 | };
62 | }
63 | case actions.BACK_IMAGE_CHANGE: {
64 | let { background } = payload;
65 | let newState = { ...state, background };
66 | localSave("desktop", newState);
67 | return newState;
68 | }
69 | case actions.FONT_STYLE_CHANGE: {
70 | let { fontStyle } = payload;
71 | let newState = { ...state, fontStyle };
72 | localSave("desktop", newState);
73 | return newState;
74 | }
75 | case actions.BRIGHTNESS_CHANGE: {
76 | let { brightness } = payload;
77 | let newState = { ...state, brightness };
78 | localSave("desktop", newState);
79 | return newState;
80 | }
81 | case actions.BATTERY_STATUS: {
82 | let { battery } = payload;
83 | return { ...state, battery };
84 | }
85 | case actions.ONLINE_STATUS: {
86 | let { isOnline } = payload;
87 | return { ...state, isOnline };
88 | }
89 | case actions.NETWORK_TYPE: {
90 | let { networkType } = payload;
91 | return { ...state, networkType };
92 | }
93 | case actions.DATE_STATUS: {
94 | let { date } = payload;
95 | return { ...state, date };
96 | }
97 | case actions.ACTIVITY_TOGGLE_DROP_DOWN: {
98 | let { activityDropDown } = payload;
99 | return { ...state, activityDropDown };
100 | }
101 | case actions.POWER_OFF_STATUS: {
102 | let { powerOff } = payload;
103 | return { ...state, powerOff };
104 | }
105 | default:
106 | return state;
107 | }
108 | };
109 |
110 | export default desktopReducers;
111 |
--------------------------------------------------------------------------------
/src/reducers/fileSystemReducers.js:
--------------------------------------------------------------------------------
1 | import * as actions from "../actions/types";
2 | import PROFILE_IMAGE from "../assets/icons/profile.svg";
3 | import PROJECT_IMAGE from "../assets/icons/project.svg";
4 | import ARCHIVE_IMAGE from "../assets/icons/archive.svg";
5 | import SPONSOR_IMAGE from "../assets/icons/sponsorship.png";
6 | import USER_IMAGE from "../assets/icons/user.png";
7 | import FORK_IMAGE from "../assets/icons/transfer.png";
8 | import GITHUB_IMAGE from "../assets/icons/octocat.svg";
9 |
10 | const initialState = {
11 | fileSystem: [
12 | {
13 | name: "desktop",
14 | type: "folder",
15 | location: [],
16 | child: [
17 | {
18 | name: "Portfolio",
19 | type: "file",
20 | icon: USER_IMAGE,
21 | link: "https://portfolio.raghavdhingra.com",
22 | inPage: true,
23 | location: ["desktop"],
24 | },
25 | {
26 | name: "Resume",
27 | type: "file",
28 | icon: PROFILE_IMAGE,
29 | link: "https://portfolio.raghavdhingra.com/static/assets/resume.pdf",
30 | inPage: true,
31 | location: ["desktop"],
32 | },
33 | {
34 | name: "Projects",
35 | type: "file",
36 | icon: PROJECT_IMAGE,
37 | link: "https://portfolio.raghavdhingra.com/projects",
38 | inPage: true,
39 | location: ["desktop"],
40 | },
41 | {
42 | name: "Archive",
43 | type: "file",
44 | icon: ARCHIVE_IMAGE,
45 | link: "https://archive.raghavdhingra.com",
46 | inPage: false,
47 | location: ["desktop"],
48 | },
49 | { name: "Docs", type: "file", child: "", location: ["desktop"] },
50 | {
51 | name: "Follow me",
52 | type: "file",
53 | icon: GITHUB_IMAGE,
54 | link: "https://github.com/raghavdhingra",
55 | inPage: false,
56 | location: ["desktop"],
57 | },
58 | {
59 | name: "Clone Repo",
60 | type: "file",
61 | icon: FORK_IMAGE,
62 | link: "https://github.com/raghavdhingra/Web-OS/fork",
63 | inPage: false,
64 | location: ["desktop"],
65 | },
66 | {
67 | name: "Sponsor",
68 | type: "file",
69 | icon: SPONSOR_IMAGE,
70 | link: "https://github.com/sponsors/raghavdhingra",
71 | inPage: false,
72 | location: ["desktop"],
73 | },
74 | ],
75 | },
76 | {
77 | name: "raghavdhingra",
78 | type: "folder",
79 | child: [],
80 | location: [],
81 | },
82 | {
83 | name: "public",
84 | type: "folder",
85 | child: [],
86 | location: [],
87 | },
88 | ],
89 | };
90 | const localSave = (key, val) => localStorage.setItem(key, JSON.stringify(val));
91 |
92 | const fileSystemReducers = (state = initialState, action) => {
93 | const { type, payload } = action;
94 | switch (type) {
95 | case actions.CHANGE_TEXT_IN_FILE: {
96 | const { pathArray, name, child } = payload;
97 | let curDir = state.fileSystem;
98 | pathArray.forEach(
99 | (path) => (curDir = curDir.find((system) => system.name === path).child)
100 | );
101 | let changedFile = curDir.find((system) => system.name === name);
102 | changedFile.child = child;
103 | localSave("fileSystem", { ...state });
104 | return { ...state };
105 | }
106 | case actions.MAKE_DIRECTORY_IN_SYSTEM: {
107 | const { pathArray, folderName } = payload;
108 | let curDir = state.fileSystem;
109 | pathArray.forEach(
110 | (path) => (curDir = curDir.find((system) => system.name === path).child)
111 | );
112 | let newFolder = {
113 | name: folderName,
114 | type: "folder",
115 | child: [],
116 | location: pathArray,
117 | };
118 | curDir.push(newFolder);
119 | localSave("fileSystem", { ...state });
120 | return { ...state };
121 | }
122 | case actions.MAKE_FILE_IN_SYSTEM: {
123 | const { pathArray, fileName } = payload;
124 | let curDir = state.fileSystem;
125 | pathArray.forEach(
126 | (path) => (curDir = curDir.find((system) => system.name === path).child)
127 | );
128 | let newFile = {
129 | name: fileName,
130 | type: "file",
131 | location: pathArray,
132 | child: "",
133 | };
134 | curDir.push(newFile);
135 | localSave("fileSystem", { ...state });
136 | return { ...state };
137 | }
138 | case actions.REMOVE_DIRECTORY_IN_SYSTEM: {
139 | const { pathArray, folderName } = payload;
140 | let newDirectory = [...state.fileSystem];
141 | let curDir = newDirectory;
142 | pathArray.forEach((path) => {
143 | curDir = curDir = curDir.find((system) => system.name === path).child;
144 | });
145 | let index = curDir.findIndex((dir) => dir.name === folderName);
146 |
147 | if (index === -1) return { ...state };
148 | curDir.splice(index, 1);
149 | localSave("fileSystem", { ...state, fileSystem: [...newDirectory] });
150 | return { ...state, fileSystem: [...newDirectory] };
151 | }
152 | case actions.PREVIOUS_STATE_SET: {
153 | let previousState = JSON.parse(localStorage.getItem("fileSystem"));
154 | if (previousState) return { ...previousState };
155 | else return state;
156 | }
157 | default:
158 | return state;
159 | }
160 | };
161 |
162 | export default fileSystemReducers;
163 |
--------------------------------------------------------------------------------
/src/serviceWorkerRegistration.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://cra.link/PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === "localhost" ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === "[::1]" ||
17 | // 127.0.0.0/8 are considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process?.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process?.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener("load", () => {
35 | const swUrl = `${process?.env.PUBLIC_URL}/service-worker.js`;
36 | if (isLocalhost) {
37 | // This is running on localhost. Let's check if a service worker still exists or not.
38 | checkValidServiceWorker(swUrl, config);
39 |
40 | // Add some additional logging to localhost, pointing developers to the
41 | // service worker/PWA documentation.
42 | navigator.serviceWorker.ready.then(() => {
43 | console.log(
44 | "This web app is being served cache-first by a service " +
45 | "worker. To learn more, visit https://cra.link/PWA"
46 | );
47 | });
48 | } else {
49 | // Is not localhost. Just register service worker
50 | registerValidSW(swUrl, config);
51 | }
52 | });
53 | }
54 | }
55 |
56 | function registerValidSW(swUrl, config) {
57 | navigator.serviceWorker
58 | .register(swUrl)
59 | .then((registration) => {
60 | registration.onupdatefound = () => {
61 | const installingWorker = registration.installing;
62 | if (installingWorker == null) {
63 | return;
64 | }
65 | installingWorker.onstatechange = () => {
66 | if (installingWorker.state === "installed") {
67 | if (navigator.serviceWorker.controller) {
68 | // At this point, the updated precached content has been fetched,
69 | // but the previous service worker will still serve the older
70 | // content until all client tabs are closed.
71 | console.log(
72 | "New content is available and will be used when all " +
73 | "tabs for this page are closed. See https://cra.link/PWA."
74 | );
75 |
76 | // Execute callback
77 | if (config && config.onUpdate) {
78 | config.onUpdate(registration);
79 | }
80 | } else {
81 | // At this point, everything has been precached.
82 | // It's the perfect time to display a
83 | // "Content is cached for offline use." message.
84 | console.log("Content is cached for offline use.");
85 |
86 | // Execute callback
87 | if (config && config.onSuccess) {
88 | config.onSuccess(registration);
89 | }
90 | }
91 | }
92 | };
93 | };
94 | })
95 | .catch((error) => {
96 | console.error("Error during service worker registration:", error);
97 | });
98 | }
99 |
100 | function checkValidServiceWorker(swUrl, config) {
101 | // Check if the service worker can be found. If it can't reload the page.
102 | fetch(swUrl, {
103 | headers: { "Service-Worker": "script" },
104 | })
105 | .then((response) => {
106 | // Ensure service worker exists, and that we really are getting a JS file.
107 | const contentType = response.headers.get("content-type");
108 | if (
109 | response.status === 404 ||
110 | (contentType != null && contentType.indexOf("javascript") === -1)
111 | ) {
112 | // No service worker found. Probably a different app. Reload the page.
113 | navigator.serviceWorker.ready.then((registration) => {
114 | registration.unregister().then(() => {
115 | window.location.reload();
116 | });
117 | });
118 | } else {
119 | // Service worker found. Proceed as normal.
120 | registerValidSW(swUrl, config);
121 | }
122 | })
123 | .catch(() => {
124 | console.log(
125 | "No internet connection found. App is running in offline mode."
126 | );
127 | });
128 | }
129 |
130 | export function unregister() {
131 | if ("serviceWorker" in navigator) {
132 | navigator.serviceWorker.ready
133 | .then((registration) => {
134 | registration.unregister();
135 | })
136 | .catch((error) => {
137 | console.error(error.message);
138 | });
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect';
6 |
--------------------------------------------------------------------------------
/src/store.js:
--------------------------------------------------------------------------------
1 | import { configureStore } from "@reduxjs/toolkit";
2 | import thunk from "redux-thunk";
3 | import rootReducer from "./reducers/combinedReducers";
4 |
5 | export const store = configureStore({
6 | reducer: rootReducer,
7 | devTools: process.env.NODE_ENV !== "production",
8 | middleware: [thunk.withExtraArgument({})],
9 | });
10 |
11 | export default store;
12 |
--------------------------------------------------------------------------------