diff --git a/workflowos/apps/WelcomeApp.js b/workflowos/apps/WelcomeApp.js
new file mode 100644
index 0000000000..9ae944ff87
--- /dev/null
+++ b/workflowos/apps/WelcomeApp.js
@@ -0,0 +1,21 @@
+
+export class WelcomeApp {
+ static title = "Welcome";
+
+ constructor(container) {
+ this.container = container;
+ this.render();
+ }
+
+ render() {
+ this.container.innerHTML = `
+
Welcome to Workflow OS!
+ This is the first micro-app.
+ `;
+ }
+
+ destroy() {
+ // Cleanup logic for the app
+ console.log("WelcomeApp destroyed.");
+ }
+}
diff --git a/workflowos/index.html b/workflowos/index.html
new file mode 100644
index 0000000000..f8d963f611
--- /dev/null
+++ b/workflowos/index.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+ Workflow OS
+
+
+
+
+
+
+
+
+ Welcome
+
+
+
+ System Monitor
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/workflowos/main.js b/workflowos/main.js
new file mode 100644
index 0000000000..1bd6378ff6
--- /dev/null
+++ b/workflowos/main.js
@@ -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 = `
+
+
+ `;
+
+ 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();
+});
diff --git a/workflowos/style.css b/workflowos/style.css
new file mode 100644
index 0000000000..ebf41b3aa7
--- /dev/null
+++ b/workflowos/style.css
@@ -0,0 +1,182 @@
+body, html {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ font-family: sans-serif;
+ background-color: #333;
+ color: #fff;
+}
+
+#os-container {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+#desktop {
+ flex-grow: 1;
+ position: relative;
+ padding: 20px;
+}
+
+.desktop-icon {
+ width: 70px;
+ text-align: center;
+ padding: 10px;
+ border-radius: 5px;
+ margin: 10px;
+ cursor: pointer;
+ transition: background-color 0.2s ease-in-out;
+}
+
+.desktop-icon:hover {
+ background-color: rgba(255, 255, 255, 0.1);
+}
+
+.desktop-icon:active {
+ background-color: rgba(255, 255, 255, 0.2);
+}
+
+.desktop-icon span {
+ display: block;
+ margin-top: 5px;
+ font-size: 12px;
+ color: #fff;
+ text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
+}
+
+#taskbar {
+ width: 100%;
+ height: 40px;
+ background-color: #222;
+ border-top: 1px solid #444;
+}
+
+.window {
+ position: absolute;
+ top: 50px;
+ left: 50px;
+ width: 400px;
+ height: 300px;
+ background-color: #444;
+ border: 1px solid #555;
+ border-radius: 5px;
+ box-shadow: 0 0 10px rgba(0,0,0,0.5);
+ display: flex;
+ flex-direction: column;
+}
+
+.window-header {
+ height: 30px;
+ background-color: #555;
+ padding: 0 10px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ cursor: grab;
+}
+
+.window-title {
+ font-weight: bold;
+}
+
+.window-controls {
+ display: flex;
+}
+
+.close-btn {
+ background: #ff5f56;
+ border: 1px solid #e0443e;
+ border-radius: 50%;
+ width: 12px;
+ height: 12px;
+ cursor: pointer;
+}
+
+.window-content {
+ flex-grow: 1;
+ padding: 10px;
+}
+
+@keyframes open-window {
+ from {
+ opacity: 0;
+ transform: scale(0.9);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+@keyframes close-window {
+ from {
+ opacity: 1;
+ transform: scale(1);
+ }
+ to {
+ opacity: 0;
+ transform: scale(0.9);
+ }
+}
+
+.window.opening {
+ animation: open-window 0.2s ease-in-out;
+}
+
+.window.closing {
+ animation: close-window 0.2s ease-in-out;
+}
+
+#particle-container {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ z-index: 9999;
+}
+
+.particle {
+ position: absolute;
+ width: 5px;
+ height: 5px;
+ background-color: #00ffcc;
+ border-radius: 50%;
+ opacity: 0;
+ animation: fade-out 1s forwards;
+}
+
+#ripple-container {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ z-index: 9998;
+}
+
+.ripple {
+ position: absolute;
+ width: 20px;
+ height: 20px;
+ border: 2px solid #00ffcc;
+ border-radius: 50%;
+ transform: scale(0);
+ animation: ripple-effect 0.6s forwards;
+}
+
+@keyframes fade-out {
+ from { opacity: 1; transform: scale(1); }
+ to { opacity: 0; transform: scale(0); }
+}
+
+@keyframes ripple-effect {
+ from { transform: scale(0); opacity: 1; }
+ to { transform: scale(5); opacity: 0; }
+}