Ramesh-vani commited on
Commit
9fc2761
·
verified ·
1 Parent(s): ba28a2b

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +579 -503
index.html CHANGED
@@ -3,13 +3,11 @@
3
  <head>
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Advanced Windows-like System with App Icons</title>
7
  <style>
8
- /* Basic Reset & Fonts */
9
  * { margin: 0; padding: 0; box-sizing: border-box; }
10
  body, html { width: 100%; height: 100%; overflow: hidden; font-family: Arial, sans-serif; }
11
 
12
- /* Desktop */
13
  #desktop {
14
  position: absolute;
15
  width: 100%;
@@ -39,7 +37,6 @@
39
  font-size: 14px;
40
  }
41
 
42
- /* Taskbar */
43
  #taskbar {
44
  position: absolute;
45
  bottom: 0;
@@ -49,7 +46,6 @@
49
  background: rgba(0,0,0,0.8);
50
  z-index: 1000;
51
  }
52
- /* Left side of Taskbar (Start button & pinned apps) */
53
  #taskbar-left {
54
  display: flex;
55
  align-items: center;
@@ -64,7 +60,6 @@
64
  cursor: pointer;
65
  margin-right: 10px;
66
  }
67
- /* Pinned Apps on Taskbar */
68
  #taskbar-pinned {
69
  display: flex;
70
  align-items: center;
@@ -77,7 +72,6 @@
77
  font-size: 18px;
78
  cursor: pointer;
79
  }
80
- /* Running Apps: प्रत्येक running window का अलग tab – केवल icon */
81
  #taskbar-windows {
82
  display: flex;
83
  align-items: center;
@@ -98,7 +92,6 @@
98
  display: inline-block;
99
  }
100
 
101
- /* Start Menu */
102
  #start-menu {
103
  position: absolute;
104
  bottom: 45px;
@@ -117,7 +110,6 @@
117
  }
118
  #start-menu li:hover { background: #f0f0f0; }
119
 
120
- /* Window Styles */
121
  .window {
122
  position: absolute;
123
  width: 400px;
@@ -137,16 +129,13 @@
137
  display: flex;
138
  align-items: center;
139
  justify-content: space-between;
140
- position: relative;
141
  }
142
  .window-header .app-icon {
143
  font-size: 16px;
144
  margin-right: 5px;
145
- display: inline-block;
146
  }
147
  .window-header .title {
148
  font-weight: bold;
149
- display: inline-block;
150
  }
151
  .window-header .controls button {
152
  background: transparent;
@@ -162,7 +151,6 @@
162
  max-height: calc(100% - 40px);
163
  }
164
 
165
- /* Resizer Styles */
166
  .resizer {
167
  position: absolute;
168
  background: transparent;
@@ -177,8 +165,7 @@
177
  .resizer.bottom-left { bottom: -5px; left: -5px; width: 10px; height: 10px; cursor: nesw-resize; }
178
  .resizer.bottom-right { bottom: -5px; right: -5px; width: 10px; height: 10px; cursor: nwse-resize; }
179
 
180
- /* Custom Context Menu */
181
- #custom-context-menu {
182
  position: absolute;
183
  display: none;
184
  background: #fff;
@@ -186,550 +173,592 @@
186
  z-index: 5000;
187
  box-shadow: 2px 2px 10px rgba(0,0,0,0.5);
188
  }
189
- #custom-context-menu ul { list-style: none; }
190
- #custom-context-menu li {
191
  padding: 8px 12px;
192
  cursor: pointer;
193
  }
194
- #custom-context-menu li:hover {
195
  background: #f0f0f0;
196
  }
197
-
198
- /* Customizer Form */
199
- .customizer-form label {
200
- display: block;
201
- margin: 5px 0 2px;
202
- }
203
- .customizer-form input,
204
- .customizer-form textarea {
205
- width: 100%;
206
- padding: 5px;
207
- margin-bottom: 10px;
208
- }
209
- .customizer-form button {
210
- padding: 5px 10px;
211
- cursor: pointer;
212
- }
213
  </style>
214
  </head>
215
  <body>
216
- <!-- A style element for custom CSS -->
217
  <style id="custom-css"></style>
218
-
219
- <!-- Desktop -->
220
  <div id="desktop">
221
- <div id="desktop-icons">
222
- <!-- Built-In App Icons (updated via JS) -->
223
- </div>
224
  </div>
225
-
226
- <!-- Taskbar -->
227
  <div id="taskbar">
228
  <div id="taskbar-left">
229
  <button id="start-button">Start</button>
230
- <div id="taskbar-pinned">
231
- <!-- Built-In Pinned Apps -->
232
- </div>
233
- <div id="taskbar-windows">
234
- <!-- Running windows: प्रत्येक window का एक अलग tab (केवल icon) -->
235
- </div>
236
  </div>
237
  </div>
238
-
239
- <!-- Start Menu -->
240
  <div id="start-menu">
241
- <ul id="start-menu-list">
242
- <!-- Built-In Start Menu Items -->
243
- </ul>
244
  </div>
245
-
246
- <!-- Custom Context Menu (Right-Click on Desktop) -->
247
  <div id="custom-context-menu">
248
  <ul>
249
  <li data-custom="openCustomizer">Open Customizer</li>
250
  <li data-custom="reset">Reset Customizations</li>
251
  </ul>
252
  </div>
 
 
 
 
 
 
 
253
 
254
  <script>
