Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions workflowos/apps/WelcomeApp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

export class WelcomeApp {
static title = "Welcome";

constructor(container) {
this.container = container;
this.render();
}

render() {
this.container.innerHTML = `
<h1>Welcome to Workflow OS!</h1>
<p>This is the first micro-app.</p>
`;
}

destroy() {
// Cleanup logic for the app
console.log("WelcomeApp destroyed.");
}
}
26 changes: 26 additions & 0 deletions workflowos/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Workflow OS</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="os-container">
<div id="particle-container"></div>
<div id="ripple-container"></div>
<div id="desktop">
<div class="desktop-icon" data-app-id="welcome">
<span>Welcome</span>
</div>
<div class="desktop-icon" data-app-id="system-monitor">
<canvas id="systemMonitorIconCanvas" width="50" height="40"></canvas>
<span>System Monitor</span>
</div>
</div>
<div id="taskbar"></div>
</div>
<script src="main.js" type="module"></script>
</body>
</html>
180 changes: 180 additions & 0 deletions workflowos/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@

import { WelcomeApp } from './apps/WelcomeApp.js';

class WorkflowOS {
constructor() {
this.desktop = document.getElementById('desktop');
this.taskbar = document.getElementById('taskbar');
this.windows = new Map();
this.appRegistry = new Map();
this.nextZIndex = 100;

this.init();
}

init() {
this.registerApp('welcome', WelcomeApp);
this.setupDesktopIcons();
this.startSystemMonitorIcon();
this.setupCursorEffects();
}

setupDesktopIcons() {
const icons = document.querySelectorAll('.desktop-icon');
icons.forEach(icon => {
icon.addEventListener('dblclick', () => {
const appId = icon.dataset.appId;
this.launchApp(appId);
});
});
}

createWindow(appId, title) {
const windowId = `window-${appId}-${Date.now()}`;
const win = document.createElement('div');
win.className = 'window opening';
win.style.zIndex = this.nextZIndex++;
win.dataset.windowId = windowId;

win.innerHTML = `
<div class="window-header">
<span class="window-title">${title}</span>
<div class="window-controls">
<button class="close-btn">X</button>
</div>
</div>
<div class="window-content"></div>
`;

win.addEventListener('animationend', () => {
win.classList.remove('opening');
}, { once: true });

this.desktop.appendChild(win);
this.windows.set(windowId, { element: win, app: null });

this.setupWindowEvents(win);
return win;
}

setupWindowEvents(win) {
const header = win.querySelector('.window-header');

win.addEventListener('mousedown', () => this.focusWindow(win));

const closeBtn = win.querySelector('.close-btn');
closeBtn.addEventListener('click', (e) => {
e.stopPropagation();
this.closeWindow(win.dataset.windowId);
});

let isDragging = false;
let offset = { x: 0, y: 0 };

header.addEventListener('mousedown', (e) => {
isDragging = true;
offset.x = e.clientX - win.offsetLeft;
offset.y = e.clientY - win.offsetTop;
header.style.cursor = 'grabbing';
});

document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
win.style.left = `${e.clientX - offset.x}px`;
win.style.top = `${e.clientY - offset.y}px`;
});

document.addEventListener('mouseup', () => {
isDragging = false;
header.style.cursor = 'grab';
});
}

focusWindow(win) {
win.style.zIndex = this.nextZIndex++;
}

closeWindow(windowId) {
const windowData = this.windows.get(windowId);
if (windowData) {
const win = windowData.element;
win.classList.add('closing');
win.addEventListener('animationend', () => {
if (windowData.app && typeof windowData.app.destroy === 'function') {
windowData.app.destroy();
}
win.remove();
this.windows.delete(windowId);
}, { once: true });
}
}

registerApp(appId, appClass) {
this.appRegistry.set(appId, appClass);
}

launchApp(appId) {
const AppClass = this.appRegistry.get(appId);
if (AppClass) {
const win = this.createWindow(appId, AppClass.title || 'Application');
const content = win.querySelector('.window-content');
const appInstance = new AppClass(content);
const windowId = win.dataset.windowId;
this.windows.get(windowId).app = appInstance;
} else {
console.error(`App ${appId} not found.`);
}
}

startSystemMonitorIcon() {
const canvas = document.getElementById('systemMonitorIconCanvas');
if (!canvas) return;
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
let data = Array(width).fill(height);

function draw() {
data.shift();
data.push(height - Math.random() * height * 0.8);

ctx.clearRect(0, 0, width, height);
ctx.beginPath();
ctx.moveTo(0, data[0]);
for (let i = 1; i < width; i++) {
ctx.lineTo(i, data[i]);
}
ctx.strokeStyle = '#00ff00';
ctx.stroke();
}

setInterval(draw, 100);
}

setupCursorEffects() {
const particleContainer = document.getElementById('particle-container');
const rippleContainer = document.getElementById('ripple-container');

document.addEventListener('mousemove', e => {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.left = `${e.clientX}px`;
particle.style.top = `${e.clientY}px`;
particleContainer.appendChild(particle);
setTimeout(() => particle.remove(), 1000);
});

document.addEventListener('mousedown', e => {
const ripple = document.createElement('div');
ripple.className = 'ripple';
ripple.style.left = `${e.clientX - 10}px`;
ripple.style.top = `${e.clientY - 10}px`;
rippleContainer.appendChild(ripple);
setTimeout(() => ripple.remove(), 600);
});
}
}

window.addEventListener('load', () => {
window.os = new WorkflowOS();
});
Loading