Added virtual drive + terminal directory commands

This commit is contained in:
Prozilla 2023-07-22 15:18:55 +02:00
parent 1f70f610f7
commit f11820436b
No known key found for this signature in database
GPG key ID: 5858DFE71CAF31EE
20 changed files with 347 additions and 152 deletions

View file

@ -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 (
<WindowsManagerProvider>
<div className="App">
<Taskbar/>
<WindowsView/>
</div>
</WindowsManagerProvider>
<VirtualRootProvider>
<WindowsManagerProvider>
<div className="App">
<Taskbar/>
<WindowsView/>
</div>
</WindowsManagerProvider>
</VirtualRootProvider>
);
}

View file

@ -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() {

View file

@ -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 (
<span className={styles.Input}>
{prefix && <p className={[styles.Prefix]}>{prefix}</p>}
<label for="input"/>
<label htmlFor="input"/>
<input
id="input"
value={value}
@ -22,6 +21,7 @@ function InputLine({ value, prefix, onChange, onKeyUp, onKeyDown }) {
onKeyUp={onKeyUp}
onKeyDown={onKeyDown}
spellCheck={false}
autoComplete={null}
autoFocus
/>
</span>
@ -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()}
<InputLine
value={inputValue}
prefix={PREFIX}
prefix={prefix}
onKeyDown={onKeyDown}
onChange={onChange}
/>

View file

@ -16,6 +16,7 @@
.Prefix {
width: max-content;
white-space: nowrap;
}
.Input, .Output {

View file

@ -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 };
})
]
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -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 "";
}
}

View file

@ -1,24 +0,0 @@
import { createContext, useContext } from "react";
import { VirtualDrive } from "../modules/virtual-drive/virtual-drive.js";
const VirtualDriveContext = createContext();
/**
* @returns {React.Provider<any>}
*/
export function VirtualDriveProvider({ children }) {
const virtualDrive = new VirtualDrive();
return (
<VirtualDriveContext.Provider value={virtualDrive}>
{children}
</VirtualDriveContext.Provider>
);
}
/**
* @returns {VirtualDrive}
*/
export function useWindowsManager() {
return useContext(VirtualDriveContext);
}

View file

@ -0,0 +1,36 @@
import { createContext, useContext } from "react";
import { VirtualRoot } from "../features/virtual-drive/virtual-root.js";
const VirtualRootContext = createContext();
/**
* @returns {React.Provider<VirtualRoot>}
*/
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 (
<VirtualRootContext.Provider value={virtualRoot}>
{children}
</VirtualRootContext.Provider>
);
}
/**
* @returns {VirtualRoot}
*/
export function useVirtualRoot() {
return useContext(VirtualRootContext);
}

View file

@ -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();

View file

@ -1,7 +0,0 @@
import { VirtualFolder } from "./virtual-folder.js";
export class VirtualDrive extends VirtualFolder {
constructor() {
}
}

View file

@ -1,6 +0,0 @@
export class VirtualFile {
constructor(name, extension) {
this.name = name;
this.extension = extension;
}
}

View file

@ -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);
}
})
}
}