255
- /***** Global Variables *****/
256
- let windowCounter = 1;
257
- let runningWindows = {}; // Mapping windowId -> window element
258
- let zIndexCounter = 300; // For layering windows
259
-
260
- // Built-In Apps Array (always present)
261
- const builtInApps = [
262
- { id: "appInstaller", name: "App Installer", icon: "🛠" },
263
- { id: "customizer", name: "Customizer", icon: "🎨" }
264
- ];
265
-
266
- /***** Utility Functions *****/
267
- // Retrieve installed apps from localStorage (excluding built-ins)
268
- function getInstalledApps() {
269
- let apps = [];
270
- for (let i = 0; i < localStorage.length; i++) {
271
- let key = localStorage.key(i);
272
- if (key.startsWith("app_")) {
273
- try { apps.push(JSON.parse(localStorage.getItem(key))); }
274
- catch(e) { console.error(e); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  }
276
- }
277
- return apps;
278
- }
279
-
280
- // Update Desktop Icons: built-in apps first, then installed apps
281
- function updateDesktopIcons() {
282
- const container = document.getElementById("desktop-icons");
283
- container.innerHTML = "";
284
- builtInApps.forEach(app => {
285
- let icon = document.createElement("div");
286
- icon.className = "icon";
287
- icon.setAttribute("data-app", app.id);
288
- icon.innerHTML = `${app.icon}<br>${app.name}`;
289
- icon.onclick = () => openApp(app.id);
290
- container.appendChild(icon);
291
- });
292
- let apps = getInstalledApps();
293
- apps.forEach(app => {
294
- if (!builtInApps.find(b => b.id === app.id)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  let icon = document.createElement("div");
296
  icon.className = "icon";
297
  icon.setAttribute("data-app", app.id);
298
  icon.innerHTML = `${app.icon || '🔳'}<br>${app.name}`;
299
- icon.onclick = () => launchInstalledApp(app.id);
300
- container.appendChild(icon);
301
- }
302
- });
303
- }
304
 
305
- // Update Start Menu Items
306
- function updateStartMenuApps() {
307
- const list = document.getElementById("start-menu-list");
308
- list.innerHTML = "";
309
- builtInApps.forEach(app => {
310
- let li = document.createElement("li");
311
- li.setAttribute("data-app", app.id);
312
- li.innerHTML = `${app.icon} ${app.name}`;
313
- li.onclick = () => { openApp(app.id); hideStartMenu(); };
314
- list.appendChild(li);
315
- });
316
- let apps = getInstalledApps();
317
- apps.forEach(app => {
318
- if (!builtInApps.find(b => b.id === app.id)) {
319
  let li = document.createElement("li");
320
  li.setAttribute("data-app", app.id);
321
  li.innerHTML = `${app.icon || '🔳'} ${app.name}`;
322
- li.onclick = () => { launchInstalledApp(app.id); hideStartMenu(); };
323
- list.appendChild(li);
324
- }
325
- });
326
- }
327
-
328
- // Update Pinned Apps in Taskbar (built-in apps)
329
- function updateTaskbarPinned() {
330
- const container = document.getElementById("taskbar-pinned");
331
- container.innerHTML = "";
332
- builtInApps.forEach(app => {
333
- let btn = document.createElement("button");
334
- btn.setAttribute("data-app", app.id);
335
- btn.title = app.name;
336
- btn.innerHTML = `${app.icon}`;
337
- btn.onclick = () => openApp(app.id);
338
- container.appendChild(btn);
339
- });
340
- }
341
-
342
- // Update all system apps UI
343
- function updateSystemAppsUI() {
344
- updateDesktopIcons();
345
- updateStartMenuApps();
346
- updateTaskbarPinned();
347
- }
348
-
349
- function hideStartMenu() {
350
- document.getElementById("start-menu").style.display = "none";
351
- }
352
-
353
- /***** Start Menu Toggle *****/
354
- const startButton = document.getElementById("start-button");
355
- const startMenu = document.getElementById("start-menu");
356
- startButton.addEventListener("click", (e) => {
357
- e.stopPropagation();
358
- startMenu.style.display = startMenu.style.display === "block" ? "none" : "block";
359
- });
360
- document.addEventListener("click", (e) => {
361
- if (!startMenu.contains(e.target) && e.target !== startButton) {
362
- hideStartMenu();
363
- }
364
- document.getElementById("custom-context-menu").style.display = "none";
365
- });
366
-
367
- /***** Window Creation, Resizing & Controls *****/
368
- function createWindow(title, contentHTML = "", customControls = null) {
369
- const win = document.createElement("div");
370
- win.classList.add("window");
371
- win.style.top = "100px";
372
- win.style.left = "100px";
373
- win.style.width = "400px";
374
- win.style.height = "300px";
375
- win.dataset.windowId = windowCounter++;
376
- win.dataset.maximized = "false";
377
- // appId and appIcon can be set later
378
-
379
- const header = document.createElement("div");
380
- header.classList.add("window-header");
381
- let iconHTML = "";
382
- if(win.dataset.appIcon) {
383
- iconHTML = `<span class="app-icon">${win.dataset.appIcon}</span>`;
384
  }
385
- let controlsHTML = customControls ? customControls : "<button class='minimize-btn'>_</button><button class='maximize-btn'>[ ]</button><button class='close'>X</button>";
386
- header.innerHTML = `${iconHTML}<span class="title">${title}</span><span class="controls">${controlsHTML}</span>`;
387
- win.appendChild(header);
388
-
389
- const content = document.createElement("div");
390
- content.classList.add("window-content");
391
- content.innerHTML = contentHTML;
392
- win.appendChild(content);
393
-
394
- // Add resizers for all edges/corners
395
- makeResizable(win);
396
-
397
- document.body.appendChild(win);
398
- win.addEventListener("mousedown", () => bringToFront(win));
399
- makeDraggable(win, header);
400
- bringToFront(win);
401
-
402
- const minimizeBtn = header.querySelector(".minimize-btn");
403
- if(minimizeBtn) { minimizeBtn.addEventListener("click", () => minimizeWindow(win)); }
404
- const maximizeBtn = header.querySelector(".maximize-btn");
405
- if(maximizeBtn) { maximizeBtn.addEventListener("click", () => toggleMaximizeWindow(win, maximizeBtn)); }
406
- const closeBtn = header.querySelector(".close");
407
- if(closeBtn) { closeBtn.addEventListener("click", () => closeWindow(win)); }
408
-
409
- // **Note:** हम createTaskbarWindowButton को बाद में call करेंगे, जब appIcon सेट हो जाए
410
- runningWindows[win.dataset.windowId] = win;
411
- return win;
412
- }
413
 
414
- function makeDraggable(win, handle) {
415
- let offsetX = 0, offsetY = 0;
416
- handle.addEventListener("mousedown", function(e) {
417
- if(win.dataset.maximized === "true") return;
418
- offsetX = e.clientX - win.offsetLeft;
419
- offsetY = e.clientY - win.offsetTop;
420
- document.addEventListener("mousemove", mouseMove);
421
- document.addEventListener("mouseup", mouseUp);
422
- });
423
- function mouseMove(e) {
424
- let newLeft = e.clientX - offsetX;
425
- let newTop = e.clientY - offsetY;
426
- const desktopRect = document.getElementById("desktop").getBoundingClientRect();
427
- if(newLeft < 0) newLeft = 0;
428
- if(newTop < 0) newTop = 0;
429
- if(newLeft + win.offsetWidth > desktopRect.width) newLeft = desktopRect.width - win.offsetWidth;
430
- if(newTop + win.offsetHeight > desktopRect.height) newTop = desktopRect.height - win.offsetHeight;
431
- win.style.left = newLeft + "px";
432
- win.style.top = newTop + "px";
433
- }
434
- function mouseUp() {
435
- document.removeEventListener("mousemove", mouseMove);
436
- document.removeEventListener("mouseup", mouseUp);
437
- }
438
- }
439
-
440
- function makeResizable(win) {
441
- const directions = ["top", "right", "bottom", "left", "top-left", "top-right", "bottom-left", "bottom-right"];
442
- directions.forEach(direction => {
443
- const resizer = document.createElement("div");
444
- resizer.classList.add("resizer", direction);
445
- win.appendChild(resizer);
446
- resizer.addEventListener("mousedown", initResize);
447
- function initResize(e) {
448
- e.preventDefault();
449
- e.stopPropagation();
450
- const startX = e.clientX;
451
- const startY = e.clientY;
452
- const startWidth = parseInt(document.defaultView.getComputedStyle(win).width, 10);
453
- const startHeight = parseInt(document.defaultView.getComputedStyle(win).height, 10);
454
- const startLeft = win.offsetLeft;
455
- const startTop = win.offsetTop;
456
- const desktopRect = document.getElementById("desktop").getBoundingClientRect();
457
- function doResize(e) {
458
- let dx = e.clientX - startX;
459
- let dy = e.clientY - startY;
460
- let newWidth = startWidth;
461
- let newHeight = startHeight;
462
- let newLeft = startLeft;
463
- let newTop = startTop;
464
- if(direction.includes("right")) {
465
- newWidth = startWidth + dx;
466
- }
467
- if(direction.includes("left")) {
468
- newWidth = startWidth - dx;
469
- newLeft = startLeft + dx;
470
- }
471
- if(direction.includes("bottom")) {
472
- newHeight = startHeight + dy;
473
- }
474
- if(direction.includes("top")) {
475
- newHeight = startHeight - dy;
476
- newTop = startTop + dy;
477
- }
478
- if(newLeft < 0) { newWidth += newLeft; newLeft = 0; }
479
- if(newTop < 0) { newHeight += newTop; newTop = 0; }
480
- if(newLeft + newWidth > desktopRect.width) newWidth = desktopRect.width - newLeft;
481
- if(newTop + newHeight > desktopRect.height) newHeight = desktopRect.height - newTop;
482
- const minWidth = 100, minHeight = 50;
483
- if(newWidth < minWidth) {
484
- newWidth = minWidth;
485
- if(direction.includes("left")) { newLeft = startLeft + (startWidth - minWidth); }
486
- }
487
- if(newHeight < minHeight) {
488
- newHeight = minHeight;
489
- if(direction.includes("top")) { newTop = startTop + (startHeight - minHeight); }
490
  }
491
- win.style.width = newWidth + "px";
492
- win.style.height = newHeight + "px";
493
- win.style.left = newLeft + "px";
494
- win.style.top = newTop + "px";
495
- }
496
- function stopResize() {
497
- document.removeEventListener("mousemove", doResize);
498
- document.removeEventListener("mouseup", stopResize);
499
  }
500
- document.addEventListener("mousemove", doResize);
501
- document.addEventListener("mouseup", stopResize);
502
  }
503
- });
504
- }
505
-
506
- function bringToFront(win) {
507
- zIndexCounter++;
508
- win.style.zIndex = zIndexCounter;
509
- }
510
-
511
- function minimizeWindow(win) {
512
- win.style.display = "none";
513
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
514
 
515
- function restoreWindow(win) {
516
- win.style.display = "block";
517
- bringToFront(win);
518
- }
 
 
 
 
519
 
520
- function toggleMaximizeWindow(win, btn) {
521
- if(win.dataset.maximized === "true") {
522
- win.style.top = win.dataset.originalTop;
523
- win.style.left = win.dataset.originalLeft;
524
- win.style.width = win.dataset.originalWidth;
525
- win.style.height = win.dataset.originalHeight;
526
- win.dataset.maximized = "false";
527
- } else {
528
- win.dataset.originalTop = win.style.top;
529
- win.dataset.originalLeft = win.style.left;
530
- win.dataset.originalWidth = win.style.width;
531
- win.dataset.originalHeight = win.style.height;
532
- win.style.top = "0";
533
- win.style.left = "0";
534
- win.style.width = "100%";
535
- win.style.height = "100%";
536
- win.dataset.maximized = "true";
537
- }
538
- }
539
 
540
- function closeWindow(win) {
541
- const winId = win.dataset.windowId;
542
- const taskbarWindows = document.getElementById("taskbar-windows");
543
- const btn = taskbarWindows.querySelector(`button[data-window-id="${winId}"]`);
544
- if(btn) { btn.remove(); }
545
- delete runningWindows[winId];
546
- win.remove();
547
- }
548
 
549
- // Create a taskbar button for each running window – only icon shown
550
- function createTaskbarWindowButton(win) {
551
- const taskbarWindows = document.getElementById("taskbar-windows");
552
- let btn = document.createElement("button");
553
- btn.classList.add("taskbar-window-btn");
554
- btn.setAttribute("data-window-id", win.dataset.windowId);
555
- let icon = win.dataset.appIcon || '🔳';
556
- btn.innerHTML = `<span class="taskbar-icon">${icon}</span>`;
557
- btn.onclick = () => {
558
- if(win.style.display === "none") { restoreWindow(win); }
559
- else { bringToFront(win); }
560
- };
561
- taskbarWindows.appendChild(btn);
562
- }
563
 
564
- // Update window header to include app icon if available
565
- function updateWindowHeaderIcon(win) {
566
- const header = win.querySelector('.window-header');
567
- if(header && win.dataset.appIcon) {
568
- if(!header.querySelector('.app-icon')) {
569
- let iconSpan = document.createElement("span");
570
- iconSpan.className = "app-icon";
571
- iconSpan.style.marginRight = "5px";
572
- iconSpan.innerText = win.dataset.appIcon;
573
- const titleElem = header.querySelector(".title");
574
- if(titleElem) { header.insertBefore(iconSpan, titleElem); }
 
 
 
 
 
 
 
 
 
 
 
 
575
  }
576
  }
577
- }
578
-
579
- /***** Built-In Apps & Open Functions *****/
580
- // openApp: if built-in, call respective function; else launch installed app
581
- function openApp(appId) {
582
- if(appId === "appInstaller") {
583
- createAppInstaller();
584
- } else if(appId === "customizer") {
585
- createCustomizerApp();
586
- } else {
587
- launchInstalledApp(appId);
588
- }
589
- }
590
-
591
- // Built-In App: App Installer
592
- function createAppInstaller() {
593
- const win = createWindow("App Installer", "");
594
- win.dataset.appId = "appInstaller";
595
- win.dataset.appIcon = "🛠";
596
- updateWindowHeaderIcon(win);
597
- // अब taskbar button create करें
598
- createTaskbarWindowButton(win);
599
- const content = document.createElement("div");
600
- content.innerHTML = `
601
- <h3>Install App</h3>
602
- <textarea id="app-json" placeholder="Paste App JSON here"></textarea><br>
603
- <button id="install-app-btn">Install</button>
604
- <hr>
605
- <h3>Installed Apps (LocalStorage)</h3>
606
- <div id="installed-apps-list"></div>
607
- `;
608
- win.querySelector(".window-content").appendChild(content);
609
- win.querySelector("#install-app-btn").addEventListener("click", () => {
610
- installApp();
611
- updateSystemAppsUI();
612
- loadInstalledAppsList();
613
- });
614
- loadInstalledAppsList();
615
- }
616
 
617
- // Built-In App: Customizer
618
- function createCustomizerApp() {
619
- const win = createWindow("Customizer", "");
620
- win.dataset.appId = "customizer";
621
- win.dataset.appIcon = "🎨";
622
- updateWindowHeaderIcon(win);
623
- createTaskbarWindowButton(win);
624
- const content = document.createElement("div");
625
- content.innerHTML = `
626
- <h3>Customize System</h3>
627
- <div class="customizer-form">
628
- <label for="desktop-bg-color">Desktop Background Color:</label>
629
- <input type="color" id="desktop-bg-color" value="#ffffff">
630
- <label for="desktop-bg-image">Desktop Background Image URL:</label>
631
- <input type="text" id="desktop-bg-image" placeholder="Image URL">
632
- <hr>
633
- <label for="taskbar-bg-color">Taskbar Background Color:</label>
634
- <input type="color" id="taskbar-bg-color" value="#0078d7">
635
- <label for="taskbar-height">Taskbar Height (px):</label>
636
- <input type="number" id="taskbar-height" value="40">
637
- <hr>
638
- <label for="startmenu-bg-color">Start Menu Background Color:</label>
639
- <input type="color" id="startmenu-bg-color" value="#ffffff">
640
- <label for="startmenu-text-color">Start Menu Text Color:</label>
641
- <input type="color" id="startmenu-text-color" value="#000000">
642
- <hr>
643
- <label for="custom-css-input">Custom CSS:</label>
644
- <textarea id="custom-css-input" placeholder="Enter your custom CSS here"></textarea><br>
645
- <button onclick="applyAllCustomizations()">Apply Customizations</button>
646
- </div>
647
- `;
648
- win.querySelector(".window-content").appendChild(content);
649
- }
650
 
651
- // Apply customizations from Customizer app
652
- function applyAllCustomizations() {
653
- const desktopBgColor = document.getElementById("desktop-bg-color").value;
654
- const desktopBgImage = document.getElementById("desktop-bg-image").value;
655
- let desktopBgStyle = desktopBgColor;
656
- if(desktopBgImage.trim() !== "") { desktopBgStyle = `url('${desktopBgImage}')`; }
657
- document.getElementById("desktop").style.background = desktopBgStyle;
658
- document.getElementById("desktop").style.backgroundSize = "cover";
659
-
660
- const taskbarBgColor = document.getElementById("taskbar-bg-color").value;
661
- const taskbarHeight = document.getElementById("taskbar-height").value;
662
- document.getElementById("taskbar").style.background = taskbarBgColor;
663
- document.getElementById("taskbar").style.height = taskbarHeight + "px";
664
-
665
- const startmenuBgColor = document.getElementById("startmenu-bg-color").value;
666
- const startmenuTextColor = document.getElementById("startmenu-text-color").value;
667
- document.getElementById("start-menu").style.background = startmenuBgColor;
668
- document.getElementById("start-menu").style.color = startmenuTextColor;
669
-
670
- const customCss = document.getElementById("custom-css-input").value;
671
- document.getElementById("custom-css").innerHTML = customCss;
672
-
673
- alert("Customizations applied!");
674
- }
675
 
676
- // Launch Installed App (from localStorage)
677
- function launchInstalledApp(appId) {
678
- const key = "app_" + appId;
679
- let appData = JSON.parse(localStorage.getItem(key));
680
- if (!appData) return;
681
- const win = createWindow(appData.name, appData.ui, appData.windowControls);
682
- win.dataset.appId = appData.id;
683
- win.dataset.appIcon = appData.icon || '🔳';
684
- updateWindowHeaderIcon(win);
685
- createTaskbarWindowButton(win);
686
- if(appData.methods) {
687
- let appMethods = {};
688
- for(let method in appData.methods) {
689
- try {
690
- appMethods[method] = new Function("return " + appData.methods[method])();
691
- } catch(e) {
692
- console.error("Error in method", method, e);
693
- }
694
- }
695
- window.app = { methods: appMethods };
696
  }
697
- }
698
-
699
- // Load Installed Apps List in App Installer
700
- function loadInstalledAppsList() {
701
- const container = document.getElementById("installed-apps-list");
702
- if(container) {
703
- let apps = getInstalledApps();
704
- container.innerHTML = apps.map(app => `<div>${app.icon || '🔳'} ${app.name}</div>`).join("");
705
  }
706
- }
707
-
708
- // Install App from JSON input in App Installer
709
- function installApp() {
710
- let appData;
711
- const jsonText = document.getElementById("app-json").value;
712
- try { appData = JSON.parse(jsonText); }
713
- catch (error) { alert("Invalid JSON. Please check your syntax."); return; }
714
- if (!appData.id) { alert("App JSON must contain an 'id' property."); return; }
715
- localStorage.setItem("app_" + appData.id, JSON.stringify(appData));
716
- alert("App installed successfully!");
717
- document.getElementById("app-json").value = "";
718
- }
719
 
720
- /***** Custom Context Menu & Shortcut *****/
721
- const customContextMenu = document.getElementById("custom-context-menu");
722
  document.getElementById("desktop").addEventListener("contextmenu", function(e) {
723
  e.preventDefault();
 
724
  customContextMenu.style.display = "block";
725
  customContextMenu.style.top = e.clientY + "px";
726
  customContextMenu.style.left = e.clientX + "px";
727
  });
728
- customContextMenu.addEventListener("click", function(e) {
 
729
  const option = e.target.getAttribute("data-custom");
730
- if(option === "openCustomizer") {
731
- openApp("customizer");
732
- } else if(option === "reset") {
733
  document.getElementById("desktop").style.background = "url('background.jpg') no-repeat center center fixed";
734
  document.getElementById("desktop").style.backgroundSize = "cover";
735
  document.getElementById("taskbar").style.background = "rgba(0,0,0,0.8)";
@@ -739,13 +768,60 @@
739
  document.getElementById("custom-css").innerHTML = "";
740
  alert("Customizations reset to default!");
741
  }
742
- customContextMenu.style.display = "none";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
743
  });
744
 
745
- /***** Attach Static Events for Built-In Apps *****/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
746
  document.addEventListener("DOMContentLoaded", () => {
747
- updateSystemAppsUI();
 
748
  });
749
  </script>
750
  </body>
751
- </html>
 
3
  <head>
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Custom OS with Fixed FileSystem CRUD</title>
7
  <style>
 
8
  * { margin: 0; padding: 0; box-sizing: border-box; }
9
  body, html { width: 100%; height: 100%; overflow: hidden; font-family: Arial, sans-serif; }
10
 
 
11
  #desktop {
12
  position: absolute;
13
  width: 100%;
 
37
  font-size: 14px;
38
  }
39
 
 
40
  #taskbar {
41
  position: absolute;
42
  bottom: 0;
 
46
  background: rgba(0,0,0,0.8);
47
  z-index: 1000;
48
  }
 
