My personal website and portfolio that resembles a desktop operating system.
Built with React + TypeScript.
First, install NodeJS if not already installed.
Run npm install
and then npm run dev
to start the development server.
The website is easy to make with a basic knowledge of React even though the result looks great in my opinion.
The website wraps mostly everything inside a Desktop
component. This component contains all the application states and handles their actions like opening, closing, and minimizing them. It is also responsible for handling the StartMenu
component.
The website is only viewable in a computer because it simulates a desktop OS. If visited on a mobile device a warning is displayed and the user is asked to please visit the website using a computer.
This is a zustand store that handles z-indices of windows, so it keeps track of the latest one and always display newly opened apps at the top.
Application icons have a predefined position and they can be freely moved by dragging them around like in a real OS. The app supports both hyperlinks
and application icons (AppIcon
s), the difference is that hyperlinks
only open a link in a new tab, while application icons open a window containing the app. Currently, the only hyperlink is one that takes the user to the GitHub repository (this website).
When double clicking an AppIcon
, the application is opened if it was closed. If the application was opened it gets moved to the top (highest z-index).
Windows contain all applications' contents.
These are the Window props:
export interface WindowProps {
name: string; // Name shown on the top of the app
appName: string; // Internal app name
width: number; // Window width
height: number; // Window height
isOpen: boolean; // If true the window is shown in the task bar and also in the desktop if isHidden is false
isHidden: boolean; // If true the window isn't shown in the desktop (display: hidden)
handleClose: () => void; // Gets triggered when clicking the close icon
handleHide: () => void; // Gets triggered when clicking the minimize icon
zIndex: number; // Stores the window's current z-index
nonResizable: boolean; // If true window cannot be resized
}
Applcations just render a Window with all the app's contents (what was mentioned earlier). This is an example:
import classes from './ContactApplication.module.css';
import Window from '../../Window/Window';
import { WindowProps } from '../../../shared/WindowProps';
import { IconBrandGithub, IconBrandLinkedin } from '@tabler/icons-react';
const ContactApplication = ({ winProps }: { winProps: WindowProps }) => {
return (
winProps.isOpen && (
<Window
name="Contact me"
isHidden={winProps.isHidden}
handleClose={winProps.handleClose}
handleHide={winProps.handleHide}
width={800}
height={800}
appName={winProps.appName}
zIndex={winProps.zIndex}
nonResizable
>
<div className={classes.window}>
<h1>
Have an interesting idea? A job offer? Just want to reach out to me?
</h1>
<p>Email me at</p>
<a href="https://github.com/MiguelHigueraDev/raccoonOS/blob/main/mailto:me@miguelhiguera.dev">me@miguelhiguera.dev</a>
<p>You can also find me at:</p>
<div className={classes.links}>
<a target="_blank" href="https://github.com/miguelhigueradev">
<IconBrandGithub />
</a>
<a
target="_blank"
href="https://www.linkedin.com/in/miguelhigueradev/"
>
<IconBrandLinkedin />
</a>
</div>
</div>
</Window>
)
);
};
export default ContactApplication;
All applications are stored in the apps.ts
file so it's easy to add a new one by following the provided format.
This is a list of all the implemented applications:
Just displays a bit of information about me.
Displays a showcase of some of the projects I've worked on.
Displays contact information and social media websites.
Allows the user to chat with an AI that can answer questions about me (like my tech stack, projects, hobbies, etc.) and technology topics. Chat messages are stored in a ChatStore
to persist them even when the window is closed.
Displays my resume as a PDF embedded in an iframe.
Displays my main technology stack.
Contains a music player that has three tracks. It has synchronized lyrics.
Shows my Discord profile card with my current status, showing the game I'm playing or music I'm listening to. (currently hidden to improve this application).
Shows all the music and technologies used to make the website, besides some special thanks.
Just a hyperlink that redirects to this website.
A list of some of my favorite video games.
There is a notifications system built-in using a NotificationStore
. This is the notification interface:
interface Notification {
id: string;
title: string;
message: ReactNode;
}
To add a new notification, initialize the store's addNotification()
method:
import NotificationStore from './stores/NotificationStore';
// your code...
const { addNotification } = NotificationStore();
And call it:
addNotification(
'welcome-notification', // ID
'Welcome to RaccoonOS!', // Notification title
// Notification contents (just any JSX)
<>
<strong>Double click</strong> apps to open them or use the
<img
style={{
width: 20,
display: 'inline',
margin: '0 6px',
cursor: 'default',
}}
alt=""
src="https://github.com/MiguelHigueraDev/raccoonOS/raw/main/raccoonos-logo.webp"
/>
<strong>start menu</strong> at the bottom.
</>
);
Notifications are displayed in the top right corner of the screen:
The start menu is an alternative way of opening apps. It works exactly the same as opening them by double clicking but it is more accesible because it doesn't require a double click to open them.
There is an easter egg if you press the Konami Code. This is handled by using a useKonamiCode
custom React hook that detects when a key combination is entered and triggers a callback. The code can be customized to your needs.
Contact
applicationthree.js
@react-three/fiber
The chat backend is a just a Next.js application written with TypeScript.
If you think something could be improved or added to the website, feel free to submit an issue.
Thanks for visiting my website and reading this! Even though the website is in a good state, it's not completely finished and I have some applications yet to implement, alongside improving current ones and adding more features.