diff --git a/src/components/task-bar/menus/HomeMenu.jsx b/src/components/task-bar/menus/HomeMenu.jsx
index 51abba1..47c6a94 100644
--- a/src/components/task-bar/menus/HomeMenu.jsx
+++ b/src/components/task-bar/menus/HomeMenu.jsx
@@ -3,7 +3,7 @@ import styles from "./HomeMenu.module.css";
import appStyles from "./AppList.module.css";
import { faCircleInfo, faFileLines, faGear, faImage, faPowerOff } from "@fortawesome/free-solid-svg-icons";
import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext.js";
-import ApplicationsManager from "../../../features/applications/applications.js";
+import AppsManager from "../../../features/applications/applications.js";
import { ReactSVG } from "react-svg";
import { closeViewport } from "../../../features/utils/browser.js";
import { useKeyboardListener } from "../../../hooks/utils/keyboard.js";
@@ -11,6 +11,7 @@ import { useVirtualRoot } from "../../../hooks/virtual-drive/virtualRootContext.
import { useEffect, useState } from "react";
import Vector2 from "../../../features/math/vector2.js";
import utilStyles from "../../../styles/utils.module.css";
+import { APPS } from "../../../constants/applications.js";
/**
* @param {object} props
@@ -82,13 +83,13 @@ export function HomeMenu({ active, setActive, search }) {
@@ -96,7 +97,7 @@ export function HomeMenu({ active, setActive, search }) {
ProzillaOS
- {ApplicationsManager.APPLICATIONS.map(({ name, id }) =>
+ {AppsManager.APPLICATIONS.map(({ name, id }) =>
)}
diff --git a/src/components/task-bar/menus/SearchMenu.jsx b/src/components/task-bar/menus/SearchMenu.jsx
index 92e7e4b..559deda 100644
--- a/src/components/task-bar/menus/SearchMenu.jsx
+++ b/src/components/task-bar/menus/SearchMenu.jsx
@@ -1,6 +1,6 @@
import styles from "./SearchMenu.module.css";
import appStyles from "./AppList.module.css";
-import ApplicationsManager from "../../../features/applications/applications.js";
+import AppsManager from "../../../features/applications/applications.js";
import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext.js";
import { ReactSVG } from "react-svg";
import { useEffect } from "react";
@@ -32,7 +32,7 @@ export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inp
}, [inputRef]);
useEffect(() => {
- setApps(ApplicationsManager.APPLICATIONS.filter(({ name }) =>
+ setApps(AppsManager.APPLICATIONS.filter(({ name }) =>
name.toLowerCase().includes(searchQuery.toLowerCase().trim())
));
}, [searchQuery]);
@@ -78,7 +78,7 @@ export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inp
windowsManager.open(id);
}}
>
-
+
{name}
)}
diff --git a/src/components/windows/WindowView.jsx b/src/components/windows/WindowView.jsx
index ac41ace..e2c74f1 100644
--- a/src/components/windows/WindowView.jsx
+++ b/src/components/windows/WindowView.jsx
@@ -12,6 +12,7 @@ import utilStyles from "../../styles/utils.module.css";
import { useModals } from "../../hooks/modals/modals.js";
import { ModalsView } from "../modals/ModalsView.jsx";
import { useContextMenu } from "../../hooks/modals/contextMenu.js";
+import AppsManager from "../../features/applications/applications.js";
/**
* @param {object} props
@@ -40,6 +41,7 @@ export const WindowView = memo(({ id, app, size, position, onInteract, options,
const [screenHeight, setScreenHeight] = useState(100);
const [title, setTitle] = useState(app.name);
+ const [iconUrl, setIconUrl] = useState(AppsManager.getAppIconUrl(app.id));
const { onContextMenu } = useContextMenu({
modalsManager,
@@ -137,7 +139,7 @@ export const WindowView = memo(({ id, app, size, position, onInteract, options,
diff --git a/src/constants/applications.js b/src/constants/applications.js
new file mode 100644
index 0000000..54b7861
--- /dev/null
+++ b/src/constants/applications.js
@@ -0,0 +1,7 @@
+export const APPS = {
+ TERMINAL: "terminal",
+ SETTINGS: "settings",
+ MEDIA_VIEWER: "media-viewer",
+ TEXT_EDITOR: "text-editor",
+ FILE_EXPLORER: "file-explorer"
+};
\ No newline at end of file
diff --git a/src/features/applications/applications.js b/src/features/applications/applications.js
index 71ebf68..226c058 100644
--- a/src/features/applications/applications.js
+++ b/src/features/applications/applications.js
@@ -7,17 +7,18 @@ import { TextEditor } from "../../components/applications/text-editor/TextEditor
import { Settings } from "../../components/applications/settings/Settings.jsx";
// import { Calculator } from "../../components/applications/calculator/Calculator.jsx";
import Vector2 from "../math/vector2.js";
+import { APPS } from "../../constants/applications.js";
-export default class ApplicationsManager {
+export default class AppsManager {
static APPLICATIONS = [
- new Application("Commands", "terminal", Terminal),
- new Application("Settings", "settings", Settings),
- new Application("Photos", "media-viewer", MediaViewer),
+ new Application("Commands", APPS.TERMINAL, Terminal),
+ new Application("Settings", APPS.SETTINGS, Settings),
+ new Application("Photos", APPS.MEDIA_VIEWER, MediaViewer),
// new Application("Browser", "browser"),
// new Application("Calculator", "calculator", Calculator, { size: new Vector2(400, 600) }),
- new Application("Notes", "text-editor", TextEditor),
+ new Application("Notes", APPS.TEXT_EDITOR, TextEditor),
// new Application("Code Editor", "code-editor"),
- new Application("Files", "file-explorer", FileExplorer),
+ new Application("Files", APPS.FILE_EXPLORER, FileExplorer),
new Application("Wordle", "wordle", WebView, {
source: "https://prozilla.dev/wordle",
size: new Vector2(400, 650)
@@ -36,7 +37,7 @@ export default class ApplicationsManager {
* @param {string} id
* @returns {Application | null}
*/
- static getApplication(id) {
+ static getApp(id) {
let application = null;
this.APPLICATIONS.forEach((app) => {
@@ -54,21 +55,35 @@ export default class ApplicationsManager {
* @param {string} fileExtension
* @returns {Application}
*/
- static getFileApplication(fileExtension) {
+ static getFileApp(fileExtension) {
let app = null;
// eslint-disable-next-line default-case
switch (fileExtension) {
case "png":
- app = this.getApplication("media-viewer");
+ app = this.getApp(APPS.MEDIA_VIEWER);
break;
case "txt":
case "md":
case "xml":
- app = this.getApplication("text-editor");
+ app = this.getApp(APPS.TEXT_EDITOR);
break;
}
return app;
}
+
+ /**
+ * Returns the url of an icon inside the icons folder or the default icon of an app
+ * @param {string} appId
+ * @param {string | null} iconName
+ * @returns {string}
+ */
+ static getAppIconUrl(appId, iconName) {
+ if (iconName == null) {
+ return `${process.env.PUBLIC_URL}/media/applications/icons/${appId}.svg`;
+ } else {
+ return `${process.env.PUBLIC_URL}/media/applications/${appId}/icons/${iconName}.svg`;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/features/applications/terminal/commands/neofetch.js b/src/features/applications/terminal/commands/neofetch.js
index 5f50d27..208270c 100644
--- a/src/features/applications/terminal/commands/neofetch.js
+++ b/src/features/applications/terminal/commands/neofetch.js
@@ -1,7 +1,8 @@
+import { APPS } from "../../../../constants/applications.js";
import { ASCII_LOGO, NAME } from "../../../../constants/branding.js";
import { START_DATE } from "../../../../index.js";
import { formatRelativeTime } from "../../../utils/date.js";
-import ApplicationsManager from "../../applications.js";
+import AppsManager from "../../applications.js";
import Command from "../command.js";
export const neofetch = new Command("neofetch", (args, { username, hostname }) => {
@@ -32,7 +33,7 @@ export const neofetch = new Command("neofetch", (args, { username, hostname }) =
`RESOLUTION: ${window.innerWidth}x${window.innerHeight}`,
"THEME: default",
"ICONS: Font Awesome",
- `TERMINAL: ${ApplicationsManager.getApplication("terminal")?.name ?? "Unknown"}`,
+ `TERMINAL: ${AppsManager.getApp(APPS.TERMINAL)?.name ?? "Unknown"}`,
`BROWSER: ${browserName}`,
`PLATFORM: ${navigator.platform}`,
`LANGUAGE: ${navigator.language}`,
diff --git a/src/features/virtual-drive/virtualBase.js b/src/features/virtual-drive/virtualBase.js
index d550809..7cefed0 100644
--- a/src/features/virtual-drive/virtualBase.js
+++ b/src/features/virtual-drive/virtualBase.js
@@ -69,6 +69,27 @@ export class VirtualBase extends EventEmitter {
return this;
}
+ /**
+ * @param {string} iconUrl
+ * @returns {VirtualBase}
+ */
+ setIconUrl(iconUrl) {
+ if (this.iconUrl === iconUrl || !this.canBeEdited)
+ return;
+
+ this.iconUrl = iconUrl;
+
+ this.confirmChanges();
+ return this;
+ }
+
+ /**
+ * @returns {string}
+ */
+ getIconUrl = () => {
+ return this.iconUrl ?? this.linkedFile?.iconUrl ?? this.linkedFolder?.iconUrl;
+ };
+
delete() {
if (!this.canBeEdited)
return;
@@ -168,7 +189,8 @@ export class VirtualBase extends EventEmitter {
*/
toJSON() {
const object = {
- nam: this.name
+ nam: this.name,
+ ico: this.iconUrl,
};
return object;
diff --git a/src/features/virtual-drive/virtualFolder.js b/src/features/virtual-drive/virtualFolder.js
index 63dc836..7d6d2ee 100644
--- a/src/features/virtual-drive/virtualFolder.js
+++ b/src/features/virtual-drive/virtualFolder.js
@@ -26,7 +26,7 @@ export class VirtualFolder extends VirtualBase {
/**
* @param {string} alias
- * @returns {VirtualFolder}
+ * @returns {ThisType}
*/
setAlias(alias) {
return super.setAlias(alias);
@@ -36,7 +36,7 @@ export class VirtualFolder extends VirtualBase {
* Returns true if this folder contains a file matching a name and extension
* @param {string} name
* @param {string} extension
- * @returns {VirtualFile}
+ * @returns {ThisType}
*/
hasFile(name, extension) {
return this.findFile(name, extension) !== null;
@@ -61,7 +61,9 @@ export class VirtualFolder extends VirtualBase {
let resultFile = null;
this.files.forEach((file) => {
- if ((file.name === name || (file.alias && file.alias === name)) && file.extension === extension) {
+ const matchingName = (file.name === name || (file.alias && file.alias === name));
+ const matchingExtension = (extension == null || file.extension === extension);
+ if (matchingName && matchingExtension) {
return resultFile = file;
}
});
diff --git a/src/features/virtual-drive/virtualRoot.js b/src/features/virtual-drive/virtualRoot.js
index 55d3779..a9f40d8 100644
--- a/src/features/virtual-drive/virtualRoot.js
+++ b/src/features/virtual-drive/virtualRoot.js
@@ -1,4 +1,6 @@
+import { APPS } from "../../constants/applications.js";
import { WALLPAPERS } from "../../constants/desktop.js";
+import AppsManager from "../applications/applications.js";
import { StorageManager } from "../storage/storageManager.js";
import { VirtualFolderLink } from "./VirtualFolderLink.js";
import { VirtualFile } from "./virtualFile.js";
@@ -56,7 +58,7 @@ export class VirtualRoot extends VirtualFolder {
});
})
.createFolder("Images", (folder) => {
- linkedPaths.images = folder.path;
+ folder.setIconUrl(AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "folder-images"));
folder.createFolder("Wallpapers", (folder) => {
folder.setProtected(true);
for (let i = 0; i < WALLPAPERS.length; i++) {
@@ -68,15 +70,19 @@ export class VirtualRoot extends VirtualFolder {
}).createFile("ProzillaOS", "png", (file) => {
file.setSource("/media/banner-logo-title.png");
});
+ linkedPaths.images = folder.path;
})
.createFolder("Documents", (folder) => {
- linkedPaths.documents = folder.path;
+ folder.setIconUrl(AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "folder-text"));
folder.createFile("text", "txt", (file) => {
file.setContent("Hello world!");
}).createFile("info", "md", (file) => {
+ file.setProtected(true)
+ .setSource("/documents/info.md")
+ .setIconUrl(AppsManager.getAppIconUrl(APPS.FILE_EXPLORER, "file-info"));
linkedPaths.info = file.path;
- file.setProtected(true).setSource("/documents/info.md");
});
+ linkedPaths.documents = folder.path;
})
.createFolder("Desktop", (folder) => {
folder.createFileLink("info.md", (fileLink) => {
@@ -126,10 +132,20 @@ export class VirtualRoot extends VirtualFolder {
const shortcuts = {...object.scs};
- const addFile = ({ nam: name, ext: extension, src: source, cnt: content, lnk: link }, parent = this) => {
+ const addFile = ({
+ nam: name,
+ ext: extension,
+ src: source,
+ cnt: content,
+ lnk: link,
+ ico: iconUrl,
+ }, parent = this) => {
if (link) {
parent.createFileLink(name, (fileLink) => {
fileLink.setLinkedPath(link);
+ if (iconUrl != null) {
+ fileLink.setIconUrl(iconUrl);
+ }
});
return;
}
@@ -140,13 +156,25 @@ export class VirtualRoot extends VirtualFolder {
} else if (content != null) {
file.setContent(content);
}
+ if (iconUrl != null) {
+ file.setIconUrl(iconUrl);
+ }
});
};
- const addFolder = ({ nam: name, fds: folders, fls: files, lnk: link }, parent = this) => {
+ const addFolder = ({
+ nam: name,
+ fds: folders,
+ fls: files,
+ lnk: link,
+ ico: iconUrl,
+ }, parent = this) => {
if (link) {
parent.createFolderLink(name, (folderLink) => {
folderLink.setLinkedPath(link);
+ if (iconUrl != null) {
+ folderLink.setIconUrl(iconUrl);
+ }
});
return;
}
@@ -170,6 +198,9 @@ export class VirtualRoot extends VirtualFolder {
addFile(file, folder);
});
}
+ if (iconUrl != null) {
+ folder.setIconUrl(iconUrl);
+ }
});
};
diff --git a/src/features/windows/windowsManager.js b/src/features/windows/windowsManager.js
index 0334126..4523e94 100644
--- a/src/features/windows/windowsManager.js
+++ b/src/features/windows/windowsManager.js
@@ -1,5 +1,5 @@
import { SCREEN_MARGIN, TASKBAR_HEIGHT } from "../../constants/windows.js";
-import ApplicationsManager from "../applications/applications.js";
+import AppsManager from "../applications/applications.js";
import { randomRange } from "../math/random.js";
import Vector2 from "../math/vector2.js";
import { VirtualFile } from "../virtual-drive/virtualFile.js";
@@ -16,7 +16,7 @@ export default class WindowsManager {
* @returns {object | null}
*/
open(appId, options) {
- const app = ApplicationsManager.getApplication(appId);
+ const app = AppsManager.getApp(appId);
if (app == null) {
console.warn(`Failed to open app ${appId}: app not found`);
@@ -52,14 +52,13 @@ export default class WindowsManager {
/**
* @param {VirtualFile} file
+ * @param {object} options
* @returns {object}
*/
- openFile(file) {
- console.log(file);
- const app = ApplicationsManager.getFileApplication(file.extension);
- console.log(app);
+ openFile(file, options = {}) {
+ const app = AppsManager.getFileApp(file.extension);
if (app != null)
- return this.open(app.id, { file });
+ return this.open(app.id, { file, ...options });
}
/**