49
  #taskbar-left {
50
  display: flex;
51
  align-items: center;
 
60
  cursor: pointer;
61
  margin-right: 10px;
62
  }
 
63
  #taskbar-pinned {
64
  display: flex;
65
  align-items: center;
 
72
  font-size: 18px;
73
  cursor: pointer;
74
  }
 
75
  #taskbar-windows {
76
  display: flex;
77
  align-items: center;
 
92
  display: inline-block;
93
  }
94
 
 
95
  #start-menu {
96
  position: absolute;
97
  bottom: 45px;
 
110
  }
111
  #start-menu li:hover { background: #f0f0f0; }
112
 
 
113
  .window {
114
  position: absolute;
115
  width: 400px;
 
129
  display: flex;
130
  align-items: center;
131
  justify-content: space-between;
 
132
  }
133
  .window-header .app-icon {
134
  font-size: 16px;
135
  margin-right: 5px;
 
136
  }
137
  .window-header .title {
138
  font-weight: bold;
 
139
  }
140
  .window-header .controls button {
141
  background: transparent;
 
151
  max-height: calc(100% - 40px);
152
  }
153
 
 
154
  .resizer {
155
  position: absolute;
156
  background: transparent;
 
165
  .resizer.bottom-left { bottom: -5px; left: -5px; width: 10px; height: 10px; cursor: nesw-resize; }
166
  .resizer.bottom-right { bottom: -5px; right: -5px; width: 10px; height: 10px; cursor: nwse-resize; }
167
 
168
+ #custom-context-menu, #file-context-menu {
 
169
  position: absolute;
170
  display: none;
171
  background: #fff;
 
173
  z-index: 5000;
174
  box-shadow: 2px 2px 10px rgba(0,0,0,0.5);
175
  }
