diff --git a/src/App.js b/src/App.js
index 7775cc8..07549bb 100644
--- a/src/App.js
+++ b/src/App.js
@@ -2,15 +2,18 @@ import "./App.css";
import { Taskbar } from "./components/TaskBar.js";
import { WindowsManagerProvider } from "./hooks/WindowsManagerContext.js";
import { WindowsView } from "./components/WindowsView.js";
+import { VirtualRootProvider } from "./hooks/VirtualRootContext.js";
function App() {
return (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
);
}
diff --git a/src/components/TaskBar.js b/src/components/TaskBar.js
index 00f1c48..1c54842 100644
--- a/src/components/TaskBar.js
+++ b/src/components/TaskBar.js
@@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
import "./TaskBar.css";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faBatteryFull, faSearch, faVolumeHigh, faWifi } from "@fortawesome/free-solid-svg-icons";
-import ApplicationsManager from "../modules/applications/applications.js";
+import ApplicationsManager from "../features/applications/applications.js";
import { AppButton } from "./task-bar/AppButton.js";
export function Taskbar() {
diff --git a/src/components/applications/terminal/Terminal.js b/src/components/applications/terminal/Terminal.js
index 3ab9e19..97b485b 100644
--- a/src/components/applications/terminal/Terminal.js
+++ b/src/components/applications/terminal/Terminal.js
@@ -1,8 +1,7 @@
import { useState } from "react";
import styles from "./Terminal.module.css";
import { Command } from "./commands.js";
-
-const PREFIX = "$ ";
+import { useVirtualRoot } from "../../../hooks/VirtualRootContext.js";
function OutputLine({ text }) {
return (
@@ -14,7 +13,7 @@ function InputLine({ value, prefix, onChange, onKeyUp, onKeyDown }) {
return (
{prefix && {prefix}
}
-
+
@@ -31,6 +31,10 @@ function InputLine({ value, prefix, onChange, onKeyUp, onKeyDown }) {
export function Terminal() {
const [inputValue, setInputValue] = useState("");
const [history, setHistory] = useState([]);
+ const virtualRoot = useVirtualRoot();
+ const [currentDirectory, setCurrentDirectory] = useState(virtualRoot);
+
+ const prefix = `user@prozilla-os:${currentDirectory.formattedPath}$ `;
const updatedHistory = history;
const pushHistory = (entry) => {
@@ -51,7 +55,7 @@ export function Terminal() {
return;
pushHistory({
- text: PREFIX + value,
+ text: prefix + value,
isInput: true
});
@@ -65,7 +69,7 @@ export function Terminal() {
const command = Command.find(commandName);
if (!command) {
- return promptOutput("Command not found: " + commandName);
+ return promptOutput(`${commandName}: Command not found`);
}
let response = null;
@@ -73,17 +77,20 @@ export function Terminal() {
try {
response = command.execute(args, {
promptOutput,
- pushHistory
+ pushHistory,
+ virtualRoot,
+ currentDirectory,
+ setCurrentDirectory
});
if (response == null)
- return promptOutput("Command failed.");
+ return promptOutput(`${commandName}: Command failed`);
if (!response.blank)
promptOutput(response);
} catch (error) {
- promptOutput("Command failed.");
console.error(error);
+ promptOutput(`${commandName}: Command failed`);
}
};
@@ -120,7 +127,7 @@ export function Terminal() {
{displayHistory()}
diff --git a/src/components/applications/terminal/Terminal.module.css b/src/components/applications/terminal/Terminal.module.css
index 7991854..01ab882 100644
--- a/src/components/applications/terminal/Terminal.module.css
+++ b/src/components/applications/terminal/Terminal.module.css
@@ -16,6 +16,7 @@
.Prefix {
width: max-content;
+ white-space: nowrap;
}
.Input, .Output {
diff --git a/src/components/applications/terminal/commands.js b/src/components/applications/terminal/commands.js
index 44df198..e554782 100644
--- a/src/components/applications/terminal/commands.js
+++ b/src/components/applications/terminal/commands.js
@@ -38,5 +38,35 @@ export class Command {
return { blank: true };
}),
+ new Command("ls", (args, { currentDirectory }) => {
+ let directory = currentDirectory;
+
+ if (args.length > 0) {
+ directory = currentDirectory.navigate(args[0]);
+ }
+
+ if (!directory)
+ return `ls: Cannot access '${args[0]}': No such file or directory`;
+
+ const fodlerNames = directory.subFolders.map((folder) => folder.id);
+ const fileNames = directory.files.map((file) => file.id);
+
+ const contents = fodlerNames.concat(fileNames);
+
+ if (contents.length === 0)
+ return { blank: true };
+
+ return contents.sort((nameA, nameB) => nameA.localeCompare(nameB)).join(" ");
+ }),
+ new Command("cd", (args, { currentDirectory, setCurrentDirectory }) => {
+ const destination = currentDirectory.navigate(args[0]);
+
+ if (!destination)
+ return `cd: ${args[0]}: No such file or directory`;
+
+ console.log(destination);
+ setCurrentDirectory(destination);
+ return { blank: true };
+ })
]
}
\ No newline at end of file
diff --git a/src/modules/applications/application.js b/src/features/applications/application.js
similarity index 100%
rename from src/modules/applications/application.js
rename to src/features/applications/application.js
diff --git a/src/modules/applications/applications.js b/src/features/applications/applications.js
similarity index 100%
rename from src/modules/applications/applications.js
rename to src/features/applications/applications.js
diff --git a/src/modules/math/random.js b/src/features/math/random.js
similarity index 100%
rename from src/modules/math/random.js
rename to src/features/math/random.js
diff --git a/src/modules/math/vector2.js b/src/features/math/vector2.js
similarity index 100%
rename from src/modules/math/vector2.js
rename to src/features/math/vector2.js
diff --git a/src/features/virtual-drive/virtual-base.js b/src/features/virtual-drive/virtual-base.js
new file mode 100644
index 0000000..e256de6
--- /dev/null
+++ b/src/features/virtual-drive/virtual-base.js
@@ -0,0 +1,61 @@
+
+export class VirtualBase {
+ /**
+ * @param {String} name
+ */
+ constructor(name) {
+ this.name = name;
+ }
+
+ get id() {
+ return this.name;
+ }
+
+ /**
+ * @param {String} name
+ */
+ setName(name) {
+ this.name = name;
+ return this;
+ }
+
+ setAlias(alias) {
+ this.alias = alias;
+ this.getRoot().addShortcut(alias, this);
+ return this;
+ }
+
+ /**
+ * @param {VirtualBase} parent
+ */
+ setParent(parent) {
+ this.parent = parent;
+ return this;
+ }
+
+ delete() {
+ this.parent.remove?.(this);
+ }
+
+ open() {
+
+ }
+
+ get path() {
+ return this.parent?.path + "/" + this.id;
+ }
+
+ get formattedPath() {
+ return this.alias ?? this.path;
+ }
+
+ getRoot() {
+ const root = this.root ?? this.parent.getRoot();
+
+ if (root === null) {
+ throw new Error("Root not found");
+ }
+
+ return root;
+ }
+}
\ No newline at end of file
diff --git a/src/features/virtual-drive/virtual-file.js b/src/features/virtual-drive/virtual-file.js
new file mode 100644
index 0000000..fe5192f
--- /dev/null
+++ b/src/features/virtual-drive/virtual-file.js
@@ -0,0 +1,12 @@
+import { VirtualBase } from "./virtual-base.js";
+
+export class VirtualFile extends VirtualBase {
+ constructor(name, extension) {
+ super(name);
+ this.extension = extension;
+ }
+
+ get id() {
+ return this.name + this.extension;
+ }
+}
\ No newline at end of file
diff --git a/src/features/virtual-drive/virtual-folder.js b/src/features/virtual-drive/virtual-folder.js
new file mode 100644
index 0000000..ba62a63
--- /dev/null
+++ b/src/features/virtual-drive/virtual-folder.js
@@ -0,0 +1,159 @@
+import { VirtualBase } from "./virtual-base.js";
+import { VirtualFile } from "./virtual-file.js";
+
+export class VirtualFolder extends VirtualBase {
+ static TYPE = {
+ GENERAL: 0,
+ MEDIA: 1,
+ }
+
+ /**
+ * @param {String} name
+ * @param {Number} type
+ */
+ constructor(name, type) {
+ super(name);
+ this.subFolders = [];
+ this.files = [];
+ this.type = type ?? VirtualFolder.TYPE.GENERAL;
+ }
+
+ // add(element) {
+
+ // }
+
+ hasFile(name, extension) {
+ return this.findFile(name, extension) !== null;
+ }
+
+ hasFolder(name) {
+ return this.findSubFolder(name) !== null;
+ }
+
+ findFile(name, extension) {
+ let resultFile = null;
+
+ this.files.forEach((file) => {
+ if ((file.name === name || (file.alias && file.alias === name)) && file.extension === extension) {
+ return resultFile = file;
+ }
+ });
+
+ return resultFile;
+ }
+
+ findSubFolder(name) {
+ let resultFolder = null;
+
+ this.subFolders.forEach((folder) => {
+ if (folder.name === name || (folder.alias && folder.alias === name)) {
+ return resultFolder = folder;
+ }
+ });
+
+ return resultFolder;
+ }
+
+ createFile(name, extension) {
+ const newFile = new VirtualFile(name, extension);
+ this.files.push(newFile);
+ newFile.parent = this;
+ return newFile;
+ }
+
+ createFolder(name) {
+ const newFolder = new VirtualFolder(name);
+ this.subFolders.push(newFolder);
+ newFolder.parent = this;
+ return newFolder;
+ }
+
+ /**
+ * @param {String} destination
+ */
+ addFile(destination) {
+ const folderNames = destination.split("/");
+ const fileExtendedName = folderNames.pop().split();
+ const fileExtendedNameSegments = fileExtendedName.split(".");
+
+ const fileName = fileExtendedNameSegments[0];
+ const fileExtension = fileExtendedNameSegments.length > 1 ? fileExtendedNameSegments[1] : "txt";
+
+ // To do: check if file already exists
+
+ const file = new VirtualFile(fileName, fileExtension);
+
+ const folders = this.addFolder(folderNames.join("/"));
+
+ const parent = folders[folders.length - 1];
+ parent.files.push(file);
+ file.parent = parent;
+ }
+
+ /**
+ * @param {String} destination
+ */
+ addFolder(destination) {
+ if (destination.endsWith("/"))
+ destination = destination.slice(0, -1);
+
+ const folderNames = destination.split("/");
+
+ let currentFolder = this;
+ folderNames.forEach((folderName) => {
+ if (!currentFolder.hasFolder(folderName)) {
+ currentFolder.createFolder(folderName);
+ }
+ })
+ }
+
+ remove(child) {
+ if (child instanceof VirtualFile) {
+ // Remove file by id
+ } else if (child instanceof VirtualFolder) {
+ // Remove folder by id
+ }
+ }
+
+ navigate(relativePath) {
+ const segments = relativePath.split("/");
+ let currentDirectory = this;
+
+ const getDirectory = (path, isStart) => {
+ if (isStart && path === "") {
+ return this.getRoot();
+ } else if (isStart && Object.keys(this.getRoot().shortcuts).includes(path)) {
+ return this.getRoot().shortcuts[path];
+ } else if (path === ".") {
+ return this;
+ } else if (path === "..") {
+ return currentDirectory?.parent;
+ } else {
+ return currentDirectory?.findSubFolder(path);
+ }
+ }
+
+ if (segments.length === 1) {
+ const directory = getDirectory(segments[0], true);
+ if (directory !== null)
+ return directory;
+ }
+
+ for (let i = 0; i < segments.length - 1; i++) {
+ const segment = segments[i];
+ currentDirectory = getDirectory(segment, i === 0);
+ }
+
+ const lastSegment = segments[segments.length - 1];
+
+ if (lastSegment === "") {
+ return currentDirectory;
+ } else if (currentDirectory !== null) {
+ // To do: add support for file names with dots
+ const [name, extension] = lastSegment.split(".");
+ return currentDirectory.findFile(name, extension);
+ } else {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/features/virtual-drive/virtual-root.js b/src/features/virtual-drive/virtual-root.js
new file mode 100644
index 0000000..75abc74
--- /dev/null
+++ b/src/features/virtual-drive/virtual-root.js
@@ -0,0 +1,21 @@
+import { VirtualFolder } from "./virtual-folder.js";
+
+/**
+ * A virtual folder that serves as the root folder
+ */
+export class VirtualRoot extends VirtualFolder {
+ constructor() {
+ super("root");
+ this.root = this;
+ this.shortcuts = {};
+ }
+
+ addShortcut(name, destination) {
+ this.shortcuts[name] = destination;
+ return this;
+ }
+
+ get path() {
+ return "";
+ }
+}
\ No newline at end of file
diff --git a/src/modules/windows/windows.js b/src/features/windows/windows.js
similarity index 100%
rename from src/modules/windows/windows.js
rename to src/features/windows/windows.js
diff --git a/src/hooks/VirtualDriveContext.js b/src/hooks/VirtualDriveContext.js
deleted file mode 100644
index e292de2..0000000
--- a/src/hooks/VirtualDriveContext.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import { createContext, useContext } from "react";
-import { VirtualDrive } from "../modules/virtual-drive/virtual-drive.js";
-
-const VirtualDriveContext = createContext();
-
-/**
- * @returns {React.Provider}
- */
-export function VirtualDriveProvider({ children }) {
- const virtualDrive = new VirtualDrive();
-
- return (
-
- {children}
-
- );
-}
-
-/**
- * @returns {VirtualDrive}
- */
-export function useWindowsManager() {
- return useContext(VirtualDriveContext);
-}
\ No newline at end of file
diff --git a/src/hooks/VirtualRootContext.js b/src/hooks/VirtualRootContext.js
new file mode 100644
index 0000000..31a05e6
--- /dev/null
+++ b/src/hooks/VirtualRootContext.js
@@ -0,0 +1,36 @@
+import { createContext, useContext } from "react";
+import { VirtualRoot } from "../features/virtual-drive/virtual-root.js";
+
+const VirtualRootContext = createContext();
+
+/**
+ * @returns {React.Provider}
+ */
+export function VirtualRootProvider({ children }) {
+ const virtualRoot = new VirtualRoot().setAlias("/");
+
+ virtualRoot.createFolder("bin");
+ virtualRoot.createFolder("dev");
+ virtualRoot.createFolder("etc");
+ virtualRoot.createFolder("usr");
+ virtualRoot.createFolder("home").createFolder("prozilla-os").setAlias("~");
+ virtualRoot.createFolder("lib");
+ virtualRoot.createFolder("sbin");
+ virtualRoot.createFolder("tmp");
+ virtualRoot.createFolder("var");
+
+ console.log(virtualRoot.subFolders);
+
+ return (
+
+ {children}
+
+ );
+}
+
+/**
+ * @returns {VirtualRoot}
+ */
+export function useVirtualRoot() {
+ return useContext(VirtualRootContext);
+}
\ No newline at end of file
diff --git a/src/hooks/WindowsManagerContext.js b/src/hooks/WindowsManagerContext.js
index e8a90d6..3736437 100644
--- a/src/hooks/WindowsManagerContext.js
+++ b/src/hooks/WindowsManagerContext.js
@@ -1,5 +1,5 @@
import { createContext, useContext } from "react";
-import WindowsManager from "../modules/windows/windows.js";
+import WindowsManager from "../features/windows/windows.js";
import { WindowsProvider } from "./WindowsContext.js";
const WindowsManagerContext = createContext();
diff --git a/src/modules/virtual-drive/virtual-drive.js b/src/modules/virtual-drive/virtual-drive.js
deleted file mode 100644
index f824517..0000000
--- a/src/modules/virtual-drive/virtual-drive.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import { VirtualFolder } from "./virtual-folder.js";
-
-export class VirtualDrive extends VirtualFolder {
- constructor() {
-
- }
-}
\ No newline at end of file
diff --git a/src/modules/virtual-drive/virtual-file.js b/src/modules/virtual-drive/virtual-file.js
deleted file mode 100644
index 4e17c4f..0000000
--- a/src/modules/virtual-drive/virtual-file.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export class VirtualFile {
- constructor(name, extension) {
- this.name = name;
- this.extension = extension;
- }
-}
\ No newline at end of file
diff --git a/src/modules/virtual-drive/virtual-folder.js b/src/modules/virtual-drive/virtual-folder.js
deleted file mode 100644
index 1806e5f..0000000
--- a/src/modules/virtual-drive/virtual-folder.js
+++ /dev/null
@@ -1,98 +0,0 @@
-import { VirtualFile } from "./virtual-file.js";
-
-export class VirtualFolder {
- static TYPE = {
- GENERAL: 0,
- MEDIA: 1,
- }
-
- /**
- * @param {String} name
- * @param {Number} type
- */
- constructor(name, type) {
- this.name = name;
- this.subFolders = [];
- this.files = [];
- this.type = type ?? this.TYPE.GENERAL;
- }
-
- // add(element) {
-
- // }
-
- hasFile(name, extension) {
- let exists = false;
-
- this.files.forEach(({ name: fileName, extension: fileExtension }) => {
- if (fileName === name && fileExtension === extension) {
- return exists = true;
- }
- });
-
- return exists;
- }
-
- hasFolder(name) {
- let exists = false;
-
- this.folders.forEach(({ name: folderName }) => {
- if (folderName === name) {
- return exists = true;
- }
- });
-
- return exists;
- }
-
- createFile(name, extension) {
- const file = new VirtualFile(name, extension);
- this.files.push(file);
- file.parent = this;
- }
-
- createFolder(name) {
- const newFolder = new VirtualFolder(name);
- this.subFolders.push(newFolder);
- newFolder.parent = this;
- }
-
- /**
- * @param {String} destination
- */
- addFile(destination) {
- const folderNames = destination.split("/");
- const fileExtendedName = folderNames.pop().split();
- const fileExtendedNameSegments = fileExtendedName.split(".");
-
- const fileName = fileExtendedNameSegments[0];
- const fileExtension = fileExtendedNameSegments.length > 1 ? fileExtendedNameSegments[1] : "txt";
-
- // To do: check if file already exists
-
- const file = new VirtualFile(fileName, fileExtension);
-
- const folders = this.addFolder(folderNames.join("/"));
-
- const parent = folders[folders.length - 1];
- parent.files.push(file);
- file.parent = parent;
- }
-
- /**
- * @param {String} destination
- */
- addFolder(destination) {
- if (destination.endsWith("/"))
- destination = destination.slice(0, -1);
-
- const folderNames = destination.split("/");
-
- let currentFolder = this;
- folderNames.forEach((folderName) => {
- if (!currentFolder.hasFolder(folderName)) {
- currentFolder.createFolder(folderName);
- }
- })
- }
-}
\ No newline at end of file