176
+ #custom-context-menu ul, #file-context-menu ul { list-style: none; }
177
+ #custom-context-menu li, #file-context-menu li {
178
  padding: 8px 12px;
179
  cursor: pointer;
180
  }
181
+ #custom-context-menu li:hover, #file-context-menu li:hover {
182
  background: #f0f0f0;
183
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  </style>
185
  </head>
186
  <body>
 
187
  <style id="custom-css"></style>
 
 
188
  <div id="desktop">
189
+ <div id="desktop-icons"></div>
 
 
190
  </div>
 
 
191
  <div id="taskbar">
192
  <div id="taskbar-left">
193
  <button id="start-button">Start</button>
194
+ <div id="taskbar-pinned"></div>
195
+ <div id="taskbar-windows"></div>
 
 
 
 
196
  </div>
197
  </div>
 
 
198
  <div id="start-menu">
199
+ <ul id="start-menu-list"></ul>
 
 
200
  </div>
 
 
201
  <div id="custom-context-menu">
202
  <ul>
203
  <li data-custom="openCustomizer">Open Customizer</li>
204
  <li data-custom="reset">Reset Customizations</li>
205
  </ul>
206
  </div>
207
+ <div id="file-context-menu">
208
+ <ul>
209
+ <li data-action="open">Open</li>
210
+ <li data-action="rename">Rename</li>
211
+ <li data-action="delete">Delete</li>
212
+ </ul>
213
+ </div>
214
 
215
  <script>
216
+ /***** Core OS Object *****/
217
+ const OS = {
218
+ windowCounter: 1,
219
+ runningWindows: {},
220
+ zIndexCounter: 300,
221
+ eventListeners: {},
222
+
223
+ // File System API
224
+ FileSystem: {
225
+ init: function() {
226
+ if (!localStorage.getItem('fs_root')) {
227
+ localStorage.setItem('fs_root', JSON.stringify({
228
+ name: "Root",
229
+ type: "directory",
230
+ contents: []
231
+ }));
232
+ }
233
+ },
234
+ getRoot: function() {
235
+ return JSON.parse(localStorage.getItem('fs_root'));
236
+ },
237
+ saveRoot: function(root) {
238
+ console.log("Saving root:", JSON.stringify(root));
239
+ localStorage.setItem('fs_root', JSON.stringify(root));
240
+ OS.triggerEvent('fileSystemChanged', root);
241
+ },
242
+ addItem: function(name, type, content = "", appId = null, parentPath = "/") {
243
+ console.log(`Adding ${type}: ${name} to ${parentPath}`);
244
+ const root = this.getRoot();
245
+ let parent = parentPath === "/" ? root : this.findItem(parentPath);
246
+ if (!parent || parent.type !== "directory") {
247
+ console.error(`Invalid parent directory: ${parentPath}`);
248
+ return false;
249
+ }
250
+ if (parent.contents.some(item => item.name === name)) {
251
+ console.error(`Item ${name} already exists in ${parentPath}`);
252
+ return false;
253
+ }
254
+ const newItem = {
255
+ name: name,
256
+ type: type,
257
+ created: new Date().toISOString()
258
+ };
259
+ if (type === "file") {
260
+ newItem.content = content;
261
+ newItem.fileType = "text";
262
+ newItem.associatedAppId = appId;
263
+ } else if (type === "directory") {
264
+ newItem.contents = [];
265
+ }
266
+ parent.contents.push(newItem);
267
+ this.saveRoot(root);
268
+ OS.triggerEvent('itemAdded', { path: `${parentPath}${name}${type === 'directory' ? '/' : ''}`, item: newItem });
269
+ return true;
270
+ },
271
+ findItem: function(path) {
272
+ console.log(`Finding item at: ${path}`);
273
+ if (path === "/") return this.getRoot();
274
+ const parts = path.split('/').filter(p => p);
275
+ let current = this.getRoot();
276
+ for (let part of parts) {
277
+ current = current.contents.find(item => item.name === part);
278
+ if (!current) {
279
+ console.error(`Item not found: ${path}`);
280
+ return null;
281
+ }
282
+ }
283
+ return current;
284
+ },
285
+ removeItem: function(path) {
286
+ console.log(`Removing item: ${path}`);
287
+ const parent = this.getParent(path);
288
+ if (!parent) {
289
+ console.error(`Parent not found for: ${path}`);
290
+ return false;
291
+ }
292
+ const itemName = path.split('/').filter(p => p).pop();
293
+ const index = parent.contents.findIndex(item => item.name === itemName);
294
+ if (index === -1) {
295
+ console.error(`Item ${itemName} not found in parent`);
296
+ return false;
297
+ }
298
+ parent.contents.splice(index, 1);
299
+ this.saveRoot(this.getRoot());
300
+ OS.triggerEvent('itemRemoved', { path });
301
+ return true;
302
+ },
303
+ renameItem: function(oldPath, newName) {
304
+ console.log(`Renaming ${oldPath} to ${newName}`);
305
+ const item = this.findItem(oldPath);
306
+ if (!item) {
307
+ console.error(`Item not found: ${oldPath}`);
308
+ return false;
309
+ }
310
+ const parent = this.getParent(oldPath);
311
+ if (!parent) {
312
+ console.error(`Parent not found for: ${oldPath}`);
313
+ return false;
314
+ }
315
+ if (parent.contents.some(i => i.name === newName && i !== item)) {
316
+ console.error(`Name ${newName} already exists in directory`);
317
+ return false;
318
+ }
319
+ item.name = newName;
320
+ this.saveRoot(this.getRoot());
321
+ const newPath = `${parentPath(oldPath)}${newName}${item.type === 'directory' ? '/' : ''}`;
322
+ OS.triggerEvent('itemRenamed', { oldPath, newPath });
323
+ return true;
324
+ },
325
+ getParent: function(path) {
326
+ console.log(`Getting parent of: ${path}`);
327
+ if (path === "/") return null;
328
+ const parts = path.split('/').filter(p => p);
329
+ parts.pop();
330
+ return parts.length === 0 ? this.getRoot() : this.findItem("/" + parts.join('/'));
331
+ },
332
+ readFile: function(path) {
333
+ console.log(`Reading file: ${path}`);
334
+ const file = this.findItem(path);
335
+ if (file && file.type === "file") {
336
+ return file.content;
337
+ }
338
+ console.error(`File not found or not a file: ${path}`);
339
+ return null;
340
+ },
341
+ writeFile: function(path, content) {
342
+ console.log(`Writing to file: ${path}`);
343
+ const file = this.findItem(path);
344
+ if (!file || file.type !== "file") {
345
+ console.error(`File not found or not a file: ${path}`);
346
+ return false;
347
+ }
348
+ file.content = content;
349
+ this.saveRoot(this.getRoot());
350
+ OS.triggerEvent('fileWritten', { path, content });
351
+ return true;
352
  }
353
+ },
354
+
355
+ // Window Management API (unchanged for brevity, assumed working)
356
+ WindowManager: {
357
+ createWindow: function(title, contentHTML = "", options = {}) {
358
+ const win = document.createElement("div");
359
+ win.classList.add("window");
360
+ win.style.top = options.top || "100px";
361
+ win.style.left = options.left || "100px";
362
+ win.style.width = options.width || "400px";
363
+ win.style.height = options.height || "300px";
364
+ win.dataset.windowId = OS.windowCounter++;
365
+ win.dataset.maximized = "false";
366
+ win.dataset.appId = options.appId || '';
367
+
368
+ const header = document.createElement("div");
369
+ header.classList.add("window-header");
370
+ const controlsHTML = options.controls || "<button class='minimize-btn'>_</button><button class='maximize-btn'>[ ]</button><button class='close'>X</button>";
371
+ header.innerHTML = `<span class="app-icon">${options.icon || ''}</span><span class="title">${title}</span><span class="controls">${controlsHTML}</span>`;
372
+ win.appendChild(header);
373
+
374
+ const content = document.createElement("div");
375
+ content.classList.add("window-content");
376
+ content.innerHTML = contentHTML;
377
+ win.appendChild(content);
378
+
379
+ OS.makeResizable(win);
380
+ document.body.appendChild(win);
381
+ win.addEventListener("mousedown", () => OS.WindowManager.bringToFront(win));
382
+ OS.makeDraggable(win, header);
383
+ OS.WindowManager.bringToFront(win);
384
+
385
+ const minimizeBtn = header.querySelector(".minimize-btn");
386
+ if(minimizeBtn) minimizeBtn.addEventListener("click", () => OS.WindowManager.minimizeWindow(win));
387
+ const maximizeBtn = header.querySelector(".maximize-btn");
388
+ if(maximizeBtn) maximizeBtn.addEventListener("click", () => OS.WindowManager.toggleMaximizeWindow(win));
389
+ const closeBtn = header.querySelector(".close");
390
+ if(closeBtn) closeBtn.addEventListener("click", () => OS.WindowManager.closeWindow(win));
391
+
392
+ OS.runningWindows[win.dataset.windowId] = win;
393
+ OS.createTaskbarWindowButton(win);
394
+ OS.triggerEvent('windowCreated', { windowId: win.dataset.windowId });
395
+ return win;
396
+ },
397
+ minimizeWindow: function(win) { win.style.display = "none"; OS.triggerEvent('windowMinimized', { windowId: win.dataset.windowId }); },
398
+ maximizeWindow: function(win) {
399
+ if (win.dataset.maximized === "true") {
400
+ win.style.top = win.dataset.originalTop;
401
+ win.style.left = win.dataset.originalLeft;
402
+ win.style.width = win.dataset.originalWidth;
403
+ win.style.height = win.dataset.originalHeight;
404
+ win.dataset.maximized = "false";
405
+ } else {
406
+ win.dataset.originalTop = win.style.top;
407
+ win.dataset.originalLeft = win.style.left;
408
+ win.dataset.originalWidth = win.style.width;
409
+ win.dataset.originalHeight = win.style.height;
410
+ win.style.top = "0";
411
+ win.style.left = "0";
412
+ win.style.width = "100%";
413
+ win.style.height = "100%";
414
+ win.dataset.maximized = "true";
415
+ }
416
+ OS.triggerEvent('windowMaximized', { windowId: win.dataset.windowId, maximized: win.dataset.maximized === "true" });
417
+ },
418
+ toggleMaximizeWindow: function(win) { this.maximizeWindow(win); },
419
+ closeWindow: function(win) {
420
+ const winId = win.dataset.windowId;
421
+ const taskbarWindows = document.getElementById("taskbar-windows");
422
+ const btn = taskbarWindows.querySelector(`button[data-window-id="${winId}"]`);
423
+ if(btn) btn.remove();
424
+ delete OS.runningWindows[winId];
425
+ win.remove();
426
+ OS.triggerEvent('windowClosed', { windowId: winId });
427
+ },
428
+ bringToFront: function(win) {
429
+ OS.zIndexCounter++;
430
+ win.style.zIndex = OS.zIndexCounter;
431
+ OS.triggerEvent('windowFocused', { windowId: win.dataset.windowId });
432
+ },
433
+ getWindow: function(windowId) { return OS.runningWindows[windowId]; }
434
+ },
435
+
436
+ // App Management API (unchanged for brevity)
437
+ AppManager: {
438
+ apps: {},
439
+ registerApp: function(appId, appDef) {
440
+ this.apps[appId] = appDef;
441
+ localStorage.setItem(`app_${appId}`, JSON.stringify(appDef));
442
+ OS.triggerEvent('appRegistered', { appId });
443
+ OS.updateSystemAppsUI();
444
+ },
445
+ launchApp: function(appId, options = {}) {
446
+ const appDef = this.apps[appId] || JSON.parse(localStorage.getItem(`app_${appId}`));
447
+ if (!appDef) return null;
448
+ const win = OS.WindowManager.createWindow(appDef.name, appDef.ui || '', {
449
+ appId: appId,
450
+ icon: appDef.icon || '🔳',
451
+ controls: appDef.windowControls
452
+ });
453
+ if (appDef.init) appDef.init(win, options);
454
+ OS.triggerEvent('appLaunched', { appId, windowId: win.dataset.windowId });
455
+ return win;
456
+ }
457
+ },
458
+
459
+ // Event System
460
+ on: function(eventName, callback) {
461
+ if (!this.eventListeners[eventName]) this.eventListeners[eventName] = [];
462
+ this.eventListeners[eventName].push(callback);
463
+ },
464
+ triggerEvent: function(eventName, data) {
465
+ if (this.eventListeners[eventName]) {
466
+ this.eventListeners[eventName].forEach(callback => callback(data));
467
+ }
468
+ },
469
+
470
+ // Utility Functions (unchanged for brevity)
471
+ makeDraggable: function(win, handle) {
472
+ let offsetX = 0, offsetY = 0;
473
+ handle.addEventListener("mousedown", function(e) {
474
+ if (win.dataset.maximized === "true") return;
475
+ offsetX = e.clientX - win.offsetLeft;
476
+ offsetY = e.clientY - win.offsetTop;
477
+ document.addEventListener("mousemove", mouseMove);
478
+ document.addEventListener("mouseup", mouseUp);
479
+ });
480
+ function mouseMove(e) {
481
+ let newLeft = e.clientX - offsetX;
482
+ let newTop = e.clientY - offsetY;
483
+ const desktopRect = document.getElementById("desktop").getBoundingClientRect();
484
+ newLeft = Math.max(0, Math.min(newLeft, desktopRect.width - win.offsetWidth));
485
+ newTop = Math.max(0, Math.min(newTop, desktopRect.height - win.offsetHeight));
486
+ win.style.left = newLeft + "px";
487
+ win.style.top = newTop + "px";
488
+ }
489
+ function mouseUp() {
490
+ document.removeEventListener("mousemove", mouseMove);
491
+ document.removeEventListener("mouseup", mouseUp);
492
+ }
493
+ },
494
+ makeResizable: function(win) {
495
+ const directions = ["top", "right", "bottom", "left", "top-left", "top-right", "bottom-left", "bottom-right"];
496
+ directions.forEach(direction => {
497
+ const resizer = document.createElement("div");
498
+ resizer.classList.add("resizer", direction);
499
+ win.appendChild(resizer);
500
+ resizer.addEventListener("mousedown", initResize);
501
+ function initResize(e) {
502
+ e.preventDefault();
503
+ e.stopPropagation();
504
+ const startX = e.clientX;
505
+ const startY = e.clientY;
506
+ const startWidth = parseInt(document.defaultView.getComputedStyle(win).width, 10);
507
+ const startHeight = parseInt(document.defaultView.getComputedStyle(win).height, 10);
508
+ const startLeft = win.offsetLeft;
509
+ const startTop = win.offsetTop;
510
+ const desktopRect = document.getElementById("desktop").getBoundingClientRect();
511
+ function doResize(e) {
512
+ let dx = e.clientX - startX;
513
+ let dy = e.clientY - startY;
514
+ let newWidth = startWidth;
515
+ let newHeight = startHeight;
516
+ let newLeft = startLeft;
517
+ let newTop = startTop;
518
+ if(direction.includes("right")) newWidth = startWidth + dx;
519
+ if(direction.includes("left")) { newWidth = startWidth - dx; newLeft = startLeft + dx; }
520
+ if(direction.includes("bottom")) newHeight = startHeight + dy;
521
+ if(direction.includes("top")) { newHeight = startHeight - dy; newTop = startTop + dy; }
522
+ if(newLeft < 0) { newWidth += newLeft; newLeft = 0; }
523
+ if(newTop < 0) { newHeight += newTop; newTop = 0; }
524
+ if(newLeft + newWidth > desktopRect.width) newWidth = desktopRect.width - newLeft;
525
+ if(newTop + newHeight > desktopRect.height) newHeight = desktopRect.height - newTop;
526
+ const minWidth = 100, minHeight = 50;
527
+ newWidth = Math.max(minWidth, newWidth);
528
+ newHeight = Math.max(minHeight, newHeight);
529
+ win.style.width = newWidth + "px";
530
+ win.style.height = newHeight + "px";
531
+ win.style.left = newLeft + "px";
532
+ win.style.top = newTop + "px";
533
+ }
534
+ function stopResize() {
535
+ document.removeEventListener("mousemove", doResize);
536
+ document.removeEventListener("mouseup", stopResize);
537
+ }
538
+ document.addEventListener("mousemove", doResize);
539
+ document.addEventListener("mouseup", stopResize);
540
+ }
541
+ });
542
+ },
543
+ createTaskbarWindowButton: function(win) {
544
+ const taskbarWindows = document.getElementById("taskbar-windows");
545
+ let btn = document.createElement("button");
546
+ btn.classList.add("taskbar-window-btn");
547
+ btn.setAttribute("data-window-id", win.dataset.windowId);
548
+ let icon = win.querySelector('.app-icon')?.innerText || '🔳';
549
+ btn.innerHTML = `<span class="taskbar-icon">${icon}</span>`;
550
+ btn.onclick = () => {
551
+ if(win.style.display === "none") {
552
+ win.style.display = "block";
553
+ OS.WindowManager.bringToFront(win);
554
+ } else {
555
+ OS.WindowManager.bringToFront(win);
556
+ }
557
+ };
558
+ taskbarWindows.appendChild(btn);
559
+ },
560
+ updateSystemAppsUI: function() {
561
+ const desktopIcons = document.getElementById("desktop-icons");
562
+ const startMenuList = document.getElementById("start-menu-list");
563
+ const taskbarPinned = document.getElementById("taskbar-pinned");
564
+ desktopIcons.innerHTML = startMenuList.innerHTML = taskbarPinned.innerHTML = "";
565
+
566
+ const allApps = { ...builtInApps.reduce((acc, app) => ({ ...acc, [app.id]: app }), {}), ...OS.AppManager.apps };
567
+ Object.values(allApps).forEach(app => {
568
  let icon = document.createElement("div");
569
  icon.className = "icon";
570
  icon.setAttribute("data-app", app.id);
571
  icon.innerHTML = `${app.icon || '🔳'}<br>${app.name}`;
572
+ icon.onclick = () => OS.AppManager.launchApp(app.id);
573
+ desktopIcons.appendChild(icon);
 
 
 
574
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
  let li = document.createElement("li");
576
  li.setAttribute("data-app", app.id);
577
  li.innerHTML = `${app.icon || '🔳'} ${app.name}`;
578
+ li.onclick = () => { OS.AppManager.launchApp(app.id); hideStartMenu(); };
579
+ startMenuList.appendChild(li);
580
+
581
+ let btn = document.createElement("button");
582
+ btn.setAttribute("data-app", app.id);
583
+ btn.title = app.name;
584
+ btn.innerHTML = `${app.icon || '🔳'}`;
585
+ btn.onclick = () => OS.AppManager.launchApp(app.id);
586
+ taskbarPinned.appendChild(btn);
587
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
588
  }
589
+ };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
590
 
591
+ /***** Built-in Apps *****/
592
+ const builtInApps = [
593
+ {
594
+ id: "appInstaller",
595
+ name: "App Installer",
596
+ icon: "🛠",
597
+ init: function(win) {
598
+ win.querySelector(".window-content").innerHTML = `
599
+ <h3>Install App</h3>
600
+ <textarea id="app-json" placeholder="Paste App JSON here"></textarea><br>
601
+ <button id="install-app-btn">Install</button>
602
+ <hr>
603
+ <h3>Installed Apps</h3>
604
+ <div id="installed-apps-list"></div>
605
+ `;
606
+ win.querySelector("#install-app-btn").addEventListener("click", () => {
607
+ const jsonText = win.querySelector("#app-json").value;
608
+ try {
609
+ const appData = JSON.parse(jsonText);
610
+ if (!appData.id) throw new Error("App JSON must contain an 'id'");
611
+ OS.AppManager.registerApp(appData.id, appData);
612
+ win.querySelector("#app-json").value = "";
613
+ updateInstalledAppsList();
614
+ } catch (e) {
615
+ alert("Invalid JSON: " + e.message);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
616
  }
617
+ });
618
+ function updateInstalledAppsList() {
619
+ const list = win.querySelector("#installed-apps-list");
620
+ list.innerHTML = Object.values(OS.AppManager.apps).map(app => `<div>${app.icon || '🔳'} ${app.name}</div>`).join("");
 
 
 
 
621
  }
622
+ updateInstalledAppsList();
 
623
  }
624
+ },
625
+ {
626
+ id: "fileExplorer",
627
+ name: "File Explorer",
628
+ icon: "📁",
629
+ init: function(win) {
630
+ win.dataset.currentPath = "/";
631
+ win.querySelector(".window-content").innerHTML = `
632
+ <div style="margin-bottom: 10px;">
633
+ <button id="new-file-btn">New File</button>
634
+ <button id="new-folder-btn">New Folder</button>
635
+ <button id="up-btn">Up</button>
636
+ <span id="current-path" style="margin-left: 10px;">/</span>
637
+ </div>
638
+ <div id="file-list"></div>
639
+ `;
640
+ win.querySelector("#new-file-btn").addEventListener("click", () => createNewFile());
641
+ win.querySelector("#new-folder-btn").addEventListener("click", () => createNewFolder());
642
+ win.querySelector("#up-btn").addEventListener("click", () => navigateUp());
643
+ win.querySelector("#file-list").addEventListener("contextmenu", showContextMenu);
644
+ updateFileList();
645
+
646
+ function updateFileList() {
647
+ const currentPath = win.dataset.currentPath;
648
+ console.log(`Updating file list for: ${currentPath}`);
649
+ const currentDir = OS.FileSystem.findItem(currentPath) || OS.FileSystem.getRoot();
650
+ win.querySelector("#current-path").textContent = currentPath;
651
+ const fileList = win.querySelector("#file-list");
652
+ fileList.innerHTML = currentDir.contents.map(item => `
653
+ <div style="padding: 5px; cursor: pointer;" data-path="${currentPath}${item.name}${item.type === 'directory' ? '/' : ''}">
654
+ ${item.type === "file" ? "📄" : "📁"} ${item.name}
655
+ ${item.type === "file" ? `<button class="open-btn" data-path="${currentPath}${item.name}">Open</button>` :
656
+ `<button class="enter-btn" data-path="${currentPath}${item.name}/">Enter</button>`}
657
+ </div>
658
+ `).join("");
659
+ fileList.querySelectorAll(".open-btn").forEach(btn => btn.addEventListener("click", () => OS.AppManager.launchApp("textViewer", { filePath: btn.dataset.path })));
660
+ fileList.querySelectorAll(".enter-btn").forEach(btn => btn.addEventListener("click", () => { win.dataset.currentPath = btn.dataset.path; updateFileList(); }));
661
+ }
662
 
663
+ function createNewFile() {
664
+ const filename = prompt("Enter file name:");
665
+ if (filename && OS.FileSystem.addItem(filename, "file", "New file content", null, win.dataset.currentPath)) {
666
+ updateFileList();
667
+ } else {
668
+ alert("Failed to create file!");
669
+ }
670
+ }
671
 
672
+ function createNewFolder() {
673
+ const foldername = prompt("Enter folder name:");
674
+ if (foldername && OS.FileSystem.addItem(foldername, "directory", "", null, win.dataset.currentPath)) {
675
+ updateFileList();
676
+ } else {
677
+ alert("Failed to create folder!");
678
+ }
679
+ }
 
 
 
 
 
 
 
 
 
 
 
680
 
681
+ function navigateUp() {
682
+ if (win.dataset.currentPath === "/") return;
683
+ const parts = win.dataset.currentPath.split('/').filter(p => p);
684
+ parts.pop();
685
+ win.dataset.currentPath = parts.length ? `/${parts.join('/')}/` : "/";
686
+ updateFileList();
687
+ }
 
688
 
689
+ function showContextMenu(e) {
690
+ e.preventDefault();
691
+ const target = e.target.closest("[data-path]");
692
+ if (target) {
693
+ const path = target.dataset.path;
694
+ const contextMenu = document.getElementById("file-context-menu");
695
+ contextMenu.style.top = e.clientY + "px";
696
+ contextMenu.style.left = e.clientX + "px";
697
+ contextMenu.style.display = "block";
698
+ contextMenu.querySelector("ul").dataset.path = path;
699
+ contextMenu.querySelector("[data-action='open']").style.display = OS.FileSystem.findItem(path)?.type === "file" ? "block" : "none";
700
+ }
701
+ }
 
702
 
703
+ OS.on('fileSystemChanged', updateFileList);
704
+ OS.on('itemAdded', updateFileList);
705
+ OS.on('itemRemoved', updateFileList);
706
+ OS.on('itemRenamed', updateFileList);
707
+ OS.on('fileWritten', updateFileList);
708
+ }
709
+ },
710
+ {
711
+ id: "textViewer",
712
+ name: "Text Viewer",
713
+ icon: "📄",
714
+ init: function(win, options) {
715
+ const filePath = options.filePath;
716
+ const content = OS.FileSystem.readFile(filePath) || "";
717
+ win.querySelector(".window-content").innerHTML = `<textarea style="width: 100%; height: 100%;">${content}</textarea>`;
718
+ win.querySelector("textarea").addEventListener("change", () => {
719
+ const newContent = win.querySelector("textarea").value;
720
+ if (OS.FileSystem.writeFile(filePath, newContent)) {
721
+ console.log(`Content updated for ${filePath}`);
722
+ } else {
723
+ alert("Failed to save file content!");
724
+ }
725
+ });
726
  }
727
  }
728
+ ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
729
 
730
+ /***** System Initialization *****/
731
+ builtInApps.forEach(app => OS.AppManager.registerApp(app.id, app));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
732
 
733
+ document.getElementById("start-button").addEventListener("click", (e) => {
734
+ e.stopPropagation();
735
+ const startMenu = document.getElementById("start-menu");
736
+ startMenu.style.display = startMenu.style.display === "block" ? "none" : "block";
737
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
738
 
739
+ document.addEventListener("click", (e) => {
740
+ if (!document.getElementById("start-menu").contains(e.target) && e.target !== document.getElementById("start-button")) {
741
+ hideStartMenu();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
742
  }
743
+ if (!e.target.closest('#file-context-menu')) {
744
+ document.getElementById("file-context-menu").style.display = "none";
 
 
 
 
 
 
745
  }
746
+ document.getElementById("custom-context-menu").style.display = "none";
747
+ });
 
 
 
 
 
 
 
 
 
 
 
748
 
 
 
749
  document.getElementById("desktop").addEventListener("contextmenu", function(e) {
750
  e.preventDefault();
751
+ const customContextMenu = document.getElementById("custom-context-menu");
752
  customContextMenu.style.display = "block";
753
  customContextMenu.style.top = e.clientY + "px";
754
  customContextMenu.style.left = e.clientX + "px";
755
  });
756
+
757
+ document.getElementById("custom-context-menu").addEventListener("click", function(e) {
758
  const option = e.target.getAttribute("data-custom");
759
+ if (option === "openCustomizer") {
760
+ // Placeholder for customizer app
761
+ } else if (option === "reset") {
762
  document.getElementById("desktop").style.background = "url('background.jpg') no-repeat center center fixed";
763
  document.getElementById("desktop").style.backgroundSize = "cover";
764
  document.getElementById("taskbar").style.background = "rgba(0,0,0,0.8)";
 
768
  document.getElementById("custom-css").innerHTML = "";
769
  alert("Customizations reset to default!");
770
  }
771
+ document.getElementById("custom-context-menu").style.display = "none";
772
+ });
773
+
774
+ document.getElementById("file-context-menu").addEventListener("click", (e) => {
775
+ const action = e.target.dataset.action;
776
+ const path = e.target.closest("ul").dataset.path;
777
+ const fileExplorerWin = OS.WindowManager.getWindow(Object.keys(OS.runningWindows).find(id => OS.runningWindows[id].dataset.appId === "fileExplorer"));
778
+
779
+ if (action === "open") {
780
+ OS.AppManager.launchApp("textViewer", { filePath: path });
781
+ } else if (action === "rename") {
782
+ const newName = prompt("Enter new name:");
783
+ if (newName && OS.FileSystem.renameItem(path, newName)) {
784
+ if (fileExplorerWin) updateFileExplorer(fileExplorerWin);
785
+ } else {
786
+ alert("Failed to rename item!");
787
+ }
788
+ } else if (action === "delete") {
789
+ if (confirm("Are you sure you want to delete this item?") && OS.FileSystem.removeItem(path)) {
790
+ if (fileExplorerWin) updateFileExplorer(fileExplorerWin);
791
+ } else {
792
+ alert("Failed to delete item!");
793
+ }
794
+ }
795
+ document.getElementById("file-context-menu").style.display = "none";
796
  });
797
 
798
+ function updateFileExplorer(win) {
799
+ const updateFileList = () => {
800
+ const currentPath = win.dataset.currentPath;
801
+ const currentDir = OS.FileSystem.findItem(currentPath) || OS.FileSystem.getRoot();
802
+ win.querySelector("#current-path").textContent = currentPath;
803
+ const fileList = win.querySelector("#file-list");
804
+ fileList.innerHTML = currentDir.contents.map(item => `
805
+ <div style="padding: 5px; cursor: pointer;" data-path="${currentPath}${item.name}${item.type === 'directory' ? '/' : ''}">
806
+ ${item.type === "file" ? "📄" : "📁"} ${item.name}
807
+ ${item.type === "file" ? `<button class="open-btn" data-path="${currentPath}${item.name}">Open</button>` :
808
+ `<button class="enter-btn" data-path="${currentPath}${item.name}/">Enter</button>`}
809
+ </div>
810
+ `).join("");
811
+ fileList.querySelectorAll(".open-btn").forEach(btn => btn.addEventListener("click", () => OS.AppManager.launchApp("textViewer", { filePath: btn.dataset.path })));
812
+ fileList.querySelectorAll(".enter-btn").forEach(btn => btn.addEventListener("click", () => { win.dataset.currentPath = btn.dataset.path; updateFileList(); }));
813
+ };
814
+ updateFileList();
815
+ }
816
+
817
+ function hideStartMenu() {
818
+ document.getElementById("start-menu").style.display = "none";
819
+ }
820
+
821
  document.addEventListener("DOMContentLoaded", () => {
822
+ OS.FileSystem.init();
823
+ OS.updateSystemAppsUI();
824
  });
825
  </script>
826
  </body>
827
+ </html>